;--------------------------------------- ; based on 1bir - 1 block interactive raycaster for Commodore 64 ; coded by huseyin kilic (wisdom) copyright (c) 2009-2013 crescent ; ; converted to Apple II by Vince `deater` Weaver .include "hardware.inc" ; zero page V2 = $2D COLOR = $30 RAYPOSX = $61 RAYPOSXH = $62 RAYPOSY = $63 RAYPOSYH = $64 STEPX = $66 ; leave 1 byte between x and y STEPY = $68 PLAYERX = $69 PLAYERXH = $6a PLAYERY = $6b PLAYERYH = $6c DISTANCE = $70 NEWLOC = $71 ROWPTR = $d1 ROWPTRH = $d2 LINEH_T = $d9 WALL_HEIGHT = $f8 FLOOR_SKY_HEIGHT= $f9 ; external value dependencies HEADING = $81 SINADD = $9a COLORS = $b1 ; 3 bytes consecutively ; constants sin_t = $1000 blocksize = $28 ;--------------------------------------- ; main ;--------------------------------------- main: jsr SETGR bit FULLGR lda #$20 sta HEADING ;--------------------------------------- ; sin/cos table generator ;--------------------------------------- ; first generate sine for 0..63 (0..90 degrees) lda #3 sta SINADD lda #$00 tay gensin_loop: sta sin_t,Y iny clc adc SINADD ldx SINADD dec sincount_t,X bne gensin_loop dec SINADD bpl gensin_loop ; x = $00 ; y = $40 ; next generate gensin_loop2: lda sin_t,X sta sin_t+$0100,X ; copy at $100 so cosine easier sta sin_t-1+$40,Y ; store 90-180 degrees eor #$ff ; invert sta sin_t+$80,X ; store for 180-270 degrees sta sin_t-1+$c0,Y ; store for 270-360 degrees inx dey bpl gensin_loop2 ;--------------------------------------- ; raycaster ;--------------------------------------- loop_main: ; cast 40 rays for each screen column ; starting with rightmost one ; Y is used as global column index ; throughout the rest of the program ldy #39 loop_ray: ; determine current ray's direction ; by taking player's current direction ; and fov into account ; fov is 40 b-rads out of 256 b-rads tya clc adc HEADING ;sec sbc #19+1 ; half of the fov (+1 because of sec) tax ; get sin/cos values accordingly ; and copy player position to current ray position ; distance is reset on return from this call jsr getsincos_copyplr2ray ; reset line row before each column gets drawn ; (needed in vertical line section) ; X is 0 here? stx DISTANCE loop_dist: ; step along current ray's path and find distance inc DISTANCE ; limit distance when it is needed in larger maps ; or open (wrapped) maps ; max distance = $29 ; lda DISTANCE ; cmp #$29 ; bcs skip_dist ; max distance = $40 (make sure ar is always 0 here) ; bit DISTANCE ; bvs skip_dist ; max DISTANCE = $80 bmi skip_dist jsr addsteptopos ; on return from last call, A is cell (block) value ; A == 0 means empty cell, so continue tracing beq loop_dist skip_dist: ; now A contains the value in reached map position ; (or last cell value fetched if max distance is reached) ; use A or X to colorize the block ; and #$07 ; ora #$03 sta COLORS+1 ; find out visible block height ; according to distance ldx #$ff ; calculate visible block height through simple division lda # 24? bcc vline_validheight lda #23 ; make sure max height = 24 vline_validheight: asl ; calculate full height sta WALL_HEIGHT ; store for looping below eor #$ff ; subtract full height from screen height ; sec ; (48 rows) adc #48+1 ; +1 because of sec lsr ; sky/floor heights are equal to each other sta FLOOR_SKY_HEIGHT ; loop through 3 sections of one screen column ; i.e. sky - wall - floor vline_loop: ;========== ; vline sky, 0 to FLOOR_SKY_HEIGHT ; load color ;lda #$77 ; sky blue lda #$00 ; sky black sta COLOR lda FLOOR_SKY_HEIGHT sta V2 lda #0 jsr VLINE ; VLINE A,$2D at Y (Y preserved, A=V2) ;================= ; vline wall, FLOOR_SKY_HEIGHT to FLOOR_SKY_HEIGHT+WALL_HEIGHT ldx COLORS+1 stx COLOR ; A already FLOOR_SKY_HEIGHT clc adc WALL_HEIGHT sta V2 lda FLOOR_SKY_HEIGHT jsr VLINE ; VLINE A,$2D at Y ;============= ; vline floor, WALL_HEIGHT+FLOOR_SKY_HEIGHT to 47 ldx #$88 stx COLOR ; A already WALL_HEIGHT+FLOOR_SKY_HEIGHT ldx #47 stx V2 jsr VLINE ; VLINE A,$2D at Y ;--------------------------------------- ; advance to next ray/column dey bpl loop_ray ;--------------------------------------- ; user input ;--------------------------------------- ; common preparation code to set up sin/cos and ; to copy player position to ray position to trace movement ; direction to determine if player hits a block ; in case player actually tries to move forward or backwards ldx HEADING jsr getsincos_copyplr2ray ; get joystick 2 status (lowest 4 bits) ; and check each bit to determine action lda KEYPRESS beq done_user_input cmp #'W'+$80 bne skip_j1 ; try to move forward pha jsr stepandcopy pla skip_j1: cmp #'S'+$80 bne skip_j2 ; try to move backward pha jsr invertstepandcopy pla skip_j2: cmp #'A'+$80 bne skip_j3 ; turn right dec HEADING dec HEADING skip_j3: cmp #'D'+$80 bne done_user_input ; turn left inc HEADING inc HEADING done_user_input: bit KEYRESET ; clear keyboard buffer ; absolute jump, as carry is always 0 here jmp loop_main ;--------------------------------------- ; ray tracing subroutines ;--------------------------------------- ; heart of tracing, very slow, because of looping ; for x and y components and also because of ; brute force approach addsteptopos: ldx #$0 stx NEWLOC ldx #$02 loop_stepadd: lda STEPX,X ; & y ora #$7f ; sign extend 8 bit step value to 16 bit bmi was_neg ; was negative lda #$00 was_neg: pha ;clc lda STEPX,X ; & y adc RAYPOSX,X ; & y sta RAYPOSX,X ; & y pla php bcc blah ; no carry cpx #2 bne blah stx NEWLOC blah: plp adc RAYPOSXH,X ; & y sta RAYPOSXH,X ; & y dex dex bpl loop_stepadd ; A = RAYPOSXH ; calculate index to look up the map cell ; the map area is 8x8 bytes ; + instead of the usual y * 8 + x ; x * 8 + y done here, to save some bytes ; (just causing a flip of the map as a side effect) asl asl asl asl adc RAYPOSYH tax lda map_t,X beq step_exit ldx NEWLOC cpx #2 bne step_exit sec sbc #$88 step_exit: rts ;--------------------------------------- ; getsincos_copyplr2ray ;--------------------------------------- getsincos_copyplr2ray: lda sin_t,X ; sin(x) sta STEPX lda sin_t+$40,X ; cos(x) sta STEPY ; copy player position to ray position for a start ; through the basic rom ; copy 4 bytes, from 69,6A,6B,6C to 61,62,63,64 copyplr2ray: ldx #$04 copyloop: lda $68,X sta $60,X dex bne copyloop rts ;====================================== ; invert step and copy ;====================================== invertstepandcopy: ; invert step variables for backward motion invertstepx: ; from BFB8 in C64 ROM lda $66 eor #$ff sta $66 invertstepy: lda STEPY eor #$ff sta STEPY ;======================================= ; stepandcopy ;======================================= stepandcopy: ; see if player can move to the direction desired jsr addsteptopos bne step_exit ; no, return without doing anything ; yes, move player by ; copying ray position to player position copyray2plr: ; Copy 61,62,63,64 to 69,6A,6B,6C ldx #$4 r2_loop: lda $60,X sta $68,X dex bne r2_loop rts ;--------------------------------------- ; data ;--------------------------------------- ; number of sin additions (backwards) sincount_t: .byte 6,14,19,25 ;--------------------------------------- .if 0 map_t: .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff .byte $55,$00,$00,$00,$00,$00,$00,$55 .byte $55,$00,$44,$00,$00,$00,$00,$55 .byte $55,$00,$00,$00,$00,$11,$00,$55 .byte $55,$22,$00,$00,$00,$00,$00,$55 .byte $55,$00,$00,$99,$00,$00,$00,$55 .byte $55,$00,$00,$00,$00,$00,$00,$55 .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff .endif map_t: .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$00,$99,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$CC,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$CC,$CC,$CC,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$CC,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$00,$99,$99,$11,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$00,$EE,$00,$DD,$00,$CC,$00,$BB,$00,$AA,$00,$99,$00,$00,$ff .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff