„Amiga Machine Language (Chapter 5)” változatai közötti eltérés
Innen: amigaspirit.hu - pegasos.hu Wiki
Ugrás a navigációhozUgrás a kereséshez
(intial import as separate page) |
(Nincs különbség)
|
A lap jelenlegi, 2009. május 25., 13:29-kori változata
CHAPTER 5.
---------
5.Hardware Registers.
--------------------
You can get information about hardware functions without using
library functions.You can use the hardware registers instead.These
are memory locations at particular addresses that are neither in
RAM nor in ROM.They are direct interfaces between the processor
and its peripheral devices.
Each device has a number of hardware registers that the processor
accesses to control graphics,sound and input/output.There are lots
of possibilities for assembly language programmers.We'll only be
able to go into a few examples.
The registers are generally used in byte-wise fashion.You'll find
an example in the next chapter.
5.1.Checking For Special Keys.
-----------------------------
Load AssemPro and enter the debugger,select "Parameter-Display-
From-Address"and enter $BFEC00.Next select "Parameter-Display-Hex
-Dump"to display the memory.(To use the SEKA assembler or a similar
monitor program,enter "q $bfec00".)
Yoy'll see a byte-wise listing of the addresses starting at
$BFEC00 in which two bytes always repeat.These two bytes represent
the status of the two hardware registers.
The mirroring occurs because not all the address bits are used in
decoding the address.In addressing this register,only the upper
two bytes of the address and the low bit,bit 0,are used.The
address of the two registers goes like this:$BFECxx,where the
lower address byte xx doesn't contain any information in bits 1-7.
Only bit 0 contains information about the desired register.You'll
find this odd form of addressing with most hardware registers.
Let's look at the information in these registers.Let's look at the
second register,$BFEC01.Hold down the<ALT>key and select"Parameter
-Display-HEX-Dump"to redisplay the screen.(SEKA owners must enter
"q $bfec00"and press the<ALT>key right after pressing the<Return>
key.)You'll see that contents of every two bytes($BFEC01,$BFEC03,
etc...)have been changed to $37.This is the status of the special
keys.This is also true for the other special keys.The following
keys produce the bytes:
Shift left $3F
Shift right $3D
Control $39
Alternate $37
Amiga left $33
Amiga right $31
You can use this register to have a machine language program check
if one of these keys was pressed and then respond by calling or
ending a function.A program section might look like this:
skeys=$bfec01
...
cmp.b #$37,skeys ;Alternate pressed?
beq function1 ;Yes!
cmp.b #$31,skeys ;or right Amiga?
beq function2 ;Yes!
... ;and so on...
5.2.Timing.
----------
If you want to find out how much time elapsed between two events,
you can use a hardware register to keep track of time quickly and
precisely.The Amiga contains just such a timekeeper:the I/O port
componant.The chip has a 24 bit wide counter that has a 60 Hertz
clock.
These 24 bits can't be read at once,for instance with a MOVE.L
command,because the register is divided into three bytes.The low
byte is at address $BFE801,the middle at $BFE901,and the high byte
with bits 16-23 at $BFEA01.
Here's an example of a way to use this register:finding out how
long a subroutine takes to run.
test:
bsr gettime ;put current time in D7
move.l d7,d6 ;save it in D6
bsr routine ;routine to be timed
bsr gettime ;get the time again
sub.l d6,d7 ;elapsed time in
... ;1/50 seconds in D7!
nop ;set break point here to stop
routine: ;test routine
move #500,d0 ;delay counter
loop:
dbra d0,loop ;count down
rts
gettime:
move.b $bfea01,d7 ;hi-byte in D0
lsl.l #4,d7 ;shift twice by 4 bits
lsl.l #4,d7 ;(8 bits shifted)
move.b $bfe901,d7 ;get mid-byte
lsl.l #4,d7
lsl.l #4,d7 ;shift again
move.b $bfe801,d7 ;get the lo-byte
rts ;done
5.3.Reading The Mouse-Joystick.
-------------------------------
There are two hardware registers for the mouse and the joystick.
They contain the state(or the position)of these input devices.Its
interesting that the same port is used with both the mouse and the
joystick even through they work completely different.
The joystick as four switches that are closed during movement and
give off a potential(-)that is related to the movement of the
joystick/mouse.The mouses movements give off lots of quick signals
-two for horizontal and two for vertical movements.
The computor must keep an eye on the ports so that it can evaluate
the signals and calculate the new mouse position.This isn't the
work of the processor though;it already has too much to do.
You find the status of the mouse/joystick port at address $DFF00A
for port 1 and $DFF00C for port 2.The information in these words
is for vertical mouse movement in the lower byte and for
horizontal movement in the upper byte.
AssemPro owners be careful!Don't read these addresses,because for
some reason that causes the computor to crash.This looks
interesting(the screen begins to dance)but you can only recover by
pressing <RESET>and losing all your data.
To read this register,lets write a short program:
;(5.3A) mouse
test:
jsr run ;test subroutine
jmp test ;continue until broken
nop ;breakpoint here
joy= $dff00a
run:
move joy,d6 ;data item 1 in D6
move joy+2,d7 ;data item 2 in D7
jmp run ;rts for SEKA and other
end
If you assemble the program and start breakable in the debugger
(SEKA-"j run"),D6 and D7 contain the contents of the two registers
Move the mouse a bit and watch the register contents.
As you see,the value in D6 is different.If you just move the mouse
horizontaly,only the upper bytes value is different,if just moved
vertically only the upper byte is different.
You are not getting the absolute position of the mouse pointer on
the screen.You can see that easily by moving the mouse in the
upper left corner,then reading the value by restarting the program
and moving the mouse left again.As you can see,the registers
contents are always relative.
Change the program as follows:
;(5.3B) mouse difference
test:
jsr run ;test subroutine
jmp test ;continue until broken
nop ;breakpoint here
joy= $dff00a
run:
move d7,d6 ;old position in D6
move joy,d7 ;new position in D7
sub d7,d6 ;difference in D6
jmp run ;rts for SEKA and other
end
Start Breakable(right-Amiga-A)in the AssemPro debugger and watch
D6,the result is zero or D7.(SEKA owners have to start the program
two times.The result in D6 is zero.)If you move the mouse,D6
contains the difference between the old and new positions since
the start.You'll find the vertical and horizontal positions of the
mouse relative to the last time you looked.In this way,you can use
this register to find the relative mouse movement between two
checks.
Now to check the joysticks.Put a joystick in port 2 and change the
address $DFF00A to $DFF00C in the program.Start Breakable in the
AssemPro debugger and watch D6,the result is zero or D7.(SEKA
owners have to start the program two times.The result in D6 is
zero.)
Move the joystick up.You'll get the value $FF00.One was subtracted
from the upper byte.Let the joystick loose.This time you get the
value $100-one is added.You'll get the same effect if you move the
joystick left-after you let go,one is subtracted.
The individual movements and their effects on the joystick program
are:
UP $FF00 HI-BYTE -1
DOWN $FFFF LO-BYTE -1
LEFT $0100 HI-BYTE +1
RIGHT $0001 LO-BYTE +1
These values aren't terribly reliable.If you move the joystick a
lot and then look at the value,you'll find a crazy value in D6.
This is because the input driver thinks that a mouse is attached.
Nevertheless,this is the quickest way to read a joystick.In this
way,an external device that gives off evaluatable TTL signals can
be connected to the port and watched by a machine language
program.
Now you just need to find out whether the fire button has been
pressed,and you'll know how to get all the information you need
from the joystick.The buttons state is in bit 7 of the byte that
is in memory location $BFE001.If the bit is set,the button was'nt
pressed.That's true for the joystick connected to port 2.Bit 6 of
this byte contains the buttons state when the joystick is in port
1 or the state of the left mouse button.
Let's stay on port 2.You can test bit 7 to execute a function when
the joystick button is pressed without any problems.Bit 7 is the
sign bit.You can use this program segment:
tst.b $bfe001 ;was fire button 2 hit?
bpl fire ;yes!branch
The TST.B instruction tests the addressed byte and sets the Z and
the N flag.If the N flag is set,you know that bit 7 of the tested
byte is set.Since the fire button turns on LO potential,the bit is
erased when the button is pressed.The N flag works that way with
the TST command as well.The BPL command in the program above
branches if the button was pressed.The PL stands for plus and is
set when the sign bit is cleared.
Here is the complete program to check the fire button and joystick
difference:
;(5.3C) fire button and joy difference
test:
jsr run ;test subroutine
tst.b $bfe001 ;was fire button 2 hit?
bpl fire ;yes! branch
jmp test ;continue until broken
joy = $dff00a
run:
move d7,d6 ;old position in D6
move joy,d7 ;new position in D7
sub d7,d6 ;difference in D6
jmp run ;rts for SEKA and other
fire:
nop ;breakpoint here
end
5.4.Tone Production.
-------------------
It's fun to make noises and sounds.The Amiga lets you use Audio
Devices and various I\O structures to play tones,noises and/or
music pieces in the background.You'll leave this method to C or
Basic programmers,since you can use short machine language
programs to directly program the audio hardware.
The Paula chip has all the capabilities needed for tone production
This chip can be accessed using the hardware registers of the
processor.No library of any high level language can do more than
you can-program the chip.
How does it work?Since the disk uses Direct Memory Access(DMA)to
get information,you just need to tell it where to look for the
tone or tone sequences that you would like played.You also need to
tell it how to interpret the data.
Lets start with the easiest case-producing a constant tone.A tone
like this consists of a single oscillation that is repeated over
and over.If you make a diagram of the oscillation,you see the wave
form of the oscillation.There are several standard waves: sine,
square,triangle and saw tooth.The simplest is the square wave.
To produce a square wave,you just need to turn the loud speaker on
and off.The frequency that occurs here is the frequency of the
tone.
You want to produce such a tone using the Amiga.First you need to
make a table that contains the amplitude of the tone you wish to
produce.For a square wave,you only need two entries in the table,a
large and a small value.Since the sound chip in the Amiga has
amplitude values between -128 and +127,our table looks like this:
soundtab:
dc.b -100,100
You need to give the address of the table to the sound chip.You
have four choices,since the Amiga as four sound channels.The
address of the hardware register in which the table address for
channel 0 must be written is $DFF0A0;for channel 1 it is $DFF0B0;
for channel 2 its $DFF0C0;for channel 3 its $DFF0D0.For stereo
output,channels 0 and 3 control the left loud speaker.Channels 1
and 2 control the right loud speaker.For example,choose channel 0
and write the following:
move.l #soundtab,$DFF0A0 ;address of the table
Next you need to tell the sound chip how many items there are in
the table.The data is read from beginning to end and sent to the
loud speaker.Once it reaches the end,it starts over at the
beginning.Since the sound chip gets this one word at a time,even
though the data is in bytes,the table must always have an even
number of bytes.The length that you give it is the number of words
the number of bytes/2.
You put the length for channel 0 in the register at address
$DFF0A4(for channel x just add x*$10!):
move #1,$dff0a4 ;length of table in words
Now you have to tell it how quickly to read the data and output it
to the loud speaker.This word determines the frequency.However,it
does this "backwards".The larger the value,the lower the frequency
Choose the value 600 for this example:
move #600,$dff0a6 ;read in rate
Now you need to decide the loudness level for the tone or noise.
You have 65 different levels to choose from.Lets choose the middle
value 40 for our example:
move #40,$dff0a8 ;loudness level
Thats the data that the sound chip needs to produce the tone.
However nothing happens yet.What next?The chip can't tell if the
data thats in the registers is valid,so it doesn't know if it
should use the data.
You need to work with the DMA control register at address $DFF096
to let it know.You only need six bits of this word for your
purposes:
Bit 15 ($8000) If this bit is set,every bit that is written to
this internal register is set.Otherwise the bits
are erased.Zero bits aren't affected.This is very
useful because this word also contains DMA
information for disk operations that should'nt be
changed.
Bit 9 ($200) This bit makes it posible for the chip to access
DMA memory.If you want to start playing the tone,
you need to set this bit.
Bit 0-3 Turn channel 0-3 on when bits are set.
You'll start your tone by setting bits 15,9 and 0:
move #$8000+$200+1,$dff096 ;start DMA
Heres an example of tone production-this time with tone using a
sine wave:
;**Sound Generation using hardware registers** (5.5A)
ctlw = $dff096 ;DMA control
cothi = $dff0a0 ;table address HI
c0tlo = $c0thi+2 ;table address LO
c0tl = $c0thi+4 ;table length
c0per = $c0thi+6 ;read in rate
c0vol = $c0thi+8 ;loudness level
run: ;*Produce a simple tone
move.l #table,c0thi ;table beginning
move #8,c0tl ;table length--8 words
move #400,c0per ;read in rate
move #40,c0vol ;loudness level (volume)
move #$8201,ctlw ;DMA/Start sound
rts
data ;>500K place in CHIP memory
table: ;sound table:sine
dc.b -40,-70,-40,0,40,70,40,0
end
To test this subroutine,use AssemPro to assemble the routine,save
the program and load it into the debugger.Next set a breakpoint at
the RTS,to set the breakpoint in AssemPro select the correct
address with the mouse and press the right-Amiga-B keys.Start the
program and listen to the tone.You need another routine to turn
the tone off,turn your sound down for now.
To turn the tone off,you just need to erase bit 0 of the DMA
control register.To do this,you just need to write a 0 in bit 15
and all the set bits in this register are erased.To erase bit 0,
just write a one to the memory location:bit 15=0=> bit 0 is erased
Heres a small routine to stop the tone coming from channel 0:
still: ;*turn off tone
move #1,ctlw ;turn off channel 1
rts
Now lets use the routine in a program to produce a short peep tone
that you culd,for instance,use as a key click:
;** Producing a Peep Tone **
ctlw = $dff096 ;DMA control
c0thi = $dff0a0 ;HI table address
c0tlo = $c0thi+2 ;LO table address
c0tl = $c0thi+4 ;table length
c0per = $c0thi+6 ;read in rate
c0vol = $c0thi+8 ;volume
beep: ;*Produce a short peep tone
move.l #table,c0thi ;table beginning
move #8,c0tl ;table length
move #400,c0per ;read in rate
move #65,c0vol ;volume
move #$8201,ctlw ;Start DMA (sound)
move.l #20000,d0 ;delay counter
loop:
dbra d0,loop ;count down
still:
move #1,ctlw ;turn off tone
rts
table:
dc.b 40,70,90,100,90,70,40,0,-4,0
end
You can play upto four tones at the same time in such a way that
they are independant of each other.The Amiga also offers another
method of making the sound more interesting:you can modulate the
tone.
Lets produce a siren tone.You could do this by figuring out the
entire sequence and programming it.However,as you can well imagine
thats a lot of work.
Its much easier to use two tone channels.Lets use channel 1 for
the bass tone and channel 0 for its modulation.Channel 0 needs to
hold the envelope of the siren tone.It needs to give the expanding
and contracting of the tone at the right speed.
You then have two ways that you can have channel zero work with
channel one.You can control the volume via channel 0,the read in
rate(frequency),or both.For our example,you'll use frequency
modulation.
Change the program as follows:
;** Modulated sound generation via hardware registers **
ctlw = $dff096 ;DMA control
adcon = $dff09e ;Audio/Disk control
c0thi = $dff0a0 ;HI table address
c0tlo = c0thi+2 ;LO table address
c0tl = c0thi+4 ;table length
c0per = c0thi+6 ;read in rate
c0vol = c0thi+8 ;volume
run:
move.l #table,c0thi+16 ;table start for channel 1
move #8,c0tl+16 ;table length--8 words
move #300,c0per+16 ;read in rate
move #40,c0vol+16 ;volume
move.l #table2,c0thi ;table start for channel 0
move #8,c0tl ;table length
move #60000,c0per ;read in rate
move #30,c0vol ;volume
move #$8010,adcon ;modulation mode:FM
move #$8203,ctlw ;start DMA
rts
still: ;*Turn Off Tone
move #$10,adcon ;no more modulations
move #3,ctlw ;turn off channels
rts
table: ;data for basic tone
dc.b -40,-70,-90,-100,-90,-70,-40,0
dc.b 40,70,90,100,90,70,40,0
table2: ;data for modulation
dc.w 400,430,470,500,530,500,470,430
end
When you start the program,you'll here a siren.You can change this
tone to your hearts content.
Did you notice the added "adcon"register.This register controls
the modulation of the audio channel as well as handling disk
functions.The same technique is used here as for the DMA control
register,bits can only be set if bit 15 is.As a result,you don't
have to worry about the disk bits.I'd recommend against
experimentation.
Control bit 15 isn't the only one of interest to you.You can also
use bits 0-7,because they determine which audio channel modulates
another channel.There is a restriction,though.A channel can only
modulate the next higher numbered channel.For this reason you use
channel 1 for the basic tone and channel 0 for the modulation in
the example.You can't for example,modulate channel three with
channel zero.Channel 3 can't be used to modulate any other
channel.
Here is an overview of bits 0-7 of the "adcon"register.
Bit Function
-----------------------------------------------------------------
0 Channel 0 modulates the volume of channel 1
1 Channel 1 modulates the volume of channel 2
2 Channel 2 modulates the volume of channel 3
3 Turn of channel 3
4 Channel 0 modulates the frequency of channel 1
5 Channel 1 modulates the frequency of channel 2
6 Channel 2 modulates the frequency of channel 3
7 Turn off channel 3
In the example,you set bit 4,which put channel 0 in charge of
channel one's frequency modulations.
When you've chosen a channel for use in modulating another channel
some of the parameters of the channel change.You don't need to
give volume for this channel,so you can omit it.Now the tables
data is looked at as words instead of as bytes.These words are
read into the register of the modulated register at a
predetermined rate.The Read in Rate Register determines the rate.
If you want to modulate the frequency and the volume of another
channel,(In the example,set bits 0 and 4 of "adcon"),the data is
interpreted a little differently.The first word in the table is
the volume,the second is the read in rate,and so on.It alternates
back and forth.In this way,you can for instance,produce the siren
tone.
5.5.Hardware Registers Overview.
-------------------------------
The following tables should give you an overview of the most
important hardware registers.Theres not enough room to describe
each register,so I'd recommend getting a hold of the appropriate
literature.If you experiment with these registers,you should keep
in mind that this can cause the computor to crash.Save your data
to disk and then take the disk out of the drive,because you might
cause the disk drive to execute some wierd functions.
Lets start with the PIA's.This covers the PIA type 8520.You should
keep in mind that some functions and connection of the 8520 are
integrated into the Amiga and so there are limitations on what you
can do with the PIA's.
PIA A PIA B Registers Meaning
------------------------------------------------------------------
BFE001 BFE000 Data register A
BFE101 BFE100 Data register B
BFE201 BFE200 Data direction register A
BFE301 BFE300 Data direction register B
BFE401 BFE400 Timer A LO
BFE501 BFE500 Timer A HI
BFE601 BFE600 Timer B LO
BFE701 BFE700 Timer B HI
BFE801 BFE800 Event register Bits 0-7
BFE901 BFE900 Event register Bits 8-15
BFEA01 BFEA00 Event register Bits 16-23
BFEB01 BFEB00 Unused
BFEC01 BFEC00 Serial data register
BFED01 BFED00 Interrupt control register
BFEE01 BFEE00 Control register A
BFEF01 BFEF00 Control register B
Some internal meanings:
$BFE101 Data register for parallel interface
$BFE301 Data direction register for the parallel interface
$BFEC01 State of the keyboard,contains the last special key
pressed(Shift,Alternate,Control,Amiga)
Now come the registers that are used for tone production.The first
two registers should be treated especially carefully-if they are
used wrong,very nasty effects can occur.
These registers can be either read or written only.This
information is included under R/W in the table.
Address R/W Meaning
------------------------------------------------------------------
DFF096 W Write DMA Control
DFF002 R Read DMA Control and Blitter Status
--Audio Channel 0--
DFF0AA W Data register
DFF0A0 W Pointer to table beginning Bits 16-18
DFF0A2 W Pointer to table beginning Bits 0-15
DFF0A4 W Table length
DFF0A6 W Read in Rate
DFF0A8 W Volume
--Audio Channel 1--
DFF0BA W Data register
DFF0B0 W Pointer to table beginning Bits 16-18
DFF0B2 W Pointer to table beginning Bits 0-15
DFF0B4 W Table length
DFF0B6 W Read in Rate
DFF0B8 W Volume
--Audio Channel 3--
DFF0CA W Data register
DFF0C0 W Pointer to table beginning Bits 16-18
DFF0C2 W Pointer to table beginning Bits 0-15
DFF0C4 W Table length
DFF0C6 W Read in Rate
DFF0C8 W Volume
--Audio Channel 4--
DFF0DA W Data register
DFF0D0 W Pointer to table beginning Bits 16-18
DFF0D2 W Pointer to table beginning Bits 0-15
DFF0D4 W Table length
DFF0D6 W Read in Rate
DFF0D8 W Volume
Now for the registers that contain information about the joystick,
mouse or potentiometer.These addresses have been gone over in part
previously.
Address R/W Meaning
------------------------------------------------------------------
DFF00A R Joystick/Mouse Port 1
DFF00C R Joystick/Mouse Port 2
DFF012 R Potentiometer pair 1 Counter
DFF014 R Potentiometer pair 2 Counter
DFF018 R Potentiometer connection
DFF034 W Potentiometer port direction