Copy Link
Add to Bookmark
Report

Saxonia Issue 02 Part 047

eZine's profile picture
Published in 
Saxonia
 · 22 Aug 2019

  

Coding tutorial part 2
by Rumrunner/VOID
[l

Welcome back to this article series. This time, I will give a new example
which will introduce some new instructions and show you how to display a
copperlist on screen.

Right, let's take a look at the example code first and then I will comment
everything below :[1

move.l 4.w,a6 ;execbase
lea.l graphicsname(pc),a1 ;load adress into a1
jsr -408(a6) ;exacbase, openlibrary
move.l d0,gfxbase ;store graphic library base
move.l d0,a0 ;copy gfxbase to a0
move.l 38(a0),savecopper ;systemcopperlist found here
jsr -132(a6) ;stop multitasking

move.l #copperlist,$dff080 ;install own copperlist
wait:
btst #6,$bfe001 ;wellknown mousewait routine
bne.s wait

move.l savecopper,$dff080 ;get back systemcopperlist

move.l gfxbase,a1 ;put gfxbase into register a1
jsr -414(a6) ;close graphics library
jsr -138(a6) ;enable multitasking again

moveq #0,d0 ;there are no errors
rts ;finished

gfxbase:
dc.l 0 ;space to store graphicsbase

savecopper: ;space to store copperlist
dc.l 0

graphicsname: ;name of library to open
dc.b "graphics.library",0
even

**************
* Copperlist *
**************
section needschip,data_c

copperlist:
dc.w $100,0 ;no bitplanes
dc.w $180,$329 ;colour register 0 (background)
dc.w $6501,$fffe ;wait for line 65 position 01
dc.w $180,0 ;colour register 0 again
dc.w $7501,$fffe ;wait for line 75 position 01
dc.w $180,$ff0 ;again background colour register
dc.l -2 ;copperlist finished[0
[l

Right, that's all it takes for now, not that frightening. So let's start
commenting what we do here.

[1
move.l 4.w,a6 ;execbase[0

The move instruction can move data in various ways. What this particular
instruction does is take the number that's located at memory adress 4 and
put this value into the register a6. .l means that it takes a longword.
A longword is 4 bytes (refer to the previous tutorial to learn about how
bytes are built up. Another possibility is move.w which takes a word. A
word is two bytes (half a longword). But as this is an adress, we need
the whole longword. The .w in 4.w is a little trick for optimisation. What
is basically does is only assemble 0004 into the instruction instead of
00000004, which means that we save two bytes. Don't worry about this for
now, it will become clear in a while. Just remember that you can use this
kind of adressing for "low" adresses, like 4, $6c and so on.
[1
lea.l graphicsname(pc),a1 ;load adress into a1[0

Lea is a command that loads a particular adress into a register. Here we
load the adress where the name of the graphics.library is located into a1.
Basically this means that if we look at the adress a1 now contains, we will
find the string "graphics.library". The (pc) means that in this case,
there's no need to store the whole adress in the code, we can use the
program counter to find it. The program counter is the processor's register
which it uses to find out where (in memory) it should execute the command
from.
[1
jsr -408(a6) ;exacbase, openlibrary[0

This is an easy one, however it is also somewhat diffucult. Let me start
by saying that jsr jumps to the adress given and continues to execute the
program from there. When it reached the rts command (which should be
familiar), it continues below the point where the jsr was executed. Right
now, we don't jump to a directly specified adress though, we jump to the
adress 408 bytes below the content of a6. If you remember that we put the
content of memory adress 4 into a6, this will soon become clear. The adress
4 is used by the operating system for the most important library on the
Amiga, the exec.library. This library deals with allocating memory, opening
and closing other libraries and other important stuff. So what we are
working with here is really systemcode. -408 from execbase is the system
function for opening a library. That's why we put the adress to the
graphics.library string in a1, the openlibrary (-408) function in
exec.library expects to find the adress of this string in register a1.
There are some system functions that doesn't need any parameters, but for
those who do, you always put the parameters in the given registers. These
can be found for instance in the system programmer's manual.
[1
move.l d0,gfxbase ;store graphic library base[0

Now, after executing the openlibrary function, that particular action
gives us something interesting back in the d0 register. This is the
graphics.library base. Just like exec, all registers have a baseadress,
from which you can call functions. However, this is not why we open the
library, but you will soon see why we do so. This is also another good
example of a move command. We are still working with longwords, but now
we take the value in d0 (the graphics.library base) and put it at a safe
place in memory. If you read the previous tutorial and understood the part
about labels, you will recognise this, as we now store something into the
memory a label represents instead of jumping there.
[1
move.l d0,a0 ;copy gfxbase to a0[0

This is yet another example of the move command. This is the simples one,
we move the content of d0 (still the graphics.library base) into a0. This
because adress registers have some possibilities dataregisters don't have.
You will see it in the next instruction.
[1
move.l 38(a0),savecopper ;systemcopperlist found here[0

Now, we are talking. This is one of the most important tasks you can
perform with the move command. I guess that it's clear that we store
something at the label savecopper, but what we store is perhaps not so
clear. What 38(a0) means is that we don't want to store anything that has
to do with the content of a0. What we want to store is the value located
38 bytes above the adress that a0 is pointing to. I will illustrate this
here :

Let's say that a0 is $40000. Now, while the command move.l a0,savecopper
would store the value $40000, we now want to store the content of adress
$40038. If the value located at $40038 is $3ff0, then savecopper will now
contain the value $3ff0. You can think a little about the jsr command we
have taken a look at, as it is the same kind of adressing. Also take a look
at the first command (move.l 4.w,a6). We want the content of adress 4 and
not the number 4, this is also the case here.
[1
jsr -132(a6) ;stop multitasking[0

Here, it's time for yet another system call. Execbase stops multitasking
when you jump to -132(execbase), so let's do that in order to not interfere
too much with other running programs.
[1
move.l #copperlist,$dff080 ;install own copperlist[0

Another move again. Hopefully, you will now understand that this is an
important instruction. You will certainly use it a lot. What we do here
is to take the adress of the adress copperlist: and put this into the
register $dff080, which by the way is called cop1lch. It's here that the
Amiga finds out where the copperlist is located.
A little help now, some people might already have found this out, but here
we go. We want the adress of the copperlist right? Yes, so we could also
have used :

lea.l copperlist,a0
move.l a0,$dff080

Refer to the notes about the lea instruction of this seems strange.
However, as the copperlist is in another section (remember those from
last time?) we cannot use the optimising method (pc) in order to shorten
the program and get a little faster execution.
By the way, it's time to tell a little about the hardware registers. They
are located from the adress $dff000 to $dffffe, however not all are used.
The best way of getting to know these is to get a copy of the hardware
reference manual. You will need this as registers have several ways of
working. The cop1lch register is easy because you only need to feed it
with an adress. Other register however, have certain functions connected
to each bit and works in special ways (which we will come back to in the
next tutorial), so an overview on paper is very good.


The next part of the code is the famous mousewait routine from last time,
so there's no need to comment that again.
[1
move.l savecopper,$dff080 ;get back systemcopperlist[0

Right, when the user presses the mousebutton, we want to get the system
back on track. What we need to do is to put back the copperlist we found
38 bytes over execbase. So, we simply write it to $dff080. This also shows
yet another way of using the move instrucion. We now move the value located
in memory adress the savecopper label is pointing to into $dff080. If we
tried move.l #savecopper,$dff080, we would move the adress where the label
was into $dff080 and not what it was pointing to. This is a bit confusing
in the start, but it will quickly clear out.
[1
move.l gfxbase,a1 ;gfxbase into register a1 [0

We stored the graphics.library base earlier, so let's move it into a1,
because that's where the exec.library expects it when you want to close
graphics.library again.

[1
jsr -414(a6) ;close graphics library[0

So, again an exec.library function. It simply closes the graphics.library.
[1
jsr -138(a6) ;enable multitasking again[0

We stopped multitasking earlier, -138(execbase) enables multitasking again.
[1
moveq #0,d0 ;there are no errors[0

Moveq is a special kind of move. It is faster than the original move but
can only be used on numbers which can be represented with a few bits only.
It doesn't matter however, where these bits are set. When exiting a program,
a zero in d0 means that there are no errors. So the system will not put
up an error message. If you want to try to make an error however, you need
to run your program from a script (like the startup-sequence) as it doesn't
show when run directly from CLI.
[1
rts ;finished[0

Finally, we have reached the end of the program. There is some more to
comment though.

[1
savecopper: ;space to store copperlist
dc.l 0[0

Just like last time, this is a label. It's no code at this memory location
this time however, we just want to have a place to store a longword.
dc.l simply means declare longword, in other words we put a longword of
zero at the location. You can use dc.l 4598 or whatever if you want to
as the content will be replaced when we do a move.l to the location.

The graphicsname label also shows that you can put ascii numbers into
memory. We use dc.b since one character uses one byte, so longword would
be meaningless here. The "" signs tells that it is ascii-values we want,
the assembler replaces these with the correct bytes for us.

Now, we have come to the interesting part, the copperlist. The copper is
one of the Amiga's most unique features, until the Commodore One (which
sound really exciting read separate article) arrived, the Amiga was the
only computer who could have several resolutions on screen at once. And
since the Commodore One is a kind of special interest computer, just like
the Amiga has become, Amiga is still the only widely used computer ever
which has had this possibility.

Now, how can the copper do such magic? Well, the principle is simple,
however, the workings is a little hard to cath at first.

The copper has three functions and the two most important ones are move
and wait. Move moves a value into the adress specified. The wait command
stops execution until the screen is drawn at the specified waiting
position. In the copperlist in our program, we can see examples of both
commands. It's important to remember that every adress we write to is
written in words, not longwords, as the copper is a wordprocessor, or to
avoid confusion, it processes words as in two bytes. However, when we
write to $180, we really write to $dff180 (remember hardware registers?).
So we can only write to hardware registers using the copper but those are
the only ones of interest here, so that's not a problem.

A move is carried out in the following way. Note that most assemblers
doesn't support copper instructions, so we have to use dc.w (declare word) :

dc.w $hardwareregister,$value

example:
dc.w $180,$0

$dff180 is the background colour register, and it will here get the
value zero, which means black screen. When we write to $dff100 in the
program's copperlist, this is the bplcon0 register, which we need to
set up bitplanes to get images on screen.

The wait command is carried out this way :

dc.w waitposition,$fffe

example:
dc.w $6501,$fffe

Try to change the values written to $180 and the waits in the program
copperlist and you will soon find out how it works. There are some things
you need to know about waitpositions first though. First of all, the
first byte (in this example $65) is the vertical position and the next
byte ($01) is the horisontal position. You can wait for all vertical
positions, but the horisontal positions need to be odd, like 1,3,5 and so
on. Next, the screens doesn't start at vertical $00, it starts at about
$20,$30. If you try values like this in the copperlist, you will see that
way on top of the screen, the colourchange occurs. Then, the bottom of
the screen (the PAL area) starts at zero. So, let's illustrate

____________________
---/ | | Ntsc part
| | |
| s | |$30 to $ff
| c | |
| r | |
| e | |
| e | |-------
| n | |Pal part
| | |$01 to $30
| | |
---\ |____________________|


With some possible difference on ntsc start and pal stop.

How to use the palscreen, I will come back to later, but for now, have
fun with the example code, it's included on the disk so you won't have
to type everything in. Try altering parts when you figure out what it
does and see what happens. Not just in the copperlist, but in the code
like I suggested (remember the move.l #copperlist.....).

And good luck.

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT