made setting/restoring the IRQ vector explicit

This commit is contained in:
Irmen de Jong 2019-01-01 17:55:01 +01:00
parent 7b41a4b5fa
commit ac8e7f4fa9
10 changed files with 94 additions and 42 deletions

View File

@ -57,15 +57,19 @@
}
c64.SPENA = 255 ; enable all sprites
set_irqvec() ; enable animation
;set_irqvec_excl() ; enable animation
}
sub foobar() {
A=99
}
}
~ irq {
; @todo no longer auto-set this as irq handler. instead, add builtins functions activate_irqvec() / restore_irqvec()
sub irq() {
;return ; @todo return statements in the irqhandler should not do rts, but instead jmp c64.IRQDFRT (RETURNFROMIRQ)
; @todo also when including this return, the jmp c64.IRQDFRT at the end gets omitted.....:(
c64.EXTCOL++
for ubyte i in 0 to 7 {
@(main.SP0Y+i*2)-- ; float up

View File

@ -171,7 +171,7 @@ private fun compileMain(args: Array<String>) {
if(startEmu) {
println("\nStarting C64 emulator...")
val cmdline = listOf("x64", "-moncommands", "$programname.vice-mon-list",
val cmdline = listOf("x64", "-silent", "-moncommands", "$programname.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programname+".prg")
val process = ProcessBuilder(cmdline).inheritIO().start()
process.waitFor()

View File

@ -100,7 +100,7 @@ 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.
// which will be used as the 60hz irq routine in the vm if it's present (and enabled via set_irqvec()/set_irqvec_excl())
val irqBlock = module.statements.singleOrNull { it is Block && it.name=="irq" } as? Block?
val irqSub = irqBlock?.subScopes()?.get("irq") as? Subroutine
if(irqSub!=null) {
@ -238,25 +238,17 @@ 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 amount = subroutine.statements
val amountOfRtsInAsm = subroutine.statements
.asSequence()
.filter { it is InlineAssembly }
.map { (it as InlineAssembly).assembly }
.count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it }
if (amount == 0) {
if(subroutine.returntypes.isNotEmpty()) {
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
if (amountOfRtsInAsm == 0) {
if (subroutine.returntypes.isNotEmpty()) {
// for asm subroutines with an address, no statement check is possible.
if(subroutine.asmAddress==null)
if (subroutine.asmAddress == null)
err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)")
}
// if there's no return statement, we add the implicit one at the end, but only if it's not a kernel routine.
// @todo move this out of the astchecker
if(subroutine.asmAddress==null) {
if(subroutine.name=="irq" && subroutine.definingScope().name=="irq") {
subroutine.statements.add(ReturnFromIrq(subroutine.position))
} else
subroutine.statements.add(Return(emptyList(), subroutine.position))
}
}
}

View File

@ -97,6 +97,18 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
val directives = subroutine.statements.filter {it is Directive && it.directive in directivesToMove}
subroutine.statements.removeAll(directives)
subroutine.statements.addAll(0, directives)
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) {
if (subroutine.statements.lastOrNull {it !is VarDecl} !is Return) {
val returnStmt = Return(emptyList(), subroutine.position)
returnStmt.linkParents(subroutine)
subroutine.statements.add(returnStmt)
}
}
}
return subroutine
}

View File

@ -1605,11 +1605,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.RETURN)
}
private fun translate(stmt: ReturnFromIrq) {
prog.line(stmt.position)
prog.instr(Opcode.RETURNFROMIRQ)
}
private fun translate(stmt: Label) {
prog.label(stmt.scopedname)
}

View File

@ -236,7 +236,6 @@ enum class Opcode {
// subroutine calling
CALL,
RETURN,
RETURNFROMIRQ,
SYSCALL,
// misc

View File

@ -179,19 +179,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
out("\tldx #\$ff\t; init estack pointer")
out("\tclc")
val irqBlock = program.blocks.singleOrNull { it.scopedname=="irq" }
val haveIrqSub = irqBlock?.instructions?.any { it is LabelInstr && it.name=="irq"}
if(haveIrqSub==true) {
out("\t; install custom irq vector")
out("\tsei")
out("\tlda #<irq.irq")
out("\tsta c64.CINV")
out("\tlda #>irq.irq")
out("\tsta c64.CINV+1")
out("\tcli")
}
out("\tjmp main.start\t; jump to program entrypoint")
out("")
@ -422,7 +409,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.JUMP -> " jmp ${ins.callLabel}"
Opcode.CALL -> " jsr ${ins.callLabel}"
Opcode.RETURN -> " rts"
Opcode.RETURNFROMIRQ -> " jmp c64.IRQDFRT\t\t; continue with normal kernel irq routine"
Opcode.RSAVE -> {
// save cpu status flag and all registers A, X, Y.
// see http://6502.org/tutorials/register_preservation.html
@ -444,7 +430,49 @@ 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() }
" jsr prog8_lib.${call.toString().toLowerCase()}"
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
lda #>c64.IRQDFRT
sta c64.CINV+1
cli
"""
else -> " jsr prog8_lib.${call.toString().toLowerCase()}"
}
}
Opcode.BREAKPOINT -> {
breakpointCounter++

View File

@ -54,6 +54,9 @@ 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),
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),

View File

@ -69,7 +69,10 @@ enum class Syscall(val callNr: Short) {
FUNC_SUM_B(131),
FUNC_SUM_UW(132),
FUNC_SUM_W(133),
FUNC_SUM_F(134)
FUNC_SUM_F(134),
FUNC_SET_IRQVEC(135),
FUNC_SET_IRQVEC_EXCL(136),
FUNC_RESTORE_IRQVEC(137)
// note: not all builtin functions of the Prog8 language are present as functions:
// some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)!
@ -773,7 +776,7 @@ class StackVm(private var traceOutputFile: String?) {
}
Opcode.CALL ->
callstack.push(ins.nextAlt)
Opcode.RETURN, Opcode.RETURNFROMIRQ -> {
Opcode.RETURN -> {
if(callstack.empty())
throw VmTerminationException("return instruction with empty call stack")
return callstack.pop()
@ -1609,6 +1612,8 @@ 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 -> TODO()
Syscall.FUNC_RESTORE_IRQVEC -> TODO()
}
}

View File

@ -622,10 +622,24 @@ set_carry() / clear_carry()
Set (or clear) the CPU status register Carry flag. No result value.
(translated into ``SEC`` or ``CLC`` cpu instruction)
set_irqd() / clear_irqd()
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