mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
added for loop over constant ranges
This commit is contained in:
parent
0dc592b819
commit
0a0c58d450
@ -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() {}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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),
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user