; Eric R. Quinn ; July 21, 1999 ; snail.asm ; This program plays the theme from "Maze: The Snail Game" on SMS hardware. ; It has been tested on the emulator MASSAGE. ; Feel free to use this code as you wish. It is provided "As-is" ; Submitted to S8-Dev on July 22, 1999 ; Update March 7, 2000: Added read to VDP Status Port (0xBF) in interrupt ; handler to improve compatibility with Meka. #define EQU .equ ; Key offsets ; START EQU $0000 MASKABLE_INTERRUPT_VECTOR EQU $0038 NONMASKABLE_INTERRUPT_VECTOR EQU $0066 INTERRUPT_HANDLER_OFFSET EQU $2000 MAIN EQU $1000 DATA_SEGMENT EQU $4000 HEADER_OFFSET EQU $7FF0 LAST_PAGE_OFFSET EQU $8000 ; Useful definitions ; VDP_ADDRESS_PORT EQU $BF PSG_PORT EQU $7F VDP_REG_WRITE EQU $80 VDP_REG_1 EQU $01 VDP_REG_0 EQU $00 VDP_REG_A EQU $0A VDPREG1_VSYNC_FLAG EQU $20 VDPREG1_DISP_ENABLE_FLAG EQU $40 SOUND_VOLUME_WRITE EQU $90 SOUND_CHANNEL_0 EQU $00 SOUND_VOLUME_LOUDEST EQU $00 SOUND_VOLUME_OFF EQU $0F SOUND_FREQUENCY_WRITE_LO EQU $80 SOUND_FREQUENCY_WRITE_HI EQU $00 ; Frequency table ; CONCERT_G3_LO EQU $0E CONCERT_G3_HI EQU $13 CONCERT_GS3_LO EQU $0E CONCERT_GS3_HI EQU $12 CONCERT_AF3_LO EQU CONCERT_GS3_LO CONCERT_AF3_HI EQU CONCERT_GS3_HI CONCERT_A4_LO EQU $0C CONCERT_A4_HI EQU $11 CONCERT_AS4_LO EQU $0C CONCERT_AS4_HI EQU $01 CONCERT_BF4_LO EQU CONCERT_AS4_LO CONCERT_BF4_HI EQU CONCERT_AS4_HI CONCERT_B4_LO EQU $0D CONCERT_B4_HI EQU $0F CONCERT_C4_LO EQU $0F CONCERT_C4_HI EQU $0E CONCERT_CS4_LO EQU $01 CONCERT_CS4_HI EQU $0E CONCERT_DF4_LO EQU CONCERT_CS4_LO CONCERT_DF4_HI EQU CONCERT_CS4_HI CONCERT_D4_LO EQU $05 CONCERT_D4_HI EQU $0D CONCERT_DS4_LO EQU $09 CONCERT_DS4_HI EQU $0C CONCERT_EF4_LO EQU CONCERT_DS4_LO CONCERT_EF4_HI EQU CONCERT_DS4_HI CONCERT_E4_LO EQU $0E CONCERT_E4_HI EQU $0B CONCERT_F4_LO EQU $03 CONCERT_F4_HI EQU $0B CONCERT_FS4_LO EQU $09 CONCERT_FS4_HI EQU $0A CONCERT_GF4_LO EQU CONCERT_FS4_LO CONCERT_GF4_HI EQU CONCERT_FS4_HI CONCERT_G4_LO EQU $0F CONCERT_G4_HI EQU $09 CONCERT_GS4_LO EQU $07 CONCERT_GS4_HI EQU $09 CONCERT_AF4_LO EQU CONCERT_GS4_LO CONCERT_AF4_HI EQU CONCERT_GS4_HI CONCERT_A5_LO EQU $0E CONCERT_A5_HI EQU $08 CONCERT_AS5_LO EQU $06 CONCERT_AS5_HI EQU $08 CONCERT_BF5_LO EQU CONCERT_AS5_LO CONCERT_BF5_HI EQU CONCERT_AS5_HI CONCERT_B5_LO EQU $0F CONCERT_B5_HI EQU $07 CONCERT_C5_LO EQU $07 CONCERT_C5_HI EQU $07 ; Music note durations DURATION_1S EQU 60 DURATION_05S EQU 30 DURATION_025S EQU 15 DURATION_INTRO EQU 15 DURATION_BETWEEN_NOTES EQU 1 .ORG START ; This code contains standard SMS initialization routines. DI ; Disable Interrupts IM 1 ; Interrupt Mode 1 LD SP, $D000 ; Initialize stack pointer LD HL, $FFFD LD (HL), $00 ; Map ROM page 0 INC HL LD (HL), $01 ; Map ROM page 1 INC HL LD (HL), $00 ; Map ROM page 0 JP MAIN .ORG MASKABLE_INTERRUPT_VECTOR JP INTERRUPT_HANDLER_OFFSET .ORG NONMASKABLE_INTERRUPT_VECTOR RETN .ORG MAIN ; Turn off HBlank interrupt XOR A OUT (VDP_ADDRESS_PORT), A LD A, VDP_REG_WRITE OR VDP_REG_0 OUT (VDP_ADDRESS_PORT), A ; HBlank interrupts at line 255 LD A, $FF OUT (VDP_ADDRESS_PORT), A LD A, VDP_REG_WRITE OR VDP_REG_A OUT (VDP_ADDRESS_PORT), A ; Turn on VBlank interrupt XOR A LD A, VDPREG1_VSYNC_FLAG OUT (VDP_ADDRESS_PORT), A LD A, VDP_REG_WRITE OR VDP_REG_1 OUT (VDP_ADDRESS_PORT), A ; Set up sound channel 0 volume. ;LD A, SOUND_VOLUME_WRITE ;OR SOUND_CHANNEL_0 ;OR SOUND_VOLUME_LOUDEST ;OUT (PSG_PORT), A ; Set up music pointer and counters. ; HL register is pointer, BC is duration counter. LD D, $00 LD HL, MUSIC_DATA_TABLE LD C, $01 EI ; Start loop MAIN_LOOP: JR MAIN_LOOP HALT .ORG INTERRUPT_HANDLER_OFFSET DI IN A, (VDP_ADDRESS_PORT) ; Read VDP status to clear bits 6,7 DEC C ; Decrement duration counter JR Z, NEXT_NOTE ; If counter is 0, then play next note LD A, C CP DURATION_BETWEEN_NOTES ; Play short silence between notes. JR NZ, I_HANDLER_DONE ; Silence sound channel 0 for inter-note break. LD A, SOUND_VOLUME_WRITE OR SOUND_CHANNEL_0 OR SOUND_VOLUME_OFF OUT (PSG_PORT), A JR I_HANDLER_DONE NEXT_NOTE: ; Turn sound channel 0 back on. LD A, SOUND_VOLUME_WRITE OR SOUND_CHANNEL_0 OR SOUND_VOLUME_LOUDEST OUT (PSG_PORT), A ; Load low frequency bits LD A, (HL) OR SOUND_FREQUENCY_WRITE_LO OR SOUND_CHANNEL_0 OUT (PSG_PORT), A ; Point to next byte in music data table. ; Register D contains the count of bytes accessed in music ; data table. This count is used to determine when end ; of music data is reached so song can be repeated. INC HL INC D ; Load high frequency bits LD A, (HL) OR SOUND_FREQUENCY_WRITE_HI OR SOUND_CHANNEL_0 OUT (PSG_PORT), A ; Load duration counter. INC HL INC D LD C, (HL) INC HL INC D ; Check if at we're at the end of the music data table. LD A, D CP (END_MUSIC_DATA_TABLE-MUSIC_DATA_TABLE+3) JR NZ, I_HANDLER_DONE ; If we are set HL to point back to beginning. ; Clear D ; Set C to allow a short break between songs. LD HL, MUSIC_DATA_TABLE LD D, $00 LD C, DURATION_INTRO ; Silence sound channel 0. LD A, SOUND_VOLUME_WRITE OR SOUND_CHANNEL_0 OR SOUND_VOLUME_OFF OUT (PSG_PORT), A I_HANDLER_DONE: EI RETI .ORG DATA_SEGMENT ; This data table contains the information necessary to play the ; theme from "Maze: The Snail Game" The data is in the form of bytes ; arranged as ordered pairs of (frequency, duration). Duration is ; measured in VBlanks. Consequently, a value of 60 for duration implies ; a note lasting one second on NTSC machines. MUSIC_DATA_TABLE: .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_G4_LO .DB CONCERT_G4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_G4_LO .DB CONCERT_G4_HI .DB DURATION_025S .DB CONCERT_F4_LO .DB CONCERT_F4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_G3_LO .DB CONCERT_G3_HI .DB DURATION_025S .DB CONCERT_A4_LO .DB CONCERT_A4_HI .DB DURATION_025S .DB CONCERT_G3_LO .DB CONCERT_G3_HI .DB DURATION_025S .DB CONCERT_G3_LO .DB CONCERT_G3_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_G4_LO .DB CONCERT_G4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_G4_LO .DB CONCERT_G4_HI .DB DURATION_025S .DB CONCERT_F4_LO .DB CONCERT_F4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_E4_LO .DB CONCERT_E4_HI .DB DURATION_025S .DB CONCERT_D4_LO .DB CONCERT_D4_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_B4_LO .DB CONCERT_B4_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S .DB CONCERT_C4_LO .DB CONCERT_C4_HI .DB DURATION_025S END_MUSIC_DATA_TABLE: .ORG HEADER_OFFSET .DB "TMR MESA" ; Tradmark .DW 9919h ; Year .DW 0000h ; Checksum not correct .DW 0000h ; Part Num not correct .DW 2107h ; Day / Month .ORG LAST_PAGE_OFFSET .END