; sidplay for commodore 64 ; ; sidplay.s - SID-Player for Commodore 64 ; Copyright (C) 2003 Tommi Saviranta ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ; ; ; Version: sidplay.s v0.2.6 23-Jul-2003 tsaviran@cs.helsinki.fi processor 6502 seg data org $0801, 0 dc $0b, $08, $d3, $07, $9e, "2061", $00 dc $00, $00 seg code org 2061, 0 lda #$17 ; Switch to lowercase sta $d018 ; --- ; Print banner ; --- begin: lda #txt_screen jsr STROUT ; --- ; Request filename ; --- request: ldx #5 ; Row 5 ldy #8 ; Column 8 clc ; Clear C jsr PLOT ; Move cursor ldx #$00 stx $c6 ; Clear buffer stx $cc ; Cursor on input: stx $8d in_get: jsr IGETIN cmp #$03 ; RUN/STOP beq begin ; Quit ? ldx $8d ; Input ? beq in_got ; X = 0 ? cmp #$14 ; DELETE beq in_del ; -> Delete character cmp #$0d ; RETURN beq open ; -> End input in_got: cmp #$20 ; SPACE bcc in_get ; Read next character cpx #$10 ; Up to 16 characters beq in_get ; Read next character inx in_dis: jsr ICHROUT jmp input ; Read next character in_del: dex ; Reduce length jmp in_dis ; Branch back ; --- ; Prepare screen for opening file and copy filename to buffer ; --- open: ; Calculate filename length sec ; Get cursor position jsr PLOT tya sbc #8 tay sta flen + 1 fname: lda $04cf,y tax sbc #$20 bmi fconv_40 ; @ab...z...SPACE txa sbc #$40 bmi fconv_none ; ?AB...Z... ; What's left, is ?A-Z... fconv_80: txa ora #$80 jmp fconv_done fconv_40: txa ora #$40 jmp fconv_done fconv_none: txa fconv_done: sta $bfff,y dey bne fname ; Prepare screen lda #$01 sta $cc ; No cursor lda #$20 ; 'space' jsr ICHROUT ; Remove possibly still visible cursor ldx #12 ; Row 12 ldy #8 ; Column 8 clc ; Clear C jsr PLOT ; Move cursor lda #txt_loading jsr STROUT ; --- ; Open file ; --- ldx #$00 ; Filename at c000 ldy #$c0 flen: lda #$00 ; Filelength jsr SETNAM ; Set filename lda #8 ; File #8 tax ; Device #8 tay jsr SETLFS ; Set file parameters jsr IOPEN ; Open file jsr READSS ; Check for errors beq file_ok lda #8 ; File #8 jsr ICLOSE ; Close file jmp begin ; Opening file failed... ; Get 32 bytes from file and print 'em out to (8, y) get32: ldy #8 ; Column 8 clc ; Move cursor to (8, x) jsr PLOT ldy #$20 get32_0: jsr ICHRIN ; Load byte ; Convert characters beq get32_2 ; Ignore zeros ($00) cmp #$40 ; No conversion needed for < $40 bmi get32_1 eor #$20 ; For the others, "or $20" get32_1: jsr ICHROUT ; Pop it out get32_2: dey bne get32_0 rts file_ok: ; Prepare for input from file #1 ldx #8 ; File #8 jsr ICHKIN ; Set Input ; TODO: Check errors again ; Read PSID-header -- bytes $00 - $15 ldy #$00 ; Bufferpointer to 0 read0: jsr ICHRIN ; Load next byte sta BUFFER,y ; Save byte to buffer iny ; Increase pointer tya cmp #$16 ; Got 22 bytes already ? bne read0 ; No. Keep reading. lda #$00 ; Terminator sta BUFFER,y ; Add terminator ; Check header bytes $00-$03 ; TODO -- we could try using xor (0x50 ^ 0x53 ^ 0x49 ^ 0x44 = 0x0e) ; Get version - ignore $04, only read $05 ldy #$05 lda BUFFER,y sta version ; We don't care about dataOffset ; Get loadAddr, initAddr, playAddr, songs, startSong and speed all at once ldx #$08 ; Source pointer ldy #$00 ; Target pointer h_c_0: lda BUFFER,x ; Read byte sta loadaddr,y ; Save byte inx iny tya cmp #$0e bne h_c_0 ; Copy until 14 bytes are copied ; Set current song to default song ldx #$11 ; Forget msb ldy BUFFER,x dey sty current_song ; Get title -- bytes $16 - $35 ldx #7 ; Move cursor to (8, 7) jsr get32 ; Get author -- bytes $36 - $55 ldx #8 ; Move cursor to (8, 8) jsr get32 ; Get copyright -- bytes $56 - $75 ldx #9 ; Move cursor to (8, 9) jsr get32 ; Handle PSID v2 lda version cmp #$01 beq calc_addr ; Actually, we don't care what those bytes say, we just want to ; move on. ldy #6 ; Bufferpointer to 0 read2: jsr ICHRIN ; Load next byte dey bne read2 ; --- ; See if loadaddr is zero and if neccessary, load loadaddr from the file ; --- calc_addr: ; See if loadaddr is non-zero lda loadaddr bne load ; Not zero lda loadaddr + 1 bne load ; Not zero ; loadaddr is zero. We need to read it from the file jsr ICHRIN sta loadaddr + 1 jsr ICHRIN sta loadaddr ; --- ; Load real payload -- PSID-file ; --- load: lda loadaddr + 1 ; Addresses in PSID are in big-endian ldx loadaddr sta sta_d + 1 ; Copy loadaddr to sta_d stx sta_d + 2 ldx #$00 ldy #$00 ; Set pointer to zero l_sid: jsr ICHRIN ; Get next byte sta $d020 ; Change border color sta_d: sta $0000,y ; sta ----, y stx $d020 jsr READSS ; Check for EOF and #$40 bne close ; Caught EOF iny ; Increase y bne l_sid ; Y != 0 -- no need to increase msb inc sta_d + 2 ; Increase target's msb jmp l_sid ; --- ; Close file ; --- close: lda #8 ; file #8 jsr ICLOSE ; Close Vector [f291] ; --- ; Everything's set. Now we can initialize things and start playing. ; --- ; See if initaddr is non-zero lda loadaddr bne init ; Not zero lda loadaddr + 1 bne init ; Not zero ; initaddr was zero - lets use loadaddr as initaddr lda loadaddr ldx loadaddr + 1 sta initaddr stx initaddr + 1 init: ; Create proper jsr lda initaddr ; In big-endian... ldx initaddr + 1 sta ini_d + 2 stx ini_d + 1 ; Set A to default subsong ldy startsong + 1 ; Mind only lsb - after all, A is 8-bit dey tya ; Initialize subsong A init_song: ; Set volume to max lda #$0f sta SID_FIL_VOL ; TODO - print current subsong ini_d: jsr $0000 ; jsr ---- ; See if we need to install playroutine lda playaddr bne set_player lda playaddr + 1 bne set_player ; No playaddr was defined. jmp play set_player: ; Write playaddr to interrupt-handler's jsr lda playaddr ; Big-endian ldx playaddr + 1 sta irqhandler + 2 stx irqhandler + 1 ; Copy old interrupt-handler's address to new's jmp lda $0314 ldx $0315 sta irq_orig + 1 stx irq_orig + 2 ; Install new interrupt-handler lda #irqhandler sei sta $0314 stx $0315 cli jsr set_timer_a play: lda #$20 ; Write spaces to remove "loading" from screen. ldy #10 clear_status: sta $05e7,y dey bne clear_status play_loop: ; Play until a key is pressed lda $c5 ; Get key tax ; --- Show keycodes in binary ; ldy #$44 ; and #$80 ; beq b7 ; ldy #$41 ;b7: tya ; sta $0410 ; ; txa ; ldy #$44 ; and #$40 ; beq b6 ; ldy #$41 ;b6: tya ; sta $0411 ; ; txa ; ldy #$44 ; and #$20 ; beq b5 ; ldy #$41 ;b5: tya ; sta $0412 ; ; txa ; ldy #$44 ; and #$10 ; beq b4 ; ldy #$41 ;b4: tya ; sta $0413 ; ; txa ; ldy #$44 ; and #$08 ; beq b3 ; ldy #$41 ;b3: tya ; sta $0414 ; ; txa ; ldy #$44 ; and #$04 ; beq b2 ; ldy #$41 ;b2: tya ; sta $0415 ; ; txa ; ldy #$44 ; and #$02 ; beq b1 ; ldy #$41 ;b1: tya ; sta $0416 ; ; txa ; ldy #$44 ; and #$01 ; beq b0 ; ldy #$41 ;b0: tya ; sta $0417 ; txa cmp #$40 beq play_loop ; Find out which key was pressed txa cmp #$3f ; RUN/STOP beq stop_playing txa cmp #$2c ; > beq next_song txa cmp #$2f ; < beq prev_song ; No functional key was pressed jmp play_loop ; --- ; Wait until key has been released ; --- release_key: lda $c5 ; Get key cmp #$40 bne release_key rts ; --- ; Play next subsong ; --- next_song: jsr release_key ; Wait for key-release jsr restore_vecs ldy current_song ; Get current song iny tya cmp songs + 1 ; Forget msb beq n_s_0 ; Save song to current_song and initialize new tune tya sta current_song jmp init_song n_s_0: ; Set song to zero and initialize new tune lda #$00 sta current_song jmp init_song ; --- ; Previous song ; --- prev_song: jsr release_key ; Wait for key-release jsr restore_vecs lda current_song ; Get current song bne p_s_1 ; Set song number to song count and initialize new tune ldy songs + 1 ; Forget msg p_s_0: dey sty current_song tya jmp init_song p_s_1: ; Set song to previous song and initialize new tune ldy current_song ; Get current song jmp p_s_0 ; --- ; We were playing, but wanted to stop. ; --- stop_playing: lda #$00 ; Mute SID sta SID_FIL_VOL jsr release_key ; Wait for key-release jsr restore_vecs ; Restore vectors jmp begin ; Start ip all over again ; --- ; Restore "things" so we can change song. ; --- restore_vecs: jsr RESTOR ; jsr $ffcc ; jsr $a81d jmp ICLRCH ; jsr $fc93 ; --- ; Set CIA Timer A ; --- set_timer_a: sei ; Disable interrupts lda CIA_CTRL_A ; Load CIA 1 CR A and #$fe ; Stop timer A sta CIA_CTRL_A ; Write back jsr cia_60 ; beq cia_60 ; Set CIA timer A to 50 Hz lda #$4c ldx #$f8 jmp cia_set cia_60: ; Set CIA timer A to 60 Hz -- TODO - calculate proper value here lda #$40 ldx #$25 cia_set: ; Set CIA timer A sta TIMER_A_H stx TIMER_A_L lda CIA_CTRL_A ; Loead CIA 1 CR A ora #$01 ; Start timer A sta CIA_CTRL_A ; Write back cli ; Enable interrupts rts ; XXX jam: iny sty $d020 jmp jam ; --- ; Interrupt handler ; --- irqhandler: jsr $0000 ; Jump to play-routine irq_orig: jmp $0000 ; Original interrupt handler ; --- ; Variables and buffers ; --- ; TODO -- use memory instead of program-space !!! --- XXX orig_irq_lsb: dc $00 ; TODO - Write something sensible here orig_irq_msb: dc $00 version: dc $00 ; ...in ASCII, I mean. It like nice loadaddr: dc $00, $00 initaddr: dc $00, $00 ; program's hexdump :-) playaddr: dc $00, $00 songs: dc $00, $00 startsong: dc $00, $00 speed0: dc $00 speed1: dc $00 speed2: dc $00 speed3: dc $00 current_song: dc $00 ; --- ; Strings ; --- ;txt_screen: dc $93, $0d ; row 0 ; dc "sidplay64 v0.2.2", $0d ; row 1 ; dc "Copyright 2003 Tommi Saviranta", $0d ; row 2 ; dc "", $0d ; row 3 ; dc $0d ; row 4 ; dc "File :", $0d ; row 5 ; dc $0d ; row 6 ; dc "Title :", $0d ; row 7 ; dc "Author:", $0d ; row 8 ; dc "(C) :", $0d ; row 9 txt_screen: dc $93, $0d ; row 0 dc "SIDPLAY64 V0.2.2", $0d ; row 1 dc "cOPYRIGHT 2003 tOMMI sAVIRANTA", $0d ; row 2 dc "", $0d ; row 3 dc $0d ; row 4 dc "fILE :", $0d ; row 5 dc $0d ; row 6 dc "tITLE :", $0d ; row 7 dc "aUTHOR:", $0d ; row 8 dc "(c) :", $0d ; row 9 dc $00 txt_loading: dc "lOADING..." dc $00 BUFFER equ $c000 SID_FIL_VOL equ $d418 TIMER_A_H equ $dc05 TIMER_A_L equ $dc04 CIA_CTRL_A equ $dc0e STROUT equ $ab1e RESTOR equ $ff8a READSS equ $ffb7 SETLFS equ $ffba SETNAM equ $ffbd IOPEN equ $ffc0 ICLOSE equ $ffc3 ICHKIN equ $ffc6 ICLRCH equ $ffcc ICHRIN equ $ffcf ICHROUT equ $ffd2 IGETIN equ $ffe4 PLOT equ $fff0 ZERO_0 equ $fb ; Zeropages for User Programs ZERO_1 equ $fc ZERO_2 equ $fd ZERO_3 equ $fe