scene.org File Archive

File download

<root>­/­mirrors­/­amigascne­/­Groups­/­Sourcecodes/AmigaMachineLang2.txt

File size:
147 985 bytes (144.52K)
File date:
2010-04-13 23:05:20
Download count:
all-time: 604

Preview

              _____          _________________________________
             /    /\        /                                 \
            /    / /       /    ___________________________    \
           /    / /       /    / _________________________/\    \
          /    / /       /    /_/___________      _____   \ \    \
         /    / /       /                   \    /\    \   \ \    \
        /    / /       /_________________    \   \ \    \   \ \    \
       /    / /        \________________/\    \   \ \    \   \ \    \
      /    /_/__________________________\_\    \   \ \    \___\/     \
     /                                          \   \ \              / 
    /____________________________________________\   \ \____________/
    \____________________________________________/    \/___________/FISH

                                P R E S E N T

                 AMIGA MACHINE LANGUAGE - FROM ABACUS BOOKS

                                P A R T  I I


                     Typed by DEE JAY of X-CELL for LSD




      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
         



      Chapter 6
      ---------
      6.The Operating System.
      -----------------------
        Now lets take a step forward in your ability to write assembly
        language programs.Its not enough to put a piece of text in memory 
        someplace.You want to be able to put it on the screen.Do you know
        how to write a character on the screen?Do you know how to draw a
        window on the screen that can be modified by the mouse?Actually
        you don't have to have terribly precise knowledge about such
        topics.
        Fortunately,the Amigas operating system supplies routines that
        take care of common tasks like this.It can seem quite complicated
        due to the number of routines necessary.These routines are in
        libraries.We'll look at the libraries in some depth now.

      6.1.Load Libraries.
      -------------------
        Before you can use a library,it must be available.It has to be
        loaded into memory.Unfortunately,the whole library must be loaded,
        even if you only need one of the functions.
        First you need to decide what the program must be able to do,so
        you can see which libraries you'll need.For simple I/O text,you
        don't need a library that contains routines for moving graphics!
        There are a number of libraries onj a normal Workbench disk.Heres
        an overview of the names and the sort of functions they do:
   
      Exec.Library;
        This library is needed to load the other libraries.It is already
        in memory and doesn't need to be loaded.Its in charge of basic
        functions like reserving memory and working with I/O channels.
  
      Dos.Library;
        Contains all the functions for normal I/O operations,for instance
        screen or disk access.
  
   
      Intuition.Library;
        Used for working with screens,windows,menus,etc...
   
      Clist.Library;
        This contains routines for working with the Copper lists that are
        used for controlling the screen.
     
      Console.Library;
        Contains graphics routines for text output in console windows.
   
      Diskfont.Library;
        Used for working with the character fonts that are stored on the
        disk.
     
      Graphics.Library;
        This library contains functions to control the Blitter(or graphics
        )chip.Its used for basic graphics functions.
 
      Icon.Library;
        Used in the development and use of workbench symbols(icons).

      Layers.Library;
        Used for working with screen memory (layers).

      Mathffp.Library;
        Contains basic math floating point operations.

      Mathieeedoubbas.Library;
        Contains basic math functions for integers.

      Mathtrans.Library;
        Contains higher level mathmatical functions.

      Potgo.Library;
        Used for evaluating analog input to the Amiga.

      Timer.Library;
        Contains routines for time critical programs.They can be used to
        program exact time intervals.

      Translator.Library;
        Contains the single function "Translate",that translates normal
        text written phonetically for the narrator,the speech synthesisor.

        You can open(load)all these libraries of course.You should
        remember that this takes time and memory.For this reason,you
        should always think about which functions you need and which
        libraries they are in.
        For example,lets say you want to write a program that does text
        input/output.You need the "Dos.Library",so it can be loaded.
        The "exec.library"is in charge of loading.This library contains
        the OpenLib function that can be called once you've passed the
        needed parameters.AssemPro Amiga includes all the libraries
        necessary for the Amiga,it also includes files that contain the 
        offsets for the operating system calls.The macros contained in
        AssemPro ease assembly language programming considerably.To make
        the programs in this book useful to the largest audience the
        following examples are written for generic assemblers and do not
        include AssemPro's macros.We have used the AssemPro ILABEL and the
        macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the
        programs from the desktop.(If you are using a different assembler
        check your documentation for instructions on linking programs).

      6.2.Calling Functions.
      ----------------------
        Since this chapter is rather complex we'll first describe the
        fundamental routines necessary to use the Amiga's operating system
        after a description a complete program is listed.Every library
        begins in memory with a number of JMP commands.These JMPs branch
        to the routines that are in the library.To call a function,you
        need to find the beginning of this JMP table and call function x
        by going to the xth JMP command.Usually you use an offset to get
        to the right JMP command.Normally,you don't start at the beginning
        but at the end of the JMP table,so use negative offsets.
        It works out very easily.Now lets open the "dos.library"by using
        "exec.library's"base address.This address is $000004.To call a
        fuction from another library,you need to use another base address.
        Now you need the offset for the function that you want.You want
        the OpenLib function that has -408 as an offset.You'll find a list
        of function offsets in the appendix.
        You need a pointer to the name of the library you are loading for
        the OpenLib function(in this case "dos.library")and a long word in
        memory that you can use to store the base address of the DOS
        library.You get this back from the OpenLib function.You need to be
        sure to write the library name in lower case letters(dos.library),
        otherwise you can't open it.I entered a name in capitol letters
        once and spent a lot of time finding this error.

        The routine looks like this:

        ;** Load the DOS library 'dos.library' (6.2A) **
        Execbase = 4                    ;base address of the EXEC library
        OpenLib  = -408                 ;offset for the OpenLib function

        IoErr    = -132                 ;offset for IoErr information

        init:
               move.l  Execbase,a6      ;base address in A6
               lea     dosname,a1       ;address of library name
               moveq   #0,d0            ;version number
               jsr     OpenLib(a6)      ;open DOS library
               move.l  d0,dosbase       ;save DOS base address
               beq     error            ;if zero,then error!
               ...                      ;your program goes here
               ...                      ;more program...


        error:                          ;error
               move.l  dosbase,a6       ;address of library name
               jsr     IoErr(a6)        ;call IoErr for error info
               move.l  d0,d5            
               ...                      ;your error routine goes here
               rts


        dosname:                        ;name of library to open
               dc.b    'dos.library',0,0
               align                    ;SEKA uses-even

        dosbase:                        ;storage for DOS base address
               blk.l   1

               end

        This is the way to load the DOS library so that you can use it.All
        library functions are called this way.Parameters are put in
        registers and passed to the function.When there is an error,when
        the function doesn't run correctly,a zero is usually put in data
        register D0.
        Once your program is done with its work,you need to close the
        libraries that are still open before you return to the CLI or 
        Workbench.The CloseLib function (offset -414)takes care of this
        job.This function is in the EXEC library just like the OpenLib.The
        only parameter it needs is the base address of the library that is
        closed.To close "dos.library",do the following:

        CloseLib = -414               ; (6.2B)
               ...
               move.l  Execbase,a6    ;EXEC base address
               move.l  dosbase,a1     ;DOS base address
               jsr     CloseLib(a6)   ;close library

      6.3.Program Initialization.
      ---------------------------
        Before you can start a program,you need to initialize many things
        so that the program can run.
        Lets take an example program that does some text editing.A program
        like this must be able to store text,so it needs to be able to
        access memory.It also needs to be able to accept keyboard input
        and do screen output,so it needs an output window.
        To do this,you need to open one or more of the libraries that we
        talked about earlier.Lets assume that you've loaded the DOS
        library,so that you can do the next steps.

      6.3.1.Reserve Memory.
      ---------------------
        There are several ways to get the operating system to assign you a
        chunk of memory.You need to use one of them,so that during multi-
        tasking,you don't have one program overwriting another programs
        memory area.
        Lets look at the function that is normally used.This function is
        in the resident EXEC library and has the name AllocMem (offset
        -$c6).It reserves a memory area,using the value in D0 as the
        length.The address that the memory area begins at is returned in
        the D0 data register.If it returns zero,the program could'nt give
        you that much memory.
        You can also use a mode word in D1 to determine whether the memory
        area that is reserved should be erased or not.
        The routine looks like this:

        ExecBase = 4                  ; (6.3.1A)
        AllocMem = -$c6
               ...
               move.l  #number,d0     ;number of bytes to reserve
               move    #mode,a6       ;mode word
               move.l  ExecBase,a6    ;DOS base address in A6
               jsr     AllocMem(a6)   ;call function
               move.l  d0,address     ;save memory's start address
               beq     error          ;memory not reserved
               ...

        The second way to reserve memory is to use the AllocAbs function
        (offset -$CC).This function in contrast to the AllocMem function
        reserves a particular memory area.The D0 register contains the
        number of bytes that should be reserved.Address register A1
        contains the desired start address.This function returns a zero in
        D0 if the memory area can't be reserved.

        ExecBase = 4                  ; (6.3.1B)
        AllocAbs = -$cc
               ...
               move.l  #number,d0     ;number of bytes to reserve
               lea     address,a1     ;desired start address
               move.l  execbase,a6    ;EXEC base address
               jsr     AllocAbs(a6)   ;reserve memory
               tst.l   d0             ;everything ok?
               beq     error          ;no!
               ...

        When the program has done its work and must return to the CLI or
        the Workbench,it needs to return the memory it as reserved to the
        system.The FreeMem function (offset -$D2) handles this.
        The function works like AllocAbs in that the number of bytes is
        put in D0 and the start address of the memory area is put in A1.
        If you try to free up a memory area that was'nt reserved,you'll
        usually crash the computor.
        The routine to free up a memory area looks like this:

        ExexBase = 4                  ; (6.3.1C)
        FreeMem = -$d2
               ...
               move.l  #number,d0     ;number of bytes released
               lea     address,a1     ;start address from AllocAbs
               move.l  ExecBase,a6    ;ExecBase address
               jsr     FreeMem(a6)    ;free up memory
               tst.l   d0             ;everything ok?
               beq     error          ;no!
               ...

      6.3.2.Opening a Simple Window.
      ------------------------------
        The title of this chapter may sound a bit strange.However,the
        differences between the two different methods of opening a window
        are so great that they should be handled in seperate chapters.
        The method of opening a window presented here is very simple,but
        it doesn't allow you to work with all the gadgets.These gadgets
        include the close symbol in the upper left corner of a window and
        the size symbol in the lower left corner.
        If you open the window in the simple manner,almost all the gadgets
        are present.However,the close symbol is not.As a result,this
        method isn't appropriate for every application.Now lets look at
        the method.
        To open a window,use a function from the DOS library,so you need
        to open the library first (see the section "Load Library").This
        open function is an all purpose function that can be used for many
        things.For this reason,it makes good sense to put a "open"
        subroutine in your program.You can use it a lot.Lets do the basic
        steps:

        ;** Load the DOS Library 'dos.library'  (6.3.2A) **
        ExecBase = 4                   ;base addres of the EXEC library
        OpenLib = -408                 ;offset of OpenLib function
        Open = -30                     ;Offset of the DOS function OPEN

        init:
               move.l  ExecBase,a6     ;base address in A6
               lea     dosname(pc),a1  ;address of library name
               move.q  #0,d0           ;version number:unimportant
               jsr     OpenLib(a6)     ;call the function
               move.l  d0,dosbase      ;save DOS base address
               beq     error           ;if zero,then error!
               ...                     ;more of your program
               ...                     ;now open window,etc...


        error:
               ...                     ;error occured
               ...                     ;your error routine


        openfile:                      ;general open function
               move.l  dosbase,a6      ;DOS base address in A6
               jsr     Open(a6)        ;call OPEN function
               tst.l   d0              ;test if ok
               rts                     ;done,evaluate test later

        dosname:                       ;name of library to be opened
               dc.b    'dos.library',0,0
               align                   ;even

        dosbase:                       ;spot for DOS base address
               blk.l   1

        You call the Openfile routine,because the label "Open"is already
        being used for the offset.This routine calls the Open function
        that is in the DOS library.
        This isn't everything.The function must be given some parameters
        so that it knows what to open.The parameters are sent in registers
        D1 and D2.D1 points to a definition block what specifies what
        should be opened.You need to have a filename ended with a null
        byte there.D1 must be passed as a long word like all addresses.D2
        contains the mode that the function should run in.There is an old
        (1005) and a new (1006) mode.This number must be passed in D2's
        long word.
        Heres an overview of how windows are opened.Fortunately,AmigaDos
        allows you to use input and output channels in the same way.The
        standard channels are disk files,the console (keyboard and screen)
        the printer interface and the serial RS232 interface.
        The console input/output is what you'll work with now.When you
        specify the console as the filename of the channel to be opened,a
        window is opened automatically.
        The name must begin with CON:to do this.Its similar to DF0:for
        disk operations.A little more infotmation about the window is
        still needed.
        You need to specify the X and Y coordinates of the upper left and
        lower right corners of the window as well as the name that should
        appear in the title line of the window.A complete definition block
        for a window like this would appear like the following line:

              consolname: dc.b 'CON:0/100/640/100/**Window**',0

        To open this window,the line above needs to be inserted in the
        following program:

        mode_old = 1005

        lea     consolname(pc),a1    ;consol definition
        move.l  #mode_old,d0         ;mode
        bsr     openfile             ;console open
        beq     error                ;didn't work
        move.l  d0,conhandle

        rts
                ...
        conhandle:   dc.l 1          ;space for handle

        There are two points to clear up yet.
        You should use mode_old as the the mode when you open a window.
        Logically the window doesn't exist before opening so this seems
        wierd but it doesn't hurt anything.
        The parameter that returns from "openfile"in D0 is zero in the
        case of an error,in the case that opening didn't work.Otherwise
        the value is the identification number (handle number) of the
        opened channel.You need to store it away,because every function
        that wants to use this channel must give the handle number.In the
        example,you stored this number in the "conhandle"long word.
        As mentioned,the window you've opened doesn't have a close symbol
        but it can be made bigger and smaller and moved forward and back.
        The manipulations that are carried out using the mouse are
        completely taken care of by the Amiga (in contrast to the ATARI ST
        where the programmer has to take care of these things).
        An important function that uses the handle number is the one that
        closes the channel (in your case the window).This function is also
        in the DOS library and is called "Close".Its offset is -36 and it
        only needs one parameter;the handle number of the channel that is
        closed must be in the D1 register.
        After your work is done,you need to put the following lines in
        your program to close the window:

        Close = -36                    ; (6.3.2C)
                ...
                move.l  conhandle,d1   ;handle number in D1
                move.l  dosbase,a6     ;DOS base address in A6
                jsr     Close(a6)      ;close channel!

        The window disappears!

        Now for a few remarks about opening and closing the window in this
        way.If you open several windows in the same way,you'll get several
        windows and thus several handle numbers.In this way,you can put as
        many windows on the screen as you like.You can do your work with
        them and close them individually.
        Here is the complete program to open and close a simple window in
        AssemPro format (We have used the AssemPro ILABEL and the macros
        INIT_AMIGA and EXIT_AMIGA so AssemPro owners can start the program
        from desktop.If you are using a different assembler check your
        documentation for instructions on starting and exiting programs):

        ;***** 6.3.2 S.D *****

        OpenLib        =-30-378
        closelib       =-414
        ;execbase      =4                 ;defined in AssemPro macros


        *calls to Amiga DOS:

        open           =-30
        close          =-30-6
        IoErr          =-132
        mode_old       = 1005
        alloc_abs      =-$cc

        ILABEL AssemPro:includes/Amiga.l  ;AssemPro only

        INIT_AMIGA                        ;AssemPro only

        run:
               bsr     init               ;initialization
               bra     test               ;system-test

        init:                             ;system initialization and open
               move.l  execbase,a6        ;number of execute-library
               lea     dosname(pc),a1     
               moveq   #0,d0
               jsr     openlib(a6)        ;open DOS-Library
               move.l  d0,dosbase
               beq     error

               lea     consolname(pc),a1  ;consol definition
               move.l  #mode_old,d0
               bsr     openfile           ;consol open
               beq     error
               move.l  d0,conhandle

               rts

        test:

              bra qu                      ;quit and exit


        error:
              move.l  dosbase,a6
              jsr     IoErr(a6)
              move.l  d0,d5

              move.l  #-1,d7              ;flag

        qu:
              move.l  conhandle,d1        ;window close
              move.l  dosbase,a6
              jsr     close(a6)
              move.l  dosbase,a1          ;DOS.Lib close
              move.l  execbase,a6
              jsr     closelib(a6)

              EXIT_AMIGA                  ;AssemPro only

        openfile:                         ;open file
              move.l  a1,d1               ;pointer to I/O-Definition-Text
              move.l  d0,d2
              move.l  dosbase,a6
              jsr     open(a6)
              tst.l   d0
              rts

        dosname: dc.b 'dos.library',0,0
              Align.w
        
        dosbase: dc.l 0

        consolname: dc.b 'CON:0/100/640/100/**CLI-Test**',0
              Align.w

        conhandle: dc.l 0


              end

        There is another way to open a window easily.Just use RAW:instead
        of CON:as the channel designator.All the other parameters and
        operations remain the same.
        If you try them both out,you won't see any differences between the
        two windows.They both look the same and can be worked with in the 
        same way with the mouse.The difference comes when you input to the
        window.In the RAW:window,the cursor keys are ignored.In the CON:
        window and in CLI,they do work.

      6.4.Input/Output.
      -----------------
        Besides managing and making calculations with data,the most
        important work of a program is to input and output the data.There
        are many methods of data transfer in and out of the computor,for
        instance screen or printer output,keyboard input,using the serial
        or the parallel interface,tone or speech output and finally disk
        operations.
        You want to learn about all these methods of data input and output
        for programming and applications.We've written some programs as
        subroutines that should be useful for later programs.It makes good
        sense to make a library of these subroutines that can either be
        directly integrated in a new program or linked to a program.At the
        end of the sections there is a list of a complete program so you
        can see how the subroutines are used.
        To prepare for input/output,you need to have data to output and
        space to input data.To get this ready,you need a correct program
        beginning in which the EXEC and DOS libraries are opened and
        memory is reserved.After this,you begin most programs by outputing
        some text.The text can be a program title or the instruction to
        input data over the keyboard.Lets start looking at screen output.

      6.4.1.Screen Output.
      --------------------
        For a computor like the Amiga the first question is where should
        the screen output be sent?The answer is simple for many computors;
        they only have one screen,and output goes there.You need to
        specify which window to write to when you use the Amiga,however.

        There are two possibilites:

        1.Output to CLI

        2.Output to another window

        The first posibillity only exists if the program that makes the
        output was started from CLI.If not,you need to open your own
        custom window for your program.If so,you can use the window that
        was opened by the CLI for output.
        If you use the second method,you need to open a window.As you've
        already seen,there are three methods.For simple text and character
        output,the difference between the three sorts of windows isn't
        very great.Here you have a free hand in determining which sort of
        window to use.Lets open a CON:window and put its handle number in
        "conhandle".
        You've opened your window and want to output a title.You choose
        text to output and then put it in memory using a code segment like
        this:

              title: dc.b "** Welcome to this Program! **"
              titleend:
              align              ;even

        The "align"(even) is a pseudo-op that should follow text when it
        is followed by either word data or program lines.It causes the
        assembler to insert a null byte if necessary to make the address
        even.
        To output this text you need another DOS function:Write.This has
        an offset of -48 and needs three parameters:

        In D1   the handle of an opened output channel that should be
                written to (in your case,this is the handle number that
                you go back from the Open command when you opened your
                window.).
        In D2   the address of the text to be output (in the example,the
                address "title").
        In D3   the number of characters to be output in bytes.

        To find the number of bytes to output,you need to count the number
        of characters in your text.Use "titleend"to calculate this.Using
        this label,the assembler can calculate the length of your text for
        itself (after all,why should you count when you have a computor?) 
        if you write:

             move.l  #titleend-title,d3

        The advantage of specifying the length is that you can put control
        characters between the beginning and end of the text.In this way,
        you can execute certain functions using text output.You'll learn
        about the control characters in a bit.

        Heres the routine:

        Write = -48                          ; (6.4.1A)
                ...                          ;open window
                ...
                move.l  dosbase,a6           ;DOS base address
                move.l  conhandle,d1         ;pass handle
                move.l  #title,d2            ;text address
                move.l  #titleend-title,d3   ;and length
                jsr     Write(a6)            ;call function
                ...


        title: dc.b "** Welcome to this Program! **"
         
        titleend:
        
        align                                ;event

                end

        You'll certainly use this function a lot.You'll often want to
        output just one character though.To allow you to do this and
        similar text related tasks,there are four subroutines,each of
        which do a different sort of output:

      Pmsg;
        Outputs the text from (D2) to the first null byte.

      Pline;
        Is the same as the routine above except that the text is
        automatically followed by a CR,the cursor is positioned at the
        beginning of the next line.

      Pchar;
        Outputs the character in D0

      Pcrlf;
        Puts the cursor at the beginning of the next line.

        Heres the subroutine package:

        Write = -48                  ; (6.4.1B)
                ...

        pline:                       ;*output line and then a CR
                bsr     pmsg         ;output line

        pcrlf:
                move    #10,d0       ;line feed
                bsr     pchar        ;output
                move    #13,d0       ;and CR

        pchar:
                move.b  d0,outline   ;character in output buffer
                move.l  #outline,d2  ;address of the character
                 
        pmsg:                        ;*output line (D2) upto null
                move.l  d2,a0        ;address in A0
                clr     d3           ;length = 0

        ploop:
                tst.b   (a0)+        ;null byte ?
                beq     pmsg2        ;yes:length found
                addq.l  #1,d3        ;else length + 1
                bra     ploop        ;and continue looking

        pmsg2:
                move.l  dosbase,a6   ;DOS base address in A6
                move.l  conhandle,d1 ;our window handle
                jsr     Write(a6)    ;call write function
                rts                  ;done!

        outline:        dc.w 0       ;output buffer for 'pchar'

        conhandle:      dc.l 0       ;windows handle

        Here is an example program to open and close a simple window and
        output a text message in AssemPro format (We have used the
        AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can
        start the program from desktop.If you are using a different
        assembler check your documentation for instructions on starting
        and exiting programs.):

        Here is the complete program in AssemPro format:

        ;***** 6.4.1C.asm S.D. *****

        Openlib     =-30-378
        closelib    =-414
        ;execbase   = 4                    ;Defined in AssemPro
                                           ;Macros

        * calls to Amiga Dos:

        open        =-30
        close       =-30-6
        write       =-48
        IoErr       =-132
        mode_old    = 1005
        alloc_abs   =-$cc

               ILABEL AssemPro:include/Amiga.l ;AssemPro only

               INIT_AMIGA                  ;AssemPro only

        run:
               bsr     init                ;initialization
               bsr     test                ;system test
               nop 
               bra qu                      ;quit and exit

        test:
               move.l  #title,d0
               bsr     pmsg
               bsr     pcrlf
               bsr     pcrlf

               rts

        init:                              ;system initialization and
                                           ;open
               move.l  execbase,a6         ;number of execute-library
               lea     dosname(pc),a1
               moveq   #0,d0
               jsr     openlib(a6)         ;open DOS-library
               move.l  d0,dosname
               beq     error

               lea     consolname(pc),a1   ;console definition
               move.l  #mode_old,d0
               bsr     openfile            ;console open
               beq     error
               move.l  d0,conhandle

               rts

        pmsg:                              ;print message (D0)
               movem.l d0-d7/a0-a6,-(sp)
               move.l  d0,a0
               move.l  a0,d2
               clr.l   d3

        ploop:
               tst.b   (a0)+
               beq     pmsg2
               addq.l  #1,d3
               bra     ploop               ;length calculate

        pmsg2:
               move.l  conhandle,d1
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6
               rts

        pcrlf:
               move    #10,d0
               bsr     pchar
               move    #13,d0

        pchar:                             ;output char in D0
               movem.l d0-d7/a0-a6,-(sp)   ;save all
               move.l  conhandle,d1

        pch1:
               lea     outline,a1
               move.b  d0,(a1)
               move.l  a1,d2
               move.l  #1,d3               ;1 letter
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6   ;restore all

        error:
               move.l  dosbase,a6
               jsr     IoErr(a6)
               move.l  d0,d5

               move.l  #-1,d7              ;flag

        qu:
               move.l  conhandle,d1        ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1          ;DOS.Lib close
               move.l  execbase,a6
               jsr     closelib(a6)

               EXIT_AMIGA                  ;AssemPro only

        openfile:                          ;open file
               move.l  a1,d1               ;pointer to I/O-definition-
                                           ;text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        dosname: dc.b 'dos.library',0,0
               align.w

        dosbase: dc.l 0

        consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0
               align.w

        conhandle: dc.l 0

        title: dc.b '** Welcome to this Program! **'

        titleend:
               align

        outline: dc.w 0                    ;output buffer for char

               end

        Using this program,you can very easily put whatever you want in
        the CON:window.These functions also work in RAW:window.You should
        rename "conhandle"as "rawhandle",so that you don't get things
        mixed up later.
        Lets stay with the CON:window.As mentioned earlier,you can output
        special characters that execute functions or change parameters for
        output.These characters are called control characters.
        You've already learned about one of these control characters,Line
        Feed ($A).This character isn't just output;instead,it calls a
        function that moves the cursor into the next line and moves the
        screen up.This is very useful,but there are much more interesting
        control characters.
        Here's a list of control characters that execute functions.These
        characters are given in hex.

      Control Sequence;

        Sequence     Function
        ------------------------------------------------------------------
         08          Backspace
         0A          Line Feed,Cursor down
         0B          Move Cursor up a line
         0C          Clear screen
         0D          Carrige return,cursor in the first column
         0E          Turn on normal characters (Cancel Of Effects)
         0F          Turn on special characters
         1B          Escape

        The following sequences begin with $9B,the CSI (Control Sequence
        Introducer).The characters that follow execute a function.The
        values in square brackets can be left off.The n's you see
        represent one or more digit decimal numbers given using ASCII
        characters.The value that is used when n is left off,is given in
        the parenthesis that follow n in the description of the function
        in the table.

      Control Sequence Introducer;

        Sequence       Function
        ------------------------------------------------------------------
        9B[n]40        Insert n blanks
        9B[n]41        Move cursor n (1) lines up
        9B[n]42        Move cursor n (1) lines down
        9B[n]43        Move cursor n (1) characters to the right
        9B[n]44        Move cursor n (1) characters to the left
        9B[n]45        Move cursor down n (1) lines into column 1
        9B[n]46        Move cursor up n (1) lines and into column 1
        9B[n][3B n]48  Cursor in line;Set column
        9B 4A          Erase screen from cursor
        9B 4B          Erase line from the cursor
        9B 4C          Insert line
        9B 4D          Delete line
        9B[n]50        Delete n characters starting at cursor
        9B[n]53        Move up n lines
        9B[n]54        Move down n lines
        9B 32 30 68    Line feed => Line feed + return
        9B 32 30 6C    Line feed => just Line feed
        9B 6E          Sends the cursor position!A string of the following
                       form is returned:
                       9B (line) 3B (column) 52
        9B(style);(foreground colour);(Background Colour)6D
                       The three parameters are decimal numbers in ASCII
                       format.They mean:
                       Style:  0 = normal
                               1 = bold
                               3 = italic
                               4 = underline
                               7 = inverse
                       Foreground colour: 30-37
                       Colour 0-7 for Text
                       Background colour: 40-47
                       Colour 0-7 for background
        9B(length)74   sets the maximum number of lines to be displayed
        9B(width)75    sets the maximum line length
        9B(distance)78 defines the distance in pixels from the left border
                       of the window to the place where output should
                       begin
        9B(distance)79 defines the distance in pixels from the upper
                       border of the window to the place where output
                       should begin
                       The last four functions yield the normal values if
                       you leave off the parameters.

        9B 30 20 70    Make cursor invisible
        9B 20 70       Make cursor visible
        9B 71          Sends window construction.A string of the following
                       form is returned:
                       9B 31 3B 31 3B (lines) 3B (columns) 73

        To see how the control characters work,have "pmsg"output this text
        to your window:

        mytext: dc.b $9b,"4;31;40m"                ; (6.3.2D)
                dc.b "underline"
                dc.b $9b,"3;33;40m",$9b,"5;20H"
                dc.b "** Hello World! **",0

        The parameters for the control sequence are put in quotation marks
        so they are treated as an ASCII string.Now you see,just how easy
        it is to do text output!
        Here is the complete program to open and output the text and
        control codes to your window in AssemPro format (We have used the
        AssemPro macros INIT_AMIGA and EXIT_AMIGA so AssemPro owners can
        start the programs from desktop.If you are using a different
        assembler check your documentation for instructions on starting
        and exiting programs):

        ; ***** 6.4.1D.ASM S.D. *****


        openlib    =-30-378
        closelib   =-414
        ;execbase  = 4                     ;defined in AssemPro macros

        * calls to Amiga Dos:

        open       =-30
        close      =-30-6
        write      =-48
        IoErr      =-132
        mode_old   = 1005
        alloc_abs  =-$cc

               ILABEL AssemPro:includes/Amiga.l   ;AssemPro only

               INIT_AMIGA                  ;AssemPro only

        run:
               bsr     init                ;initialization
               bsr     test                ;system test
               nop
               bra qu                      ;quit and exit

        test:
               move.l  #mytext,d0
               bsr     pmsg
               bsr     pcrlf
               bsr     pcrlf

               rts

        init:                              ;system initialization and open
               move.l  execbase,a6         ;number of execute-library
               lea     dosname(pc),a1
               moveq   #0,d0
               jsr     openlib(a6)         ;open DOS-Library
               move.l  d0,dosbase
               beq     error

               lea     consolname(pc),a1   ;console definition
               move.l  #mode_old,d0
               bsr     openfile            ;console open
               beq     error
               move.l  d0,conhandle

               rts

        pmsg:                              ;print message (D0)
               movem.l d0-d7/a0-a6,-(sp)
               move.l  d0,a0
               move.l  a0,d2
               clr.l   d3

        ploop: 
               tst.b   (a0)+
               beq     pmsg2
               addq.l  #1,d3
               bra     ploop

        pmsg2:
               move.l  conhandle,d1
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6
               rts

        pcrlf:
               move    #10,d0
               bsr     pchar
               move    #13,d0

        pchar:                             ;output char in D0
               movem.l d0-d7/a0-a6,-(sp)   ;save all
               move.l  conhandle,d1

        pch1:
               lea     outline,a1
               move.b  d0,(a1)
               move.l  a1,d2
               move.l  #1,d3               ;one letter
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6   ;restore all
               rts

        error:
               move.l  dosbase,a6
               jsr     IoErr(a6)
               move.l  d0,d5

               move.l  #-1,d7              ;flag

        qu:
               move.l  conhandle,d1        ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1          ;DOS.Lib close
               move.l  execbase,a6
               jsr     closelib(a6)

               EXIT_AMIGA                  ;AssemPro only

        openfile:                          ;open file
               move.l  a1,d1               ;pointer to I/O-definition-
                                           ;text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        dosname: dc.b 'dos.library',0,0
               align.w

        dosbase: dc.l 0

        consolname: dc.b 'CON:0/100/640/100/ ** CLI-Test **',0
               align.w

        conhandle: dc.l 0

        mytext:
               dc.b $9b,'4;31;40m'
               dc.b 'underline'
               dc.b $9b,'3;33;40m',$9b,'5;20H'
               dc.b '** Hello World !! **',0

               align

        outline: dc.w 0                    ;output buffer for pchar

               end

        Now that you've done text and character output,its time to move on
        to text input.

      6.4.2.Keyboard Input.
      ---------------------
        You can read keyboard input very easily.You just need to open the
        I/O channel of the CON:window and read from it.You need the read 
        function from the DOS library to do this.Its offset is -42.
        The function has three parameters just like the WRITE function.

        In D1   the handle number that you get from the WRITE function.
        In D2   the address that the data read in is to start.
        In D3   the number of bytes to read.

        Here is a subroutine that reads the number of characters from the
        keyboard that it finds in D3.It puts them in a buffer.

        read    = -42                 ; (6.4.2A)
               ...

        getchr:                       ;* Get (D3) characters from the
                                      ;keyboard
               move.l  #inbuff,d2     ;address of buffer in D2
               move.l  dosbase,a6     ;DOS base address in A6
               move.l  conhandle,d1   ;our window handle
               jsr     read(a6)       ;call read function
               rts                    ;done!

        inbuff:        blk.b 80,0     ;buffer for keyboard input

        This routine returns to the main program when <Return> is entered.
        If more than D3 characters are entered,"inbuff"only gets the first
        characters.The routine gets the remaining characters when called a
        second time.
        This sort of input is fairly easy.You can backspace,because only
        the characters that should be there are put in the memory block
        starting at "inbuff".The number of characters moved into "inbuff"
        is put in D0.
         
        Try the program out as follows:

        After opening the CON:window,put the following lines in the main
        program:

               move   #80,d3          ;read 80 characters (6.4.2B)
               bsr    readchr         ;get line from keyboard
               lea    inline,a0       ;address of line in A0
               clr.b  0(a0,d0)        ;null byte on the end
               bsr    pmsg            ;output line again
        
        bp:

        After this comes the code segment that closes the window again.
        After loading the program into the AssemPro debugger,make "bp"a
        breakpoint and start the program.(SEKA users start the program
        with "g run"and enter "bp"as the breakpoint).The program quits at
        the breakpoint and you can take a look at the results on the
        screen.Then you can continue the program (SEKA with "j bp") and
        let the window close.
        After starting the program and opening the window,the cursor
        appears in the upper left corner of the window.Enter some text and
        press <Return>.The string that you just entered is output again on
        the screen.
        You use the "pmsg"routine from the previous chapter to do this
        output.This routine needs a null byte at the end of the text to be
        output.You put a null byte there by putting the address of the
        input buffer in A0 and then erasing the byte at A0+D0 using the
        CLR.B command.Since D0 contains the number of characters that were
        entered,this byte is the first unused byte.
        Since you're in the debugger you can redisplay the disassembled
        output when the program ends to see what "getchr"put in "inbuff"
        (SEKA owners can use "q inbuff"when the program ends to see what
        "getchr"put there.)You'll find the characters that you typed plus
        a closing $A.The $A stands for the <Return> key and its counted
        too,so if you entered a 12 and then hit <Return>,for example,D0
        will contain a 3.
        Try this again with a RAW:window.Change the window definition from
        CON: to RAW:and reassemble the program.You'll notice the diference
        right away.After you've entered one character,a return is executed
        D0 always as one bit in it.
        The advantage of this form of input is that cursor and function
        keys can be recognized.Using your own routine,you can repeatedly
        accept input of characters using "getchr"and then work with the
        special characters.
        Theres another form of keyboard input:checking for a single key.
        This is important when a program is about to execute an important
        function and the user must say he wants it executed by entering
        "Y"for yes.This can be treated as normal input,but in some cases,
        there is a better method.
        There is a function in the DOS library that waits a certain
        specified length of time for a key to be pressed,and returns a
        zero (FALSE) if no key was hit in this time period.It returns a -1
        ($FFFFFFFF = TRUE) if one was.To find out which key it takes
        another function.The WaitForChar function,is only good for tasks
        like waiting for the user to let the program know that it can
        continue scrolling text.

        The function needs two parameters:

        In D1    the handle number of the window or file from which the
                 character should be read.It can also wait for a character
                 from an interface.
        In D2    you pass the length of time in microseconds that you
                 should wait for a key stroke.

        To wait one second for one key to be hit,you can use the following
        routine:

        WaitForCh=-30-174                 ; (6.4.2C)
               ...

        scankey:                          ;* Wait for a key stroke
               move.l  conhandle,d1       ;in our window
               move.l  #1000000,d2        ;waiting time 1 second
               move.l  dosbase,a6         ;DOS base address
               jsr     waitforch(a6)      ;wait...
               tst.l   d0                 ;test result
               rts

        The TST command at the end of the program allows the calling
        routine to use a BEQ or BNE command to evaluate the results of the
        routine-BEQ branches if no key was hit.BNE doesn't.
        Heres an example program in AssemPro format covering what you have
        learned so far.Opening and closing a window,displaying text in the
        window and inputting text:

        ;***** 6.4.2A.ASM S.D *****

        openlib    =-30-378
        closelib   =-414
        ;execbase  =4                      ;defined in AssemPro
                                           ;Macros

        * call to Amiga.Dos:

        open       =-30
        close      =-30-6
        read       =-42
        write      =-48
        IoErr      =-132
        mode_old   =1005
        alloc_abs  =-$cc

               ILABEL AssemPro:include/Amiga.l  ;AssemPro only

               INIT_AMIGA                  ;AssemPro only

        run:
               bsr     init                ;initialization
               bsr     test                ;system test
               nop                         
               bra     qu                  ;quit and exit

        test:
               move.l  #mytext,d0
               bsr     pmsg
               bsr     pcrlf
               bsr     pcrlf
               move.l  #80,d3              ;80 characters to read in (D3)
               bsr     getchr              ;get character
               bsr     pmsg                ;output line

               rts

        init:                              ;system initialization and open
               move.l  execbase,a6         ;number of execute-library
               lea     dosname(pc),a1
               moveq   #0,d0
               jsr     openlib             ;open DOS-Library
               move.l  d0,dosbase
               beq     error
  
               lea     consolname(pc),a1   ;console definition
               move.l  #mode_old,d0
               bsr     openfile            ;console open
               beq     error
               move.l  d0,conhandle

               rts

        pmsg:                              ;print message (D0)
               movem.l d0-d7/a0-a6,-(sp)
               move.l  d0,a0
               move.l  a0,d2
               clr.l   d3

        ploop:
               tst.b   (a0)+
               beq     pmsg2
               addq.l  #1,d3
               bra     ploop               ;check length

        pmsg2:
               move.l  conhandle,d1
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6
               rts

        pcrlf:
               move    #10,d0
               bsr     pchar
               move    #13,d0

        pchar:                             ;character in D0 output
               movem.l d0-d7/a0-a6,-(sp)   ;save all
               move.l  conhandle,d1

        pch1:
               lea     outline,a1
               move.b  d0,(a1)
               move.l  a1,d2
               move.l  #1,d3               ;1 letter
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6   ;restore all
               rts

        getchr:                            ;get character for keyboard
               move.l  #1,d3               ;1 character
               move.l  conhandle,d1
               lea     inbuff,a1           ;buffer address
               move.l  a1,d2
               move.l  dosbase,a6
               jsr     read(a6)
               clr.l   d0
               move.b  inbuff,d0
               rts

        error:
               move.l  dosbase,a6
               jsr     IoErr(a6)
               move.l  d0,d5

               move.l  #-1,d7              ;flag

        qu:
               move.l  conhandle,d1        ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1          ;DOS.Lib close
               move.l  execbase,a6 jsr     ;close lib (A6)

               EXIT_AMIGA                  ;AssemPro only


        openfile:                          ;open file
               move.l  a1,d1               ;pointer to I/O-Definition-
                                           ;Text
               move.l  d0,d2               
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        dosname: dc.b 'dos.library',0,0
               align.w

        dosbase: dc.l 0

        consolname: dc.b 'CON:0/100/640/100/** CLI-TEST **',0
               align.w

        conhandle: dc.l 0

        mytext: dc.b '** Hello World !! **',0

               align

        outline: dc.w 0                     ;output buffer for pchar 
    
        inbuff: blk.b 8                     ;input buffer

               end


      6.4.3.Printer Control.
      ----------------------
        Now that you've looked at console I/O,lets look at outputting data
        from the computor.The first device that we'll discuss is the
        printer.
        Its very easy to use the printer.You just need to open another
        channel.It goes just the way you learned it with CON: and RAW:
        windows;the only difference is you enter PRT:instead.
        You open this channel using the same lines that you used above for
        the window except that the pointer is to the channel name PRT:in
        D1.You pass the mode "new"(1006) in D2 in the "do_open"routine as
        well.Save the handle number that comes back at a label called
        "prthandle".
        Now you can use the same output routines that you used with the
        windows to send text to the printer.You need to put "prthandle"
        instead of "conhandle"in the line with the "move.l conhandle,d1"
        command.
        Actually it would be better to eliminate this line from the
        routine totally.Then you can use the same routine for window and
        printer output.The calling procedure would then need to put
        "conhandle"in D1 for window output.It would put "prthandle" in D1
        for printer output.This is a very flexible output routine that can
        be used for window and printer output now.You can't accept input
        from the printer,because the printer doesn't send data.It just
        accepts it and prints it.

      6.4.4.Serial I/O.
      -----------------
        Its just as easy to use the serial interface as the printer.Just
        enter SER:as the filename.Now you can use the DOS functions READ
        and WRITE just as before to do I/O channels you've just opened.
        You can set the parameters for the interface (like Hand shake and
        Transfer rate) with the Preferences program. 

      6.4.5.Speech Output.
      --------------------
        The Amiga has a speech synthesizer built in.This isn't quite as
        easy to program as the I/O devices discussed earlier,however.You
        use the "narrator.device"to do this.
        This device requires several program steps to install it and then
        causes it to speak.You need to open the device,start the I/O,etc..
        Lets look at how to translate the text into the proper form and
        then output the text.
        First we need to do some initialization.Lets define the constants
        now.Some of them are new.

        ;***** Narrator Basic Functions 3/87 S.D ***** (6.4.5A)

        openlib     =-408                   
        closelib    =-414
        execbase    = 4

        open        =-30                    ;open file
        close       =-36                    ;close file
        mode_old    = 1005                  ;old mode
        
        opendevice  =-444                   ;open device
        closedev    =-450                   ;close device

        sendIo      =-462                   ;start I/O
        abortIO     =-480                   ;abort I/O

        translate   =-30                    ;translate text

        ;The initialization routine follows:
               
        init:                               ;initialize and open system

        ;* open DOS library * 

               move.l  execbase,a6          ;pointer to execbase
               lea     dosname,a1           ;pointer to DOS name
               moveq   #0,d0                ;version unimportant
               jsr     openlib(a6)          ;open DOS library
               move.l  d0,dosbase           ;save handle
               beq     error                ;error handle

        ;* Open translator.library *

               lea     transname,a1         ;pointer to translator name
               clr.l   d0 
               jsr     openlib(a6)          ;open translator
               move.l  d0,transbase         ;save handle
               beq     error                ;error handling

        ;* Set up I/O area for Narrator *

               lea     talkio,a1            ;pointer to I/O area in A1
               move.l  #nwrrep,14(a1)       ;enter port address
               move.l  #amaps,48+8(a1)      ;pointer to audio mask
               move    #4,48+12(a1)         ;number of the mask
               move.l  #512,36(a1)          ;length of the output area
               move    #3,28(a1)            ;command:write
               move.l  #outtext,40(a1)      ;address of output area

        ;* Open Narrator device *

               clr.l   d0                   ;number 0
               clr.l   d1                   ;no flags
               lea     nardevice,a0         ;pointer to device name
               jsr     opendevice(a6)       ;open narrator.device
               tst.l   d0                   :error?
               bne     error                ;Yes!

        ;* Open Window *

               move.l  #consolname,d1       ;console definition
               move.l  #mode_old,d2         ;old mode
               move.l  dosbase,a6           ;DOS base address
               jsr     open(a6)             ;open window
               tst.l   d0                   ;error?
               beq     error                ;Yes!
               move.l  d0,conhandle         ;else save handle

        After you've done this initialization,you can have the computor
        save the text you have prepared for it.To see what the Amiga is
        saying,use the "pmsg"function to have the text written to the
        window:


               move.l  #intext,d2           ;text for the Amiga to say
               bsr     pmsg                 ;output in window also

        sayit:                              ;have the text said

        ;*Translate the text into a form that the computor can use *

               lea     intext,a0            ;address of the text
               move.l  #outtext-intext,d0   ;length of the text
               lea     outtext,a1           ;address of output area
               move.l  #512,d1              ;length of output area
               move.l  tranbase,a6          ;translator base address
               jsr     translate(a6)        ;translate text

        ;* Speech output *

               lea     talkio,a1            ;address of I/O structure
               move.l  #512,36(a1)          ;length of output area
               move.l  execbase,a6          ;EXEC base address
               jsr     sendIO(a6)           ;start I/O (speech output)


        Once the program ends,the I/O stops as well,so you need to put in
        something that keeps the program going longer.You'll use the
        "getchr"function that you programmed earlier to take care of this:


               bsr     getchr               ;wait for keyboard input

        The computor waits until the <Return> key is pressed.Now you can
        listen to what the Amiga as to say.Once the <Return> key is
        pressed,the program stops.


        qu:                                 ; (6.4.5C)
               move.l  execbase,a6          ;EXEC base address
               lea     talkio,a1            ;pointer to I/O area
               jsr     abortio(a6)          ;stop the I/O

               move.l  conhandle,d1         
               move.l  dosbase,a6
               jsr     close(a6)            ;close window
               
               move.l  dosbase,d1
               move.l  execbase,a6
               jsr     closelib(a6)         ;close DOS library

               lea     talkio,a1
               jsr     closedev(a6)         ;close narrator.device

               move.l  tranbase,a1              
               jsr     closelib(a6)         ;close translator library

               rts                          ;* end of program


        Now comes the data that you need for the program above:


        mytext:      dc.b  'This is a test text !',10,13,10,13,0,0
        dosmame:     dc.b  'dos.library',0,0
        transname:   dc.b  "translator.library",0
        consolname:  dc.b  'RAW:0/100/640/100/** Test window',0
        nardevice    dc.b  'narrator.device',0
           align
        dosbase:     dc.l  0
        tranbase     dc.l  0
        amaps:       dc.b  3,5,10,12
           align
        conhandle:   dc.l  0
        talkio:      blk.l 20,0
        nwrrep:      blk.l 8,0
        intext:      dc.b  'hello,i am the amiga talking to you',0
           align
        outtext:     blk.b 512,0


        This is quite a bit of work,but its worth it because it opens so
        many possibilities for you.There are a lot of variations possible
        if you modify parameters.These parameters are entries in the I/O
        area starting at the "talkio"label.The area is built as follows:

        Offset        Length       Meaning
        ----------------------------------------------------------------
        ** Port Data **
          0             L          Pointer to next block 
          4             L          Pointer to last block
          8             B          I/O type
          9             B          Priority
          10            L          Pointer to I/O name
          14            L          Pointer to port
          18            W          Length
        ** I/O Data **
          20            L          Pointer to device
          24            L          Pointer to device unit
          28            W          Command word
          30            B          I/O flags
          31            B          I/O status
          32            L          I/O pointer
          36            L          I/O length
          40            L          Pointer to Data
          44            L          I/O offset
        ** Narrator data items **
          48            W          Speech speed
          50            W          Highness of voice
          52            W          Speech mode
          54            W          Sex (male/female voice)
          56            L          Pointer to audio mask
          60            W          Number of mask
          62            W          Volume
          64            W          Read in rate
          66            B          Flag for producing graphics (0=off)
          67            B          Actual mask (internal use)
          68            B          Channel used (internal use)

        We would'nt recommend experimenting with the data in the first two
        blocks.If you do,you can easily cause a system crash.You can use
        the last entries of the structure to produce some interesting
        effects though.
        Heres an overview of the parameters you can use to vary the speech
        output.The value in parenthesis is the standard value,the value
        set when narrator.device is opened.

      Speech speed (150);
        You can use this to set the speed of speech.The pitch of the voice
        is not affected by this value.

      Pitch of voice (110);
        You can choose a value between 65 and 320 for the pitch (from
        Goofy to Mickey Mouse).  

      Speech mode (0);
        The zero gives half-way naturel speech.A one lets the Amiga speak
        in monotone like a robot.

      Sex (0);
        A zero means masculine and a one means feminine (more or less..)

      Volume (64);
        The volume can range from 0 to 64.The standard value is the
        loudest possible.

      Read in rate (22200);
        By lowering this value,the voice is lowered.If you change this
        very much,you'll get some wierd voices!

        You can experiment a bit until you find a interesting voice.Have
        fun! 
        Here is a complete talking program in AssemPro format:

        ;***** Speech output S.D. *****

        openlib      =-30-378
        closelib     =-414
        ;execbase    =4                     ;defined by AssemPro

        * calls to Amiga Dos:         

        open         =-30
        close        =-30-6
        opendevice   =-444
        closedev     =-450
        addport      =-354
        remport      =-360
        ;DoIo        =-456
        sendIo       =-462
        abortIo      =-480
        read         =-30-12
        write        =-30-18
        ;myinput     =-30-24
        ;output      =-30-30
        ;currdir     =-30-96
        ;exit        =-30-114
        waitforch    =-30-174
        findtask     =-294
        translate    =-30
        mode_old     = 1005
        ;mode_new    = 1006
        ;alloc_abs   =-$cc
        ;free_mem    =-$d2

        ;!!!when>500KB !!! or place in chip memory
        ;org $40000
        ;load $40000
        ;!!!!!!!!!!!!!!!!!!!!!!!

               
               ILABEL AssemPro:includes/Amiga.l  ;AssemPro only

               INIT_AMIGA                   ;AssemPro only

        run:
               bsr     init                 ;initialization
               bra     test                 ;system-test

        init:                               ;system initialization and
                                            ;open
               move.l  execbase,a6          ;pointer to exec library
               lea     dosname(pc),a1       ;pointer to dos name
               moveq   #0,d0                ;version:not important
               jsr     openlib(a6)          ;open DOS-Library
               move.l  d0,dosbase           ;save handle
               beq     error                ;error routine

        ;*                                  ;open translator library
               move.l  execbase,a6          ;pointer to exec library
               lea     transname,a1         ;pointer to translator library
               clr.l   d0
               jsr     openlib(a6)          ;open translator
               move.l  d0,tranbase          ;save handle
               beq     error                ;error routine

        ;*                                  ;set up
               sub.l   a1,a1
               move.l  execbase,a6
               jsr     findtask(a6)         ;find task
               move.l  d0,nwrrep+2

               lea     nwrrep,a1
               jsr     addport(a6)          ;add port

        ;*                                  ;open narrator device
               lea     talkio,a1            ;pointer to I/O area in A1
               move.l  #nwrrep,14(a1)       ;enter port address
               clr.l   d0                   ;number 0
               clr.l   d1                   ;no flags
               lea     nardevice,a0         ;pointer to device name
               jsr     opendevice(a6)       ;open narrator.device
               tst.l   d0                   ;error?
               bne     error                ;Yes!

        ;*                                  ;set up I/O for narrator
                                            ;device

        bp:
               lea     talkio,a1            ;pointer to I/O in A1
               move.l  #nwrrep,14(a1)       ;enter port address
               move.l  #amaps,48+8(a1)      ;pointer to audio mask
               move    #4,48+12(a1)         ;size of mask

               lea     consolname(pc),a1    ;console-definition
               move.l  #mode_old,d0
               bsr     openfile             ;console open
               beq     error        
               move.l  d0,conhandle

               rts

        test:
               move.l  #mytext,d0           
               bsr     pmsg                 ;test-text output 
 
               bsr     sayit                ;say text

               bsr     readin               ;input
               move    #10,d0
               bsr     pchar                ;LF output
               move.l  #inline+2,d0
               bsr     pmsg                 ;and again
               bsr     pcrlf
               bra     qu

        error:
               move.l  #-1,d7               ;flag

        qu:
               move.l  execbase,a6 
               lea     talkio,a1
               jsr     abortio(a6)
        
               move.l  conhandle,d1         ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1           ;DOS.Lib close
               move.l  execbase,a6
               jsr     closelib(a6)

               lea     nwrrep,a1
               jsr     remport(a6)          ;remove port
               lea     talkio,a1
               jsr     closedev(a6)         ;close narrator device
               move.l  tranbase,a1
               jsr     closelib(a6)         ;close translator library

               EXIT_AMIGA                   ;AssemPro only

        openfile:                           ;open file
               move.l  a1,d1                ;pointer to I/O definition-
                                            ;text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        pmsg:                               ;print message (D0)
               movem.l d0-d7/a0-a6,-(sp)    
               move.l  d0,a0
               move.l  a0,d2
               clr.l   d3

        mess1:
               tst.b   (a0)+
               beq     mess2
               addq.l  #1,d3
               bra     mess1                ;length calculate

        mess2:
               move.l  conhandle,d1
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6
               rts

        pcrlf:
               move    #10,d0
               bsr     pchar
               move    #13,d0

        pchar:                              ;output characters in D0
               movem.l d0-d7/a0-a6,-(sp)    ;save all
               move.l  conhandle,d1

        pch1:
               lea     chbuff,a1
               move.b  d0,(a1)
               move.l  a1,d2
               move.l  #1,d3                ;1 letter
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6    ;restore all
               rts

        scankey:                            ;test key
               move.l  conhandle,d1
               move.l  #500,d2              ;wait value
               move.l  dosbase,a6
               jsr     waitforch(a6)
               tst.l   d0
               rts

        readin:                             ;input from keyboard
               movem.l d0-d7/a0-a6,-(sp)    ;save registers
               lea     inline+2,a2          ;pointer to input buffer
               clr.l   (a2)

        inplop:
               bsr     getchr
               cmp.b   #8,d0
               beq     backspace
               cmp.b   #127,d0              ;delete?
               beq     backspace
               bsr     pchar                ;character output
               cmp.b   #13,d0
               beq     inputx
               move.b  d0,(a2)+
               bra     inplop

        inputx:
               clr.b   (a2)+
               sub.l   #inline,a2
               move    a2,inline            ;length in lines+1
               movem.l (sp)+,d0-d7/a0-a6    ;registers
               rts

        backspace:
               cmp.l   #inline,a2           ;at the beginning?
               beq     inplop               ;yes
               move.b  #8,d0
               bsr     pchar                ;backspace
               move    #32,d0
               bsr     pchar                ;blank
               move    #8,d0
               bsr     pchar                ;backspace
               clr.b   (a2)
               subq.l  #1,a2
               bra     inplop

        getchr:                             ;get one character from
                                            ;keyboard
               move.l  #1,d3                ;one character
               move.l  conhandle,d1      
               lea     inbuff,a1            ;buffer address
               move.l  a1,d2
               move.l  dosbase,a6
               jsr     read(a6)
               clr.l   d0
               move.b  inbuff,d0
               rts

        sayit:
               lea     intext,a0
               move.l  #outtext-intext,d0
               lea     outtext,a1
               move.l  #512,d1
               move.l  tranbase,a6
               jsr     translate(a6)

        p:
               lea     talkio,a1
               move    #3,28(a1)            ;??
               move.l  #512,36(a1)
               move.l  #outtext,40(a1)
               move.l  execbase,a6
               jsr     sendio(a6)
               
               rts

        mytext:        dc.b 'This is our Test-Text !',10,13,10,13,0,0

        dosname:       dc.b 'dos.library',0,0
 
        transname:     dc.b "translator.library",0
               align.w

        dosbase:       dc.l 0
  
        tranbase:      dc.l 0

        consolname:    dc.b 'CON:0/100/640/100/* Speech-Test S.D.* ',0

        nardevice:     dc.b 'narrator.device',0

        amaps:         dc.b 3,5,10,12,0,0
               align.w

        conhandle:     dc.l 0

        inbuff:        blk.b 8

        inline:        blk.b 180,0
     
        chbuff:        blk.b 82,0

        narread:       blk.l 20,0
   
        talkio:        blk.l 20,0

        nwrrep:        blk.l 8,0

        intext:        dc.b 'hello,i am the amiga computor',0
               align.w

        outtext:       blk.l 128,0

        
        end

      6.5.Disk Operations.
      --------------------
        The most important peripheral device for a computor like the Amiga
        is the disk drive.You use it to save data,so that you don't lose
        it when you turn off the computor.We'll look at saving and
        retrieving data in this chapter.                                  
        Lets first look at the simple disk operations that are used for
        data management.To gain access to a file,you must open it first.  
        This is done using the OPEN function from the DOS library,a
        function that you're already familiar with.I'll assume in the
        following examples,that you've already opened the DOS library.

      6.5.1.Open Files.
      -----------------
        The open function needs a parameter for the mode.The mode has a
        particular meaning.If the file is opened for reading,it must
        already exist.The mode for the OPEN function must be "old"(1005)
        in this case.
        If you want to produce a file,you must open it first.Since it does
        not exist,you use the "new"(1006) mode.If a file is opened for
        writing using this mode even though a file with this name already
        exists,the old file with this name is erased and replaced.To avoid
        loss of data,you should check if a file by that name already
        exists and then output an error message if it does.
        You're going to start with a subroutine that opens a file.Lets
        assume that the filename starts at the label "filename",and that
        it is closed with a null byte.You just need to pass the mode in
        register D2.
        The routine puts the file handle number in "filehd"and returns to
        the main program.Since the operation with the handle is the last
        one performed by the subroutine,the status of the operation can be
        evaluated once the return has been executed.If the operation went
        smoothly and the file is opened,the handle number has a non-zero
        value.If it is zero and "bsr openfile"is followed by "beq error", 
        you can branch to an error handling routine when problems occur.

        Here is a subroutine for opening and closing a file:

        open       =-30                   ; (6.5.1A)                    
        close      =-36
        mode_old   = 1005
        mode_new   = 1006
             ...
        openfile:                       ;*open file,mode in D0
               move.l  dosbase,a6       ;DOS base address in A6
               move.l  #filename,d1     ;pointer to filename
               jsr     open(a6)         ;open file
               move.l  d0,filehd        ;save handle
               rts

        closefile:                      ;*close file
               move.l  dosbase,a6       ;DOS base address in A6
               move.l  filehd,d1        ;file handle in D1
               jsr     close(a6)        ;close file
               rts

        filehd:     dc.l   0            ;storage for file handle
        filename:   dc.b  "filename",0  ;file to be opened
               align                    ;even

        To use these subroutines,you must look at how you can load and
        save data.                                                        
                                                                          
      6.5.2.Reading and Writing Data.                                     
      -------------------------------                                     
        Lets write a new file.To start,write the following lines:      

                move.l  #mode_new,d2    ;open new file (6.5.2A)
                bsr     openfile        ;open file
                beq     error           ;did'nt work!

        For the filename,write a name like "Testfile"in the line labelled 
        "filename".After calling the "openfile"routine,a file with this
        name is created on the disk.If one existed already,it is erased.

        Lets assume you want to write a short text file.For the example
        lets use:

        text:  dc.b   "This is a test text for the Testfile",0
        textend:

        The "textend"label is used so that you can calculate the number of
        data bytes by subtracting "text".
        You want to write this text in the file.Use the WRITE function
        which needs three parameters:

        In D1   the file handle that you got back from the OPEN function.
        In D2   a pointer to the data that should be written.
        In D3   the number of bytes to be written.

        For the example,you'll need another segment of code to put the
        pointer to the data in D2 and the number of bytes in D3:

        write  =-48                      ; (6.5.2B)
              ...
        writedata:                       ;*write data in the file
              move.l  dosbase,a6         ;DOS base address
              move.l  filehd,d1          ;file handle in D1
              jsr     write(a6)          ;write data
              rts

        After opening the file,you can call the subroutine from the main
        program with the following lines: 

              move.l  #text,d2           ;pointer to data
              move.l  #textend-text,d3   ;number of bytes
              bsr     writedata          ;write data in the file

        Then close the file with:

              bsr     closefile          ;close file
              bra     end                ;end program

        After running the program,look at the directory of the diskette,  
        you should find the file "testfile".It is just as long as your
        text.You want to read this file in,to make sure it contains the
        right data.
        You need the DOS function READ,which needs the same parameters as
        the WRITE function.You can use parameters for the number of bytes
        to read just part of the file.If you give a larger number than the
        file contains,the whole file is loaded.You'll find the number of
        bytes read in D0.
        Lets set up a field that as enough space for the data you want to
        read.You can do this with the following line:

        field: blk.b  100     ;reserve 100 bytes

        For the example data,this is plenty.If you want to load another
        file,you may need to reserve more space.
        Now lets write a subroutine to read the data.You always want to
        load whole files.You just need to pass the address of the buffer
        so the data is loaded into the subroutine.In the example,its the
        address "field".
        Heres the subroutine that reads the entire opened file into the
        memory area pointed to by D2:

        read   = -42                  ; (6.5.2C)
               ...
        readdata:                     ;*read file
               move.l  dosbase,a6     ;DOS base address in A6
               move.l  filehd,d1      ;file handle in D1
               move.l  #$ffffff,d3    ;read an arbitrary number of bytes
               jsr     read(a6)       ;read data
               rts

        To use this routine to load the file into the buffer "field",use
        the following main program:

               move,l  #mode_old,d2   ;old file
               bsr     openfile       ;open file
               beq     error          ;did'nt work!
               move.l  #field,d2      ;pointer to data buffer
               bsr     readdata       ;read file
               move.l  d0,d6          ;save number of bytes in D6
               bsr     closefile      ;close file
               bra     end            ;program end

        After assembling and starting this program,you can use the
        debugger to look at the data buffer that you filled with data from
        the file.In D6,you'll find the number of bytes that were read from
        the file. 

      6.5.3.Erase Files.
      ------------------
        Once you've experimented enough with the program above,you'll
        certainly want to erase the "Testfile"file.The DELETEFILE function
        in the DOS library has an offset of -72.It only needs 1 parameter.
        The parameter is passed in D1.The parameter is a pointer to the
        filename.The name must be closed with a null byte.

        To erase "Testfile",use the following lines:

        deletefile  =-72                ; (6.5.3)
               ...
               move.l  dosbase,a6       ;DOS base address in A6
               move.l  #filename,d1     ;pointer to filename in D1
               jsr     deletefile(a6)   ;erase file

        The file is deleted.You can't save the file with normal methods if
        you accidently erase it!You can use a trick that saves the data.  
        We'll take a look at this trick later.Its used in lots of programs

      6.5.4.Rename Files.
      -------------------
        When a text editing program writes a text that as be altered back
        to the disk,the old file usually isn't erased.Often the old file
        is renamed.For example,it might get the name "Backup".The new file
        is written to disk with the old name.
        The function in the DOS library that allows you to change the
        names of programs is called RENAME and has -78 as an offset.You
        need to pass two parameters-D1 as a pointer to the old name and D2
        as a pointer to the new name of the file.

        To rename "Testfile"as "Backup"(before you erase it),use the
        following lines: 

        rename  =-78
               ...
               move.l  dosbase,a6    ;DOS base address in A6
               move.l  #oldname,d1   ;pointer to old name in D1
               move.l  #newname,d2   ;pointer to new name in D2
               jsr     rename(a6)    ;rename file
               ...
        oldname: dc.b "testfile",0
        newname: dc.b "backup",0

      6.5.5.CLI Directory.
      --------------------
        Lets pretend you've programmed a text editor and started it.Now
        you want to load a text from disk and edit it-but whats the name
        of that file?
        You need a function to read and display the directory of the disk.
        There are several ways to do this.First lets use the easiest
        method.It doesn't require much programming and can be quite
        useful.
        The trick is to call the Dir or List programs that are in the C   
        directory.You'll use the CLI commands.The DOS library contains a
        command called "Execute"with offset -222 that allows you to
        execute CLI commands. 
        The function needs three parameters:

        In D1   a pointer to a string closed with a zero that contains the
                name of the command to be executed.This string must
                contain the same command that you would give in the CLI.It
                can be a null pointer as well.
        In D2   the input file is determined.Normally theres a zero here. 
                If however,you give the file handle of a text file,though,
                this file is read and interpreted as a command sequence.If
                you define a window as the input medium,you've programmed
                a new CLI window!
        In D3   the output file is determined.If there a zero here,the
                output of the commands (for example,DIR output) is sent to
                the standard CLI window.

        To try this out,insert this subroutine in a program that has
        already opened the DOS library and a window.

        execute  = -222              ; (6.5.5)
              ...
        dir: 
              move.l  dosbase,a6     ;DOS base address in A6
              move.l  #command,d1    ;pointer to command line
              clr.l   d2             ;no input (CLI window)
              move.l  conhandle,d3   ;output in our window
              jsr     execute(a6)    ;execute command
              rts

        command:
              dc.b    "dir",0

        This program works with the List command as well.The disadvantage 
        of this method is that the disk that the Workbench is loaded from 
        must be in the drive or the system requests you to put it in.The  
        Dir command is just a program,and the Amiga must load it before it
        can run.
        The disadvantage isn't too great.The program is short,and it
        allows you to use any CLI command in a program.
        Here is the complete program in AssemPro format that calls the dir
        program:

        ;***** 6.5.5A DIR.ASM S.D.*****   
        
        openlib      =-408
        closelib     =-414
        ;execbase    = 4                    ;defined in AssemPro
                                            ;macros
                       
                                       
        *calls to Amiga Dos:

        open         =-30
        close        =-36
        execute      =-222
        IoErr        =-132
        mode_old     = 1005
        alloc_abs    =-$cc

               ILABEL AssemPro:includes/Amiga.l   ;AssemPro only

               INIT_AMIGA                   ;AssemPro only                
                                                                          
        run:
               bsr     init                 ;initialization
               bra     test                 ;system test

        init:                               ;system initialization and
                                            ;open
               move.l  execbase,a6          ;number of execute-library
               lea     dosname(pc),a1
               moveq   #0,d0
               jsr     openlib(a6)          ;open DOS-library
               move.l  d0,dosbase
               beq     error

               lea     consolname(pc),a1    ;console definition
               move.l  #mode_old,d0
               bsr     openfile             ;console open
               beq     error
               move.l  d0,conhandle

               rts

        test:
               bsr     dir                  ;do directory
               bra     qu                   ;quit and exit

        dir:
               move.l  dosbase,a6           ;DOS base address in A6
               move.l  #command,d1          ;pointer to command line
               clr.l   d2                   ;no input (CLI window)
               move.l  conhandle,d3         ;output in our window
               jsr     execute(a6)          ;execute command
               rts

        error:
               move.l  dosbase,a6
               jsr     IoErr(a6)
               move.l  d0,d5

               move.l  #-1,d7               ;flag

        qu:
               move.l  conhandle,d1         ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1           ;DOS.Lib close
               move.l  execbase,a6
               jsr     closelib(a6)

               EXIT_AMIGA                   ;AssemPro only

        openfile:                           ;open file
               move.l  a1,d1                ;pointer to I/O-Definition-
                                            ;text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        dosname: dc.b 'dos.library',0,0
               align.w
        dosbase: dc.l 0
        consolname: dc.b 'CON:0/100/640/100/** CLI-Test **',0
               align.w
        conhandle: dc.l 0
        command:
               dc.b    "dir",0

               end

      6.5.6.Read Directory.
      ---------------------
        Now,lets look at another method that doesn't need the CLI.In this
        way,you can read the directory of any disk without having to play
        Disk Jockey.                                                      
        You need to writ a program that does what CLI's Dir program does. 
        There are several steps.
        First you must give the system a key to the desired directory.That
        means you must call DOS'Lock function.It needs two parameters:

        In D1    pass a pointer to a text that contains the name of the
                 directory you wish to read.If,for example,you want to
                 read the contents of the RAM disk,the text would be 
                 'RAM:',0.
        In D2    put the mode that determines whether to read or write.Let
                 us use the "Read"(-2) mode.

        You call the Lock function (offset -84) and get either a point to
        the key or a zero returned to you in the D0 register.If you get a
        zero,the call did'nt work,the file was'nt found.This function can
        be used to find if a file is on the disk.You use this function
        with the name and see if D0 comes back zero.If not,the file
        exists.
        Lets assume the file or path exists.You need to save the value
        that came back in D0.You'll need it for both functions that you'll
        call.
        The next function you need is called Examine.You use it to search 
        the disk for an acceptable entry.It returns parameters like name, 
        length and date that correspond to the entry.You need to reserve a
        memory block for this information and put the beginning of the
        block in D2 before calling the Examine function.Put the key you
        got from the Lock function in the D1 register.
        The memory area that is filled with information is called a
        FileInfoBlock.Its 260 bytes long and contains information about
        the file.The name starts in the 9th byte and ends with a null byte
        so you can easily print it with our "pmsg"routine.The information
        that Examine gives isn't about a particular file,but about the
        disk.The name in FileInfoBlock is the disk name.
        The Examine function sends the status back in the D0 register.
        Since the Lock function already tested if the file existed,evalua-
        ting the status really isn't necessary.
        Now to the function that you can use to read individual files from
        the directory.The function is called ExNext (Examine Next).This   
        function searches for the next entry that fits the key every time
        it is called.ExNext gets the same parameters as Examine gets.     
        However,the return parameter in D0 is more important here.
        The ExNext function is always called in the same way.It always
        gets the next entry of the directory.If no more entries exist in
        the directory,ExNext puts a zero in the D0 register.
        You need to continue performing this operation until there aren't 
        any more entries.You can find this using the IoErr function from
        the DOS library.
        This function doesn't need any parameters.It returns the status of
        the last I/O operation that was performed in the D0 register.After
        the last ExNext,this value is 232,which means no_more_Entries.

        Heres a complete routine for reading the directory of the disk in 
        DFO:and displaying the contents in the window.

        ; 6.5.5B.ASM
        ;***** DOS-Sample function  3/87 S.D. *****

        openlib      =-30-378
        closelib     =-414
        exbase       =4

        * calls to amiga dos:

        open         =-30
        close        =-30-6
        read         =-30-12
        write        =-30-18
        myinput      =-30-24
        output       =-30-30
        currdir      =-30-96
        lock         =-30-54
        examine      =-30-72
        exnext       =-30-78
        exit         =-30-114
        IoErr        =-30-102
        waitforch    =-30-174
        mode         = 0
        mode_old     = 1005
        mode_new     = 1006
        alloc_abs    =-$cc
        free_mem     =-$d2

               ILABEL AssemPro:includes/Amiga.l   ;AssemPro only

               INIT_AMIGA                   ;AssemPro only

        run:                                                              
               bsr     init                 ;initialization
               bra     test                 ;system-test

        init:                               ;system initialization and
                                            ;open
               move.l  exbase,a6            ;pointer to exec.library
               lea     dosname(pc),a1
               moveq   #0,d0
               jsr     openlib(a6)          ;open dos-library
               move.l  do,dosbase
               beq     error

               lea     consolname(pc),a1    ;console definition
               move.l  #mode_old,d0
               bsr     openfile             ;console open
               beq     error
               moveq   d0,conhandle

               rts

        test:
               move.l  #mytext,d0
               bsr     pmsg                 ;test-text output

               move.l  dosbase,a6
               move.l  #name,d1
               move.l  #-2,d2
               jsr     lock(a6)
               move.l  d0,d5
               tst.l   d0
               beq     error
               move.l  d0,locksav

               move.l  dosbase,a6
               move.l  locksav,d1
               move.l  #fileinfo,d2
               jsr     examine(a6)
               move.l  d0,d6
               tst.l   d0
               beq     error

        loop:
               move.l  dosbase,a6
               move.l  locksav,d1
               move.l  #fileinfo,d2
               jsr     exnext(a6)
               tst.l   d0
               beq     error

               move.l  #fileinfo+8,d0
               bsr     pmsg
               bsr     pcrlf
               bra     loop

        error:
               move.l  dosbase,a6
               jsr     ioerr(a6)
               move.l  d0,d6

               move.l  #presskey,d0
               bsr     pmsg
               bsr     getch
               move.l  #-1,d7               ;flag

        qu:
               move.l  conhandle,d1         ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1           ;dos.lib close
               move.l  exbase,a6
               jsr     closelib(a6)
              
               EXIT_AMIGA                   ;AssemPro only

        openfile:                           ;open file
               move.l  a1,d1                ;pointer to I/O-Definition-
                                            ;Text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        pmsg:                               ;print message (D0)
               movem.l d0-d7/a0-a6,-(sp)
               move.l  d0,a0
               move.l  a0,d2
               clr.l   d3

        mess1:
               tst.b   (a0)+
               beq     mess2
               addq.l  #1,d3
               bra     mess1

        mess2:
               move.l  conhandle,d1
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6
               rts

        pcrlf:
               move    #10,d0
               bsr     pchar
               move    #13,d0

        pchar:                              ;character in D0 output
               movem.l d0-d7/a0-a6,-(sp)    ;save all
               move.l  conhandle,d1

        pch1:
               lea     chbuff,a1
               move.b  d0,(a1)
               move.l  a1,d2
               move.l  #1,d3
               move.l  dosbase,a6
               jsr     write(a6)
               movem.l (sp)+,d0-d7/a0-a6    ;restore all
               rts
       
        scankey:                            ;test key
               move.l  conhandle,d1
               move.l  #500,d2              ;wait value
               move.l  dosbase,a6
               jsr     waitforch(a6)
               tst.l   d0
               rts

        readin:                             ;input from keyboard
               movem.l d0-d7/a0-a6,-(sp)    ;registers
               lea     inline+2,a2          ;pointer to input buffer
               clr.l   (a2)

        inplop:
               bsr     getchr
               cmp.b   #8,d0
               beq     backspace
               cmp.b   #127,d0              ;delete?
               beq     backspace
               bsr     pchar                ;character output
               cmp.b   #13,d0
               beq     inputx
               move.b  d0,(a2)+
               bra     inplop

        input:
               clr.b   (a2)+
               sub.l   #inline,a2
               move    a2,inline            ;length in inline+1
               movem.l (sp)+,d0-d7/a0-a6    ;registers
               rts

        backspace:
               cmp.l   #inline,a2           ;at beginning?
               beq     inplop               ;yes
               move.b  #8,d0
               bsr     pchar                ;backspace
               move    #32,d0
               bsr     pchar                ;blank
               move    #8,d0
               bsr     pchar                ;backspace
               clr.b   (a2)
               subq.l  #1,a2
               bra     inplop

        getchr:                             ;get 1 character from keyboard
               move.l  #1,d3                ;1 character
               move.l  conhandle,d1
               lea     inbuff,a1            ;buffer-address
               move.l  a1,d2
               move.l  dosbase,a6
               jsr     read(a6)
               clr.l   d0
               move.b  inbuff,d0
               rts


        mytext:     dc.b 'Directory of Diskette: DFO:',10,13,10,13,0,0
        dosname:    dc.b 'dos.library',0,0
        presskey:   dc.b 'Press thr Return key!!',0
               align.w
        dosbase:    dc.l 0
        consolname: dc.b 'CON:0/100/640/100/** Directory-Test **',0
        name:       dc.b 'DFO:',0
               align.w
        locksav:    dc.l 0
        fileinfo:   ds.l 20
        conhandle:  dc.l 0
        inbuff:     DS.B 8
        inline:     DS.B 180
        chbuff      DS.B 82

               end

        The FileInfoBlock contains the following entries:

        Offset   Name             Meaning
        ----------------------------------------------------------------
          0      DiskKey.L        Disk Number
          4      DieEntryType.L   Entry Type (+=Directory,-=File)
          8      FileName         108 bytes with the filename
         116     Protection.L     File Protected?
         120     EntryType.L      Entry type
         124     Size.L           Length of file in bytes
         128     NumBlocks.L      Number of blocks
         132     Days.L           Creation day
         136     Minute.L         Creation time
         140     Tick.L           Creation time
         144     Comment          116 bytes with comments

        If you want to have the program output the file length as well,you
        can read the length with "move.l fileinfo+124,d0"and then use a
        conversion routine to produce a decimal number.You can output this
        result with the name.

      6.5.7.Direct Access To Disk.
      ----------------------------                                        
        There isn't a simple function in the library for accessing single 
        disk sectors.Here,you must work with a device just like you did
        with speech output.This time you'll be working with the trackdisk.
        device.
        You want to work with this device to directly program the disk
        drives.Once you've built up the necessary program machinery,you
        can experiment with various commands for disk access.Remember that
        an error can cause the disk to be modified and thus unusable.Make
        sure you're using a non-essential disk.Don't use one which
        contains your only copy of something.                             
        The initialization here is similar to that for speech output.Here
        is the initialization routine for your program:

        ;** Direct disk access via trackdisk.device ** (6.5.7)

        openlib    =-408
        closelib   =-414
        execbase   = 4
        open       =-30
        close      =-36
        opendevice =-444
        closedev   =-450
        sendIo     =-462
        read       =-30-12
        write      =-30-18
        waitforch  =-30-174
        mode_old   = 1005

        run:                             
               bsr     init                 ;initialization                     
               bra     test                 ;system-test

        init:                               ;initialize and open system
               move.l  execbase,a6          ;pointer to exec.library
               lea     dosname,a1
               moveq   #0,d0
               jsr     openlib(a6)          ;open dos.library
               move.l  d0,dosbase
               beq     error
               lea     diskio,a1            ;pointer to disk I/O area
               move.l  #diskrep,14(a1)      ;pointer to port
               clr.l   d0                   ;drive 0 (built in)
               clr.l   d1                   ;no flags
               lea     trddevice,a0         ;pointer to device name
               jsr     opendevice(a6)       ;open trackdisk.device
               tst.l   d0                   ;error?
               bne     error                ;yes!
               move.l  #consolname(pc),d1   ;console definition
               move.l  #mode_old,d2         ;old mode
               move.l  dosbase,a6           ;dos base address
               jsr     open(a6)             ;open window
               tst.l   d0                   ;error?
               beq     error                ;yes!
               move.l  d0,conhandle         ;else save handle
               rts                          ;done

        test:                               ;place for test routine


        And now for the functions that take care of the various messages
        at the end of the program.


        error:
               move.l  #-1,d7               ;flag for error (for SEKA)

        qu:
               move.l  execbase,a6          ;exec base address
               lea     diskio,a1            ;pointer to disk I/O
               move.l  32(a1),d7            ;IO_ACTUAL in D7 (for testing)
               move    #9,28(a1)            ;command motor on/off
               move.l  #0,36(a1)            ;0=off,1=on,so turn motor
               jsr     sendio(a6)           ;off
               move.l  conhandle,d1         ;close window
               move.l  dosbase,a6
               jsr     close(a6)
               move.l  dosbase,d1           ;close dos.lib
               move.l  execbase,a6
               jsr     closelib(a6)
               lea     diskio,a1
               jsr     closedev(a6)         ;close trackdisk.device
               rts


        Lets not forget the routine that waits for the user to press
        <Return>,so that you can watch the effects of the test function in
        peace:

        getchr:                             ;get a character from keyboard
               move.l  #1,d3                ;1 character
               move.l  conhandle,d1         ;window handle
               move.l  #inbuff,d2           ;buffer address
               move.l  dosbase,a6           ;dos base address
               jsr     read(a6)             ;read character
               rts                          ;thats it


        The last thing you need is the section of code that declares the
        text and data fields that your program needs:                     
                                                                          
        dosname:       dc.b  'dos.library',0
               align
        consolname:    dc.b  'RAW:0/100/640/50/** Wait window',0
               align
        trddevice:     dc.b  'trackdisk.device',0
               align
        dosbase:       dc.l 0               ;dos base address
        conhandle:     dc.l 0               ;window handle
        inbuff:        blk.b 80,0           ;keyboard buffer
        diskio:        blk.l 20,0           ;I/O structure
        diskrep:       blk.l 8,0            ;I/O port
        diskbuff:      blk.b 512*2,0        ;place for 2 sectors

        There,now you've done with the set up work.Lets look at how you
        can give commands to the disk drives.The first and easiest command
        is the one for turning the drive motor on and off.You've already
        seen this command in the program.This is command number nine.This
        number goes in the command word of the I/O structure (bytes 28 and
        29 of the structure).
        You need to pass a parameter that lets the computor know whether
        to turn the motor off or on.This information goes in the I/O long
        word that starts at byte 36:its zero for off,and one for on.
        You already chose the motor that should be turned on or off when
        you opened the device.You put the number of the chosen disk drive
        in D0-in your case you put a zero there because you are using the
        DFO:disk drive.
        Heres an overview of the commands you can use to access
        information on the disk:

        No    Name          Function
        -----------------------------------------------------------------
        2     READ          Read one or more sectors
        3     WRITE         Write sectors
        4     UPDATE        Update the track buffer
        5     CLEAR         Erase track buffer
        9     MOTOR         Turn motor on/off
        10    SEEK          Search for a track
        11    FORMAT        Format tracks
        12    REMOVE        Initialize routine that is called when you
                            remove the disk
        13    CHANGENUM     Find out number of disk changes
        14    CHANGESTATE   Test if disk is in drive
        15    PROTSTATUS    Test if disk is write protected

        You've already learned about command number nine.Lets look at the
        three commands you can use to make tests.These are the last three
        commands.They put a return value in the long word that begins in
        the 32nd byte in the I/O structure.This value was written in D7 in
        the program above for testing purposes.You can read its contents  
        directly if you ran the program with AssemPro.
        Here is a simple routine that you can use to run one of these
        commands with:

        test:                               ; (6.5.7B)
               lea     diskio,a1            ;pointer to I/O structure
               move    #13,28(a1)           ;pass command (for example 13)
               move.l  execbase,a6          ;execbase address in A6
               jsr     sendio(a6)           ;call function

        If CHANGENUM (command 13) is executed,in D7 you'll get the number
        of times a disk was taken out and put in the drive.If you call the
        program,you'll get a value back.If you take the disk out and put
        it back in,the number is two higher the next time you call the
        program.
        The CHANGESTATE command (command 14) tells whether a disk is in
        the drive or not.If one is,a zero comes back.Otherwise,a $FF is
        returned.
        You get the same values back from the PROTSTATUS function (command
        15).Here a zero means that the disk isn't write protected,while
        $FF means that it is.
        Now lets look at the READ and WRITE functions.These operations
        need a few more parameters than the status functions.You need to
        pass the following parameters: 

        The address of the I/O buffer in the data pointer,the number of
        bytes to be transfered in I/O length,and the data address on the
        disk in I/O offset. 

        The number of data bytes must be a multiple of 512,since every
        sector is 512 bytes,and only whole sectors can be read.

        The data address is the number of the first byte in the sector.If 
        you want to use the first sector,the offset is zero.For the second
        sector,its 512,etc...The formula is:

            offset = (sector_number -1) *512

        Here is a routine that loads the first two sectors of the disk
        into the buffer:

        test:  (6.5.7C)
               lea     diskio,a1           
               move    #2,28(a1)           ;command:READ
               move.l  #diskbuff,40(a1)    ;buffer
               move.l  #2*512,36(a1)       ;length:2 sectors
               move.l  #0*512,44(a1)       ;offset:0 sectors
               move.l  execbase,a6         ;exec base address
               jsr     sendio(a6)          ;start function

        Start the program from the debugger and then look at the buffers  
        contents after the program ends.You can find out the format of the
        disk here.If you want to read a sector thats being used,change the
        0 in the offset definition to 700 and start again.Its highly
        probable that theres some data there.
        To modify and write back the data that you've read from the disk, 
        you need command number three,the WRITE command.The parameters are
        the same.
        If you've executed the WRITE commandyou're probably wondering why
        the disk light did'nt go on.Thats because the Amiga writes a track
        that as been read into a buffer on its own.It WRITE's data there
        as well.It won't write the data to disk until another track is
        accessed.
        You can have the data updated directly as well using command four,
        the UPDATE command.
        Command 11,the FORMAT command,is also quite interesting.This
        command needs a data field that is 11*512=5632 bytes long-the
        length of a track.The offset must be a multiple of this number so 
        that you start at the beginning of a track.
        The length must be a multiple of 5632 as a result.If several
        tracks are formatted,each track is filled with the same data.
        You can use this function to easy write a disk copy program.You
        READ the source disk and then FORMAT the corresponding track on
        the destination disk.Thats how the DiskCopy program works-it
        reformats the destination disk.
        Command ten,the SEEK command,just needs the offset.It moves the
        Read/Write head of the drive to the position specified without
        making a disk access or testing if its at the right position.
        Command 12,the REMOVE command,is used to install an interrupt
        routine that is called when the disk is removed from the disk
        drive.The address of the interrupt structure is passed in the data
        pointer of the I/O structure.If theres a zero here,the interrupt  
        routine is turned off.

        Heres a complete example program in AssemPro format:

        ;***** Track disk-Basic function  10/86 S.D. *****

               ILABEL ASSEMPRO:includes/Amiga.l   :AssemPro only

        openlib      =-30-378
        closelib     =-414
        ;execbase    = 4                    ;defined in INIT_AMIGA

        * calls to amiga dos:

        open         =-30
        close        =-30-6
        opendevice   =-444
        closedev     =-450
        sendIo       =-462
        read         =-30-12
        write        =-30-18
        waitforch    =-30-174
        mode_old     = 1005

               INIT_AMIGA                   ;AssemPro only

        run:
               bsr     init                 ;initialization
               bra     test                 ;system test

        init:                               ;system initialization and
                                            ;open 
               move.l  execbase,a6          ;pointer to exec-library
               lea     dosname,a1
               moveq   #0,d0
               jsr     openlib(a6)          ;open dos-library
               move.l  d0,dosbase
               beq     error

               lea     diskio,a1
               move.l  #diskrep,14(a1)
               clr.l   d0
               clr.l   d1
               lea     trddevice,a0
               jsr     opendevice(a6)       ;open trackdisk.device
               tst.l   d0
               bne     error

        bp:
               lea     consolname(pc),a1    ;console-definition
               move.l  #mode_old,d0
               bsr     openfile             ;console open
               beq     error
               move.l  d0,conhandle

               rts

        test:
               bsr     accdisk

               bsr     getchr               ;wait for character
               bra     qu

        error:
               move.l  #-1,d7               ;flag

        qu:
               move.l  execbase,a6
               lea     diskio,a1
               move    #9,28(a1)
               move.l  #0,36(a1)
               jsr     sendio(a6)

               move.l  conhandle,d1         ;window close
               move.l  dosbase,a6
               jsr     close(a6)

               move.l  dosbase,a1           ;dos.lib close
               move.l  execbase,a6
               jsr     closelib(a6)

               lea     diskio,a1
               move.l  32(a1),d7
               jsr     closedev(a6)

               EXIT_AMIGA                   ;AssemPro only

        openfile:                           ;open file
               move.l  a1,d1                ;pointer to the I/O-definition
                                            ;text
               move.l  d0,d2
               move.l  dosbase,a6
               jsr     open(a6)
               tst.l   d0
               rts

        scankey:                            ;test for key
               move.l  conhandle,d1                      
               move.l  #500,d2              ;wait value
               move.l  dosbase,a6
               jsr     waitforch(a6)
               tst.l   d0
               rts

        getchr:                             ;get one character from
                                            ;keyboard
               move.l  #1,d3                ;1 character
               move.l  conhandle,d1
               lea     inbuff,a1            ;buffer-address
               move.l  a1,d2
               move.l  dosbase,a6
               jsr     read(a6)
               clr.l   d0
               move.b  inbuff,d0
               rts

        accdisk:
               lea     diskio,a1
               move    #2,28(a1)            ;command:READ                 
               move.l  #diskbuff,40(a1)     ;buffer
               move.l  #2*512,36(a1)        ;length:2 sectors
               move.l  #20*512,44(a1)       ;offset: n sectors
               move.l  execbase,a6
               jsr     sendio(a6)
               rts

        dosname:       dc.b 'dos.library',0,0
               align.w
        dosbase:       dc.l 0
        consolname:    dc.b 'RAW:0/100/640/100/** Test-Window S.D.V0.1',0
        trddevice:     dc.b 'trackdisk.device',0
               align.w
        conhandle      dc.l 0
        inbuff:        ds.b 8                                             
        diskio:        ds.l 20,0                                          
        diskrep:       ds.l 8,0                                           
        diskbuff:      ds.b 512*2,0                                       
                                                                          
               end
           
                            NOW LOAD PART THREE
                            
end.