Project name: labesc-23.asm LAByrinth Escape - Source Code is EDASS-128 compatible NOTE: EDASS-128 is Commodore-C128 native assembler/editor But it can also be used under emulation, e.g. VICE SOURCE CODE {It can be copy/pasted to EDASS-128 in C128 emulation under VICE-Emulator} {It uses 80-column screen!} Last update: 14.05.2026 ; ;------------------------------------------------------------------------------- ;NOTE: Use 80 column screen for the source! ;------------------------------------------------------------------------------- ; ; I've decided to make my game i had a ; plan for in the past... 40 years ago! ; Nothing big! Just one level to ; demonstrate the 2nd collision register ; meaning, sprite vs graphics ; ; Let's do it! (c) mzretro.ch, lived.ch - 14th of May 2026 ; Coding: Misel Zivanovic ; ;------------------------------------------------------------------------------- ; prerequisites to load a file: ;------------------------------------------------------------------------------- setbnk = $ff68 getcfg = $ff6b setlfs = $ffba setnam = $ffbd loadsp = $ffd5 gospr = $0e00 ; sprite start area $0e00 - $0fff ;------------------------------------------------------------------------------- ; ; BASIC STARTLINES ; *= $1c00 .obj m .bank 15 ; .byte $00,$1a,$1c,$0a,$00,$8f,$20,$28 .byte $43,$29,$20,$57,$57,$57,$2e,$4d .byte $5a,$52,$45,$54,$52,$4f,$2e,$43 .byte $48,$00,$31,$1c,$14,$00,$8f,$20 .byte $4c,$41,$42,$59,$52,$49,$4e,$54 .byte $48,$20,$45,$53,$43,$41,$50,$45 .byte $00,$46,$1c,$1e,$00,$8f,$20,$43 .byte $4f,$4d,$4d,$4f,$44,$4f,$52,$45 .byte $20,$43,$31,$32,$38,$00,$51,$1c .byte $28,$00,$9e,$20,$37,$32,$35,$36 .byte $00,$00,$00 ; ; PROGRAM START ; *= $1c58 ; lda #$00 ;reset winner variable sta winner ; lda $d01e ;empty collision registers lda $d01f ; lda #$93 ;clear screen jsr $ffd2 lda #14 ;small charset jsr $ffd2 ; lda #$00 ;screen colors sta $d020 lda #$00 sta $d021 ; clc ;text position ldx #$0 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 loop lda mytxt,x cmp #$05 beq a5 jsr $ffd2 inx bne loop ; a5 jsr $ffe4 ; check SPACE cmp #32 ; if hit beq more ; continue bne a5 ; ; ; 13.05.2026 more jsr getspr ; sprite load ; ; to enable later lda #$01 ; set variable to 1 to avoid sta spritedone ; additional sprite loading ; ; sprites can be also loaded from ; ; BASIC with BLOAD"sprite*" command ; ;------------------------------------------------------------------------------- ;sprite activation and position ;------------------------------------------------------------------------------- ; ldy #$00 ;short delay w0 ldx #$00 w1 dex bne w1 dey bne w0 ; lda #$93 jsr $ffd2 ; ; jsr $77b3 ;fast mode / unused ; jsr $77c4 ;slow mode / unused ; lda #$01 ; sprite access=disable BASIC IRQ service sta $12fd ; some BASIC commands won't work now! ; lda #$0f ; sprite 0,1,2,3 sta $d015 ; bits 0 1 2 3 ; ; value 1 2 4 8 ; ; = 15, #$0f sta $d01b ; sprites to background (0-3 sprites) ; lda sprx ; the same x & y sta $d000 ; pos for all sta $d002 ; 3 sprites (0-2) sta $d004 lda spry sta $d001 sta $d003 sta $d005 ; lda #232 ;sprite 3 (4) location sta $d006 ;x lda #138 sta $d007 ;y ; lda #$07 ; sprite colors sta $d027 ; 0 lda #$04 sta $d028 ; 1 lda #$0b sta $d029 ; 2 ; lda #$03 ; color sprite 3 sta $d02a ; lda #142 ; capitol characters jsr $ffd2 ; to show the level data correctly ; ; rts ; temp usage for level tests ; ; LOAD one and only LEVEL! ; This could have been done in one go. ; We have learned that already, but me is I and I used this process, ; because why not! Hehe... ; However, thanks to all the bytes I am wasting, we will move ; our program to $2000, or graphic area dec 8192! ; ; NOTE: One line is 40 characters. That means we can display 6 full lines ; (6x40=240), however, our labyrinth has 30 characters per line and we ; can display 8 lines accordingly (8x30=240) ; ; 255 chars would go too, but making a level wouldn't be so easy then. ; as you would need to connect the data properly. ; clc ;position (left upper corner) ldx #$00 ;row [0-24] ldy #$00 ;column [0-39] jsr $c018 ; ldx #$00 ;level is divided in 3 areas b1 lda levp1,x ;as said, why not! cmp #$05 ;good wat to learn how it later beq part2 ;can be optimized. jsr $ffd2 inx bne b1 ;------------------------------------------------------------------------------- part2 clc ;position ldx #$8 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 b2 lda levp2,x cmp #$05 beq part3 jsr $ffd2 inx bne b2 ;------------------------------------------------------------------------------- part3 clc ;position ldx #$10 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 b3 lda levp3,x cmp #$05 beq checksys jsr $ffd2 inx bne b3 ; ; ;------------------------------------------------------------------------------- ;MAIN LOOP AREA ;------------------------------------------------------------------------------- checksys jsr environ ; main jsr checkcollision ; lda winner ;if EXIT is reached then exit/quit cmp #$01 beq gameover ; lda alive cmp #0 beq ohnooo ; lda $d4 ;read keyboard cmp #$55 ;input beq left ;all this should be pretty much cmp #$53 ;very clear beq up ;we are reading arrow keys status cmp #$56 ;and are branching accordingly beq right cmp #$54 beq down cmp #$3e ;quit with Q key (EXIT) beq exit ;you can add more if you want jmp main ;main loop ; ; left jmp goleft ;I am using this to avoid facing right jmp goright ;the limitations of the branching up jmp goup ;commands. Which is 127 bytes +- down jmp godown ;from the source jmp main ; ;final breath ohnooo jmp dropdead ; ;EXIT DEPARTMENT ;------------------------------------------------------------------------------- exit lda #$00 ;all sprites off sta $d015 ; ; lda #$93 jsr $ffd2 clc ldx #$0 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 loop8 lda chickenmsg,x cmp #$05 beq b8 jsr $ffd2 inx bne loop8 ; b8 jsr $ffe4 ;check SPACE cmp #32 ;if hit beq runner ;continue bne b8 runner lda #15 sta $f1 lda #$39 sta lives ;reset lives and status lda #$01 sta alive lda #$00 ;keyboard buffer = 0 sta $d0 ;dec 208 rts ;back to BASIC ; ;GAME OVER DEPARTMENT ;------------------------------------------------------------------------------- gameover lda #$00 sta $d015 ; lda #$93 jsr $ffd2 clc ldx #$0 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 loop5 lda endmsg,x cmp #$05 beq b7 jsr $ffd2 inx bne loop5 ; b7 jsr $ffe4 ;check SPACE cmp #32 ;if hit beq okidoki ;continue bne b7 okidoki lda #15 sta $f1 lda #$39 sta lives ;reset lives and status lda #$01 sta alive lda #$00 sta $d0 rts ;back to BASIC ; ; ;------------------------------------------------------------------------------- ;ALL LIVES ARE GONE! ;you didn't make it... what a bad luck! ; dropdead lda #$00 sta $d015 ; lda #$93 jsr $ffd2 clc ldx #$0 ;row [0-24] ldy #$0 ;column [0-39] jsr $c018 ; ldx #$00 loop6 lda finalwords,x cmp #$05 beq b6 jsr $ffd2 inx bne loop6 ; b6 jsr $ffe4 ;check SPACE cmp #32 ;if hit beq rip ;continue bne b6 rip lda #15 sta $f1 lda #$39 sta lives ;reset lives (9 lives like a cat!) lda #$01 sta alive ;and alive (1=alive, 0=dead) lda #$00 sta $d0 ;dec 208, keyboard buffer = 0 rts ;back to BASIC ; ;------------------------------------------------------------------------------- ; ;left jmp goleft ;I am using this to avoid facing ;right jmp goright ;the limitations of the branching ;up jmp goup ;commands. Which is 127 bytes +- ;down jmp godown ;from the source ; jmp main ; environ lda $d021 ;In case someone wants to change cmp #$00 ;the BG color, we will set it back to beq norepair ;black lda #$00 sta $d021 rts ; norepair rts ; ;------------------------------------------------------------------------------- ; branching with JMP is helping to avoid 127 bytes limitations of BEQ, BNE, BCC ; relative addressing. Just pay attention to JSR/RTS calls/returns. The JSR/RTS ; circles must be closed. Otherwise you'll return to BASIC and hava no idea why! ; sometimes when you do additionally branching after the JSR you must return ; with RTS, always. That's why it is important to keep an extra eye on that. ; there are also a few such cases here. So check them out! ; control subroutines ;------------------------------------------------------------------------------- ; goleft dec $d000 ;3 sprites are controlled here dec $d002 dec $d004 jsr delay lda #$01 ;we went left. this is used to jump back sta direction ;after the collision jmp main ; goright inc $d000 inc $d002 inc $d004 jsr delay lda #$03 ;we went up sta direction jmp main ; goup dec $d001 dec $d003 dec $d005 jsr delay lda #$02 ;we went right sta direction jmp main ; godown inc $d001 inc $d003 inc $d005 jsr delay lda #$04 ;we went down sta direction jmp main ; delay ldy slow ;how fast is our/light movement m0 ldx #$00 m1 dex bne m1 dey bne m0 rts ; delay2 ldy slow2 ;correction delay m2 ldx #$00 m3 dex bne m3 dey bne m2 rts ; ; Collision check ;------------------------------------------------------------------------------- checkcollision lda $d019 ;check the address $d019, 53273 and #$02 ;1st bit (val 2) indicates if a collision cmp #$02 ;registered. Then the result should be 2 beq collided ;if 2, was also sprite 0 hit? go check! ; lda $d019 ;sprite sprite collision and #$04 ;bit 2, value 4 cmp #$04 beq spritehit rts ;otherwise back to jsr call next command! ; ; ;sprite <> background (graphics) coll collided lda $d01f ;read the register showing how many and #$01 ;sprites were involved and check if also cmp #$01 ;sprite one (0) is part of the game! beq effect ;if yes, then do what's needed. jmp noeffect ; spritehit lda $d01e ;sprite <> sprite collision (0,1,2,3) and #$0f ;007 06 05 04 3 2 1 0 - bit cmp #$0f ;128 64 32 16 8 4 2 1 - value beq sprspr ; 0 0 0 0 1 1 1 1 = 15. $0f jmp noeffect ;if no, then do what's needed. ; effect ldx #$00 ;cycle ldy #60 ;do a little something just to show cycle ldy $d012 ;the effect and signal the collision cpy #60 bne cycle nop nop nop lda #$06 ;blue raster bar sta $d020 rast2 ldy $d012 cpy #80 bne rast2 nop nop nop lda #14 sta $d020 rast3 ldy $d012 cpy #100 bne rast3 nop nop nop lda #3 sta $d020 rast4 ldy $d012 cpy #120 bne rast4 nop nop nop lda #0 sta $d020 inx cpx #$30 bne cycle lda $d01f ;to make sure collisions registers ; lda $d01e ;are empty. ; ;life taker! ;one life less! be more careful! ; dec lives lda lives sta 1024+32 lda #13 sta 55296+32 lda lives cmp #$30 ;zero, 0 beq boothil ; jmp corrections rts ;jump back to JSR call (next command) ; boothil lda #0 sta alive ;if zero, you wasted all lives! rts ; noeffect lda $d01f ;read sprite coll data to reset ; lda $d01e ;you can never have enough of these. rts ;then back as it is not sprite 1 collisio ; sprspr lda #$01 ;set winner to 1 sta winner ;which will cause the branch to exit rts ;this game. Main Loop! ; corrections lda direction ;thanks to direction variable, cmp #$01 ;we know what was our last move and what beq leftside ;we need to do to jump away from the wall cmp #$02 ; beq upside ;this below is taking care if all cmp #$03 ;four directions beq rightside cmp #$04 beq downside ; leftside jmp leftcorr ;using this to make sure to NOT hit the upside jmp upcorr ;127 bytes branching limitation in cases rightside jmp rightcorr ;of direct branching with BEQ, BNE etc. downside jmp downcorr ; leftcorr ldx #$00 ;increment sprite pos x position with 2 aa1 inc $d000 ;basically, if our last move that caused inc $d002 ;a collision was to the left, we need to inc $d004 ;increment the value to go back to the inx ;to the right! cpx #$02 ; bne aa1 ;the same for all other directions jsr delay2 rts ; rightcorr ldx #$00 aa2 dec $d000 dec $d002 dec $d004 inx cpx #$02 bne aa2 jsr delay2 rts ; upcorr ldx #$00 aa3 inc $d001 inc $d003 inc $d005 inx cpx #$02 bne aa3 jsr delay2 rts ; downcorr ldx #$00 aa4 dec $d001 dec $d003 dec $d005 inx cpx #$02 bne aa4 jsr delay2 rts ; ; ;------------------------------------------------------------------------------- ;Data & Co Production LTD. Department ;------------------------------------------------------------------------------- ; ; sprite loading lines ; getspr lda spritedone cmp #$01 beq spriteback ldx #$00 ;banknumber jsr getcfg ;mmu-config jsr setbnk ;set bank lda #$01 ;logical fil name ldx #$08 ;device address ldy #$00 ;secondary add. jsr setlfs ;set config lda #$0c ;file length ldx #loadname ;high-byte name jsr setnam ;set name lda #$00 ;verify (1=yes) ldx #gospr ;high-byte start jmp loadsp ;load & close ; loadname .text "sprites01.bl" ; file ; spriteback rts ; if sprite is loaded (1), go back spritedone .byte 0 ; ; showing off a little bit here, hehe! ; mytxt .text $9c,"LAB",$9b,"yrinth " .text $9c,"ESC",$9b,"ape",13 .text "Commodore C128 Game",13,13 .text "----------------------------------",13,13 .text "(c) 2026, May",13 .text "www.lived.ch",13 .text "www.mzretro.ch",13,13 .text 154,"Programmed by:" .text "Misel Zivanovic",13,13,$9b .text 159,"ARROW KEYS to navigate.",13 .text "Q to Quit the game.",13,13 .text $1e,"HIT SPACE",$05 ; ;Chicken Message chickenmsg .text 28,14,"OH, LOOK AT HERE, A CHICKEN!",13,13 .text 159,"Wants to run away without finishing",13 .text "the masterful ESCAPE-PLAN!",13,13 .text "Well, your bad!",13 .text "You are seriously missing out here!",13,13 .text $1e,"HIT SPACE! [ Chicken... :-) ]",$05 ; ;Game Over (meaning: you win) endmsg .text 28,14,"NOW WE ARE TALKING...",13,13 .text 159,"We have a golden winner!",13 .text "You indeed managed to escape! WOW!",13,13 .text "Have a pleasant day...",13 .text "And enjoy your freedom...",13,13 .text $1e,"HIT SPACE!",$05 ; ;RIP! Someone didn't make it! finalwords .text 28,14,"DEAD - RIP!",13,13 .text 159,"What the heck!?",13 .text "Everyone, don't blame me!",13,13 .text "Who coded this game?",13 .text "Narrow walls, lame collision detection,",13 .text "lame interrupt bars, stupid light!",13,13 .text "You see...!",13 .text "Definitely not my fault!",13,13 .text "Hmmm...",13 .text "Mother, i've convinced them, hehe...!",13,13 .text $1e,"HIT SPACE",$05 ; ;sprite position sprx .byte 28 spry .byte 160 ; ;sprite movement speed. higher values are slower speed! slow .byte 30 slow2 .byte 50 ; ;color / unused! color .byte 14,15,13,12,13,15,14,15,13,15,13,15,14,15,13,12 ; ;collision status collstatus .byte 0 ; ;correction values xyback .byte 3 ; ;where are we going? 1=left, 2=up, 3=right, 4=down direction .byte 0 ; ;if EXIT sprite was hit, then this will turn to 1 and we won! winner .byte 0 ; ;9 lives to make it interesting (should be enough) ;collision is 1 life less! lives .byte $39 alive .byte 1 ; ;------------------------------------------------------------------------------- ;to see the level data correctly, switch to capitol characters! ;------------------------------------------------------------------------------- ; levp1 .text $99 .text "Blabyrinth escape! mzretro.chB",159," [ ]",13,144 .text "KCCCCCCCCCCCCCCCCCCCCCCCCCCCCS",13 .text "KCCCCCCCCCRC CCC C CCRCCCCCS",13 .text "B CCCCCCCCQCCCC CCCCCCC? CN B",13 .text "B CCCCCCN PCCCC PCN PCCRCCC? B",13 .text "B PCCCC B KCCCRCS B B B PCN B",13 .text "B MCCCCCQCQCN B B B B",13 .text "B CCCCC PCCC? B KCQCQCC? B B B",$05 levp2 .text "B CCCCCCS CCCCS MCCCCCCC MC? B",13 .text "B B B B",13 .text "B CCCCRCQCCCC MCCN CCCCCCCCC B",13 .text "B PCCCQCCCCCN PCCQCCCCCCCCCCCS",13 .text "B KCCCCCCCC B CCCRCCCN B",13 .text "B MN P N CCCCQR CCQCN KCCCCCS",13 .text "B B B B CCCCCS PCN MCCCC B",13 .text "B B MCCQCCCCCCQC? MCRCCCCC B",$05 levp3 .text "KCC? CCCC PCCCCCCCC MCRC PCCS",13 .text "B PCCCQCCN PCCCCCN B M B",13 .text "B B B B P S B CRC B MCRCN B",13 .text "B B B B B MCS B MCCCS B",13 .text "B B MC?CS B CN B KCCCCC B PCS",13 .text "B B B B KCC?CS CQCCCCN B B B",13 .text "B B B B CCCCCCS Q B",13 .text "MCQCQCCCQCQCCCCQCCCCCCCQCCCQC?",$05