initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
*.prg
|
||||
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "deps",
|
||||
"type": "shell",
|
||||
"command": "make deps",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "build",
|
||||
"type": "shell",
|
||||
"command": "make build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "make run",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "clear",
|
||||
"type": "shell",
|
||||
"command": "make clear",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "rebuild",
|
||||
"type": "shell",
|
||||
"command": "make rebuild",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "rebuildrun",
|
||||
"type": "shell",
|
||||
"command": "make rebuildrun",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
steps:
|
||||
- name: version
|
||||
image: alpine
|
||||
commands:
|
||||
- 'apk add --no-cache make'
|
||||
- 'make ci-version'
|
||||
|
||||
- name: build
|
||||
image: debian:stable-slim
|
||||
commands:
|
||||
- 'apt-get update && apt-get install -y --no-install-recommends make acme'
|
||||
- 'make ci-build'
|
||||
|
||||
- name: artifact
|
||||
image: alpine
|
||||
environment:
|
||||
DROPAREA_HOST: vps.teletype.hu
|
||||
DROPAREA_PORT: 2223
|
||||
DROPAREA_TARGET_PATH: /home/drop
|
||||
DROPAREA_USER: drop
|
||||
DROPAREA_SSH_PASSWORD:
|
||||
from_secret: droparea_ssh_password
|
||||
commands:
|
||||
- 'apk add --no-cache make openssh-client sshpass'
|
||||
- 'make ci-artifact'
|
||||
|
||||
- name: update
|
||||
image: alpine
|
||||
environment:
|
||||
UPDATE_SERVER: https://games.vps.teletype.hu
|
||||
UPDATE_SECRET:
|
||||
from_secret: update_secret_key
|
||||
commands:
|
||||
- 'apk add --no-cache make curl'
|
||||
- 'make ci-update'
|
||||
@@ -0,0 +1,91 @@
|
||||
# -----------------------------------------
|
||||
# Makefile – C64 project builder (ACME + VICE)
|
||||
# -----------------------------------------
|
||||
|
||||
PROJECT = labyrinth
|
||||
|
||||
ASM = acme
|
||||
EMU = x64sc
|
||||
SRC = main.asm
|
||||
SRCS = $(wildcard *.asm)
|
||||
TARGET ?= $(PROJECT).prg
|
||||
|
||||
METADATA = metadata.json
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
||||
# CI/CD variables
|
||||
VERSION_FILE = .version
|
||||
DROPAREA_HOST ?= vps.teletype.hu
|
||||
DROPAREA_PORT ?= 2223
|
||||
DROPAREA_TARGET_PATH ?= /home/drop
|
||||
DROPAREA_USER ?= drop
|
||||
UPDATE_SERVER ?= https://games.vps.teletype.hu
|
||||
|
||||
all: build
|
||||
|
||||
deps:
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
@command -v brew >/dev/null 2>&1 || { echo "Homebrew kell ehhez. Telepitsd: https://brew.sh"; exit 1; }
|
||||
brew install acme vice
|
||||
else
|
||||
@echo "A 'deps' target csak macOS-en (Darwin) automatikus. Detektalt OS: $(UNAME_S)"
|
||||
@echo "Telepitsd kezzel: acme assembler + VICE emulator (x64sc)."
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
build: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRCS)
|
||||
$(ASM) -f cbm -o $(TARGET) $(SRC)
|
||||
|
||||
run: build
|
||||
$(EMU) $(TARGET)
|
||||
|
||||
clear:
|
||||
rm -f $(TARGET) $(PROJECT)-*.prg $(PROJECT)-*.metadata.json $(VERSION_FILE)
|
||||
|
||||
rebuild: clear build
|
||||
|
||||
rebuildrun: clear build run
|
||||
|
||||
# -----------------------------------------
|
||||
# CI/CD Pipeline targets
|
||||
# -----------------------------------------
|
||||
|
||||
ci-version:
|
||||
@VERSION=$$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' $(METADATA) | head -n 1); \
|
||||
if [ -z "$$VERSION" ]; then \
|
||||
echo "ERROR: no \"version\" field in $(METADATA)!"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
BRANCH=$${CI_COMMIT_BRANCH:-$${WOODPECKER_BRANCH}}; \
|
||||
BRANCH=$$(echo "$$BRANCH" | tr '/' '-'); \
|
||||
if [ "$$BRANCH" != "main" ] && [ "$$BRANCH" != "master" ] && [ -n "$$BRANCH" ]; then \
|
||||
VERSION=dev-$$VERSION-$$BRANCH; \
|
||||
fi; \
|
||||
echo "VERSION is: $$VERSION"; \
|
||||
echo $$VERSION > $(VERSION_FILE)
|
||||
|
||||
ci-build: build
|
||||
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||
echo "==> Creating versioned files for $$VERSION"; \
|
||||
cp $(TARGET) $(PROJECT)-$$VERSION.prg; \
|
||||
cp $(METADATA) $(PROJECT)-$$VERSION.metadata.json; \
|
||||
ls -lh $(PROJECT)-$$VERSION.*
|
||||
|
||||
ci-artifact:
|
||||
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||
echo "==> Uploading artifacts for version $$VERSION"; \
|
||||
SCP_TARGET="$(DROPAREA_USER)@$(DROPAREA_HOST):$(DROPAREA_TARGET_PATH)/"; \
|
||||
sshpass -p "$(DROPAREA_SSH_PASSWORD)" scp -o StrictHostKeyChecking=no -P $(DROPAREA_PORT) \
|
||||
$(PROJECT)-$$VERSION.prg \
|
||||
$(PROJECT)-$$VERSION.metadata.json \
|
||||
$$SCP_TARGET
|
||||
|
||||
ci-update:
|
||||
@VERSION=$$(cat $(VERSION_FILE)); \
|
||||
echo "==> Triggering update for version $$VERSION"; \
|
||||
curl "$(UPDATE_SERVER)/update?secret=$(UPDATE_SECRET)&name=$(PROJECT)&platform=c64&version=$$VERSION"
|
||||
|
||||
.PHONY: all deps build run clear rebuild rebuildrun ci-version ci-build ci-artifact ci-update
|
||||
@@ -0,0 +1,47 @@
|
||||
# labyrinth
|
||||
|
||||
Two-player maze duel for the Commodore 64 (ACME + VICE), in the same
|
||||
spirit as [rabbitc64](../rabbitc64).
|
||||
|
||||
A fresh maze is carved every round on a 39x23 lattice where the walls
|
||||
are exactly as wide as the corridors. **Player 1** (joystick 2, or
|
||||
`w a s d`) runs the corridors toward the green diamond exit while
|
||||
dodging the roaming colored enemies. **Player 2** (joystick 1, or
|
||||
`i j k l`) rides the corners of the walls with a blinking cursor -
|
||||
fire (or `return`) spins the four wall pieces around the cursor by
|
||||
90 degrees, reshaping the maze in real time. A spin is denied while
|
||||
anyone is standing in one of the four slots.
|
||||
|
||||
Screens: TTG intro (ported from rabbitc64) -> main menu
|
||||
(play / rules / controls / credits) -> game -> win (back to the menu)
|
||||
or game over (fire retries with a fresh maze, `m` backs out to the
|
||||
menu).
|
||||
|
||||
## Build & run
|
||||
|
||||
```
|
||||
make deps # macOS: brew install acme vice
|
||||
make build # -> labyrinth.prg
|
||||
make run # build + launch in x64sc
|
||||
```
|
||||
|
||||
Test builds can boot straight into one state:
|
||||
|
||||
```
|
||||
acme -DAUTOPLAY=1 -f cbm -o labyrinth.prg main.asm
|
||||
# also: AUTORULES, AUTOCONTROLS, AUTOCREDITS, AUTOWIN, AUTOLOSE
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
- `main.asm` - entry point, BASIC stub, per-frame state machine
|
||||
- `defs.asm` - registers, constants, zero page, macros
|
||||
- `intro.asm` - TTG boot logo with the gold color wash
|
||||
- `menu.asm` - main menu card
|
||||
- `rules.asm` - how to play
|
||||
- `controls.asm` - joystick + keyboard mappings
|
||||
- `credits.asm` - the crew
|
||||
- `play.asm` - maze generation (recursive backtracker) + the game
|
||||
- `win.asm` / `over.asm` - end screens
|
||||
- `common.asm` - shared routines: printing, big block font, input, RNG
|
||||
- `sound.asm` - single-voice SID SFX driver
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
; -----------------------------------------------------------
|
||||
; common.asm - routines shared by the states: screen drawing,
|
||||
; text printing, big block font, input and the RNG
|
||||
; (ported from rabbitc64, trimmed to what this game needs)
|
||||
; -----------------------------------------------------------
|
||||
|
||||
; ---- clear screen RAM, fill color RAM with black ----
|
||||
clear_screen:
|
||||
ldx #0
|
||||
cls_loop:
|
||||
lda #$20 ; space
|
||||
sta SCREEN,x
|
||||
sta SCREEN+$100,x
|
||||
sta SCREEN+$200,x
|
||||
sta SCREEN+$2e8,x
|
||||
lda #BLACK
|
||||
sta COLRAM,x
|
||||
sta COLRAM+$100,x
|
||||
sta COLRAM+$200,x
|
||||
sta COLRAM+$2e8,x
|
||||
inx
|
||||
bne cls_loop
|
||||
rts
|
||||
|
||||
; ---- print a text line: txtlo/hi, prow, pcol, pcolor, plen ----
|
||||
print_text:
|
||||
ldx prow
|
||||
lda mul40lo,x
|
||||
clc
|
||||
adc pcol
|
||||
sta scrlo
|
||||
sta collo
|
||||
lda mul40hi,x
|
||||
adc #>SCREEN ; carry still valid from the low-byte add
|
||||
sta scrhi
|
||||
clc
|
||||
adc #$d4 ; COLRAM - SCREEN high byte distance
|
||||
sta colhi
|
||||
ldy #0
|
||||
pt_loop:
|
||||
lda (txtlo),y
|
||||
sta (scrlo),y
|
||||
lda pcolor
|
||||
sta (collo),y
|
||||
iny
|
||||
cpy plen
|
||||
bne pt_loop
|
||||
rts
|
||||
|
||||
; ---- set scrlo/hi + collo/hi to the start of char row X ----
|
||||
row_ptr:
|
||||
lda mul40lo,x
|
||||
sta scrlo
|
||||
sta collo
|
||||
lda mul40hi,x
|
||||
clc
|
||||
adc #>SCREEN
|
||||
sta scrhi
|
||||
clc
|
||||
adc #$d4 ; COLRAM - SCREEN high byte distance
|
||||
sta colhi
|
||||
rts
|
||||
|
||||
; ---- input ----
|
||||
; joystick 1 shares the CIA port B lines the keyboard rows use,
|
||||
; so before any key scan we note which rows the stick itself is
|
||||
; grounding (jmask) and treat those rows as released - a held
|
||||
; joystick 1 direction must not read as a phantom keypress
|
||||
|
||||
; read joystick port 2 merged with w/a/s/d + space (the runner),
|
||||
; active-high bits in A and joyb
|
||||
read_joy:
|
||||
lda #$ff ; deselect every keyboard column first
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
eor #$ff
|
||||
and #$1f
|
||||
sta jmask ; rows grounded by joystick 1 right now
|
||||
lda CIA1_PA
|
||||
eor #$ff
|
||||
and #$1f
|
||||
sta joyb ; joystick 2 bits
|
||||
lda #$fd ; keyboard column 1: w / a / s
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$02 ; 'w' = up: row bit 1
|
||||
bne rj_now
|
||||
lda joyb
|
||||
ora #JOY_UP
|
||||
sta joyb
|
||||
rj_now:
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$04 ; 'a' = left: row bit 2
|
||||
bne rj_noa
|
||||
lda joyb
|
||||
ora #JOY_LEFT
|
||||
sta joyb
|
||||
rj_noa:
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$20 ; 's' = down: row bit 5
|
||||
bne rj_nos
|
||||
lda joyb
|
||||
ora #JOY_DOWN
|
||||
sta joyb
|
||||
rj_nos:
|
||||
lda #$fb ; 'd' = right: keyboard column 2, row bit 2
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$04
|
||||
bne rj_nod
|
||||
lda joyb
|
||||
ora #JOY_RIGHT
|
||||
sta joyb
|
||||
rj_nod:
|
||||
lda #$7f ; space = fire: keyboard column 7, row bit 4
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$10
|
||||
bne rj_nosp
|
||||
lda joyb
|
||||
ora #JOY_FIRE
|
||||
sta joyb
|
||||
rj_nosp:
|
||||
lda #$ff
|
||||
sta CIA1_PA
|
||||
lda joyb
|
||||
rts
|
||||
|
||||
; read joystick port 1 merged with i/j/k/l + return (the wall
|
||||
; spinner), active-high bits in A and joyb2
|
||||
read_joy2:
|
||||
lda #$ff ; no column selected: port B = joystick 1
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
eor #$ff
|
||||
and #$1f
|
||||
sta joyb2 ; joystick 1 bits
|
||||
sta jmask ; ... which also ground the key rows below
|
||||
lda #$ef ; keyboard column 4: i / j / k
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$02 ; 'i' = up: row bit 1
|
||||
bne rk_noi
|
||||
lda joyb2
|
||||
ora #JOY_UP
|
||||
sta joyb2
|
||||
rk_noi:
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$04 ; 'j' = left: row bit 2
|
||||
bne rk_noj
|
||||
lda joyb2
|
||||
ora #JOY_LEFT
|
||||
sta joyb2
|
||||
rk_noj:
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$20 ; 'k' = down: row bit 5
|
||||
bne rk_nok
|
||||
lda joyb2
|
||||
ora #JOY_DOWN
|
||||
sta joyb2
|
||||
rk_nok:
|
||||
lda #$df ; 'l' = right: keyboard column 5, row bit 2
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$04
|
||||
bne rk_nol
|
||||
lda joyb2
|
||||
ora #JOY_RIGHT
|
||||
sta joyb2
|
||||
rk_nol:
|
||||
lda #$fe ; return = fire: keyboard column 0, row bit 1
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
ora jmask
|
||||
and #$02
|
||||
bne rk_noret
|
||||
lda joyb2
|
||||
ora #JOY_FIRE
|
||||
sta joyb2
|
||||
rk_noret:
|
||||
lda #$ff
|
||||
sta CIA1_PA
|
||||
lda joyb2
|
||||
rts
|
||||
|
||||
; fire held on either side? (stick fire, space or return)
|
||||
; returns A != 0 when pressed
|
||||
start_pressed:
|
||||
jsr read_joy
|
||||
and #JOY_FIRE
|
||||
bne sp_done
|
||||
jsr read_joy2
|
||||
and #JOY_FIRE
|
||||
sp_done:
|
||||
rts
|
||||
|
||||
; edge-detected start: A = 1 only on the frame fire/space goes down
|
||||
start_edge:
|
||||
jsr start_pressed
|
||||
beq se_up
|
||||
lda fheld
|
||||
bne se_no
|
||||
lda #1
|
||||
sta fheld
|
||||
rts ; A = 1: fresh press
|
||||
se_up:
|
||||
lda #0
|
||||
sta fheld
|
||||
se_no:
|
||||
lda #0
|
||||
rts
|
||||
|
||||
; ---- RNG: 8-bit LFSR stirred with the free-running CIA timer
|
||||
; (the timer counts every cycle, so call timing = entropy) ----
|
||||
rand8:
|
||||
lda seed
|
||||
asl
|
||||
bcc r8_tap
|
||||
eor #$1d
|
||||
r8_tap:
|
||||
eor CIA1_TA
|
||||
sta seed
|
||||
rts
|
||||
|
||||
; ---- draw a text line with big 3x5 block letters ----
|
||||
; txtlo/hi = text (screen codes), bigrow/bigcol = top-left cell,
|
||||
; bigcolor = color, biglen = char count; each letter is 3 cells
|
||||
; wide + 1 gap, 5 cells tall
|
||||
draw_big:
|
||||
lda #0
|
||||
sta bigi
|
||||
db_char:
|
||||
ldy bigi
|
||||
lda (txtlo),y
|
||||
cmp #$20 ; space: leave the cell empty
|
||||
beq db_next
|
||||
tax
|
||||
lda bigmap,x ; screen code -> glyph index
|
||||
sta glyphbase
|
||||
asl ; glyph base = index * 5
|
||||
asl
|
||||
clc
|
||||
adc glyphbase
|
||||
sta glyphbase
|
||||
lda bigi ; cell x = bigcol + charindex * 4
|
||||
asl
|
||||
asl
|
||||
clc
|
||||
adc bigcol
|
||||
sta charx
|
||||
lda #0
|
||||
sta rowidx
|
||||
db_row:
|
||||
lda bigrow ; row pointers from the row*40 tables
|
||||
clc
|
||||
adc rowidx
|
||||
tax
|
||||
lda mul40lo,x
|
||||
sta scrlo
|
||||
sta collo
|
||||
lda mul40hi,x
|
||||
clc
|
||||
adc #>SCREEN
|
||||
sta scrhi
|
||||
clc
|
||||
adc #$d4
|
||||
sta colhi
|
||||
lda glyphbase
|
||||
clc
|
||||
adc rowidx
|
||||
tax
|
||||
lda bigglyphs,x
|
||||
asl ; 3-bit pattern up to bits 7-5
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
asl
|
||||
sta curbits
|
||||
ldy charx
|
||||
lda #3
|
||||
sta bitcnt
|
||||
db_bit:
|
||||
asl curbits ; leftmost pixel first
|
||||
bcc db_nobit
|
||||
lda #$a0 ; inverse space = solid block
|
||||
sta (scrlo),y
|
||||
lda bigcolor
|
||||
sta (collo),y
|
||||
db_nobit:
|
||||
iny
|
||||
dec bitcnt
|
||||
bne db_bit
|
||||
inc rowidx
|
||||
lda rowidx
|
||||
cmp #5
|
||||
bne db_row
|
||||
db_next:
|
||||
inc bigi
|
||||
lda bigi
|
||||
cmp biglen
|
||||
beq db_done
|
||||
jmp db_char ; too far for a relative branch
|
||||
db_done:
|
||||
rts
|
||||
|
||||
; ---- shared tables ----
|
||||
|
||||
; row * 40 lookup tables (25 rows)
|
||||
mul40lo:
|
||||
!byte $00, $28, $50, $78, $a0, $c8, $f0, $18, $40, $68
|
||||
!byte $90, $b8, $e0, $08, $30, $58, $80, $a8, $d0, $f8
|
||||
!byte $20, $48, $70, $98, $c0
|
||||
mul40hi:
|
||||
!byte $00, $00, $00, $00, $00, $00, $00, $01, $01, $01
|
||||
!byte $01, $01, $01, $02, $02, $02, $02, $02, $02, $02
|
||||
!byte $03, $03, $03, $03, $03
|
||||
|
||||
; 3x5 block font for the big titles (5 bytes per glyph,
|
||||
; low 3 bits used, MSB side = left)
|
||||
bigglyphs:
|
||||
!byte 0, 0, 0, 0, 0 ; 0 space
|
||||
!byte 2, 5, 7, 5, 5 ; 1 A
|
||||
!byte 3, 4, 4, 4, 3 ; 2 C
|
||||
!byte 6, 5, 5, 5, 6 ; 3 D
|
||||
!byte 7, 4, 6, 4, 7 ; 4 E
|
||||
!byte 7, 4, 6, 4, 4 ; 5 F
|
||||
!byte 7, 2, 2, 2, 7 ; 6 I
|
||||
!byte 4, 4, 4, 4, 7 ; 7 L
|
||||
!byte 5, 7, 7, 5, 5 ; 8 M
|
||||
!byte 6, 5, 5, 5, 5 ; 9 N
|
||||
!byte 7, 5, 5, 5, 7 ; 10 O
|
||||
!byte 6, 5, 6, 4, 4 ; 11 P
|
||||
!byte 6, 5, 6, 5, 5 ; 12 R
|
||||
!byte 3, 4, 2, 1, 6 ; 13 S
|
||||
!byte 7, 2, 2, 2, 2 ; 14 T
|
||||
!byte 5, 5, 2, 2, 2 ; 15 Y
|
||||
!byte 3, 4, 5, 5, 7 ; 16 G
|
||||
!byte 6, 5, 6, 5, 6 ; 17 B
|
||||
!byte 5, 5, 5, 5, 2 ; 18 V
|
||||
!byte 5, 5, 7, 5, 5 ; 19 H
|
||||
!byte 5, 5, 5, 5, 7 ; 20 U
|
||||
!byte 5, 5, 7, 7, 5 ; 21 W
|
||||
|
||||
bigmap: ; screen code (a=1..z=26) -> glyph index
|
||||
!byte 0 ; (unused)
|
||||
!byte 1, 17, 2, 3, 4, 5, 16, 19, 6, 0 ; a-j
|
||||
!byte 0, 7, 8, 9, 10, 11, 0, 12, 13, 14 ; k-t
|
||||
!byte 20, 18, 21, 0, 15, 0 ; u-z
|
||||
|
||||
; ---- shared texts (screen codes) ----
|
||||
txt_title: !scr "labyrinth"
|
||||
txt_ttgname: !scr "teletype games"
|
||||
txt_presents:!scr "presents"
|
||||
txt_return: !scr "fire = return"
|
||||
txt_blank: !fill 16, $20
|
||||
|
||||
washtbl: ; gold glow ramp (16 entries, cyclic)
|
||||
!byte $09, $02, $08, $0a, $07, $07, $01, $01
|
||||
!byte $01, $01, $07, $07, $0a, $08, $02, $09
|
||||
|
||||
; ---- shared variables ----
|
||||
framectr: !byte 0 ; frame counter, incremented by the main loop
|
||||
washphase: !byte 0 ; gold wash offset on the big letters
|
||||
seed: !byte $a7 ; rand8 LFSR state
|
||||
fheld: !byte 0 ; fire/space edge detection state
|
||||
joyb: !byte 0 ; player 1 input bits from read_joy
|
||||
joyb2: !byte 0 ; player 2 input bits from read_joy2
|
||||
jmask: !byte 0 ; key rows grounded by joystick 1 this scan
|
||||
prow: !byte 0 ; print_text: row, column, color, length
|
||||
pcol: !byte 0
|
||||
pcolor: !byte 0
|
||||
plen: !byte 0
|
||||
bigrow: !byte 0 ; draw_big: row, column, color, length
|
||||
bigcol: !byte 0
|
||||
bigcolor: !byte 0
|
||||
biglen: !byte 0
|
||||
bigi: !byte 0 ; draw_big internals
|
||||
glyphbase: !byte 0
|
||||
charx: !byte 0
|
||||
rowidx: !byte 0
|
||||
bitcnt: !byte 0
|
||||
curbits: !byte 0
|
||||
rtmp: !byte 0 ; scratch
|
||||
rtmp2: !byte 0
|
||||
@@ -0,0 +1,52 @@
|
||||
; -----------------------------------------------------------
|
||||
; controls.asm - who steers what: joystick and keyboard
|
||||
; mappings for both players, one static card; fire returns
|
||||
; -----------------------------------------------------------
|
||||
|
||||
controls_init:
|
||||
jsr clear_screen
|
||||
|
||||
+prbig 1, 4, WHITE, txt_controls, 8
|
||||
|
||||
+prtext 8, 9, LGREEN, txt_c1, txt_c1e-txt_c1
|
||||
+prtext 10, 6, WHITE, txt_c2, txt_c2e-txt_c2
|
||||
|
||||
+prtext 13, 6, LBLUE, txt_c3, txt_c3e-txt_c3
|
||||
+prtext 15, 6, WHITE, txt_c4, txt_c4e-txt_c4
|
||||
+prtext 17, 10, WHITE, txt_c5, txt_c5e-txt_c5
|
||||
|
||||
+prtext 20, 5, LGREY, txt_c6, txt_c6e-txt_c6
|
||||
|
||||
+setupd controls_update
|
||||
rts
|
||||
|
||||
controls_update:
|
||||
; blink the return prompt
|
||||
lda framectr
|
||||
and #32
|
||||
bne co_hide
|
||||
+prtext 22, 13, YELLOW, txt_return, 13
|
||||
jmp co_input
|
||||
co_hide:
|
||||
+prtext 22, 13, YELLOW, txt_blank, 13
|
||||
co_input:
|
||||
jsr start_edge
|
||||
beq co_stay
|
||||
jmp menu_init
|
||||
co_stay:
|
||||
rts
|
||||
|
||||
; ---- controls texts ----
|
||||
txt_controls: !scr "controls"
|
||||
txt_c1: !scr "player 1 - the runner"
|
||||
txt_c1e:
|
||||
txt_c2: !scr "move: joystick 2 or w a s d"
|
||||
txt_c2e:
|
||||
txt_c3: !scr "player 2 - the wall spinner"
|
||||
txt_c3e:
|
||||
txt_c4: !scr "move: joystick 1 or i j k l"
|
||||
txt_c4e:
|
||||
txt_c5: !scr "spin: fire or return"
|
||||
txt_c5e:
|
||||
txt_c6: !scr "menus: fire, space or return"
|
||||
txt_c6e:
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
; -----------------------------------------------------------
|
||||
; credits.asm - the same crew as rabbitc64, but a new look:
|
||||
; rainbow bars scrolling along the top and bottom edges in
|
||||
; opposite directions, a rainbow sweep across the big
|
||||
; CREDITS, each crew line in its own fixed color and the
|
||||
; ai warning pulsing grey-white (no scroller, no parade)
|
||||
; -----------------------------------------------------------
|
||||
|
||||
credits_init:
|
||||
jsr clear_screen
|
||||
|
||||
; rainbow bars: solid blocks across rows 0 and 24
|
||||
ldx #39
|
||||
ci_bars:
|
||||
lda #$a0
|
||||
sta SCREEN,x
|
||||
sta SCREEN+24*40,x
|
||||
dex
|
||||
bpl ci_bars
|
||||
|
||||
+prbig 2, 6, WHITE, txt_credits, 7
|
||||
|
||||
+prtext 9, 13, WHITE, txt_ttgname, 14
|
||||
|
||||
+prtext 12, 8, YELLOW, txt_crew0, txt_crew0e-txt_crew0
|
||||
+prtext 13, 10, LGREEN, txt_crew1, txt_crew1e-txt_crew1
|
||||
+prtext 14, 9, CYAN, txt_crew2, txt_crew2e-txt_crew2
|
||||
+prtext 15, 10, LRED, txt_crew3, txt_crew3e-txt_crew3
|
||||
|
||||
+prtext 17, 9, LGREY, txt_ai1, txt_ai1e-txt_ai1
|
||||
+prtext 18, 12, LGREY, txt_ai2, txt_ai2e-txt_ai2
|
||||
|
||||
lda #0
|
||||
sta barphase
|
||||
+setupd credits_update
|
||||
rts
|
||||
|
||||
credits_update:
|
||||
; the edge bars crawl in opposite directions
|
||||
lda framectr
|
||||
and #3
|
||||
bne cu_barsdone
|
||||
inc barphase
|
||||
cu_barsdone:
|
||||
ldx #39
|
||||
cu_bars:
|
||||
txa
|
||||
clc
|
||||
adc barphase
|
||||
and #7
|
||||
tay
|
||||
lda raintbl,y
|
||||
sta COLRAM,x ; top bar: colors drift left
|
||||
lda barphase
|
||||
stx rtmp
|
||||
sec
|
||||
sbc rtmp
|
||||
and #7
|
||||
tay
|
||||
lda raintbl,y
|
||||
sta COLRAM+24*40,x ; bottom bar: colors drift right
|
||||
dex
|
||||
bpl cu_bars
|
||||
|
||||
; rainbow sweep across the big CREDITS (rows 2-6)
|
||||
lda #0
|
||||
sta rtmp2
|
||||
cu_sweep:
|
||||
lda #2
|
||||
clc
|
||||
adc rtmp2
|
||||
tax
|
||||
jsr row_ptr
|
||||
lda framectr
|
||||
lsr
|
||||
lsr
|
||||
clc
|
||||
adc rtmp2
|
||||
and #7
|
||||
tax
|
||||
lda raintbl,x
|
||||
sta rtmp
|
||||
ldy #32 ; the letters span columns 6-32
|
||||
cu_scol:
|
||||
lda (scrlo),y
|
||||
cmp #$a0 ; only touch the letter blocks
|
||||
bne cu_sskip
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
cu_sskip:
|
||||
dey
|
||||
cpy #5
|
||||
bne cu_scol
|
||||
inc rtmp2
|
||||
lda rtmp2
|
||||
cmp #5
|
||||
bne cu_sweep
|
||||
|
||||
; the ai warning pulses grey-white
|
||||
lda framectr
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
and #7
|
||||
tax
|
||||
lda pulsetbl,x
|
||||
sta rtmp
|
||||
ldx #17
|
||||
jsr row_ptr
|
||||
ldy #30
|
||||
cu_p1:
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
dey
|
||||
cpy #8
|
||||
bne cu_p1
|
||||
ldx #18
|
||||
jsr row_ptr
|
||||
ldy #27
|
||||
cu_p2:
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
dey
|
||||
cpy #11
|
||||
bne cu_p2
|
||||
|
||||
; blink the return prompt
|
||||
lda framectr
|
||||
and #32
|
||||
bne cu_hide
|
||||
+prtext 21, 13, YELLOW, txt_return, 13
|
||||
jmp cu_input
|
||||
cu_hide:
|
||||
+prtext 21, 13, YELLOW, txt_blank, 13
|
||||
cu_input:
|
||||
jsr start_edge
|
||||
beq cu_stay
|
||||
jmp menu_init ; back to the menu
|
||||
cu_stay:
|
||||
rts
|
||||
|
||||
; ---- tables ----
|
||||
pulsetbl: ; grey-white pulse (8 entries, cyclic)
|
||||
!byte $0b, $0c, $0f, $01, $01, $0f, $0c, $0b
|
||||
|
||||
raintbl: ; rainbow ramp
|
||||
!byte $02, $08, $07, $0d, $03, $0e, $04, $0a
|
||||
|
||||
; ---- the crew (content ported from rabbitc64) ----
|
||||
txt_crew0: !scr "mr.zero - tasnadi zsolt"
|
||||
txt_crew0e:
|
||||
txt_crew1: !scr "mr.one - tari balazs"
|
||||
txt_crew1e:
|
||||
txt_crew2: !scr "mr.two - timar zoltan"
|
||||
txt_crew2e:
|
||||
txt_crew3: !scr "mr.three - mezo bela"
|
||||
txt_crew3e:
|
||||
txt_ai1: !scr "warning: this game was"
|
||||
txt_ai1e:
|
||||
txt_ai2: !scr "written by an ai"
|
||||
txt_ai2e:
|
||||
|
||||
; ---- credits variables ----
|
||||
barphase: !byte 0 ; edge bar scroll offset
|
||||
@@ -0,0 +1,137 @@
|
||||
; -----------------------------------------------------------
|
||||
; defs.asm - hardware registers, constants, zero page, macros
|
||||
; included first by main.asm
|
||||
; -----------------------------------------------------------
|
||||
|
||||
; ---- hardware registers ----
|
||||
RASTER = $d012 ; current raster line
|
||||
BORDER = $d020 ; border color register
|
||||
BG = $d021 ; background color register
|
||||
SCREEN = $0400 ; screen RAM (40x25 chars)
|
||||
COLRAM = $d800 ; color RAM
|
||||
SPR_EN = $d015 ; sprite enable bits (parked at 0)
|
||||
CIA1_PA = $dc00 ; CIA1 port A: keyboard columns / joystick 2
|
||||
CIA1_PB = $dc01 ; CIA1 port B: keyboard rows / joystick 1
|
||||
CIA1_TA = $dc04 ; CIA1 timer A low byte: free-running entropy
|
||||
|
||||
; ---- SID registers (single-voice SFX driver) ----
|
||||
SID_V1FL = $d400 ; voice 1 frequency low
|
||||
SID_V1FH = $d401 ; voice 1 frequency high
|
||||
SID_V1PL = $d402 ; voice 1 pulse width low
|
||||
SID_V1PH = $d403 ; voice 1 pulse width high
|
||||
SID_V1CR = $d404 ; voice 1 control (waveform + gate)
|
||||
SID_V1AD = $d405 ; voice 1 attack/decay
|
||||
SID_V1SR = $d406 ; voice 1 sustain/release
|
||||
SID_VOL = $d418 ; filter mode + master volume
|
||||
|
||||
; ---- SFX IDs ----
|
||||
SFX_CURSOR = 0 ; corner cursor step: click
|
||||
SFX_SELECT = 1 ; menu fire: rising tone
|
||||
SFX_ROTATE = 2 ; wall spin: noise whoosh
|
||||
SFX_DENY = 3 ; blocked spin: low buzz
|
||||
SFX_WIN = 4 ; escape: rising jingle
|
||||
SFX_LOSE = 5 ; caught: descending sweep
|
||||
NSFX = 6
|
||||
|
||||
; ---- colors ----
|
||||
BLACK = 0
|
||||
WHITE = 1
|
||||
RED = 2
|
||||
CYAN = 3
|
||||
PURPLE = 4
|
||||
GREEN = 5
|
||||
BLUE = 6
|
||||
YELLOW = 7
|
||||
ORANGE = 8
|
||||
BROWN = 9
|
||||
LRED = 10
|
||||
DGREY = 11
|
||||
MGREY = 12
|
||||
LGREEN = 13
|
||||
LBLUE = 14
|
||||
LGREY = 15
|
||||
|
||||
; ---- joystick bits (after read_joy / read_joy2, active high) ----
|
||||
JOY_UP = 1
|
||||
JOY_DOWN = 2
|
||||
JOY_LEFT = 4
|
||||
JOY_RIGHT = 8
|
||||
JOY_FIRE = 16
|
||||
|
||||
; ---- the maze ----
|
||||
; 19x11 cells on a 39x23 char lattice (screen rows 1-23):
|
||||
; cell = odd column, even row (walkable)
|
||||
; post = even column, odd row (pivot, always solid)
|
||||
; slot = the rest: wall pieces that spin around the posts
|
||||
; wall thickness therefore equals corridor width: one cell
|
||||
MAZE_CW = 19 ; cells across
|
||||
MAZE_CH = 11 ; cells down
|
||||
NCELLS = MAZE_CW * MAZE_CH
|
||||
NENEMIES = 4
|
||||
|
||||
CH_WALL = $a0 ; inverse space: solid wall block
|
||||
CH_FLOOR = $20 ; space: corridor
|
||||
CH_EXIT = $5a ; diamond: the goal
|
||||
CH_PLAYER = $51 ; filled ball: the runner
|
||||
CH_ENEMY = $57 ; open circle: an enemy
|
||||
|
||||
COL_WALL = MGREY ; spinnable wall pieces
|
||||
COL_POST = DGREY ; fixed pivot posts
|
||||
COL_EXIT = LGREEN ; the exit diamond
|
||||
|
||||
; ---- game timing ----
|
||||
REP_HOLD = 10 ; runner: frames before a held dir repeats
|
||||
REP_PERIOD = 5 ; runner: frames between repeat steps
|
||||
CREP_HOLD = 12 ; cursor: same, a touch slower
|
||||
CREP_PERIOD = 7
|
||||
RAS_SYNC = 251 ; raster line polled for the frame sync
|
||||
|
||||
; ---- zero page pointers (same slots as rabbitc64) ----
|
||||
txtlo = $f5 ; text pointer for the print routines (2 bytes)
|
||||
txthi = $f6
|
||||
updvec = $f9 ; state update vector (2 bytes)
|
||||
scrlo = $fb
|
||||
scrhi = $fc
|
||||
collo = $fd
|
||||
colhi = $fe
|
||||
|
||||
; ---- helper macros: print a normal / big text line ----
|
||||
!macro prtext .row, .col, .color, .txt, .len {
|
||||
lda #<.txt
|
||||
sta txtlo
|
||||
lda #>.txt
|
||||
sta txthi
|
||||
lda #.row
|
||||
sta prow
|
||||
lda #.col
|
||||
sta pcol
|
||||
lda #.color
|
||||
sta pcolor
|
||||
lda #.len
|
||||
sta plen
|
||||
jsr print_text
|
||||
}
|
||||
|
||||
!macro prbig .row, .col, .color, .txt, .len {
|
||||
lda #<.txt
|
||||
sta txtlo
|
||||
lda #>.txt
|
||||
sta txthi
|
||||
lda #.row
|
||||
sta bigrow
|
||||
lda #.col
|
||||
sta bigcol
|
||||
lda #.color
|
||||
sta bigcolor
|
||||
lda #.len
|
||||
sta biglen
|
||||
jsr draw_big
|
||||
}
|
||||
|
||||
; ---- set the state update vector ----
|
||||
!macro setupd .addr {
|
||||
lda #<.addr
|
||||
sta updvec
|
||||
lda #>.addr
|
||||
sta updvec+1
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
; -----------------------------------------------------------
|
||||
; intro.asm - boot screen ported from rabbitc64: giant TTG
|
||||
; letters built from 8x8 bitmaps with a gold color wash
|
||||
; running down them, TELETYPE GAMES / PRESENTS below;
|
||||
; advances to the menu after a few seconds or on fire
|
||||
; -----------------------------------------------------------
|
||||
|
||||
INTRO_LROW = 3 ; top cell row of the giant letters
|
||||
INTRO_TIME = 250 ; frames before it advances on its own
|
||||
|
||||
intro_init:
|
||||
jsr clear_screen
|
||||
|
||||
; T T G at columns 6 / 16 / 26
|
||||
lda #<glyph_t
|
||||
sta txtlo
|
||||
lda #>glyph_t
|
||||
sta txthi
|
||||
lda #6
|
||||
sta g8col
|
||||
jsr draw_glyph8
|
||||
lda #16
|
||||
sta g8col
|
||||
jsr draw_glyph8
|
||||
lda #<glyph_g
|
||||
sta txtlo
|
||||
lda #>glyph_g
|
||||
sta txthi
|
||||
lda #26
|
||||
sta g8col
|
||||
jsr draw_glyph8
|
||||
|
||||
+prtext 13, 13, WHITE, txt_ttgname, 14
|
||||
+prtext 15, 16, LGREY, txt_presents, 8
|
||||
|
||||
lda #INTRO_TIME
|
||||
sta introt
|
||||
lda #0
|
||||
sta washphase
|
||||
+setupd intro_update
|
||||
rts
|
||||
|
||||
intro_update:
|
||||
; gold wash: recolor the letter rows from the cyclic ramp
|
||||
inc washphase
|
||||
lda #0
|
||||
sta g8row
|
||||
iw_row:
|
||||
lda #INTRO_LROW
|
||||
clc
|
||||
adc g8row
|
||||
tax
|
||||
jsr row_ptr
|
||||
lda washphase
|
||||
lsr ; half speed keeps the shimmer gentle
|
||||
clc
|
||||
adc g8row
|
||||
and #15
|
||||
tax
|
||||
lda washtbl,x
|
||||
sta rtmp
|
||||
ldy #33 ; the letters span columns 6-33
|
||||
iw_col:
|
||||
lda (scrlo),y
|
||||
cmp #$a0 ; only touch the letter blocks
|
||||
bne iw_skip
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
iw_skip:
|
||||
dey
|
||||
cpy #5
|
||||
bne iw_col
|
||||
inc g8row
|
||||
lda g8row
|
||||
cmp #8
|
||||
bne iw_row
|
||||
|
||||
; fire skips, otherwise wait out the timer
|
||||
jsr start_edge
|
||||
beq iw_tick
|
||||
jmp menu_init
|
||||
iw_tick:
|
||||
dec introt
|
||||
bne iw_done
|
||||
jmp menu_init
|
||||
iw_done:
|
||||
rts
|
||||
|
||||
; ---- draw an 8x8-cell giant letter: txtlo/hi = 8-byte
|
||||
; bitmap, g8col = left cell column ----
|
||||
draw_glyph8:
|
||||
lda #0
|
||||
sta g8row
|
||||
g8_row:
|
||||
lda #INTRO_LROW
|
||||
clc
|
||||
adc g8row
|
||||
tax
|
||||
jsr row_ptr
|
||||
ldy g8row
|
||||
lda (txtlo),y
|
||||
sta curbits
|
||||
ldy g8col
|
||||
lda #8
|
||||
sta bitcnt
|
||||
g8_bit:
|
||||
asl curbits ; leftmost pixel first
|
||||
bcc g8_next
|
||||
lda #$a0 ; inverse space = solid block
|
||||
sta (scrlo),y
|
||||
lda #YELLOW
|
||||
sta (collo),y
|
||||
g8_next:
|
||||
iny
|
||||
dec bitcnt
|
||||
bne g8_bit
|
||||
inc g8row
|
||||
lda g8row
|
||||
cmp #8
|
||||
bne g8_row
|
||||
rts
|
||||
|
||||
; ---- 8x8 letter bitmaps ----
|
||||
glyph_t:
|
||||
!byte %11111111
|
||||
!byte %11111111
|
||||
!byte %00111100
|
||||
!byte %00111100
|
||||
!byte %00111100
|
||||
!byte %00111100
|
||||
!byte %00111100
|
||||
!byte %00111100
|
||||
glyph_g:
|
||||
!byte %00111100
|
||||
!byte %01111110
|
||||
!byte %11000011
|
||||
!byte %11000000
|
||||
!byte %11001111
|
||||
!byte %11000011
|
||||
!byte %01111110
|
||||
!byte %00111100
|
||||
|
||||
; ---- intro variables ----
|
||||
introt: !byte 0 ; frames left before auto-advancing
|
||||
g8row: !byte 0 ; glyph renderer / wash internals
|
||||
g8col: !byte 0
|
||||
@@ -0,0 +1,116 @@
|
||||
; -----------------------------------------------------------
|
||||
; main.asm - LABYRINTH for the C64
|
||||
; two-player maze duel: player 1 runs the corridors toward
|
||||
; the green diamond exit while dodging the roaming colored
|
||||
; enemies; player 2 rides the wall corners with a cursor
|
||||
; and fire spins the four wall pieces around it by 90
|
||||
; degrees, reshaping the maze under the runner's feet
|
||||
;
|
||||
; Files:
|
||||
; defs.asm - constants, zero page, macros
|
||||
; intro.asm - TTG boot logo (ported from rabbitc64)
|
||||
; menu.asm - main menu: play / rules / credits
|
||||
; rules.asm - how to play
|
||||
; controls.asm- joystick + keyboard mappings
|
||||
; credits.asm - the crew (content from rabbitc64, new look)
|
||||
; play.asm - maze generation + the game itself
|
||||
; win.asm - escape screen, returns to the menu
|
||||
; over.asm - game over screen with retry
|
||||
; common.asm - shared routines, input, RNG, texts
|
||||
; sound.asm - single-voice SFX driver
|
||||
;
|
||||
; Build: acme -f cbm -o labyrinth.prg main.asm
|
||||
; -----------------------------------------------------------
|
||||
|
||||
* = $0801
|
||||
|
||||
; ---- BASIC stub: 10 SYS 2064 ----
|
||||
; so after LOAD"*",8,1 it's enough to type RUN
|
||||
!byte $0c, $08 ; address of next line
|
||||
!byte $0a, $00 ; line number: 10
|
||||
!byte $9e ; SYS token
|
||||
!byte $20, $32, $30, $36, $34 ; " 2064"
|
||||
!byte $00 ; end of line
|
||||
!byte $00, $00 ; end of program
|
||||
|
||||
* = $0810 ; = 2064 in decimal
|
||||
|
||||
!src "defs.asm"
|
||||
|
||||
start:
|
||||
sei ; no KERNAL IRQ - raster polling must not jitter
|
||||
lda #BLACK
|
||||
sta BORDER
|
||||
sta BG
|
||||
lda #0
|
||||
sta SPR_EN ; character-only game: sprites stay off
|
||||
sta framectr
|
||||
sta fheld
|
||||
jsr sfx_init
|
||||
|
||||
!ifdef AUTOPLAY { ; test builds boot straight into one state
|
||||
jsr play_init
|
||||
} else {
|
||||
!ifdef AUTORULES {
|
||||
jsr rules_init
|
||||
} else {
|
||||
!ifdef AUTOCONTROLS {
|
||||
jsr controls_init
|
||||
} else {
|
||||
!ifdef AUTOCREDITS {
|
||||
jsr credits_init
|
||||
} else {
|
||||
!ifdef AUTOWIN {
|
||||
jsr win_init
|
||||
} else {
|
||||
!ifdef AUTOLOSE {
|
||||
jsr lose_init
|
||||
} else {
|
||||
jsr intro_init ; TTG logo, then the menu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
; ============================================================
|
||||
; main loop - one state update per PAL frame, synced to the
|
||||
; raster beam in the lower border
|
||||
; ============================================================
|
||||
main:
|
||||
jsr wait_frame
|
||||
inc framectr
|
||||
jsr rand8 ; stir the RNG once per frame
|
||||
jsr sfx_update ; advance the sound effect
|
||||
jsr call_update
|
||||
jmp main
|
||||
|
||||
call_update:
|
||||
jmp (updvec) ; each update runs exactly one frame, ends with rts
|
||||
|
||||
; ---- wait for raster line RAS_SYNC (leave it first, so a
|
||||
; fast update cannot run twice in the same frame) ----
|
||||
wait_frame:
|
||||
lda RASTER
|
||||
cmp #RAS_SYNC
|
||||
beq wait_frame
|
||||
wf_hit:
|
||||
lda RASTER
|
||||
cmp #RAS_SYNC
|
||||
bne wf_hit
|
||||
rts
|
||||
|
||||
; ============================================================
|
||||
; the states and everything they share
|
||||
; ============================================================
|
||||
!src "intro.asm"
|
||||
!src "menu.asm"
|
||||
!src "rules.asm"
|
||||
!src "controls.asm"
|
||||
!src "credits.asm"
|
||||
!src "play.asm"
|
||||
!src "win.asm"
|
||||
!src "over.asm"
|
||||
!src "common.asm"
|
||||
!src "sound.asm"
|
||||
@@ -0,0 +1,194 @@
|
||||
; -----------------------------------------------------------
|
||||
; menu.asm - full-screen menu card: big LABYRINTH title with
|
||||
; the gold wash, and a four-item menu: play / rules /
|
||||
; controls / credits (joystick or w/s up/down + fire, space
|
||||
; or return)
|
||||
; -----------------------------------------------------------
|
||||
|
||||
MENU_ITEMS = 4
|
||||
|
||||
menu_init:
|
||||
jsr clear_screen
|
||||
|
||||
+prbig 2, 2, YELLOW, txt_title, 9
|
||||
+prtext 9, 13, LGREY, txt_ttgname, 14
|
||||
|
||||
lda #0
|
||||
sta menusel
|
||||
sta washphase
|
||||
|
||||
+setupd menu_update
|
||||
rts
|
||||
|
||||
menu_update:
|
||||
; gold wash rolling down the big title (rows 2-6)
|
||||
inc washphase
|
||||
lda #0
|
||||
sta g8row
|
||||
mw_row:
|
||||
lda #2
|
||||
clc
|
||||
adc g8row
|
||||
tax
|
||||
jsr row_ptr
|
||||
lda washphase
|
||||
lsr
|
||||
clc
|
||||
adc g8row
|
||||
and #15
|
||||
tax
|
||||
lda washtbl,x
|
||||
sta rtmp
|
||||
ldy #37 ; the title spans columns 2-36
|
||||
mw_col:
|
||||
lda (scrlo),y
|
||||
cmp #$a0 ; only touch the letter blocks
|
||||
bne mw_skip
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
mw_skip:
|
||||
dey
|
||||
cpy #1
|
||||
bne mw_col
|
||||
inc g8row
|
||||
lda g8row
|
||||
cmp #5
|
||||
bne mw_row
|
||||
|
||||
; joystick up/down steps the cursor (edge detected)
|
||||
jsr read_joy
|
||||
lda joyb
|
||||
and #3
|
||||
beq mu_udrel
|
||||
lda udheld
|
||||
bne mu_udend
|
||||
lda #1
|
||||
sta udheld
|
||||
lda joyb
|
||||
and #1 ; up
|
||||
beq mu_down
|
||||
lda menusel
|
||||
beq mu_udend
|
||||
dec menusel
|
||||
lda #SFX_CURSOR
|
||||
jsr sfx_play
|
||||
jmp mu_udend
|
||||
mu_down:
|
||||
lda menusel
|
||||
cmp #MENU_ITEMS-1
|
||||
beq mu_udend
|
||||
inc menusel
|
||||
lda #SFX_CURSOR
|
||||
jsr sfx_play
|
||||
jmp mu_udend
|
||||
mu_udrel:
|
||||
lda #0
|
||||
sta udheld
|
||||
mu_udend:
|
||||
|
||||
; footer: both control lines always visible
|
||||
+prtext 21, 8, LGREY, txt_info, txt_infoe-txt_info
|
||||
+prtext 22, 5, LGREY, txt_info2, txt_info2e-txt_info2
|
||||
|
||||
; the menu items: the picked line is yellow
|
||||
ldx #0
|
||||
mm_items:
|
||||
txa
|
||||
pha
|
||||
jsr draw_mitem
|
||||
pla
|
||||
tax
|
||||
inx
|
||||
cpx #MENU_ITEMS
|
||||
bne mm_items
|
||||
|
||||
; blinking '>' cursor in front of the picked line
|
||||
lda #$20
|
||||
sta SCREEN+12*40+15
|
||||
sta SCREEN+14*40+15
|
||||
sta SCREEN+16*40+15
|
||||
sta SCREEN+18*40+15
|
||||
lda framectr
|
||||
and #16
|
||||
bne mu_start
|
||||
ldx menusel
|
||||
lda mcur_lo,x
|
||||
sta scrlo
|
||||
lda mcur_hi,x
|
||||
sta scrhi
|
||||
ldy #0
|
||||
lda #62 ; '>'
|
||||
sta (scrlo),y
|
||||
lda scrhi
|
||||
clc
|
||||
adc #$d4 ; same cell in color RAM
|
||||
sta scrhi
|
||||
lda #YELLOW
|
||||
sta (scrlo),y
|
||||
|
||||
mu_start:
|
||||
; fire or space opens the picked line
|
||||
jsr start_edge
|
||||
beq mu_done
|
||||
lda #SFX_SELECT
|
||||
jsr sfx_play
|
||||
lda menusel
|
||||
beq mg_play
|
||||
cmp #1
|
||||
beq mg_rules
|
||||
cmp #2
|
||||
beq mg_controls
|
||||
jmp credits_init ; each init's rts returns to the main loop
|
||||
mg_play:
|
||||
jmp play_init
|
||||
mg_rules:
|
||||
jmp rules_init
|
||||
mg_controls:
|
||||
jmp controls_init
|
||||
mu_done:
|
||||
rts
|
||||
|
||||
; ---- draw menu item X, yellow when it is the picked one ----
|
||||
draw_mitem:
|
||||
lda mitem_lo,x
|
||||
sta txtlo
|
||||
lda mitem_hi,x
|
||||
sta txthi
|
||||
lda mitem_row,x
|
||||
sta prow
|
||||
lda #17
|
||||
sta pcol
|
||||
lda mitem_len,x
|
||||
sta plen
|
||||
lda #WHITE
|
||||
cpx menusel
|
||||
bne dm_store
|
||||
lda #YELLOW
|
||||
dm_store:
|
||||
sta pcolor
|
||||
jmp print_text
|
||||
|
||||
; ---- menu tables ----
|
||||
mitem_lo: !byte <txt_play, <txt_rules, <txt_controls, <txt_credits
|
||||
mitem_hi: !byte >txt_play, >txt_rules, >txt_controls, >txt_credits
|
||||
mitem_len: !byte 4, 5, 8, 7
|
||||
mitem_row: !byte 12, 14, 16, 18
|
||||
mcur_lo: ; cursor cells: column 15 of those rows
|
||||
!byte <(SCREEN+12*40+15), <(SCREEN+14*40+15)
|
||||
!byte <(SCREEN+16*40+15), <(SCREEN+18*40+15)
|
||||
mcur_hi:
|
||||
!byte >(SCREEN+12*40+15), >(SCREEN+14*40+15)
|
||||
!byte >(SCREEN+16*40+15), >(SCREEN+18*40+15)
|
||||
|
||||
; ---- menu texts ----
|
||||
txt_play: !scr "play"
|
||||
txt_rules: !scr "rules"
|
||||
txt_credits: !scr "credits"
|
||||
txt_info: !scr "p1: joy2 / wasd - runner"
|
||||
txt_infoe:
|
||||
txt_info2: !scr "p2: joy1 / ijkl - wall spinner"
|
||||
txt_info2e:
|
||||
|
||||
; ---- menu variables ----
|
||||
menusel: !byte 0 ; picked line: 0-2
|
||||
udheld: !byte 0 ; joystick up/down edge detection state
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "labyrinth",
|
||||
"title": "Labyrinth",
|
||||
"author": "Teletype Games",
|
||||
"desc": "Two-player maze duel: one player runs for the exit while the other spins the walls of the labyrinth around their corners. Dodge the enemies, find the diamond.",
|
||||
"site": "https://git.teletype.hu/games/labyrinth",
|
||||
"license": "MIT",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
; -----------------------------------------------------------
|
||||
; over.asm - game over screen: big red GAME OVER, fire
|
||||
; restarts with a fresh maze, the m key backs out to the menu
|
||||
; -----------------------------------------------------------
|
||||
|
||||
lose_init:
|
||||
jsr clear_screen
|
||||
|
||||
+prbig 3, 2, RED, txt_gameover, 9
|
||||
|
||||
+prtext 11, 10, WHITE, txt_caught, txt_caughte-txt_caught
|
||||
|
||||
+prtext 21, 13, LGREY, txt_mmenu, txt_mmenue-txt_mmenu
|
||||
|
||||
+setupd lose_update
|
||||
rts
|
||||
|
||||
lose_update:
|
||||
; blink the retry prompt
|
||||
lda framectr
|
||||
and #32
|
||||
bne lu_hide
|
||||
+prtext 19, 12, YELLOW, txt_retry, txt_retrye-txt_retry
|
||||
jmp lu_input
|
||||
lu_hide:
|
||||
+prtext 19, 12, YELLOW, txt_blank, txt_retrye-txt_retry
|
||||
lu_input:
|
||||
; fire or space: straight back in with a fresh maze
|
||||
jsr start_edge
|
||||
beq lu_nofire
|
||||
jmp play_init
|
||||
lu_nofire:
|
||||
; the m key: keyboard column 4, row bit 4 (start_edge has
|
||||
; already returned zero here, so joystick fire is not
|
||||
; ghosting this row line)
|
||||
lda #$ef
|
||||
sta CIA1_PA
|
||||
lda CIA1_PB
|
||||
and #$10
|
||||
bne lu_done
|
||||
lda #$ff
|
||||
sta CIA1_PA
|
||||
jmp menu_init
|
||||
lu_done:
|
||||
lda #$ff
|
||||
sta CIA1_PA
|
||||
rts
|
||||
|
||||
; ---- game over texts ----
|
||||
txt_gameover: !scr "game over"
|
||||
txt_caught: !scr "the enemies got you"
|
||||
txt_caughte:
|
||||
txt_retry: !scr "fire = try again"
|
||||
txt_retrye:
|
||||
txt_mmenu: !scr "m = main menu"
|
||||
txt_mmenue:
|
||||
@@ -0,0 +1,783 @@
|
||||
; -----------------------------------------------------------
|
||||
; play.asm - the game: a fresh maze is carved with a
|
||||
; recursive backtracker every round, player 1 (joystick 2)
|
||||
; runs the corridors toward the exit diamond, player 2
|
||||
; (joystick 1) rides the wall corners and fire spins the
|
||||
; four wall pieces around the cursor by 90 degrees; the
|
||||
; roaming colored enemies end the round on touch
|
||||
;
|
||||
; screen layout: status on row 0, the 39x23 maze lattice on
|
||||
; rows 1-23 (cells = odd col / even row, posts = even col /
|
||||
; odd row, wall slots = the rest), a hint line on row 24
|
||||
; -----------------------------------------------------------
|
||||
|
||||
play_init:
|
||||
jsr clear_screen
|
||||
+prtext 0, 0, WHITE, txt_title, 9
|
||||
+prtext 0, 28, LGREY, txt_goal, txt_goale-txt_goal
|
||||
+prtext 24, 6, DGREY, txt_hint, txt_hinte-txt_hint
|
||||
|
||||
; fill the whole lattice with wall blocks
|
||||
ldx #1
|
||||
pi_fillrow:
|
||||
stx ptmp
|
||||
jsr row_ptr
|
||||
ldy #38
|
||||
pi_fillcol:
|
||||
lda #CH_WALL
|
||||
sta (scrlo),y
|
||||
lda #COL_WALL
|
||||
sta (collo),y
|
||||
dey
|
||||
bpl pi_fillcol
|
||||
ldx ptmp
|
||||
inx
|
||||
cpx #24
|
||||
bne pi_fillrow
|
||||
|
||||
; the pivot posts get their own darker shade
|
||||
ldx #1 ; post rows 1, 3, ... 23
|
||||
pi_postrow:
|
||||
stx ptmp
|
||||
jsr row_ptr
|
||||
ldy #38 ; post columns 38, 36, ... 0
|
||||
pi_postcol:
|
||||
lda #COL_POST
|
||||
sta (collo),y
|
||||
dey
|
||||
dey
|
||||
bpl pi_postcol
|
||||
ldx ptmp
|
||||
inx
|
||||
inx
|
||||
cpx #25
|
||||
bcc pi_postrow
|
||||
|
||||
; carve the maze
|
||||
ldx #0
|
||||
lda #0
|
||||
pi_clrv:
|
||||
sta visited,x
|
||||
inx
|
||||
cpx #NCELLS
|
||||
bne pi_clrv
|
||||
jsr gen_maze
|
||||
|
||||
; the exit: a green diamond in the right border wall
|
||||
pi_exit:
|
||||
jsr rand8
|
||||
and #$0f
|
||||
cmp #MAZE_CH
|
||||
bcs pi_exit
|
||||
asl ; cell row -> screen row
|
||||
clc
|
||||
adc #2
|
||||
sta exrow
|
||||
tax
|
||||
jsr row_ptr
|
||||
ldy #38
|
||||
lda #CH_EXIT
|
||||
sta (scrlo),y
|
||||
lda #COL_EXIT
|
||||
sta (collo),y
|
||||
|
||||
; the runner starts in the top-left cell
|
||||
lda #1
|
||||
sta px
|
||||
lda #2
|
||||
sta py
|
||||
jsr draw_player
|
||||
|
||||
; the corner cursor starts on the center post
|
||||
lda #18
|
||||
sta kx
|
||||
lda #11
|
||||
sta ky
|
||||
|
||||
jsr spawn_enemies
|
||||
|
||||
; input state: a fire still held from the previous screen
|
||||
; must be released before it can spin walls
|
||||
lda #0
|
||||
sta rheld
|
||||
sta cheld
|
||||
sta endflag
|
||||
lda #1
|
||||
sta cfheld
|
||||
+setupd play_update
|
||||
rts
|
||||
|
||||
; ============================================================
|
||||
; maze generation - recursive backtracker over the 19x11
|
||||
; cells; the screen itself is the wall data structure
|
||||
; ============================================================
|
||||
gen_maze:
|
||||
lda #0
|
||||
sta gcx
|
||||
sta gcy
|
||||
sta gsp
|
||||
jsr gv_mark
|
||||
jsr carve_cell
|
||||
gm_loop:
|
||||
; collect the unvisited neighbor directions
|
||||
lda #0
|
||||
sta gn
|
||||
ldx #0
|
||||
gm_scan:
|
||||
stx gtd
|
||||
lda gcx
|
||||
clc
|
||||
adc dxtab,x
|
||||
sta gnx
|
||||
cmp #MAZE_CW ; unsigned: -1 wraps to $ff and fails too
|
||||
bcs gm_next
|
||||
ldx gtd
|
||||
lda gcy
|
||||
clc
|
||||
adc dytab,x
|
||||
sta gny
|
||||
cmp #MAZE_CH
|
||||
bcs gm_next
|
||||
ldx gny
|
||||
lda mul19,x
|
||||
clc
|
||||
adc gnx
|
||||
tax
|
||||
lda visited,x
|
||||
bne gm_next
|
||||
ldx gn
|
||||
lda gtd
|
||||
sta gdlist,x
|
||||
inc gn
|
||||
gm_next:
|
||||
ldx gtd
|
||||
inx
|
||||
cpx #4
|
||||
bne gm_scan
|
||||
lda gn
|
||||
beq gm_pop
|
||||
; pick a random one of them
|
||||
gm_pick:
|
||||
jsr rand8
|
||||
and #3
|
||||
cmp gn
|
||||
bcs gm_pick
|
||||
tax
|
||||
lda gdlist,x
|
||||
sta gtd
|
||||
; push the current cell, open the wall, step through it
|
||||
ldx gsp
|
||||
lda gcx
|
||||
sta stackx,x
|
||||
lda gcy
|
||||
sta stacky,x
|
||||
inc gsp
|
||||
jsr carve_wall
|
||||
ldx gtd
|
||||
lda gcx
|
||||
clc
|
||||
adc dxtab,x
|
||||
sta gcx
|
||||
ldx gtd
|
||||
lda gcy
|
||||
clc
|
||||
adc dytab,x
|
||||
sta gcy
|
||||
jsr gv_mark
|
||||
jsr carve_cell
|
||||
jmp gm_loop
|
||||
gm_pop:
|
||||
; dead end: back up; empty stack = the maze is done
|
||||
lda gsp
|
||||
beq gm_done
|
||||
dec gsp
|
||||
ldx gsp
|
||||
lda stackx,x
|
||||
sta gcx
|
||||
lda stacky,x
|
||||
sta gcy
|
||||
jmp gm_loop
|
||||
gm_done:
|
||||
rts
|
||||
|
||||
gv_mark: ; visited[gcy * 19 + gcx] = 1
|
||||
ldx gcy
|
||||
lda mul19,x
|
||||
clc
|
||||
adc gcx
|
||||
tax
|
||||
lda #1
|
||||
sta visited,x
|
||||
rts
|
||||
|
||||
carve_cell: ; open the cell (2*gcx+1, 2*gcy+2)
|
||||
lda gcy
|
||||
asl
|
||||
clc
|
||||
adc #2
|
||||
tax
|
||||
jsr row_ptr
|
||||
lda gcx
|
||||
asl
|
||||
clc
|
||||
adc #1
|
||||
tay
|
||||
lda #CH_FLOOR
|
||||
sta (scrlo),y
|
||||
lda #BLACK
|
||||
sta (collo),y
|
||||
rts
|
||||
|
||||
carve_wall: ; open the wall slot toward direction gtd
|
||||
ldx gtd
|
||||
lda gcy
|
||||
asl
|
||||
clc
|
||||
adc #2
|
||||
clc
|
||||
adc dytab,x
|
||||
tax
|
||||
jsr row_ptr
|
||||
ldx gtd
|
||||
lda gcx
|
||||
asl
|
||||
clc
|
||||
adc #1
|
||||
clc
|
||||
adc dxtab,x
|
||||
tay
|
||||
lda #CH_FLOOR
|
||||
sta (scrlo),y
|
||||
lda #BLACK
|
||||
sta (collo),y
|
||||
rts
|
||||
|
||||
dxtab: !byte 0, 0, $ff, 1 ; up, down, left, right
|
||||
dytab: !byte $ff, 1, 0, 0
|
||||
mul19: !byte 0, 19, 38, 57, 76, 95, 114, 133, 152, 171, 190
|
||||
|
||||
; ---- enemies: random colors, random right-half cells ----
|
||||
spawn_enemies:
|
||||
lda #0
|
||||
sta ei
|
||||
sn_loop:
|
||||
sn_rx:
|
||||
jsr rand8
|
||||
and #$0f
|
||||
cmp #MAZE_CH
|
||||
bcs sn_rx
|
||||
clc
|
||||
adc #MAZE_CW-MAZE_CH ; cell column 8-18: away from the runner
|
||||
asl
|
||||
clc
|
||||
adc #1
|
||||
sta gnx ; candidate screen column
|
||||
sn_ry:
|
||||
jsr rand8
|
||||
and #$0f
|
||||
cmp #MAZE_CH
|
||||
bcs sn_ry
|
||||
asl
|
||||
clc
|
||||
adc #2
|
||||
sta gny ; candidate screen row
|
||||
ldx gny
|
||||
jsr row_ptr
|
||||
ldy gnx
|
||||
lda (scrlo),y
|
||||
cmp #CH_FLOOR ; only an empty cell will do
|
||||
bne sn_rx
|
||||
lda #CH_ENEMY
|
||||
sta (scrlo),y
|
||||
jsr rand8
|
||||
and #7
|
||||
tax
|
||||
lda ecoltab,x
|
||||
sta (collo),y
|
||||
ldx ei
|
||||
sta ecol,x
|
||||
lda gnx
|
||||
sta ex,x
|
||||
lda gny
|
||||
sta ey,x
|
||||
jsr rand8
|
||||
and #3
|
||||
sta edir,x
|
||||
inc ei
|
||||
lda ei
|
||||
cmp #NENEMIES
|
||||
bne sn_loop
|
||||
rts
|
||||
|
||||
ecoltab: ; anything but the white runner / grey walls
|
||||
!byte RED, CYAN, PURPLE, YELLOW, ORANGE, LRED, LBLUE, BLUE
|
||||
|
||||
; ============================================================
|
||||
; one frame of play
|
||||
; ============================================================
|
||||
play_update:
|
||||
jsr upd_runner
|
||||
lda endflag
|
||||
bne pu_end
|
||||
jsr upd_enemies
|
||||
lda endflag
|
||||
bne pu_end
|
||||
jsr upd_cursor
|
||||
jsr upd_flash
|
||||
rts
|
||||
pu_end:
|
||||
cmp #1
|
||||
beq pu_win
|
||||
lda #SFX_LOSE
|
||||
jsr sfx_play
|
||||
jmp lose_init ; its rts returns to the main loop
|
||||
pu_win:
|
||||
lda #SFX_WIN
|
||||
jsr sfx_play
|
||||
jmp win_init
|
||||
|
||||
; ---- player 1: walk the corridors, one char per step ----
|
||||
upd_runner:
|
||||
jsr read_joy
|
||||
and #$0f
|
||||
sta rdir
|
||||
bne ur_held
|
||||
lda #0
|
||||
sta rheld
|
||||
rts
|
||||
ur_held:
|
||||
lda rheld
|
||||
beq ur_first
|
||||
dec rtimer
|
||||
beq ur_rep
|
||||
rts
|
||||
ur_rep:
|
||||
lda #REP_PERIOD
|
||||
sta rtimer
|
||||
jmp ur_move
|
||||
ur_first:
|
||||
lda #1
|
||||
sta rheld
|
||||
lda #REP_HOLD
|
||||
sta rtimer
|
||||
ur_move:
|
||||
ldx px
|
||||
ldy py
|
||||
lda rdir
|
||||
lsr
|
||||
bcs ur_up
|
||||
lsr
|
||||
bcs ur_down
|
||||
lsr
|
||||
bcs ur_left
|
||||
inx ; right
|
||||
jmp ur_try
|
||||
ur_up:
|
||||
dey
|
||||
jmp ur_try
|
||||
ur_down:
|
||||
iny
|
||||
jmp ur_try
|
||||
ur_left:
|
||||
dex
|
||||
ur_try:
|
||||
stx ttx
|
||||
sty tty
|
||||
ldx tty
|
||||
jsr row_ptr
|
||||
ldy ttx
|
||||
lda (scrlo),y
|
||||
cmp #CH_FLOOR
|
||||
beq ur_go
|
||||
cmp #CH_EXIT
|
||||
beq ur_win
|
||||
cmp #CH_ENEMY
|
||||
beq ur_dead
|
||||
rts ; a wall: blocked
|
||||
ur_go:
|
||||
ldx py
|
||||
jsr row_ptr
|
||||
ldy px
|
||||
lda #CH_FLOOR
|
||||
sta (scrlo),y
|
||||
lda #BLACK
|
||||
sta (collo),y
|
||||
lda ttx
|
||||
sta px
|
||||
lda tty
|
||||
sta py
|
||||
jmp draw_player
|
||||
ur_win:
|
||||
lda #1
|
||||
sta endflag
|
||||
rts
|
||||
ur_dead:
|
||||
lda #2
|
||||
sta endflag
|
||||
rts
|
||||
|
||||
draw_player:
|
||||
ldx py
|
||||
jsr row_ptr
|
||||
ldy px
|
||||
lda #CH_PLAYER
|
||||
sta (scrlo),y
|
||||
lda #WHITE
|
||||
sta (collo),y
|
||||
rts
|
||||
|
||||
; ---- player 2: the corner cursor + the wall spin ----
|
||||
upd_cursor:
|
||||
jsr read_joy2
|
||||
sta j2bits
|
||||
and #$0f
|
||||
sta cdir
|
||||
bne uc_held
|
||||
lda #0
|
||||
sta cheld
|
||||
jmp uc_fire
|
||||
uc_held:
|
||||
lda cheld
|
||||
beq uc_first
|
||||
dec ctimer
|
||||
beq uc_rep
|
||||
jmp uc_fire
|
||||
uc_rep:
|
||||
lda #CREP_PERIOD
|
||||
sta ctimer
|
||||
jmp uc_move
|
||||
uc_first:
|
||||
lda #1
|
||||
sta cheld
|
||||
lda #CREP_HOLD
|
||||
sta ctimer
|
||||
uc_move:
|
||||
ldx kx
|
||||
ldy ky
|
||||
lda cdir
|
||||
lsr
|
||||
bcs uc_up
|
||||
lsr
|
||||
bcs uc_down
|
||||
lsr
|
||||
bcs uc_left
|
||||
cpx #36 ; right: interior posts end at column 36
|
||||
bcs uc_fire
|
||||
inx
|
||||
inx
|
||||
jmp uc_go
|
||||
uc_up:
|
||||
cpy #4 ; top interior post row is 3
|
||||
bcc uc_fire
|
||||
dey
|
||||
dey
|
||||
jmp uc_go
|
||||
uc_down:
|
||||
cpy #21 ; bottom interior post row is 21
|
||||
bcs uc_fire
|
||||
iny
|
||||
iny
|
||||
jmp uc_go
|
||||
uc_left:
|
||||
cpx #3 ; left interior post column is 2
|
||||
bcc uc_fire
|
||||
dex
|
||||
dex
|
||||
uc_go:
|
||||
stx ttx
|
||||
sty tty
|
||||
ldx ky ; the old post gets its color back
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda #COL_POST
|
||||
sta (collo),y
|
||||
lda ttx
|
||||
sta kx
|
||||
lda tty
|
||||
sta ky
|
||||
lda #SFX_CURSOR
|
||||
jsr sfx_play
|
||||
uc_fire:
|
||||
lda j2bits
|
||||
and #JOY_FIRE
|
||||
beq uc_frel
|
||||
lda cfheld
|
||||
bne uc_done
|
||||
lda #1
|
||||
sta cfheld
|
||||
jmp do_rotate ; its rts returns to play_update's caller
|
||||
uc_frel:
|
||||
lda #0
|
||||
sta cfheld
|
||||
uc_done:
|
||||
rts
|
||||
|
||||
; ---- spin the four wall slots around post (kx, ky) by 90
|
||||
; degrees clockwise; denied while anyone stands in them ----
|
||||
do_rotate:
|
||||
ldx ky ; gather the four slot chars
|
||||
dex
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda (scrlo),y
|
||||
sta nch
|
||||
ldx ky
|
||||
inx
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda (scrlo),y
|
||||
sta sch
|
||||
ldx ky
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
dey
|
||||
lda (scrlo),y
|
||||
sta wch
|
||||
iny
|
||||
iny
|
||||
lda (scrlo),y
|
||||
sta ech
|
||||
; every slot must be plain wall or floor
|
||||
lda nch
|
||||
jsr rot_ok
|
||||
bcs rot_deny
|
||||
lda sch
|
||||
jsr rot_ok
|
||||
bcs rot_deny
|
||||
lda wch
|
||||
jsr rot_ok
|
||||
bcs rot_deny
|
||||
lda ech
|
||||
jsr rot_ok
|
||||
bcs rot_deny
|
||||
; clockwise: north<-west, east<-north, south<-east, west<-south
|
||||
ldx ky
|
||||
dex
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda wch
|
||||
jsr put_slot
|
||||
ldx ky
|
||||
inx
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda ech
|
||||
jsr put_slot
|
||||
ldx ky
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
dey
|
||||
lda sch
|
||||
jsr put_slot
|
||||
iny
|
||||
iny
|
||||
lda nch
|
||||
jsr put_slot
|
||||
lda #SFX_ROTATE
|
||||
jmp sfx_play
|
||||
rot_deny:
|
||||
lda #SFX_DENY
|
||||
jmp sfx_play
|
||||
|
||||
rot_ok: ; C clear when A is spinnable (wall or floor)
|
||||
cmp #CH_WALL
|
||||
beq ro_yes
|
||||
cmp #CH_FLOOR
|
||||
beq ro_yes
|
||||
sec
|
||||
rts
|
||||
ro_yes:
|
||||
clc
|
||||
rts
|
||||
|
||||
put_slot: ; write char A at (scrlo),y + matching color
|
||||
sta (scrlo),y
|
||||
cmp #CH_WALL
|
||||
beq ps_wall
|
||||
lda #BLACK
|
||||
sta (collo),y
|
||||
rts
|
||||
ps_wall:
|
||||
lda #COL_WALL
|
||||
sta (collo),y
|
||||
rts
|
||||
|
||||
; ---- the enemies: staggered random walkers ----
|
||||
upd_enemies:
|
||||
lda #0
|
||||
sta ei
|
||||
ue_loop:
|
||||
ldx ei
|
||||
lda framectr
|
||||
clc
|
||||
adc estag,x
|
||||
and #7 ; each enemy steps every 8th frame
|
||||
bne ue_next
|
||||
jsr enemy_step
|
||||
lda endflag
|
||||
bne ue_out
|
||||
ue_next:
|
||||
inc ei
|
||||
lda ei
|
||||
cmp #NENEMIES
|
||||
bne ue_loop
|
||||
ue_out:
|
||||
rts
|
||||
|
||||
estag: !byte 0, 2, 4, 6
|
||||
|
||||
enemy_step:
|
||||
jsr rand8 ; 1 in 4: wander off in a new direction
|
||||
and #3
|
||||
beq es_newdir
|
||||
ldx ei
|
||||
lda edir,x
|
||||
jsr etry_dir
|
||||
bcc es_done
|
||||
es_newdir:
|
||||
lda #8
|
||||
sta etries
|
||||
es_pick:
|
||||
jsr rand8
|
||||
and #3
|
||||
jsr etry_dir
|
||||
bcc es_done
|
||||
dec etries ; walled in on all sides: sit this one out
|
||||
bne es_pick
|
||||
es_done:
|
||||
rts
|
||||
|
||||
etry_dir: ; A = direction; C clear when it moved (or won)
|
||||
sta etd
|
||||
tax
|
||||
ldy ei
|
||||
lda ex,y
|
||||
clc
|
||||
adc dxtab,x
|
||||
sta ttx
|
||||
lda ey,y
|
||||
clc
|
||||
adc dytab,x
|
||||
sta tty
|
||||
ldx tty
|
||||
jsr row_ptr
|
||||
ldy ttx
|
||||
lda (scrlo),y
|
||||
cmp #CH_FLOOR
|
||||
beq ed_move
|
||||
cmp #CH_PLAYER
|
||||
beq ed_kill
|
||||
sec ; wall, exit or another enemy: blocked
|
||||
rts
|
||||
ed_kill:
|
||||
lda #2
|
||||
sta endflag
|
||||
clc
|
||||
rts
|
||||
ed_move:
|
||||
ldx ei
|
||||
lda ey,x
|
||||
sta gny ; old position
|
||||
lda ex,x
|
||||
sta gnx
|
||||
lda ttx
|
||||
sta ex,x
|
||||
lda tty
|
||||
sta ey,x
|
||||
lda etd
|
||||
sta edir,x
|
||||
ldx gny ; erase the old cell
|
||||
jsr row_ptr
|
||||
ldy gnx
|
||||
lda #CH_FLOOR
|
||||
sta (scrlo),y
|
||||
lda #BLACK
|
||||
sta (collo),y
|
||||
ldx tty ; draw at the new one
|
||||
jsr row_ptr
|
||||
ldy ttx
|
||||
lda #CH_ENEMY
|
||||
sta (scrlo),y
|
||||
ldx ei
|
||||
lda ecol,x
|
||||
sta (collo),y
|
||||
clc
|
||||
rts
|
||||
|
||||
; ---- per-frame flashes: the exit blinks, the cursor pulses ----
|
||||
upd_flash:
|
||||
ldx exrow
|
||||
jsr row_ptr
|
||||
ldy #38
|
||||
lda framectr
|
||||
and #8
|
||||
bne uf_exw
|
||||
lda #COL_EXIT
|
||||
jmp uf_exset
|
||||
uf_exw:
|
||||
lda #WHITE
|
||||
uf_exset:
|
||||
sta (collo),y
|
||||
ldx ky
|
||||
jsr row_ptr
|
||||
ldy kx
|
||||
lda framectr
|
||||
and #4
|
||||
bne uf_cred
|
||||
lda #YELLOW
|
||||
jmp uf_cset
|
||||
uf_cred:
|
||||
lda #RED
|
||||
uf_cset:
|
||||
sta (collo),y
|
||||
rts
|
||||
|
||||
; ---- play texts ----
|
||||
txt_goal: !scr "reach the "
|
||||
!byte CH_EXIT
|
||||
txt_goale:
|
||||
txt_hint: !scr "p1: joy2/wasd p2: joy1/ijkl"
|
||||
txt_hinte:
|
||||
|
||||
; ---- play variables ----
|
||||
px: !byte 0 ; runner position (screen col / row)
|
||||
py: !byte 0
|
||||
kx: !byte 0 ; corner cursor position (post col / row)
|
||||
ky: !byte 0
|
||||
exrow: !byte 0 ; screen row of the exit diamond
|
||||
endflag: !byte 0 ; 0 = playing, 1 = escaped, 2 = caught
|
||||
rheld: !byte 0 ; runner direction repeat state
|
||||
rtimer: !byte 0
|
||||
rdir: !byte 0
|
||||
cheld: !byte 0 ; cursor direction repeat state
|
||||
ctimer: !byte 0
|
||||
cdir: !byte 0
|
||||
cfheld: !byte 0 ; cursor fire edge detection state
|
||||
j2bits: !byte 0
|
||||
ptmp: !byte 0 ; scratch
|
||||
ttx: !byte 0 ; move target (col / row)
|
||||
tty: !byte 0
|
||||
nch: !byte 0 ; the four slot chars around the cursor
|
||||
ech: !byte 0
|
||||
sch: !byte 0
|
||||
wch: !byte 0
|
||||
ei: !byte 0 ; enemy loop index
|
||||
etries: !byte 0
|
||||
etd: !byte 0
|
||||
gcx: !byte 0 ; maze generator state
|
||||
gcy: !byte 0
|
||||
gsp: !byte 0
|
||||
gn: !byte 0
|
||||
gtd: !byte 0
|
||||
gnx: !byte 0
|
||||
gny: !byte 0
|
||||
gdlist: !fill 4, 0
|
||||
ex: !fill NENEMIES, 0 ; enemy positions, headings, colors
|
||||
ey: !fill NENEMIES, 0
|
||||
edir: !fill NENEMIES, 0
|
||||
ecol: !fill NENEMIES, 0
|
||||
|
||||
; ---- maze generator buffers ----
|
||||
visited: !fill NCELLS, 0
|
||||
stackx: !fill NCELLS, 0
|
||||
stacky: !fill NCELLS, 0
|
||||
@@ -0,0 +1,56 @@
|
||||
; -----------------------------------------------------------
|
||||
; rules.asm - how to play, one static card; fire returns
|
||||
; -----------------------------------------------------------
|
||||
|
||||
rules_init:
|
||||
jsr clear_screen
|
||||
|
||||
+prbig 1, 10, WHITE, txt_rules, 5
|
||||
|
||||
+prtext 8, 9, LGREEN, txt_r1, txt_r1e-txt_r1
|
||||
+prtext 9, 8, WHITE, txt_r2, txt_r2e-txt_r2
|
||||
+prtext 10, 7, WHITE, txt_r3, txt_r3e-txt_r3
|
||||
|
||||
+prtext 12, 9, LBLUE, txt_r4, txt_r4e-txt_r4
|
||||
+prtext 13, 6, WHITE, txt_r5, txt_r5e-txt_r5
|
||||
+prtext 14, 6, WHITE, txt_r6, txt_r6e-txt_r6
|
||||
+prtext 15, 7, WHITE, txt_r7, txt_r7e-txt_r7
|
||||
|
||||
+prtext 17, 7, LRED, txt_r8, txt_r8e-txt_r8
|
||||
|
||||
+setupd rules_update
|
||||
rts
|
||||
|
||||
rules_update:
|
||||
; blink the return prompt
|
||||
lda framectr
|
||||
and #32
|
||||
bne ru_hide
|
||||
+prtext 21, 13, YELLOW, txt_return, 13
|
||||
jmp ru_input
|
||||
ru_hide:
|
||||
+prtext 21, 13, YELLOW, txt_blank, 13
|
||||
ru_input:
|
||||
jsr start_edge
|
||||
beq ru_stay
|
||||
jmp menu_init
|
||||
ru_stay:
|
||||
rts
|
||||
|
||||
; ---- rules texts ----
|
||||
txt_r1: !scr "player 1 - joystick 2"
|
||||
txt_r1e:
|
||||
txt_r2: !scr "run the maze and escape"
|
||||
txt_r2e:
|
||||
txt_r3: !scr "through the green diamond."
|
||||
txt_r3e:
|
||||
txt_r4: !scr "player 2 - joystick 1"
|
||||
txt_r4e:
|
||||
txt_r5: !scr "ride the corners of the walls;"
|
||||
txt_r5e:
|
||||
txt_r6: !scr "fire spins the four wall pieces"
|
||||
txt_r6e:
|
||||
txt_r7: !scr "around the cursor by 90 degrees."
|
||||
txt_r7e:
|
||||
txt_r8: !scr "avoid the colored enemies!"
|
||||
txt_r8e:
|
||||
@@ -0,0 +1,71 @@
|
||||
; -----------------------------------------------------------
|
||||
; sound.asm - single-voice SFX driver: each effect is a
|
||||
; waveform + start pitch + per-frame pitch slide + duration,
|
||||
; played on SID voice 1 (sustain 0, the decay shapes it)
|
||||
; -----------------------------------------------------------
|
||||
|
||||
sfx_init:
|
||||
lda #$0f ; full volume, no filter
|
||||
sta SID_VOL
|
||||
lda #0
|
||||
sta SID_V1CR
|
||||
sta sfx_timer
|
||||
rts
|
||||
|
||||
; ---- start effect A ----
|
||||
sfx_play:
|
||||
tax
|
||||
lda #0
|
||||
sta SID_V1CR ; retrigger the envelope cleanly
|
||||
lda sfxad,x
|
||||
sta SID_V1AD
|
||||
lda #0
|
||||
sta SID_V1SR
|
||||
sta SID_V1FL
|
||||
lda sfxfreq,x
|
||||
sta SID_V1FH
|
||||
sta sfx_fh
|
||||
lda #$08 ; 50% pulse width, if a pulse wave is used
|
||||
sta SID_V1PH
|
||||
lda #0
|
||||
sta SID_V1PL
|
||||
lda sfxctrl,x
|
||||
sta SID_V1CR
|
||||
sta sfx_ctrl
|
||||
lda sfxslide,x
|
||||
sta sfx_slide
|
||||
lda sfxdur,x
|
||||
sta sfx_timer
|
||||
rts
|
||||
|
||||
; ---- called once per frame by the main loop ----
|
||||
sfx_update:
|
||||
lda sfx_timer
|
||||
beq su_idle
|
||||
dec sfx_timer
|
||||
bne su_slide
|
||||
lda sfx_ctrl ; time is up: gate off
|
||||
and #$fe
|
||||
sta SID_V1CR
|
||||
rts
|
||||
su_slide:
|
||||
lda sfx_fh ; bend the pitch by the slide value
|
||||
clc
|
||||
adc sfx_slide
|
||||
sta sfx_fh
|
||||
sta SID_V1FH
|
||||
su_idle:
|
||||
rts
|
||||
|
||||
; ---- effect tables: cursor, select, rotate, deny, win, lose ----
|
||||
sfxfreq: !byte $50, $20, $12, $06, $20, $38
|
||||
sfxslide: !byte $00, $04, $fe, $00, $02, $ff
|
||||
sfxctrl: !byte $11, $21, $81, $21, $11, $21
|
||||
sfxad: !byte $06, $08, $08, $08, $0a, $0a
|
||||
sfxdur: !byte $04, $0a, $08, $0c, $28, $30
|
||||
|
||||
; ---- driver variables ----
|
||||
sfx_timer: !byte 0 ; frames left of the current effect
|
||||
sfx_fh: !byte 0 ; frequency high byte shadow
|
||||
sfx_slide: !byte 0 ; signed per-frame pitch step
|
||||
sfx_ctrl: !byte 0 ; control register shadow
|
||||
@@ -0,0 +1,76 @@
|
||||
; -----------------------------------------------------------
|
||||
; win.asm - the escape screen: big ESCAPED with the gold
|
||||
; wash; fire returns to the main menu
|
||||
; -----------------------------------------------------------
|
||||
|
||||
win_init:
|
||||
jsr clear_screen
|
||||
|
||||
+prbig 3, 6, YELLOW, txt_escaped, 7
|
||||
|
||||
+prtext 11, 15, WHITE, txt_welldone, txt_welldonee-txt_welldone
|
||||
+prtext 13, 9, LGREY, txt_beaten, txt_beatene-txt_beaten
|
||||
|
||||
lda #0
|
||||
sta washphase
|
||||
+setupd win_update
|
||||
rts
|
||||
|
||||
win_update:
|
||||
; the gold wash rolls down the big letters (rows 3-7)
|
||||
inc washphase
|
||||
lda #0
|
||||
sta rtmp2
|
||||
wu_row:
|
||||
lda #3
|
||||
clc
|
||||
adc rtmp2
|
||||
tax
|
||||
jsr row_ptr
|
||||
lda washphase
|
||||
lsr
|
||||
clc
|
||||
adc rtmp2
|
||||
and #15
|
||||
tax
|
||||
lda washtbl,x
|
||||
sta rtmp
|
||||
ldy #32 ; the letters span columns 6-32
|
||||
wu_col:
|
||||
lda (scrlo),y
|
||||
cmp #$a0
|
||||
bne wu_skip
|
||||
lda rtmp
|
||||
sta (collo),y
|
||||
wu_skip:
|
||||
dey
|
||||
cpy #5
|
||||
bne wu_col
|
||||
inc rtmp2
|
||||
lda rtmp2
|
||||
cmp #5
|
||||
bne wu_row
|
||||
|
||||
; blink the prompt
|
||||
lda framectr
|
||||
and #32
|
||||
bne wu_hide
|
||||
+prtext 20, 14, YELLOW, txt_firemenu, txt_firemenue-txt_firemenu
|
||||
jmp wu_input
|
||||
wu_hide:
|
||||
+prtext 20, 14, YELLOW, txt_blank, txt_firemenue-txt_firemenu
|
||||
wu_input:
|
||||
jsr start_edge
|
||||
beq wu_stay
|
||||
jmp menu_init ; a win leads back to the main screen
|
||||
wu_stay:
|
||||
rts
|
||||
|
||||
; ---- win texts ----
|
||||
txt_escaped: !scr "escaped"
|
||||
txt_welldone: !scr "well done!"
|
||||
txt_welldonee:
|
||||
txt_beaten: !scr "you beat the labyrinth"
|
||||
txt_beatene:
|
||||
txt_firemenu: !scr "fire = menu"
|
||||
txt_firemenue:
|
||||
Reference in New Issue
Block a user