-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathsystemoff_example.mfk
137 lines (124 loc) · 3.26 KB
/
systemoff_example.mfk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// ================================================
//
// 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
}
}