Amiga Machine Language (Chapter 5)
Innen: amigaspirit.hu - pegasos.hu Wiki
Ugrás a navigációhozUgrás a kereséshez
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