A complete (I think) set of code generator macros to generate published op-codes
Révision | b281f65ed7aeceaaf5feefa53f71dc6a4c0a99ac |
---|---|
Taille | 11,545 octets |
l'heure | 2020-10-08 20:37:04 |
Auteur | Joel Matthew Rees |
Message de Log | As it survived the trip across the Mac and ocean
|
Notes for the code generator for the 68000/68010 by Joel Matthew Rees
This was written for CS431 by Joel Matthew Rees, at BYU, January 1988.
Assigned by the author to the public domain, February 2000, Takino, Japan.
joel_rees@sannet.ne.jp
http://www.page.sannet.ne.jp/joel_rees
It isn't actually a code generator, just the lowest level piece thereof.
It provides embedded assembly, such as might be used in direct code generation.
code_array and data_array are provided as default assembly target spaces. I
wrote these before I had the C skills to generalize the targeting. Now that I
know several ways, I don't have the motivation or the time.
Unsigned short integer should be 16 bits for native machine compile.
Not designed for cross-compiling.
The include file should be named code.h, not 68kcode.h. If you like the
latter name, change the include line in code.c (68kcode.c) to
#include "68kcode.h"
Much of the code generation is done through C pre-processor macros, so code.c
and code.h should be used together in most situations.
Parameter order and number is intended to reflect the assembler syntax. The
include file describes the use of several of the generation functions, and
the macro definitions in the include file may be examined to understand
most of the rest.
Definition of external functions is controlled in the include file by the
variable "IS_code". "IS_code" is defined in the file "code.c" only, by which
convention the external definition of the code generator functions is
suppressed while "code.c" is compiling. All files which use code.o should
leave "IS_code" undefined.
***************************************************************************
A general M68000/M68010 assembler operand has one of the forms below:
(one size specified for both operands)
n for an absolute address n
mode: E_ABSw, E_ABSl (word and long addresses)
arguments: mode and value
#n for an immediate value n
mode: E_IMM
arguments: mode and value
Dn or An for an address or data register n
mode: E_Dn, E_An (with register number masked in: E_Dn|Dreg, E_An|Areg)
arguments: mode|reg only
(An) for indirection through an address register
mode: E_In (with register number masked in: E_In|Areg)
arguments: mode|Areg only
-(An) or (An)+ for auto-indirection
mode: E_InDEC, E_InINC (with register number masked in: E_InDEC|Areg)
arguments: mode|Areg only
d(An) for indirection with constant displacement d
mode: E_InOFF (with register number masked in: E_InOFF|Areg)
arguments: mode|Areg and offset
d(PC) for pc-relative by constant displacement d
mode: E_pcOFF
arguments: mode and offset
d(An, Rm.l) for complex indexing with index register Rm of length l,
(Rm is either a data or address register, l is either word or long.)
mode: E_AnXi (with address register number masked in: E_AnXi|Areg)
arguments: mode|Areg, index register, and offset
d(PC, Rm.l) for pc-relative indexing
mode: E_pcXi
arguments: mode, index register, and offset
Move multiple registers (push, pop, et. al.) uses a register list operand:
Rm/Rn/../Rz Rm -- Rz can be any data or address registers
mode: not relevant for register list operand (see macro definition)
arguments: register list with source/destination arguments
Special operands include the following:
CCR the condition code register (low byte of SR)
mode: E_CCR with size = CBYTE
SR the status register (CCR and interrupt, processor state bits)
mode: E_SR with size = CWORD
SP equivalent to A7, the hardware or subroutine stack pointer
mode: E_An|7, E_In|7, E_InINC|7, E_InDEC|7, E_InOFF|7, E_InXi|7
SSP system stack pointer (A7 in supervisor state)
mode: irrelevant
USP user stack pointer (A7 in user state)
mode: E_USP (MOVEC, MOVE to/from USP)
VBR M68010 virtual machine vector base register
mode: E_VBR (MOVEC)
SFC and DFC M68010 source and destination function code registers
mode: E_SFC and E_DFC (MOVEC)
(for moving from or to alternate memory spaces)
The code generator arguments and definitions for the above are as follows:
sz: the operand size
CBYTE, CWORD, CLONG
sm or dm: the mode argument, with the primary register number r masked in
E_Dn, E_An, et. al.
sx or dx: the index register number for complex and pc-relative indexing
iDw(n), iDl(n), iAw(n), iAl(n) -- word (16 bit) and long (32 bit)
svo, dvo, sao, dao, val, etc.: the value, address, or offset, when appropriate
no specific relevant definitions
regs: a register list for MOVEM (push, pop, etc.)
_PSH(r), _POP(r), _PUL(r), _CTL(r), _TBL(r)
**************************************************************************
Some examples of usage:
( ***** Not intended to be an assembler manual! *****
I recommend the purchase of the M68000 16/32-bit Microprocessor Programmer's
Reference Manual by Motorola.)
int err; /* all functions return an integer error code, non-zero on error */
/* to generate a register to register add: */
/* ADD.w D0, D1 */
err = genADD(CWORD, E_Dn|0, 0, 0, E_Dn|1, 0, 0);
/* to generate a dynamic register shift */
/* ASR.l D0, D1 arithmetic shift left D1 by count in D0 */
err = genASR(CLONG, E_Dn|0, 0, E_Dn|1, 0, 0);
/* to generate a dynamic bit operation */
char FLAG;
/* BCHG D2, FLAG toggle bit of memory numbered in D2 */
err = genBCHG(E_Dn|2, E_ABSl, 0, (long) &FLAG);
/* to generate a backward branch */
CODE *targ1;
/* . . . . */
/* TARGET EQU * */
targ1 = cap;
/* . . . . */
/* BVC TARGET */
err = genB(cc_VC, targ1); /* automatically chooses short or long */
/* to generate a short forward branch */
CODE *b1;
/* . . . . */
/* BNE TARGET */
b1 = cap;
err = genB(cc_NE, cap); /* small offset forces short branch */
/* . . . . */
/* TARGET EQU * */
err = fillB(CBYTE, b1);
/* to generate a long forward branch */
CODE *b1;
/* . . . . */
/* BNE TARGET */
b1 = cap;
err = genB(cc_NE, cap[128]); /* large offset forces long branch */
/* . . . . */
/* TARGET EQU * */
err = fillB(CWORD, b1);
/* err = fillB(CWORD, label) will fill in a DBcc forward reference */
/* to generate a 64 bit binary integer add in memory: N2 = N2 + N1 */
CODE *N1, *N2;
/* . . . . */
/* N1 DC.w $0012 initial contents of N1, N2
DC.w $3289
DC.w $5543
DC.w $1287
N2 DC.w $0404
DC.w $9840
DC.w $1743
DC.w $6637 */
N1 = dap; /* dap is the data allocation pointer */
*dap++ = 0x12; *dap++ = 0x3289; *dap++ = 0x5543; *dap++ = 0x1287;
N2 = dap;
*dap++ = 0x404; *dap++ = 0x9840; *dap++ = 0x1743; *dap++ = 0x6637;
/* LEA N1+8, A0
LEA N2+8, A1 */
err = genLEA(E_ABSl, 0, (long) N1+4, E_An|0);
err = genLEA(E_ABSl, 0, (long) N2+4, E_An|1);
/* AND #$ee, CCR clear carry and extend bits */
err = genAND(CBYTE, E_IMM, 0, ~0x11, E_CCR, 0, 0);
/* ADDX.l -(A0), -(A1)
ADDX.l -(A0), -(A1) */
err = genADDX(CLONG, E_InDEC|0, E_InDEC|1);
err = genADDX(CLONG, E_InDEC|0, E_InDEC|1);
/* to generate a 32 bit pc relative call using no registers */
CODE *jump, *ret;
long offs;
/* . . . . */
/* PEA RTLBL-*-2(PC) push return address */
jump = cap; /* cap is code allocation pointer */
err = genPEA(E_pcOFF, 0, 0); /* will fill in later */
/* PEA 2(PC) push run-time PC */
err = genPEA(E_pcOFF, 0, 2);
/* ADD.L #TARGET-*, (A7) add constant offset to top of stack */
ret = cap;
err = genADD(CLONG, E_IMM, 0, 0, E_In|7, 0, 0); /* fill in later */
/* RTS pop destination into PC */
err = genRTS;
/* RTLBL EQU * */
err = fillpcR(E_pcOFF, jump); /* calculate return offset and fill in */
/* . . . . */
/* TARGET EQU * */
offs = ((long) cap) - ((long) ret); /* calculate constant offset */
ret[1] = (short) (offs >> 16); /* fill it in */
ret[2] = (short) offs;
/* to generate a PC relative call through a relocatable call table: */
/* table index in D0 */
CODE *tblent, *tblbase;
long offs;
/* . . . . */
/* ASL.w #2, D0 convert index to offset */
err = genASL(CWORD, E_IMM, 2, E_Dn|0, 0, 0);
/* MOVE.l JMPTBL-*-2(PC, D0.w), A1 get table entry */
tblent = cap;
err = genMOVEA(CLONG, E_pcXi, iDw(0), 0, E_An|1); /* fill in later */
/* JMP 2(PC, A1.l) add entry to PC, with adjustment */
err = genJMP(E_pcXi, iAl(1), 2);
/* JMPTBL EQU * */
err = fillpcR(E_pcXi, tblent); /* calculate table offset and fill */
tblbase = cap;
/* DC.l CODE1-JMPTBL */
offs = ((long) CODE1) - ((long) tblbase);
err = gcode((short) (offs >> 16));
err = gcode((short) offs);
/* DC.l CODE2-JMPTBL etc. */
/* Move a constant length character string at s1 to s2, absolute addressed, */
/* one byte at a time (there are other ways to do this): */
char s1[STRLEN];
char s2[STRLEN];
CODE *L1;
/* . . . */
/* MOVE.w #STRLEN, D0 */
err = genMOVE(CWORD, E_IMM, 0, STRLEN, E_Dn|0);
/* MOVE.l #s1, A0 */
err = genMOVE(CLONG, E_IMM, 0, (long) s1, E_An|0, 0, 0);
/* MOVE.l #s2, A0 */
err = genMOVE(CLONG, E_IMM, 0, (long) s2, E_An|1, 0, 0);
/* L1 MOVE.b (A0)+, (A1)+ */
L1 = cap;
err = genMOVE(CBYTE, E_InINC|0, 0, 0, E_InINC|1, 0, 0);
/* DBRA D0, L1 */
err = genDB(cc_F, E_Dn|0, L1); /* Decrement and Branch until False */
/* there are several ways to push and pop (pull) registers: */
/* using the auto modes: */
/* MOVE.l D1, -(A7) push D1 */
err = genMOVE(CLONG, E_Dn|1, 0, 0, E_InDEC|7, 0, 0);
/* MOVE.b (A7)+, CCR pop (pull) the condition codes */
err = genMOVE(CBYTE, E_InINC|7, 0, 0, E_CCR, 0, 0); /* pops a word */
/* MOVE.b (A4)+, CCR pop (pull) the condition codes */
err = genMOVE(CBYTE, E_InINC|4, 0, 0, E_CCR, 0, 0); /* pops a byte */
/* push an immediate value: */
/* MOVE.w #VALUE, -(A7) */
err = genMOVE(CWORD, E_IMM, 0, VALUE, E_InDEC|7, 0, 0);
/* using the MOVE Multiple instruction: */
/* MOVEM.l A1/A3/A5/D2/D4, -(A7) push a register list */
err = genPUSH(CLONG, _PSH(E_An|1)|_PSH(E_An|3)|_PSH(E_An|5)
|_PSH(E_Dn|2)|_PSH(E_Dn|4), E_InDEC|7, 0, 0);
/* MOVEM.w D0-D4, (A6)+ pop a register list */
err = genPULL(CWORD, E_InINC|6, 0, 0, _PUL(E_Dn|0)|_PUL(E_Dn|1)
|_PUL(E_Dn|2)|_PUL(E_Dn|3)|_PUL(E_Dn|4));
/* load a register list from a control table */
/* MOVE.w PROCNUM, D6
MUL #ENTSIZE, D6 convert entry number to offset
LEA CTLTBL, A5
MOVE.w 0(A5, D6.l), SR
MOVEM.l 6(A5, D6.l), A0-A7/D0-D7 wipes out index regs */
err = genMOVE(CWORD, E_ABSw, 0, (long) PROCNUM, E_Dn|6, 0, 0);
err = genMUL(E_IMM, 0, ENTSIZE, E_Dn|6);
err = genLEA(E_ABSl, 0, (long) CTLTBL, E_An|5);
err = genMOVE(CWORD, E_AnXi|5, iDl(6), 0, E_SR, 0, 0);
err = genPULL(CLONG, E_AnXi|5, iDl(6), 6, 0xffff);
/* note that A7 is the hardware (flow of control) stack pointer. Any
time a byte is pushed referencing A7, a word actually goes on the stack with
its most significant byte clear.
For the above methods of pushing and popping registers, any address register
may be used, but only A7 forces all pushes to word boundaries. */
**************************************************************************
On the AT&T PC-7300 (UNIX PC), sdb misreads certain obscure combinations
of machine codes and addressing modes:
Scc to an absolute address (sdb says it's TRAPcc -- 68020 code)
BTST Dn, #val
ILLEGAL
JSR to an absolute address (sdb says it's JSR immediate)
The code generator generated these codes correctly when I released it.
Joel Matthew Rees
(801) 486-4812 (Salt Lake City).