diff --git a/duke/Makefile b/duke/Makefile index 5dccec73..6a7d2349 100644 --- a/duke/Makefile +++ b/duke/Makefile @@ -28,7 +28,8 @@ DUKE: duke.o ld65 -o DUKE duke.o -C ../linker_scripts/apple2_2000.inc duke.o: duke.s zp.inc hardware.inc duke.s duke.inc \ - status_bar.s + status_bar.s draw_duke.s gr_putsprite_crop.s \ + keyboard.s handle_laser.s ca65 -o duke.o duke.s -l duke.lst #### diff --git a/duke/draw_duke.s b/duke/draw_duke.s new file mode 100644 index 00000000..5781250b --- /dev/null +++ b/duke/draw_duke.s @@ -0,0 +1,48 @@ +move_duke: + rts + + + ; draw duke + +draw_duke: + + lda DUKE_X + sta XPOS + lda DUKE_Y + sta YPOS + + lda DUKE_DIRECTION + bmi duke_facing_left + + lda #duke_sprite_stand_right + jmp actually_draw_duke +duke_facing_left: + + lda #duke_sprite_stand_left + +actually_draw_duke: + sta INH + jsr put_sprite_crop + + rts + +duke_sprite_stand_right: + .byte 4,5 + .byte $AA,$dA,$dA,$AA + .byte $AA,$dd,$bb,$AA + .byte $AA,$b3,$7A,$7A + .byte $AA,$66,$6b,$AA + .byte $AA,$56,$65,$AA + +duke_sprite_stand_left: + .byte 4,5 + .byte $AA,$dA,$dA,$AA + .byte $AA,$bb,$dd,$AA + .byte $7A,$7A,$b3,$AA + .byte $AA,$6b,$66,$AA + .byte $AA,$65,$56,$AA + diff --git a/duke/duke.s b/duke/duke.s index d004ae06..c6b4f26f 100644 --- a/duke/duke.s +++ b/duke/duke.s @@ -18,15 +18,30 @@ duke_start: bit LORES bit TEXTGR + ;===================== + ; init vars + ;===================== + lda #0 sta ANIMATE_FRAME sta FRAMEL sta FRAMEH sta DISP_PAGE + sta JOYSTICK_ENABLED + sta DUKE_WALKING + sta DUKE_JUMPING + sta LEVEL_OVER + sta LASER_OUT lda #4 sta DRAW_PAGE + lda #18 + sta DUKE_X + lda #20 + sta DUKE_Y + lda #1 + sta DUKE_DIRECTION ;==================================== @@ -52,14 +67,31 @@ duke_loop: jsr gr_copy_to_current + ; draw laser + + jsr draw_laser + + ; draw duke + + jsr draw_duke + + ; draw a status bar + jsr draw_status_bar jsr page_flip + jsr handle_keypress + jsr move_duke - ; early escape if keypressed - lda KEYPRESS + jsr move_laser + + ;=========================== + ; check end of level + ;=========================== + + lda LEVEL_OVER bpl do_duke_loop jmp done_with_duke @@ -68,8 +100,8 @@ duke_loop: do_duke_loop: ; delay - lda #200 - jsr WAIT +; lda #200 +; jsr WAIT jmp duke_loop @@ -91,6 +123,14 @@ done_with_duke: ; .include "gr_fast_clear.s" .include "gr_copy.s" .include "gr_pageflip.s" + .include "gr_putsprite_crop.s" .include "decompress_fast_v2.s" .include "status_bar.s" + .include "keyboard.s" + .include "joystick.s" + + .include "draw_duke.s" + .include "handle_laser.s" + + diff --git a/duke/gr_putsprite_crop.s b/duke/gr_putsprite_crop.s new file mode 100644 index 00000000..72fd8ab2 --- /dev/null +++ b/duke/gr_putsprite_crop.s @@ -0,0 +1,359 @@ + ;============================================= + ; put_sprite_crop + ;============================================= + ; Sprite to display in INH,INL + ; Location is XPOS,YPOS + ; Note, only works if YPOS is multiple of two + + ; transparent color is $A (grey #2) + ; this means we can have black ($0) in a sprite + + ; FIXME: force YPOS to be even? + +put_sprite_crop: + + ldy #0 ; byte 0 is xsize ; 2 + lda (INL),Y ; 5 + sta CH ; xsize is in CH ; 3 + iny ; 2 + clc + adc XPOS + sta XMAX + + lda (INL),Y ; byte 1 is ysize ; 5 + sta CV ; ysize is in CV ; 3 + iny ; 2 + + lda YPOS ; make a copy of ypos ; 3 + sta TEMPY ; as we modify it ; 3 + ;=========== + ; 28 +put_sprite_crop_loop: + sty TEMP ; save sprite pointer ; 3 + ldy TEMPY ; 3 + + bpl put_sprite_crop_pos ; if < 0, skip to next + + clc ; skip line in sprite too + lda TEMP + adc CH + tay + + bne crop_increment_y + +put_sprite_crop_pos: + +psc_smc1: + cpy #48 ; bge if >= 48, done sprite + bcs crop_sprite_done + + + lda gr_offsets,Y ; lookup low-res memory address ; 4 + clc ; 2 + adc XPOS ; add in xpos ; 3 + sta OUTL ; store out low byte of addy ; 3 + clc ; never wraps, handle negative + lda gr_offsets+1,Y ; look up high byte ; 4 + adc DRAW_PAGE ; ; 3 + sta OUTH ; and store it out ; 3 + ldy TEMP ; restore sprite pointer ; 3 + + ; OUTH:OUTL now points at right place + + ldx XPOS ; load xposition into x ; 3 + ;=========== + ; 34 +crop_put_sprite_pixel: + lda (INL),Y ; get sprite colors ; 5 + iny ; increment sprite pointer ; 2 + sty TEMP ; save sprite pointer ; 3 + + + cpx #0 ; if off-screen left, skip draw + bmi skip_drawing + cpx #40 + bcs skip_drawing ; if off-screen right, skip draw + + ldy #$0 ; 2 + + ; check if completely transparent + ; if so, skip + + cmp #$aa ; if all zero, transparent ; 2 + beq crop_put_sprite_done_draw ; don't draw it ; 2nt/3 + ;============== + ; 16/17 + + sta COLOR ; save color for later ; 3 + + ; check if top pixel transparent + + and #$f0 ; check if top nibble zero ; 2 + cmp #$a0 + bne crop_put_sprite_bottom ; if not skip ahead ; 2nt/3 + ;============== + ; 7/8 + + lda COLOR + and #$0f + sta COLOR + + lda #$f0 ; setup mask ; 2 + sta MASK ; 3 + bmi crop_put_sprite_mask ; always? ; 3 + ;============= + ; 8 + +crop_put_sprite_bottom: + lda COLOR ; re-load color ; 3 + and #$0f ; check if bottom nibble zero ; 2 + cmp #$0a + bne crop_put_sprite_all ; if not, skip ahead ; 2nt/3 + ;============= + ; 7/8 + + lda COLOR + and #$f0 + sta COLOR + lda #$0f ; 2 + sta MASK ; setup mask ; 3 + ;=========== + ; 5 + +crop_put_sprite_mask: + lda (OUTL),Y ; get color at output ; 5 + and MASK ; mask off unneeded part ; 3 + ora COLOR ; or the color in ; 3 + sta (OUTL),Y ; store it back ; 6 + + jmp crop_put_sprite_done_draw ; we are done ; 3 + ;=========== + ; 20 + +crop_put_sprite_all: + lda COLOR ; load color ; 3 + sta (OUTL),Y ; and write it out ; 6 + ;============ + ; 9 + +crop_put_sprite_done_draw: +skip_drawing: + + ldy TEMP ; restore sprite pointer ; 3 + + inc OUTL ; increment output pointer ; 5 + inx ; increment x counter ; 2 + cpx XMAX + bne crop_put_sprite_pixel ; if not done, keep looping ; 2nt/3 + ;============== + ; 12/13 +crop_increment_y: + + inc TEMPY ; each line has two y vars ; 5 + inc TEMPY ; 5 + dec CV ; decemenet total y count ; 5 + bne put_sprite_crop_loop ; loop if not done ; 2nt/3 + ;============== + ; 17/18 +crop_sprite_done: + rts ; return ; 6 + + + +; 0,0 = 400+0 +; -1,0 = 400+ff=4ff, inc=400 + +; sprite: 5x4 +; +; -2,0 Xstart=0, sprite_offset=2, xsize=3 +; -1,0, Xstart=0, sprite_offset=1, xsize=4 +; 0,0, Xstrat=0, sprite_offset=0, xsize=5 +; 1,0, Xstart=1, sprite_offset=0, xsize=5 +; +; 39,0 Xstart=39, sprite_offset=0, xsize=1 +; +; +; +; + + +.if 0 + ;============================================= + ; put_sprite_flipped_crop + ;============================================= + ; Sprite to display in INH,INL + ; Location is XPOS,YPOS + ; Note, only works if YPOS is multiple of two + + ; transparent color is $A (grey #2) + ; this means we can have black ($0) in a sprite + + +put_sprite_flipped_crop: + + ldy #0 ; byte 0 is xsize ; 2 + lda (INL),Y ; 5 + sta CH ; xsize is in CH ; 3 + iny ; 2 + + + lda (INL),Y ; byte 1 is ysize ; 5 + sta CV ; ysize is in CV ; 3 + dey ; make Y zero again ; 2 + + lda INH ; ??? + sta ppfc_smc+2 + clc + lda INL + adc #1 ; add one (not two) because X counts + ; from CH to 1 (not CH-1 to 0) + sta ppfc_smc+1 + bcc psfc16 + inc ppfc_smc+2 +psfc16: + + + lda YPOS ; make a copy of ypos ; 3 + + sta TEMPY ; as we modify it ; 3 + ;=========== + ; 28 + + + +put_spritefc_loop: +; sty TEMP ; save sprite pointer ; 3 + ldy TEMPY ; 3 + + bmi fcrop_increment_y ; if < 0, skip to next +psc_smc2: + cpy #48 ; bge if >= 48, done sprite + bcs fcrop_sprite_done + + + lda gr_offsets,Y ; lookup low-res memory address ; 4 + clc ; 2 + adc XPOS ; add in xpos ; 3 + sta OUTL ; store out low byte of addy ; 3 + lda gr_offsets+1,Y ; look up high byte ; 4 + clc + adc DRAW_PAGE ; ; 3 + sta OUTH ; and store it out ; 3 +; ldy TEMP ; restore sprite pointer ; 3 + + ; OUTH:OUTL now points at right place + + ldx CH ; load xsize into x ; 3 + ;=========== + ; 34 +put_spritefc_pixel: + + clc + txa ; want (CH-X-1)+XPOS + eor #$ff + adc CH + adc XPOS + + bmi cskip_drawing + cmp #40 + + bcs cskip_drawing ; if off-screen right, skip draw + +ppfc_smc: + lda $C000,X ; get sprite colors ; 5 +; iny ; increment sprite pointer ; 2 +; sty TEMP ; save sprite pointer ; 3 + ldy #$0 ; 2 + + ; check if completely transparent + ; if so, skip + + cmp #$aa ; if all zero, transparent ; 2 + beq put_spritefc_done_draw ; don't draw it ; 2nt/3 + ;============== + ; 16/17 + + sta COLOR ; save color for later ; 3 + + ; check if top pixel transparent + + and #$f0 ; check if top nibble zero ; 2 + cmp #$a0 + bne put_spritefc_bottom ; if not skip ahead ; 2nt/3 + ;============== + ; 7/8 + + lda COLOR + and #$0f + sta COLOR + + lda #$f0 ; setup mask ; 2 + sta MASK ; 3 + bmi put_spritefc_mask ; always? ; 3 + ;============= + ; 8 + +put_spritefc_bottom: + lda COLOR ; re-load color ; 3 + and #$0f ; check if bottom nibble zero ; 2 + cmp #$0a + bne put_spritefc_all ; if not, skip ahead ; 2nt/3 + ;============= + ; 7/8 + + lda COLOR + and #$f0 + sta COLOR + lda #$0f ; 2 + sta MASK ; setup mask ; 3 + ;=========== + ; 5 + +put_spritefc_mask: + lda (OUTL),Y ; get color at output ; 5 + and MASK ; mask off unneeded part ; 3 + ora COLOR ; or the color in ; 3 + sta (OUTL),Y ; store it back ; 6 + + jmp put_spritefc_done_draw ; we are done ; 3 + ;=========== + ; 20 + +put_spritefc_all: + lda COLOR ; load color ; 3 + sta (OUTL),Y ; and write it out ; 6 + ;============ + ; 9 + +put_spritefc_done_draw: +cskip_drawing: +; ldy TEMP ; restore sprite pointer ; 3 + + inc OUTL ; increment output pointer ; 5 + dex ; decrement x counter ; 2 + bne put_spritefc_pixel ; if not done, keep looping ; 2nt/3 + ;============== + ; 12/13 +fcrop_increment_y: + inc TEMPY ; each line has two y vars ; 5 + inc TEMPY ; 5 + + lda CH + clc + adc ppfc_smc+1 + sta ppfc_smc+1 + bcc psfco + inc ppfc_smc+2 +psfco: + + dec CV ; decemenet total y count ; 5 + beq fcrop_sprite_done ; loop if not done ; 2nt/3 + jmp put_spritefc_loop + ;============== + ; 17/18 + +fcrop_sprite_done: + rts ; return ; 6 + + +.endif diff --git a/duke/handle_laser.s b/duke/handle_laser.s new file mode 100644 index 00000000..71eff3e5 --- /dev/null +++ b/duke/handle_laser.s @@ -0,0 +1,60 @@ + ; draw/move laser + + + ;==================== + ; move laser + ;==================== +move_laser: + lda LASER_OUT + beq done_move_laser + + lda LASER_X + clc + adc LASER_DIRECTION + sta LASER_X + + cmp #33 + bcc not_too_far_right + lda #0 + sta LASER_OUT + beq done_move_laser + +not_too_far_right: + cmp #6 + bcs done_move_laser + lda #0 + sta LASER_OUT + +done_move_laser: + rts + + ;==================== + ; draw laser + ;==================== + +draw_laser: + + lda LASER_OUT + beq done_draw_laser + + lda LASER_X + sta XPOS + lda LASER_Y + sta YPOS + +; lda LASER_DIRECTION + + + lda #laser_sideways_sprite + sta INH + jsr put_sprite_crop + +done_draw_laser: + rts + +laser_sideways_sprite: + .byte 4,1 + .byte $3A,$cA,$3A,$cA + diff --git a/duke/joystick.s b/duke/joystick.s new file mode 100644 index 00000000..751fd039 --- /dev/null +++ b/duke/joystick.s @@ -0,0 +1,46 @@ +; Oliver Schmidt +; comp.sys.apple2.programmer + +; Call with joystick number (0 or 1) in A. +; Results are stored in value0 and value1. +; UPPER_THRESHOLD is the paddle value you want to consider as "right enough" / +; "down enough". + +UPPER_THRESHOLD = 128 + +;PTRIG = $c070 +PADDL1 = $C065 + +handle_joystick: + lda #0 + + ; Read both paddles simultaneously + asl ; Joystick number -> paddle number + tax + ldy #$00 + sty value0 + sty value1 + lda PTRIG ; Trigger paddles +loop: lda PADDL0,x ; Read paddle (0 or 2) + bmi set0 ; Cycles: 2 3 + nop ; Cycles: 2 + bpl nop0 ; Cycles: 3 +set0: sty value0 ; Cycles: 4 +nop0: ; - - + ; Cycles: 7 7 + lda PADDL1,x ; Read paddle (1 or 3) + bmi set1 ; Cycles: 2 3 + nop ; Cycles: 2 + bpl nop1 ; Cycles: 3 +set1: sty value1 ; Cycles: 4 +nop1: ; - - + ; Cycles: 7 7 + iny + cpy #UPPER_THRESHOLD+1 + bne loop + + rts + +value0: .byte $00 +value1: .byte $00 + diff --git a/duke/keyboard.s b/duke/keyboard.s new file mode 100644 index 00000000..b4862014 --- /dev/null +++ b/duke/keyboard.s @@ -0,0 +1,230 @@ + + ;============================== + ; Handle Keypress + ;============================== +handle_keypress: + + ; first handle joystick + lda JOYSTICK_ENABLED + beq actually_handle_keypress + + ; only check joystick every-other frame + lda FRAMEL + and #$1 + beq actually_handle_keypress + +check_button: + lda PADDLE_BUTTON0 + bpl button_clear + + lda JS_BUTTON_STATE + bne js_check + + lda #1 + sta JS_BUTTON_STATE + lda #' ' + jmp check_sound + +button_clear: + lda #0 + sta JS_BUTTON_STATE + +js_check: + jsr handle_joystick + +js_check_left: + lda value0 + cmp #$20 + bcs js_check_right ; if less than 32, left + lda #'A' + bne check_sound + +js_check_right: + cmp #$40 + bcc js_check_up + lda #'D' + bne check_sound + +js_check_up: + lda value1 + cmp #$20 + bcs js_check_down + lda #'W' + + bne check_sound + +js_check_down: + cmp #$40 + bcc done_joystick + lda #'S' + bne check_sound + + +done_joystick: + + + +actually_handle_keypress: + lda KEYPRESS + bmi keypress + + jmp no_keypress + +keypress: + and #$7f ; clear high bit + cmp #' ' + beq check_sound ; make sure not to lose space + and #$df ; convert uppercase to lower case + +check_sound: + cmp #$14 ; control-T + bne check_joystick + + lda SOUND_STATUS + eor #SOUND_DISABLED + sta SOUND_STATUS + jmp done_keypress + + ; can't be ^J as that's the same as down +check_joystick: + cmp #'J' ; J + bne check_left + + lda JOYSTICK_ENABLED + eor #1 + sta JOYSTICK_ENABLED + jmp done_keypress + +check_left: + cmp #'A' + beq left_pressed + cmp #8 ; left key + bne check_right +left_pressed: + + lda DUKE_DIRECTION + cmp #$ff ; check if facing left + bne face_left + + lda #1 + sta DUKE_WALKING + jmp done_left_pressed + +face_left: + lda #$ff + sta DUKE_DIRECTION + lda #0 + sta DUKE_WALKING + +done_left_pressed: + jmp done_keypress + +check_right: + cmp #'D' + beq right_pressed + cmp #$15 ; right key + bne check_up +right_pressed: + lda DUKE_DIRECTION + cmp #$1 ; check if facing right + bne face_right + + lda #1 + sta DUKE_WALKING + jmp done_left_pressed + +face_right: + lda #$1 + sta DUKE_DIRECTION + lda #0 + sta DUKE_WALKING + +done_right_pressed: + jmp done_keypress + +check_up: + cmp #'W' + beq up_pressed + cmp #$0B ; up key + bne check_down +up_pressed: + lda CURSOR_Y ; if 49