Fixed the new lexer.
[m68k-assembler.git] / tests / ymamoto.s
blobd9502d28d8034729a654e6a8139f52f1ba10d990
2 * XXX NOTE XXX
3 * This version is for testing m68k-assembler. It's certainly not the
4 * most up to date one (look for the ymamoto or mumble packages for that).
7 * YMamoto - a playback routine for the Atari ST
8 * Julian Squires / 2004-2005
10 * See the README file for the broad strokes.
12 * TODO NEXT:
13 * * check alignment of everything, make sure it's correct.
14 * * make sure the way we're unpacking relative >>2 pointers works
15 * properly for large pointers.
16 * * add hw-envelope envelopes/macros.
17 * * portamento/slur/glissando.
18 * * noise support.
19 * * AM sample support.
21 * NOTES FOR CODE READERS:
23 * * The implementations of arpeggio effects and software volume
24 * envelopes are almost identical; so if you find a bug in one,
25 * please look for that bug in the other. ;-)
28 ;; CONSTANT SYMBOLS
30 number_of_channels = 3
32 ;; song data structure
33 song_data_arpeggio_pointer = 0
34 song_data_venv_pointer = 2
35 song_data_vibrato_pointer = 4
36 song_data_pad = 6
37 song_data_number_of_tracks = 7
38 song_data_track_ptrs = 8
40 ;; track data structure; repeats for each channel.
41 track_data_channel_ptr = 0
43 ;; arpeggio table structure
44 arpeggio_length = 0
45 arpeggio_loop = 1
46 arpeggio_data = 2
48 ;; volume envelope table structure
49 venv_length = 0
50 venv_loop = 1
51 venv_data = 2
53 ;; vibrato table structure
54 vibrato_length = 0 ; not really necessary, but...
55 vibrato_delay = 1
56 vibrato_depth = 2
57 vibrato_speed = 3
58 vibrato_osc_mask = 4
60 ;; song status structure
61 song_current_track = 0
62 song_registers_to_write = 1
63 song_ym_shadow = 2 ; to 15.. we don't touch the IO registers.
64 song_status_size = 16
66 ;; channel status structure
67 channel_counter = 0
68 channel_data_ptr = 2
69 channel_state = 6
70 channel_arpeggio = 7
71 channel_arp_position = 8
72 channel_current_note = 9
73 channel_current_volume = 10
74 channel_volume_envelope = 11
75 channel_venv_position = 12
76 channel_pitch_envelope = 13
77 channel_pitchenv_position = 14
78 channel_env_shift = 15
79 channel_vibrato_position = 16 ; and 17
80 channel_vibrato = 18
81 channel_status_size = 20 ; I'd prefer to keep this a multiple of 4,
82 ; for easy wiping of the structure.
84 ;; channel state bits
85 channel_state_enabled = 0
86 channel_state_tone = 1 ; tentative;
87 channel_state_noise = 2 ; tentative;
88 channel_state_env_follow = 3 ; will change;
89 channel_state_first_frame = 4 ; tentative.
92 ; FUNCTIONS
94 SECTION text
96 extern plot_debug_dword
98 ; ymamoto_init (a0 = song pointer, d0 = track to setup.)
99 GLOBAL ymamoto_init
100 ymamoto_init:
101 MOVEM.L D0-D1/A0-A2, -(A7)
102 BSR ymamoto_reset
104 MOVE.L A0, songptr
106 ;; FIXME verify that the supplied track is not out of bounds.
108 ;; setup pointers: channels for this track, tables.
109 LEA song_status, A1
110 MOVE.B D0, song_current_track(A1)
112 ;; setup YM shadow registers.
113 MOVEQ #0, D1
114 MOVE.L D1, song_ym_shadow(A1)
115 MOVE.L D1, song_ym_shadow+4(A1)
116 MOVE.L D1, song_ym_shadow+8(A1)
117 MOVE.W D1, song_ym_shadow+12(A1)
118 MOVE.B #$FF, song_ym_shadow+7(A1) ; mixer.
120 ;; setup each channel
121 MOVEQ #number_of_channels-1, D0
122 .reset_channels:
123 BSR reset_channel
124 DBF D0, .reset_channels
126 MOVEM.L (A7)+, D0-D1/A0-A2
130 * Sets up the YM with some sane values so that when we leave,
131 * we don't have horrible screaming chip noise.
132 GLOBAL ymamoto_reset
133 ymamoto_reset:
134 ;; Reset YM.
135 MOVE.B #7, $FF8800
136 MOVE.B #$FF, $FF8802
137 MOVE.B #8, $FF8800
138 MOVE.B #0, $FF8802
139 MOVE.B #9, $FF8800
140 MOVE.B #0, $FF8802
141 MOVE.B #$A, $FF8800
142 MOVE.B #0, $FF8802
146 ;; ymamoto_update: call once per frame.
147 GLOBAL ymamoto_update
148 ymamoto_update:
149 MOVEM.L D0-D6/A0-A1, -(A7)
151 MOVE.L songptr, A0
152 LEA song_status, A1
153 MOVE.B #13-1, song_registers_to_write(A1)
155 MOVEQ #0, D0
156 BSR update_channel
157 MOVEQ #1, D0
158 BSR update_channel
159 MOVEQ #2, D0
160 BSR update_channel
162 MOVE.B song_registers_to_write(A1), D0
163 MOVEQ #0, D1
164 MOVEQ #0, D2
165 LEA song_ym_shadow(A1), A1
166 MOVEA.L #$FF8800, A0
167 .write_ym_registers:
168 MOVE.B 0(A1,D1), D2
169 MOVE.B D1, (A0)
170 MOVE.B D2, 2(A0)
171 ADDQ.B #1, D1
172 DBF D0, .write_ym_registers
174 MOVEM.L (A7)+, D0-D6/A0-A1
178 * update_channel: expects the song data pointer in A0, and the channel
179 * index in D0.
180 update_channel:
181 MOVEM.L A1-A4, -(A7) ; save registers... don't bother with Dx
182 ; because they're saved in ymamoto_update.
184 LEA song_status, A1
185 LEA song_ym_shadow(A1), A3
187 ;; load channel status ptr into A1
188 LEA channel_status, A1
189 MOVE.W D0, D1
190 BEQ .channel_status_loaded
191 SUBQ.W #1, D1
192 .next_channel_status:
193 ADD.L #channel_status_size, A1
194 DBEQ D1, .next_channel_status
195 .channel_status_loaded:
197 BTST.B #channel_state_enabled, channel_state(A1)
198 BEQ .update_end
200 BCLR.B #channel_state_first_frame, channel_state(A1)
202 ;; decrement and check counter
203 SUBQ.W #1, channel_counter(A1)
204 BPL .update_playing_note
207 ; Command processing.
208 MOVEA.L channel_data_ptr(A1), A2
209 .load_new_command:
210 MOVE.W (A2)+, D1
211 BPL .process_new_note
212 ;; otherwise, this is a command.
213 BTST.L #14, D1
214 BEQ .global_command
217 ;; channel command
218 MOVE.W D1, D2
219 LSR.W #8, D2
220 AND.B #$3F, D2
221 CMP.B #command_jump_table_len, D2
222 BGE .unknown_channel_command ; valid entries from 0 to c_j_t_len-1.
223 LEA .command_jump_table, A4
224 LSL.B #2, D2
225 MOVEA.L (A4,D2), A4
226 JMP (A4)
227 .command_jump_table:
228 ;; 0 -> reserved.
229 DC.L .unknown_channel_command, .arpeggio_command
230 ;; 2 -> detune (reserved).
231 DC.L .unknown_channel_command, .volume_command, .venv_command
232 ;; 6 -> AM sample playback (reserved); 7 -> hard env (reserved).
233 DC.L .noise_command, .unknown_channel_command, .unknown_channel_command
234 DC.L .env_follow_command, .pitch_env_command, .slur_command
235 DC.L .vibrato_command
236 command_jump_table_len = (. - .command_jump_table)/4
238 .arpeggio_command:
239 ;; if the arp value in D1 is 0, disable arpeggiation.
240 ;; otherwise, set the arp bit in channel status.
241 MOVE.B D1, channel_arpeggio(A1)
242 MOVE.B #0, channel_arp_position(A1)
243 BRA .load_new_command
245 .volume_command:
246 MOVE.B D1, channel_current_volume(A1)
247 BRA .load_new_command
249 .venv_command:
250 MOVE.B D1, channel_volume_envelope(A1)
251 MOVE.B #0, channel_venv_position(A1)
252 BRA .load_new_command
254 .noise_command:
255 ; XXX unimplemented
256 BRA .load_new_command
258 .env_follow_command:
259 BTST.B #0, D1
260 BEQ .disable_env_follow
261 BSET.B #channel_state_env_follow, channel_state(A1)
262 MOVE.B #4, channel_env_shift(A1) ; should be /16 by default.
263 BTST.B #1, D1
264 BEQ .load_new_command
265 SUB.B #1, channel_env_shift(A1) ; Follow one octave lower.
266 BRA .load_new_command
267 .disable_env_follow:
268 BCLR.B #channel_state_env_follow, channel_state(A1)
269 BRA .load_new_command
271 .pitch_env_command:
272 ; XXX unimplemented
273 BRA .load_new_command
275 .slur_command:
276 ; XXX unimplemented
277 BRA .load_new_command
279 .vibrato_command:
280 MOVE.B D1, channel_vibrato(A1)
281 MOVE.W #0, channel_vibrato_position(A1)
282 BRA .load_new_command
284 .unknown_channel_command:
285 ;; Just ignore it.
286 BRA .load_new_command
289 .global_command:
290 MOVE.W D1, D2
291 AND.B #$7F, D2
292 BNE .track_loop_command
293 ;; This is a track end command ($8000). Mute this channel, set
294 ;; channel disable bit, and then end immediately.
295 MOVE.B 7(A3), D2
296 BSET D0, D2
297 MOVE.B D2, 7(A3)
298 BCLR.B #channel_state_enabled, channel_state(A1)
299 BRA .update_end
301 .track_loop_command:
302 CMP.B #1, D2
303 BNE .trigger_command
304 MOVE.L songptr, A0
305 MOVE.B #1, D0
306 BSR ymamoto_init
307 BRA .update_end
308 ;; XXX below unused, potentially flaky
309 MOVEQ #0, D2
310 MOVE.W (A2)+, D2
311 BSR reset_channel
312 ADD.W D2, D2
313 ADD.L D2, channel_data_ptr(A1)
314 MOVEA.L channel_data_ptr(A1), A2
315 BRA .load_new_command
317 .trigger_command:
318 CMP.B #2, D2
319 BNE .unknown_global_command
320 ;; XXX unimplemented; just have to setup a vector to call on
321 ;; this command, or similar.
322 BRA .load_new_command
324 .unknown_global_command:
325 ;; Just ignore it.
326 BRA .load_new_command
329 .process_new_note:
330 MOVE.W D1, D2
331 ANDI.W #$FF, D2
332 CMPI.B #95, D2
333 BGT .special_note
334 ;; otherwise, we need to start playing a tone.
335 BSET.B #channel_state_first_frame, channel_state(A1)
336 BSET.B #channel_state_tone, channel_state(A1)
337 MOVE.B D2, channel_current_note(A1)
338 MOVE.B #0, channel_arp_position(A1) ; reset arpeggio.
339 MOVE.B #0, channel_venv_position(A1) ; reset volume envelope.
340 MOVE.W #0, channel_vibrato_position(A1)
341 ;; unmute this channel
342 MOVE.B 7(A3), D3
343 BCLR D0, D3
344 MOVE.B D3, 7(A3)
345 BRA .calculate_duration
347 .special_note:
348 CMPI.B #126, D2 ; Is it a wait (as opposed to a rest)?
349 BEQ .calculate_duration
351 BCLR.B #channel_state_tone, channel_state(A1)
352 MOVEQ #7, D2
353 MOVE.B 7(A3), D2
354 BSET D0, D2 ; Mute channel.
355 MOVE.B D2, 7(A3)
357 .calculate_duration:
358 LSR.W #8, D1
359 MOVE.W D1, channel_counter(A1)
361 .update_channel_data_ptr:
362 MOVE.L A2, channel_data_ptr(A1)
365 * Effects processing.
367 * The note value is loaded from channel status, and effects
368 * which have an effect at a chromatic tone level are
369 * processed, first (arpeggios). Then the frequency is looked
370 * up, and effects which change the raw frequency (portamento)
371 * are processed. Finally any other effects (venv, hard
372 * envelope) are processed.
374 .update_playing_note:
375 BTST.B #channel_state_tone, channel_state(A1)
376 BEQ .update_end
377 MOVE.B channel_current_note(A1), D3
378 MOVEQ #0, D1
380 ; Note value effects.
382 .update_arpeggio: ; Arpeggios.
383 MOVE.B channel_arpeggio(A1), D1
384 BEQ .lookup_frequency ; ... or next effect, if there is one.
385 MOVE.W song_data_arpeggio_pointer(A0), D2
386 BSR load_table_entry
387 MOVE.B channel_arp_position(A1), D2
389 MOVE.B arpeggio_length(A2), D1
390 CMP.B D2, D1 ; if arp position greater than length,
391 BGT .arp_update_note
392 MOVE.B arpeggio_loop(A2), D2 ; ... reset to loop point.
393 .arp_update_note:
394 ;; load arp delta, update playing note.
395 MOVE.B arpeggio_data(A2,D2), D1
396 ADD.B D1, D3
397 MOVE.B D3, channel_current_note(A1)
398 ;; update arp position.
399 ADDQ.B #1, D2
400 MOVE.B D2, channel_arp_position(A1)
403 ; Frequency value effects.
404 .lookup_frequency:
405 LEA note_to_ymval_xlate, A2
406 EXT.W D3
407 ADD.W D3,D3
408 MOVE.W (A2,D3), D3
410 .update_vibrato:
411 MOVE.B channel_vibrato(A1), D1
412 BEQ .update_hw_envelope
413 MOVE.W song_data_vibrato_pointer(A0), D2
414 BSR load_table_entry
415 ;; always update position
416 ADD.W #1, channel_vibrato_position(A1)
417 MOVE.W channel_vibrato_position(A1), D2
419 MOVEQ #0, D4
420 MOVE.B vibrato_delay(A2), D4
421 SUB.W D4, D2
422 BMI .update_hw_envelope ; Next effect.
424 ;; vibrato freq = ((frequency*2^1/12 - frequency)/2)/depth
425 ;; Note that 1/((2^1/12)-1) is pretty close to 16, so this can be
426 ;; approximated with (frequency>>4)/depth or so. Because this
427 ;; value can get quite small, we represent the vibrato frequency
428 ;; as a 12.4 fixed point fraction.
429 MOVE.W D3, D1
430 MOVEQ #8, D4
431 CMP.B vibrato_depth(A2), D4
432 BEQ .vibrato_oscillator
433 SUB.B vibrato_depth(A2), D4 ; Could replace this division with
434 LSL.B #2, D4 ; something more clever -- note that the
435 DIVU.W D4, D1 ; divisor is (8-depth)*4.
437 ;; low n bits of position are our oscillator. (This classic trick
438 ;; stolen from Rob Hubbard... except he used a fixed-frequency
439 ;; oscillator.)
440 .vibrato_oscillator:
441 MOVE.B vibrato_osc_mask(A2), D4
442 SUBQ.B #1, D4
443 AND.W D4, D2 ; AND (2^speed)-1
444 ADDQ.B #1, D4
445 LSR.B #1, D4
446 CMP.B D4, D2 ; CMP 2^(speed-1)
447 BCC .i_love_rob_hubbard
448 LSL.B #1, D4
449 SUBQ.B #1, D4
450 EOR.B D4, D2 ; XOR (2^speed)-1
452 .i_love_rob_hubbard: ; I'm glad I strip debugging symbols.
453 MOVE.W D1, D4
454 MULU.W D2, D4 ; Add vibrato frequency <oscillator> times.
455 LSR.L #4, D4 ; Fixup vibrato fraction.
456 SUB.W D4, D3
458 MOVE.B vibrato_speed(A2), D4
459 LSL.L D4, D1 ; (frq << (speed-1)) >> 4... it's safe for
460 LSR.L #5, D1 ; us to simplify this to (frq<<speed)>>5.
461 ADD.W D1, D3 ; Center the vibrato.
463 .update_hw_envelope:
464 BTST.B #channel_state_env_follow, channel_state(A1)
465 BEQ .set_frequency ; ... or next effect.
466 MOVE.W D3, D1 ; D1 <- current frequency.
467 MOVE.B channel_env_shift(A1), D2
468 LSR.W D2, D1
470 MOVE.B D1, $B(A3) ; Env fine adjustment.
471 LSR.W #8, D1
472 MOVE.B D1, $C(A3) ; Env rough adjustment.
473 BTST.B #channel_state_first_frame, channel_state(A1)
474 BEQ .set_frequency ; only update on first frame of note.
475 MOVE.B #$E, $D(A3) ; Env shape: CONT;ATT
476 LEA song_status, A2
477 MOVE.B #14-1, song_registers_to_write(A2)
479 .set_frequency:
480 MOVEQ #0, D1
481 ADD.B D0, D1
482 ADD.B D0, D1
483 MOVE.B D3, (A3,D1)
485 LSR.W #8, D3
486 MOVEQ #1, D1
487 ADD.B D0, D1
488 ADD.B D0, D1
489 MOVE.B D3, (A3,D1)
491 ;; Volume effects.
492 .lookup_volume:
493 MOVE.B channel_current_volume(A1), D3
495 .update_venv: ; Soft volume envelope.
496 MOVE.B channel_volume_envelope(A1), D1
497 BEQ .set_volume ; ... or next effect, if there is one.
498 MOVE.W song_data_venv_pointer(A0), D2
499 BSR load_table_entry
500 MOVE.B channel_venv_position(A1), D2
501 MOVE.B venv_length(A2), D1 ; Load length.
502 CMP.B D2, D1 ; If position greater than length,
503 BGT .venv_update_note
504 MOVE.B venv_loop(A2), D2 ; ... reset to loop point.
505 .venv_update_note:
506 MOVE.B venv_data(A2,D2), D3 ; Note that this might become
507 ADDQ.B #1, D2 ; relative someday soon.
508 MOVE.B D2, channel_venv_position(A1)
510 .set_volume:
511 BTST.B #channel_state_env_follow, channel_state(A1)
512 BEQ .store_volume
513 OR.B #$10, D3 ; Envelope on.
514 .store_volume:
515 MOVE.B D3, 8(A3,D0)
517 .update_end:
518 MOVEM.L (SP)+, A1-A4
520 ;;; End of main playroutine.
523 ;; Takes D0 = channel number.
524 ;; Returns channel status pointer in A1.
525 reset_channel:
526 MOVEM.L D0-D1/A0/A2, -(A7) ; save registers
528 MOVE.L songptr, A0
530 ;; load appropriate track address.
531 LEA song_status, A1
532 MOVEQ #0, D1
533 MOVE.B song_current_track(A1), D1
534 SUBQ.B #1, D1
535 LSL.B #1, D1
536 MOVE.W song_data_track_ptrs(A0,D1), D1
537 ASL.L #2, D1 ; XXX: should be .L?
538 LEA.L track_data_channel_ptr(A0,D1.L), A2
540 ;; load appropriate channel status structure.
541 LEA channel_status, A1
542 .l1: CMP.W #0, D0
543 BEQ .l2
544 ADD.L #channel_status_size, A1 ; next channel status.
545 ADD.L #2, A2 ; next channel pointer.
546 DBF D0, .l1
547 .l2:
549 ;; wipe channel status structure first.
550 MOVEQ #0, D0
551 MOVE.L D0, 0(A1)
552 MOVE.L D0, 4(A1)
553 MOVE.L D0, 8(A1)
554 MOVE.L D0, 12(A1)
555 MOVE.L D0, 16(A1)
557 ;; setup data pointer.
558 MOVE.W (A2), D0
559 ASL.L #2, D0
560 ADD.L A0, D0
561 MOVE.L D0, channel_data_ptr(A1)
563 ;; enable channel.
564 BSET.B #channel_state_enabled, channel_state(A1)
566 MOVEM.L (A7)+, D0-D1/A0/A2 ; restore registers
570 * Generic routine for loading values from tables of form
571 * num_entries:byte
572 * entry_length:byte, entry_data:(length+1 bytes)
573 * Takes D1 -> record idx, D2 -> packed pointer to table,
574 * A0 -> song data.
575 * Returns pointer to record in A2. Modifies D1,D2.
576 load_table_entry:
577 ASL.L #2, D2
578 MOVE.W D2, A2
579 MOVE.L A0, D2
580 ADD.L D2, A2
581 MOVEQ #0, D2
582 ADDQ.L #1, A2 ; skip length of table.
583 ;; find our entry in the table.
584 SUBQ.W #1, D1
585 BEQ .lookup_finished
586 SUBQ.W #1, D1
587 .next_entry:
588 MOVE.B (A2), D2
589 ADDQ.L #2, D2
590 ADD.L D2, A2
591 DBF D1, .next_entry
592 .lookup_finished:
595 section data
597 ; CONSTANT TABLES
598 EVEN
599 note_to_ymval_xlate:
600 DC.W $EEE,$E18,$D4D,$C8E,$BDA,$B2F,$A8F,$9F7,$968,$8E1,$861,$7E9
601 DC.W $777,$70C,$6A7,$647,$5ED,$598,$547,$4FC,$4B4,$470,$431,$3F4
602 DC.W $3BC,$386,$353,$324,$2F6,$2CC,$2A4,$27E,$25A,$238,$218,$1FA
603 DC.W $1DE,$1C3,$1AA,$192,$17B,$166,$152,$13F,$12D,$11C,$10C,$FD
604 DC.W $EF,$E1,$D5,$C9,$BE,$B3,$A9,$9F,$96,$8E,$86,$7F
605 DC.W $77,$71,$6A,$64,$5F,$59,$54,$50,$48,$47,$43,$3F
606 DC.W $3C,$38,$35,$32,$2F,$2D,$2A,$28,$26,$24,$22,$20
607 DC.W $1E,$1C,$1B,$19,$18,$16,$15,$14,$13,$11,$10,$0F
610 ; GLOBAL VARIABLES
612 SECTION BSS
614 EVEN
615 channel_status: DS.B channel_status_size*number_of_channels
616 EVEN
617 song_status: DS.B song_status_size
618 EVEN
619 songptr: DS.L 1
621 * vim:syn=asm68k