;=============== ; VMW PT3 Player ;=============== ; zero page definitions .include "zp.inc" .include "hardware.inc" ; Location the files load at. ; If you change this, you need to update the Makefile PT3_LOC = $4000 PT3_USE_ZERO_PAGE = 1 ; Number of files. Should probably detect this automatically NUM_FILES = 20 ;============================= ; Setup ;============================= pt3_setup: jsr HOME jsr TEXT bit LORES ; Lo-res graphics bit SET_GR bit TEXTGR ; split text/graphics jsr clear_screens ;=============================== ; Check for Apple II/II+/IIe/IIc ;=============================== ; this is used to see if we have lowecase support lda $FBB3 ; IIe and newer is $06 cmp #6 beq done_lowecase_smc lda #$d0 ; set if older than a IIe sta apple_ii_smc done_lowecase_smc: ;=============== ; Init disk code ;=============== jsr rts_init ;=============== ; init variables ;=============== lda #0 sta DRAW_PAGE sta DONE_PLAYING sta WHICH_FILE sta LOOP ;======================= ; Detect mockingboard ;======================== jsr mockingboard_detect ; call detection routine bcs mockingboard_found lda #not_message sta OUTH inc CV jsr move_and_print ; jmp forever_loop ; and wait forever jmp init_mockingboard ; try anyway in slot#4 mockingboard_found: ; lda #found_message ; sta OUTH ; inc CV ; jsr move_and_print ;================================================== ; patch the playing code with the proper slot value ;================================================== jsr mockingboard_patch ;============================ ; Init the Mockingboard ;============================ init_mockingboard: ; set up 50Hz interrupt jsr mockingboard_init jsr mockingboard_setup_interrupt ; init the mockingboard jsr reset_ay_both jsr clear_ay_both ;================== ; load first song ;================== jsr new_song ;============================ ; Init Background ;============================ jsr set_gr_page0 jsr fire_init ;============================ ; Enable 6502 interrupts ;============================ start_interrupts: cli ; clear interrupt mask ;============================ ; Loop forever ;============================ main_loop: ; Do the visualization jsr draw_fire_frame jsr put_letters jsr page_flip check_done: lda DONE_PLAYING asl ; bit 7 to carry, bit 6 to bit 7 beq main_loop ; if was all zeros, loop bcs main_loop ; if high bit set, paused sei ; disable interrupts bmi minus_song ; if bit 6 set, then left pressed ; else, either song finished or ; right pressed plus_song: jsr increment_file bcc done_play ; branch always minus_song: jsr decrement_file done_play: lda #0 sta DONE_PLAYING ; clear the flame ; FIXME: doesn't matter as we aren't displaying right now jsr fire_setline jsr new_song ; re-enable the flame lda #7 jsr fire_setline bmi start_interrupts ;======================================== ;======================================== ; Helper routines below ;======================================== ;======================================== ;================= ; load a new song ;================= new_song: ;========================= ; Init Variables ;========================= ; ? ;=========================== ; Print loading message ;=========================== jsr clear_bottoms ; clear bottom of page 0/1 lda #0 ; print LOADING message sta CH lda #21 sta CV lda #loading_message sta OUTH jsr print_both_pages ;=========================== ; Load in PT3 file ;=========================== jsr get_filename lda #8 ; print filename to screen sta CH ;lda #21 ;sta CV lda INL sta OUTL lda INH sta OUTH jsr print_both_pages ; needs to be space-padded $A0 30-byte filename lda #readfile_filename sta namhi ldy #0 ldx #30 ; 30 chars name_loop: lda (INL),Y beq space_loop ora #$80 sta (namlo),Y iny dex bne name_loop beq done_name_loop space_loop: lda #$a0 ; pad with ' ' sta (namlo),Y iny dex bne space_loop done_name_loop: ; open and read a file ; loads to whatever it was BSAVED at (default is $4000) jsr read_file ; read PT3 file from disk ;========================= ; Print Info ;========================= jsr clear_bottoms ; clear bottom of page 0/1 ; NUL terminate the strings we want to print lda #0 sta PT3_LOC+$3E sta PT3_LOC+$62 upcase: ; if Apple II/II+, uppercase the lowercase letters apple_ii_smc: beq no_uppercase ldy #$1e upcase_loop: lda PT3_LOC,Y cmp #$60 bcc not_lowercase ; blt sbc #$20 sta PT3_LOC,Y not_lowercase: iny cpy #$63 bne upcase_loop no_uppercase: ; print title lda #>(PT3_LOC+$1E) ; point to header title sta OUTH lda #<(PT3_LOC+$1E) sta OUTL lda #20 ; VTAB 20: HTAB 4 sta CV lda #4 sta CH jsr print_both_pages ; print, tail call ; Print Author lda #<(PT3_LOC+$42) sta OUTL inc CV ; VTAB 21: HTAB 4 jsr print_both_pages ; print, tail call ; Print clock lda #>(clock_string) ; point to clock string sta OUTH lda #<(clock_string) sta OUTL lda #23 ; VTAB 23: HTAB 13 sta CV lda #13 sta CH jsr print_both_pages ; print, tail call ; Print which file ; first update with current values lda WHICH_FILE adc #$1 ldy #0 jsr convert_decimal lda #NUM_FILES ldy #3 jsr convert_decimal ; now print modified string lda #>(which_song_string) ; point to which song string sta OUTH lda #<(which_song_string) sta OUTL lda #0 ; HTAB 1 sta CH jsr print_both_pages ; print, tail call ; Print MHz indicator lda #>(mhz_string) ; point to MHz string sta OUTH lda #<(mhz_string) sta OUTL lda #34 ; HTAB 34 sta CH jsr print_both_pages ; print, tail call ; update the MHz indicator with current state ldx #'0'+$80 lda convert_177_smc1 cmp #$18 beq done_MHz ldx #'7'+$80 done_MHz: stx $7F4 stx $BF4 ; Print Left Arrow (INVERSE) lda #'<' sta $6D0 sta $AD0 lda #'-' sta $6D1 sta $AD1 ; Print Rright Arrow (INVERSE) sta $6F6 sta $AF6 lda #'>' sta $6F7 sta $AF7 jsr pt3_init_song ;================================= ; Calculate Length of Song ;================================= ; There's no easy way to do this? (???) ; We walk through the song counting frames ; We can't even do this quickly, as the number of frames ; per pattern can vary, and you have to parse a channel ; to see this, and channel data is varying-width and so ; you have to parse it all. ; Time is just number of frames/50Hz sta current_pattern_smc+1 frame_count_loop_store: sta current_line_smc+1 sta current_subframe_smc+1 frame_count_loop: lda current_line_smc+1 ora current_subframe_smc+1 bne fc_pattern_good ; load a new pattern in jsr pt3_set_pattern lda DONE_SONG bne done_counting fc_pattern_good: ldx current_subframe_smc+1 ;;ldx #(NOTE_STRUCT_SIZE*0) bne fc_line_good ; we only calc length of chanel A, hopefully enough lda #1 sta pt3_pattern_done_smc+1 ; decode_note(&pt3->a,&(pt3->a_addr),pt3); jsr decode_note lda pt3_pattern_done_smc+1 bne fc_line_good inc current_pattern_smc+1 ; increment pattern bne frame_count_loop_store ; branch always fc_line_good: inc current_subframe_smc+1 ; subframe++ lda current_subframe_smc+1 eor pt3_speed_smc+1 ; if we hit pt3_speed, move to next bne fc_do_frame fc_next_line: sta current_subframe_smc+1 ; reset subframe to 0 inc current_line_smc+1 ; and increment line lda current_line_smc+1 eor #64 ; always end at 64. bne fc_do_frame ; is this always needed? fc_next_pattern: sta current_line_smc+1 ; reset line to 0 inc current_pattern_smc+1 ; increment pattern fc_do_frame: inc time_frame_smc+1 time_frame_smc: lda #$00 eor #50 bne frame_count_loop sta time_frame_smc+1 ; see if overflow low s ldx $BD0+13+10 cpx #'9'+$80 bne inc_low_s ; see if overflow high s ldx $BD0+13+9 cpx #'5'+$80 bne inc_high_s inc $7D0+13+7 inc $BD0+13+7 ldx #'0'+$80-1 inc_high_s: inx stx $7D0+13+9 stx $BD0+13+9 clear_low_s: ldx #'0'+$80-1 inc_low_s: inx stx $7D0+13+10 stx $BD0+13+10 inc_done: fc_bayern: bne frame_count_loop done_counting: ; re-init, as we've run through it lda #0 sta DONE_PLAYING sta current_pattern_smc+1 jmp pt3_init_song ;================== ; Get filename ;================== ; WHICH_FILE holds number ; MAX_FILES has max ; Scroll through until find ; point INH:INL to it get_filename: ldy #0 lda #song_list sta INH ldx WHICH_FILE beq filename_found get_filename_loop: inner_loop: iny lda (INL),Y bne inner_loop iny dex bne get_filename_loop filename_found: tya clc adc INL sta INL bcc skip_inh_inc inc INH skip_inh_inc: rts ;=============================== ; Increment file we want to load ;=============================== increment_file: inc WHICH_FILE lda WHICH_FILE eor #NUM_FILES bne done_increment sta WHICH_FILE done_increment: rts ;=============================== ; Decrement file we want to load ;=============================== decrement_file: dec WHICH_FILE bpl done_decrement lda #(NUM_FILES-1) sta WHICH_FILE done_decrement: rts ;========================= ; convert_decimal ;========================= ; convert byte (<100) to tens/ones decimal ; this is probably not the optimal way to do this ; value in A, output in ASCII+$80 in which_10s:which_1s ; trashes X convert_decimal: ldx #'0'+$80 stx which_1s_smc+1 stx which_10s_smc+1 tax ; special case zero beq conv_decimal_done conv_decimal_loop: inc which_1s_smc+1 which_1s_smc: lda #$d1 cmp #':'+$80 bne conv_decimal_not_10 lda #'0'+$80 sta which_1s_smc+1 inc which_10s_smc+1 conv_decimal_not_10: dex bne conv_decimal_loop conv_decimal_done: which_10s_smc: lda #$d1 sta which_song_string,y lda which_1s_smc+1 sta which_song_string+1,y rts ;========== ; filenames ;========== song_list: .include "song_list.inc" ;========= ;routines ;========= .include "gr_offsets.s" .include "text_print.s" .include "gr_fast_clear.s" .include "pageflip.s" .include "gr_setpage.s" .include "qkumba_rts.s" .include "keypress_minimal.s" ; pt3_lib stuff .include "pt3_lib_core.s" .include "pt3_lib_init.s" .include "pt3_lib_mockingboard_setup.s" .include "interrupt_handler.s" .include "pt3_lib_mockingboard_detect.s" ; visualization .include "fire.s" .include "random16.s" .include "gr_putsprite.s" .include "put_letters.s" ;========= ; strings ;========= ;mocking_message: .asciiz "LOOKING FOR MOCKINGBOARD IN SLOT #4" not_message: .byte "NOT " found_message: .asciiz "FOUND" ;done_message: .asciiz "DONE PLAYING" loading_message: .asciiz "LOADING" clock_string: .asciiz "0:00 / 0:00" mhz_string: .asciiz "1.7MHZ" which_song_string: .asciiz "00/00"