
Tutorial 2
==========


To enable you to assemble all the examples if you select decrunch from the
main menu and decrunch to df1: a directory Tutorial is created. All
examples and includes are copied into this directory for you.


 Last month we covered the fundamentals of coding and were introduced to the
RTS and MOVE instructions. Hopefully you tried all the examples and attempted
to answer the questions at the end. This month we will finish with the move
instruction and look at a few more. I am not going to give examples of each
instruction with all addressing modes as I have with move, I leave you to
experiment ( some pointers will be given to help you ).

MOVE contd.
-----------

 We finished up looking at Address Register Indirect addressing. Just to 
refresh your memories here is an example:

main      move.l      #my_age,a0   a0 points to my age
          move.w      #18,(a0)     use Addr Reg Indirect to make my_age=18
          rts
          
my_age    dc.w        10

 If we had a list of variables in memory, one after the other, and we wanted
to set each to a certain value say 0 we would require a means of increasing
the address held in a0 so that it would point to the next variable in the 
list after each variable had been set. Well it just so happens that there is
an addressing mode that does just that and it is called Address Register
Indirect With Post-Increment. What that mouthful means is after the 
instruction is executed the address in a0 is incremented so it points to the
next variable. An example:

main      move.l       #entry_1,a0     a0--> entry_1
          move.w       #0,d0           d0=0
          move.w       d0,(a0)+        entry_1=0, a0--> entry_2
          move.w       d0,(a0)+        entry_2=0, a0--> entry_3
          move.w       d0,(a0)+        entry_3=0, a0--> list_end        
          display_mem  entry_3         verify entry_3 has been altered
          rts                          finish
          
entry_1   dc.w         20
entry_2   dc.w         30
entry_3   dc.w         40
list_end
 
 Taking this step by step, the address of entry_1 is moved into register a0.
Zero is moved into register d0. The contents of register d0 ( 0 ) are copied
into memory at the address held in register a0 ( entry_1 ), a0 is then
increased by one word so it contains the address of entry_2. The contents of
register d0 ( 0 ) are copied into memory at the address held in register a0
( entry_2 ), a0 is then increased by one word so it contains the address of
entry_3. The contents of register d0 ( 0 ) are copied into memory at the
address held in register a0 ( entry_3 ), a0 is then increased by one word so
it contains the address list_end. The contents of memory at location entry_3 
is then displayed to verify that an alteration has taken place and the 
program then finishes.
 
 As you will see the assembler syntax for this addressing mode is (An)+ .
Note that the data movement in this example is of word length, so the CPU
adds 2 to the contents of the address register. If we had used byte or long
word the CPU would have added 1 or 4 respectively to the contents of the 
address register.

 A further example will demonstrate the power of this addressing mode. This
example is a solution to question 2 set last month:

main       move.l      #d_reg0,a0
           move.l      d0,(a0)+
           move.l      d1,(a0)+
           move.l      d2,(a0)+
           move.l      d3,(a0)+
           move.l      d4,(a0)+
           move.l      d5,(a0)+
           move.l      d6,(a0)+
           move.l      d7,(a0)+
           
           move.l      #0,d0
           move.l      d0,d1
           move.l      d0,d2
           move.l      d0,d3
           move.l      d0,d4
           move.l      d0,d5
           move.l      d0,d6
           move.l      d0,d7
           
           move.l      #d_reg0,a0
           move.l      (a0)+,d0
           move.l      (a0)+,d1
           move.l      (a0)+,d2
           move.l      (a0)+,d3
           move.l      (a0)+,d4
           move.l      (a0)+,d5
           move.l      (a0)+,d6
           move.l      (a0)+,d7
           
           rts
           

d_reg0     dc.l         0
d_reg1     dc.l         0
d_reg2     dc.l         0
d_reg3     dc.l         0
d_reg4     dc.l         0
d_reg5     dc.l         0
d_reg6     dc.l         0
d_reg7     dc.l         0

 Notice the use of addr reg indirect with post increment in the source field
to recover initial values at end of program ( You would never use this method
as there is a single command that achieves the same result as you will soon
discover ).

 The above method is fine for stepping forward through a list of variables,
but what if you want to be awkward and start at the bottom and step backwards
through the list. Well there is even an addressing mode to cover this, take a
deep breath before reading this aloud, Address Register Indirect with 
Pre-Decrement. This mode differs from the last in that the address register 
is decremented by 1, 2 or 4 BEFORE the data is operated on. The syntax for
this addressing mode is -(An) . Try to figure what the following example does
before reading the text that follows.

;eg_5.s

main      move.l      #table_s,a0
          move.l      #table_e,a1
          
          move.w      (a0),d0
          move.w      -(a1),(a0)+
          move.w      d0,(a1)
          
          move.w      (a0),d0
          move.w      -(a1),(a0)+
          move.w      d0,(a1)
          
          rts
          
table_s   dc.w        10
          dc.w        20
          dc.w        30
          dc.w        40
table_e

 This example can be found in the Tutorial directory and is called eg_5.s

 Did you figure it out ? The program reverses the order of the numbers in the
table ( inverts the table ). So the table starts off as 10,20,30,40 and ends
up as 40,30,20,10. Load the example into GenAm and either use monam or the
display_mem macro to verify this.
 
 The table used above could have been sprite data and this short routine 
would have flipped the image vertically. I popped that in so you will realise
that the techniques you are now learning will be useful later and that these
silly routines really are worth studying.

 The above example firsts moves the address of the table start into register
a0 and the address of the table finish into register a1. Next the first entry
in the table is moved into register d0 for temporary storage. The last entry
in the table is copied into the first entry, the top pointer is increased by
one word so it points to the second entry and the bottom pointer is decreased
by one word so it points to the last entry. Now the temporary copy of the
first entry is copied into the last entry. The last three steps are repeated
to swap the middle two entries and the program finishes.

 Now lets look at an addressing mode that you will see all the time in Amy 
code, Address Register Indirect With 16 Bit Displacement. The assembler syntax
is : d16(An). An is an address register that points to a memory location and
d16 is an immediate, signed 16 bit number. The effective address is calculated
by adding the 16 bit value to An before operating on data. The content of An
is not changed by this instruction. To see how we could use this look at the
following memory initialization:

; Program flags area

flags
move_up        dc.b     0        disp=0
move_left      dc.b     0        disp=1
move_missile   dc.b     0        disp=2
collision      dc.b     0        disp=3
killed         dc.b     0        disp=4

 Notice that flags is not followed with a dc directive. This is not a mistake,
but a way of assigning flags and move_up to the same memory location.

 Now we want to write a chunk of code that is going to examine these flags.
Obviously we could use the label for a flag and immediate addressing, but a
more elegant and faster method is to use a 16 bit displacement. First we need
to get the address of the start of the flags area into an address register:

               move.l      #flags,a3
               
 Now suppose a collision has just occurred and you need to set the collision
flag to $05. The displacement from flags of collision is 3 bytes so we need
to use the following:

               move.w    #$05,$3(a3)
               
 This works fine, but as you will see from the last instruction it is difficult
to see where the data is being moved to, $3(a3) does not mean much without
looking at the data definitions. To make your code more readable you could use
the ds directive, this sets aside consecutive memory locations of the size
indicated. To clarify, in the above example we need 5 bytes for our flags so
we would reserve this as follows:

flags          ds.b      5

 Now the memory for the flags has been reserved we can use equ's to define the
offset to each individual flag from the start of this table as follows:

move_up        equ     0        disp=0
move_left      equ     1        disp=1
move_missile   equ     2        disp=2
collision      equ     3        disp=3
killed         equ     4        disp=4

 We can now write a neat and readable piece of code to set the collision flag
to $05 as follows:

               move.l  #flags,a3
               move.w  #$05,collision(a3)
               rts
               
; Data and equates area

flags          ds.b    5

move_up        equ     0        disp=0
move_left      equ     1        disp=1
move_missile   equ     2        disp=2
collision      equ     3        disp=3
killed         equ     4        disp=4

 From this example you will see that using this method you soon end up with
a list of equ's that is longer than the code itself, to overcome this most
programmers will write all their equ's into a separate file which they then
include in the main program, this stops the main program from becoming
cluttered with hundreds of equates. If is worth noting that having loads of
equates does not increase the size of the final assembled code, remember that
the equ directive is an assembler instruction not a 68000 instruction.

 To include a file in your main program you use the include directive, this
should be followed by the name of the file to include. When the assembles is
invoked it will merge the named file into the main file at the point where
the directive was given. It is usual to put includes at the start of your code

 To demonstrate this properly I have taken the above example and added a few
more equates for the type of collision, as hit_border makes more sense than
#$05 and leaves the code more readable still. The following program is eg_6.s
and is on the disk along with the equate file.

Here is the main program ( notice that there are other files included ).
************************************************************************

 opt o+,ow- tell Devpac to optimize

; The next four lines bring in the startup code which handles the CLI
;or Workbench correctly and then opens a console window for I/O.
;macros.i includes the macro routines explained in the text.
;subroutines.i includes the routines required for the macros.

 incdir 'source4:tutorial/'
 include 'startup.i'
 include 'macros.i'
 include 'subroutines.i'
 
; Your program should start at the label main and should end with an
;rts instruction.  M.Meany  June 1990 
 
 include          'equates.i'     equ's for this program

main move.l            #flags,a3
                move.b            #hit_border,collision(a3)
                move.w            #0,d0
                move.b            collision(a3),d0
                display_d0
                move.b            #hit_alien1,collision(a3)
                move.b            collision(a3),d0
                display_d0
                rts
                
flags           ds.b              5

***************************************************************************

 The directive incdir that precedes the includes tells the assembler which
directory too look in for the include files. Here is the equates file:

***************************************************************************

; equ's to be used in second steps examples, M.Meany  August 1990

move_up        equ     0        disp=0
move_left      equ     1        disp=1
move_missile   equ     2        disp=2
collision      equ     3        disp=3
killed         equ     4        disp=4

hit_missile    equ     1
hit_alien1     equ     2
hit_alien2     equ     3
hit_star       equ     4
hit_border     equ     5

***************************************************************************

 The flags used above were only one byte in size, this made the calculation
of the displacement easy. If we had used word size flags then we would need
to use the following:

flags          ds.w     5        space for 5 word flags

move_up        equ     0        disp=0
move_left      equ     2        disp=2
move_missile   equ     4        disp=4
collision      equ     6        disp=6
killed         equ     8        disp=8

 So remember that the displacement is the number of bytes from the start of
the list.

 Now to the last addressing mode I am going to cover for now ( pc relative
will come later ), Address Register Indirect With 8 Bit Index. The assembler
syntax is d8(An,Rn). Again An holds the address of the start of the list and
d8 is a displacement value, only 8 bits this time. Rn can be either a data or
address register and is a further displacement value, so the effective address
is calculated by adding d8 and Rn to the contents of An. Why bother with more
displacements when I can already use a 16 bit value ? well simply put you can
use d8 and Rn to access values stored in a table. Rn is used to hold the row
number and d8 is the column number. This allows all the values in a 
particular row or column to be accessed easily. Here is a very simple example
to access just one value from a table 5 columns wide and 3 rows deep:

main           move.l        #table,a0       a0--> start of table
               move.w        #5,d0          d0=disp to 2nd row (5 bytes/row)
               move.b        2(a0,d0),d1    d1=3rd value in 2nd row
               display_d1
               move.w        #10,d0         d0=disp to 3rd row (5 bytes/row)
               move.b        2(a0,d0),d1    d1=3rd value in 3rd row
               display_d1
               rts
               
table          dc.b          1,2,3,4,5
               dc.b          2,4,6,8,10
               dc.b          3,6,9,12,15
               
 This is eg_7.s and can be found on the disk.
 
 MOVEM
 -----
 
 Syntax: MOVEM.s   register list,DESTINATION
         MOVEM.s   SOURCE,register list
         
 .s is either .w or .l byte operations not allowed.
 
 This is the move multiple registers command. The contents of selected 
registers can be copied into memory or loaded from memory. The list of
registers to operate on is built up by separating individual registers with
a / also a - may be used to include registers ie d3-d7 would specify the
registers d3,d4,d5,d6,d7. Address registers may be included in the list as
well. This instruction is often used at the start and end of a subroutine
so that all values are left intact after the subroutine has finished.

 Here is how to move all values on the stack and then retrieve them again:
 
main           movem.l       d0-d7/a0-a7,-(sp)

                  "
                  "
                  "
          some other code here
                  "
                  "
                  "
               movem.l       (sp)+,d0-d7/a0-a7
               rts
               
 Notice we use pre decrement to put values on the stack, this is because the 
stack grows down through memory. When we take a value off the stack we must
adjust the stack pointer sp so that it points to the next value, to do this
we must use post increment. This is true wether moving individual values on
and off the stack or register lists.

 MOVEQ
 -----
 
 Syntax: MOVEQ.l  #data,Dn
 
 This is one of those go faster instructions that the 68000 is famous for. You
should use this instruction whenever you want to move a small value into a 
data register. The data must be between -127 and 128. This is much faster than
using a straight forward move instruction.

Example        moveq.l       #105,d0

 Well that is the move instruction finished with for now. Lets look at some
simple arithmetic.

 Binary Arithmetic, A Discussion
 -------------------------------
 
  A computer always applies arithmetic operators on multiples of 8 bits. If
the operation is addition or multiplication then on occasions the result is
going to be larger than the size of data being worked on. Consider the 
following example of adding two bytes together:


 255           11111111
+  5           00000101
 ---           --------
 260          100000100
              ^
 The bit marked with the ^ is stored in the carry and extend flags (C and X)
the remaining 8 bits are stored in the destination. This is true for all
forms of ADD and also the same principle is true for word and long word
additions.

 ADD
 ---
 
 Syntax: ADD.s  SOURCE,DESTINATION
 
 Either source or destination MUST be a data register. Size of addition
may be byte, word or long word.

 The source value is added to the destination value and the result is put in
the destination. To see how the internal flags (condition codes) are effected
by this ( and all other ) instruction see the appendix at the end of this 
tutorial.

 An example:
 
main           moveq.l       #10,d0
               add.w         #15,d0
               display_d0
               rts
               
 Not exactly earth shattering is it. Still the value 10 is moved into register
d0 and then 15 is added to it, the result being stored in d0. When we display
d0 we can see the result, 25.

 For a more practical example lets look again at example eg_7.s where we 
displayed values from the same column but different rows of a table. As there
are five entries in each row adding 5 to d0 in this example will move us to
same column of the next row, here is the modified example eg_8.s:

main           move.l        #table,a0       a0--> start of table
               moveq.l       #0,d0          d0=disp to 1st row 
               move.b        2(a0,d0),d1    d1=3rd value in 1st row
               display_d1
               add.w         #5,d0          d0=disp to 2nd row (5 bytes/row)
               move.b        2(a0,d0),d1    d1=3rd value in 2nd row
               display_d1
               add.w         #5,d0          d0=disp to 3rd row (5 bytes/row)
               move.b        2(a0,d0),d1    d1=3rd value of 3rd row
               display_d1
               rts
               
table          dc.b          1,2,3,4,5
               dc.b          2,4,6,8,10
               dc.b          3,6,9,12,15
               
 Even better still suppose that you wanted to add 10 to each value in column
3 of the table. We will start off by putting this value into d1 and then use
add to step us through the table and to add 10 to the relevant entries:

; eg_8.s
 
main           move.l        #table,a0      a0--> start of table
               moveq.l       #0,d0          d0=disp to 1st row (5 bytes/row)
               moveq.l       #10,d1         d1=value to add to entries
               moveq.l       #0,d2          d2 used to display results
               move.b        2(a0,d0),d2    show value before addition
               display_d2
               add.b         d1,2(a0,d0)    add 10 to 3rd entry,1st row
               move.b        2(a0,d0),d2    show value after addition
               display_d2
               add.b         #5,d0          d0=disp to 2nd row
               move.b        2(a0,d0),d2    show value before addition
               display_d2
               add.b         d1,2(a0,d0)    add 10 to 3rd entry,2nd row
               move.b        2(a0,d0),d2    show value after addition
               display_d2
               add.b         #5,d0          d0=disp to 3rd row
               move.b        2(a0,d0),d2    show value before addition
               display_d2
               add.b         d1,2(a0,d0)    add 10 to 3rd entry,3rd row
               move.b        2(a0,d0),d2    show value after addition
               display_d2
               rts
               
table          dc.b          1,2,3,4,5
               dc.b          2,4,6,8,10
               dc.b          3,6,9,12,15

 Register d2 is used in the above example to display the contents of each
memory location before and after each addition. This complicates the code
a little so once you have seen it run delete all lines that have comments
starting with an *. This will make the code more readable.

 ADDA
 ----
 
 Syntax: ADDA.s  SOURCE,An
 
 .s is either .w or .l
 
 Basically the same as add except the destination is always an address
register. I will leave you to experiment with this one.

 ADDI
 ----
 
 Syntax: ADDI.s  #data,DESTINATION
 
 .s is either .b .w or .l
 
 Use this to add immediate data to the destination which can be any register
or memory location.

 For example to add 5 to a variable in memory called counter:
 
main           addi.w        #5,counter
               display_mem   counter
               rts
               
counter        dc.w          1895

 ADDQ
 ----
 
 Syntax: ADDQ.s  #data,DESTINATION
 
 Add quickly, yet another go faster instruction. Use the same as ADDI except
the immediate data must be between 0 and 8. So the above example could be
speeded up by using:

main           addq.w        #5,counter
               display_mem   counter
               rts
               
counter        dc.w          1895

 ADDX
 ----
 
 Syntax: ADDX.s Dn,Dn
         ADDX.s -(An),-(An)
         
 .s is either .b .w or .l
 
 Add with extend. The extend flag is set if a previous operation produced a
value to large for the data type being used, when using addx the value of
this flag ( 0 or 1 ) is also added to the destination.

 ADDX gives us a means of using the extend bit. For example if you had a 
byte counter and you wanted to know how many times it had clocked itself
you could use d0 to hold the counter, d1 to store the number of times that
d0 is clocked which is initially 0. Whenever you add a value to d0 you could
then use ADDX.b d2,d1 to keep count of all the times d0 exceeds 255 (d2=0).
This works since if after increasing d0 the value is less than 255 then X=0,
so 0+0 is added to d1. If d0 exceeds 255 after being incremented then X=1 so
0+1 will be added to d1. Here is an example:

main           moveq.l       #0,d0     zero all registers used
               move.l        d0,d1
               move.l        d0,d2
               addi.b        #150,d0
               addx.w        d2,d1
               display_d1
               addi.b        #150,d0
               addx.w        d2,d1
               display_d1
               addi.b        #150,d0
               addx.w        d2,d1
               display_d1
               addi.b        #150,d0
               addx.w        d2,d1
               display_d1
               rts
               
 SUBTRACTION
 -----------
 SUB.s  SOURCE,DESTINATION
 SUBA.s SOURCE,An
 SUBI.s #DATA,DESTINATION
 SUBQ.s #DATA,DESTINATION
 SUBX.s Dn,Dn
 SUBX.s -(An),-(An) 
 
 To save time and space I am not going to run through each of these commands
as in operation they are similar to the add commands except the source is
subtracted from the destination ( along with the x flag for subx ) and the
result is stored in the destination.

 A few points to mention are if the source is larger than the destination and
a borrow has to be made then the carry flag is set and the x flag. Also
the result will be a negative number.

 The following example will demonstrate:
 


 opt o+,ow- tell Devpac to optimize

; The next four lines bring in the startup code which handles the CLI
;or Workbench correctly and then opens a console window for I/O.
;macros.i includes the macro routines explained in the text.
;subroutines.i includes the routines required for the macros.

 incdir 'source4:tutorial/'
 include 'startup.i'
 include 'macros.i'
 include 'subroutines.i'
 
; Your program should start at the label main and should end with an
;rts instruction.  M.Meany  June 1990 
 
main moveq.l #50,d0
 subi.b #60,d0
 display_d0
 rts
 
 Thats it for this month. Next month we will look at multiplication, division
and loops. Until then experiment with what you have learned so far.

                                               Mark August 1990.

Appendix 1 : Effect on condition codes
                                               
     - = not affected    0 = cleared         * = modified accordingly
     1 = set             u = undetermined    P = privileged

Mnemonic         X N Z V C      P
==================================
ABCD             * u * u *
ADD              * * * * *
ADDA             - - - - -
ADDI             * * * * *
ADDQ             * * * * *
ADDX             * * * * *
AND              - * * 0 0
ANDI             - * * 0 0
ASL, ASR         * * * * *
Bcc              - - - - -
BCHG             - - * - -
BCLR             - - * - -
BRA              - - - - -
BSET             - - * - -
BSR              - - - - -
BTST             - - * - -
CHK              - * u u u
CLR              - 0 1 0 0
CMP              - * * * *
CMPA             - * * * *
CMPI             - * * * *
CMPM             - * * * *
cpGEN            - - - - -
DBcc             - - - - -
DIVS             - * * * 0
DIVU             - * * * 0
EOR              - * * 0 0
EORI             - * * 0 0
EORI CCR         * * * * *
EORI SR          * * * * *
EXG              - - - - -
EXT              - * * 0 0
EXTB             - * * 0 0
ILLEGAL          - - - - -
JMP              - - - - -
JSR              - - - - -
LEA              - - - - -
LINK             - - - - -
LSL, LSR         * * * 0 *
MOVE             - * * 0 0
MOVEA            - - - - -
MOVE to CCR      * * * * *
MOVE from SR     - - - - -      P
MOVE to SR       * * * * *      P
MOVE USP         - - - - -      P
MOVEM            - - - - -
MOVEPs           - - - - -
MOVEQd           - * * 0 0
MULS             - * * 0 0
MULU             - * * 0 0
NBCD             * u * u *
NEG              * * * * *
NEG              * * * * *
NOP              - - - - -
NOT              - * * 0 0
OR               - * * 0 0
ORI              - * * 0 0
PEA              - - - - -
RESET            - - - - -      P
ROL, ROR         - * * 0 *
ROXL, ROXR       - * * 0 *
RTE              - - - - -      P
RTR              * * * * *
RTS              - - - - -
SBCD             * u * u *
Scc              - - - - -
STOP             - - - - -
SUB              * * * * *
SUBA             - - - - -
SUBI             * * * * *
SUBQ             * * * * *
SUB              * * * * *
SWAP             - * * 0 0
TAS              - * * 0 0
TRAP             - - - - -
TRAPV            - - - - -
TST              - * * 0 0
UNLK             - - - - -


