millfork/examples/a8/systemoff_example.mfk

138 lines
3.3 KiB
Plaintext

// ================================================
//
// antic_nmien = $40
//
// %01000000 $40 VBI
// %10000000 $80 DLI
// %11000000 $c0 VBI + DLI
//
// ================================================
//
// pia_portb = $fe
//
// PORTB_BASIC_OFF + PORTB_SELFTEST_OFF + %01111100
//
// PORTB_SELFTEST_OFF = %10000000; portb bit value to turn Self-Test off
// PORTB_BASIC_OFF = %00000010; portb bit value to turn Basic off
// PORTB_SYSTEM_ON = %00000001; portb bit value to turn System on
//
// ================================================
//
// Now the routine that lets you get to
// the RAM that is under the OS.
// There are actually 2 memory areas
// present:
// 4K at $C000 to $CFFF, 49152 to 53247
// 10K at $D800 to $FFFF, 55296 to 65535
//
// The last 6 bytes of the 10K area are not
// usable, since that is where the interrupt
// routines are located. Therefore do not
// use any RAM above $FFF9 (65529) or you
// will crash the system.
//
// ================================================
byte nmien = $c0
byte rti @ $15 // default routine for VBI & DLI
word vbivec @ $10 // vector for VBI
word vdslst @ $16 // vector for DLI
// simple display list; LMS = $e000
const array(byte) dl align(32) = [
$70,$70,$70,
$42,$00,$e0,2,2,$f0,2,2,2,$f0,2,2,2,
$41,@word[dl.addr]
]
// init procedure
void system_off(){
asm { sei } // turn off IRQ
antic_nmien = 0 // turn off NMI
pia_portb = $fe // turn off ROM
rti = $40 // set RTI opcode
vbivec = rti.addr // set address for VBI routine
vdslst = rti.addr // set address for DLI routine
os_NMIVEC = nmi.addr // set address for custom NMI handler
antic_nmien = nmien
}
// custom NMI handler
asm void nmi(){
bit antic_nmist // test nmist
bpl .vblclock // if 7-bit not set handle VBI
jmp (vdslst) // indirect jump to DLI routine
.vblclock: // RTCLOK maintainer
inc os_RTCLOK.b2
bne .tickend
inc os_RTCLOK.b1
bne .tickend
inc os_RTCLOK.b0
.tickend:
jmp (vbivec) // indirect jump to VBI routine
}
// example dli
interrupt asm void dli_first(){
pha
lda #$2a
sta gtia_colpf2
sta antic_wsync
lda #<dli_second.addr
sta vdslst.lo
lda #>dli_second.addr
sta vdslst.hi
pla
rti
}
// example dli
interrupt void dli_second(){
gtia_colpf2 = $de
antic_wsync = $de
vdslst = dli_first.addr
}
// wait for VBLANK
asm void pause() {
lda os_RTCLOK.b2
.rt_check:
cmp os_RTCLOK.b2
beq .rt_check
rts
}
// wait 0-255 frames
noinline asm void wait(byte register(a) f) {
clc
adc os_RTCLOK.b2
.rt_check:
cmp os_RTCLOK.b2
bne .rt_check
rts
}
// example vbi
interrupt void vbi(){
gtia_colpf2 = os_RTCLOK.b2
}
// main procedure
void main(){
system_off() // turn off OS
wait(100) // waint 2 sec on PAL for fun
antic_dlist = dl.addr // set custom display list
wait(100) // waint 2 sec on PAL for the lulz
vbivec = vbi.addr // set custom VBI
wait(100) // waint 2 sec on PAL because we can
vdslst = dli_first.addr // set custom DLI
while(true){
wait(100)
nmien ^= %10000000 // toggle DLI
antic_nmien = nmien
}
}