Copy Link
Add to Bookmark
Report
Saxonia Issue 03 Part 031
Coding tutorial 3
By Rumrunner/VOID
l
Hello everybody, I hope that you are still following this tutorial, because
this time, we'll get more into the interesting parts of coding issues. I
will show some examples of how to use the copperlist both to get images on
the screen and also how to make a simple copperbar move. In addition the
code example this time will have a systemtakeover-routine which is complete
except for saving the vbr-register. You need this for using interrupts on
processors other than the plain 68000, so this will come next time.
Before I start commenting the code, remember that the source and needed
files are to be found in the file coding-tutorial.lha which should be spread
in the Saxonia archive.
Right let's start to take a look at the code then.2
section moststuff,code
startup:
move.l 4.w,a6
lea.l graphicsname(pc),a1
jsr -408(a6) ;open graphics.library
move.l d0,a6
move.l 38(a6),-(a7) ;save the systemcopperlist
move.l $22(a6),-(a7) ;save the systemview
0
First of all, we start off by letting this part of the file load into
whatever memory the computer has, it's not important whether it's chip or
fast, although fastmemory is preferable when the code gets "hard" as it's
faster than chipmemory, especially when you use the blitter at the same
time.
Then, we open graphics.library, which has some interesting routines in it.
Since we are going to use some of them, we put it in a6. I don't think that
I have said this before, but remember that when writing systemcode, many
systemroutines expect to find the baseadress of it's library in a6. Although
you sometimes can use another register (not the low ones though, but for
instance a5 might work in some cases) sometimes, this can make strange
things happen.
Then, before we call any routines in graphics.library, we store the system's
copperlist and the view belonging to the system. If you look close, you
will notice that the copperlist is found at an offset of 38 bytes from the
graphicsbase, while the view is found 34 bytes from the same base, I have
just used hex-notation here. Since we don't need these values before
switching the system back on, we can just store them on the stack
Back to the code :2
sub.l a1,a1
jsr -222(a6) ;loadview
jsr -270(a6) ;waittof
jsr -270(a6) ;assure that view is reset
0
Here we do a loadview-call with zero in a1, which most often is referred
to as loadview null. This gives a basic screen with a copperlist doing
nothing and it's the easiest way of assuring that things will look right
even if people use other screenmodes than pal. The waittof call is simply
the system's way of waiting for a vertical blank, the known and easy way
of timing effects. It simply halts the code until the screenraster starts
drawing at the top of the screen again, which happens 50 times a second in
palmode. We need to do this call two times to be sure that the view is reset
before continuing.2
move.l a6,-(a7) ;save graphicsbase on stack
move.w intenar,-(a7) ;save interrupt enable
or.w #$c000,(a7) ;set bit 14 and 15
move.w intreqr,-(a7) ;save interrupt request
or.w #$8000,(a7) ;set bit 14
move.w dmaconr,-(a7) ;save dmastatus
or.w #$8000,(a7) ;set bit 15
move.w adkconr,-(a7) ;save audio/disk controller
or.w #$8000,(a7) ;set bit 15
move.w #$7fff,intena ;stop all interrupts
move.w #$7fff,intreq ;stop all interrupt request
move.w #$7fff,dmacon ;stop all dma action
move.w #$7fff,adkcon ;stop audio/disk controller
0
Since we are done with the graphics.library for now, let's also store the
graphicsbase on the stack and then store the important registers which we
are going to change. Intenar is the register where you read out the
interrupt enable-status on Amiga. There are several interrupts you can
enable or disable, there are even more than one belonging to each of the
seven interrupts in the MC680X0 processors, I will tell more about this
next time. Also worth noticing is that hardwareregisters in the Amiga is
either readonly or writeonly. So whereas you read interruptstatus from
intenar, you would write to intena. Trying to read a writeregister or write
to a read-register can cause strange things to happen.
You see that I store the registers also on the stack, then or them with
either $c000 or $8000, this is to set bit 15 (and also bit 14 in the case
of $c000), this has to do with the workings of these registers, which I
also will cover in the next tutorial.
Having stored the necessary registers, it's now time to take over the
system, it's done by writing $7fff to the same registers (and remember to
write to the write-one and not the read-one here).2
move.l #picture,d0
lea.l copperbpladress,a0
move.w d0,6(a0) ;the low word of the adress
swap d0
move.w d0,2(a0) ;the high word of the adress
0
The copperlist is the perfect place of controlling bitplanes that go on
screen, you can do it with the processor, but the bitplane-pointer
registers need to be written each vertical blank, and since the copper goes
through it's list each vbl, it's better to let this handle it. So, we
store the adress of the one bitplane we want to display at the proper
place in the copperlist. To find these, look at the full code and see for
yourself and try to figure out where the values end up. And remember that
move.l #picture,d0 gives you the adress where the picture is located, if
you had used move.l picture,d0, this would have given you the first longword
in the picture, which you don't need here.2
move.l #copperlist,cop1lch ;set own copperlist
move.w #$8380,dmacon ;switch on dmaen, copen, bplen
0
Here, we just write the copperlist to the proper register that get's it
run every vertical blank. This register doesn't need to be rewritten before
you want another copperlist being done. You can usually figure out if a
register needs to be rewritten every vertical blank or not by looking at
the ending of the registername. If it's ptr, pth or ptl (pointer, pointer to
highword, pointer to lowword), it needs to be rewritten. If it's lch, lcl,
it doesn't.
The dmacon register controls, as the name suggests, which dmachannels are
to be switched either on or off. Here, we switch on dma-enable, copper-
enable and bitplane-enable, which will give us the opportunity of using
the copper and bitplanes.2
mainroutine:
btst #0,$dff005
beq.s mainroutine
.vbl btst #0,$dff005
bne.s .vbl ;vertical blank timing routine
bsr.s copperbar ;do a little copperroutine
btst #6,$bfe001
bne.s mainroutine
0
Here we have come to the mainroutine, this will execute once every vertical
blank. The first four lines, excluding the mainroutine-label is the piece
of code we need to wait for the vertical blank. Then we jump to the routine
that moves the copperbar on screen, before checking left mousebutton. If
the button is not pressed, the code jumps back to the start of mainroutine
and waits for the next vertical blank to happen.2
enditall:
move.w (a7)+,adkcon ;set back audio/disk controller
move.w (a7)+,dmacon ;set back dma
move.w (a7)+,intreq ;set back interrupt request
move.w (a7)+,intena ;set back interrupts
move.l (a7)+,a6 ;get graphicsbase again
move.l (a7)+,a1 ;the systemview
jsr -222(a6) ;loadview on systemview
jsr -270(a6) ;waittof
jsr -270(a6) ;again
move.l (a7)+,cop1lch ;set back systemcopperlist
move.l a6,a1
move.l 4.w,a6
jsr -414(a6) ;close graphics.library
0
This is the piece of code that will execute when the user presses the left
mousebutton. First we put all the register we have changed back to the
state they were in before we took over the system, we do this by getting
them back from the stack. Then we fetch the graphicsbase from the stack
aswell as the systemview. A loadview with the systemview in a1 will make
the screen go back to the state it had before we took over. Then, we also
need to put the copperlist back, before we can close the graphics.library.
2
moveq #0,d0 ;no error
rts ;end
0
This is the last part to end the demo. First we set the d0 register to zero
as to signal to dos that there are no errors. Any other error would be
noticable if you tried to run the demo from a script. In that case the
script would stop and display an errormessage. The rts jumps out.
Now, let's take a look at the routine that makes the copperbar move : 2
copperbar:
move.w addcoppervalue,d0
lea.l waitposition1,a0
add.w d0,(a0)
addq.l #8,a0
add.w d0,(a0)
cmp.w #$ea01,(a0) ;has the bar reached bottom
bne.s .fine1 ;if no continue
neg.w addcoppervalue ;if yes, switch direction
.fine1:
cmp.w #$4a01,(a0) ;has the bar reached the top
bne.s .fine2 ;if no continue
neg.w addcoppervalue ;if yes, switch direction
.fine2:
rts
addcoppervalue:
dc.w $1000
First, we have a value of $100 which we either add or substract in the
correct place in the copperlist. Where we do this is at waitposition1 and
waitposition2, however only the waitposition1 is referred here as we can
get to the next one by adding eight to the first. Then we add the value to
the position found in the aforementioned labels. If we substract, then we
simply add -$100. In order to change the value, we check if the bar is at
either the top or bottom and simply reverse the $100 found in
addcoppervalue, comfortably setting it appropriate for the next time the
routine is called.
That's it, this is all there is to move a copperbar and display a picture.
There's no need to describe the copperlist here, as I told about this last
time and the copperlist found in the source is commented enough so there
should not be any problems.
I would suggest that you load the code in your assembler, try it out,
try to change the routines, like changing the addcoppervalue to $200 to
double the speed, add a label to the copperline where the colour change
between the two waits and change this colour when the copperbar moves and
so on. There are some places you can optimise the code if you are ready
for that now. For instance, instead of doing move.w $7fff.... in each
register in the takeoverroutine you could do move.w $7fff,d0 and then write
do to each register. You could load $dff000 in an adressregister and then
write to the correct offset from this adress in the takeover and
store-routine. Use your imagination, and if you wonder what happens here and
there, I suggest using an assembler like Asm-One and tracing trough the
routines with it. Just use ad to assemble and enter the debugger. Watch
what happens in the registers and try to enter the code and check again.
What happens now? If you want to singlestep into the copperbar-routine,
use rightarrow to do this instead of tracing it with arrow down.
For next time, I think that I will use the same takeover and systemstore-
routines but a little more optimised. So you will see how this can look if
you don't get the hang of it now. And if there are some instructions you
wonder what do, try the debugger first, just write the lines of code needed
to make you understand it and trace/singestep this. If this doesn't help,
try to find some documentation. The programmers reference manual from
Motorola is very thorough describing the instructions, just skip the
unnecessesary pages if you cannot print it out at work/school, since it's
a lot.
Good luck