mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 22:30:46 +00:00
IR: BIT instruction added
This commit is contained in:
parent
e174b31344
commit
827df04b32
@ -75,9 +75,6 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
|
||||
|
||||
|
||||
private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): Int {
|
||||
if(options.compTarget.machine.cpu == CpuType.VIRTUAL)
|
||||
return 0 // the special bittest optimization is not yet valid for the IR
|
||||
|
||||
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
|
||||
require(bitmask==128 || bitmask==64)
|
||||
val setOrNot = if(condition.operator=="!=") "set" else "notset"
|
||||
|
@ -1454,8 +1454,51 @@ class IRCodeGen(
|
||||
|
||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) {
|
||||
|
||||
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_"))
|
||||
throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND")
|
||||
fun ifElseUsingBIT(testBitSet: Boolean, value: PtIdentifier, bitnumber: Int) {
|
||||
addInstr(result, IRInstruction(Opcode.BIT, IRDataType.BYTE, labelSymbol = value.name), null)
|
||||
val bitBranchOpcode = when(testBitSet) {
|
||||
true -> when(bitnumber) {
|
||||
6 -> Opcode.BSTVC
|
||||
7 -> Opcode.BSTPOS
|
||||
else -> throw AssemblyError("need bit 6 or 7")
|
||||
}
|
||||
false -> when(bitnumber) {
|
||||
6 -> Opcode.BSTVS
|
||||
7 -> Opcode.BSTNEG
|
||||
else -> throw AssemblyError("need bit 6 or 7")
|
||||
}
|
||||
}
|
||||
|
||||
if(ifElse.hasElse()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(bitBranchOpcode, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
}
|
||||
|
||||
if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_")) {
|
||||
// use a BIT instruction to test for bit 8 or 7
|
||||
when(condition.name) {
|
||||
"prog8_ifelse_bittest_set" -> {
|
||||
ifElseUsingBIT(true, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
|
||||
return
|
||||
}
|
||||
"prog8_ifelse_bittest_notset" -> {
|
||||
ifElseUsingBIT(false, condition.args[0] as PtIdentifier, condition.args[1].asConstInteger()!!)
|
||||
return
|
||||
}
|
||||
else -> throw AssemblyError("weird bittest")
|
||||
}
|
||||
}
|
||||
|
||||
val tr = expressionEval.translateExpression(condition)
|
||||
if(addCmpiZero)
|
||||
|
@ -566,4 +566,35 @@ main {
|
||||
ifelse.hasElse() shouldBe true
|
||||
ifelse.condition shouldBe instanceOf<PtFunctionCall>()
|
||||
}
|
||||
|
||||
test("bit instruction is used for testing bits 6 and 7 of a byte") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
if cx16.r0L & ${'$'}80 != 0
|
||||
return
|
||||
if cx16.r1L & ${'$'}80 == 0
|
||||
return
|
||||
if cx16.r2L & ${'$'}40 != 0
|
||||
return
|
||||
if cx16.r3L & ${'$'}40 == 0
|
||||
return
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
|
||||
val assembly = assemblyFile.readText()
|
||||
assembly shouldContain "bit cx16.r0L"
|
||||
assembly shouldContain "bit cx16.r1L"
|
||||
assembly shouldContain "bit cx16.r2L"
|
||||
assembly shouldContain "bit cx16.r3L"
|
||||
|
||||
val resultIR = compileText(VMTarget(), true, text, writeAssembly = true)!!
|
||||
val irFile = resultIR.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
val ir = irFile.readText()
|
||||
ir shouldContain "bit.b ${'$'}ff02" // r0
|
||||
ir shouldContain "bit.b ${'$'}ff04" // r1
|
||||
ir shouldContain "bit.b ${'$'}ff06" // f2
|
||||
ir shouldContain "bit.b ${'$'}ff08" // r3
|
||||
}
|
||||
})
|
@ -22,7 +22,6 @@ Future Things and Ideas
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
|
||||
- don't do BIT instruction tests via makeBittestCall() fake builtin function. Just do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
|
||||
- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
|
||||
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
|
||||
Add a -library $xxxx command line option (and/or some directive) to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
|
||||
@ -36,6 +35,7 @@ Future Things and Ideas
|
||||
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
|
||||
|
||||
- Allow normal subroutines to return multiple values as well (just as asmsubs already can)
|
||||
- don't do BIT instruction tests via optimizeBitTest() using a fake builtin function. Do it in the code generator when it encounters the correct bitwise and sequence. (also IR)
|
||||
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first)
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- Improve register load order in subroutine call args assignments:
|
||||
@ -55,6 +55,8 @@ Future Things and Ideas
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- make a liast in the P8IR file of the data type of every used virtual register (it should have 1 unique type assigned to it when it is allocated, and never used for other types)
|
||||
- getting it in shape for code generation...
|
||||
- fix TODO("IR rol/ror on split words array")
|
||||
- fix "<< in array" / ">> in array"
|
||||
- implement missing operators in AssignmentGen (array shifts etc)
|
||||
@ -67,9 +69,6 @@ IR/VM
|
||||
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
||||
maybe another representation is needed once actual codegeneration is done from the IR...?
|
||||
- split word arrays, both _msb and _lsb arrays are tagged with an alignment. This is not what's intended; only the one put in memory first should be aligned (the other one should follow straight after it)
|
||||
- getting it in shape for code generation...
|
||||
- make optimizeBitTest work for IR too to use the BIT instruction?
|
||||
- make sure that a 6502 codegen based off the IR, still generates BIT instructions when testing bit 7 or 6 of a byte var.
|
||||
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change
|
||||
|
||||
|
||||
|
@ -1,22 +1,40 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
bool bb
|
||||
ubyte ub
|
||||
uword uw
|
||||
uw, void = thing2()
|
||||
uw, bb = thing2()
|
||||
uw, ub = thing2()
|
||||
}
|
||||
if cx16.r0L & $80 != 0
|
||||
return
|
||||
if cx16.r1L & $80 == 0
|
||||
return
|
||||
if cx16.r2L & $40 != 0
|
||||
return
|
||||
if cx16.r3L & $40 == 0
|
||||
return
|
||||
|
||||
asmsub thing2() -> ubyte @A, bool @Pc {
|
||||
%asm {{
|
||||
lda #$aa
|
||||
clc
|
||||
rts
|
||||
}}
|
||||
cx16.r0L = 0
|
||||
test()
|
||||
txt.nl()
|
||||
cx16.r0L = 255
|
||||
test()
|
||||
txt.nl()
|
||||
|
||||
sub test() {
|
||||
if cx16.r0L & $80 != 0
|
||||
txt.chrout('1')
|
||||
if cx16.r0L & $80 == 0
|
||||
txt.chrout('2')
|
||||
if cx16.r0L & $40 != 0
|
||||
txt.chrout('3')
|
||||
if cx16.r0L & $40 == 0
|
||||
txt.chrout('4')
|
||||
if cx16.r0L & $20 != 0
|
||||
txt.chrout('5')
|
||||
if cx16.r0L & $20 == 0
|
||||
txt.chrout('6')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,6 +215,7 @@ rorm address - rotate memory right by 1 bits, no
|
||||
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
|
||||
bit address - test bits in byte value at address, this is a special instruction available on other systems to optimize testing and branching on bits 7 and 6
|
||||
|
||||
|
||||
FLOATING POINT CONVERSIONS AND FUNCTIONS
|
||||
@ -400,6 +401,7 @@ enum class Opcode {
|
||||
ROLM,
|
||||
ROXL,
|
||||
ROXLM,
|
||||
BIT,
|
||||
|
||||
FFROMUB,
|
||||
FFROMSB,
|
||||
@ -476,6 +478,7 @@ val OpcodesThatBranch = arrayOf(
|
||||
)
|
||||
|
||||
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
|
||||
Opcode.BIT,
|
||||
Opcode.CMP,
|
||||
Opcode.CMPI
|
||||
)
|
||||
@ -764,6 +767,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.ROLM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ROXL to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ROXLM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.BIT to InstructionFormat.from("B,<a"),
|
||||
|
||||
Opcode.FFROMUB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
|
@ -56,6 +56,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
var statusCarry = false
|
||||
var statusZero = false
|
||||
var statusNegative = false
|
||||
var statusOverflow = false
|
||||
internal var randomGenerator = Random(0xa55a7653)
|
||||
internal var randomGeneratorFloats = Random(0xc0d3dbad)
|
||||
internal var mul16LastUpper = 0u
|
||||
@ -224,7 +225,8 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.BSTNE -> InsBSTNE(ins)
|
||||
Opcode.BSTNEG -> InsBSTNEG(ins)
|
||||
Opcode.BSTPOS -> InsBSTPOS(ins)
|
||||
Opcode.BSTVC, Opcode.BSTVS -> TODO("overflow status flag not yet supported in VM (BSTVC,BSTVS)")
|
||||
Opcode.BSTVC -> InsBSTVC(ins)
|
||||
Opcode.BSTVS -> InsBSTVS(ins)
|
||||
Opcode.BGTR -> InsBGTR(ins)
|
||||
Opcode.BGTSR -> InsBGTSR(ins)
|
||||
Opcode.BGER -> InsBGER(ins)
|
||||
@ -324,6 +326,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.CLC -> { statusCarry = false; nextPc() }
|
||||
Opcode.SEC -> { statusCarry = true; nextPc() }
|
||||
Opcode.CLI, Opcode.SEI -> throw IllegalArgumentException("VM doesn't support interrupt status bit")
|
||||
Opcode.BIT -> InsBIT(ins)
|
||||
|
||||
Opcode.FFROMUB -> InsFFROMUB(ins)
|
||||
Opcode.FFROMSB -> InsFFROMSB(ins)
|
||||
@ -786,6 +789,20 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsBSTVS(i: IRInstruction) {
|
||||
if(statusOverflow)
|
||||
branchTo(i)
|
||||
else
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsBSTVC(i: IRInstruction) {
|
||||
if(!statusOverflow)
|
||||
branchTo(i)
|
||||
else
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsBGTR(i: IRInstruction) {
|
||||
val (left: UInt, right: UInt) = getBranchOperandsU(i)
|
||||
if(left>right)
|
||||
@ -1751,6 +1768,17 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else -> throw IllegalArgumentException("operator word $operator")
|
||||
}
|
||||
|
||||
private fun InsBIT(i: IRInstruction) {
|
||||
if (i.type!! == IRDataType.BYTE) {
|
||||
val value = memory.getUB(i.address!!)
|
||||
statusNegative = value.toInt() and 0x80 != 0
|
||||
statusOverflow = value.toInt() and 0x40 != 0
|
||||
// NOTE: the 'AND' part of the BIT instruction as it does on the 6502 CPU, is not utilized in prog8 so we don't implement it here
|
||||
}
|
||||
else throw IllegalArgumentException("bit needs byte")
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsEXT(i: IRInstruction) {
|
||||
when(i.type!!){
|
||||
IRDataType.BYTE -> registers.setUW(i.reg1!!, registers.getUB(i.reg2!!).toUShort())
|
||||
|
Loading…
x
Reference in New Issue
Block a user