diff --git a/compiler/examples/cube3d.p8 b/compiler/examples/cube3d.p8 index c0d868ad7..0352a8743 100644 --- a/compiler/examples/cube3d.p8 +++ b/compiler/examples/cube3d.p8 @@ -6,6 +6,7 @@ ubyte time_changed sub irq() { + ; activated automatically if run in StackVm global_time++ time_changed = 1 } @@ -31,7 +32,6 @@ float[len(zcoor)] rotatedz sub start() { - set_irqvec() while true { if irq.time_changed { irq.time_changed = 0 diff --git a/compiler/examples/sprites.p8 b/compiler/examples/sprites.p8 index a379fc6bb..fce18ac57 100644 --- a/compiler/examples/sprites.p8 +++ b/compiler/examples/sprites.p8 @@ -53,24 +53,28 @@ @(SP0Y+i*2) = rnd() } - c64.SPENA = 255 ; enable all sprites - - set_irqvec() ; enable animation + c64.SPENA = 255 ; enable all sprites + c64utils.set_rasterirq(51) ; enable animation } } ~ irq { sub irq() { + c64.EXTCOL-- ; float up & wobble horizontally + + ; @todo for loop with step 2 doesn't work + for ubyte i in 0 to 7 { - @(main.SP0Y+i*2)-- + @(main.SP0Y+i+i)-- ubyte r = rnd() if r>208 - @(main.SP0X+i*2)++ + @(main.SP0X+i+i)++ else if r<48 - @(main.SP0X+i*2)-- + @(main.SP0X+i+i)-- } + c64.EXTCOL++ } } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 4ba671d6e..a77106a2b 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1658,6 +1658,12 @@ class Subroutine(override val name: String, override fun toString(): String { return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" } + + fun amountOfRtsInAsm(): Int = statements + .asSequence() + .filter { it is InlineAssembly } + .map { (it as InlineAssembly).assembly } + .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 8d2825db4..3da39979b 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -100,12 +100,21 @@ class AstChecker(private val namespace: INameScope, } // there can be an optional 'irq' block with a 'irq' subroutine in it, - // which will be used as the 60hz irq routine in the vm if it's present (and enabled via set_irqvec()/set_irqvec_excl()) + // which will be used as the 60hz irq routine in the vm if it's present val irqBlock = module.statements.singleOrNull { it is Block && it.name=="irq" } as? Block? val irqSub = irqBlock?.subScopes()?.get("irq") as? Subroutine if(irqSub!=null) { if(irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty()) checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)) + } else { + // @todo this is a little hack to make the assembler happy; + // certain assembler routines are -for now- always included and *require* an irq.irq routine to be present + val pos = module.statements.last().position + val dummyIrqBlock = Block("irq", address = null, statements = mutableListOf( + Subroutine("irq", listOf(), listOf(), listOf(), listOf(), setOf(), null, true,mutableListOf(), pos) + ), position = pos) + dummyIrqBlock.linkParents(module) + module.statements.add(dummyIrqBlock) } } @@ -238,12 +247,7 @@ class AstChecker(private val namespace: INameScope, // subroutine must contain at least one 'return' or 'goto' // (or if it has an asm block, that must contain a 'rts' or 'jmp') if(subroutine.statements.count { it is Return || it is Jump } == 0) { - val amountOfRtsInAsm = subroutine.statements - .asSequence() - .filter { it is InlineAssembly } - .map { (it as InlineAssembly).assembly } - .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } - if (amountOfRtsInAsm == 0) { + if (subroutine.amountOfRtsInAsm() == 0) { if (subroutine.returntypes.isNotEmpty()) { // for asm subroutines with an address, no statement check is possible. if (subroutine.asmAddress == null) diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 8248c36bf..31e492a13 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -101,7 +101,8 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He if(subroutine.returntypes.isEmpty()) { // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine. - if(subroutine.asmAddress==null) { + // and if an assembly block doesn't contain a rts/rti + if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) { if (subroutine.statements.lastOrNull {it !is VarDecl} !is Return) { val returnStmt = Return(emptyList(), subroutine.position) returnStmt.linkParents(subroutine) diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 29857534f..7da556aba 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -433,49 +433,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr }) throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}") val call = Syscall.values().find { it.callNr==ins.arg.numericValue() } - when (call) { - Syscall.FUNC_SET_IRQVEC -> - """ - sei - lda #<_prog8_irq_handler - sta c64.CINV - lda #>_prog8_irq_handler - sta c64.CINV+1 - cli - jmp + - -_prog8_irq_handler jsr irq.irq - jmp c64.IRQDFRT ; continue with normal kernel irq routine -+ - """ - Syscall.FUNC_SET_IRQVEC_EXCL -> - """ - sei - lda #<_prog8_irq_handler_excl - sta c64.CINV - lda #>_prog8_irq_handler_excl - sta c64.CINV+1 - cli - jmp + - -_prog8_irq_handler_excl - jsr irq.irq - lda ${'$'}dc0d ; acknowledge CIA interrupt - jmp c64.IRQDFEND ; end irq processing - don't call kernel -+ - """ - Syscall.FUNC_RESTORE_IRQVEC -> - """ - sei - lda #c64.IRQDFRT - sta c64.CINV+1 - cli - """ - else -> " jsr prog8_lib.${call.toString().toLowerCase()}" - } - + " jsr prog8_lib.${call.toString().toLowerCase()}" } Opcode.BREAKPOINT -> { breakpointCounter++ diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 753efcbc7..6ccbf4dc2 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -54,10 +54,7 @@ val BuiltinFunctions = mapOf( "clear_carry" to FunctionSignature(false, emptyList(), null), "set_irqd" to FunctionSignature(false, emptyList(), null), "clear_irqd" to FunctionSignature(false, emptyList(), null), - "set_irqvec" to FunctionSignature(false, emptyList(), null), - "set_irqvec_excl" to FunctionSignature(false, emptyList(), null), - "restore_irqvec" to FunctionSignature(false, emptyList(), null), - "memcopy" to FunctionSignature(false, listOf( + "memcopy" to FunctionSignature(false, listOf( BuiltinFunctionParam("from", IntegerDatatypes + IterableDatatypes), BuiltinFunctionParam("to", IntegerDatatypes + IterableDatatypes), BuiltinFunctionParam("numbytes", IntegerDatatypes)), null), diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index fc06d1271..5f0310bc1 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -70,9 +70,6 @@ enum class Syscall(val callNr: Short) { FUNC_SUM_UW(132), FUNC_SUM_W(133), FUNC_SUM_F(134), - FUNC_SET_IRQVEC(135), - FUNC_SET_IRQVEC_EXCL(136), - FUNC_RESTORE_IRQVEC(137), FUNC_MEMCOPY(138) // note: not all builtin functions of the Prog8 language are present as functions: @@ -133,7 +130,7 @@ class StackVm(private var traceOutputFile: String?) { private val rnd = Random() private val bootTime = System.currentTimeMillis() private lateinit var currentIns: Instruction - private var irqStartInstruction: Instruction? = null // set to first instr of irq routine, if any + private var irqStartInstruction: Instruction? = null var sourceLine: String = "" private set @@ -161,7 +158,7 @@ class StackVm(private var traceOutputFile: String?) { P_irqd = false sourceLine = "" currentIns = this.program[0] - irqStartInstruction = null + irqStartInstruction = labels["irq.irq"] // set to first instr of irq routine, if any } fun step(instructionCount: Int = 5000) { @@ -1647,12 +1644,6 @@ class StackVm(private var traceOutputFile: String?) { val value = heap.get(iterable.heapId) evalstack.push(Value(DataType.UBYTE, if (value.array!!.all { v -> v != 0 }) 1 else 0)) } - Syscall.FUNC_SET_IRQVEC, Syscall.FUNC_SET_IRQVEC_EXCL -> { - irqStartInstruction = labels["irq.irq"] - } - Syscall.FUNC_RESTORE_IRQVEC -> { - irqStartInstruction = null - } Syscall.FUNC_MEMCOPY -> { val numbytes = evalstack.pop().integerValue() val to = evalstack.pop().integerValue() diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 37347b7fe..e6af81ce8 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -633,20 +633,6 @@ set_irqd() / clear_irqd() Set (or clear) the CPU status register Interrupt Disable flag. No result value. (translated into ``SEI`` or ``CLI`` cpu instruction) -set_irqvec_excl() - Sets the system's IRQ vector to the special ``irq.irq`` subroutine exclusively -- the system's - default IRQ handler is no longer called. The routine should be defined as a parameterless subroutine - ``irq`` in a block ``irq``. - -set_irqvec() - Add the special ``irq.irq`` subroutine to the system's IRQ vector -- the system's - default IRQ handler will still be called after your custom subroutine finishes. - The routine should be defined as a parameterless subroutine - ``irq`` in a block ``irq``. - -restore_irqvec() - Restore the IRQ vector to the default system IRQ handling subroutine. - rsave() Saves the CPU registers and the status flags. You can now more or less 'safely' use the registers directly, until you diff --git a/prog8lib/c64lib.p8 b/prog8lib/c64lib.p8 index 4c5661c8f..974b19b2e 100644 --- a/prog8lib/c64lib.p8 +++ b/prog8lib/c64lib.p8 @@ -94,6 +94,44 @@ ; ---- end of VIC-II registers ---- +; ---- CIA 1 & 2 registers ---- + + memory ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive + memory ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port + memory ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column + memory ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row + memory ubyte CIA1TALO = $DC04 ; CIA 1 timer A low byte + memory ubyte CIA1TAHI = $DC05 ; CIA 1 timer A high byte + memory ubyte CIA1TBLO = $DC06 ; CIA 1 timer B low byte + memory ubyte CIA1TBHI = $DC07 ; CIA 1 timer B high byte + memory ubyte CIA1TOD10 = $DC08 ; time of day, 1/10 sec. + memory ubyte CIA1TODS = $DC09 ; time of day, seconds + memory ubyte CIA1TODM = $DC0A ; time of day, minutes + memory ubyte CIA1TODH = $DC0B ; time of day, hours + memory ubyte CIA1SDR = $DC0C ; Serial Data Register + memory ubyte CIA1ICR = $DC0D + memory ubyte CIA1CRA = $DC0E + memory ubyte CIA1CRB = $DC0F + + memory ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address + memory ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT + memory ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address + memory ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT + memory ubyte CIA2TALO = $DD04 ; CIA 2 timer A low byte + memory ubyte CIA2TAHI = $DD05 ; CIA 2 timer A high byte + memory ubyte CIA2TBLO = $DD06 ; CIA 2 timer B low byte + memory ubyte CIA2TBHI = $DD07 ; CIA 2 timer B high byte + memory ubyte CIA2TOD10 = $DD08 ; time of day, 1/10 sec. + memory ubyte CIA2TODS = $DD09 ; time of day, seconds + memory ubyte CIA2TODM = $DD0A ; time of day, minutes + memory ubyte CIA2TODH = $DD0B ; time of day, hours + memory ubyte CIA2SDR = $DD0C ; Serial Data Register + memory ubyte CIA2ICR = $DD0D + memory ubyte CIA2CRA = $DD0E + memory ubyte CIA2CRB = $DD0F + +; ---- end of CIA registers ---- + ; ---- C64 basic and kernal ROM float constants and functions ---- ; note: the fac1 and fac2 are working registers and take 6 bytes each, diff --git a/prog8lib/c64utils.p8 b/prog8lib/c64utils.p8 index a6642b435..b655e6754 100644 --- a/prog8lib/c64utils.p8 +++ b/prog8lib/c64utils.p8 @@ -408,7 +408,120 @@ _mod2b lda #0 ; self-modified bne - _done rts .pend -}} + +}} + + +asmsub set_irqvec_excl() -> clobbers(A) -> () { + %asm {{ + sei + lda #<_irq_handler + sta c64.CINV + lda #>_irq_handler + sta c64.CINV+1 + cli + rts +_irq_handler jsr irq.irq + lda #$ff + sta c64.VICIRQ ; acknowledge raster irq + lda c64.CIA1ICR ; acknowledge CIA1 interrupt + jmp c64.IRQDFEND ; end irq processing - don't call kernel + }} +} + +asmsub set_irqvec() -> clobbers(A) -> () { + %asm {{ + sei + lda #<_irq_handler + sta c64.CINV + lda #>_irq_handler + sta c64.CINV+1 + cli + rts +_irq_handler jsr irq.irq + jmp c64.IRQDFRT ; continue with normal kernel irq routine + + }} +} + + +asmsub restore_irqvec() -> clobbers() -> () { + %asm {{ + sei + lda #c64.IRQDFRT + sta c64.CINV+1 + lda #0 + sta c64.IREQMASK ; disable raster irq + lda #%10000001 + sta c64.CIA1ICR ; restore CIA1 irq + cli + rts + }} +} + + +asmsub set_rasterirq(uword rasterpos @ AY) -> clobbers(A) -> () { + %asm {{ + sei + jsr _setup_raster_irq + lda #<_raster_irq_handler + sta c64.CINV + lda #>_raster_irq_handler + sta c64.CINV+1 + cli + rts + +_raster_irq_handler + jsr irq.irq + lda #$ff + sta c64.VICIRQ ; acknowledge raster irq + jmp c64.IRQDFRT + +_setup_raster_irq + pha + lda #%01111111 + sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1 + sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2 + and c64.SCROLY + sta c64.SCROLY ; clear most significant bit of raster position + lda c64.CIA1ICR ; ack previous irq + lda c64.CIA2ICR ; ack previous irq + pla + sta c64.RASTER ; set the raster line number where interrupt should occur + cpy #0 + beq + + lda c64.SCROLY + ora #%10000000 + sta c64.SCROLY ; set most significant bit of raster position ++ lda #%00000001 + sta c64.IREQMASK ;enable raster interrupt signals from vic + rts + }} +} + +asmsub set_rasterirq_excl(uword rasterpos @ AY) -> clobbers(A) -> () { + %asm {{ + sei + jsr set_rasterirq._setup_raster_irq + lda #<_raster_irq_handler + sta c64.CINV + lda #>_raster_irq_handler + sta c64.CINV+1 + cli + rts + +_raster_irq_handler + jsr irq.irq + lda #$ff + sta c64.VICIRQ ; acknowledge raster irq + jmp c64.IRQDFEND ; end irq processing - don't call kernel + + }} +} + + } ; ------ end of block c64utils diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index 252812060..3065a6349 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -476,7 +476,6 @@ abs_f .proc add_w .proc ; -- push word+word / uword+uword - ; @todo INLINE THIS inx clc lda ESTACK_LO,x @@ -490,7 +489,6 @@ add_w .proc sub_w .proc ; -- push word-word - ; @todo INLINE THIS inx sec lda ESTACK_LO+1,x