Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 7692 → Rev 7693

/programs/develop/oberon07/Source/MSP430.ob07
0,0 → 1,1793
(*
BSD 2-Clause License
 
Copyright (c) 2019, Anton Krotov
All rights reserved.
*)
 
MODULE MSP430;
 
IMPORT IL, LISTS, REG, CHL := CHUNKLISTS, ERRORS, FILES, WRITER,
UTILS, C := CONSOLE, PROG, RTL := MSP430RTL;
 
 
CONST
 
minRAM* = 128; maxRAM* = 10240;
minROM* = 2048; maxROM* = 49152;
 
minStackSize = 64;
 
IntVectorSize* = RTL.IntVectorSize;
 
PC = 0; SP = 1; SR = 2; CG = 3;
 
R4 = 4; R5 = 5; R6 = 6; R7 = 7;
 
IR = 13; HP = 14; BP = 15;
 
ACC = R4;
 
opRRC = 1000H; opSWPB = 1080H; opRRA = 1100H; opSXT = 1180H;
opPUSH = 1200H; opCALL = 1280H; opRETI = 1300H;
 
opMOV = 04000H; opADD = 05000H; opADDC = 06000H; opSUBC = 07000H;
opSUB = 08000H; opCMP = 09000H; opDADD = 0A000H; opBIT = 0B000H;
opBIC = 0C000H; opBIS = 0D000H; opXOR = 0E000H; opAND = 0F000H;
 
opJNE = 2000H; opJEQ = 2400H; opJNC = 2800H; opJC = 2C00H;
opJN = 3000H; opJGE = 3400H; opJL = 3800H; opJMP = 3C00H;
 
sREG = 0; sIDX = 16; sINDIR = 32; sINCR = 48; BW = 64; dIDX = 128;
 
NOWORD = 10000H;
 
RCODE = 0; RDATA = 1; RBSS = 2;
 
je = 0; jne = je + 1;
jge = 2; jl = jge + 1;
jle = 4; jg = jle + 1;
jb = 6;
 
 
TYPE
 
ANYCODE = POINTER TO RECORD (LISTS.ITEM)
 
offset: INTEGER
 
END;
 
WORD = POINTER TO RECORD (ANYCODE)
 
val: INTEGER
 
END;
 
LABEL = POINTER TO RECORD (ANYCODE)
 
num: INTEGER
 
END;
 
JMP = POINTER TO RECORD (ANYCODE)
 
cc, label: INTEGER;
short: BOOLEAN
 
END;
 
CALL = POINTER TO RECORD (ANYCODE)
 
label: INTEGER
 
END;
 
COMMAND = IL.COMMAND;
 
RELOC = POINTER TO RECORD (LISTS.ITEM)
 
section: INTEGER;
WordPtr: WORD
 
END;
 
 
VAR
 
R: REG.REGS;
 
CodeList: LISTS.LIST;
RelList: LISTS.LIST;
 
mem: ARRAY 65536 OF BYTE;
 
Labels: CHL.INTLIST;
 
IV: ARRAY RTL.LenIV OF INTEGER;
 
IdxWords: RECORD src, dst: INTEGER END;
 
 
PROCEDURE EmitLabel (L: INTEGER);
VAR
label: LABEL;
 
BEGIN
NEW(label);
label.num := L;
LISTS.push(CodeList, label)
END EmitLabel;
 
 
PROCEDURE EmitWord (val: INTEGER);
VAR
word: WORD;
 
BEGIN
IF val < 0 THEN
ASSERT(val >= -32768);
val := val MOD 65536
ELSE
ASSERT(val <= 65535)
END;
NEW(word);
word.val := val;
LISTS.push(CodeList, word)
END EmitWord;
 
 
PROCEDURE EmitJmp (cc, label: INTEGER);
VAR
jmp: JMP;
 
BEGIN
NEW(jmp);
jmp.cc := cc;
jmp.label := label;
jmp.short := FALSE;
LISTS.push(CodeList, jmp)
END EmitJmp;
 
 
PROCEDURE EmitCall (label: INTEGER);
VAR
call: CALL;
 
BEGIN
NEW(call);
call.label := label;
LISTS.push(CodeList, call)
END EmitCall;
 
 
PROCEDURE bw (b: BOOLEAN): INTEGER;
RETURN BW * ORD(b)
END bw;
 
 
PROCEDURE src_x (x, Rn: INTEGER): INTEGER;
BEGIN
IdxWords.src := x
RETURN Rn * 256 + sIDX
END src_x;
 
 
PROCEDURE dst_x (x, Rn: INTEGER): INTEGER;
BEGIN
IdxWords.dst := x
RETURN Rn + dIDX
END dst_x;
 
 
PROCEDURE indir (Rn: INTEGER): INTEGER;
RETURN Rn * 256 + sINDIR
END indir;
 
 
PROCEDURE incr (Rn: INTEGER): INTEGER;
RETURN Rn * 256 + sINCR
END incr;
 
 
PROCEDURE imm (x: INTEGER): INTEGER;
VAR
res: INTEGER;
 
BEGIN
CASE x OF
| 0: res := CG * 256
| 1: res := src_x(0, CG); IdxWords.src := NOWORD
| 2: res := indir(CG)
| 4: res := indir(SR)
| 8: res := incr(SR)
|-1: res := incr(CG)
ELSE
res := incr(PC);
IdxWords.src := x
END
 
RETURN res
END imm;
 
 
PROCEDURE Op2 (op, src, dst: INTEGER);
BEGIN
ASSERT(BITS(op) + {6, 12..15} = {6, 12..15});
ASSERT(BITS(src) + {4, 5, 8..11} = {4, 5, 8..11});
ASSERT(BITS(dst) + {0..3, 7} = {0..3, 7});
 
EmitWord(op + src + dst);
 
IF IdxWords.src # NOWORD THEN
EmitWord(IdxWords.src);
IdxWords.src := NOWORD
END;
 
IF IdxWords.dst # NOWORD THEN
EmitWord(IdxWords.dst);
IdxWords.dst := NOWORD
END
END Op2;
 
 
PROCEDURE Op1 (op, reg, As: INTEGER);
BEGIN
EmitWord(op + reg + As)
END Op1;
 
 
PROCEDURE MovRR (src, dst: INTEGER);
BEGIN
Op2(opMOV, src * 256, dst)
END MovRR;
 
 
PROCEDURE PushImm (imm: INTEGER);
BEGIN
imm := UTILS.Long(imm);
CASE imm OF
| 0: Op1(opPUSH, CG, sREG)
| 1: Op1(opPUSH, CG, sIDX)
| 2: Op1(opPUSH, CG, sINDIR)
|-1: Op1(opPUSH, CG, sINCR)
ELSE
Op1(opPUSH, PC, sINCR);
EmitWord(imm)
END
END PushImm;
 
 
PROCEDURE PutWord (word: INTEGER; VAR adr: INTEGER);
BEGIN
ASSERT(~ODD(adr));
ASSERT((0 <= word) & (word <= 65535));
mem[adr] := word MOD 256;
mem[adr + 1] := word DIV 256;
INC(adr, 2)
END PutWord;
 
 
PROCEDURE NewLabel (): INTEGER;
BEGIN
CHL.PushInt(Labels, 0)
RETURN IL.NewLabel()
END NewLabel;
 
 
PROCEDURE LabelOffs (n: INTEGER): INTEGER;
RETURN CHL.GetInt(Labels, n)
END LabelOffs;
 
 
PROCEDURE Fixup (CodeAdr, IntVectorSize: INTEGER): INTEGER;
VAR
cmd: ANYCODE;
adr: INTEGER;
offset: INTEGER;
diff: INTEGER;
cc: INTEGER;
shorted: BOOLEAN;
 
BEGIN
REPEAT
shorted := FALSE;
offset := CodeAdr DIV 2;
 
cmd := CodeList.first(ANYCODE);
WHILE cmd # NIL DO
cmd.offset := offset;
CASE cmd OF
|LABEL: CHL.SetInt(Labels, cmd.num, offset)
|JMP: INC(offset);
IF ~cmd.short THEN
INC(offset);
IF cmd.cc # opJMP THEN
INC(offset)
END
END
 
|CALL: INC(offset, 2)
|WORD: INC(offset)
END;
cmd := cmd.next(ANYCODE)
END;
 
cmd := CodeList.first(ANYCODE);
WHILE cmd # NIL DO
IF (cmd IS JMP) & ~cmd(JMP).short THEN
diff := LabelOffs(cmd(JMP).label) - cmd.offset - 1;
IF ABS(diff) <= 512 THEN
cmd(JMP).short := TRUE;
shorted := TRUE
END
END;
cmd := cmd.next(ANYCODE)
END
 
UNTIL ~shorted;
 
IF offset * 2 > 10000H - IntVectorSize THEN
ERRORS.Error(203)
END;
 
adr := CodeAdr;
cmd := CodeList.first(ANYCODE);
WHILE cmd # NIL DO
CASE cmd OF
|LABEL:
 
|JMP: IF ~cmd.short THEN
CASE cmd.cc OF
|opJNE: cc := opJEQ
|opJEQ: cc := opJNE
|opJNC: cc := opJC
|opJC: cc := opJNC
|opJGE: cc := opJL
|opJL: cc := opJGE
|opJMP: cc := opJMP
END;
 
IF cc # opJMP THEN
PutWord(cc + 2, adr) (* jcc L *)
END;
 
PutWord(4030H, adr); (* MOV @PC+, PC *)
PutWord(LabelOffs(cmd.label) * 2, adr)
(* L: *)
ELSE
diff := LabelOffs(cmd.label) - cmd.offset - 1;
ASSERT((-512 <= diff) & (diff <= 511));
PutWord(cmd.cc + diff MOD 1024, adr)
END
 
|CALL: PutWord(12B0H, adr); (* CALL @PC+ *)
PutWord(LabelOffs(cmd.label) * 2, adr)
 
|WORD: PutWord(cmd.val, adr)
 
END;
cmd := cmd.next(ANYCODE)
END
 
RETURN adr - CodeAdr
END Fixup;
 
 
PROCEDURE Push (reg: INTEGER);
BEGIN
Op1(opPUSH, reg, sREG)
END Push;
 
 
PROCEDURE Pop (reg: INTEGER);
BEGIN
Op2(opMOV, incr(SP), reg)
END Pop;
 
 
PROCEDURE Test (reg: INTEGER);
BEGIN
Op2(opCMP, imm(0), reg)
END Test;
 
 
PROCEDURE Clear (reg: INTEGER);
BEGIN
Op2(opMOV, imm(0), reg)
END Clear;
 
 
PROCEDURE mov (dst, src: INTEGER);
BEGIN
MovRR(src, dst)
END mov;
 
 
PROCEDURE xchg (reg1, reg2: INTEGER);
BEGIN
Push(reg1);
Push(reg2);
Pop(reg1);
Pop(reg2)
END xchg;
 
 
PROCEDURE Reloc (section: INTEGER);
VAR
reloc: RELOC;
 
BEGIN
NEW(reloc);
reloc.section := section;
reloc.WordPtr := CodeList.last(WORD);
LISTS.push(RelList, reloc)
END Reloc;
 
 
PROCEDURE CallRTL (proc, params: INTEGER);
BEGIN
EmitCall(RTL.rtl[proc].label);
RTL.Used(proc);
IF params > 0 THEN
Op2(opADD, imm(params * 2), SP)
END
END CallRTL;
 
 
PROCEDURE UnOp (VAR reg: INTEGER);
BEGIN
REG.UnOp(R, reg)
END UnOp;
 
 
PROCEDURE BinOp (VAR reg1, reg2: INTEGER);
BEGIN
REG.BinOp(R, reg1, reg2)
END BinOp;
 
 
PROCEDURE GetRegA;
BEGIN
ASSERT(REG.GetReg(R, ACC))
END GetRegA;
 
 
PROCEDURE drop;
BEGIN
REG.Drop(R)
END drop;
 
 
PROCEDURE GetAnyReg (): INTEGER;
RETURN REG.GetAnyReg(R)
END GetAnyReg;
 
 
PROCEDURE PushAll (NumberOfParameters: INTEGER);
BEGIN
REG.PushAll(R);
DEC(R.pushed, NumberOfParameters)
END PushAll;
 
 
PROCEDURE PushAll_1;
BEGIN
REG.PushAll_1(R)
END PushAll_1;
 
 
PROCEDURE cond (op: INTEGER): INTEGER;
VAR
res: INTEGER;
 
BEGIN
CASE op OF
|IL.opGT, IL.opGTC: res := jg
|IL.opGE, IL.opGEC: res := jge
|IL.opLT, IL.opLTC: res := jl
|IL.opLE, IL.opLEC: res := jle
|IL.opEQ, IL.opEQC: res := je
|IL.opNE, IL.opNEC: res := jne
END
 
RETURN res
END cond;
 
 
PROCEDURE jcc (cc, label: INTEGER);
VAR
L: INTEGER;
 
BEGIN
CASE cc OF
|jne:
EmitJmp(opJNE, label)
|je:
EmitJmp(opJEQ, label)
|jge:
EmitJmp(opJGE, label)
|jl:
EmitJmp(opJL, label)
|jle:
EmitJmp(opJL, label);
EmitJmp(opJEQ, label)
|jg:
L := NewLabel();
EmitJmp(opJEQ, L);
EmitJmp(opJGE, label);
EmitLabel(L)
|jb:
EmitJmp(opJNC, label)
END
END jcc;
 
 
PROCEDURE setcc (cc, reg: INTEGER);
VAR
L: INTEGER;
 
BEGIN
L := NewLabel();
Op2(opMOV, imm(1), reg);
jcc(cc, L);
Clear(reg);
EmitLabel(L)
END setcc;
 
 
PROCEDURE Shift2 (op, reg, n: INTEGER);
VAR
reg2: INTEGER;
 
BEGIN
IF n >= 8 THEN
CASE op OF
|IL.opASR2: Op1(opSWPB, reg, sREG); Op1(opSXT, reg, sREG)
|IL.opROR2: Op1(opSWPB, reg, sREG)
|IL.opLSL2: Op1(opSWPB, reg, sREG); Op2(opBIC, imm(255), reg)
|IL.opLSR2: Op2(opBIC, imm(255), reg); Op1(opSWPB, reg, sREG)
END;
DEC(n, 8)
END;
 
IF (op = IL.opROR2) & (n > 0) THEN
reg2 := GetAnyReg();
MovRR(reg, reg2)
ELSE
reg2 := -1
END;
 
WHILE n > 0 DO
CASE op OF
|IL.opASR2: Op1(opRRA, reg, sREG)
|IL.opROR2: Op1(opRRC, reg2, sREG); Op1(opRRC, reg, sREG)
|IL.opLSL2: Op2(opADD, reg * 256, reg)
|IL.opLSR2: Op2(opBIC, imm(1), SR); Op1(opRRC, reg, sREG)
END;
DEC(n)
END;
 
IF reg2 # -1 THEN
drop
END
 
END Shift2;
 
 
PROCEDURE Neg (reg: INTEGER);
BEGIN
Op2(opXOR, imm(-1), reg);
Op2(opADD, imm(1), reg)
END Neg;
 
 
PROCEDURE translate (code: IL.CODES);
VAR
cmd, next: COMMAND;
 
opcode, param1, param2, label, L, a, n, c1, c2: INTEGER;
 
reg1, reg2: INTEGER;
 
cc: INTEGER;
 
BEGIN
cmd := code.commands.first(COMMAND);
 
WHILE cmd # NIL DO
 
param1 := cmd.param1;
param2 := cmd.param2;
 
opcode := cmd.opcode;
 
CASE opcode OF
|IL.opJMP:
EmitJmp(opJMP, param1)
 
|IL.opCALL:
EmitCall(param1)
 
|IL.opCALLP:
UnOp(reg1);
Op1(opCALL, reg1, sREG);
drop;
ASSERT(R.top = -1)
 
|IL.opPRECALL:
PushAll(0)
 
|IL.opLABEL:
EmitLabel(param1)
 
|IL.opSADR_PARAM:
Op1(opPUSH, PC, sINCR);
EmitWord(param2);
Reloc(RDATA)
 
|IL.opERR:
CallRTL(RTL._error, 2)
 
|IL.opPUSHC:
PushImm(param2)
 
|IL.opLEAVEC:
Pop(PC)
 
|IL.opENTER:
ASSERT(R.top = -1);
 
EmitLabel(param1);
 
Push(BP);
MovRR(SP, BP);
 
IF param2 > 8 THEN
Op2(opMOV, imm(param2), R4);
L := NewLabel();
EmitLabel(L);
Push(CG);
Op2(opSUB, imm(1), R4);
jcc(jne, L)
ELSIF param2 > 0 THEN
WHILE param2 > 0 DO
Push(CG);
DEC(param2)
END
END
 
|IL.opLEAVE, IL.opLEAVER:
ASSERT(param2 = 0);
IF opcode = IL.opLEAVER THEN
UnOp(reg1);
IF reg1 # ACC THEN
GetRegA;
ASSERT(REG.Exchange(R, reg1, ACC));
drop
END;
drop
END;
 
ASSERT(R.top = -1);
 
IF param1 > 0 THEN
MovRR(BP, SP)
END;
 
Pop(BP);
Pop(PC)
 
|IL.opRES:
ASSERT(R.top = -1);
GetRegA
 
|IL.opCLEANUP:
IF param2 # 0 THEN
Op2(opADD, imm(param2 * 2), SP)
END
 
|IL.opCONST:
next := cmd.next(COMMAND);
IF next.opcode = IL.opCONST THEN
c1 := param2;
c2 := next.param2;
next := next.next(COMMAND);
IF (next.opcode = IL.opSAVE) OR (next.opcode = IL.opSAVE16) OR (next.opcode = IL.opSAVE8) THEN
Op2(opMOV + bw(next.opcode = IL.opSAVE8), imm(c1), dst_x(c2, SR));
cmd := next
ELSE
Op2(opMOV, imm(param2), GetAnyReg())
END
ELSIF (next.opcode = IL.opSAVE) OR (next.opcode = IL.opSAVE16) OR (next.opcode = IL.opSAVE8) THEN
UnOp(reg1);
Op2(opMOV + bw(next.opcode = IL.opSAVE8), reg1 * 256, dst_x(param2, SR));
drop;
cmd := next
ELSE
Op2(opMOV, imm(param2), GetAnyReg())
END
 
|IL.opSADR:
Op2(opMOV, incr(PC), GetAnyReg());
EmitWord(param2);
Reloc(RDATA)
 
|IL.opGADR:
Op2(opMOV, incr(PC), GetAnyReg());
EmitWord(param2);
Reloc(RBSS)
 
|IL.opLADR:
reg1 := GetAnyReg();
MovRR(BP, reg1);
Op2(opADD, imm(param2 * 2), reg1)
 
|IL.opLLOAD8:
Op2(opMOV + BW, src_x(param2 * 2, BP), GetAnyReg())
 
|IL.opLLOAD16, IL.opVADR:
Op2(opMOV, src_x(param2 * 2, BP), GetAnyReg())
 
|IL.opGLOAD8:
Op2(opMOV + BW, src_x(param2, SR), GetAnyReg());
Reloc(RBSS)
 
|IL.opGLOAD16:
Op2(opMOV, src_x(param2, SR), GetAnyReg());
Reloc(RBSS)
 
|IL.opLOAD8:
UnOp(reg1);
Op2(opMOV + BW, indir(reg1), reg1)
 
|IL.opLOAD16:
UnOp(reg1);
Op2(opMOV, indir(reg1), reg1)
 
|IL.opVLOAD8:
reg1 := GetAnyReg();
Op2(opMOV, src_x(param2 * 2, BP), reg1);
Op2(opMOV + BW, indir(reg1), reg1)
 
|IL.opVLOAD16:
reg1 := GetAnyReg();
Op2(opMOV, src_x(param2 * 2, BP), reg1);
Op2(opMOV, indir(reg1), reg1)
 
|IL.opSAVE, IL.opSAVE16:
BinOp(reg2, reg1);
Op2(opMOV, reg2 * 256, dst_x(0, reg1));
drop;
drop
 
|IL.opSAVE8:
BinOp(reg2, reg1);
Op2(opMOV + BW, reg2 * 256, dst_x(0, reg1));
drop;
drop
 
|IL.opSAVE8C:
UnOp(reg1);
Op2(opMOV + BW, imm(param2), dst_x(0, reg1));
drop
 
|IL.opSAVE16C, IL.opSAVEC:
UnOp(reg1);
Op2(opMOV, imm(param2), dst_x(0, reg1));
drop
 
|IL.opUMINUS:
UnOp(reg1);
Neg(reg1)
 
|IL.opADD:
BinOp(reg1, reg2);
Op2(opADD, reg2 * 256, reg1);
drop
 
|IL.opADDL, IL.opADDR:
IF param2 # 0 THEN
UnOp(reg1);
Op2(opADD, imm(param2), reg1)
END
 
|IL.opSUB:
BinOp(reg1, reg2);
Op2(opSUB, reg2 * 256, reg1);
drop
 
|IL.opSUBR, IL.opSUBL:
UnOp(reg1);
IF param2 # 0 THEN
Op2(opSUB, imm(param2), reg1)
END;
IF opcode = IL.opSUBL THEN
reg2 := GetAnyReg();
Clear(reg2);
Op2(opSUB, reg1 * 256, reg2);
drop;
drop;
ASSERT(REG.GetReg(R, reg2))
END
 
|IL.opLADR_SAVEC:
Op2(opMOV, imm(param2), dst_x(param1 * 2, BP))
 
|IL.opLADR_SAVE:
UnOp(reg1);
Op2(opMOV, reg1 * 256, dst_x(param2 * 2, BP));
drop
 
|IL.opGADR_SAVEC:
Op2(opMOV, imm(param2), dst_x(param1, SR));
Reloc(RBSS)
 
|IL.opCONST_PARAM:
PushImm(param2)
 
|IL.opPARAM:
IF param2 = 1 THEN
UnOp(reg1);
Push(reg1);
drop
ELSE
ASSERT(R.top + 1 <= param2);
PushAll(param2)
END
 
|IL.opEQ..IL.opGE,
IL.opEQC..IL.opGEC:
 
IF (IL.opEQ <= opcode) & (opcode <= IL.opGE) THEN
BinOp(reg1, reg2);
Op2(opCMP, reg2 * 256, reg1);
drop
ELSE
UnOp(reg1);
Op2(opCMP, imm(param2), reg1)
END;
 
drop;
cc := cond(opcode);
 
IF cmd.next(COMMAND).opcode = IL.opJE THEN
label := cmd.next(COMMAND).param1;
jcc(cc, label);
cmd := cmd.next(COMMAND)
 
ELSIF cmd.next(COMMAND).opcode = IL.opJNE THEN
label := cmd.next(COMMAND).param1;
jcc(ORD(BITS(cc) / {0}), label);
cmd := cmd.next(COMMAND)
 
ELSE
setcc(cc, GetAnyReg())
END
 
|IL.opNOP:
 
|IL.opCODE:
EmitWord(param2)
 
|IL.opACC:
IF (R.top # 0) OR (R.stk[0] # ACC) THEN
PushAll(0);
GetRegA;
Pop(ACC);
DEC(R.pushed)
END
 
|IL.opDROP:
UnOp(reg1);
drop
 
|IL.opJNZ:
UnOp(reg1);
Test(reg1);
jcc(jne, param1)
 
|IL.opJZ:
UnOp(reg1);
Test(reg1);
jcc(je, param1)
 
|IL.opJG:
UnOp(reg1);
Test(reg1);
jcc(jg, param1)
 
|IL.opJE:
UnOp(reg1);
Test(reg1);
jcc(jne, param1);
drop
 
|IL.opJNE:
UnOp(reg1);
Test(reg1);
jcc(je, param1);
drop
 
|IL.opNOT:
UnOp(reg1);
Test(reg1);
setcc(je, reg1)
 
|IL.opORD:
UnOp(reg1);
Test(reg1);
setcc(jne, reg1)
 
|IL.opLOOP:
|IL.opENDLOOP:
 
|IL.opGET:
BinOp(reg1, reg2);
drop;
drop;
Op2(opMOV + bw(param2 = 1), indir(reg1), dst_x(0, reg2))
 
|IL.opGETC:
UnOp(reg2);
drop;
Op2(opMOV + bw(param2 = 1), src_x(param1, SR), dst_x(0, reg2))
 
|IL.opCHKIDX:
UnOp(reg1);
Op2(opCMP, imm(param2), reg1);
jcc(jb, param1)
 
|IL.opCHKIDX2:
BinOp(reg1, reg2);
IF param2 # -1 THEN
Op2(opCMP, reg1 * 256, reg2);
MovRR(reg2, reg1);
drop;
jcc(jb, param1)
ELSE
INCL(R.regs, reg1);
DEC(R.top);
R.stk[R.top] := reg2
END
 
|IL.opINCC, IL.opINCCB:
UnOp(reg1);
Op2(opADD + bw(opcode = IL.opINCCB), imm(param2), dst_x(0, reg1));
drop
 
|IL.opDECCB:
UnOp(reg1);
Op2(opSUB + BW, imm(param2), dst_x(0, reg1));
drop
 
|IL.opINC, IL.opINCB:
BinOp(reg1, reg2);
Op2(opADD + bw(opcode = IL.opINCB), reg1 * 256, dst_x(0, reg2));
drop;
drop
 
|IL.opDEC, IL.opDECB:
BinOp(reg1, reg2);
Op2(opSUB + bw(opcode = IL.opDECB), reg1 * 256, dst_x(0, reg2));
drop;
drop
 
|IL.opLADR_INCC, IL.opLADR_INCCB:
Op2(opADD + bw(opcode = IL.opLADR_INCCB), imm(param2), dst_x(param1 * 2, BP))
 
|IL.opLADR_DECCB:
Op2(opSUB + BW, imm(param2), dst_x(param1 * 2, BP))
 
|IL.opLADR_INC, IL.opLADR_INCB:
UnOp(reg1);
Op2(opADD + bw(opcode = IL.opLADR_INCB), reg1 * 256, dst_x(param2 * 2, BP));
drop
 
|IL.opLADR_DEC, IL.opLADR_DECB:
UnOp(reg1);
Op2(opSUB + bw(opcode = IL.opLADR_DECB), reg1 * 256, dst_x(param2 * 2, BP));
drop
 
|IL.opPUSHT:
UnOp(reg1);
Op2(opMOV, src_x(-2, reg1), GetAnyReg())
 
|IL.opISREC:
PushAll(2);
PushImm(param2);
CallRTL(RTL._guardrec, 3);
GetRegA
 
|IL.opIS:
PushAll(1);
PushImm(param2);
CallRTL(RTL._is, 2);
GetRegA
 
|IL.opTYPEGR:
PushAll(1);
PushImm(param2);
CallRTL(RTL._guardrec, 2);
GetRegA
 
|IL.opTYPEGP:
UnOp(reg1);
PushAll(0);
Push(reg1);
PushImm(param2);
CallRTL(RTL._guard, 2);
GetRegA
 
|IL.opTYPEGD:
UnOp(reg1);
PushAll(0);
Op1(opPUSH, reg1, sIDX);
EmitWord(-2);
PushImm(param2);
CallRTL(RTL._guardrec, 2);
GetRegA
 
|IL.opMULS:
BinOp(reg1, reg2);
Op2(opAND, reg2 * 256, reg1);
drop
 
|IL.opMULSC:
UnOp(reg1);
Op2(opAND, imm(param2), reg1)
 
|IL.opDIVS:
BinOp(reg1, reg2);
Op2(opXOR, reg2 * 256, reg1);
drop
 
|IL.opDIVSC:
UnOp(reg1);
Op2(opXOR, imm(param2), reg1)
 
|IL.opADDS:
BinOp(reg1, reg2);
Op2(opBIS, reg2 * 256, reg1);
drop
 
|IL.opSUBS:
BinOp(reg1, reg2);
Op2(opBIC, reg2 * 256, reg1);
drop
 
|IL.opADDSL, IL.opADDSR:
UnOp(reg1);
Op2(opBIS, imm(param2), reg1)
 
|IL.opSUBSL:
UnOp(reg1);
Op2(opXOR, imm(-1), reg1);
Op2(opAND, imm(param2), reg1)
 
|IL.opSUBSR:
UnOp(reg1);
Op2(opBIC, imm(param2), reg1)
 
|IL.opUMINS:
UnOp(reg1);
Op2(opXOR, imm(-1), reg1)
 
|IL.opLENGTH:
PushAll(2);
CallRTL(RTL._length, 2);
GetRegA
 
|IL.opMIN:
BinOp(reg1, reg2);
Op2(opCMP, reg2 * 256, reg1);
EmitWord(opJL + 1); (* jl L *)
MovRR(reg2, reg1);
(* L: *)
drop
 
 
|IL.opMAX:
BinOp(reg1, reg2);
Op2(opCMP, reg2 * 256, reg1);
EmitWord(opJGE + 1); (* jge L *)
MovRR(reg2, reg1);
(* L: *)
drop
 
|IL.opMINC:
UnOp(reg1);
Op2(opCMP, imm(param2), reg1);
L := NewLabel();
jcc(jl, L);
Op2(opMOV, imm(param2), reg1);
EmitLabel(L)
 
|IL.opMAXC:
UnOp(reg1);
Op2(opCMP, imm(param2), reg1);
L := NewLabel();
jcc(jge, L);
Op2(opMOV, imm(param2), reg1);
EmitLabel(L)
 
|IL.opSWITCH:
UnOp(reg1);
IF param2 = 0 THEN
reg2 := ACC
ELSE
reg2 := R5
END;
IF reg1 # reg2 THEN
ASSERT(REG.GetReg(R, reg2));
ASSERT(REG.Exchange(R, reg1, reg2));
drop
END;
drop
 
|IL.opENDSW:
 
|IL.opCASEL:
Op2(opCMP, imm(param1), ACC);
jcc(jl, param2)
 
|IL.opCASER:
Op2(opCMP, imm(param1), ACC);
jcc(jg, param2)
 
|IL.opCASELR:
Op2(opCMP, imm(param1), ACC);
jcc(jl, param2);
jcc(jg, cmd.param3)
 
|IL.opSBOOL:
BinOp(reg2, reg1);
Test(reg2);
setcc(jne, reg2);
Op2(opMOV + BW, reg2 * 256, dst_x(0, reg1));
drop;
drop
 
|IL.opSBOOLC:
UnOp(reg1);
Op2(opMOV + BW, imm(param2), dst_x(0, reg1));
drop
 
|IL.opODD:
UnOp(reg1);
Op2(opAND, imm(1), reg1)
 
|IL.opEQS .. IL.opGES:
PushAll(4);
PushImm((opcode - IL.opEQS) * 12);
CallRTL(RTL._strcmp, 5);
GetRegA
 
|IL.opLEN:
UnOp(reg1);
drop;
EXCL(R.regs, reg1);
 
WHILE param2 > 0 DO
UnOp(reg2);
drop;
DEC(param2)
END;
 
INCL(R.regs, reg1);
ASSERT(REG.GetReg(R, reg1))
 
|IL.opCHKBYTE:
BinOp(reg1, reg2);
Op2(opCMP, imm(256), reg1);
jcc(jb, param1)
 
|IL.opLSL, IL.opASR, IL.opROR, IL.opLSR:
PushAll(2);
CASE opcode OF
|IL.opLSL: CallRTL(RTL._lsl, 2)
|IL.opASR: CallRTL(RTL._asr, 2)
|IL.opROR: CallRTL(RTL._ror, 2)
|IL.opLSR: CallRTL(RTL._lsr, 2)
END;
GetRegA
 
|IL.opLSL1, IL.opASR1, IL.opROR1, IL.opLSR1:
UnOp(reg1);
PushAll_1;
PushImm(param2);
Push(reg1);
drop;
CASE opcode OF
|IL.opLSL1: CallRTL(RTL._lsl, 2)
|IL.opASR1: CallRTL(RTL._asr, 2)
|IL.opROR1: CallRTL(RTL._ror, 2)
|IL.opLSR1: CallRTL(RTL._lsr, 2)
END;
GetRegA
 
|IL.opASR2, IL.opROR2, IL.opLSL2, IL.opLSR2:
param2 := param2 MOD 16;
IF param2 # 0 THEN
UnOp(reg1);
Shift2(opcode, reg1, param2)
END
 
|IL.opMUL:
PushAll(2);
CallRTL(RTL._mul, 2);
GetRegA
 
|IL.opMULC:
UnOp(reg1);
 
a := param2;
IF a > 1 THEN
n := UTILS.Log2(a)
ELSIF a < -1 THEN
n := UTILS.Log2(-a)
ELSE
n := -1
END;
 
IF a = 1 THEN
 
ELSIF a = -1 THEN
Neg(reg1)
ELSIF a = 0 THEN
Clear(reg1)
ELSE
IF n > 0 THEN
IF a < 0 THEN
Neg(reg1)
END;
Shift2(IL.opLSL2, reg1, n)
ELSE
PushAll(1);
PushImm(a);
CallRTL(RTL._mul, 2);
GetRegA
END
END
 
|IL.opDIV:
PushAll(2);
CallRTL(RTL._divmod, 2);
GetRegA
 
|IL.opDIVR:
ASSERT(param2 > 0);
 
IF param2 > 1 THEN
n := UTILS.Log2(param2);
IF n > 0 THEN
UnOp(reg1);
Shift2(IL.opASR2, reg1, n)
ELSE
PushAll(1);
PushImm(param2);
CallRTL(RTL._divmod, 2);
GetRegA
END
END
 
|IL.opDIVL:
UnOp(reg1);
PushAll_1;
PushImm(param2);
Push(reg1);
drop;
CallRTL(RTL._divmod, 2);
GetRegA
 
|IL.opMOD:
PushAll(2);
CallRTL(RTL._divmod, 2);
ASSERT(REG.GetReg(R, R5))
 
|IL.opMODR:
ASSERT(param2 > 0);
 
IF param2 = 1 THEN
UnOp(reg1);
Clear(reg1)
ELSE
IF UTILS.Log2(param2) > 0 THEN
UnOp(reg1);
Op2(opAND, imm(param2 - 1), reg1)
ELSE
PushAll(1);
PushImm(param2);
CallRTL(RTL._divmod, 2);
ASSERT(REG.GetReg(R, R5))
END
END
 
|IL.opMODL:
UnOp(reg1);
PushAll_1;
PushImm(param2);
Push(reg1);
drop;
CallRTL(RTL._divmod, 2);
ASSERT(REG.GetReg(R, R5))
 
|IL.opCOPYS:
ASSERT(R.top = 3);
Push(R.stk[2]);
Push(R.stk[0]);
Op2(opCMP, R.stk[1] * 256, R.stk[3]);
EmitWord(3801H); (* JL L1 *)
MovRR(R.stk[1], R.stk[3]);
(* L1: *)
Push(R.stk[3]);
drop;
drop;
drop;
drop;
CallRTL(RTL._move, 3)
 
|IL.opCOPY:
PushAll(2);
PushImm(param2);
CallRTL(RTL._move, 3)
 
|IL.opMOVE:
PushAll(3);
CallRTL(RTL._move, 3)
 
|IL.opCOPYA:
PushAll(4);
PushImm(param2);
CallRTL(RTL._arrcpy, 5);
GetRegA
 
|IL.opROT:
PushAll(0);
MovRR(SP, ACC);
Push(ACC);
PushImm(param2);
CallRTL(RTL._rot, 2)
 
|IL.opSAVES:
UnOp(reg1);
PushAll_1;
Op1(opPUSH, PC, sINCR);
EmitWord(param2);
Reloc(RDATA);
Push(reg1);
drop;
PushImm(param1);
CallRTL(RTL._move, 3)
 
|IL.opCASET:
Push(R5);
Push(R5);
PushImm(param2);
CallRTL(RTL._guardrec, 2);
Pop(R5);
Test(ACC);
jcc(jne, param1)
 
|IL.opCHR:
UnOp(reg1);
Op2(opAND, imm(255), reg1)
 
|IL.opABS:
UnOp(reg1);
Test(reg1);
L := NewLabel();
jcc(jge, L);
Neg(reg1);
EmitLabel(L)
 
|IL.opEQB, IL.opNEB:
BinOp(reg1, reg2);
drop;
 
Test(reg1);
L := NewLabel();
jcc(je, L);
Op2(opMOV, imm(1), reg1);
EmitLabel(L);
 
Test(reg2);
L := NewLabel();
jcc(je, L);
Op2(opMOV, imm(1), reg2);
EmitLabel(L);
 
Op2(opCMP, reg2 * 256, reg1);
IF opcode = IL.opEQB THEN
setcc(je, reg1)
ELSE
setcc(jne, reg1)
END
 
|IL.opSAVEP:
UnOp(reg1);
Op2(opMOV, incr(PC), reg1 + dIDX);
EmitWord(param2);
Reloc(RCODE);
EmitWord(0);
drop
 
|IL.opPUSHP:
Op2(opMOV, incr(PC), GetAnyReg());
EmitWord(param2);
Reloc(RCODE)
 
|IL.opEQP, IL.opNEP:
UnOp(reg1);
Op2(opCMP, incr(PC), reg1);
EmitWord(param1);
Reloc(RCODE);
drop;
reg1 := GetAnyReg();
 
IF opcode = IL.opEQP THEN
setcc(je, reg1)
ELSIF opcode = IL.opNEP THEN
setcc(jne, reg1)
END
 
|IL.opVADR_PARAM:
Op1(opPUSH, BP, sIDX);
EmitWord(param2 * 2)
 
|IL.opNEW:
PushAll(1);
n := param2 + 2;
ASSERT(UTILS.Align(n, 2));
PushImm(n);
PushImm(param1);
CallRTL(RTL._new, 3)
 
|IL.opRSET:
PushAll(2);
CallRTL(RTL._set, 2);
GetRegA
 
|IL.opRSETR:
PushAll(1);
PushImm(param2);
CallRTL(RTL._set, 2);
GetRegA
 
|IL.opRSETL:
UnOp(reg1);
PushAll_1;
PushImm(param2);
Push(reg1);
drop;
CallRTL(RTL._set, 2);
GetRegA
 
|IL.opRSET1:
PushAll(1);
CallRTL(RTL._set1, 1);
GetRegA
 
|IL.opINCLC:
UnOp(reg1);
Op2(opBIS, imm(ORD({param2})), dst_x(0, reg1));
drop
 
|IL.opEXCLC:
UnOp(reg1);
Op2(opBIC, imm(ORD({param2})), dst_x(0, reg1));
drop
 
|IL.opIN:
PushAll(2);
CallRTL(RTL._in, 2);
GetRegA
 
|IL.opINR:
PushAll(1);
PushImm(param2);
CallRTL(RTL._in, 2);
GetRegA
 
|IL.opINL:
PushAll(1);
PushImm(param2);
CallRTL(RTL._in2, 2);
GetRegA
 
|IL.opINCL:
PushAll(2);
CallRTL(RTL._incl, 2)
 
|IL.opEXCL:
PushAll(2);
CallRTL(RTL._excl, 2)
 
|IL.opLADR_INCL, IL.opLADR_EXCL:
PushAll(1);
MovRR(BP, ACC);
Op2(opADD, imm(param2 * 2), ACC);
Push(ACC);
IF opcode = IL.opLADR_INCL THEN
CallRTL(RTL._incl, 2)
ELSIF opcode = IL.opLADR_EXCL THEN
CallRTL(RTL._excl, 2)
END
 
|IL.opLADR_INCLC:
Op2(opBIS, imm(ORD({param2})), dst_x(param1 * 2, BP))
 
|IL.opLADR_EXCLC:
Op2(opBIC, imm(ORD({param2})), dst_x(param1 * 2, BP))
 
END;
 
cmd := cmd.next(COMMAND)
END;
 
ASSERT(R.pushed = 0);
ASSERT(R.top = -1)
END translate;
 
 
PROCEDURE prolog (ramSize: INTEGER);
VAR
i: INTEGER;
 
BEGIN
RTL.Init(EmitLabel, EmitWord, EmitCall, ramSize);
FOR i := 0 TO LEN(RTL.rtl) - 1 DO
RTL.Set(i, NewLabel())
END;
 
IV[LEN(IV) - 1] := NewLabel();
EmitLabel(IV[LEN(IV) - 1]);
Op2(opMOV, incr(PC), SP);
EmitWord(0);
Op2(opMOV, incr(PC), HP);
EmitWord(0);
Op2(opMOV, imm(5A80H), dst_x(0120H, SR)); (* stop WDT *)
Op2(opMOV, imm(RTL.empty_proc), dst_x(RTL.int, SR));
Op2(opMOV, imm(0), dst_x(RTL.trap, SR))
END prolog;
 
 
PROCEDURE epilog;
VAR
L1, i: INTEGER;
 
BEGIN
Op2(opBIS, imm(10H), SR); (* CPUOFF *)
 
L1 := NewLabel();
FOR i := 0 TO LEN(IV) - 2 DO
IV[i] := NewLabel();
EmitLabel(IV[i]);
PushImm(i);
IF i # LEN(IV) - 2 THEN
EmitJmp(opJMP, L1)
END
END;
 
EmitLabel(L1);
 
MovRR(SP, IR);
 
FOR i := 0 TO 15 DO
IF i IN R.regs + R.vregs THEN
Push(i)
END
END;
 
Push(IR);
Op1(opPUSH, IR, sINDIR);
Op1(opCALL, SR, sIDX);
EmitWord(RTL.int);
Op2(opADD, imm(4), SP);
 
FOR i := 15 TO 0 BY -1 DO
IF i IN R.regs + R.vregs THEN
Pop(i)
END
END;
 
Op2(opADD, imm(2), SP);
Op1(opRETI, 0, 0);
 
RTL.Gen
END epilog;
 
 
PROCEDURE hexdgt (n: BYTE): BYTE;
BEGIN
IF n < 10 THEN
n := n + ORD("0")
ELSE
n := n - 10 + ORD("A")
END
 
RETURN n
END hexdgt;
 
 
PROCEDURE WriteHexByte (file: FILES.FILE; byte: BYTE);
BEGIN
WRITER.WriteByte(file, hexdgt(byte DIV 16));
WRITER.WriteByte(file, hexdgt(byte MOD 16));
END WriteHexByte;
 
 
PROCEDURE WriteHex (file: FILES.FILE; mem: ARRAY OF BYTE; idx, cnt: INTEGER);
VAR
i, len, chksum: INTEGER;
 
BEGIN
WHILE cnt > 0 DO
len := MIN(cnt, 16);
chksum := len + idx DIV 256 + idx MOD 256;
WRITER.WriteByte(file, ORD(":"));
WriteHexByte(file, len);
WriteHexByte(file, idx DIV 256);
WriteHexByte(file, idx MOD 256);
WriteHexByte(file, 0);
FOR i := 1 TO len DO
WriteHexByte(file, mem[idx]);
INC(chksum, mem[idx]);
INC(idx)
END;
WriteHexByte(file, (-chksum) MOD 256);
DEC(cnt, len);
WRITER.WriteByte(file, 0DH);
WRITER.WriteByte(file, 0AH)
END
END WriteHex;
 
 
PROCEDURE CodeGen* (code: IL.CODES; outname: ARRAY OF CHAR; target: INTEGER; options: PROG.OPTIONS);
VAR
i, adr, heap, stack, TextSize, TypesSize, bits, n: INTEGER;
 
Code, Data, Bss, Free: RECORD address, size: INTEGER END;
 
ram, rom: INTEGER;
 
reloc: RELOC;
 
file: FILES.FILE;
 
BEGIN
IdxWords.src := NOWORD;
IdxWords.dst := NOWORD;
 
ram := options.ram;
rom := options.rom;
 
IF ODD(ram) THEN DEC(ram) END;
IF ODD(rom) THEN DEC(rom) END;
 
ram := MIN(MAX(ram, minRAM), maxRAM);
rom := MIN(MAX(rom, minROM), maxROM);
 
IF code.bss > ram - minStackSize - RTL.VarSize THEN
ERRORS.Error(204)
END;
 
Labels := CHL.CreateIntList();
FOR i := 1 TO code.lcount DO
CHL.PushInt(Labels, 0)
END;
 
FOR i := 0 TO LEN(mem) - 1 DO
mem[i] := 0
END;
 
TypesSize := CHL.Length(code.types) * 2;
CodeList := LISTS.create(NIL);
RelList := LISTS.create(NIL);
REG.Init(R, Push, Pop, mov, xchg, NIL, NIL, {R4, R5, R6, R7}, {});
 
prolog(ram);
translate(code);
epilog;
 
Code.address := 10000H - rom;
Code.size := Fixup(Code.address, IntVectorSize + TypesSize);
Data.address := Code.address + Code.size;
Data.size := CHL.Length(code.data);
Data.size := Data.size + ORD(ODD(Data.size));
TextSize := Code.size + Data.size;
 
IF Code.address + TextSize + MAX(code.dmin - Data.size, IntVectorSize + TypesSize) > 10000H THEN
ERRORS.Error(203)
END;
 
Bss.address := RTL.ram + RTL.VarSize;
Bss.size := code.bss + ORD(ODD(code.bss));
heap := Bss.address + Bss.size;
stack := RTL.ram + ram;
ASSERT(stack - heap >= minStackSize);
adr := Code.address + 2;
PutWord(stack, adr);
adr := Code.address + 6;
PutWord(heap, adr);
 
reloc := RelList.first(RELOC);
WHILE reloc # NIL DO
adr := reloc.WordPtr.offset * 2;
CASE reloc.section OF
|RCODE: PutWord(LabelOffs(reloc.WordPtr.val) * 2, adr)
|RDATA: PutWord(reloc.WordPtr.val + Data.address, adr)
|RBSS: PutWord(reloc.WordPtr.val + Bss.address, adr)
END;
reloc := reloc.next(RELOC)
END;
 
adr := Data.address;
 
FOR i := 0 TO CHL.Length(code.data) - 1 DO
mem[adr] := CHL.GetByte(code.data, i);
INC(adr)
END;
 
adr := 10000H - IntVectorSize - TypesSize;
 
FOR i := TypesSize DIV 2 - 1 TO 0 BY -1 DO
PutWord(CHL.GetInt(code.types, i), adr)
END;
 
FOR i := 0 TO 15 DO
PutWord((33 - i) * i, adr);
END;
 
FOR n := 0 TO 15 DO
bits := ORD({0 .. n});
FOR i := 0 TO 15 - n DO
PutWord(bits, adr);
bits := LSL(bits, 1)
END
END;
 
Free.address := Code.address + TextSize;
Free.size := rom - (IntVectorSize + TypesSize + TextSize);
 
PutWord(Free.address, adr);
PutWord(Free.size, adr);
PutWord(4130H, adr); (* RET *)
PutWord(stack, adr);
 
FOR i := 0 TO LEN(IV) - 1 DO
PutWord(LabelOffs(IV[i]) * 2, adr)
END;
 
file := FILES.create(outname);
WriteHex(file, mem, Code.address, TextSize);
WriteHex(file, mem, 10000H - IntVectorSize - TypesSize, IntVectorSize + TypesSize);
 
WRITER.WriteByte(file, ORD(":"));
WriteHexByte(file, 0);
WriteHexByte(file, 0);
WriteHexByte(file, 0);
WriteHexByte(file, 1);
WriteHexByte(file, 255);
WRITER.WriteByte(file, 0DH);
WRITER.WriteByte(file, 0AH);
 
FILES.close(file);
 
INC(TextSize, IntVectorSize + TypesSize);
INC(Bss.size, minStackSize + RTL.VarSize);
 
C.StringLn("--------------------------------------------");
C.String( " rom: "); C.Int(TextSize); C.String(" of "); C.Int(rom); C.String(" ("); C.Int(TextSize * 100 DIV rom); C.StringLn("%)");
IF Free.size > 0 THEN
C.String( " "); C.Int(Free.size); C.String(" bytes free (0");
C.Hex(Free.address, 4); C.String("H..0"); C.Hex(Free.address + Free.size - 1, 4); C.StringLn("H)")
END;
C.Ln;
C.String( " ram: "); C.Int(Bss.size); C.String(" of "); C.Int(ram); C.String(" ("); C.Int(Bss.size * 100 DIV ram); C.StringLn("%)");
C.StringLn("--------------------------------------------")
 
END CodeGen;
 
 
END MSP430.