mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 22:30:46 +00:00
c64 irq handling routines
This commit is contained in:
parent
aea1292f92
commit
2f9eabeac7
@ -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
|
||||
|
@ -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++
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
lda #>c64.IRQDFRT
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
"""
|
||||
else -> " jsr prog8_lib.${call.toString().toLowerCase()}"
|
||||
}
|
||||
|
||||
" jsr prog8_lib.${call.toString().toLowerCase()}"
|
||||
}
|
||||
Opcode.BREAKPOINT -> {
|
||||
breakpointCounter++
|
||||
|
@ -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),
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user