-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdriver.z80
313 lines (270 loc) · 8.67 KB
/
driver.z80
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
; this source file handles reading note data and SMCing certain bytes in bitbang.z80 to change what notes are playing
; the general-purpose driver, ch_driver, is relocatable
; it gets copied to RAM on start four times, the drumbass driver is appended once, this drumbass driver will also wait for the next frame and jump back to the first general-purpose driver
; relocate_a are addresses within the driver itself (org $0000 is assumed)
; relocate_w are addresses inside the pulsewave bitbang routines (thus depending on the channel)
; the drumbass driver, db_driver, is not relocatable, its origin is assumed to be driver_ram + 4 * driver.size
driver0_ram := driver_ram
driver1_ram := driver0_ram + ch_driver.size + ch0sync.size
drumbass_ram := driver1_ram + ch_driver.size + ch1sync.size
driver2_ram := drum0_ram + drum.size
driver3_ram := driver2_ram + ch_driver.size + ch2sync.size
drumlcd_ram := driver3_ram + ch_driver.size + ch3sync.size
include "driver/init.z80"
include "driver/parse_command.z80"
relocate_a.table:
relocatable relocate_a,relocate_a.len,relocate_a
relocate_w.table:
relocatable relocate_w,relocate_w.len,relocate_a
ch_driver:
db ch_driver.data
virtual at relocate_a
include "driver/general.z80"
ch_driver.size := $ - $$
load ch_driver.data: ch_driver.size from $$
end virtual
repeat 1,x:ch_driver.size
display "driver size per channel: ",`x,10
end repeat
drumbass:
db drumbass.data
virtual at drumbass_ram
call_drum_1:
call drum
include "driver/bass.z80"
call wait_frame
drumbass.size := $ - $$
load drumbass.data: drumbass.size from $$
end virtual
drumlcd:
db drumlcd.data
virtual at drumlcd_ram
call_drum_2:
call drum
lcd:
; TODO
call wait_frame
jq driver_ram
wait_frame:
in a,[$04]
and $40
jr z,wait_frame
ld a,$01
out [$34],a
in a,[$04]
and $08
ret nz
pop af
ret
smcable drum_rest_len,drum_rest_len.addr
smcable drum_mask,drum_mask.addr
smcable drum_globalmask,drum_globalmask.addr
smcable drum_pointer,drum_pointer.addr
smcable drum_len,drum_len.addr
smcable drum_sequence_pointer,drum_sequence_pointer.addr
drum:
ld a,drum_rest_len + 1 ; if (!--drum_rest_len) {
dec a
jq nz,.not_read_next
ld hl,drum_pointer
.read_next_note: ; drum_next_note:
ld a,[hl] ; drum_rest_len = *drum_pointer++;
inc hl
or a ; if (!drum_rest_len) {
jq z,.parse_command
; ... (see below)
; }
ld [drum_rest_len.addr],a
ld e,[hl] ; uint16_t read = *drum_pointer++;
inc hl
ld d,[hl] ; read |= *drum_pointer++ << 8;
inc hl
ld [drum_pointer.addr],hl
ld hl,[appvar_start] ; read += appvar_start;
add hl,de ; drum_sequence_pointer = (uint8_t *) read;
ld a,[hl] ; drum_len = *drum_sequence_pointer++;
inc hl
jq .new_note
; inc a
; ld [drum_len.addr],a
; ld [drum_sequence_pointer.addr],hl
.not_read_next: ; }
ld [drum_rest_len.addr],a
ld a,drum_len ; if (--drum_len) {
dec a
jr z,.no_drum
ld hl,drum_sequence_pointer
.new_note:
ld [drum_len.addr],a
ld a,[hl] ; uint8_t read = *drum_sequence_pointer++;
inc hl
ld [drum_sequence_pointer.addr],hl
or a ; if (read) {
jq z,.play_noise
ld [basswave.length],a ; bass.wavelength = read;
ld a,basswave and $00ff ; noise.playing = false;
ld [pulsewave3.next],a
jq .mute_bass ; } else {
.play_noise:
ld a,noisewave and $00ff ; noise.playing = true;
ld [pulsewave3.next],a
.mute_bass: ; }
ld a,_jrc ; mute_bass = true;
ld [bass.smc_mute_c],a
ld a,drum_mask + $03
and drum_globalmask + $03
ld [noisewave.mask],a
ld [basswave.mask],a
ret
.no_drum: ; } else {
; drum_len = 1;
; disable drum driver, give back control over the drumbass channel to bass driver
; ld [noisewave.mask],a ; noise.mask = 0;
; ^ not actually necessary because the bass channel will take over anyway
ld a,[bass_outlen.addr] ; bass.wavelength = bass.out.length;
ld [basswave.length],a
ld a,[bass_outmask.addr] ; bass.mask = bass.out.mask;
ld [basswave.mask],a
ld a,basswave and $00ff
ld [pulsewave3.next],a
ld a,_jrnc ; mute_bass = false;
ld [bass.smc_mute_c],a
ret ; }
.parse_command: ; the ... above goes here
ld a,[hl] ; switch (*drum_pointer++) {
inc hl
and $f0
ex de,hl
ld hl,.read_next_note
push hl
rrca
rrca
rrca
ld hl,.switch_cases
add a,l
ld l,a
adc a,h
sub l
ld h,a
ld a,[hl]
inc hl
ld h,[hl]
ld l,a
xor a
jp hl
.switch_cases:
dw cmd_nothing,.cmd_mask,cmd_nothing,cmd_nothing
dw cmd_nothing,cmd_nothing,cmd_nothing,cmd_nothing
dw .cmd_rest,cmd_jump,cmd_djnz,cmd_table_value
dw cmd_set_phase,cmd_nothing,.cmd_finish,cmd_exit_extra_pop
.cmd_mask: ; case 0x10:
ex de,hl
ld a,[hl] ; mask = *drum_pointer++;
inc hl
ld [drum_mask.addr],a
ret ; break;
.cmd_rest: ; case 0x80:
ex de,hl
ld a,[hl] ; drum_rest_len = *drum_pointer++ + 1;
inc hl
inc a
ld [drum_rest_len.addr],a
ret ; break;
.cmd_finish: ; case 0xe0:
pop hl
ld hl,channels_playing ; playing--;
dec [hl]
jq nz,.finish_no_exit
pop hl
ret
.finish_no_exit:
ld a,[bass_outlen.addr] ; bass.wavelength = bass.out.length;
ld [basswave.length],a
ld a,[bass_outmask.addr] ; bass.mask = bass.out.mask;
ld [basswave.mask],a
ld a,basswave and $00ff
ld [pulsewave3.next],a
ld a,_jrnc ; mute_bass = false;
ld [bass.smc_mute_c],a
ld a,_ldhl
ld [call_drum_1],a
ld [call_drum_2],a
ret
drumlcd.size := $ - $$
load drumlcd.data: drumlcd.size from $$
end virtual
include "driver/sync.z80"
cmd_jump: ; case 0x90:
ex de,hl
ld e,[hl] ; uint16_t ofs = *channel_pointer++;
inc hl
ld d,[hl] ; ofs |= *channel_pointer << 8;
ld hl,[appvar_start] ; channel_pointer = appvar_start + ofs;
add hl,de
ret ; break;
cmd_djnz: ; case 0xa0:
ld a,[de] ; if (--jump_counters[*bass_pointer++]) {
inc de
ld l,a
ld h,djnz_counters_ram shr 8
dec [hl]
ex de,hl
jq z,.djnz_z
ld e,[hl] ; uint16_t ofs = *channel_pointer++;
inc hl
ld d,[hl] ; ofs |= *channel_pointer << 8;
ld hl,[appvar_start] ; channel_pointer = appvar_start + ofs;
add hl,de
ret ; break;
.djnz_z: ; } else
inc hl ; channel_pointer += 2;
inc hl
ret ; break;
cmd_table_value: ; case 0xb0:
ex de,hl
ld e,[hl] ; uint8_t i = *channel_pointer++;
inc hl
ld d,djnz_counters_ram shr 8; jump_counters[i] = *channel_pointer++;
ldi
ret
cmd_set_phase: ; case 0xc0:
ld a,[de] ; uint8_t bits = *channel_pointer++;
inc de
rrca ; if (bits & 0x01)
di
exx
jq nc,.not_wave0
ld ix,0 ; channel0.phase = 0;
.not_wave0:
rrca ; if (bits & 0x02)
jq nc,.not_wave1
ld iy,0 ; channel1.phase = 0;
.not_wave1:
rrca ; if (bits & 0x04)
jq nc,.not_wave2
ld bc,0 ; channel2.phase = 0;
.not_wave2:
rrca ; if (bits & 0x08)
jq nc,.not_wave3
ld de,0 ; channel3.phase = 0;
.not_wave3:
rrca ; if (bits & 0x10)
exx ; HL cannot be destroyed, use other HL
jq nc,.not_bass
ld hl,$0000 ; bass.state = 0;
ld [basswave.state],hl
.not_bass:
rrca ; if (bits & 0x20)
jq nc,.not_noise
ld hl,$0001 ; noise.seed = 1;
ld [noisewave.seed],hl
.not_noise:
ei
ex de,hl
cmd_nothing: ; default:
ret ; break;
cmd_exit_extra_pop:
pop hl
cmd_exit: ; case 0xf0:
pop hl
ret