mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
made setting/restoring the IRQ vector explicit
This commit is contained in:
parent
7b41a4b5fa
commit
ac8e7f4fa9
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -236,7 +236,6 @@ enum class Opcode {
|
||||
// subroutine calling
|
||||
CALL,
|
||||
RETURN,
|
||||
RETURNFROMIRQ,
|
||||
SYSCALL,
|
||||
|
||||
// misc
|
||||
|
@ -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++
|
||||
|
@ -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),
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user