CPC-POWER :
CPC-SOFTS
(Base de données) -
CPCArchives
(Articles) -
NGServers
(Hébergement)
Donations 2026 : 410.00 €
Accueil
20154 PROGRAMMES
PROTECTIONS
Dumpeurs
10119 commentaires
Liens
Contact
Donations
FORUM
Session end : 12h00m00s
Hide picture Sexe : OFF
20111
© _Public_Domain_ (
2026
)
 
Return Home
NOTICE / MANUAL
TXT (1)
NOTICE TEXTE n° 1 (59.62 Ko)
;;============================================================================ ;; RETURN HOME - Amstrad CPC Z80 Space Shooter (Mode 0) ;; FIRMWARE-LESS, HARDWARE DOUBLE-BUFFERED engine. ;; ;; You head home to the LEFT: ship sits on the RIGHT facing LEFT, firing LEFT. ;; Enemies pour in from the LEFT moving right. 3-layer parallax starfield ;; scrolls right. Two screen buffers (&C000 / &8000) flipped each frame via ;; the CRTC for flicker-free display. ;; ;; Controls: Q / Cursor-Up = Up ;; A / Cursor-Down = Down ;; Space / Joy-Fire = Shoot ;; ;; Boot uses the firmware ONLY to set mode 0, the palette, and to copy the ;; character font into RAM. After that interrupts are disabled and the program ;; drives the hardware directly (CRTC display base, VSYNC, keyboard matrix). ;; ;; Assemble with the JavaCPC / Maxam assembler. ORG &4000 , RUN &4000 ;;============================================================================ ;;---------------------------------------------------------------------------- ;; Firmware entry points (used at boot only) ;;---------------------------------------------------------------------------- SCR_SET_MODE equ &BC0E SCR_SET_INK equ &BC32 SCR_SET_BORDER equ &BC38 ;;---------------------------------------------------------------------------- ;; Hardware ;;---------------------------------------------------------------------------- ;; CRTC: register-select port &BCxx, data port &BDxx ;; Gate-Array: port &7Fxx ;; PPI: port A=&F4xx, port B=&F5xx, port C=&F6xx, control=&F7xx BUF0 equ &C000 ; screen buffer 0 (R12 = &30) BUF1 equ &8000 ; screen buffer 1 (R12 = &20) R12_BUF0 equ &30 R12_BUF1 equ &20 ;;---------------------------------------------------------------------------- ;; Screen / layout ;;---------------------------------------------------------------------------- SCR_W equ 80 ; bytes per scanline (Mode 0) PL_W equ 8 ; player sprite width (bytes) = 16 px PL_H equ 8 EN_W equ 4 ; enemy width (bytes) = 8 px EN_H equ 8 BL_W equ 2 ; bullet width (bytes) = 4 px BL_H equ 2 EX_W equ 4 ; explosion 8 px EX_H equ 8 MAX_ENE equ 10 MAX_BUL equ 4 MAX_EXP equ 10 N_STARS equ 36 ; 12 per parallax layer WAVE_SIZE equ 5 ; enemies per wave PL_XSTART equ 64 PL_XMIN equ 22 PL_XMAX equ 72 PL_YMIN equ 22 PL_YMAX equ 150 ; within enemy reach (no safe camping spot) PL_YSTART equ 80 PL_SPEED equ 3 BL_SPEED equ 6 ; bullets fly LEFT EN_ESCAPE equ 76 ; enemy reaching here (right edge) is gone PLAY_TOP equ 22 PLAY_BOT equ 170 ; enemies/stars stay above the terrain TERR_TOP equ 188 ; scrolling terrain band: y 188..199 TERR_NT equ 12 ; terrain band height in scanlines FIRE_DELAY equ 6 N_LEVELS equ 5 ;; Enemy struct (13 bytes): logical + sine state + per-buffer draw shadow EN_X equ 0 EN_Y equ 1 ; current (computed) y EN_ACTIVE equ 2 EN_TYPE equ 3 EN_SPEED equ 4 EN_BASEY equ 5 ; centre of vertical oscillation EN_PHASE equ 6 ; sine phase (0..31) EN_SHADOW equ 7 ; OX0,OY0,OA0,OX1,OY1,OA1 EN_SZ equ 13 ;; Bullet struct (9 bytes) BL_X equ 0 BL_Y equ 1 BL_ACTIVE equ 2 BL_SHADOW equ 3 ; OX0,OY0,OA0,OX1,OY1,OA1 BL_SZ equ 9 ;; Explosion struct (9 bytes) EX_X equ 0 EX_Y equ 1 EX_TIMER equ 2 ; counts down; 0 = inactive EX_SHADOW equ 3 ; OX0,OY0,OA0,OX1,OY1,OA1 EX_SZ equ 9 ;; Star struct (3 bytes) - deterministic motion, old position is computed ST_X equ 0 ST_Y equ 1 ST_SPD equ 2 STAR_SZ equ 3 ;; Keyboard matrix key positions (line, bit) - pressed = bit CLEAR ;; line0 b0 Cursor-Up b2 Cursor-Down ;; line5 b7 Space ;; line8 b3 Q b5 A ;; line9 b0 Joy-Up b1 Joy-Down b4 Joy-Fire2 b5 Joy-Fire1 ;;============================================================================ org &4000 start: di ld sp,stack_end call build_tables call init_sprites call sound_init call set_text_pen_white jp show_title ;;============================================================================ ;; GATE ARRAY palette (firmware-less; hardware colour codes from garray docs) ;;============================================================================ ;; A = value to send to the Gate Array (&7F) ga_out: ld bc,&7F00 out (c),a ret ;; program the 12 game pens + border (Mode 0) directly via the Gate Array set_game_palette: ld hl,pal_game ld d,0 ; pen number sgp_loop: ld bc,&7F00 ld a,d out (c),a ; select pen d ld a,(hl) out (c),a ; set its hardware colour inc hl inc d ld a,d cp 12 jr nz,sgp_loop ld bc,&7F00 ld a,&10 out (c),a ; select border ld a,&54 ; black out (c),a ret ;; program the 4 title pens + border (Mode 1) via the Gate Array set_title_palette: ld hl,pal_title ld d,0 stp_loop: ld bc,&7F00 ld a,d out (c),a ld a,(hl) out (c),a inc hl inc d ld a,d cp 4 jr nz,stp_loop ld bc,&7F00 ld a,&10 out (c),a ld a,&54 ; black border (raster bars override it per line) out (c),a ret ;;============================================================================ ;; BUILD SCANLINE TABLES for both buffers ;; tbl_c -> BUF0 (&C000), tbl_8 -> BUF1 (&8000) = tbl_c - &4000 ;;============================================================================ build_tables: ld ix,tbl_c ld hl,BUF0 ld b,200 bt_loop: ld (ix+0),l ld (ix+1),h inc ix inc ix ld de,&800 add hl,de ; next scanline within char block ld a,h and &38 jr nz,bt_nowrap ld de,&C050 ; (-&4000 + &50) mod 65536 : next char row add hl,de bt_nowrap: djnz bt_loop ;; derive tbl_8 = tbl_c - &4000 ld ix,tbl_c ld iy,tbl_8 ld b,200 bt2_loop: ld l,(ix+0) ld h,(ix+1) ld de,&C000 ; -&4000 mod 65536 add hl,de ld (iy+0),l ld (iy+1),h inc ix inc ix inc iy inc iy djnz bt2_loop ret ;;============================================================================ ;; CLEAR BOTH BUFFERS to pen 0 ;;============================================================================ clear_buffers: ld hl,BUF1 ld de,BUF1+1 ld bc,&3FFF ld (hl),0 ldir ld hl,BUF0 ld de,BUF0+1 ld bc,&3FFF ld (hl),0 ldir ret ;;============================================================================ ;; SET CRTC R12 (display base page) A = R12 value ;;============================================================================ set_r12: ld bc,&BC0C ; select register 12 out (c),c ld b,&BD ld c,a out (c),c ; write value ret ;;============================================================================ ;; WAIT FOR VSYNC (PPI port B bit 0) ;;============================================================================ wait_vsync: ld b,&F5 wv_loop: in a,(c) rra ; bit0 -> carry jr nc,wv_loop ; wait until VSYNC active ret ;;============================================================================ ;; READ KEYBOARD MATRIX into key_buf[0..9] (pressed = bit clear) ;; Standard PPI/PSG sequence. ;;============================================================================ read_keys: ld bc,&F782 ; PPI control: Port A output out (c),c ld bc,&F40E ; Port A = 14 (PSG register index of port A) out (c),c ld bc,&F6C0 ; Port C: PSG "select register" (bits7,6=11) out (c),c ld bc,&F600 ; Port C: inactive (bits7,6=00) out (c),c ld bc,&F792 ; PPI control: Port A input out (c),c ld hl,key_buf ld a,&40 ; Port C value: read (bit6=1) + line 0 rk_loop: ld b,&F6 ld c,a out (c),c ; select matrix line + PSG read ld b,&F4 in e,(c) ; read Port A = line data ld (hl),e inc hl inc a cp &4A ; &40 + 10 lines done jr nz,rk_loop ld bc,&F600 ; Port C inactive out (c),c ld bc,&F782 ; Port A back to output out (c),c ret ;;============================================================================ ;; TEXT - render using the copied font, opaque (set bit = text pen, else pen0) ;;============================================================================ ;; SET TEXT PEN: A = pen -> builds text_pp[4] pair table set_text_pen_white: ld a,6 set_text_pen: push af call lookup_left ; A = m0_left[pen] ld (text_pp+2),a ld d,a pop af call lookup_right ; A = m0_right[pen] ld (text_pp+1),a or d ld (text_pp+3),a xor a ld (text_pp+0),a ret ;; PP LOOKUP: A = 0..3 -> A = text_pp[A]. Preserves everything else. pp_lookup: push hl ld hl,text_pp add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) pop hl ret ;; PUTC: A=char, B=col(0-19), C=row(0-24) -> draw into draw_tbl buffer my_putc: sub 32 ld l,a ld h,0 add hl,hl add hl,hl add hl,hl ; *8 ld de,font_tbl add hl,de push hl pop ix ; IX = glyph ld a,b add a,a add a,a ; col*4 = X byte ld (mpc_x),a ld a,c add a,a add a,a add a,a ; row*8 = Y line ld (mpc_y),a ld b,8 mpc_loop: push bc ld a,(mpc_x) ld h,a ld a,(mpc_y) ld l,a call get_scr_addr ; HL = screen addr ld a,(ix+0) ld e,a ; glyph row bits rlca rlca and 3 call pp_lookup ld (hl),a inc hl ld a,e rlca rlca rlca rlca and 3 call pp_lookup ld (hl),a inc hl ld a,e rrca rrca and 3 call pp_lookup ld (hl),a inc hl ld a,e and 3 call pp_lookup ld (hl),a inc ix ld a,(mpc_y) inc a ld (mpc_y),a pop bc djnz mpc_loop ret ;; PRINT: HL=string(0-term), B=col, C=row my_print: ld a,(hl) or a ret z push hl push bc call my_putc pop bc pop hl inc hl inc b ; next column jr my_print ;;============================================================================ ;; GET SCREEN ADDRESS (uses draw_tbl = current back-buffer table) ;; IN: H = x byte, L = y scanline. OUT: HL = address. BC,DE preserved. ;;============================================================================ get_scr_addr: push bc push de ld c,h ld h,0 add hl,hl ld de,(draw_tbl) add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a ld b,0 add hl,bc pop de pop bc ret ;; A = pen (0-15) -> A = Mode 0 LEFT pixel bits. Preserves all but A,F. lookup_left: push hl ld hl,m0_left add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) pop hl ret ;; A = pen -> A = Mode 0 RIGHT pixel bits. lookup_right: push hl ld hl,m0_right add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) pop hl ret ;;============================================================================ ;; SOUND (direct PSG access via PPI) ;;============================================================================ ;; write PSG register: D = register, E = value psg_write: ld bc,&F782 ; PPI control: Port A output out (c),c ld b,&F4 out (c),d ; Port A = register number ld bc,&F6C0 ; Port C: PSG select-register out (c),c ld bc,&F600 ; inactive out (c),c ld b,&F4 out (c),e ; Port A = data ld bc,&F680 ; Port C: PSG write out (c),c ld bc,&F600 ; inactive out (c),c ret sound_init: ld d,7 ld e,&2E ; mixer: tone A on, noise B on, AY port A input call psg_write ld d,8 ld e,0 call psg_write ld d,9 ld e,0 call psg_write ld d,6 ld e,8 ; noise period call psg_write ret sound_fire: ld d,0 ld e,&80 call psg_write ; tone A period low ld d,1 ld e,0 call psg_write ; tone A period high (pitch) ld d,8 ld e,13 call psg_write ; volume A ld a,4 ld (snd_a_t),a ret sound_expl: ld d,9 ld e,15 call psg_write ; channel B noise, full volume ld a,12 ld (snd_b_t),a ret sound_update: ld a,(snd_a_t) or a jr z,su_b dec a ld (snd_a_t),a jr nz,su_b ld d,8 ld e,0 call psg_write ; mute fire su_b: ld a,(snd_b_t) or a jr z,su_done dec a ld (snd_b_t),a ld d,9 ld e,a ; decay noise volume call psg_write su_done: ret ;; silence both channels (also stops any decaying SFX) sound_silence: ld d,8 ld e,0 call psg_write ld d,9 ld e,0 call psg_write xor a ld (snd_a_t),a ld (snd_b_t),a ret ;; play a jingle on channel A. HL = (period_lo, period_hi, frames)*, frames 0 ends play_tune: pt_next: ld a,(hl) inc hl ld (note_lo),a ld a,(hl) inc hl ld (note_hi),a ld a,(hl) inc hl or a jr z,pt_done ld (note_dur),a push hl ld d,0 ld a,(note_lo) ld e,a call psg_write ; R0 tone A low ld d,1 ld a,(note_hi) ld e,a call psg_write ; R1 tone A high ld d,8 ld e,13 call psg_write ; R8 volume A ld a,(note_dur) ld b,a pt_wait: push bc call wait_vsync pop bc djnz pt_wait pop hl jr pt_next pt_done: call sound_silence ret ;;============================================================================ ;; TERRAIN (random scrolling green hills) ;; A 128-column random-walk height map -> terr_bmp (128 x TERR_NT). Each frame ;; an 80-byte window at scroll_off (wrapping at 128) is copied into the band. ;;============================================================================ ;; random-walk height map (128 columns) gen_terr_heights: ld ix,th_rand ld c,6 ; current height ld b,128 gth_loop: call rng and 3 add a,c sub 1 ; step -1..+2 cp 3 jr nc,gth_lo ld a,3 gth_lo: cp TERR_NT-1 jr c,gth_hi ld a,TERR_NT-2 gth_hi: ld c,a ld (ix+0),a inc ix djnz gth_loop ret ;; build the bitmap from the height map build_terr_bmp: ld hl,terr_bmp ld c,0 ; row btb_row: ld ix,th_rand ld b,128 ; columns btb_col: ld a,(ix+0) add a,c ; row + height cp TERR_NT ld a,0 jr c,btb_sky ld a,&30 ; pen 4 green btb_sky: ld (hl),a inc hl inc ix djnz btb_col inc c ld a,c cp TERR_NT jr nz,btb_row ret ;; scroll + blit the band into the current (back) buffer (window wraps at 128) draw_terrain_scroll: ld a,(scroll_off) dec a and 127 ld (scroll_off),a call regen_terr_col ; fresh random column scrolls in -> never repeats xor a ld (dts_r),a dts_row: call dts_srcbase ; HL = terr_bmp + dts_r*128 push hl ld a,(dts_r) add a,TERR_TOP ld l,a ld h,0 call get_scr_addr ; HL = dst ld d,h ld e,l ; DE = dst pop hl ; HL = src base ld a,(scroll_off) ld c,a ld b,0 add hl,bc ; HL = src + off ld a,128 sub c ; n1 = 128 - off cp 80 jr c,dts_wrap ld bc,80 ldir jr dts_next dts_wrap: ld c,a ; n1 (<80) ld b,0 ld a,80 sub c ; rem = 80 - n1 push af ldir ; copy n1 (src+off..end -> dst) call dts_srcbase ; HL = src base again pop af ld c,a ld b,0 ldir ; copy rem from src base dts_next: ld a,(dts_r) inc a ld (dts_r),a cp TERR_NT jr nz,dts_row ret ;; HL = terr_bmp + dts_r*128 dts_srcbase: ld a,(dts_r) ld l,a ld h,0 add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl ; *128 ld bc,terr_bmp add hl,bc ret ;; regenerate the column scrolling in (= scroll_off): random-walk height from ;; its right neighbour, store in th_rand and re-render that column of terr_bmp. regen_terr_col: ld a,(scroll_off) inc a and 127 ld e,a ld d,0 ld hl,th_rand add hl,de ld c,(hl) ; height of right neighbour call rng and 3 add a,c sub 1 ; step -1..+2 cp 3 jr nc,rtc_lo ld a,3 rtc_lo: cp TERR_NT-1 jr c,rtc_hi ld a,TERR_NT-2 rtc_hi: ld c,a ; new height ld a,(scroll_off) ld e,a ld d,0 ld hl,th_rand add hl,de ld (hl),c ; store new height ld hl,terr_bmp add hl,de ; HL = terr_bmp column (row 0) ld de,128 ; row stride ;; top (TERR_NT - new_h) rows = sky ld a,TERR_NT sub c jr z,rtc_grn ld b,a rtc_skyl: ld (hl),0 add hl,de djnz rtc_skyl rtc_grn: ld a,c ; bottom new_h rows = green or a ret z ld b,a rtc_grnl: ld (hl),&30 add hl,de djnz rtc_grnl ret ;;============================================================================ ;; ENCODE SPRITES - pack ASCII pen-art into interleaved (mask,data) buffers ;;============================================================================ init_sprites: ld ix,pl_pen ld de,pl_spr ld b,PL_W*PL_H ; pixel pairs call encode_spr ld ix,en0_pen ld de,en_spr0 ld b,EN_W*EN_H call encode_spr ld ix,en1_pen ld de,en_spr1 ld b,EN_W*EN_H call encode_spr ld ix,en2_pen ld de,en_spr2 ld b,EN_W*EN_H call encode_spr ld ix,en3_pen ld de,en_spr3 ld b,EN_W*EN_H call encode_spr ld ix,bul_pen ld de,bul_spr ld b,BL_W*BL_H call encode_spr ld ix,exp0_pen ld de,exp_spr0 ld b,EX_W*EX_H call encode_spr ld ix,exp1_pen ld de,exp_spr1 ld b,EX_W*EX_H call encode_spr ld ix,exp2_pen ld de,exp_spr2 ld b,EX_W*EX_H call encode_spr ret encode_spr: ;; IX = src ascii, DE = dest, B = pixel pairs ;; pen chars: '0'-'9' = 0-9 , 'A'-'F' = 10-15 es_loop: ld a,(ix+0) call pen_digit call lookup_left ld c,a ; left pixel bits ld a,(ix+1) call pen_digit call lookup_right or c ld c,a ; data byte ld l,0 ; mask accumulator and &AA jr nz,es_lo ld l,&AA ; left pixel transparent es_lo: ld a,c and &55 jr nz,es_ro ld a,l or &55 ; right pixel transparent ld l,a es_ro: ld a,l ld (de),a ; mask inc de ld a,c ld (de),a ; data inc de inc ix inc ix djnz es_loop ret ;; A = ASCII pen char -> A = pen value (0-15). '0'-'9'->0-9, 'A'-'F'->10-15 pen_digit: sub '0' cp 10 ret c sub 7 ; 'A'(=17 after sub'0') -> 10 ret ;;============================================================================ ;; DRAW SPRITE (interleaved mask,data) into draw_tbl buffer ;; IN: DE=sprite, H=x byte, L=y, B=height, C=width(bytes). Preserves IX/IY. ;;============================================================================ draw_spr: ld a,c ld (spr_w),a ld a,b ld (spr_h),a call get_scr_addr ; HL = first row addr (BC,DE preserved) dspr_row: push hl ld a,(spr_w) ld b,a dspr_col: ld a,(de) inc de and (hl) ld c,a ld a,(de) inc de or c ld (hl),a inc hl djnz dspr_col pop hl call adv_line ; HL -> next scanline (AF only) ld a,(spr_h) dec a ld (spr_h),a jr nz,dspr_row ret ;; advance HL to the next scanline (CPC interleaved layout). Clobbers AF only. adv_line: ld a,h add a,8 ld h,a and &38 ret nz ld a,l add a,&50 ld l,a ld a,h adc a,&C0 ld h,a ret ;;============================================================================ ;; ERASE RECTANGLE (fill pen 0) into draw_tbl buffer ;; IN: H=x byte, L=y, B=height, C=width. Preserves IX/IY. ;;============================================================================ erase_rect: ld a,c ld (spr_w),a ld a,b ld (spr_h),a call get_scr_addr erct_row: push hl ld a,(spr_w) ld b,a erct_col: ld (hl),0 inc hl djnz erct_col pop hl call adv_line ld a,(spr_h) dec a ld (spr_h),a jr nz,erct_row ret ;;============================================================================ ;; MEMCLR / RNG ;;============================================================================ memclr: ld (hl),0 ld d,h ld e,l inc de dec bc ld a,b or c ret z ldir ret ;; 16-bit Galois LFSR (period 65535). OUT: A. Preserves BC/DE/IX/IY. rng: push hl ld hl,(rng_seed) srl h rr l jr nc,rng_nc ld a,h xor &B4 ld h,a rng_nc: ld (rng_seed),hl ld a,h pop hl ret ;;============================================================================ ;; FIRE PRESSED? -> Carry set if any fire key/button is down ;;============================================================================ fire_pressed: ld a,(key_buf+5) bit 7,a jr z,fp_yes ld a,(key_buf+9) bit 5,a jr z,fp_yes ld a,(key_buf+9) bit 4,a jr z,fp_yes or a ret fp_yes: scf ret wait_fire_release: call read_keys call fire_pressed jr nc,wfr_done call wait_vsync jr wait_fire_release wfr_done: ret ;;============================================================================ ;; TITLE (Mode 1, centred text, static starfield, scrolling PEN-0 raster bars) ;; Raster bars change PEN 0 (the screen paper) per band, interrupt(HALT)-synced ;; so they fill the screen behind the text. Timing values are tunable. ;;============================================================================ show_title: di ld a,&8C call ga_out ; Mode 0, ROMs disabled call set_game_palette jp game_start ;; centre HL string on row C (Mode 1, 40 columns) m1_center: push hl ld b,0 m1c_cnt: ld a,(hl) or a jr z,m1c_done inc hl inc b jr m1c_cnt m1c_done: pop hl ld a,40 sub b srl a ld b,a ; col ;; fall through to m1_print m1_print: ld a,(hl) or a ret z push hl push bc call m1_putc pop bc pop hl inc hl inc b jr m1_print ;; render glyph A at (B=col 0-39, C=row 0-24), Mode 1 pen 1 m1_putc: sub 32 ld l,a ld h,0 add hl,hl add hl,hl add hl,hl ld de,font_tbl add hl,de push hl pop ix ld a,b add a,a ld (m1_x),a ; col*2 = x byte ld a,c add a,a add a,a add a,a ld (m1_y),a ; row*8 = y ld b,8 m1p_row: push bc ld a,(m1_x) ld h,a ld a,(m1_y) ld l,a call get_scr_addr ld a,(ix+0) ld c,a and &F0 ; left 4 pixels (pen 1 -> bits 7654) ld (hl),a inc hl ld a,c add a,a add a,a add a,a add a,a ; right 4 pixels ld (hl),a inc ix ld a,(m1_y) inc a ld (m1_y),a pop bc djnz m1p_row ret ;;============================================================================ ;; GAME START / INIT ;;============================================================================ game_start: call game_init call init_stars call clear_buffers call gen_terr_heights call build_terr_bmp xor a ld (scroll_off),a call hud_mark ; draw HUD into both buffers at the start ;; first frame draws to BUF1 (hidden) ld a,1 ld (parity),a ld hl,tbl_8 ld (draw_tbl),hl ld a,R12_BUF1 ld (disp_r12),a jp game_loop game_init: ld a,3 ld (lives),a xor a ld (score),a ld (game_state),a ld (fire_timer),a ld (wave_type),a ld (wave_remaining),a ; no wave in progress -> first one starts soon ld (spawn_timer),a ; start the first wave almost immediately ld (pl_boom),a ; no death boom in progress ld a,1 ld (level),a ld a,PL_XSTART ld (pl_x),a ld (pl_ox0),a ld (pl_ox1),a ld a,PL_YSTART ld (pl_y),a ld (pl_oy0),a ld (pl_oy1),a ld hl,enemy_pool ld bc,EN_SZ*MAX_ENE call memclr ld hl,bullet_pool ld bc,BL_SZ*MAX_BUL call memclr ld hl,explosion_pool ld bc,EX_SZ*MAX_EXP call memclr call init_level ret level_data: defb 8, 1, 70 ; kills, speed, spawn-delay defb 10, 1, 55 defb 12, 2, 45 defb 16, 2, 35 defb 20, 3, 28 init_level: ld a,(level) dec a ld e,a ld d,0 ld l,a ld h,0 add hl,hl add hl,de ; *3 ld de,level_data add hl,de ld a,(hl) ld (kills_needed),a inc hl ld a,(hl) ld (ene_speed),a inc hl ld a,(hl) ld (spawn_delay),a ld (spawn_timer),a xor a ld (lvl_kills),a ret ;;============================================================================ ;; MAIN LOOP (logic -> render back buffer -> vsync -> flip -> swap) ;;============================================================================ game_loop: call read_keys call update_player call update_bullets call update_enemies call update_explosions call update_pboom call update_stars call check_collisions call render_stars call render_enemies call render_bullets call render_explosions call render_player call draw_terrain_scroll ;; HUD only redraws when a value changed (covers both buffers via counter) ld a,(hud_count) or a jr z,gl_nohud dec a ld (hud_count),a call draw_hud gl_nohud: call sound_update call wait_vsync ld a,(disp_r12) call set_r12 call swap_buffers ld a,(game_state) or a jp nz,gl_end jp game_loop gl_end: cp 2 jp z,show_win jp show_gameover swap_buffers: ld a,(parity) xor 1 ld (parity),a or a jr z,swb_p0 ld hl,tbl_8 ld (draw_tbl),hl ld a,R12_BUF1 ld (disp_r12),a ret swb_p0: ld hl,tbl_c ld (draw_tbl),hl ld a,R12_BUF0 ld (disp_r12),a ret ;;============================================================================ ;; PLAYER ;;============================================================================ update_player: ld a,(pl_boom) or a ret nz ; frozen while the death boom plays ;; --- UP --- cursor-up(l0 b0), Q(l8 b3), joy-up(l9 b0) ld a,(key_buf+0) bit 0,a jr z,pu_up ld a,(key_buf+8) bit 3,a jr z,pu_up ld a,(key_buf+9) bit 0,a jr z,pu_up jr pu_dn pu_up: ld a,(pl_y) cp PL_YMIN+PL_SPEED jr c,pu_dn sub PL_SPEED ld (pl_y),a pu_dn: ;; --- DOWN --- cursor-down(l0 b2), A(l8 b5), joy-down(l9 b1) ld a,(key_buf+0) bit 2,a jr z,pu_down ld a,(key_buf+8) bit 5,a jr z,pu_down ld a,(key_buf+9) bit 1,a jr z,pu_down jr pu_lf pu_down: ld a,(pl_y) cp PL_YMAX-PL_SPEED jr nc,pu_lf add a,PL_SPEED ld (pl_y),a pu_lf: ;; --- LEFT --- cursor-left(l1 b0), joy-left(l9 b2) ld a,(key_buf+1) bit 0,a jr z,pu_left ld a,(key_buf+9) bit 2,a jr z,pu_left jr pu_rt pu_left: ld a,(pl_x) cp PL_XMIN+PL_SPEED jr c,pu_rt sub PL_SPEED ld (pl_x),a pu_rt: ;; --- RIGHT --- cursor-right(l0 b1), joy-right(l9 b3) ld a,(key_buf+0) bit 1,a jr z,pu_right ld a,(key_buf+9) bit 3,a jr z,pu_right jr pu_fire pu_right: ld a,(pl_x) cp PL_XMAX-PL_SPEED jr nc,pu_fire add a,PL_SPEED ld (pl_x),a pu_fire: ld a,(fire_timer) or a jr z,pu_fchk dec a ld (fire_timer),a ret pu_fchk: ld a,(key_buf+5) bit 7,a jr z,pu_dofire ld a,(key_buf+9) bit 5,a jr z,pu_dofire ld a,(key_buf+9) bit 4,a jr z,pu_dofire ret pu_dofire: ld a,FIRE_DELAY ld (fire_timer),a call spawn_bullet call sound_fire ret spawn_bullet: ld ix,bullet_pool ld b,MAX_BUL sb_loop: ld a,(ix+BL_ACTIVE) or a jr z,sb_found ld de,BL_SZ add ix,de djnz sb_loop ret sb_found: ld a,(pl_x) dec a dec a ld (ix+BL_X),a ; just left of the ship ld a,(pl_y) add a,4 ld (ix+BL_Y),a ld a,1 ld (ix+BL_ACTIVE),a ret update_bullets: ld ix,bullet_pool ld b,MAX_BUL ub_loop: ld a,(ix+BL_ACTIVE) or a jr z,ub_next ld a,(ix+BL_X) sub BL_SPEED ; fly LEFT jr c,ub_die ld (ix+BL_X),a jr ub_next ub_die: xor a ld (ix+BL_ACTIVE),a ub_next: ld de,BL_SZ add ix,de djnz ub_loop ret ;;============================================================================ ;; ENEMIES - traditional formation waves ;; Each wave uses one of 4 attack patterns (cycled): a vertical WALL with gaps, ;; a weaving SNAKE in single file, and two DIAGONAL sweeps. The pattern decides ;; each enemy's entry Y, whether it weaves (sine) and the release spacing. ;;============================================================================ update_enemies: ld a,(wave_remaining) or a jr z,ue_gap ; no wave running -> count to next wave ld a,(release_timer) or a jr z,ue_release dec a ld (release_timer),a jr ue_move ue_release: call spawn_one_seq ld a,(wave_remaining) dec a ld (wave_remaining),a jr z,ue_wdone ld a,(cur_reldelay) ld (release_timer),a or a jr z,ue_release ; 0 spacing -> launch the whole wall this frame jr ue_move ue_wdone: ld a,(spawn_delay) ; wave finished -> inter-wave gap ld (spawn_timer),a jr ue_move ue_gap: ld a,(spawn_timer) or a jr z,ue_newwave dec a ld (spawn_timer),a jr ue_move ue_newwave: call start_wave ue_move: ld ix,enemy_pool ld b,MAX_ENE uem_loop: ld a,(ix+EN_ACTIVE) or a jr z,uem_next ld a,(ix+EN_PHASE) cp 255 jr z,uem_movex ; 255 = straight line (no weave) inc a and 31 ld (ix+EN_PHASE),a ld e,a ld d,0 ld hl,sintab add hl,de ld a,(ix+EN_BASEY) add a,(hl) ld (ix+EN_Y),a uem_movex: ld a,(ix+EN_X) add a,(ix+EN_SPEED) cp EN_ESCAPE jr nc,uem_die ld (ix+EN_X),a jr uem_next uem_die: xor a ld (ix+EN_ACTIVE),a uem_next: ld de,EN_SZ add ix,de djnz uem_loop ret ;; begin a new wave: cycle to the next pattern, arm the release sequence start_wave: ld a,WAVE_SIZE ld (wave_remaining),a xor a ld (wave_index),a ld (release_timer),a ; release first immediately ld a,(wave_type) inc a cp 4 jr c,sw_tok xor a sw_tok: ld (wave_type),a ; pattern index (also the enemy sprite type) ld e,a ld d,0 ld hl,pat_reldelay add hl,de ld a,(hl) ld (cur_reldelay),a ret ;; release the next enemy of the current wave at the left edge, using the ;; current pattern's Y table and weave setting. spawn_one_seq: ld ix,enemy_pool ld b,MAX_ENE sos_find: ld a,(ix+EN_ACTIVE) or a jr z,sos_found ld de,EN_SZ add ix,de djnz sos_find ret ; pool full sos_found: xor a ld (ix+EN_X),a ; enter at left edge ;; base Y = pat_basey[wave_type*5 + wave_index] ld a,(wave_type) ld c,a add a,a add a,a add a,c ; *5 ld c,a ld a,(wave_index) add a,c ld e,a ld d,0 ld hl,pat_basey add hl,de ld a,(hl) ld (ix+EN_BASEY),a ld (ix+EN_Y),a ld a,1 ld (ix+EN_ACTIVE),a ld a,(wave_type) ld (ix+EN_TYPE),a ; sprite/colour by pattern ld a,(ene_speed) ld (ix+EN_SPEED),a ;; phase: weaving patterns get a staggered phase, others go straight (255) ld a,(wave_type) ld e,a ld d,0 ld hl,pat_sine add hl,de ld a,(hl) or a jr z,sos_straight ld a,(wave_index) ld c,a add a,a add a,a add a,c ; phase = index*5 and 31 jr sos_setph sos_straight: ld a,255 sos_setph: ld (ix+EN_PHASE),a ld a,(wave_index) inc a ld (wave_index),a ret ;; explosions spawn_explosion: ld ix,explosion_pool ld b,MAX_EXP spx_find: ld a,(ix+EX_TIMER) or a jr z,spx_found ld de,EX_SZ add ix,de djnz spx_find ret spx_found: ld a,(exp_x) ld (ix+EX_X),a ld a,(exp_y) ld (ix+EX_Y),a ld a,18 ld (ix+EX_TIMER),a ret update_explosions: ld ix,explosion_pool ld b,MAX_EXP uex_loop: ld a,(ix+EX_TIMER) or a jr z,uex_next dec a ld (ix+EX_TIMER),a uex_next: ld de,EX_SZ add ix,de djnz uex_loop ret ;;============================================================================ ;; STARS ;;============================================================================ init_stars: ld ix,star_data ld b,N_STARS ld c,0 is_loop: call rng isx: cp 80 jr c,isxok sub 80 jr isx isxok: ld (ix+ST_X),a call rng isy: cp 130 jr c,isyok sub 130 jr isy isyok: add a,PLAY_TOP ; y 22..151 (above terrain) ld (ix+ST_Y),a ld a,c ismod: cp 3 jr c,ismodok sub 3 jr ismod ismodok: inc a ; speed 1..3 ld (ix+ST_SPD),a inc c ld de,STAR_SZ add ix,de djnz is_loop ret update_stars: ld ix,star_data ld b,N_STARS us_loop: ld a,(ix+ST_X) add a,(ix+ST_SPD) ; scroll right cp 80 jr c,us_ok sub 80 us_ok: ld (ix+ST_X),a ld de,STAR_SZ add ix,de djnz us_loop ret render_stars: ld ix,star_data ld b,N_STARS rs_loop: push bc ;; erase pixel from 2 frames ago: x - 2*spd (this buffer is 2 frames stale) ld a,(ix+ST_SPD) add a,a ld c,a ; 2*spd ld a,(ix+ST_X) sub c jr nc,rs_eok add a,80 rs_eok: ld h,a ld l,(ix+ST_Y) call get_scr_addr ld (hl),0 ;; draw new pixel ld h,(ix+ST_X) ld l,(ix+ST_Y) call get_scr_addr ld a,(ix+ST_SPD) dec a ld e,a ld d,0 push hl ld hl,star_bytes add hl,de ld a,(hl) pop hl ld (hl),a pop bc ld de,STAR_SZ add ix,de djnz rs_loop ret ;;============================================================================ ;; COLLISIONS ;;============================================================================ check_collisions: ld iy,bullet_pool ld c,MAX_BUL cc_b: ld a,(iy+BL_ACTIVE) or a jp z,cc_b_next ld ix,enemy_pool ld b,MAX_ENE cc_e: ld a,(ix+EN_ACTIVE) or a jr z,cc_e_next ld a,(ix+EN_X) add a,EN_W ld d,a ld a,(iy+BL_X) cp d jr nc,cc_e_next ld a,(iy+BL_X) add a,BL_W ld d,a ld a,(ix+EN_X) cp d jr nc,cc_e_next ld a,(ix+EN_Y) add a,EN_H ld d,a ld a,(iy+BL_Y) cp d jr nc,cc_e_next ld a,(iy+BL_Y) add a,BL_H ld d,a ld a,(ix+EN_Y) cp d jr nc,cc_e_next ;; HIT ld a,(ix+EN_X) ld (exp_x),a ld a,(ix+EN_Y) ld (exp_y),a xor a ld (ix+EN_ACTIVE),a ld (iy+BL_ACTIVE),a push bc push iy call spawn_explosion call enemy_killed pop iy pop bc jr cc_b_next cc_e_next: ld de,EN_SZ add ix,de djnz cc_e cc_b_next: ld de,BL_SZ add iy,de dec c jr nz,cc_b call check_player_hit ret enemy_killed: call sound_expl call hud_mark ld a,(score) cp 250 jr nc,ek_nos inc a ld (score),a ek_nos: ld a,(lvl_kills) inc a ld (lvl_kills),a ld hl,kills_needed cp (hl) jr c,ek_done ld a,(level) cp N_LEVELS jr nc,ek_win inc a ld (level),a call init_level jr ek_done ek_win: ld a,2 ld (game_state),a ek_done: ret check_player_hit: ld a,(pl_boom) or a ret nz ; invulnerable while the death boom plays ld ix,enemy_pool ld b,MAX_ENE cph_loop: ld a,(ix+EN_ACTIVE) or a jr z,cph_next ld a,(ix+EN_X) add a,EN_W ld d,a ld a,(pl_x) cp d jr nc,cph_next ld a,(pl_x) add a,PL_W ld d,a ld a,(ix+EN_X) cp d jr nc,cph_next ld a,(ix+EN_Y) add a,EN_H ld d,a ld a,(pl_y) cp d jr nc,cph_next ld a,(pl_y) add a,PL_H ld d,a ld a,(ix+EN_Y) cp d jr nc,cph_next ;; record explosion at the enemy, kill it ld a,(ix+EN_X) ld (exp_x),a ld a,(ix+EN_Y) ld (exp_y),a xor a ld (ix+EN_ACTIVE),a call spawn_explosion call player_hit ret cph_next: ld de,EN_SZ add ix,de djnz cph_loop ret player_hit: call sound_expl call hud_mark ;; remember the death site and start a sustained multi-explosion boom ld a,(pl_x) ld (boom_x),a ld a,(pl_y) ld (boom_y),a ld a,44 ld (pl_boom),a ; long, dramatic death sequence ld a,(lives) dec a ld (lives),a jr nz,ph_done ld a,1 ld (game_state),a ph_done: ret ;; while pl_boom > 0 the ship is hidden; spray explosions across its wreck every ;; frame (the pool caps how many show at once). When it ends, respawn at start. update_pboom: ld a,(pl_boom) or a ret z dec a ld (pl_boom),a jr nz,upb_spray ;; sequence finished -> respawn the ship at its start position ld a,PL_XSTART ld (pl_x),a ld a,PL_YSTART ld (pl_y),a ret upb_spray: ld a,(pl_boom) cp 19 ret c ; last 18 frames: let blasts finish before respawn call rng and 7 ld c,a ld a,(boom_x) add a,c ld (exp_x),a call rng and 7 ld c,a ld a,(boom_y) add a,c ld (exp_y),a call spawn_explosion ret ;;============================================================================ ;; RENDER (per-buffer shadow erase-old / draw-new) ;;============================================================================ render_player: ld a,(pl_boom) or a jr z,rp_normal ;; ship hidden during the boom: just erase its last-drawn position ld a,(parity) or a jr nz,rph_p1 ld a,(pl_ox0) ld h,a ld a,(pl_oy0) ld l,a ld b,PL_H ld c,PL_W jp erase_rect rph_p1: ld a,(pl_ox1) ld h,a ld a,(pl_oy1) ld l,a ld b,PL_H ld c,PL_W jp erase_rect rp_normal: ld a,(parity) or a jr nz,rp_p1 ;; parity 0 ld a,(pl_ox0) ld h,a ld a,(pl_oy0) ld l,a ld b,PL_H ld c,PL_W call erase_rect ld de,pl_spr ld a,(pl_x) ld h,a ld a,(pl_y) ld l,a ld b,PL_H ld c,PL_W call draw_spr ld a,(pl_x) ld (pl_ox0),a ld a,(pl_y) ld (pl_oy0),a ret rp_p1: ld a,(pl_ox1) ld h,a ld a,(pl_oy1) ld l,a ld b,PL_H ld c,PL_W call erase_rect ld de,pl_spr ld a,(pl_x) ld h,a ld a,(pl_y) ld l,a ld b,PL_H ld c,PL_W call draw_spr ld a,(pl_x) ld (pl_ox1),a ld a,(pl_y) ld (pl_oy1),a ret render_enemies: ld ix,enemy_pool ld b,MAX_ENE re_loop: push bc push ix pop iy ld de,EN_SHADOW ld a,(parity) or a jr z,re_p0 ld de,EN_SHADOW+3 re_p0: add iy,de ld a,(iy+2) or a jr z,re_noerase ld h,(iy+0) ld l,(iy+1) ld b,EN_H ld c,EN_W call erase_rect re_noerase: ld a,(ix+EN_ACTIVE) or a jr z,re_inact ;; select sprite by type (0..3) via en_spr_tbl ld a,(ix+EN_TYPE) add a,a ld e,a ld d,0 ld hl,en_spr_tbl add hl,de ld e,(hl) inc hl ld d,(hl) ; DE = sprite ptr ld h,(ix+EN_X) ld l,(ix+EN_Y) ld b,EN_H ld c,EN_W call draw_spr ld a,(ix+EN_X) ld (iy+0),a ld a,(ix+EN_Y) ld (iy+1),a ld a,1 ld (iy+2),a jr re_next re_inact: xor a ld (iy+2),a re_next: pop bc ld de,EN_SZ add ix,de djnz re_loop ret render_bullets: ld ix,bullet_pool ld b,MAX_BUL rb_loop: push bc push ix pop iy ld de,BL_SHADOW ld a,(parity) or a jr z,rb_p0 ld de,BL_SHADOW+3 rb_p0: add iy,de ld a,(iy+2) or a jr z,rb_noerase ld h,(iy+0) ld l,(iy+1) ld b,BL_H ld c,BL_W call erase_rect rb_noerase: ld a,(ix+BL_ACTIVE) or a jr z,rb_inact ld h,(ix+BL_X) ld l,(ix+BL_Y) ld de,bul_spr ld b,BL_H ld c,BL_W call draw_spr ld a,(ix+BL_X) ld (iy+0),a ld a,(ix+BL_Y) ld (iy+1),a ld a,1 ld (iy+2),a jr rb_next rb_inact: xor a ld (iy+2),a rb_next: pop bc ld de,BL_SZ add ix,de djnz rb_loop ret render_explosions: ld ix,explosion_pool ld b,MAX_EXP rx_loop: push bc push ix pop iy ld de,EX_SHADOW ld a,(parity) or a jr z,rx_p0 ld de,EX_SHADOW+3 rx_p0: add iy,de ld a,(iy+2) or a jr z,rx_noerase ld h,(iy+0) ld l,(iy+1) ld b,EX_H ld c,EX_W call erase_rect rx_noerase: ld a,(ix+EX_TIMER) or a jr z,rx_inact ;; choose animation frame by remaining timer ld de,exp_spr0 cp 13 jr nc,rx_draw ld de,exp_spr1 cp 7 jr nc,rx_draw ld de,exp_spr2 rx_draw: ld h,(ix+EX_X) ld l,(ix+EX_Y) ld b,EX_H ld c,EX_W call draw_spr ld a,(ix+EX_X) ld (iy+0),a ld a,(ix+EX_Y) ld (iy+1),a ld a,1 ld (iy+2),a jr rx_next rx_inact: xor a ld (iy+2),a rx_next: pop bc ld de,EX_SZ add ix,de djnz rx_loop ret ;;============================================================================ ;; HUD ;;============================================================================ ;; mark the HUD dirty for 2 frames (so it refreshes in both buffers) hud_mark: ld a,2 ld (hud_count),a ret draw_hud: ld hl,str_sc ; score ld b,0 ld c,0 call my_print ld a,(score) ld b,2 ld c,0 call print_a_dec3 ld hl,str_lv ; sector / level ld b,6 ld c,0 call my_print ld a,(level) add a,'0' ld b,7 ld c,0 call my_putc ld hl,str_go ; enemies left to clear this sector ld b,9 ld c,0 call my_print ld a,(kills_needed) ld hl,lvl_kills sub (hl) jr nc,dh_gok xor a dh_gok: ld b,11 ld c,0 call print_a_dec3 ld hl,str_li ; lives ld b,15 ld c,0 call my_print ld a,(lives) add a,'0' ld b,17 ld c,0 call my_putc ret ;; print A as 3 decimal digits at (B=col,C=row) print_a_dec3: ld (pd_val),a ld a,(pd_val) ld d,'0' pdh: cp 100 jr c,pdhd sub 100 inc d jr pdh pdhd: ld (pd_val),a push bc ld a,d call my_putc pop bc inc b ld a,(pd_val) ld d,'0' pdt: cp 10 jr c,pdtd sub 10 inc d jr pdt pdtd: ld (pd_val),a push bc ld a,d call my_putc pop bc inc b ld a,(pd_val) add a,'0' call my_putc ret ;;============================================================================ ;; GAME OVER / WIN ;;============================================================================ show_gameover: call sound_silence ; stop the explosion noise that was playing call clear_buffers ld hl,tbl_c ld (draw_tbl),hl ld a,R12_BUF0 call set_r12 ld hl,str_gover ld b,6 ld c,11 call my_print ld hl,str_again ld b,4 ld c,14 call my_print ld hl,tune_gover call play_tune call wait_fire_release sgo_wait: call read_keys call fire_pressed jr c,sgo_go call wait_vsync jr sgo_wait sgo_go: call wait_fire_release jp show_title show_win: call sound_silence call clear_buffers ld hl,tbl_c ld (draw_tbl),hl ld a,R12_BUF0 call set_r12 ld hl,str_win ld b,2 ld c,11 call my_print ld hl,tune_win call play_tune ld hl,str_again ld b,4 ld c,14 call my_print call wait_fire_release sw_wait: call read_keys call fire_pressed jr c,sw_go call wait_vsync jr sw_wait sw_go: call wait_fire_release jp show_title ;;============================================================================ ;; LOOKUP / CONSTANT TABLES ;;============================================================================ ;; Mode 0 pixel encoding (pen -> display-byte bits) m0_left: defb &00,&80,&08,&88,&20,&A0,&28,&A8,&02,&82,&0A,&8A,&22,&A2,&2A,&AA m0_right: defb &00,&40,&04,&44,&10,&50,&14,&54,&01,&41,&05,&45,&11,&51,&15,&55 ;; star plot byte by (speed-1): far=pen9 dim, mid=pen8, near=pen7 bright star_bytes: defb &82,&02,&A8 ;; Gate Array hardware colour codes (from garray.html quick-reference) ;; game Mode-0 palette, pens 0..11 pal_game: defb &54,&53,&4A,&4C,&52,&4E,&4B,&4B,&57,&44,&4D,&40 ;; title Mode-1 palette, pens 0..3 (black, white text, cyan stars, yellow) pal_title: defb &54,&4B,&53,&4A ;; raster-bar gradient (one bar: dark -> bright -> dark), repeated into bar_cols bar_grad: defb &54,&44,&55,&57,&53,&46,&40,&4B,&4B,&40,&46,&53,&57,&55,&44,&54 ;; enemy sprite pointer table (by type 0..3) en_spr_tbl: defw en_spr0,en_spr1,en_spr2,en_spr3 ;; enemy attack patterns (4): WALL, SNAKE, DIAG-DOWN, DIAG-UP pat_reldelay: defb 0, 6, 7, 7 ; frames between releases (0 = whole wall at once) pat_sine: defb 0, 1, 0, 0 ; 1 = enemies weave (sine) pat_basey: ; entry Y per enemy (5 per pattern) defb 26,56,86,116,146 ; WALL - spread, dodge the gaps defb 86,86,86,86,86 ; SNAKE - single file, weaving defb 26,56,86,116,146 ; DIAG DOWN defb 146,116,86,56,26 ; DIAG UP ;; sine table, 32 entries, amplitude ~14 (signed; added to base Y) sintab: defb 0,3,5,8,10,12,13,14,14,14,13,12,10,8,5,3 defb 0,-3,-5,-8,-10,-12,-13,-14,-14,-14,-13,-12,-10,-8,-5,-3 ;; jingles: (tone_period_lo, tone_period_hi, frames)*, frames 0 = end tune_gover: ; descending "game over" defb &1C,&01,14 defb &66,&01,14 defb &A9,&01,14 defb &38,&02,28 defb 0,0,0 tune_win: ; ascending "you win" defb &38,&02,12 defb &A9,&01,12 defb &66,&01,12 defb &1C,&01,12 defb &BE,&00,24 defb 0,0,0 ;;============================================================================ ;; TEXT ;;============================================================================ str_title: defb "RETURN HOME",0 str_sub: defb "A Z80 SHOOTER",0 str_c1: defb "CURSORS = MOVE",0 str_c2: defb "Q A = UP DOWN",0 str_c3: defb "SPACE = FIRE",0 str_c4: defb "OR JOYSTICK",0 str_goal: defb "CLEAR 5 SECTORS",0 str_press: defb "FIRE TO START",0 str_gover: defb "GAME OVER",0 str_win: defb "YOU MADE IT HOME",0 str_again: defb "FIRE TO RETRY",0 str_sc: defb "SC",0 str_lv: defb "S",0 str_go: defb "GO",0 str_li: defb "LI",0 ;;============================================================================ ;; SPRITE PEN-ART (one ASCII digit per pixel; pen 0 = transparent) ;;============================================================================ pl_pen: ; 16 x 8 ship (cell_7C0A flipped, shipmap.png colours) defb "000000000006666B" defb "00000000006666B0" defb "000AAA0066666B00" defb "00AA666666666B00" defb "6666655555555553" defb "0666665555555553" defb "00BB666666666600" defb "0000BBBBBBBB0000" ;; type 0 = UFO (from ufo.png): green saucer, white/red/blue lights en0_pen: defb "00000000" defb "00000000" defb "04444440" defb "44444444" defb "63806380" defb "63806380" defb "44444444" defb "04444440" ;; types 1-3 = "meanmap" bug (from meanmap.png) recoloured: cyan / orange / yellow en1_pen: ; cyan body, white eyes, red mouth defb "00111000" defb "01111100" defb "16616610" defb "11616110" defb "01111100" defb "01131100" defb "11010110" defb "10010010" en2_pen: ; orange body defb "00555000" defb "05555500" defb "56656650" defb "55656550" defb "05555500" defb "05535500" defb "55050550" defb "50050050" en3_pen: ; yellow body, red eyes, white mouth defb "00222000" defb "02222200" defb "23323320" defb "22323220" defb "02222200" defb "02262200" defb "22020220" defb "20020020" bul_pen: ; 4 x 2 laser bolt (white) defb "6666" defb "6666" exp0_pen: ; 8 x 8 explosion frame 0 (small) defb "00000000" defb "00000000" defb "00033000" defb "00366300" defb "00366300" defb "00033000" defb "00000000" defb "00000000" exp1_pen: ; 8 x 8 explosion frame 1 (burst) defb "00033000" defb "03322330" defb "03266230" defb "32666623" defb "32666623" defb "03266230" defb "03322330" defb "00033000" exp2_pen: ; 8 x 8 explosion frame 2 (fading embers) defb "30000003" defb "05300350" defb "00533500" defb "00355300" defb "00355300" defb "00533500" defb "05300350" defb "30000003" ;;============================================================================ ;; RAM BUFFERS / VARIABLES ;;============================================================================ ;; encoded sprites (mask,data interleaved) - filled by init_sprites pl_spr: defs 2*PL_W*PL_H en_spr0: defs 2*EN_W*EN_H en_spr1: defs 2*EN_W*EN_H en_spr2: defs 2*EN_W*EN_H en_spr3: defs 2*EN_W*EN_H bul_spr: defs 2*BL_W*BL_H exp_spr0: defs 2*EX_W*EX_H exp_spr1: defs 2*EX_W*EX_H exp_spr2: defs 2*EX_W*EX_H ;; embedded bold 8x8 font, codes 32..90 (space..'Z'); my_putc indexes (char-32)*8 font_tbl: defb 0,0,0,0,0,0,0,0 ; 32 space defb %00110000,%00110000,%00110000,%00110000,%00110000,%00000000,%00110000,0 ; 33 ! defb 0,0,0,0,0,0,0,0 ; 34 " defb 0,0,0,0,0,0,0,0 ; 35 # defb 0,0,0,0,0,0,0,0 ; 36 $ defb 0,0,0,0,0,0,0,0 ; 37 % defb 0,0,0,0,0,0,0,0 ; 38 & defb 0,0,0,0,0,0,0,0 ; 39 ' defb 0,0,0,0,0,0,0,0 ; 40 ( defb 0,0,0,0,0,0,0,0 ; 41 ) defb 0,0,0,0,0,0,0,0 ; 42 * defb 0,0,0,0,0,0,0,0 ; 43 + defb 0,0,0,0,0,0,0,0 ; 44 , defb 0,0,0,%01111100,%01111100,0,0,0 ; 45 - defb 0,0,0,0,0,%00110000,%00110000,0 ; 46 . defb %00000100,%00001100,%00011000,%00110000,%01100000,%11000000,%10000000,0 ; 47 / defb %01111100,%11000110,%11001110,%11011110,%11110110,%11100110,%01111100,0 ; 48 0 defb %00110000,%01110000,%11110000,%00110000,%00110000,%00110000,%11111100,0 ; 49 1 defb %01111100,%11000110,%00000110,%00011100,%01110000,%11000000,%11111110,0 ; 50 2 defb %11111110,%00001100,%00011000,%00111100,%00000110,%11000110,%01111100,0 ; 51 3 defb %00011100,%00111100,%01101100,%11001100,%11111110,%00001100,%00001100,0 ; 52 4 defb %11111110,%11000000,%11111100,%00000110,%00000110,%11000110,%01111100,0 ; 53 5 defb %00111100,%01100000,%11000000,%11111100,%11000110,%11000110,%01111100,0 ; 54 6 defb %11111110,%00000110,%00001100,%00011000,%00110000,%00110000,%00110000,0 ; 55 7 defb %01111100,%11000110,%11000110,%01111100,%11000110,%11000110,%01111100,0 ; 56 8 defb %01111100,%11000110,%11000110,%01111110,%00000110,%00001100,%01111000,0 ; 57 9 defb 0,%00110000,%00110000,0,%00110000,%00110000,0,0 ; 58 : defb 0,0,0,0,0,0,0,0 ; 59 ; defb 0,0,0,0,0,0,0,0 ; 60 < defb 0,0,%01111100,0,%01111100,0,0,0 ; 61 = defb 0,0,0,0,0,0,0,0 ; 62 > defb 0,0,0,0,0,0,0,0 ; 63 ? defb 0,0,0,0,0,0,0,0 ; 64 @ defb %00111000,%01101100,%11000110,%11000110,%11111110,%11000110,%11000110,0 ; 65 A defb %11111100,%11000110,%11000110,%11111100,%11000110,%11000110,%11111100,0 ; 66 B defb %01111100,%11000110,%11000000,%11000000,%11000000,%11000110,%01111100,0 ; 67 C defb %11111000,%11001100,%11000110,%11000110,%11000110,%11001100,%11111000,0 ; 68 D defb %11111110,%11000000,%11000000,%11111100,%11000000,%11000000,%11111110,0 ; 69 E defb %11111110,%11000000,%11000000,%11111100,%11000000,%11000000,%11000000,0 ; 70 F defb %01111100,%11000110,%11000000,%11001110,%11000110,%11000110,%01111110,0 ; 71 G defb %11000110,%11000110,%11000110,%11111110,%11000110,%11000110,%11000110,0 ; 72 H defb %01111100,%00110000,%00110000,%00110000,%00110000,%00110000,%01111100,0 ; 73 I defb %00011110,%00001100,%00001100,%00001100,%11001100,%11001100,%01111000,0 ; 74 J defb %11000110,%11001100,%11011000,%11110000,%11011000,%11001100,%11000110,0 ; 75 K defb %11000000,%11000000,%11000000,%11000000,%11000000,%11000000,%11111110,0 ; 76 L defb %11000110,%11101110,%11111110,%11010110,%11000110,%11000110,%11000110,0 ; 77 M defb %11000110,%11100110,%11110110,%11011110,%11001110,%11000110,%11000110,0 ; 78 N defb %01111100,%11000110,%11000110,%11000110,%11000110,%11000110,%01111100,0 ; 79 O defb %11111100,%11000110,%11000110,%11111100,%11000000,%11000000,%11000000,0 ; 80 P defb %01111100,%11000110,%11000110,%11000110,%11011110,%11001100,%01110110,0 ; 81 Q defb %11111100,%11000110,%11000110,%11111100,%11011000,%11001100,%11000110,0 ; 82 R defb %01111100,%11000110,%11000000,%01111100,%00000110,%11000110,%01111100,0 ; 83 S defb %11111110,%00110000,%00110000,%00110000,%00110000,%00110000,%00110000,0 ; 84 T defb %11000110,%11000110,%11000110,%11000110,%11000110,%11000110,%01111100,0 ; 85 U defb %11000110,%11000110,%11000110,%11000110,%01101100,%00111000,%00010000,0 ; 86 V defb %11000110,%11000110,%11000110,%11010110,%11111110,%11101110,%11000110,0 ; 87 W defb %11000110,%01101100,%00111000,%00010000,%00111000,%01101100,%11000110,0 ; 88 X defb %11000110,%11000110,%01101100,%00111000,%00110000,%00110000,%00110000,0 ; 89 Y defb %11111110,%00001100,%00011000,%00110000,%01100000,%11000000,%11111110,0 ; 90 Z ;; text rendering scratch text_pp: defs 4 mpc_x: defb 0 mpc_y: defb 0 pd_val: defb 0 ;; sprite blit scratch spr_w: defb 0 spr_h: defb 0 ;; double-buffer state parity: defb 0 draw_tbl: defw tbl_c disp_r12: defb R12_BUF0 hud_count: defb 0 scroll_off: defb 0 bar_scroll: defb 0 m1_x: defb 0 m1_y: defb 0 dts_r: defb 0 ;; game variables level: defb 1 score: defb 0 lives: defb 3 pl_x: defb PL_XSTART pl_ox0: defb PL_XSTART pl_ox1: defb PL_XSTART pl_y: defb PL_YSTART pl_oy0: defb PL_YSTART pl_oy1: defb PL_YSTART ene_speed: defb 1 kills_needed: defb 8 lvl_kills: defb 0 spawn_delay: defb 70 spawn_timer: defb 70 fire_timer: defb 0 game_state: defb 0 wave_type: defb 0 wave_remaining: defb 0 wave_index: defb 0 release_timer: defb 0 cur_reldelay: defb 0 exp_x: defb 0 exp_y: defb 0 pl_boom: defb 0 boom_x: defb 0 boom_y: defb 0 snd_a_t: defb 0 snd_b_t: defb 0 note_lo: defb 0 note_hi: defb 0 note_dur: defb 0 rng_seed: defw &A55A th_rand: defs 128 terr_bmp: defs TERR_NT*128 bar_cols: defs 128 ; raster-bar gradient (built from bar_grad) ;; keyboard matrix buffer (10 lines) key_buf: defs 10 ;; object pools enemy_pool: defs EN_SZ*MAX_ENE bullet_pool: defs BL_SZ*MAX_BUL explosion_pool: defs EX_SZ*MAX_EXP star_data: defs STAR_SZ*N_STARS ;; scanline address tables (one per buffer) tbl_c: defs 200*2 tbl_8: defs 200*2 ;; stack (grows down from stack_end) defs 256 stack_end: end
CPC-POWER/CPCSOFTS
, programmation par
Kukulcan
© 2007-2026 tous droits réservés.
Reproduction sans autorisation interdite. Tous les titres utilisés appartiennent à leurs propriétaires respectifs.
Hébergement Web, Mail et serveurs de jeux haute performance