prog8/intermediate/src/prog8/intermediate/IRInstructions.kt

1122 lines
53 KiB
Kotlin

package prog8.intermediate
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex
/*
Intermediate Representation instructions for the IR Virtual machine.
--------------------------------------------------------------------
Specs of the virtual machine this will run on:
Program to execute is not stored in the system memory, it's just a separate list of instructions.
65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535
65536 virtual floating point registers (64 bits double precision) fr0-fr65535
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
Value stack, max 128 entries of 1 byte each.
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,
LOAD instructions DO affect the Z and N flags.
INC/DEC/NEG instructions DO affect the Z and N flags,
other instructions only affect Z an N flags if the value in a result register is written.
See OpcodesThatSetStatusbits
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
Value types: integers (.b=byte=8 bits, .w=word=16 bits) and float (.f=64 bits). Omitting it defaults to b if the instruction requires a type.
Currently ther is NO support for 24 or 32 bits integers.
There is no distinction between signed and unsigned integers.
Instead, a different instruction is used if a distinction should be made (for example div and divs).
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
NOTE: Labels in source text should always start with an underscore.
LOAD/STORE
----------
All have type b or w or f.
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
loadr reg1, reg2 - load reg1 with value in register reg2
loadha reg1 - load cpu hardware register A into reg1.b
loadhx reg1 - load cpu hardware register X into reg1.b
loadhy reg1 - load cpu hardware register Y into reg1.b
loadhax reg1 - load cpu hardware register pair AX into reg1.w
loadhay reg1 - load cpu hardware register pair AY into reg1.w
loadhxy reg1 - load cpu hardware register pair XY into reg1.w
storem reg1, address - store reg1 at memory address
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (only the lsb part used for indexing)
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
storezm address - store zero at memory address
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg1 (only the lsb part used for indexing)
storeha reg1 - store reg1.b into cpu hardware register A
storehx reg1 - store reg1.b into cpu hardware register X
storehy reg1 - store reg1.b into cpu hardware register Y
storehax reg1 - store reg1.w into cpu hardware register pair AX
storehay reg1 - store reg1.w into cpu hardware register pair AY
storehxy reg1 - store reg1.w into cpu hardware register pair XY
CONTROL FLOW
------------
jump location - continue running at instruction at 'location' (label/memory address)
jumpi pointervar - continue running at memory address contained in the pointer variable (indirect jump)
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
call label(argument register list) [: resultreg.type]
- calls a subroutine with the given arguments and return value (optional).
save current instruction location+1, continue execution at instruction nr of the label.
the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
For example: call $ffd2(r5.b@A)
Always preceded by parameter setup and preparecall instructions
syscall number (argument register list) [: resultreg.type]
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
will be POPped off into the given resultregister if any.
Always preceded by parameter setup and preparecall instructions
return - restore last saved instruction location and continue at that instruction. No return value.
returnr reg1 - like return, but also returns the value in reg1 to the caller
BRANCHING and CONDITIONALS
--------------------------
All have type b or w except the branches that only check status bits.
bstcc address - branch to location if Status bit Carry is clear
bstcs address - branch to location if Status bit Carry is set
bstne address - branch to location if Status bit Zero is clear
bsteq address - branch to location if Status bit Zero is set
bstpos address - branch to location if Status bit Negative is clear
bstneg address - branch to location if Status bit Negative is set
bstvc address - branch to location if Status bit Overflow is clear
bstvs address - branch to location if Status bit Overflow is set
(unsigned comparison branches:)
bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned)
blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
'bltr' reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (unsigned) ==> use bgtr with swapped operands
bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned)
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
bger reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
'bler' reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (unsigned) ==> use bger with swapped operands
(signed comparison branches:)
bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed)
blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed)
bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
'bltsr' reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (signed) ==> use bgtsr with swapped operands
bges reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (signed)
bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed)
bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
'blesr' reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (signed) ==> use bgesr with swapped operands
scc reg1 - set reg1=1 if Carry flag is clear, else 0
scs reg1 - set reg1=1 if Carry flag is set, else 0
sz reg1, reg2 - set reg1=1 if reg2==0, else 0
snz reg1, reg2 - set reg1=1 if reg2!=0, else 0
seq reg1, reg2, reg3 - set reg1=1 if reg2 == reg3, else 0
sne reg1, reg2, reg3 - set reg1=1 if reg2 != reg3, else 0
slt reg1, reg2, reg3 - set reg1=1 if reg2 < reg3 (unsigned), else 0
slts reg1, reg2, reg3 - set reg1=1 if reg2 < reg3 (signed), else 0
sle reg1, reg2, reg3 - set reg1=1 if reg2 <= reg3 (unsigned), else 0
sles reg1, reg2, reg3 - set reg1=1 if reg2 <= reg3 (signed), else 0
sgt reg1, reg2, reg3 - set reg1=1 if reg2 > reg3 (unsigned), else 0
sgts reg1, reg2, reg3 - set reg1=1 if reg2 > reg3 (signed), else 0
sge reg1, reg2, reg3 - set reg1=1 if reg2 >= reg3 (unsigned), else 0
sges reg1, reg2, reg3 - set reg1=1 if reg2 >= reg3 (signed), else 0
(note: on the M68k these instructions will set all bits to 1 (so value=-1 instead of 1), but the boolean logic here requires it to be 0 or 1 in this IR)
ARITHMETIC
----------
All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte.
exts reg1, reg2 - reg1 = signed extension of reg2 (byte to word, or word to long) (note: unlike M68k, exts.b -> word and exts.w -> long. The latter is not yet implemented yet as we don't have longs yet)
ext reg1, reg2 - reg1 = unsigned extension of reg2 (which in practice just means clearing the MSB / MSW) (note: unlike M68k, ext.b -> word and ext.w -> long. The latter is not yet implemented yet as we don't have longs yet)
inc reg1 - reg1 = reg1+1
incm address - memory at address += 1
dec reg1 - reg1 = reg1-1
decm address - memory at address -= 1
neg reg1 - reg1 = sign negation of reg1
negm address - sign negate memory at address
addr reg1, reg2 - reg1 += reg2
add reg1, value - reg1 += value
addm reg1, address - memory at address += reg1
subr reg1, reg2 - reg1 -= reg2
sub reg1, value - reg1 -= value
subm reg1, address - memory at address -= reg1
mulr reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word!
mul reg1, value - unsigned multiply reg1 *= value note: byte*byte->byte, no type extension to word!
mulm reg1, address - memory at address *= reg2 note: byte*byte->byte, no type extension to word!
divr reg1, reg2 - unsigned division reg1 /= reg2 note: division by zero yields max int $ff/$ffff
div reg1, value - unsigned division reg1 /= value note: division by zero yields max int $ff/$ffff
divm reg1, address - memory at address /= reg2 note: division by zero yields max int $ff/$ffff
divsr reg1, reg2 - signed division reg1 /= reg2 note: division by zero yields max signed int 127 / 32767
divs reg1, value - signed division reg1 /= value note: division by zero yields max signed int 127 / 32767
divsm reg1, address - signed memory at address /= reg2 note: division by zero yields max signed int 127 / 32767
modr reg1, reg2 - remainder (modulo) of unsigned division reg1 %= reg2 note: division by zero yields max signed int $ff/$ffff
mod reg1, value - remainder (modulo) of unsigned division reg1 %= value note: division by zero yields max signed int $ff/$ffff
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off)
divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off)
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
square reg1, reg2 - reg1 is the square of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sgn reg1, reg2 - reg1.b is the sign of reg2 (or fpreg1, if sgn.f) (0.b, 1.b or -1.b)
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
cmpi reg1, value - set processor status bits C, N, Z according to comparison of reg1 with immediate value. (semantics taken from 6502/68000 CMP instruction)
NOTE: because mul/div are constrained (truncated) to remain in 8 or 16 bits, there is NO NEED for separate signed/unsigned mul and div instructions. The result is identical.
LOGICAL/BITWISE
---------------
All have type b or w.
andr reg1, reg2 - reg1 = reg1 bitwise and reg2
and reg1, value - reg1 = reg1 bitwise and value
andm reg1 address - memory = memory bitwise and reg1
orr reg1, reg2 - reg1 = reg1 bitwise or reg2
or reg1, value - reg1 = reg1 bitwise or value
orm reg1, address - memory = memory bitwise or reg1
xorr reg1, reg2 - reg1 = reg1 bitwise xor reg2
xor reg1, value - reg1 = reg1 bitwise xor value
xorm reg1, address - memory = memory bitwise xor reg1
inv reg1 - reg1 = bitwise invert of reg1 (all bits flipped)
invm address - memory = bitwise invert of that memory (all bits flipped)
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit
lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit
lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit
asr reg1 - shift reg1 right by 1 bits (signed) + set Carry to shifted bit
lsr reg1 - shift reg1 right by 1 bits + set Carry to shifted bit
lsl reg1 - shift reg1 left by 1 bits + set Carry to shifted bit
lsrm address - shift memory right by 1 bits + set Carry to shifted bit
asrm address - shift memory right by 1 bits (signed) + set Carry to shifted bit
lslm address - shift memory left by 1 bits + set Carry to shifted bit
ror reg1 - rotate reg1 right by 1 bits, not using carry + set Carry to shifted bit
roxr reg1 - rotate reg1 right by 1 bits, using carry + set Carry to shifted bit
rol reg1 - rotate reg1 left by 1 bits, not using carry + set Carry to shifted bit
roxl reg1 - rotate reg1 left by 1 bits, using carry, + set Carry to shifted bit
rorm address - rotate memory right by 1 bits, not using carry + set Carry to shifted bit
roxrm address - rotate memory right by 1 bits, using carry + set Carry to shifted bit
rolm address - rotate memory left by 1 bits, not using carry + set Carry to shifted bit
roxlm address - rotate memory left by 1 bits, using carry, + set Carry to shifted bit
FLOATING POINT CONVERSIONS AND FUNCTIONS
----------------------------------------
ffromub fpreg1, reg1 - fpreg1 = reg1 from usigned byte
ffromsb fpreg1, reg1 - fpreg1 = reg1 from signed byte
ffromuw fpreg1, reg1 - fpreg1 = reg1 from unsigned word
ffromsw fpreg1, reg1 - fpreg1 = reg1 from signed word
ftoub reg1, fpreg1 - reg1 = fpreg1 as unsigned byte
ftosb reg1, fpreg1 - reg1 = fpreg1 as signed byte
ftouw reg1, fpreg1 - reg1 = fpreg1 as unsigned word
ftosw reg1, fpreg1 - reg1 = fpreg1 as signed word
fpow fpreg1, fpreg2 - fpreg1 = fpreg1 to the power of fpreg2
fabs fpreg1, fpreg2 - fpreg1 = abs(fpreg2)
fcomp reg1, fpreg1, fpreg2 - reg1 = result of comparison of fpreg1 and fpreg2: 0.b=equal, 1.b=fpreg1 is greater, -1.b=fpreg1 is smaller
fsin fpreg1, fpreg2 - fpreg1 = sin(fpreg2)
fcos fpreg1, fpreg2 - fpreg1 = cos(fpreg2)
ftan fpreg1, fpreg2 - fpreg1 = tan(fpreg2)
fatan fpreg1, fpreg2 - fpreg1 = atan(fpreg2)
fln fpreg1, fpreg2 - fpreg1 = ln(fpreg2) ; natural logarithm
flog fpreg1, fpreg2 - fpreg1 = log(fpreg2) ; base 2 logarithm
fround fpreg1, fpreg2 - fpreg1 = round(fpreg2)
ffloor fpreg1, fpreg2 - fpreg1 = floor(fpreg2)
fceil fpreg1, fpreg2 - fpreg1 = ceil(fpreg2)
MISC
----
clc - clear Carry status bit
sec - set Carry status bit
nop - do nothing
breakpoint - trigger a breakpoint
msig [b, w] reg1, reg2 - reg1 becomes the most significant byte (or word) of the word (or int) in reg2 (.w not yet implemented; requires 32 bits regs)
concat [b, w] reg1, reg2, reg3 - reg1.w = 'concatenate' two registers: lsb/lsw of reg2 (as msb) and lsb/lsw of reg3 (as lsb) into word or int (int not yet implemented; requires 32bits regs)
push [b, w, f] reg1 - push value in reg1 on the stack
pop [b, w, f] reg1 - pop value from stack into reg1
pushst - push status register bits to stack
popst - pop status register bits from stack
*/
enum class Opcode {
NOP,
LOAD, // note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
LOADM,
LOADI,
LOADX,
LOADIX,
LOADR,
LOADHA,
LOADHX,
LOADHY,
LOADHAX,
LOADHAY,
LOADHXY,
STOREM,
STOREI,
STOREX,
STOREIX,
STOREZM,
STOREZI,
STOREZX,
STOREHA,
STOREHX,
STOREHY,
STOREHAX,
STOREHAY,
STOREHXY,
JUMP,
JUMPI,
PREPARECALL,
CALLI,
CALL,
SYSCALL,
RETURN,
RETURNR,
BSTCC,
BSTCS,
BSTEQ,
BSTNE,
BSTNEG,
BSTPOS,
BSTVC,
BSTVS,
BGTR,
BGT,
BLT,
BGTSR,
BGTS,
BLTS,
BGER,
BGE,
BLE,
BGESR,
BGES,
BLES,
SCC,
SCS,
SZ,
SNZ,
SEQ,
SNE,
SLT,
SLTS,
SGT,
SGTS,
SLE,
SLES,
SGE,
SGES,
INC,
INCM,
DEC,
DECM,
NEG,
NEGM,
ADDR,
ADD,
ADDM,
SUBR,
SUB,
SUBM,
MULR,
MUL,
MULM,
DIVR,
DIV,
DIVM,
DIVSR,
DIVS,
DIVSM,
MODR,
MOD,
DIVMODR,
DIVMOD,
SQRT,
SQUARE,
SGN,
CMP,
CMPI,
EXT,
EXTS,
ANDR,
AND,
ANDM,
ORR,
OR,
ORM,
XORR,
XOR,
XORM,
INV,
INVM,
ASRN,
ASRNM,
LSRN,
LSRNM,
LSLN,
LSLNM,
ASR,
ASRM,
LSR,
LSRM,
LSL,
LSLM,
ROR,
RORM,
ROXR,
ROXRM,
ROL,
ROLM,
ROXL,
ROXLM,
FFROMUB,
FFROMSB,
FFROMUW,
FFROMSW,
FTOUB,
FTOSB,
FTOUW,
FTOSW,
FPOW,
FABS,
FSIN,
FCOS,
FTAN,
FATAN,
FLN,
FLOG,
FROUND,
FFLOOR,
FCEIL,
FCOMP,
CLC,
SEC,
PUSH,
POP,
PUSHST,
POPST,
MSIG,
CONCAT,
BREAKPOINT
}
val OpcodesThatJump = arrayOf(
Opcode.JUMP,
Opcode.JUMPI,
Opcode.RETURN,
Opcode.RETURNR
)
val OpcodesThatBranch = arrayOf(
Opcode.JUMP,
Opcode.JUMPI,
Opcode.RETURN,
Opcode.RETURNR,
Opcode.CALLI,
Opcode.CALL,
Opcode.SYSCALL,
Opcode.BSTCC,
Opcode.BSTCS,
Opcode.BSTEQ,
Opcode.BSTNE,
Opcode.BSTNEG,
Opcode.BSTPOS,
Opcode.BSTVC,
Opcode.BSTVS,
Opcode.BGTR,
Opcode.BGT,
Opcode.BLT,
Opcode.BGTSR,
Opcode.BGTS,
Opcode.BLTS,
Opcode.BGER,
Opcode.BGE,
Opcode.BLE,
Opcode.BGESR,
Opcode.BGES,
Opcode.BLES
)
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
Opcode.CMP,
Opcode.CMPI
)
val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
Opcode.LOAD,
Opcode.LOADM,
Opcode.LOADI,
Opcode.LOADX,
Opcode.LOADIX,
Opcode.LOADR,
Opcode.LOADHA,
Opcode.LOADHX,
Opcode.LOADHY,
Opcode.LOADHAX,
Opcode.LOADHAY,
Opcode.LOADHXY,
Opcode.NEG,
Opcode.NEGM,
Opcode.INC,
Opcode.INCM,
Opcode.DEC,
Opcode.DECM,
Opcode.ANDM,
Opcode.ANDR,
Opcode.AND,
Opcode.ORM,
Opcode.ORR,
Opcode.OR,
Opcode.XORM,
Opcode.XORR,
Opcode.XOR,
)
val OpcodesThatDependOnCarry = arrayOf(
Opcode.BSTCC,
Opcode.BSTCS,
Opcode.BSTPOS,
Opcode.BSTNEG,
Opcode.ROXL,
Opcode.ROXLM,
Opcode.ROXR,
Opcode.ROXRM,
)
val OpcodesThatSetRegFromStatusbits = arrayOf(
Opcode.SCC,
Opcode.SCS,
Opcode.SZ,
Opcode.SNZ,
Opcode.SEQ,
Opcode.SNE,
Opcode.SLT,
Opcode.SLTS,
Opcode.SGT,
Opcode.SGTS,
Opcode.SLE,
Opcode.SLES,
Opcode.SGE,
Opcode.SGES
)
val OpcodesThatSetStatusbits = OpcodesThatSetStatusbitsButNotCarry + OpcodesThatSetStatusbitsIncludingCarry
enum class IRDataType {
BYTE,
WORD,
FLOAT
// TODO add INT (32-bit)? INT24 (24-bit)?
}
enum class OperandDirection {
UNUSED,
READ,
WRITE,
READWRITE
}
data class InstructionFormat(val datatype: IRDataType?,
val reg1: OperandDirection,
val reg2: OperandDirection,
val reg3: OperandDirection,
val fpReg1: OperandDirection,
val fpReg2: OperandDirection,
val address: OperandDirection,
val immediate: Boolean,
val funcCall: Boolean,
val sysCall: Boolean) {
companion object {
fun from(spec: String): Map<IRDataType?, InstructionFormat> {
val result = mutableMapOf<IRDataType?, InstructionFormat>()
for(part in spec.split('|').map{ it.trim() }) {
var reg1 = OperandDirection.UNUSED
var reg2 = OperandDirection.UNUSED
var reg3 = OperandDirection.UNUSED
var fpreg1 = OperandDirection.UNUSED
var fpreg2 = OperandDirection.UNUSED
var address = OperandDirection.UNUSED
var immediate = false
val splits = part.splitToSequence(',').iterator()
val typespec = splits.next()
var funcCall = false
var sysCall = false
while(splits.hasNext()) {
when(splits.next()) {
"<r1" -> reg1 = OperandDirection.READ
">r1" -> reg1 = OperandDirection.WRITE
"<>r1" -> reg1 = OperandDirection.READWRITE
"<r2" -> reg2 = OperandDirection.READ
"<r3" -> reg3 = OperandDirection.READ
"<fr1" -> fpreg1 = OperandDirection.READ
">fr1" -> fpreg1 = OperandDirection.WRITE
"<>fr1" -> fpreg1 = OperandDirection.READWRITE
"<fr2" -> fpreg2 = OperandDirection.READ
">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value")
"<i" -> immediate = true
"<a" -> address = OperandDirection.READ
">a" -> address = OperandDirection.WRITE
"<>a" -> address = OperandDirection.READWRITE
"call" -> funcCall = true
"syscall" -> sysCall = true
else -> throw IllegalArgumentException(spec)
}
}
if(typespec=="N")
result[null] = InstructionFormat(null, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('B' in typespec)
result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('W' in typespec)
result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('F' in typespec)
result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
}
return result
}
}
}
/*
<X = X is not modified (readonly value)
>X = X is overwritten with output value (write value)
<>X = X is modified (read + written)
where X is one of:
r0... = integer register
fr0... = fp register
a = memory address
i = immediate value
*/
val instructionFormats = mutableMapOf(
Opcode.NOP to InstructionFormat.from("N"),
Opcode.LOAD to InstructionFormat.from("BW,>r1,<i | F,>fr1,<i"),
Opcode.LOADM to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
Opcode.LOADI to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<r1"),
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.LOADHA to InstructionFormat.from("B,>r1"),
Opcode.LOADHA to InstructionFormat.from("B,>r1"),
Opcode.LOADHX to InstructionFormat.from("B,>r1"),
Opcode.LOADHY to InstructionFormat.from("B,>r1"),
Opcode.LOADHAX to InstructionFormat.from("W,>r1"),
Opcode.LOADHAY to InstructionFormat.from("W,>r1"),
Opcode.LOADHXY to InstructionFormat.from("W,>r1"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.STOREHA to InstructionFormat.from("B,<r1"),
Opcode.STOREHA to InstructionFormat.from("B,<r1"),
Opcode.STOREHX to InstructionFormat.from("B,<r1"),
Opcode.STOREHY to InstructionFormat.from("B,<r1"),
Opcode.STOREHAX to InstructionFormat.from("W,<r1"),
Opcode.STOREHAY to InstructionFormat.from("W,<r1"),
Opcode.STOREHXY to InstructionFormat.from("W,<r1"),
Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPI to InstructionFormat.from("N,<a"),
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
Opcode.CALLI to InstructionFormat.from("N,<r1"),
Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.BSTCC to InstructionFormat.from("N,<a"),
Opcode.BSTCS to InstructionFormat.from("N,<a"),
Opcode.BSTEQ to InstructionFormat.from("N,<a"),
Opcode.BSTNE to InstructionFormat.from("N,<a"),
Opcode.BSTNEG to InstructionFormat.from("N,<a"),
Opcode.BSTPOS to InstructionFormat.from("N,<a"),
Opcode.BSTVC to InstructionFormat.from("N,<a"),
Opcode.BSTVS to InstructionFormat.from("N,<a"),
Opcode.BGTR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGT to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BLT to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGTSR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGTS to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BLTS to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGER to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGE to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BLE to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGESR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGES to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BLES to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.SCC to InstructionFormat.from("BW,>r1"),
Opcode.SCS to InstructionFormat.from("BW,>r1"),
Opcode.SZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SNZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SEQ to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SNE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLTS to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGTS to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.INCM to InstructionFormat.from("BW,<>a | F,<>a"),
Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.DECM to InstructionFormat.from("BW,<>a | F,<>a"),
Opcode.NEG to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.NEGM to InstructionFormat.from("BW,<>a | F,<>a"),
Opcode.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.ADD to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
Opcode.ADDM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.SUBR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.SUB to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
Opcode.SUBM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.MULR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.MUL to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
Opcode.MULM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.DIVR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.DIV to InstructionFormat.from("BW,<>r1,<i"),
Opcode.DIVM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.DIVSR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.DIVS to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
Opcode.DIVSM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.SQUARE to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.SGN to InstructionFormat.from("BW,>r1,<r2 | F,>r1,<fr1"),
Opcode.MODR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.MOD to InstructionFormat.from("BW,<>r1,<i"),
Opcode.DIVMODR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.DIVMOD to InstructionFormat.from("BW,<>r1,<i"),
Opcode.CMP to InstructionFormat.from("BW,<r1,<r2"),
Opcode.CMPI to InstructionFormat.from("BW,<r1,<i"),
Opcode.EXT to InstructionFormat.from("BW,>r1,<r2"),
Opcode.EXTS to InstructionFormat.from("BW,>r1,<r2"),
Opcode.ANDR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.AND to InstructionFormat.from("BW,<>r1,<i"),
Opcode.ANDM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.ORR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.OR to InstructionFormat.from("BW,<>r1,<i"),
Opcode.ORM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.XORR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.XOR to InstructionFormat.from("BW,<>r1,<i"),
Opcode.XORM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.INV to InstructionFormat.from("BW,<>r1"),
Opcode.INVM to InstructionFormat.from("BW,<>a"),
Opcode.ASRN to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.ASRNM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.LSRN to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.LSRNM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.LSLN to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.LSLNM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.ASR to InstructionFormat.from("BW,<>r1"),
Opcode.ASRM to InstructionFormat.from("BW,<>a"),
Opcode.LSR to InstructionFormat.from("BW,<>r1"),
Opcode.LSRM to InstructionFormat.from("BW,<>a"),
Opcode.LSL to InstructionFormat.from("BW,<>r1"),
Opcode.LSLM to InstructionFormat.from("BW,<>a"),
Opcode.ROR to InstructionFormat.from("BW,<>r1"),
Opcode.RORM to InstructionFormat.from("BW,<>a"),
Opcode.ROXR to InstructionFormat.from("BW,<>r1"),
Opcode.ROXRM to InstructionFormat.from("BW,<>a"),
Opcode.ROL to InstructionFormat.from("BW,<>r1"),
Opcode.ROLM to InstructionFormat.from("BW,<>a"),
Opcode.ROXL to InstructionFormat.from("BW,<>r1"),
Opcode.ROXLM to InstructionFormat.from("BW,<>a"),
Opcode.FFROMUB to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMUW to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSW to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FTOUB to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOSB to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOUW to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FTOSW to InstructionFormat.from("F,>r1,<fr1"),
Opcode.FPOW to InstructionFormat.from("F,<>fr1,<fr2"),
Opcode.FABS to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FCOMP to InstructionFormat.from("F,>r1,<fr1,<fr2"),
Opcode.FSIN to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FCOS to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FTAN to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FATAN to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FLN to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FLOG to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FROUND to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FFLOOR to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.FCEIL to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"),
Opcode.PUSH to InstructionFormat.from("BW,<r1 | F,<fr1"),
Opcode.POP to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.PUSHST to InstructionFormat.from("N"),
Opcode.POPST to InstructionFormat.from("N"),
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"),
Opcode.BREAKPOINT to InstructionFormat.from("N"),
)
class FunctionCallArgs(
var arguments: List<ArgumentSpec>,
val returns: List<RegSpec>
) {
class RegSpec(val dt: IRDataType, val registerNum: Int, val cpuRegister: RegisterOrStatusflag?)
class ArgumentSpec(val name: String, val address: Int?, val reg: RegSpec) {
init {
require(address==null || address>=0) {
"address must be >=0"
}
}
}
}
data class IRInstruction(
val opcode: Opcode,
val type: IRDataType?=null,
val reg1: Int?=null, // 0-$ffff
val reg2: Int?=null, // 0-$ffff
val reg3: Int?=null, // 0-$ffff
val fpReg1: Int?=null, // 0-$ffff
val fpReg2: Int?=null, // 0-$ffff
val immediate: Int?=null, // 0-$ff or $ffff if word
val immediateFp: Double?=null,
val address: Int?=null, // 0-$ffff
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
private val symbolOffset: Int? = null, // offset to add on labelSymbol (used to index into an array variable)
var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
val fcallArgs: FunctionCallArgs? = null // will be set for the CALL and SYSCALL instructions.
) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
val reg1direction: OperandDirection
val reg2direction: OperandDirection
val reg3direction: OperandDirection
val fpReg1direction: OperandDirection
val fpReg2direction: OperandDirection
val labelSymbolOffset = if(symbolOffset==0) null else symbolOffset
init {
if(labelSymbol!=null) {
require(labelSymbol.first() != '_') { "label/symbol should not start with underscore $labelSymbol" }
require(labelSymbol.all { it.isJavaIdentifierStart() || it.isJavaIdentifierPart() || it=='.' }) {
"label/symbol contains invalid character $labelSymbol"
}
}
if(labelSymbolOffset!=null) require(labelSymbolOffset>0 && labelSymbol!=null) {"labelsymbol offset inconsistency"}
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
require(reg3==null || reg3 in 0..65536) {"reg3 out of bounds"}
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type
if(reg1!=null && reg3!=null) require(reg1!=reg3) {"reg1 must not be same as reg3"} // note: this is ok for fpRegs as these are always the same type
if(reg2!=null && reg3!=null) require(reg2!=reg3) {"reg2 must not be same as reg3"} // note: this is ok for fpRegs as these are always the same type
val formats = instructionFormats.getValue(opcode)
require (type != null || formats.containsKey(null)) { "missing type" }
val format = formats.getOrElse(type) { throw IllegalArgumentException("type $type invalid for $opcode") }
if(format.reg1!=OperandDirection.UNUSED) require(reg1!=null) { "missing reg1" }
if(format.reg2!=OperandDirection.UNUSED) require(reg2!=null) { "missing reg2" }
if(format.reg3!=OperandDirection.UNUSED) require(reg3!=null) { "missing reg3" }
if(format.fpReg1!=OperandDirection.UNUSED) require(fpReg1!=null) { "missing fpReg1" }
if(format.fpReg2!=OperandDirection.UNUSED) require(fpReg2!=null) { "missing fpReg2" }
if(format.reg1==OperandDirection.UNUSED) require(reg1==null) { "invalid reg1" }
if(format.reg2==OperandDirection.UNUSED) require(reg2==null) { "invalid reg2" }
if(format.reg3==OperandDirection.UNUSED) require(reg3==null) { "invalid reg3" }
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
if(format.immediate) {
if(type==IRDataType.FLOAT)
require(immediateFp !=null) {"missing immediate fp value"}
else
require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
}
if(type!=IRDataType.FLOAT)
require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"}
if(format.address!=OperandDirection.UNUSED)
require(address!=null || labelSymbol!=null) {"missing an address or labelsymbol"}
if(format.immediate && (immediate!=null || immediateFp!=null)) {
if(opcode!=Opcode.SYSCALL) {
when (type) {
IRDataType.BYTE -> require(immediate in -128..255) { "immediate value out of range for byte: $immediate" }
IRDataType.WORD -> require(immediate in -32768..65535) { "immediate value out of range for word: $immediate" }
IRDataType.FLOAT, null -> {}
}
}
}
if(format.immediate) {
if(opcode==Opcode.LOAD)
require(immediate != null || immediateFp != null || labelSymbol!=null) { "missing immediate value or labelsymbol" }
else
require(immediate != null || immediateFp != null) { "missing immediate value" }
}
require(address==null || address>=0) {
"address must be >=0"
}
reg1direction = format.reg1
reg2direction = format.reg2
reg3direction = format.reg3
fpReg1direction = format.fpReg1
fpReg2direction = format.fpReg2
if(opcode==Opcode.SYSCALL) {
require(immediate!=null) {
"syscall needs immediate integer for the syscall number"
}
}
}
fun addUsedRegistersCounts(
readRegsCounts: MutableMap<Int, Int>,
writeRegsCounts: MutableMap<Int, Int>,
readFpRegsCounts: MutableMap<Int, Int>,
writeFpRegsCounts: MutableMap<Int, Int>,
regsTypes: MutableMap<Int, MutableSet<IRDataType>>
) {
when (this.reg1direction) {
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
if(type!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg1] = types
}
}
OperandDirection.WRITE -> {
writeRegsCounts[this.reg1!!] = writeRegsCounts.getValue(this.reg1)+1
if(type!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg1] = types
}
}
OperandDirection.READWRITE -> {
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
if(type!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg1] = types
}
writeRegsCounts[this.reg1] = writeRegsCounts.getValue(this.reg1)+1
if(type!=null) {
var types = regsTypes[this.reg1]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg1] = types
}
}
}
when (this.reg2direction) {
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
writeRegsCounts[this.reg2!!] = writeRegsCounts.getValue(this.reg2)+1
if(type!=null) {
var types = regsTypes[this.reg2]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg2] = types
}
}
else -> throw IllegalArgumentException("reg2 can only be read")
}
when (this.reg3direction) {
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
writeRegsCounts[this.reg3!!] = writeRegsCounts.getValue(this.reg3)+1
if(type!=null) {
var types = regsTypes[this.reg3]
if(types==null) types = mutableSetOf()
types += type
regsTypes[this.reg3] = types
}
}
else -> throw IllegalArgumentException("reg3 can only be read")
}
when (this.fpReg1direction) {
OperandDirection.UNUSED -> {}
OperandDirection.READ -> {
readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
}
OperandDirection.WRITE -> writeFpRegsCounts[this.fpReg1!!] = writeFpRegsCounts.getValue(this.fpReg1)+1
OperandDirection.READWRITE -> {
readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
writeFpRegsCounts[this.fpReg1] = writeFpRegsCounts.getValue(this.fpReg1)+1
}
}
when (this.fpReg2direction) {
OperandDirection.UNUSED -> {}
OperandDirection.READ -> readFpRegsCounts[this.fpReg2!!] = readFpRegsCounts.getValue(this.fpReg2)+1
else -> throw IllegalArgumentException("fpReg2 can only be read")
}
if(fcallArgs!=null) {
fcallArgs.returns.forEach {
if (it.dt == IRDataType.FLOAT)
writeFpRegsCounts[it.registerNum] = writeFpRegsCounts.getValue(it.registerNum) + 1
else {
writeRegsCounts[it.registerNum] = writeRegsCounts.getValue(it.registerNum) + 1
val types = regsTypes[it.registerNum]
if (types == null) {
regsTypes[it.registerNum] = mutableSetOf(it.dt)
} else {
types += it.dt
regsTypes[it.registerNum] = types
}
}
}
fcallArgs.arguments.forEach {
if(it.reg.dt==IRDataType.FLOAT)
readFpRegsCounts[it.reg.registerNum] = readFpRegsCounts.getValue(it.reg.registerNum)+1
else {
readRegsCounts[it.reg.registerNum] = readRegsCounts.getValue(it.reg.registerNum) + 1
val types = regsTypes[it.reg.registerNum]
if(types==null) {
regsTypes[it.reg.registerNum] = mutableSetOf(it.reg.dt)
} else {
types += it.reg.dt
regsTypes[it.reg.registerNum] = types
}
}
}
}
}
override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase())
when(type) {
IRDataType.BYTE -> result.add(".b ")
IRDataType.WORD -> result.add(".w ")
IRDataType.FLOAT -> result.add(".f ")
else -> result.add(" ")
}
if(this.fcallArgs!=null) {
immediate?.let { result.add(it.toHex()) } // syscall
if(labelSymbol!=null) {
// regular subroutine call
result.add(labelSymbol)
if(labelSymbolOffset!=null)
result.add("+$labelSymbolOffset")
}
address?.let { result.add(address.toHex()) } // romcall
result.add("(")
fcallArgs.arguments.forEach {
val location = if(it.address==null) {
if(it.name.isBlank()) "" else it.name+"="
} else "${it.address}="
val cpuReg = if(it.reg.cpuRegister==null) "" else {
if(it.reg.cpuRegister.registerOrPair!=null)
"@"+it.reg.cpuRegister.registerOrPair.toString()
else
"@"+it.reg.cpuRegister.statusflag.toString()
}
when(it.reg.dt) {
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,")
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,")
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,")
}
}
if(result.last().endsWith(',')) {
result.add(result.removeLast().trimEnd(','))
}
result.add(")")
val returns = fcallArgs.returns
if(returns.isNotEmpty()) {
result.add(":")
val resultParts = returns.map { returnspec ->
val cpuReg = if (returnspec.cpuRegister == null) "" else {
if (returnspec.cpuRegister.registerOrPair != null)
returnspec.cpuRegister.registerOrPair.toString()
else
returnspec.cpuRegister.statusflag.toString()
}
if (cpuReg.isEmpty()) {
when (returnspec.dt) {
IRDataType.BYTE -> "r${returnspec.registerNum}.b"
IRDataType.WORD -> "r${returnspec.registerNum}.w"
IRDataType.FLOAT -> "fr${returnspec.registerNum}.f"
}
} else {
when (returnspec.dt) {
IRDataType.BYTE -> "r${returnspec.registerNum}.b@" + cpuReg
IRDataType.WORD -> "r${returnspec.registerNum}.w@" + cpuReg
IRDataType.FLOAT -> "r${returnspec.registerNum}.f@" + cpuReg
}
}
}
result.add(resultParts.joinToString(","))
}
} else {
reg1?.let {
result.add("r$it")
result.add(",")
}
reg2?.let {
result.add("r$it")
result.add(",")
}
reg3?.let {
result.add("r$it")
result.add(",")
}
fpReg1?.let {
result.add("fr$it")
result.add(",")
}
fpReg2?.let {
result.add("fr$it")
result.add(",")
}
immediate?.let {
result.add(it.toHex())
result.add(",")
}
immediateFp?.let {
result.add(it.toString())
result.add(",")
}
address?.let {
result.add(it.toHex())
result.add(",")
}
labelSymbol?.let {
result.add(it)
if(labelSymbolOffset!=null)
result.add("+$labelSymbolOffset")
}
}
if(result.last() == ",")
result.removeLast()
return result.joinToString("").trimEnd()
}
}