Amiga Machine Language (Chapter 5)

A amigaspirit.hu - pegasos.hu Wiki wikiből

     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
Személyes eszközök