Amiga Machine Language (Chapter 4)

Innen: amigaspirit.hu - pegasos.hu Wiki
Ugrás a navigációhozUgrás a kereséshez
A nyomtatható változat már nem támogatott, és hibásan jelenhet meg. Kérjük, frissítsd a böngésződ könyvjelzőit, és használd a böngésző alapértelmezett nyomtatás funkcióját.
     Chapter 4.
     ---------
     4.Our First Programs.
     --------------------
       You`re getting pretty far along in your knowledge of machine
       language programming.In fact,you`re to the point where you can
       write programs,and not just programs for demonstration purposes,
       but ones that serve a real function.We`re assuming that you have
       the AssemPro assembler and have loaded it.
       If you`re using a different assembler,a few things must be done
       differently.We covered those differences already in the chapter on
       the different assemblers.
       We`ve written the example programs as subroutines so they can be
       tried out directly and used later.After assembling the program,you
       can put the desired values in the register.Then you can either
       single-step thru the programs or run the larger programs and
       observe the results in the registers directly.(using the SEKA
       assembler you can type "j program_name"to start the program.Then
       you can read the results from the register directly,or use "q
       address"to read it from memory.)
       Lets start with an easy example,adding numbers in a table.
     4.1.Adding Tables.
     -----------------
       Imagine that you have numbers in memory that you'd like to add.
       Lets assume that you have five numbers whose length is one word
       each.You want their sum to be written in register D0.The easiest
       way to do this is:
       ;(4.1A)
       adding1:
              clr.l  D0               ;Erase D0 (=0)
              move   table,d0         ;First entry in D0
              add    table+2,d0       ;Add second entry
              add    table+4,d0       ;Add third entry
              add    table+6,d0       ;Add fourth entry
              add    table+8,d0       ;add fifth entry
              rts                     ;Return to main program
       table: dc.w 2,4,6,8,10,
              end
       Try out the program using the debugger by single stepping thru the
       program until you get to the RTS instruction(left Amiga T).(SEKA
       owners use "j adding1").You see that data register D0 really
       contains the sum of the values.
       The method above only reads and adds numbers from a particular set
       of addresses.The Amigas processor has lots of different sorts of 
       addressing modes that give us a shorter and more elegant solution.
       Lets add a variable to the address of the table,so that the
       program can add different tables.
       Lets put the addresses of the table in an address register(for
       example, A0)instead.This register can be used as a pointer to the
       table.You must use move.l since only long words are relocatable.By
       using a pointer to a table you can use indirect addressing.You can
       change the expression "table+x" to"x(A0)".
       ;(4.1B)
       adding1:
              clr.l   D0            ;Erase D0 (=0)
              move.l  #table,a0     ;Put table addresses in A0
              move    0(a0),d0      ;Put first entry in d0
              add     2(a0),d0      ;Add second entry
              add     4(a0),d0      ;Add third entry
              add     6(a0),d0      ;Add forth entry
              add     8(a0),d0      ;Add fifth entry
              rts                   ;Return to main program]
       table: dc.w 2,4,6,8,10
              end
       Assemble this program,load it into the debugger.Then single step
       (left Amiga T)thru this program and you'll see that this program
       adds five numbers in order just like the last one.The reason you
       used a step size of two for the ofset is that words are two bytes
       long.AssemPro also defaults to relocate code so that you must move
       #table as a long word.
       Lets improve the program more by using "(a0)+"instead of "x(a)".
       This way,every time you access elements of the table,the address
       register A0 is automatically incremented by the number of bytes
       that are read(in this case two).The difference between this and
       the last example is that here the registers contents are modified.
       The pointer is to the next unused byte or word in memory.
       Lets make it even better.Lets make the number of words to be added
       to a variable.You'll pass the number in register D1.Now you need
       to do a different sort of programming,since you can't do it with
       the old methods.
       Lets use a loop.You need to add D1 words.You can use(a0)+ as the
       addressing method(address register indirect with post increment),
       since this automatically gets you to the next word.
       Now for the loop.You'll have D1 decremented by one every time the
       contents of the pointer are added.If D1 is zero,then you're done.
       Otherwise,you need another addition.The program looks like this:
       ;(4.1C)
       adding2:
              clr.l    d0            ;Erase D0
              move.l   #table,a0     ;Put table addresses in A0
              move     #$5,d1        ;Put number of entries in D1
              
       loop:                         ;Label for loop beginning
              add      (a0)+,d0      ;Add a word
              subq     #1,d1         ;Decrement counter
              bne      loop          ;Continue if non-zero
              rts                    ;Else done
       table: dc.w 2,4,6,8,10
              end
       Lets take a close look at this program.Load the pointer A0 with
       the addresses of the data and the counter D1 with the number of
       elements.Then you can single step thru the program and watch the
       results.Make sure not to run the final command,the RTS command,
       because otherwise a return address is popped from the stack,and
       the results of this are unpredictable.(SEKA owners can use"x pc"
       to point the program counter to "adding2".You can then step thru
       the program by using the "s"command and watch the results).
       To finish up this example,your assigning a little homework.Write
       the program so that it adds single bytes or long words.Try to
       write a program that takes the first value in a table and
       subtracts the following values.For example,the table
            table: dc.w 50,8,4,6
       should return the value 50-8-4-6, ie 32 ($20).
     4.2.Sorting a Table.
     -------------------
       Lets keep working with tables.You don't want to just read data
       from one this time.You want to change it.You'll assort the table
       in assending order.
       You need to decide how to do the sorting.The simplest method is to
       do the following.
       Compare the first and the second value.If the second value is
       larger than the first one,things are OK so far.Do the next step,
       compare the second and third values,and so on.If you come to the 
       final pair and in each case the preceding value was smaller than
       the following value,then the sorting is done(it was unnescessary).
       If you find a pair where the second value is smaller than the
       first,the two values are exchanged.You then get a flag(here let's
       use a register)that is checked once you're done going thru the
       table.If it is set,the table probably isn't completely sorted.You
       then erase the flag and start again from the beginning.If the flag
       is still zero at the end,the sorting is complete.
       Now let's write a program to do this.First let's figure out the
       variables you need.You'll use registers for the variables.You need
       a pointer to the table you're sorting(A0),a counter(D0)and a flag
       (D1).While the program is running,change these values,so you'll 
       need two more registers to store the starting values(address and
       the number of the table entries).You'll use A1 and D2.
       Let's start writing the program,each section will be written and
       then explained.Then the complete program will be given.You put the
       tables address in A1 and the number of entries in D2.
       ;(4.2A) part of sort routine
       sort:                       ;Start address of the program
               move.l  #table,a1   ;Load pointer with address
               move.l  a1,a0       ;Copy pointer to working register
               move.l  #5,d2       ;Number in the counter
               move.l  d2,d0       ;Copy number of elements
               subq    #2,d0       ;Correct conter value
               clr     d1          ;Erase flag
       table: dc.w 3,6,9,5
              end
       Now the preparations are complete.The pointer and the counter are
       ready and the flag is cleared.The counter is decremented by two
       because you want to use the DBRA command(take one off)and only X-1
       comparrisons are needed for X numbers(take off one more).
       Next let's write the loop that compares the values.You compare one
       word with another.It looks like this:
       loop:
             move   2(a0),d3   ;Next value in register D3
             cmp    (a0),d3    ;Compare values
       You need to use register D3 because CMP (A0),2(A0) isn't a legal
       choice.If the second value is greater than or equal to the first
       value,you can skip an exchange.
             bcc    noswap      ;Branch if greater than or equal
                                ;to
       Now you need to do the exchanging(unfortunatly you can't use EXC
       2(a0),(a0) since this form of addressing does not exist).
       doswap:
              move   (a0),d1      ;Save first valus
              move   2(a0),(a0)   ;Copy second into first word
              move   d1,2(a0)     ;Move first into second
              moveq  #1,d1        ;Set flag
       noswap:
       Now increment the counter and continue with the next pair.You do
       this until the counter is negative.
              addq.l  #2,a0      ;Pointer+2
              dbra    d0,loop    ;Continuing looping until the end
       Now you'll see if the flag is set.You start again at the beginning
       if it is.
              tst     d1         ;Test flag
              bne     sort       ;Not finished sorting yet!
              rts                ;Otherwise done.Return
       If the flag is zero,you're done and the subroutine ends.You jump
       back to the main program using the RTS command.
       Now a quick overview of the complete program.
       ;(4.2B)
       sort:                      ;Start address of the program
             move.l  #table,a1    ;Load pointer with address
             move.l  a1,a0        ;Copy pointer to working register
             move.l  #5,d2        ;Number in the counter
             move.l  d2,d0        ;Copy number of elements
             subq    #2,d0        ;Correct counter value
             clr     d1           ;Erase flag
       loop:
             move    2(a0),d3     ;Next value in register D3
             cmp     (a0),d3      ;Compare values
             bcc     noswap       ;Branch if greater than or equal to
       doswap:
             move    (a0),d1      ;Save first value
             move    2(a0),(a0)   ;Copy second into first word
             move    d1,2(a0)     ;Move first into second
             moveq   #1,d1        ;Set flag
       noswap: 
             addq.l  #2,a0        ;Pointer+2
             dbra    do,loop      ;Continue looping until the end
             tst     d1           ;Test flag
             bne     sort         ;Not finished sorting yet!
             rts                  ;Otherwise done.Return
       table: 
             dc.w    10,8,6,4,2   ;When finished acceding
             end
       To test this subroutine,assemble the routine with AssemPro,save it
       and then load it into the debugger.The table is directly after the
       RTS,notice its order.Set a breakpoint at the RTS,select the
       address with the mouse and press left-Amiga-B sets a breakpoint in
       AssemPro.Start the program the redisplay the screen by selecting
       "Parameter-Display-Dissassem-bled"and examine the order of the
       numbers in the table,they should now be ascending order.
       You use several registers in this example for storing values.
       Usually in machine language programming your subroutines cannot
       change any register or can only change certain registers.For this
       reason,there is a machine language command to push several
       registers onto the stack at the same time.This is the MOVEM ("MOVE
       multiple")command.If you insert this command twice in your program
       then you can have the registers return to the main program with
       the values they had when the subroutine was called.To do this,you
       need one more label.Let's call it"start";the subroutine is started
       from here.
       start:
             movem.l  d0-d7/a0-a6,-(sp)    ;save registers
             
       sort:
             etc...
             ...
             ...
             bne      sort                 ;not finished sorting yet!
             movem.l  (sp)+,d0-d7/a0-a6    ;retrieve registers
             rts                           ;finished
       The powerful command moves several registers at the same time.You
       can specify which registers should be moved.If you want to move
       the D1,D2,D3,D7,A2 and A3 registers,just write 
             movem.l  d1-d3/d7/a2-a3,-(sp)
       Before you quit sorting,do one little homework assignment.Modify
       the program,so that it sorts the elements in descending order.
     4.3.Converting Number Systems.
     -----------------------------
       As we mentioned in the chapter on number systems,converting
       numbers from one base to another can be rather difficult.There is
       another form of numeric representation-as a string that can be
       entered via the keyboard or output on the screen.
       You want to look at some of the many conversions possible and
       write programs to handle the task.You'll start by converting a hex
       number into a string using binary numbers and then print the value
       in hex.
     4.3.1.Converting Hex To ASCII.
     -----------------------------
       First you need to set the start and finish conditions.In this
       example,let's assume that data register D1 contains a long word
       that should be converted into a 8-digit long string of ASCII
       characters.You'll write it to a particular memory location so that
       you can output it later.
       The advantage of using hex instead of decimal is pretty clear in
       this example.To find out the hexadecimal digit for a particular
       spot in the number,you just need to take the corresponting 4 bits
       (half byte)and do some work on it.A half byte(also called a
       nibble)contains one hex digit.
       You'll work on a half byte in D2.To convert this to a printable
       character,you need to use the correct ASCII code.The codes of the
       16 characters that are used as hex digits are the following:
         0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
       $30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $41 $42 $43 $44 $45 $46
       To convert the digits 0-9,you just need to add $30.For the letters
       A-F that correspond to the values 10-15,you need to add $37.The
       program to evaluate a half byte must make a destinction between
       values between 0 and 9 and those between A and F and add either
       $30 or $37.
       Now let's write a machine language subroutine that you'll call for
       each digit in the long words hex representation.
       nibble:
              and    #$0f,d2     ;just keep low byte
              add    #$30,d2     ;add $30
              cmp    #$3a,d2     ;was it a digit?
              bcs    ok          ;yes:done
              add    #7,d2       ;else add 7
       ok:
              rts                ;done
       This routine converts the nibble in D2 to an ASCII character that
       corresponds to the hex value of the nibble.To convert an entire
       byte,you need to call the routine twice.Here is a program to do
       this.The program assumes that A0 contains the address of the
       buffer that the characters are to be put in and that D1 contains
       the byte that is converted.
       ;(4.3.1a) bin-hex
       ;                            ;your program
                lea     buffer,a0   ;pointer to buffer
                move    #$4a,d1     ;byte to be converted(example)
                bsr     byte        ;and convert
                rts
       ;        ...                 ;more of your program
       byte:
                move    d1,d2       ;move value into d2
                lsr     #4,d2       ;move upper nibble into lower nibble
                bsr     nibble      ;convert d2
                move.b  d2,(a0)+    ;put character into buffer
                move    d1,d2       ;value in d2
                bsr     nibble      ;convert lower nibble
                move.b  d2,(a0)+    ;and put it in buffer
                rts                 ;done
       nibble:
                and     #$0f,d2     ;just keep low byte
                add     #$30,d2     ;add $30
                cmp     #$3a,d2     ;was it a digit?
                bcs     ok          ;yes:done
                add     #7,d2       ;else add 7
       ok:
                rts                 ;done
       buffer:
                blk.b   9,0         ;space for long word data
                end
       To test this subroutine,use AssemPro to assemble the routine,save
       the program and load it intoi the debugger.Next set a breakpoint
       at the first 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 watch the contents of D2,it is first $34
       (ASCII 4)and finally $41(ASCII A).Select "Parameter-Display-HEX-
       Dump"and you'll see that 4A has been moved into the buffer.
       This is how the routine operates.First,you move the value that you
       wish to convert into D2.Then you shift the register four times to
       the right to move the upper nibble into the lower four bits.After
       the subroutine call,you use "move.b d2,(a0)+"to put the HI nibble
       in the buffer.Then the original byte is put in D2 again.It is 
       converted.This gives us the LO nibble as an ASCII character in D2.
       You put this in the next byte of the buffer.
       The buffer is long enough to hold a long word in characters and
       closing the null byte.The null byte is usually required by screen
       output routines.Screen output will be discussed in a later
       chapter.Now let's worry about converting a long word.
       When converting a long word,you need to be sure to deal with the
       nibbles in the right order.Before calling the "nibble"routine for
       the first time,you need to move the upper nibble into the lower 4
       bits of the long word.You need to do this without losing anything.
       The LSR command isn't very good for this application.If you use it
       you'll lose bits.Its better to use the rotation commands like ROR
       or ROL,since they move the bits that are shifted out,back in on
       the other side.
       If you shift the original long word in D1 four times to the left,
       the upper four bits are shifted into the lower four bits.Now you
       can use our "nibble"routine to evaluate it and then put the
       resulting ASCII character in the buffer.You repeat this eight
       times and the whole long word has been converted.You even have D1
       looking exactly the way it did before the conversion process began
       ;(4.3.1B) bin-hex-2
       hexlong:
              lea     buffer,a0      ;pointer to the buffer
              move.l  #$12345678,d1  ;data to convert
              move    #7,d3          ;counter for the nibbles:8-1
       loop:
              rol     #4,d1          ;move upper nibble into lower
              move    d1,d2          ;write in d2
              bsr     nibble         ;and convert it
              move.b  d2,(a0)+       ;character in buffer
              dbra    d3,loop        ;repeat 8 times
              rts                    ;finished!
       nibble:
              and     #$0f,d2        ;just keep low byte
              add     #$30,d2        ;add $30
              cmp     #$3a,d2        ;was it a digit?
              bcs     ok             ;yes:done
              add     #7,d2          ;else add 7
       ok:
              rts                    ;done
       buffer:
              blk.b   9,0            ;space for long word,null byte
              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 first 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 when it is finished redisplay the output by selecting
       "Parameter-Display-HEX-dump"so you can examine the new buffer
       contents.
       You'll find that there's an error in the program-the buffer
       contains the digits "56785678"instead of "12345678".Try to find
       the error.
       Have you found it?This is the sort of error that causes you to
       start pulling your hair out.This sort is hard to find.The
       assembler assumes that the rotation operation should be done on a
       word,because the ".l"was left off.As a result,only the lower word
       of D1 was rotated-so you get the same value twice.If you change
       the "rol.l",things work just right.
       The error shows how easy it is to convert the program above into
       one that converts four digit hex numbers into ASCII characters.
       Just leave off the ".l"on the "rol"command and change the counter
       from seven to three.The program is done.
       Now for a little homework:change the program so that it can handle
       six digit hex numbers(D1 doesn't nescessarily have to stay the
       same...)!
       Now lets look at a different conversion problem:converting a four
       digit decimal number.
     4.3.2.Converting Decimal To ASCII.
     ---------------------------------
       It's not quite as easy to convert decimal as hex.You can't group
       the bits to form individual digits.You need to use another method.
       Lets look at how a decimal number is constructed.In a four digit
       number,the highest place is in the thousands place,the next is the
       hundreds place,etc...
       If you have the value in a register and divide by 1000,you'll get
       the value that goes in the highest place in the decimal number.
       Since the machine language command DIV not only gives us the
       result of the division but also gives us the remainder,you can
       work out the remainder quite easily.You divide the remainder by
       100 to find the hundreds place,divide the remainder by 10 and get
       the tens place,and the final remainder is the ones place.
       This isn't so hard after all!Heres the program that follows the
       steps above to fill the buffer with D1's ASCII value.
       main:
              lea     buffer,a0    ;pointer to the buffer
              move    #1234,d1     ;number to convert
              jsr     deci_4       ;test subroutine
              illegal              ;room for breakpoint
       deci_4:                     ;subroutine-four digit numbers
              divu    #1000,d1     ;divide by 1000
              bsr     digit        ;evaluate result-move remainder
              divu    #100,d1      ;divide by 100
              bsr     digit        ;evaluate result and move
              divu    #10,d1       ;divide by 10
              bsr     digit        ;evaluate result-move remainder
                                   ;evaluate the remainder directly
       digit:
              add     #$30,d1      ;convert result into ASCII
              move.b  d1,(a0)+     ;move it into buffer
              clr     d1           ;erase lower word
              swap    d1           ;move the remainder down
              rts                  ;return
       buffer:blk.b 5,0            ;reserve bytes for result
              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 illegal instruction.To set the breakpoint in AssemPro select
       the correct address with the mouse and press the right-Amiga-B
       keys.This breakpoint stops the program.Start the program and when
       it is finished redisplay the output by selecting"Parameter-Display
       -HEX-dump"so you can examine the ASCII values now in the buffer.
       You use a little trick in this program that is typical for machine
       language programming.After calling"digit"three times from the sub-
       routine"deci_4",you go right into the"digit"subroutine.You don't 
       use a BSR or JSR command.Once the processor hits the RTS command,
       it returns to the main program,not the "deci_4"subroutine.Doing
       this,you save a fourth "bsr digit"command and an "rts"command for
       the "deci_4"routine.
       Try the program out.Make sure that you use values that are smaller
       than 9999,because otherwise strange things can happen.
       Now let's reverse what you've been doing and convert strings into
       binary numbers.
     4.3.3.Converting ASCII To Hex.
     -----------------------------
       In a string,each hex digit represents a half byte.You just need to
       write a program that exactly reverses what the hex conversion
       program did.
       You have two choices
         1.  The number of hex digits is known in advance
         2.  The number is unknown
       The first is easier to program,but has the disadvantage that if,
       you assume the strings are four digits in length and want to enter
       the value 1,you must enter 0001.That is rather awkward,so you'll
       use the second method.
       Let's convert a single digit first.You'll pass a pointer to this
       digit in address register A0.You want the binary value to come
       back in data register D0.
       The program looks like this:
              move.l  #string,a0    ;this example
              jsr     nibblein      ;test routine
              nop                   ;set breakpoint here
       nibblein:                    ;*convert the nibble from (A0)
              clr.l   d0            ;erase D0
              move.b  (a0)+,d0      ;get digit,increment A0
              sub     #'A',d0       ;subtract $41
              bcc     ischar        ;no problem:in the range A-F
              add     #7,d0         ;else correct value
       ischar:
              add     #10,d0        ;correct value
              rts
       string:dc.b 'B',0            ;character to convert
              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 first NOP,to set the breakpoint in AssemPro select the correct
       address with the mouse and press the right-Amiga-B keys.Start the
       program and watch the contents of D0.
       Let's see how the program works.A0 points to a memory location
       that contains the character "B"that is represented by the ASCII
       value $42.This number is loaded into D0 right after this register
       is erased.
       After subtracting $41,you end up with the value $1.Now you're
       almost done.Before returning to the main program,you add 10 to get
       the correct value 11,$B.
       If the buffer has a digit in it,the subtraction causes the
       register to become negative.The C flag is set.Let's take the digit
       5 as an example.
       The ASCII value of 5 is $35.After subtracting $41,you end up with
       -12 and the C flag is set.In this case,you won't branch with the
       BCC command.Instead you'll add 7 to get -5.Then 10 is added,and
       you end up with 5.Done!
       The routine as a disadvantage.If an illegal character is given,one
       that doesn't represent a hex digit,you'll get some nonsense result
       Let's ignore error checking for the moment though.
       Let's go on to multi-digit hex numbers.The first digit that you
       convert has the highest value and thus represents the highest
       nibble.To allow for this and to allow for an arbitrarily long
       number(actually not arbitrarily long,the number should fit in a
       long word-so it can only be eight digits long),you'll use a trick.
       Take a look at the whole program.It handles the calculations and
       puts the result in D1.It assumes that A0 is a pointer to a string
       and that this string is ended by null byte.
       hexin:                       ;converting a hex number
              clr.l   d1            ;first erase D1
              move.l  #string,a0    ;address of the string in A0
              jsr     hexinloop     ;test subroutine
              nop                   ;set breakpoint here
       hexinloop:
              tst.b   (a0)          ;test digit
              beq     hexinok       ;zero,then done
              bsr     nibblein      ;convert digit
              lsl.l   #4,d1         ;shift result
              or.b    d0,d1         ;insert nibble
              bra     hexinloop     ;and continue
       hexinok:
              rts
       nibblein:
              clr.l   d0            ;convert the nibble from (A0)
              move.b  (a0)+,d0      ;get digit,increment A0
              sub     #'A',d0       ;subtract $41
              bcc     ischar        ;no problem:in range A-F
              add     #7,d0         ;else correct value
         
       ischar:
              add     #10,d0        ;correct value
              rts
       string:DC.B "56789ABC',00    ;eight digit string,null byte
                                    ;to be converted
              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 NOP,to set the breakpoint in AssemPro select the correct
       address with the mouse and press the right-Amiga-B keys.Start the
       program and watch the contents of D1,the hex value is placed in
       this register.
       The trick is to shift left four times,to shift one nibble.In this
       way,the place of the last digit is incremented by one and there is
       room for the nibble that comes back from the "nibblein"routine.The
       program uses the TST.B instruction to check for the null byte at
       the end of the string,when it encounters the null byte the program
       ends.The result is in the D1 long word already!
       To do some error checking,you need to make some changes in the
       program.You'll do this right after you come back from the"nibblin"
       routine with the value of the current character.
       If the value in D0 is bigger than $F,there is an error.You can
       detect this in several ways.You chose the simplest one-you'll use
       CMP #$10,D0 to compare D0 with $10.If it smaller,then the C flag
       is set(since CMP uses subtraction)and everything is fine.If C is
       zero,there is an error.
       You can use this trick to skip the test for a null byte,since its
       an invalid character as well.The program looks like this:
       ;(4_3_3C) hex-conv2         optional disk name
       hexin:                      ;converting a hex number
              clr.l   d1           ;first erase D1
              move.l  #string,a0   ;address of string in A0
              jsr     hexinloop    ;test subroutine
              nop                  ;set breakpoint here


       hexinloop:
              bsr     nibblein     ;convert digit
              cmp     $10,d0       ;test if good
              bcc     hexinok      ;no,then done
              lsl.l   #4,d1        ;shift result
              or.b    d0,d1        ;insert nibble
              bra     hexinloop    ;and continue
       hexinok:
              rts
       nibblein:                   ;convert the nibble from (A0)
              clr.l   d0           ;erase D0
              move.b  (a0)+,d0     ;get digit,increment A0
              sub     #'A',d0      ;subtract $41
              bcc     ischar       ;no problem:in the range A-F
              add     #7,d0        ;else correct value
       ischar:
              add     #10,d0       ;correct value
              rts
       string:DC.B "56789ABC',00   ;8 digit string ending with a
                                   ;null byte to be converted
              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 NOP,to set the breakpoint in AssemPro select the correct
       address with the mouse and press the right-Amiga-B keys.Start the
       program and watch the contents of D1,the hex value is placed in
       this register.
       This is the method for converting hex to binary.If you convert
       decimal to binary,the conversion is not much harder.
     4.3.4.Converting ASCII To Decimal.
     ---------------------------------
       You can use a very similar method to the one used above.Since you
       are not sure how many digits there are,you'll use a similar method
       for putting digits of a number in the next place up.You can't do
       this with shifting,but you can multiply by 10 and add the value of
       the digit.
       Heres the program for converting decimal numbers.
       decin:                      ;converting a decimal number
              clr.l   d1           ;first erase D1
              move.l  #string,a0   ;the string to convert
              jsr     decinloop    ;test subroutine
              nop                  ;breakpoint here
       decinloop:
              bsr     digitin      ;convert digit
              cmp     #10,d0       ;test,if valid
              bcc     decinok      ;no,then done
              mulu    #10,d1       ;shift result
              add     d0,d1        ;insert nibble
              bra     decinloop    ;and continue
       decinok:
              rts                  ;end of conversion
       digitin:                    ;converting the nibble from (A0)
              clr.l   d0           ;erase D0
              move.b  (a0)+,d0     ;get digit,increment A0
              sub     #'0',d0      ;subtract $30
              rts
       string:dc.b '123456'        ;ASCII decimal string to convert


              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 NOP,to set the breakpoint in AssemPro select the correct
       address with the mouse and press thr right-Amiga-B keys.Select
       "Parameter-Output-numbers-Decimal"so the registers are displayed
       as decimal numbers.Then start the program and watch the contents
       of D1,the decimal value is placed in this register.
       This program can ONLY convert numbers upto 655350,although the hex
       conversion routine can go higher.Thats because the MULU command
       can only multiply 16-bit words.The last multiplication that can be
       done correctly is $FFFF*10--65535*10,which gives us the value
       655350.Normally this is a large enough range,so you won't
       complicate the program further.