added for loop over constant ranges

This commit is contained in:
Irmen de Jong 2022-03-30 23:40:39 +02:00
parent 0dc592b819
commit 0a0c58d450
7 changed files with 174 additions and 60 deletions

View File

@ -126,8 +126,8 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
get() = children[0] as PtExpression
val to: PtExpression
get() = children[1] as PtExpression
val step: PtExpression
get() = children[2] as PtExpression
val step: PtNumber
get() = children[2] as PtNumber
override fun printProperties() {}
}

View File

@ -54,6 +54,8 @@ class CodeGen(internal val program: PtProgram,
vmprog.addBlock(translate(block))
}
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
return vmprog
}
@ -103,7 +105,7 @@ class CodeGen(internal val program: PtProgram,
is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}")
else -> TODO("missing codegen for $node")
}
if(code.lines.isNotEmpty())
if(code.lines.isNotEmpty() && node.position.line!=0)
code.lines.add(0, VmCodeComment(node.position.toString()))
return code
}
@ -114,9 +116,10 @@ class CodeGen(internal val program: PtProgram,
val code = VmCodeChunk()
when(iterable) {
is PtRange -> {
TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ")
iterable.printIndented(0)
TODO("forloop over range")
if(iterable.from is PtNumber && iterable.to is PtNumber)
code += translateForInConstantRange(forLoop, loopvar)
else
code += translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
val arrayAddress = allocations.get(iterable.targetName)
@ -139,19 +142,9 @@ class CodeGen(internal val program: PtProgram,
} else {
// iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt).toUInt()
val lengthBytes = iterableVar.length!! * elementSize.toInt()
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * elementSize
val lengthReg = vmRegisters.nextFree()
/*
index = 0
_loop:
if index==(length * <element_size>) goto _end
loopvar = read_mem(iterable+index)
<statements>
index += <elementsize>
goto _loop
_end: ...
*/
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel)
@ -159,7 +152,7 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements)
code += addConst(VmDataType.BYTE, indexReg, elementSize)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel)
code += VmCodeLabel(endLabel)
}
@ -169,17 +162,111 @@ class CodeGen(internal val program: PtProgram,
return code
}
private fun addConst(dt: VmDataType, reg: Int, value: UInt): VmCodeChunk {
private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
val iterable = forLoop.iterable as PtRange
TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in non-constant range ${iterable} ")
iterable.printIndented(0)
}
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): VmCodeChunk {
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
val range = IntProgression.fromClosedRange(
(iterable.from as PtNumber).number.toInt(),
(iterable.to as PtNumber).number.toInt() + step,
step)
if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0")
val loopLabel = createLabelName()
val loopvarAddress = allocations.get(loopvar.scopedName)
val indexReg = vmRegisters.nextFree()
val endvalueReg = vmRegisters.nextFree()
val vmDt = vmType(loopvar.dt)
val code = VmCodeChunk()
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=endvalueReg, value=range.last)
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=indexReg, value=range.first)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1=indexReg, value=loopvarAddress)
code += VmCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
if(range.step==1 || range.step==2) {
code += addConstMem(vmDt, loopvarAddress.toUInt(), range.step)
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = indexReg, value = loopvarAddress)
} else {
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = indexReg, value = loopvarAddress)
code += addConstReg(vmDt, indexReg, range.step)
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = indexReg, value = loopvarAddress)
}
code += VmCodeInstruction(Opcode.BNE, vmDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel)
return code
}
private fun addConstReg(dt: VmDataType, reg: Int, value: Int): VmCodeChunk {
val code = VmCodeChunk()
when(value) {
0u -> { /* do nothing */ }
1u -> {
0 -> { /* do nothing */ }
1 -> {
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
}
2 -> {
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
code += VmCodeInstruction(Opcode.INC, dt, reg1=reg)
}
-1 -> {
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
}
-2 -> {
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
code += VmCodeInstruction(Opcode.DEC, dt, reg1=reg)
}
else -> {
val valueReg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt())
code += VmCodeInstruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg)
if(value>0) {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = reg, reg2 = reg, reg3 = valueReg)
}
else {
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value= -value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = reg, reg2 = reg, reg3 = valueReg)
}
}
}
return code
}
private fun addConstMem(dt: VmDataType, address: UInt, value: Int): VmCodeChunk {
val code = VmCodeChunk()
when(value) {
0 -> { /* do nothing */ }
1 -> {
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
}
2 -> {
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
code += VmCodeInstruction(Opcode.INCM, dt, value=address.toInt())
}
-1 -> {
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
}
-2 -> {
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
code += VmCodeInstruction(Opcode.DECM, dt, value=address.toInt())
}
else -> {
val valueReg = vmRegisters.nextFree()
val operandReg = vmRegisters.nextFree()
if(value>0) {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=value)
code += VmCodeInstruction(Opcode.ADD, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
else {
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=valueReg, value=address.toInt())
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=operandReg, value=-value)
code += VmCodeInstruction(Opcode.SUB, dt, reg1 = valueReg, reg2 = valueReg, reg3 = operandReg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=valueReg, value=address.toInt())
}
}
}
return code

View File

@ -483,7 +483,7 @@ class IntermediateAstMaker(val program: Program) {
val range=PtRange(type, srcRange.position)
range.add(transformExpression(srcRange.from))
range.add(transformExpression(srcRange.to))
range.add(transformExpression(srcRange.step))
range.add(transformExpression(srcRange.step) as PtNumber)
return range
}

View File

@ -34,8 +34,11 @@ main {
txt.print_uw(ww) ; 11
txt.nl()
for bc in 10 to 20 step 3 {
; 10,13,16,19
const ubyte rfrom = 10
const ubyte rto = 17
for bc in rfrom to rto step 2 {
; 10,12,14,16
txt.print_ub(bc)
txt.spc()
ww++
@ -43,7 +46,7 @@ main {
txt.print_uw(ww) ; 15
txt.nl()
for bc in 30 to 10 step -4 {
for bc in 30 to 0 step -4 {
; 30,26,22,18,14,10,6,2
txt.print_ub(bc)
txt.spc()

View File

@ -61,28 +61,28 @@ BRANCHING
---------
All have type b or w.
bz reg1, value - branch if reg1 is zero
bnz reg1, value - branch if reg1 is not zero
beq reg1, reg2, value - jump to location in program given by value, if reg1 == reg2
bne reg1, reg2, value - jump to location in program given by value, if reg1 != reg2
blt reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (unsigned)
blts reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (signed)
ble reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (unsigned)
bles reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (signed)
bgt reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (unsigned)
bgts reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (signed)
bge reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (unsigned)
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed)
seq reg1, reg2, reg3 - set reg=1 if reg2 == reg3, otherwise set reg1=0
sne reg1, reg2, reg3 - set reg=1 if reg2 != reg3, otherwise set reg1=0
slt reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (unsigned), otherwise set reg1=0
slts reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (signed), otherwise set reg1=0
sle reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (unsigned), otherwise set reg1=0
sles reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (signed), otherwise set reg1=0
sgt reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (unsigned), otherwise set reg1=0
sgts reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (signed), otherwise set reg1=0
sge reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (unsigned), otherwise set reg1=0
sges reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (signed), otherwise set reg1=0
bz reg1, location - branch to location if reg1 is zero
bnz reg1, location - branch to location if reg1 is not zero
beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2
bne reg1, reg2, location - jump to location in program given by location, if reg1 != reg2
blt reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (unsigned)
blts reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (signed)
ble reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (unsigned)
bles reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (signed)
bgt reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgts reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (signed)
bge reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bges reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (signed)
seq reg1, reg2, reg3 - set reg=1 if reg2 == reg3, otherwise set reg1=0
sne reg1, reg2, reg3 - set reg=1 if reg2 != reg3, otherwise set reg1=0
slt reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (unsigned), otherwise set reg1=0
slts reg1, reg2, reg3 - set reg=1 if reg2 < reg3 (signed), otherwise set reg1=0
sle reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (unsigned), otherwise set reg1=0
sles reg1, reg2, reg3 - set reg=1 if reg2 <= reg3 (signed), otherwise set reg1=0
sgt reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (unsigned), otherwise set reg1=0
sgts reg1, reg2, reg3 - set reg=1 if reg2 > reg3 (signed), otherwise set reg1=0
sge reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (unsigned), otherwise set reg1=0
sges reg1, reg2, reg3 - set reg=1 if reg2 >= reg3 (signed), otherwise set reg1=0
TODO: support for the prog8 special branching instructions if_XX (bcc, bcs etc.)
but we don't have any 'processor flags' whatsoever in the vm so it's a bit weird
@ -95,7 +95,9 @@ All have type b or w. Note: result types are the same as operand types! E.g. byt
ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (latter not yet implemented as we don't have longs yet)
exts reg1 - reg1 = signed extension of reg1 (byte to word, or word to long) (note: latter ext.w, not yet implemented 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
add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed)
sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signed)
@ -181,7 +183,9 @@ enum class Opcode {
SGES,
INC,
INCM,
DEC,
DECM,
NEG,
ADD,
SUB,
@ -225,19 +229,22 @@ data class Instruction(
val value: Int?=null, // 0-$ffff
val symbol: List<String>?=null // alternative to value
) {
override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase())
init {
val format = instructionFormats.getValue(opcode)
if(format.datatypes.isNotEmpty() && type==null)
throw IllegalArgumentException("missing type")
if(format.reg1 && reg1==null ||
format.reg2 && reg2==null ||
format.reg3 && reg3==null)
format.reg2 && reg2==null ||
format.reg3 && reg3==null)
throw IllegalArgumentException("missing a register")
if(format.value && (value==null && symbol==null))
throw IllegalArgumentException("missing a value or symbol")
}
override fun toString(): String {
val result = mutableListOf(opcode.name.lowercase())
when(type) {
VmDataType.BYTE -> result.add(".b ")
@ -320,7 +327,9 @@ val instructionFormats = mutableMapOf(
Opcode.SGES to InstructionFormat(BW, true, true, true, false),
Opcode.INC to InstructionFormat(BW, true, false, false, false),
Opcode.INCM to InstructionFormat(BW, false, false, false, true ),
Opcode.DEC to InstructionFormat(BW, true, false, false, false),
Opcode.DECM to InstructionFormat(BW, false, false, false, true ),
Opcode.NEG to InstructionFormat(BW, true, false, false, false),
Opcode.ADD to InstructionFormat(BW, true, true, true, false),
Opcode.SUB to InstructionFormat(BW, true, true, true, false),

View File

@ -121,7 +121,9 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.SGES -> InsSGES(ins)
Opcode.INC -> InsINC(ins)
Opcode.INCM -> InsINCM(ins)
Opcode.DEC -> InsDEC(ins)
Opcode.DECM -> InsDECM(ins)
Opcode.NEG -> InsNEG(ins)
Opcode.ADD -> InsADD(ins)
Opcode.SUB -> InsSUB(ins)
@ -507,6 +509,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsINCM(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> memory.setUB(i.value!!, (memory.getUB(i.value)+1u).toUByte())
VmDataType.WORD -> memory.setUW(i.value!!, (memory.getUW(i.value)+1u).toUShort())
}
pc++
}
private fun InsDEC(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)-1u).toUByte())
@ -515,6 +525,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsDECM(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> memory.setUB(i.value!!, (memory.getUB(i.value)-1u).toUByte())
VmDataType.WORD -> memory.setUW(i.value!!, (memory.getUW(i.value)-1u).toUShort())
}
pc++
}
private fun InsNEG(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1).toInt()).toUByte())

View File

@ -47,23 +47,20 @@ class TestInstructions: FunSpec({
}
test("missing type should fail") {
val ins = Instruction(Opcode.BZ, reg1=42, value=9999)
shouldThrow<IllegalArgumentException> {
ins.toString()
Instruction(Opcode.BZ, reg1=42, value=9999)
}
}
test("missing registers should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
shouldThrow<IllegalArgumentException> {
ins.toString()
Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
}
}
test("missing value should fail") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
shouldThrow<IllegalArgumentException> {
ins.toString()
Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
}
}