MC 68000 ASSEMBLY LANGUAGE COURSE PART X by Mark van den Boer
Now all instructions of the 68000 have been explained it's time
to bring this knowledge into practice. This last part of the
course will deal with the subject of translating high-level
constructs to the equivalent assembler constructs. The C program-
ming language will be used as the high-level language which has
to be translated to assembler. A note to those of you who are
more familiar with Pascal or BASIC: litte imagination is needed
to deduct the similar constructs in Pascal and BASIC.
What now follows is a C-program which containing several commonly
used data- and control structures. The examples show you how to
translate these structures into assembler.
There should also be a file called M68000.DOC on your ST NEWS
disk. This file contains a quick-reference card containing all
instructions and allowed addressing modes. This reference card
has been made by Trustware, Inc. (an unregistered trademark of
Victor Langeveld). I am very grateful to Victor for allowing me
to include this card, since it's a rather tedious job to put
together such a card. One thing's for sure: this card is one of
the better of its kind and it's the most compact reference card
for the 68000 I've ever seen.
/*
A function. (Called a procedure or function in Pascal
and a subroutine in BASIC)
Note how parameters are passed in the assembly language
translation.
Als pay attention to how local variables are stored.
*/
int function (number, pointer)
int number;
char *pointer;
{
register int i, j;
char *c;
i = number - 1;
c = pointer;
c = "new Queensryche album: Operation Mindcrime";
/* Note how a string is stored */
return i; /* Note how a value is returned */
}
.text
function:
* offset of number = 8
* offset of pointer = 10
LINK A6,#-4
* save registers
MOVEM.L D6-D7,-(sp) * sp = A7
* i in D7
* j in D6
* offset of c = -4
* i = number - 1
MOVE.W 8(A6),D7
SUB.W #1,D7
* c = pointer
MOVE.L 10(A6),-4(A6)
* c = "new Queensryche album: Operation Mindcrime"
.data
L2:
.dc.b 'new Queensryche album: Operation Mindcrime',0
MOVE.L #L2,-4(A6)
* D0 is used for resulting values from functions
* return i; D0 is always used for the result of a function
MOVE.W D7,D0
* restore registers
MOVEM.L (sp)+,D6-D7
UNLK A6
* }
RTS
* global variables
.bss
* int i;
i: .ds.w 1
* char character
character: .ds.b 1
* int *i_pointer
i_pointer: .ds.l 1
* int i_array[10][5]
i_array: .ds.w 10*5
* one struct is in fact 3 bytes long, but for every structure
* 4 bytes are reserved. This is because the 68000 can only
* address words at even addresses.
* struct { /* this is the equivalent of a record in PASCAL */
* int i;
* char c;
* } structure[5];
structure: .ds.b 4*5
main()
{
/* assignment of a constant to a variable */
i = 9;
/* assignment of a constant to a variable */
character = 'c';
/* assignment of a constant to a variable */
i_pointer = &i;
/* watch how indexing of array is done */
/* integer is 2 bytes, so the address of
array-element [3][4] is:
(3 * 5 (the length of i_array[3]) + 4)
* 2 (size of an integer) = 38.
So the integer should be stored at i_array + 38.
*/
i_array[3][4] = *i_pointer;
/* Now the distance in bytes from the beginning of the
array must be computed during program execution,
in contrary to the previous example.
*/
i_array[i][i - 1] = 2;
/* Assignments to arrays of structures */
structure[1].i = 3;
structure[i].c = character;
/* expression evaluation and assignment */
i = i_array[0][0] * i_array[0][1] +
i_array[0][2] / i_array[0][3];
/* conditional statement */
if (i < i_array[i][i]) i = 1;
else i = 2;
/* while loop */
while (i <= 10) i++;
/* continue and break statements */
while (i++ <= 10) {
if (i != 4) continue;
else break;
}
/* for loop */
for (i = 4; i >= 0; i--) i_array[i][i] = i;
/* do loop */
do i++; while (i < 10 && i != 5);
/* switch statement; watch the application of a
jump-table. Pay special attention to how 'case 4'
which must 'default' is solved.
*/
switch (i) {
case 0:
i = 0;
break;
case 1:
i = 5;
break;
case 2:
case 3:
i = 7;
break;
case 5:
i = 1;
break;
default:
i = 2;
break;
}
/* switch statement;
watch how 'case 999' has destroyed the
jumptable-optimization.
*/
switch (i) {
case 0:
i = 0;
break;
case 1:
i = 5;
break;
case 2:
case 3:
i = 7;
break;
case 5:
i = 1;
break;
case 999:
/* This case should be tested seperately so
the assembler code can be more efficient.
*/
i = 100;
break;
default:
i = 2;
break;
}
/* manipulating bits */
i = i & 0x2345;
i = i | 0x2345;
i = i ^ 0x2345;
i = ~i;
i <<= i;
/* using the result of a function */
i = function(5, &character);
}
.text
main:
* Reserve 4 bytes. This way, when the first parameter for a
* function is pushed onto the stack, no pre-decrementing of sp
* has to be done. This 'trick' is used by the DRI-C-compiler.
LINK A6,#-4
* i = 9
MOVE.W #9,i
* character = 'c'
MOVE.B #'c,character
* i_pointer = &i
MOVE.L #i,i_pointer
* i_array[3][4] = *i_pointer
MOVE.L i_pointer,A0
MOVE.W (A0),38+i_array
* i_array[i][i - 1] = 2
MOVE.W i,D0 * compute byte offset from first element
MULS #10,D0
MOVE.W i,D1
SUB.W #1,D1
ASL.W #1,D1 * multiply by 2 because an int is 2 bytes
EXT.L D1
ADD.L D1,D0
ADD.L #i_array,D0
MOVE.L D0,A0 * move computed address to address-reg
MOVE.W #2,(A0)
* structure[1].i = 3
MOVE.W #3,4+structure
* structure[i].c = character
MOVE.W i,A0
ADD.L A0,A0
ADD.L A0,A0
* Two ADD operations are faster than a MUL and a MOVE
ADD.L #structure,A0
MOVE.B character,2(A0)
* i = i_array[0][0] * i_array[0][1] +
* i_array[0][2] / i_array[0][3];
MOVE.W i_array,D0
MULS 2+i_array,D0
MOVE.W 4+i_array,D1
EXT.L D1
DIVS 6+i_array,D1
ADD.W D1,D0
MOVE.W D0,i
* if (i < i_array[i][i]) i = 1;
* else i = 2;
MOVE.W i,D0
MULS #10,D0
MOVE.W i,D1
ASL.W #1,D1
EXT.L D1
ADD.L D1,D0
MOVE.L D0,A0
MOVE.L #i_array,A1
MOVE.W 0(A0,A1.L),D0
CMP i,D0
BLE L4
* i = 1
MOVE.W #1,i
BRA L5
L4:
* i = 2
MOVE.W #2,i
L5:
BRA L8
* while (i <= 10) i++;
* This loop has been optimized:
* one BRA instruction was saved by putting the test after the
* do-part. The label L5: BRA L8 takes care that the while
* condition is executed first at the beginning of the loop
L7:
ADD.W #1,i
L8:
CMP.W #10,i
BLE L7
* while (i++ <= 10) {
* if (i != 4) continue;
* else break;
* }
L6:
BRA L11
L10:
CMP.W #4,i
BNE L11
BRA L9
L11:
CMP #10,i
MOVE.W SR,D0 * save condition codes
ADD.W #1,i
MOVE D0,CCR * and restore
BLE L10
* for (i = 4; i >= 0; i--) i_array[i][i] = i
L9:
MOVE.W #4,i
BRA L14
L15:
MOVE.W i,D0
MULS #10,D0
MOVE.W i,D1
ASL.W #1,D1
EXT.L D1
ADD.L D1,D0
ADD.L #i_array,D0
MOVE.L D0,A0
MOVE.W i,(A0)
L13:
SUB.W #1,i
L14:
TST i
BGE L15
L12:
* do i++; while (i < 10 && i != 5);
L18:
ADD.W #1,i
L17:
CMP.W #10,i
BGE L10000
CMP.W #5,i
BNE L18
L10000:
* switch (i) {
* case 0:
* i = 0;
* break;
* case 1:
* i = 5;
* break;
* case 2:
* case 3:
* i = 7;
* break;
* case 5:
* i = 1;
* break;
* default:
* i = 2;
* break;
* }
L16:
MOVE.W i,D0
BRA L20
L21:
CLR.W i
BRA L19
L22:
MOVE.W #5,i
BRA L19
L23:
L24:
MOVE.W #7,i
BRA L19
L25:
MOVE.W #1,i
BRA L19
L26:
MOVE.W #2,i
BRA L19
L20:
* Test if i is in case-range
* if not goto default
* if in range compute address to jump to
CMP.W #5,D0
BHI L26
ASL.W #2,D0
MOVE.W D0,A0
ADD.L #L27,A0
MOVE.L (A0),A0
JMP (A0)
.data
L27:
* jumptable
.dc.l L21
.dc.l L22
.dc.l L23
.dc.l L24
.dc.l L26
.dc.l L25
.text
L19:
* switch (i) {
* case 0:
* i = 0;
* break;
* case 1:
* i = 5;
* break;
* case 2:
* case 3:
* i = 7;
* break;
* case 5:
* i = 1;
* break;
* case 999:
* /* This case should be tested seperately so
* the assembler code can be more efficient.
* */
* i = 100;
* break;
* default:
* i = 2;
* break;
* }
MOVE.W i,D0
BRA L29
L30:
CLR.W i
BRA L28
L31:
MOVE.W #5,i
BRA L28
L32:
L33:
MOVE.W #7,i
BRA L28
L34:
MOVE.W #1,i
BRA L28
L35:
MOVE.W #100,i
BRA L28
L36:
MOVE.W #2,i
BRA L28
BRA L28
L29:
EXT.L D0
MOVE.L #L37,A0
MOVE.W #6,D1
L38:
CMP.L (A0)+,D0
DBEQ D1,L38
MOVE.L 24(A0),A0
JMP (A0)
.data
L37:
* table of case values
.dc.l 0
.dc.l 1
.dc.l 2
.dc.l 3
.dc.l 5
.dc.l 999
.dc.l 0
* jump table
.dc.l L30
.dc.l L31
.dc.l L32
.dc.l L33
.dc.l L34
.dc.l L35
.dc.l L36
.text
L28:
* i = i & 0x2345
ANDI.W #$2345,i
* i = i | 0x2345
ORI.W #$2345,i
* i = i ^ 0x2345
EORI.W #$2345,i
* i = ~i
NOT.W i
* i <<= i
MOVE.W i,D1
MOVE.W D1,D0
ASL.W D1,D0
MOVE.W D0,i
* i = function (5, &character)
MOVE.W #51,(sp)
MOVE.L #character,-(sp)
ADDQ.L #4,sp
MOVE.W D0,i
L3:
UNLK A6
RTS
I would like to thank all people who have attended this course
and who have reflected in some way.
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.