(* BSD 2-Clause License Copyright (c) 2019-2020, Anton Krotov All rights reserved. *) MODULE MSP430; IMPORT IL, LISTS, REG, CHL := CHUNKLISTS, ERRORS, WR := WRITER, HEX, UTILS, C := CONSOLE, PROG, RTL := MSP430RTL; CONST minRAM* = 128; maxRAM* = 2048; minROM* = 2048; maxROM* = 24576; minStackSize = 64; IntVectorSize* = RTL.IntVectorSize; PC = 0; SP = 1; SR = 2; CG = 3; R4 = 4; R5 = 5; R6 = 6; R7 = 7; HP = 14; IR = 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; StkCnt: INTEGER; 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; VAR res: INTEGER; BEGIN IF (x = 0) & ~(Rn IN {PC, SR, CG}) THEN res := Rn * 256 + sINDIR ELSE IdxWords.src := x; res := Rn * 256 + sIDX END RETURN res 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 := CG * 256 + sIDX | 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} = {}); ASSERT(BITS(src) - {4, 5, 8..11} = {}); ASSERT(BITS(dst) - {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; INC(StkCnt) 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); INC(StkCnt) END Push; PROCEDURE Pop (reg: INTEGER); BEGIN Op2(opMOV, incr(SP), reg); DEC(StkCnt) 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); DEC(StkCnt, params) 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 LocalOffset (offset: INTEGER): INTEGER; RETURN (offset + StkCnt - ORD(offset > 0)) * 2 END LocalOffset; PROCEDURE LocalDst (offset: INTEGER): INTEGER; RETURN dst_x(LocalOffset(offset), SP) END LocalDst; PROCEDURE LocalSrc (offset: INTEGER): INTEGER; RETURN src_x(LocalOffset(offset), SP) END LocalSrc; PROCEDURE translate; VAR cmd, next: COMMAND; opcode, param1, param2, L, a, n, c1, c2: INTEGER; reg1, reg2: INTEGER; cc: INTEGER; BEGIN cmd := IL.codes.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); INC(StkCnt); EmitWord(param2); Reloc(RDATA) |IL.opERR: CallRTL(RTL._error, 2) |IL.opPUSHC: PushImm(param2) |IL.opONERR: PushImm(param2); DEC(StkCnt); EmitJmp(opJMP, param1) |IL.opLEAVEC: Pop(PC) |IL.opENTER: ASSERT(R.top = -1); StkCnt := 0; EmitLabel(param1); 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); ASSERT(StkCnt = param1); IF param1 > 0 THEN Op2(opADD, imm(param1 * 2), SP) END; Pop(PC) |IL.opRES: ASSERT(R.top = -1); GetRegA |IL.opCLEANUP: IF param2 # 0 THEN Op2(opADD, imm(param2 * 2), SP); DEC(StkCnt, param2) 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(); n := LocalOffset(param2); Op2(opMOV, SP * 256, reg1); IF n # 0 THEN Op2(opADD, imm(n), reg1) END |IL.opLLOAD8: Op2(opMOV + BW, LocalSrc(param2), GetAnyReg()) |IL.opLLOAD16, IL.opVADR: Op2(opMOV, LocalSrc(param2), 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, LocalSrc(param2), reg1); Op2(opMOV + BW, indir(reg1), reg1) |IL.opVLOAD16: reg1 := GetAnyReg(); Op2(opMOV, LocalSrc(param2), 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 Neg(reg1) END |IL.opLADR_SAVEC: Op2(opMOV, imm(param2), LocalDst(param1)) |IL.opLADR_SAVE: UnOp(reg1); Op2(opMOV, reg1 * 256, LocalDst(param2)); 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); next := cmd.next(COMMAND); IF next.opcode = IL.opJE THEN jcc(cc, next.param1); cmd := next ELSIF next.opcode = IL.opJNE THEN jcc(ORD(BITS(cc) / {0}), next.param1); cmd := next 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); jcc(jb, param1) END; INCL(R.regs, reg1); DEC(R.top); R.stk[R.top] := reg2 |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), LocalDst(param1)) |IL.opLADR_DECCB: Op2(opSUB + BW, imm(param2), LocalDst(param1)) |IL.opLADR_INC, IL.opLADR_INCB: UnOp(reg1); Op2(opADD + bw(opcode = IL.opLADR_INCB), reg1 * 256, LocalDst(param2)); drop |IL.opLADR_DEC, IL.opLADR_DECB: UnOp(reg1); Op2(opSUB + bw(opcode = IL.opLADR_DECB), reg1 * 256, LocalDst(param2)); 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); INC(StkCnt); 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.opMAX,IL.opMIN: BinOp(reg1, reg2); Op2(opCMP, reg2 * 256, reg1); IF opcode = IL.opMIN THEN cc := opJL + 1 ELSE cc := opJGE + 1 END; EmitWord(cc); (* jge/jl L *) MovRR(reg2, reg1); (* L: *) drop |IL.opMAXC, IL.opMINC: UnOp(reg1); Op2(opCMP, imm(param2), reg1); L := NewLabel(); IF opcode = IL.opMINC THEN cc := jl ELSE cc := jge END; jcc(cc, 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.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); INC(StkCnt); 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: reg1 := GetAnyReg(); Op2(opMOV, LocalSrc(param2), reg1); Push(reg1); drop |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(SP, ACC); n := LocalOffset(param2); IF n # 0 THEN Op2(opADD, imm(n), ACC) END; 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})), LocalDst(param1)) |IL.opLADR_EXCLC: Op2(opBIC, imm(ORD({param2})), LocalDst(param1)) 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 CodeGen* (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: WR.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 IL.codes.bss > ram - minStackSize - RTL.VarSize THEN ERRORS.Error(204) END; Labels := CHL.CreateIntList(); FOR i := 1 TO IL.codes.lcount DO CHL.PushInt(Labels, 0) END; FOR i := 0 TO LEN(mem) - 1 DO mem[i] := 0 END; TypesSize := CHL.Length(IL.codes.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; epilog; Code.address := 10000H - rom; Code.size := Fixup(Code.address, IntVectorSize + TypesSize); Data.address := Code.address + Code.size; Data.size := CHL.Length(IL.codes.data); Data.size := Data.size + Data.size MOD 2; TextSize := Code.size + Data.size; IF Code.address + TextSize + MAX(IL.codes.dmin - Data.size, IntVectorSize + TypesSize) > 10000H THEN ERRORS.Error(203) END; Bss.address := RTL.ram + RTL.VarSize; Bss.size := IL.codes.bss + IL.codes.bss MOD 2; 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(IL.codes.data) - 1 DO mem[adr] := CHL.GetByte(IL.codes.data, i); INC(adr) END; adr := 10000H - IntVectorSize - TypesSize; FOR i := TypesSize DIV 2 - 1 TO 0 BY -1 DO PutWord(CHL.GetInt(IL.codes.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); PutWord(0001H, adr); (* bsl signature (adr 0FFBEH) *) FOR i := 0 TO LEN(IV) - 1 DO PutWord(LabelOffs(IV[i]) * 2, adr) END; file := WR.Create(outname); HEX.Data(file, mem, Code.address, TextSize); HEX.Data(file, mem, 10000H - IntVectorSize - TypesSize, IntVectorSize + TypesSize); HEX.End(file); WR.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("%)") END CodeGen; END MSP430.