;=========================== ; Draw the Mode7 Background ;=========================== ; opening screen, original code ; $2d070 cycles = 184,432 = 5.4 fps ; $2da70 cycles (added in 2 cycle cpx) a00 = 2560, yes, 32*40=1280 ; $2aec3 cycles (update inner loop) = 175,811 = 5.7 fps ; $29af5 cycles (move things around) = 170,741 = 5.85 fps ; $29988 cycles (save another cycle) = 170,393 = 5.86 fps ; $29968 cycles (align lookup tables) ; $28FA8 cycles (remove clc) = 167,848 = 5.95 fps ; TODO: ; could save cycle running inner X loop backwards, but would ; have to redo a lot of the math to start at right of screen ; could save avg of 2 cycles on inner X loop if we have special ; case odd vs even lines ; flying_loop -> check_done 2040 - 214f 2E ; ; check_done -> draw_background 214f - 219b 2E ; ; draw_background -> check_over_water 219b - 219e 298ad ; ; check_over_water -> no_splash 219e - 21d0 5b ; ; no_splash -> done_flying_loop 21d0 - 229b aa4 draw_background_mode7: ; setup initial odd/even color mask lda #$f0 ; 2 sta COLOR_MASK ; 3 ; start Y at 8 (below horizon line) lda #8 ; 2 sta SCREEN_Y ; 3 ;============= ; 10 screeny_loop: and #$fe ; be sure SCREEN_Y used later is even ; 2 tay ; put in Y for lookup table later ; 2 lda COLOR_MASK ; flip mask for odd/even row plotting ; 3 eor #$ff ; 2 sta COLOR_MASK ; 3 sta mask_label+1 ; setup self-modifying code ; 4 bpl odd_branch ; smc for even/odd line ; 2nt/3 lda #$1d ; ora abs,X opcode is $1d ; 2 bne ok_branch ; bra ; 3 odd_branch: lda #$2c ; bit is $2c ; 2 ok_branch: sta innersmc1 ; actually update ora/bit ; 4 ;============ ; ?27 setup_gr_addr: lda gr_offsets,Y ; lookup low-res memory row address ; 4 sta innersmc1+1 ; smc low addr ; 4 sta innersmc2+1 ; smc low addr ; 4 lda gr_offsets+1,Y ; load high part of address ; 4 clc ; clear carry for add ; 2 adc DRAW_PAGE ; add in draw page offset ; 3 sta innersmc1+2 ; smc high addr ; 4 sta innersmc2+2 ; smc high addr ; 4 ;============= ; 29 calc_horizontal_scale: ; Calculate the horizontal scale using a lookup table ; horizontal_scale.i *ALWAYS* = 0 ; unsigned char horizontal_lookup[7][32]; ;horizontal_scale.f= ; horizontal_lookup[space_z.i&0xf][(screen_y-8)/2]; ; horizontal_lookup[(space_z<<5)+(screen_y-8)] clc ; 2 lda SCREEN_Y ; 3 spacez_shifted: adc #0 ; self-modify, loads (spacez<<5)-8 ; 2 tay ; 2 lda horizontal_lookup,Y ; 4 sta NUM1L ; HORIZ_SCALE_F is input to next mul ; 3 ;============ ; 16 ; mul2 ; calculate the distance of the line we are drawing ; fixed_mul(&horizontal_scale,&scale,&distance); lda #0 ; HORIZ_SCALE_I is always zero ; 2 sta NUM1H ; 3 ; NUM1L was set to HORIZ_SCALE_F previously ; lda #CONST_SCALE_I ; SCALE_I ; 2 sta NUM2H ; 3 lda #CONST_SCALE_F ; SCALE_F ; 2 sta NUM2L ; 3 sec ; don't reuse previous settings ; 2 jsr multiply ; 6 stx DISTANCE_I ; 2 sta DISTANCE_F ; 2 ;========== ; 27 ; calculate the dx and dy to add to points in space ; we add to the starting values on each row to get the next ; space values ; dx.i=fixed_sin[(angle+8)&0xf].i // -sin() lda ANGLE ; 3 clc ; 2 adc #8 ; 2 and #$f ; 2 asl ; 2 tay ; 2 lda fixed_sin,Y ; load integer half ; 4 sta NUM2H ; use as source in upcomnig mul ; 3 ; dx.f=fixed_sin[(angle+8)&0xf].f; // -sin() iny ; point to float half ; 2 lda fixed_sin,Y ; load it from lookup table ; 4 sta NUM2L ; use as source in upcoming mul ; 3 ;========== ; 29 ;mul3 ; fixed_mul(&dx,&horizontal_scale,&dx); ; DX_I:DX_F already set in NUM2H:NUM2L clc ; reuse HORIZ_SCALE in NUM1 ; 2 jsr multiply ; 6 stx DX_I ; 3 sta DX_F ; 3 ;========== ; 14 ; dy.i=fixed_sin[(angle+4)&0xf].i; // cos() lda ANGLE ; 3 clc ; 2 adc #4 ; 2 and #$f ; 2 asl ; 2 tay ; 2 lda fixed_sin,Y ; load integer half ; 4 sta NUM2H ; use as source in upcoming mul ; 3 ; dy.f=fixed_sin[(angle+4)&0xf].f; // cos() iny ; point to float half ; 2 lda fixed_sin,Y ; load from lookup table ; 4 sta NUM2L ; use as source in upcoming mul ; 3 ;========== ; 29 ;mul4 ; fixed_mul(&dy,&horizontal_scale,&dy); ; DY_I:DY_F already in NUM2H:NUM2L clc ; reuse horiz_scale in num1 ; 2 jsr multiply ; 6 stx DY_I ; 3 sta DY_F ; 3 ;========== ; 14 ;================================= ; calculate the starting position ;================================= ; fixed_add(&distance,&factor,&space_x); clc ; fixed_add(&distance,&factor,&space_y); ; 2 lda DISTANCE_F ; 3 adc FACTOR_F ; 3 sta SPACEY_F ; 3 sta SPACEX_F ; 3 lda DISTANCE_I ; 3 adc FACTOR_I ; 3 sta SPACEY_I ; 3 sta SPACEX_I ; 3 ;========== ; 26 ; temp.i=fixed_sin[(angle+4)&0xf].i; // cos() lda ANGLE ; 3 clc ; 2 adc #4 ; 2 and #$f ; 2 asl ; 2 tay ; 2 lda fixed_sin,Y ; 4 sta NUM2H ; store as source for next mul ; 3 ; temp.f=fixed_sin[(angle+4)&0xf].f; // cos() iny ; 2 lda fixed_sin,Y ; 4 sta NUM2L ; store as source for next mul ; 3 ;========== ; 29 ; mul5 ; fixed_mul(&space_x,&temp,&space_x); lda SPACEX_I ; 3 sta NUM1H ; 3 lda SPACEX_F ; 3 sta NUM1L ; 3 ; NUM2H:NUM2L already set above sec ; don't reuse previous NUM1 ; 2 jsr multiply ; 6 ; SPACEX_I in X ; ; SPACEX_F in A ; ;========== ; 20 ; fixed_add(&space_x,&cx,&space_x); clc ; 2 ; SPACEX_F still in A ; adc CX_F ; 3 sta SPACEX_F ; 3 txa ; SPACEX_I was in X ; 2 adc CX_I ; 3 sta SPACEX_I ; 3 ;=========== ; 16 ; temp.i=fixed_sin[angle&0xf].i; // sin() lda ANGLE ; 3 and #$f ; 2 asl ; 2 tay ; 2 lda fixed_sin,Y ; 4 sta NUM2H ; store for next mul ; 3 ; fixed_temp.f=fixed_sin[angle&0xf].f; // sin() iny ; 2 lda fixed_sin,Y ; 4 sta NUM2L ; store for next mul ; 3 ;========== ; 25 ;mul6 ; fixed_mul(&space_y,&fixed_temp,&space_y); lda SPACEY_I ; 3 sta NUM1H ; 3 lda SPACEY_F ; 3 sta NUM1L ; 3 ; NUM2H:NUM2L already set sec ; don't reuse previous num1 ; 2 jsr multiply ; 6 ; SPACEY_I in X ; ; SPACEY_F in A ; ;========== ; 20 ; fixed_add(&space_y,&cy,&space_y); clc ; 2 ; SPACEY_F in A adc CY_F ; 3 sta SPACEY_F ; 3 txa ; SPACEY_I in X ; 2 adc CY_I ; 3 sta SPACEY_I ; 3 ;========== ; 16 ; mul7 ; fixed_mul(&lowres_half,&dx,&temp); lda #CONST_LOWRES_HALF_I ; 2 sta NUM1H ; 3 lda #CONST_LOWRES_HALF_F ; 2 sta NUM1L ; 3 lda DX_I ; 3 sta NUM2H ; 3 sta dxi_label+1 ; for self modify ; 4 lda DX_F ; 3 sta dxf_label+1 ; for self modify ; 4 sta NUM2L ; 3 sec ; don't reuse previous num1 ; 2 jsr multiply ; 6 ; TEMP_I in X ; ; TEMP_F in A ; ;========== ; 38 ; fixed_add(&space_x,&temp,&space_x); clc ; 2 ; TEMP_F in A adc SPACEX_F ; 3 sta SPACEX_F ; 3 txa ; TEMP_I in X ; 2 adc SPACEX_I ; 3 sta SPACEX_I ; 3 ;========== ; 16 ;mul8 ; fixed_mul(&fixed_temp,&dy,&fixed_temp); lda DY_I ; 3 sta NUM2H ; 3 sta dyi_label+1 ; for self modify ; 4 lda DY_F ; 3 sta NUM2L ; 3 sta dyf_label+1 ; for self modify ; 4 clc ; reuse CONST_LOWRES_HALF from last time ; 2 jsr multiply ; 6 ; TEMP_I in X ; TEMP_F in A ;========== ; 28 ; fixed_add(&space_y,&temp,&space_y); clc ; 2 ; TEMP_F in A adc SPACEY_F ; 3 sta SPACEY_F ; 3 txa ; TEMP_I in X ; 2 adc SPACEY_I ; 3 sta SPACEY_I ; 3 ;========== ; 16 ldx #0 ; was SCREEN_X ; 2 ;========== ; 2 ;=================================================== ; SCREEN_X LOOP!!!! ; every cycle in here counts for 32*40=1280 cycles ;=================================================== screenx_loop: nomatch: ;==================================== ; do a full lookup, takes much longer ; used to be a separate function but we inlined it here ;==================== ; lookup_map ;==================== ; finds value in space_x.i,space_y.i ; returns color in A ; CLOBBERS: A,Y ; island is 8x8 ; map is 64x64 but anything not island is ocean lda SPACEX_I ; 3 sta spacex_label+1 ; self modifying code, LAST_SPACEX_I ; 4 and #CONST_MAP_MASK_X ; wrap at 64 ; 2 sta SPACEX_I ; store i patch out ; 3 tay ; copy to Y for later ; 2 lda SPACEY_I ; 3 sta spacey_label+1 ; self modifying code, LAST_SPACEY_I ; 4 and #CONST_MAP_MASK_Y ; wrap to 64x64 grid ; 2 sta SPACEY_I ; 3 asl ; 2 asl ; 2 asl ; multiply by 8 ; 2 clc ; 2 adc SPACEX_I ; add in X value ; 3 ; only valid if x<8 and y<8 ;============ ; 37 ; SPACEX_I is n y cpy #$8 ; 2 bcs ocean_color ; bge 8 ; 2nt/3 ldy SPACEY_I ; 3 cpy #$8 ; 2 bcs ocean_color ; bge 8 ; 2nt/3 ;============= ; ?? ;============== ; lookup island ; A is spacey<<3+spacex island_color: tay ; 2 lda flying_map,Y ; load from array ; 4 jmp update_cache ; 3 ;============ ; 11 ;============= ; lookup ocean ; A is spacey<<3+spacex ocean_color: and #$1f ; 2 tay ; 2 lda water_map,Y ; the color of the sea ; 4 ;=========== ; 8 ; store cached value ; note: this does seem faster than storing in zero page update_cache: sta map_color_label+1 ; self-modifying ; 4 ;=========== ; 4 match: mask_label: ; color is in A ; this is f0 or 0f depending on odd/even row and #0 ; COLOR_MASK (self modifying) ; 2 ; this is ora or bit depending on odd/even innersmc1: ora $400,X ; we're odd, or the bottom in ; 4 innersmc2: sta $400,X ; plot double height pixel ; 5 ;============ ; 11 ;=================================== ; incremement column, see if done inx ; increment SCREEN_X ; 2 cpx #40 ; 2 beq done_screenx_loop ; branch until we've done 40 ; 2nt/3 ;============= ; 6/7 ;======================================= ; advance to the next position in space ; fixed_add(&space_x,&dx,&space_x); ; state of carry here? cpx #40 should always be less than so cc? ; clc ; 2 lda SPACEX_F ; 3 dxf_label: adc #0 ; self modifying, is DX_F ; 2 sta SPACEX_F ; 3 lda SPACEX_I ; 3 dxi_label: adc #0 ; self modifying, is DX_I ; 2 sta SPACEX_I ; 3 tay ; save for later ; 2 ;========== ; 20 ; fixed_add(&space_y,&dy,&space_y); clc ; 2 lda SPACEY_F ; 3 dyf_label: adc #0 ; self modifyig, is DY_F ; 2 sta SPACEY_F ; 3 lda SPACEY_I ; 3 dyi_label: adc #0 ; self mofidying is DY_I ; 2 sta SPACEY_I ; 3 ;============ ; 18 ; cache color and return if same as last time ; SPACEY_I is in A from above spacey_label: cmp #0 ; self modify, LAST_SPACEY_I ; 2 bne nomatch ; 2nt/3 ; SPACEX_I is in Y spacex_label: cpy #0 ; self modify, LAST_SPACEX_I ; 2 bne nomatch ; 2nt/3 map_color_label: lda #0 ; self modify, LAST_MAP_COLOR ; 2 jmp match ; 3 ;============ ; ?? ;======================== ; get here at end of loop ;========================= done_screenx_loop: inc SCREEN_Y ; 5 lda SCREEN_Y ; 3 cmp #40 ; LOWRES height ; 2 beq done_screeny ; 2nt/3 jmp screeny_loop ; too far to branch ; 3 ;============= ; 15 done_screeny: rts ; 6 ;==================== ; lookup_map ;==================== ; finds value in space_x.i,space_y.i ; returns color in A ; CLOBBERS: A,Y ; this is used to check if above water or grass ; the high-performance per-pixel version has been inlined lookup_map: lda SPACEX_I ; 3 and #CONST_MAP_MASK_X ; 2 sta SPACEX_I ; 3 tay ; 2 lda SPACEY_I ; 3 and #CONST_MAP_MASK_Y ; wrap to 64x64 grid ; 2 sta SPACEY_I ; 3 asl ; 2 asl ; 2 asl ; multiply by 8 ; 2 clc ; 2 adc SPACEX_I ; add in X value ; 3 ; only valid if x<8 and y<8 ; SPACEX_I is in y cpy #$8 ; 2 ;============ ; 31 bcs ocean_color_outline ; bgt 8 ;^2nt/3 ldy SPACEY_I ; 3 cpy #$8 ; 2 bcs ocean_color_outline ; bgt 8 ; 2nt/3 tay ; 2 lda flying_map,Y ; load from array ; 4 bcc update_cache_outline ; 3 ocean_color_outline: and #$1f ; 2 tay ; 2 lda water_map,Y ; the color of the sea ; 4 update_cache_outline: rts ; 6 ;====================================== ; draw sky ;====================================== ; Only draw sky if necessary ; (at start, or if we have switched to text, we never overwrite it) draw_sky: ; 6 ; Draw Sky on both pages ; lines 0..6 lda #COLOR_BOTH_MEDIUMBLUE ; MEDIUMBLUE color ; 2 ldx #39 sky_loop: ; draw line across screen sta $400,X sta $480,X sta $500,X sta $800,X sta $880,X sta $900,X dex bpl sky_loop ; Draw Hazy Horizon lda #$56 ; Horizon is blue/grey ; 2 ldx #39 horizon_loop: ; draw line across screen sta $580,X sta $980,X dex bpl horizon_loop rts ;horizontal_lookup_20: ; .byte $0C,$0A,$09,$08,$07,$06,$05,$05,$04,$04,$04,$04,$03,$03,$03,$03 ; .byte $26,$20,$1B,$18,$15,$13,$11,$10,$0E,$0D,$0C,$0C,$0B,$0A,$0A,$09 ; .byte $40,$35,$2D,$28,$23,$20,$1D,$1A,$18,$16,$15,$14,$12,$11,$10,$10 ; .byte $59,$4A,$40,$38,$31,$2C,$28,$25,$22,$20,$1D,$1C,$1A,$18,$17,$16 ; .byte $73,$60,$52,$48,$40,$39,$34,$30,$2C,$29,$26,$24,$21,$20,$1E,$1C ; .byte $8C,$75,$64,$58,$4E,$46,$40,$3A,$36,$32,$2E,$2C,$29,$27,$25,$23 ; .byte $A6,$8A,$76,$68,$5C,$53,$4B,$45,$40,$3B,$37,$34,$30,$2E,$2B,$29 ; we can guarantee 4 cycle indexed reads if we page-align this ; it's 16*14 bytes .align 256 horizontal_lookup: .byte $0C,$0B,$0A,$09,$09,$08,$08,$07,$07,$06,$06,$06,$05,$05,$05,$05 .byte $04,$04,$04,$04,$04,$04,$04,$03,$03,$03,$03,$03,$03,$03,$03,$03 .byte $26,$22,$20,$1D,$1B,$19,$18,$16,$15,$14,$13,$12,$11,$10,$10,$0F .byte $0E,$0E,$0D,$0D,$0C,$0C,$0C,$0B,$0B,$0A,$0A,$0A,$0A,$09,$09,$09 .byte $40,$3A,$35,$31,$2D,$2A,$28,$25,$23,$21,$20,$1E,$1D,$1B,$1A,$19 .byte $18,$17,$16,$16,$15,$14,$14,$13,$12,$12,$11,$11,$10,$10,$10,$0F .byte $59,$51,$4A,$44,$40,$3B,$38,$34,$31,$2F,$2C,$2A,$28,$26,$25,$23 .byte $22,$21,$20,$1E,$1D,$1C,$1C,$1B,$1A,$19,$18,$18,$17,$16,$16,$15 .byte $73,$68,$60,$58,$52,$4C,$48,$43,$40,$3C,$39,$36,$34,$32,$30,$2E .byte $2C,$2A,$29,$27,$26,$25,$24,$22,$21,$20,$20,$1F,$1E,$1D,$1C,$1C .byte $8C,$80,$75,$6C,$64,$5D,$58,$52,$4E,$4A,$46,$43,$40,$3D,$3A,$38 .byte $36,$34,$32,$30,$2E,$2D,$2C,$2A,$29,$28,$27,$26,$25,$24,$23,$22 .byte $A6,$97,$8A,$80,$76,$6E,$68,$61,$5C,$57,$53,$4F,$4B,$48,$45,$42 .byte $40,$3D,$3B,$39,$37,$35,$34,$32,$30,$2F,$2E,$2C,$2B,$2A,$29,$28 ; 8.8 fixed point ; should we store as two arrays, one I one F? ; 32 bytes fixed_sin: .byte $00,$00 ; 0.000000=00.00 .byte $00,$61 ; 0.382683=00.61 .byte $00,$b5 ; 0.707107=00.b5 .byte $00,$ec ; 0.923880=00.ec .byte $01,$00 ; 1.000000=01.00 .byte $00,$ec ; 0.923880=00.ec .byte $00,$b5 ; 0.707107=00.b5 .byte $00,$61 ; 0.382683=00.61 .byte $00,$00 ; 0.000000=00.00 .byte $ff,$9f ; -0.382683=ff.9f .byte $ff,$4b ; -0.707107=ff.4b .byte $ff,$14 ; -0.923880=ff.14 .byte $ff,$00 ; -1.000000=ff.00 .byte $ff,$14 ; -0.923880=ff.14 .byte $ff,$4b ; -0.707107=ff.4b .byte $ff,$9f ; -0.382683=ff.9f ; 32 bytes fixed_sin_scale: .byte $00,$00 .byte $00,$0c .byte $00,$16 .byte $00,$1d .byte $00,$20 .byte $00,$1d .byte $00,$16 .byte $00,$0c .byte $00,$00 .byte $ff,$f4 .byte $ff,$ea .byte $ff,$e3 .byte $ff,$e0 .byte $ff,$e3 .byte $ff,$ea .byte $ff,$f4