02200000000801 1 2 9[...............................................................]0 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.