Thursday, August 23, 2018

Apple II Mockingboard music player with Donkey Kong background music

My music player and sound alike Donkey Kong music from 3 of the screens.
The music was painstakingly created by hand.  This really needs an editor.
This was created to include with a hacked version of Atarisoft Donkey Kong for the Apple II, but the person I wrote if for never used it because they aren't much of a programmer.
The code is based on the Oric version since the hardware is similar (VIA + AY sound chip).


;*************************************************************
;* FILE: DonkeyKongMoockingboard.a65
;*************************************************************
;* Description:
;*  Simple music player for the Apple II's Mockingboard.
;*  Based loosely on a Z80 program I found with the Virtual Aquarius emulator
;*  It plays the same song data anyway.
;*  Initial 6502 port was for the Oric which also interfaces
;*  the AY sound chip through a VIA chip similar to the Mockingboard.
;*  
;*  Uses VIA timer to trigger the interrupt,
;*  other machines may use the vertical blank interrupt.
;*  Designed for the General Instruments AY sound chip but will work
;*  with any sound chip with an internal address register and a data register.
;*
;*  The current version assembles with the ca65 assembler included with the
;*  cc65 6502 C compiler package.
;*
;*  If you run across opcodes in capital letters, those lines were probably copied from
;*  the Mockingboard demo source code.
;*  The code is fairly well documented but there is always room for improvement
;*  The code supports 1 or 2 AY chips but 2nd chip support is untested at this time
;*
;* To do:
;*  Move setting of ORG address ranges (page zero and program) to the linker instead
;*  of hard coding it.
;*  Set build condition DEFINES externally??
;*  Make unit test code properly exit.
;*  Verify VIA settings for disabling interrupt.
;*  I should probably look at making the VIA definitions a data structure.
;*  Documentation, documentation, documentation
;*  Check the code into a version control system.
;*  The player destruct may need some revisions but I won't know until
;*  I perform further testing.
;*  Resetting the sound chip is a bad way to stop a song if other sounds are being
;*  played on the same chip.  We should output sound data to the sound chip instead.
;*  Move sound/music data external and link to it with the linker.
;*  The player is getting page 0 heavy.  I could use a catch all temporary pointer
;*  in place of dedicated pointers like songs, sound, and myjmp.  It would burn
;*  a few more clocks when starting a song but that shouldn't be too often.
;*  Data could be dumped to the sound chip to initialize settings in the playsong
;*  routine so those settings could be removed from multiple songs.
;*  playing and waitcount could be moved off of page zero if really needed.
;*  Move page zero variables; I think they are conflicting with the monitor or DOS.
;*
;* Song Data Format:
;* ;wait, number of registers to change,
;* ; register number, new value
;* ; register number, new value
;* ; etc.
;* ; ends when number of registers to change is 0.
;*  ; repeats when number of registers to change is 255.
;*  ;
;*  ; Note that register numbers with the uppermost bit set to 1 (i.e. negative)
;*  ; will be sent to the 2nd AY chip on the Mockingboard
;*  ; That bit is just a flag and is masked off to send data to the 2nd AY chip
;*
;* Author: James Diffendaffer
;*   Copyright(c) 2009-2015
;*
;*   This software may be freely distributed but please give me credit if you
;*   use it.
;*
;*   Created May 29, 2009 for the Oric
;*   Ported to the Apple II with Mockingboard Nov 23, 2012
;*
;* Version Date/Time:
;*   Mar 2, 2015 - 12:00 AM
;* 
;* Version update info:
;*   Fixed some spelling errors including ones that were in code I had copied
;*   Made small optimization to 6502 command call
;*   Added initial support for a 16 bit free running timer on page 0 
;************************************************************* 



;************************************************************* 
;* Set build conditions here.
;*
;* Nothing else *should* need to be modified if I've done this right
;* The code does currently assume songs follow the code but that will eventually change
;************************************************************* 
.DEFINE UNITTEST 1  ; set to non-zero value to enable the stand along unit test code, otherwise build to link to external program
.DEFINE USE_C02  0  ; set to use 65C02 instructions. 1 = standard 65C02 or above, 2 = Rockwell/WDC 65C02 or above
.DEFINE USE_CMD  1  ; set to non-zero value to use additional commands in the song data
.DEFINE USE_NTSC 1  ; set to non-zero value to use NTSC VIA Timer settings, otherwise PAL
.DEFINE BIGSONGS 1  ; set to non-zero value if songs will cross a 256 byte memory page. 
.DEFINE TWO_VIAS 1  ; set to non-zero value if songs will use 2 AY chips
.DEFINE DOSHEADER 0  ; set to non-zero to attach a DOS 3.3 header at the start of the file (used by a2tools)
.DEFINE USE_TIMER 0  ; set to non-zero to include code for a free running 16 bit timer

.DEFINE PAGEZERO $00EB ; set the org address for page zero variables here
;.DEFINE PAGEZERO $0090 ; set the org address for page zero variables here
.IF UNITTEST
.DEFINE STARTCODE $8000 ; set the org address for the unit test code section here
.ELSE
.DEFINE STARTCODE $8000 ; PUT YOUR START ADDRESS HERE AND SET UNIT TEST TO 0
.ENDIF
;************************************************************* 



; Apple specific hardware and ROM call definitions.  Used by the unit test code
.DEFINE KEYBOARD $C000 ; address of Apple II keyboard
.DEFINE STROBE  $C010 ; keyboard strobe
.DEFINE COUT1  $FDF0 ; ROM routine to print a character
.DEFINE CROUT  $FD8E ; ROM routine to print a return
.DEFINE PRBYTE  $FDDA ; ROM routine to output A as 2 digit HEX
.DEFINE IOSAVE  $FF4A ; save all registers
.DEFINE IOREST  $FF3F ; restore all registers
.DEFINE IRQloc  $03FE ; Apple II IRQ handler address


; VIA register definitions for the first Mockingboard 6522
.DEFINE VIAIORB  $C400 ; IO Register B
.DEFINE VIAIORA  $C401 ; IO Register A
.DEFINE VIADDRB  $C402 ; Data Direction Register B
.DEFINE VIADDRA  $C403 ; Data Direction Register A
.DEFINE VIA_T1C  $C404 ; Read/Write Counter Low-High T1
.DEFINE VIA_T1L  $C406 ; Read/Write Latch Low-High T1
.DEFINE VIA_ACR  $C40B ; Auxiliary Control Register
.DEFINE VIA_PCR  $C40C ; Peripheral Control Register;
.DEFINE VIA_IFR  $C40D ; Interrupt Flag Register
.DEFINE VIA_IER  $C40E ; Interrupt Enable Register


; VIA register definitions for the second Mockingboard 6522
.DEFINE VIA2IORB $C480 ; IO Register B
.DEFINE VIA2IORA $C481 ; IO Register A
.DEFINE VIA2DDRB $C482 ; Data Direction Register B
.DEFINE VIA2DDRA $C483 ; Data Direction Register A
.DEFINE VIA2_T1C $C484 ; Read/Write Counter Low-High T1
.DEFINE VIA2_T1L $C486 ; Read/Write Latch Low-High T1
.DEFINE VIA2_ACR $C48B ; Auxiliary Control Register
.DEFINE VIA2_PCR $C48C ; Peripheral Control Register;
.DEFINE VIA2_IFR $C48D ; Interrupt Flag Register
.DEFINE VIA2_IER  $C48E ; Interrupt Enable Register


; VIA timer settings
.IF USE_NTSC
.DEFINE VBLVIA $40FF  ; VIA Timer Latch, settings matching NTSC 60Hz (from mockingboard demo)
;.DEFINE VBLVIA $4100  ; VIA Timer Latch, settings matching NTSC 60Hz
.ELSE
.DEFINE VBLVIA $4E00  ; VIA Timer Latch, settings matching PAL 50Hz
.ENDIF


;**************************************************************
 .BSS 

.ZEROPAGE    ; page zero variables

; The zeropage address should be set by the linker
 .org PAGEZERO ; our page zero start address

playing: .RES 1 ; flag to indicate whether the player is active or not
waitcount: .RES 1 ; counter for number of interrupts till next song data is read
song:  .RES 2 ; pointer to current location in the song we are playing
songs:  .RES 2 ; pointer to song table
sound:  .RES 2 ; pointer to a block of sound data

.IF USE_CMD
;cmds:  .RES 2 ; page zero command table pointer
;myjmp:  .RES 2 ; page zero jump address for command being called
temp:  .RES 1 ; temporary storage (for the Y register)
.ENDIF

.IF USE_TIMER
timer:  .RES 2 ; page zero free running timer that is incremented every interrupt
.ENDIF

.CODE

.IF DOSHEADER
;**************************************************************
;* DOS 3.3 header
;**************************************************************
;*
;**************************************************************
 .org STARTCODE-4
 .word STARTCODE     ; Program Start Address   
 .word DATAEND-STARTCODE   ; Program Length
;**************************************************************
.ENDIF

 .org STARTCODE

.IF UNITTEST
;**************************************************************
;* main
;**************************************************************
;* Description:
;*   This is the main routine for the unit test code.
;*   It calls routines to initialize the hardware/software,
;*   plays the song, performs a busy loop until the song is done,
;*   and then calls the stopsong to disable playing.
;**************************************************************
_main:
 jsr IOSAVE    ; save registers
 jsr CROUT    ; output a carriage return

 jsr playerinit   ; initialize the player and sound hardware

.IF USE_C02 > 0
 stz temp    ; song number
.ELSE
 lda #0     ; song number
 sta temp
.ENDIF

screenloop:
 jsr PRBYTE    ; output A as HEX
 jsr CROUT    ; output a carriage return

 lda temp
 jsr playsongina

 ; wait for a keypress or for the song to finish
 ; keybounce seems to be an issue
.IF USE_C02 > 0
 stz STROBE    ; CLEAR KEYBOARD STROBE
.ELSE
 lda #$00
 STA STROBE    ; CLEAR KEYBOARD STROBE
.ENDIF
@loop:
 lda KEYBOARD   ; get input status
 bmi @quit    ; exit if a keypress has taken place
.IF USE_C02 > 1
 bbs7 playing,@loop
.ELSE
 lda #!10000000    
 bit playing    ; check to see if the song has finished
; beq @quit    ; quit if it has
 bne @loop    ; keep looking if neither condition has taken place.
.ENDIF
@quit:
 jsr stopsong   ; stop playing and stop AY output
 
 inc temp
 lda temp
 cmp #14
 bcc screenloop

 jsr playerdestruct  ; remove our interrupt and cleanup
 
exittest:
 jmp IOREST    ;restore registers and return

;temp:
; .byt 1    ; hold our screen number
 
;**************************************************************
.ENDIF


;**************************************************************
;* playerinit
;**************************************************************
;* Description:
;*   Initialize the music player and mockingboard hardware
;*   Sets up the interrupt handler and any page 0 variables
;*   it requires.
;* Parameters:
;*   None
;* Modifies:
;*   A
;* Returns:
;*   Garbage
;**************************************************************
playerinit:
 sei      ; disable interrupts
 
;.IF USE_CMD
; ; set the start address of the commandtable
; lda #<commandtable
; sta cmds
; lda #>commandtable
; sta cmds+1
;.ENDIF
 ; set the start address of the songtable
 lda #<songtable
 sta songs
 lda #>songtable
 sta songs+1

 ;save current interrupt handler address
 lda IRQloc
 sta SAVE_IRQ
 lda IRQloc+1
 sta SAVE_IRQ+1

 ;add our interrupt handler
 lda #<IrqHandler  ; address of our interrupt handler
 sta IRQloc
 lda #>IrqHandler
 sta IRQloc+1

.IF USE_C02 > 0
 stz playing
 ; reset AY chip
 stz VIAIORB    ;TO SOUND CHIP
.IF TWO_VIAS
 stz VIA2IORB   ;TO SOUND CHIP
.ENDIF
.ELSE
 ; tell the player we are not playing anything at the moment
 lda #$00
; lda playing
; and #!01111111
 sta playing

 ; reset AY chip
 lda #$00    ;SEND "RESET COMMAND"
 sta VIAIORB    ;TO SOUND CHIP
.IF TWO_VIAS
 sta VIA2IORB   ;TO SOUND CHIP
.ENDIF
.ENDIF

 lda #$04    ;THROUGH PORT B
 sta VIAIORB
.IF TWO_VIAS
 sta VIA2IORB
.ENDIF

 ;setup first AY chip for access
 LDA #$FF    ;SET PORT A FOR OUTPUT
 STA VIADDRA  
.IF TWO_VIAS
 STA VIA2DDRA  
.ENDIF
 LDA #$07    ;SET PORT B FOR OUTPUT
 STA VIADDRB
.IF TWO_VIAS
 STA VIA2DDRB
.ENDIF

 ; set up a VIA timer for our interrupt. Set our VIA_T1 value for 50Hz or 60Hz as defined above
 lda #<VBLVIA
 sta VIA_T1C
 lda #>VBLVIA
 sta VIA_T1C+1

 LDA #$40    ;!0100 0000 = continuous timed interrupts, pb7 disabled
 STA VIA_ACR
 LDA #$7F
 STA VIA_IER
 LDA #$C0    ;!1100 0000 = time-out of T1, any enabled interrupt
 STA VIA_IFR
 STA VIA_IER

 cli      ; enable interrupts
 
 rts
;**************************************************************



;**************************************************************
;* playerdestruct
;**************************************************************
;* Description:
;*   Disables the timer on the 6522 VIA
;*   and restores the previous interrupt handler's address
;* Parameters:
;*   None.
;* Modifies:
;*   A
;* Returns:
;*   Garbage
;**************************************************************
playerdestruct:
 sei      ; disable interrupts

.IF USE_C02 > 0
 stz VIA_ACR
 stz VIA_IER
.ELSE
 ; disable the timer
 lda #$00    ;!0000 0000
 sta VIA_ACR
 sta VIA_IER
.ENDIF
 ; clear any VIA timer interrupts on the chip we are using 
 lda #$C0
 sta VIA_IFR
 
 ;restore previous interrupt handler address
 lda SAVE_IRQ
 sta IRQloc
 lda SAVE_IRQ+1
 sta IRQloc+1

 cli      ; enable interrupts
 
 rts
;**************************************************************
 
 
 
;**************************************************************
;* playsongina
;**************************************************************
;* Description:
;*   This plays the song # in the A register from a table of song pointers
;* Parameters:
;*   A - song number
;* Modifies:
;*   A, Y, songstart
;* Returns:
;*   garbage
;**************************************************************
playsongina:
 asl      ; multipy by 2 to get our table index (pointers are two bytes long)
 tay
 lda (songs),y   ; load the LSB of the song address
 sta songstart   ; save in the LSB of the pointer for the currently playing song
 iny
 lda (songs),y   ; load the MSB of the song address
 sta songstart+1   ; save in the MSB of the pointer for the currently playing song

; falls through to playsong
;**************************************************************



;**************************************************************
;* playsong
;**************************************************************
;* Description:
;*  Plays a song
;* Parameters:
;*   songstart
;* Modifies:
;*   A, song
;* Returns:
;*   garbage
;**************************************************************
playsong:
 ; set the start address of the song
 lda songstart
 sta song
 lda songstart+1
 sta song+1
.IF USE_C02 > 1
 smb7 playing   ; if bit 7 is set, we are playing
.ELSE
 lda playing
 ora #!10000000   ; if bit 7 is set, we are playing
 sta playing
.ENDIF 
 rts      ; return
;**************************************************************


 
;**************************************************************
;* stopsong
;**************************************************************
;* Description:
;*   This subroutine is called to stop the music player,
;*   remove the interrupt handler, and clear VIA timer settings
;* Parameters:
;*   None.
;* Modifies:
;*   A
;* Returns:
;*   garbage
;**************************************************************
stopsong:
 sei      ; disable interrupts

.IF USE_C02 > 0
 ;stop sounds by resetting AY chip.  All AY settings are lost
 stz VIAIORB    ;SEND "RESET COMMAND" TO SOUND CHIP
.IF TWO_VIAS
 stz VIA2IORB   ;SEND "RESET COMMAND" TO 2nd SOUND CHIP
.ENDIF
.ELSE
 ;stop sounds by resetting AY chip.  All AY settings are lost
 lda #$00    ;SEND "RESET COMMAND"
 sta VIAIORB    ;TO SOUND CHIP
.IF TWO_VIAS
 sta VIA2IORB   ;TO SOUND CHIP
.ENDIF
.ENDIF
.IF USE_C02 > 1
 rmb7 playing   ; tell the player not to play
.ELSE
 lda playing
 and #!01111111
 sta playing    ; tell the player not to play
.ENDIF

 lda #$04    ;THROUGH PORT B
 sta VIAIORB
.IF TWO_VIAS
 sta VIA2IORB
.ENDIF
 
 cli      ; enable interrupts

 rts      ; return 

;**************************************************************



;**************************************************************
;* IrqHandler
;**************************************************************
;* Description:
;*   Vertical Blank or Timer interrupt handler
;*   Contains the code that actually plays the song
;*   (data is output via outputsounddata)
;* Parameters:
;*   None but the VIA_IFR should contain the interrupt info.
;* Modifies:
;*   It's an interrupt, everything we modify must be saved except status
;*   which is saved automatically.
;*   Modifies the free running timer on exit if enabled 
;* Returns:
;*   All registers are preserved but the song and sound pointers
;*   on page zero may be modified.
;**************************************************************
IrqHandler:
 pha      ; preserve A
 
 ;from the Mockingboard demo code
 LDA #$C0    ; clear the 6522 interrupt flag
 STA VIA_IFR 

.IF USE_C02 = 2
 bbr7 playing,exit_int ; exit if we aren't playing
.ELSE
 lda playing    ; check playing flag
 bpl exit_int   ; exit if not
.ENDIF

 ; wait for interrupt, waitcount times
 dec waitcount   ; decrement the counter
 bne exit_int   ; exit if not done waiting

 ; if we get here, we need to save X and Y on the stack
.IF USE_C02 > 0
 phx      ;65c02
 phy      ;65c02
.ELSE
 txa
 pha
 tya
 pha
.ENDIF 

 ; load waitcount every time, it saves more cycles that putting it in updatesong
 ldy #0     ; offset from song pointer, Y must be used for this addressing mode on the 6502
 lda (song),y   ; get the new wait counter
 sta waitcount   ; save the new wait counter

 iny      ; increment song index
 lda (song),y   ; get number of registers to modify
 beq playsongend   ; zero is end of song

.IF USE_CMD
 bmi docomnd    ; negative numbers are commands
.ELSE
 bpl notrepeat   ; If it's a positive number, it's not a repeat indicator
 
 ; song repeats endlessly until shut off
 ; set the start address of the song
 lda songstart
 sta song
.IF BIGSONGS
 lda songstart+1   ; MSB should only need updated if song can cross a 256 byte page
 sta song+1
.ENDIF


.IF USE_C02 > 0
 bra updatesong   ; update the waitcount and song pointer as well
.ELSE
; jmp updatesong   ; update the waitcount and song pointer as well
 bvc updatesong   ; update the waitcount and song pointer as well
.ENDIF

;-----------------
notrepeat:
.ENDIF

 tax      ; put AY register count in X
 iny      ; next byte in song

 ;*********
 ; copy song pointer to sound pointer for output 
 ; if sounds and songs can fit on a single page, the MSB only needs set once when initializing.
 lda <song
 sta <sound
 lda >song    ; MSB
 sta >sound    ; MSB

 jsr outputsounddata  ; output a stream of data to the sound chip
 ;*********

;-----------------
updatesong:
 ; update the song pointer (16 bit song pointer + 8 bit index)
 ; (songLSB + y, if carry set then songMSB++)
 tya      ; put song pointer offset in A
 adc song    ; add low byte (LSB)
 sta song    ; update low byte

.IF BIGSONGS
 ; the next few instructions are only needed if any songs will cross a 256 byte memory page
 bcc @skipadd   ; don't increment the MSB if the carry flag isn't set
 inc song+1    ; update the song pointer
@skipadd:
.ENDIF
;-----------------
exit_int2:
 ; restore the registers we saved
.IF USE_C02 > 0
 ply      ; 65c02
 plx      ; 65c02
.ELSE
 pla
 tay
 pla
 tax
.ENDIF

;-----------------
exit_int:
.IF USE_TIMER > 0
 ;free running timer code. 
 inc timer<    ;increment the low byte of the free running timer.  Sets Z bit if it wraps around
 bne @skip_timer   ;branch if low byte of timer doesn't roll over
 inc timer>    ;increment the high byte of the free running timer
@skip_timer:
.ENDIF
 pla      ; restore the accumulator
 rti      ; return from our interrupt
;*****

;-----------------
playsongend:
 ; restoring x is not needed for the end of a song but is included as a command example
; ldx temp    ; restore x from command call

.IF USE_C02 = 2
; stz playing    ; when we encounter the end of song marker we just set a flag and stop playing
 rmb7 playing   ; when we encounter the end of song marker we just set a flag and stop playing
.ELSEIF USE_C02 = 1
 trb playing
.ELSE
; lda #0
 lda playing
 and #!01111111
 sta playing    ; when we encounter the end of song marker we just set a flag and stop playing
.ENDIF
; beq exit_int2   ; branch always
 bvc exit_int2   ; branch always
;**************************************************************




;**************************************************************
;* outputsounddata
;**************************************************************
;* Description:
;*   Outputs a block of bytes to the Mockingboard
;*   Can be used to initialize the sound chip,
;*   play one shot sounds, etc...
;* Modifies:
;*   A, X, Y
;* Parameters:
;*   sound - points to sound data to be output
;*   X - contains number of bytes to output
;*   Y - contains offset to start at in song.  Normally zero.
;* Returns:
;*   Y - contains offset to last byte pointed to
;* Pitfalls:
;*   Does not update sound pointer when crossing a page boundary.
;*   If you output more than 128 values (why would you?)
;*   to the sound chip, Y goes negative and points to the wrong
;*   location. 
;**************************************************************
outputsounddata:
reglop:
 ; Select the register number
 lda (sound),y   ; get the register number to modify

; for using two sound chips
.IF TWO_VIAS
 bpl firstvia   ; branch if not negative (first VIA & AY)

 and #!01111111   ; mask off the sign bit
 sta VIA2IORA   ; set the AY data register number

 ; latch command
 LDA #$07    ;SEND "LATCH COMMAND"
 STA VIA2IORB   ;TO SOUND CHIP
 LDA #$04    ;THROUGH PORT B
 STA VIA2IORB
 

 iny      ; point to next byte in song
 lda (sound),y   ; get the Value for the AY register
 sta VIA2IORA
 
 ; write command
 LDA #$06    ;SEND "WRITE COMMAND"
 STA VIA2IORB   ;TO SOUND CHIP
 LDA #$04    ;THROUGH PORT B
 STA VIA2IORB
 bpl continue   ; always branch to continue
firstvia:
.ENDIF

;there is a bug in this sequence of commands.
;it works on the emulator but not the real hardware.
 sta VIAIORA    ; set the AY data register number

 ; latch command
 LDA #$07    ;SEND "LATCH COMMAND"
 STA VIAIORB    ;TO SOUND CHIP
 LDA #$04    ;THROUGH PORT B
 STA VIAIORB
 
 iny      ; point to next byte in song
 lda (sound),y   ; get the Value for the AY register
 sta VIAIORA
 
 ; write command
 LDA #$06    ;SEND "WRITE COMMAND"
 STA VIAIORB    ;TO SOUND CHIP
 LDA #$04    ;THROUGH PORT B
 STA VIAIORB

continue: 
 iny      ; point to next byte in song
 dex      ; decrement the register count
 bne reglop    ; keep looping if not done setting registers
 
 rts
;**************************************************************

 


.IF USE_CMD
;**************************************************************
;* docomnd
;**************************************************************
;* Description:
;*   code to handle optional commands embedded within the song data
;*   this is not enabled for Donkey Kong and does not assemble
;* Parameters:
;*   None
;* Modifies:
;*   A
;* Returns:
;*   Whatever the command returns (garbage?)
;**************************************************************
docomnd:
 stx temp    ; save x, x must be restored by the end of each command

 ; register A contains the command number + 128
 ;and #!01111111   ; clear the bit for 128 (not needed, shift removes it)
 ; convert the command nuber to the table offset

 ; get command address from command pointer table (self modifying code)
.IF USE_C02 > 0
 asl       ; multiply by 2 (addresses are 2 bytes in size)
 tax       ; move the command table offset to x
 jmp (commandtable,x)
.ELSE
;.IF  = 0
 ; table does not cross a page boundary and code is not in ROM
 asl       ; multiply by 2
 sta tcall+1     ; modify the table address lsb
tcall:
 jmp (commandtable)   ; make the indirect jump
;.ELSE
; ; table crosses a page boundary or code is in ROM
; tax       ; move the command table offset to x
; lda commandtableH+128,x  ; load MSB of the command address
; pha       ; push it on the stack
; lda commandtableL+128,x  ; load LSB of the command address
; pha       ; push it on the stack 
; rts       ; call address on the stack
;.ENDIF
.ENDIF


;**************************************************************



;**************************************************************
;* command table
;**************************************************************
;* Description:
;*   A table of pointers to any commands we create to be used from the player.
;*   Table must not cross a page boundary
;*   65C02 version uses word pointers 
;*   6502 version uses High and Low byte tables for pointers
;************************************************************** 
.IF USE_C02 > 0
commandtable:
 .word playsongend  ; 0 end of song
 .word playsongend  ; just in case
.ELSE
;.IF  = 0
commandtable:
 .word playsongend  ; 0 end of song
 .word playsongend  ; just in case
;.ELSE
;commandtableH:
; .byt >playsongend  ; 0 end of song
; .byt >playsongend  ; just in case
;commandtableL:
; .byt <playsongend  ; 0 end of song
; .byt <playsongend  ; just in case

.ENDIF
;**************************************************************

.ENDIF

 
 


codeend:
 
 .data       ; start of the bss data segment
;**************************************************************
;* variables
;**************************************************************
;_VblCounter: .byt 0   ; TOF flag variable (not used at this time)
SAVE_IRQ:  .byt 00,00  ; place to store the existing interrupt handler's address
songstart:  .byt 00,00  ; holds the current song pointer so we can reset the pointer to the start on repeat.
;**************************************************************



;**************************************************************
;* songtable
;**************************************************************
;* Description:
;*   table of pointers to songs
;*   any unsupported song is set to enptysong
;*   All gameplay screens initially play the girders song like the Colecovision version
;**************************************************************
songtable:
 .word emptysong    ; intro screen
 .word howhigh     ; how high can you get?
 .word screen1     ; girders
 .word screen2     ; conveyer
 .word screen1     ; springs
 .word screen4     ; rivets
 .word emptysong    ; Complete Girder Level
 .word emptysong    ; Complete Rivet 1
 .word emptysong    ; Complete Rivet 2
 .word emptysong    ; Kong Taunting
 .word emptysong    ; Kong Fall/saved girl
 .word emptysong    ; Hammertime
 .word emptysong    ; Die
 .word emptysong    ; Out of time
;**************************************************************



;**************************************************************
;* howhigh
;**************************************************************
;* Description:
;*   Song for the 'How High Can You Get?' screen
;*   It currently sounds horrible because I'm working on timing
;*   first.
;**************************************************************
howhigh:
 .byt 1,4,7,56,1,0,0,244,8,15  ;1st note
 .byt 6,1,8,8
 .byt 11,2,0,217,8,15     ;2nd note
 
 .byt 6,1,8,2
 .byt 12,2,0,205,8,15     ;3rd note
; .byt 8,1,8,0
 .byt 2,1,8,14
 .byt 2,1,8,13
 .byt 2,1,8,12
 .byt 2,1,8,0
 .byt 14,2,0,222,8,15     ;4th note
 .byt 6,1,8,7
 .byt 5,2,0,244,8,15     ;5th note
 .byt 4,1,8,0
 .byt 7,2,0,222,8,15     ;6th note
 .byt 8,1,8,0
 .byt 4,2,0,244,8,15     ;7th note
 .byt 4,1,8,12
 .byt 4,1,8,10
 .byt 4,1,8,5
 .byt 4,1,8,2
 .byt 7,3,1,1,0,232,8,11    ;8th note
 .byt 6,2,0,110,8,11     ;9th note
 .byt 6,2,0,2,8,11     ;10th note
 .byt 6,3,1,0,0,193,8,11     ;11th note
 .byt 6,3,1,1,0,70,8,11     ;12th note
 .byt 4,1,8,7
 .byt 4,1,8,4
 .byt 4,1,8,0

; .byt 1,0        ; done
 .byt 33,255       ; repeat forever
;**************************************************************



;**************************************************************
;* screen1
;**************************************************************
;* Description:
;*   Donkey Kong Screen 1 (girders) background tune
;**************************************************************
screen1:
 .byt 1,4,7,56,1,$01,0,$E8,8,9  ;1st note
 .byt 4,1,8,0
 .byt 12,2,0,$E8,8,9   ;2nd note
 .byt 4,1,8,0
 .byt 4,2,0,$83,8,9   ;3rd note
 .byt 4,1,8,0
 .byt 6,2,0,$B3,8,9   ;4th note
 .byt 4,1,8,0
 .byt 6,3,1,$02,0,$45,8,9  ;5th note
 .byt 4,1,8,0
 .byt 19,255      ;repeat forever
;**************************************************************



;**************************************************************
;* screen2
;**************************************************************
;* Description:
;*   Donkey Kong Screen 2 (conveyers) background tune
;*   Just three repeating notes with a rest
;**************************************************************
screen2:
 .byt 1,4,8,$00,9,$00,10,$00,7,56
 .byt 8,3,1,$01,0,$E8,8,9   ;1st note
 .byt 5,1,8,0
 .byt 2,2,0,$E8,8,9    ;2nd note
 .byt 5,1,8,0
 .byt 2,2,0,$E8,8,9    ;3rd note
 .byt 5,1,8,0
 .byt 1,255      ;repeat forever
;**************************************************************



;**************************************************************
;* screen4
;**************************************************************
;* Description:
;*   Donkey Kong Screen 4 (rivets) background tune
;*   Just three repeating notes with a rest
;**************************************************************
screen4:
 .byt 1,4,8,$00,9,$00,10,$00,7,56
 .byt 8,3,1,$01,0,$E8,8,9   ; 1st note, volume 9
 .byt 5,1,8,0      ; stop note by setting volume to zero
; .byt 2,3,1,$01,0,$E8,8,9   ; 2nd note, volume 9
 .byt 2,1,8,9      ; 2nd note, volume 9
 .byt 5,1,8,0      ; stop note by setting volume to zero
; .byt 2,3,1,$01,0,$B3,8,9   ;3rd note
 .byt 2,2,0,$B3,8,9    ;3rd note, volume 9
 .byt 5,1,8,0      ; stop note by setting volume to zero
 .byt 1,255      ;repeat forever
;**************************************************************


;**************************************************************
;* emptysong
;**************************************************************
;* Description:
;*   A song that just turns off the volume on the AY channel I'm using for music
;*   and then exits.  On exit, the player sets the playing flag to zero so the
;*   interrupt will exit without playing.
;**************************************************************
emptysong:
 .byt 1,1,8,0      ; set volume of chvannel A to 0
 .byt 1,0       ; done
;**************************************************************



;**************************************************************
;* chord
;**************************************************************
;* just playing with chords.  
;* These are from the Tandy Speech & Sound Pak demo.
;* It's hardware uses a different clock speed so it needs adjusted
;* for the Mockingboard.  
;**************************************************************
chord:
 .byt  1, 14,0,172,1,1,2, 83,3,1,4, 29, 5,1,6,0,7,56,8,9,9,9,10,9,11,0,12,0,13,0 ; C chord
 .byt 64,  3,          2, 64,    4,254, 5,0 ; F chord
 .byt 64,  4,0,197,    2,125,    4, 29, 5,1 ; G chord
 .byt 63,255
;**************************************************************
 
 
;**************************************************************
;*
;**************************************************************
;* Unused.
;**************************************************************
 .byt 159,15,15,191,15,15,224,15,15,98,0,14,5,0,0,0,0
;**************************************************************

DATAEND:    ; so we have the end of the data in the list file for debugging
 
 ;.end

No comments:

Post a Comment