Expand collection to thirty programs
This commit is contained in:
parent
ac2fc5ff65
commit
7a583ee313
21 changed files with 1271 additions and 117 deletions
94
README.md
94
README.md
|
|
@ -1,38 +1,43 @@
|
|||
# C64 Program Collection
|
||||
|
||||
Twenty-three small, polished programs for the Commodore 64, written in stock
|
||||
Commodore BASIC V2 and 6502 machine code. The collection mixes arcade action,
|
||||
puzzles, simulations, creative tools, sound, and visual effects. Every entry is
|
||||
self-contained and runs on an ordinary PAL or NTSC C64 without a cartridge or
|
||||
BASIC extension.
|
||||
Thirty small, polished programs for the Commodore 64, written in stock Commodore BASIC V2 and 6502 machine code. The collection mixes arcade action, puzzles, simulations, creative tools, music, utilities, and visual effects. Every entry is self-contained and runs on an ordinary PAL or NTSC C64 without a cartridge or BASIC extension.
|
||||
|
||||
Browse and run the collection at [c64.p20.dev](https://c64.p20.dev).
|
||||
|
||||
## Programs
|
||||
|
||||
| Program | Category | Language | Controls | Description | Source | Run locally |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Meteor Dodge | Arcade | BASIC | `A`, `D`, `Q` | Dodge falling meteors and build your score. | [BAS](meteor-dodge/meteor-dodge.bas) | [Run](http://localhost:8134/program/meteor-dodge) |
|
||||
| Code Breaker | Logic | BASIC | `0`-`9`, Return, `Q` | Deduce a four-digit secret in ten guesses. | [BAS](code-breaker/code-breaker.bas) | [Run](http://localhost:8134/program/code-breaker) |
|
||||
| Submarine Hunt | Strategy | BASIC | Row, Column, Return | Find three hidden submarines with limited sonar shots. | [BAS](submarine-hunt/submarine-hunt.bas) | [Run](http://localhost:8134/program/submarine-hunt) |
|
||||
| Lunar Lander | Simulation | BASIC | `0`-`9`, Return, `-1` | Choose engine burns and touch down gently. | [BAS](lunar-lander/lunar-lander.bas) | [Run](http://localhost:8134/program/lunar-lander) |
|
||||
| Memory Matrix | Memory | BASIC | `1`, `2`, `3`, `4`, `Q` | Repeat a growing sequence of flashing quadrants. | [BAS](memory-matrix/memory-matrix.bas) | [Run](http://localhost:8134/program/memory-matrix) |
|
||||
| Robot Maze | Puzzle | BASIC | `W`, `A`, `S`, `D`, `Q` | Navigate a freshly generated maze to its exit. | [BAS](robot-maze/robot-maze.bas) | [Run](http://localhost:8134/program/robot-maze) |
|
||||
| Music Box | Music | BASIC | `A`-`K`, `1`, `2`, `3`, `Q` | Play the SID chip as a small computer keyboard. | [BAS](music-box/music-box.bas) | [Run](http://localhost:8134/program/music-box) |
|
||||
| Pixel Painter | Creative | BASIC | Cursor keys, Space, `C`, `Q` | Draw character-cell pictures on the screen. | [BAS](pixel-painter/pixel-painter.bas) | [Run](http://localhost:8134/program/pixel-painter) |
|
||||
| Stock Trader | Simulation | BASIC | `B`, `S`, `W`, `Q` | Trade a volatile stock over twenty market days. | [BAS](stock-trader/stock-trader.bas) | [Run](http://localhost:8134/program/stock-trader) |
|
||||
| Starfield | Demo | BASIC | `1`, `2`, `3`, `Q` | Watch and control a layered animated starfield. | [BAS](starfield/starfield.bas) | [Run](http://localhost:8134/program/starfield) |
|
||||
| Snake Trail | Arcade | BASIC | `W`, `A`, `S`, `D`, `Q` | Guide a growing snake through a field of food and danger. | [BAS](snake-trail/snake-trail.bas) | [Run](http://localhost:8134/program/snake-trail) |
|
||||
| Dice Poker | Dice | BASIC | `1`-`5`, Return, `Q` | Keep your best dice and chase a winning poker combination. | [BAS](dice-poker/dice-poker.bas) | [Run](http://localhost:8134/program/dice-poker) |
|
||||
| Word Scramble | Word | BASIC | `A`-`Z`, Return, `Q` | Unscramble five computer-themed words from helpful clues. | [BAS](word-scramble/word-scramble.bas) | [Run](http://localhost:8134/program/word-scramble) |
|
||||
| Nim Challenge | Strategy | BASIC | `1`, `2`, `3`, `0` | Outthink the computer in the classic game of twenty-one sticks. | [BAS](nim-challenge/nim-challenge.bas) | [Run](http://localhost:8134/program/nim-challenge) |
|
||||
| Reaction Timer | Reflex | BASIC | Any key, `Q` | Test your reactions across five unpredictable timed rounds. | [BAS](reaction-timer/reaction-timer.bas) | [Run](http://localhost:8134/program/reaction-timer) |
|
||||
| Morse Trainer | Educational | BASIC | `A`-`Z`, Return, `Q` | Learn Morse letters by reading and hearing their signals. | [BAS](morse-trainer/morse-trainer.bas) | [Run](http://localhost:8134/program/morse-trainer) |
|
||||
| Kaleidoscope | Demo | BASIC | Space, `C`, `Q` | Grow colorful symmetrical patterns across the character screen. | [BAS](kaleidoscope/kaleidoscope.bas) | [Run](http://localhost:8134/program/kaleidoscope) |
|
||||
| World Quiz | Trivia | BASIC | `1`, `2`, `3`, `Q` | Take a seven-stop tour of world geography and landmarks. | [BAS](world-quiz/world-quiz.bas) | [Run](http://localhost:8134/program/world-quiz) |
|
||||
| Life Lab | Simulation | BASIC | Space, `R`, `Q` | Observe generations of Conway's cellular automaton evolve. | [BAS](life-lab/life-lab.bas) | [Run](http://localhost:8134/program/life-lab) |
|
||||
| Cipher Wheel | Utility | BASIC | `E`, `D`, `N`, `Q` | Encode and decode secret messages with a Caesar shift. | [BAS](cipher-wheel/cipher-wheel.bas) | [Run](http://localhost:8134/program/cipher-wheel) |
|
||||
| Sprite Playground | Creative | Machine Code | `W`, `A`, `S`, `D`, `C`, `Q` | Move and recolor a hardware sprite in a responsive VIC-II playground. | [ASM](sprite-playground/sprite-playground.asm) | [Run](http://localhost:8134/program/sprite-playground) |
|
||||
| Raster Bars | Demo | Machine Code | `1`, `2`, `3`, `C`, `Q` | Paint animated bands of color with cycle-aware VIC-II raster timing. | [ASM](raster-bars/raster-bars.asm) | [Run](http://localhost:8134/program/raster-bars) |
|
||||
| SID Arpeggiator | Music | Machine Code | `A`-`K`, `1`-`3`, `+`, `-`, Space, `Q` | Shape a looping SID arpeggio with notes, waveforms, and tempo controls. | [ASM](sid-arpeggiator/sid-arpeggiator.asm) | [Run](http://localhost:8134/program/sid-arpeggiator) |
|
||||
| Program | Released | Category | Language | Controls | Description | Source | Run |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| Meteor Dodge | 2026-10-07 | Arcade | BASIC | `A`, `D`, `Q` | Dodge falling meteors and build your score. | [BAS](meteor-dodge/meteor-dodge.bas) | [Run](https://c64.p20.dev/program/meteor-dodge) |
|
||||
| Code Breaker | 2026-10-07 | Logic | BASIC | `0`-`9`, Return, `Q` | Deduce a four-digit secret in ten guesses. | [BAS](code-breaker/code-breaker.bas) | [Run](https://c64.p20.dev/program/code-breaker) |
|
||||
| Submarine Hunt | 2026-10-07 | Strategy | BASIC | Row, Column, Return | Find three hidden submarines with limited sonar shots. | [BAS](submarine-hunt/submarine-hunt.bas) | [Run](https://c64.p20.dev/program/submarine-hunt) |
|
||||
| Lunar Lander | 2026-10-07 | Simulation | BASIC | `0`-`9`, Return, `-1` | Choose engine burns and touch down gently. | [BAS](lunar-lander/lunar-lander.bas) | [Run](https://c64.p20.dev/program/lunar-lander) |
|
||||
| Memory Matrix | 2026-10-07 | Memory | BASIC | `1`-`4`, `Q` | Repeat a growing sequence of flashing quadrants. | [BAS](memory-matrix/memory-matrix.bas) | [Run](https://c64.p20.dev/program/memory-matrix) |
|
||||
| Robot Maze | 2026-10-07 | Puzzle | BASIC | `W`, `A`, `S`, `D`, `Q` | Navigate a freshly generated maze to its exit. | [BAS](robot-maze/robot-maze.bas) | [Run](https://c64.p20.dev/program/robot-maze) |
|
||||
| Music Box | 2026-10-07 | Music | BASIC | `A`-`K`, `1`-`3`, `Q` | Play the SID chip as a small computer keyboard. | [BAS](music-box/music-box.bas) | [Run](https://c64.p20.dev/program/music-box) |
|
||||
| Pixel Painter | 2026-10-07 | Creative | BASIC | Cursor keys, Space, `C`, `Q` | Draw character-cell pictures on the screen. | [BAS](pixel-painter/pixel-painter.bas) | [Run](https://c64.p20.dev/program/pixel-painter) |
|
||||
| Stock Trader | 2026-10-07 | Simulation | BASIC | `B`, `S`, `W`, `Q` | Trade a volatile stock over twenty market days. | [BAS](stock-trader/stock-trader.bas) | [Run](https://c64.p20.dev/program/stock-trader) |
|
||||
| Starfield | 2026-10-07 | Demo | BASIC | `1`-`3`, `Q` | Watch and control a layered animated starfield. | [BAS](starfield/starfield.bas) | [Run](https://c64.p20.dev/program/starfield) |
|
||||
| Snake Trail | 2026-10-07 | Arcade | BASIC | `W`, `A`, `S`, `D`, `Q` | Guide a growing snake through a field of food and danger. | [BAS](snake-trail/snake-trail.bas) | [Run](https://c64.p20.dev/program/snake-trail) |
|
||||
| Dice Poker | 2026-10-07 | Dice | BASIC | `1`-`5`, Return, `Q` | Keep your best dice and chase a winning poker combination. | [BAS](dice-poker/dice-poker.bas) | [Run](https://c64.p20.dev/program/dice-poker) |
|
||||
| Word Scramble | 2026-10-07 | Word | BASIC | `A`-`Z`, Return, `Q` | Unscramble five computer-themed words from helpful clues. | [BAS](word-scramble/word-scramble.bas) | [Run](https://c64.p20.dev/program/word-scramble) |
|
||||
| Nim Challenge | 2026-10-07 | Strategy | BASIC | `1`-`3`, `0` | Outthink the computer in the classic game of twenty-one sticks. | [BAS](nim-challenge/nim-challenge.bas) | [Run](https://c64.p20.dev/program/nim-challenge) |
|
||||
| Reaction Timer | 2026-10-07 | Reflex | BASIC | Any key, `Q` | Test your reactions across five unpredictable timed rounds. | [BAS](reaction-timer/reaction-timer.bas) | [Run](https://c64.p20.dev/program/reaction-timer) |
|
||||
| Morse Trainer | 2026-10-07 | Educational | BASIC | `A`-`Z`, Return, `Q` | Learn Morse letters by reading and hearing their signals. | [BAS](morse-trainer/morse-trainer.bas) | [Run](https://c64.p20.dev/program/morse-trainer) |
|
||||
| Kaleidoscope | 2026-10-07 | Demo | BASIC | Space, `C`, `Q` | Grow colorful symmetrical patterns across the character screen. | [BAS](kaleidoscope/kaleidoscope.bas) | [Run](https://c64.p20.dev/program/kaleidoscope) |
|
||||
| World Quiz | 2026-10-07 | Trivia | BASIC | `1`-`3`, `Q` | Take a seven-stop tour of world geography and landmarks. | [BAS](world-quiz/world-quiz.bas) | [Run](https://c64.p20.dev/program/world-quiz) |
|
||||
| Life Lab | 2026-10-07 | Simulation | BASIC | Space, `R`, `Q` | Observe generations of Conway's cellular automaton evolve. | [BAS](life-lab/life-lab.bas) | [Run](https://c64.p20.dev/program/life-lab) |
|
||||
| Cipher Wheel | 2026-10-07 | Utility | BASIC | `E`, `D`, `N`, `Q` | Encode and decode secret messages with a Caesar shift. | [BAS](cipher-wheel/cipher-wheel.bas) | [Run](https://c64.p20.dev/program/cipher-wheel) |
|
||||
| Sprite Playground | 2026-10-16 | Creative | Machine Code | `W`, `A`, `S`, `D`, `C`, `Q` | Move and recolor a hardware sprite in a responsive VIC-II playground. | [ASM](sprite-playground/sprite-playground.asm) | [Run](https://c64.p20.dev/program/sprite-playground) |
|
||||
| Raster Bars | 2026-10-16 | Demo | Machine Code | `1`-`3`, `C`, `Q` | Paint animated bands of color with cycle-aware VIC-II raster timing. | [ASM](raster-bars/raster-bars.asm) | [Run](https://c64.p20.dev/program/raster-bars) |
|
||||
| SID Arpeggiator | 2026-10-16 | Music | Machine Code | `A`-`K`, `1`-`3`, `+`, `-`, Space, `Q` | Shape a looping SID arpeggio with notes, waveforms, and tempo controls. | [ASM](sid-arpeggiator/sid-arpeggiator.asm) | [Run](https://c64.p20.dev/program/sid-arpeggiator) |
|
||||
| Paddle Ball | 2026-10-16 | Arcade | Machine Code | `A`, `D`, Space, `Q` | Keep a fast-moving ball in play with a screen-edge paddle. | [ASM](paddle-ball/paddle-ball.asm) | [Run](https://c64.p20.dev/program/paddle-ball) |
|
||||
| Smooth Scroller | 2026-10-16 | Demo | Machine Code | `+`, `-`, `C`, `Q` | Send a message gliding across the screen with VIC-II fine scrolling. | [ASM](smooth-scroller/smooth-scroller.asm) | [Run](https://c64.p20.dev/program/smooth-scroller) |
|
||||
| Fireworks | 2026-10-16 | Demo | Machine Code | Space, `C`, `Q` | Launch colorful PETSCII fireworks into a night sky. | [ASM](fireworks/fireworks.asm) | [Run](https://c64.p20.dev/program/fireworks) |
|
||||
| Jiffy Clock | 2026-10-16 | Utility | Machine Code | `R`, Space, `Q` | Turn the C64 jiffy timer into a large live digital clock. | [ASM](jiffy-clock/jiffy-clock.asm) | [Run](https://c64.p20.dev/program/jiffy-clock) |
|
||||
| Typing Sprint | 2026-10-16 | Word | Machine Code | `A`-`Z`, Space, `Q` | Race the clock while copying a Commodore-themed phrase. | [ASM](typing-sprint/typing-sprint.asm) | [Run](https://c64.p20.dev/program/typing-sprint) |
|
||||
| PETSCII Aquarium | 2026-10-16 | Simulation | Machine Code | `F`, `1`-`3`, `Q` | Watch a tiny animated aquarium and feed its roaming fish. | [ASM](petscii-aquarium/petscii-aquarium.asm) | [Run](https://c64.p20.dev/program/petscii-aquarium) |
|
||||
| Fifteen Puzzle | 2026-10-16 | Puzzle | Machine Code | `W`, `A`, `S`, `D`, `R`, `Q` | Slide numbered tiles until the classic four-by-four board is restored. | [ASM](fifteen-puzzle/fifteen-puzzle.asm) | [Run](https://c64.p20.dev/program/fifteen-puzzle) |
|
||||
|
||||
## Running the programs
|
||||
|
||||
|
|
@ -42,31 +47,22 @@ Start the local browser app with Docker Compose:
|
|||
docker compose up --build -d
|
||||
```
|
||||
|
||||
Open [http://localhost:8134](http://localhost:8134), or use a **Run** link in
|
||||
the table. The app runs the VICE x64sc core through WebAssembly and downloads
|
||||
the selected PRG through its own backend. Click inside the emulator to enable
|
||||
keyboard input and sound. Direct keyboard input is enabled by default, mouse
|
||||
locking is disabled, and moving the pointer anywhere over the emulator reveals
|
||||
its control bar.
|
||||
Open [http://localhost:8134](http://localhost:8134), or visit the hosted collection at [c64.p20.dev](https://c64.p20.dev). The app runs the VICE x64sc core through WebAssembly and downloads the selected PRG through its own backend. Click inside the emulator to enable keyboard input and sound. Direct keyboard input is enabled by default, mouse locking is disabled, and moving the pointer anywhere over the emulator reveals its control bar.
|
||||
|
||||
The repository is mounted read-only at `/programs`. Add a manifest entry and
|
||||
its source and PRG files, then click **Refresh**; the app does not need to be
|
||||
rebuilt. The default host port is configured as `8134` in `.env`.
|
||||
The repository is mounted read-only at `/programs`. Add a manifest entry and its source and PRG files, then click **Refresh**; the app does not need to be rebuilt. The overview renders programs in small batches and is ready to scale without creating every card at once. The default host port is configured as `8134` in `.env`.
|
||||
|
||||
On a Commodore 64, transfer a PRG to disk or another supported medium, then use:
|
||||
|
||||
```text
|
||||
LOAD"METEOR-DODGE.PRG",8
|
||||
LOAD"PADDLE-BALL.PRG",8
|
||||
RUN
|
||||
```
|
||||
|
||||
Substitute the filename and device number as needed. The machine-code PRGs
|
||||
include a small BASIC `SYS` launcher, so the same `RUN` command starts them.
|
||||
Substitute the filename and device number as needed. Machine-code PRGs include a small BASIC `SYS` launcher, so the same `RUN` command starts them.
|
||||
|
||||
## Building machine-code programs
|
||||
|
||||
The assembly sources use ACME syntax. Build every machine-code PRG with the
|
||||
isolated tools profile; no local assembler installation is required:
|
||||
The assembly sources use ACME syntax. Build every machine-code PRG with the isolated tools profile; no local assembler installation is required:
|
||||
|
||||
```text
|
||||
docker compose --profile tools run --rm assembler
|
||||
|
|
@ -78,11 +74,7 @@ The tools profile is opt-in and is not started by the normal app command.
|
|||
|
||||
Each program directory contains a loadable `.prg` and its readable source:
|
||||
|
||||
- BASIC programs use line-numbered, uppercase `.bas` files compatible with
|
||||
stock Commodore BASIC V2. Their PRGs are tokenized at the normal `$0801`
|
||||
BASIC load address.
|
||||
- Machine-code programs use commented ACME `.asm` files. Their PRGs also load
|
||||
at `$0801`, where a short BASIC line starts the 6502 code with `SYS`.
|
||||
- BASIC programs use line-numbered, uppercase `.bas` files compatible with stock Commodore BASIC V2. Their PRGs are tokenized at the normal `$0801` BASIC load address.
|
||||
- Machine-code programs use commented ACME `.asm` files. Their PRGs also load at `$0801`, where a short BASIC line starts the 6502 code with `SYS`.
|
||||
|
||||
All programs are self-contained and designed for ordinary PAL and NTSC C64
|
||||
hardware as well as VICE-compatible emulators.
|
||||
All programs are self-contained and designed for ordinary PAL and NTSC C64 hardware as well as VICE-compatible emulators.
|
||||
|
|
|
|||
155
fifteen-puzzle/fifteen-puzzle.asm
Normal file
155
fifteen-puzzle/fifteen-puzzle.asm
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
title_loop:
|
||||
lda title,x
|
||||
beq shuffle
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne title_loop
|
||||
shuffle:
|
||||
ldx #0
|
||||
copy_board:
|
||||
lda shuffled,x
|
||||
sta board,x
|
||||
inx
|
||||
cpx #16
|
||||
bne copy_board
|
||||
lda #13
|
||||
sta blank
|
||||
main:
|
||||
jsr draw_board
|
||||
wait_key:
|
||||
jsr $ffe4
|
||||
beq wait_key
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #82
|
||||
beq shuffle
|
||||
cmp #87
|
||||
bne key_down
|
||||
lda blank
|
||||
cmp #12
|
||||
bcs wait_key
|
||||
clc
|
||||
adc #4
|
||||
jmp move_tile
|
||||
key_down:
|
||||
cmp #83
|
||||
bne key_left
|
||||
lda blank
|
||||
cmp #4
|
||||
bcc wait_key
|
||||
sec
|
||||
sbc #4
|
||||
jmp move_tile
|
||||
key_left:
|
||||
cmp #65
|
||||
bne key_right
|
||||
lda blank
|
||||
and #3
|
||||
cmp #3
|
||||
beq wait_key
|
||||
lda blank
|
||||
clc
|
||||
adc #1
|
||||
jmp move_tile
|
||||
key_right:
|
||||
cmp #68
|
||||
bne wait_key
|
||||
lda blank
|
||||
and #3
|
||||
beq wait_key
|
||||
lda blank
|
||||
sec
|
||||
sbc #1
|
||||
move_tile:
|
||||
tax
|
||||
ldy blank
|
||||
lda board,x
|
||||
sta board,y
|
||||
lda #0
|
||||
sta board,x
|
||||
stx blank
|
||||
jmp main
|
||||
draw_board:
|
||||
ldx #0
|
||||
draw_loop:
|
||||
stx tile_index
|
||||
lda positions_lo,x
|
||||
sta $fb
|
||||
lda positions_hi,x
|
||||
sta $fc
|
||||
lda board,x
|
||||
beq draw_blank
|
||||
jsr split_digits
|
||||
ldy #0
|
||||
lda tens
|
||||
beq leading_space
|
||||
ora #$30
|
||||
bne store_first
|
||||
leading_space:
|
||||
lda #32
|
||||
store_first:
|
||||
sta ($fb),y
|
||||
iny
|
||||
lda ones
|
||||
ora #$30
|
||||
sta ($fb),y
|
||||
jmp next_tile
|
||||
draw_blank:
|
||||
ldy #0
|
||||
lda #32
|
||||
sta ($fb),y
|
||||
iny
|
||||
sta ($fb),y
|
||||
next_tile:
|
||||
ldx tile_index
|
||||
inx
|
||||
cpx #16
|
||||
bne draw_loop
|
||||
rts
|
||||
split_digits:
|
||||
ldy #0
|
||||
split_loop:
|
||||
cmp #10
|
||||
bcc split_done
|
||||
sec
|
||||
sbc #10
|
||||
iny
|
||||
bne split_loop
|
||||
split_done:
|
||||
sty tens
|
||||
sta ones
|
||||
rts
|
||||
exit:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "FIFTEEN PUZZLE WASD SLIDE R RESET Q QUIT"
|
||||
!byte 13,13,13,0
|
||||
shuffled: !byte 1,2,3,4,5,6,7,8,9,10,11,12,13,0,14,15
|
||||
board: !fill 16,0
|
||||
positions_lo:
|
||||
!for row,0,3 { !for col,0,3 { !byte <($04f5+row*80+col*5) } }
|
||||
positions_hi:
|
||||
!for row,0,3 { !for col,0,3 { !byte >($04f5+row*80+col*5) } }
|
||||
blank: !byte 0
|
||||
tile_index: !byte 0
|
||||
tens: !byte 0
|
||||
ones: !byte 0
|
||||
BIN
fifteen-puzzle/fifteen-puzzle.prg
Normal file
BIN
fifteen-puzzle/fifteen-puzzle.prg
Normal file
Binary file not shown.
169
fireworks/fireworks.asm
Normal file
169
fireworks/fireworks.asm
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda $d020
|
||||
sta old_border
|
||||
lda $d021
|
||||
sta old_background
|
||||
lda #0
|
||||
sta $d020
|
||||
sta $d021
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
print_title:
|
||||
lda title,x
|
||||
beq setup
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne print_title
|
||||
setup:
|
||||
lda #7
|
||||
sta center_x
|
||||
lda #5
|
||||
sta burst_color
|
||||
main:
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #67
|
||||
bne launch_key
|
||||
inc burst_color
|
||||
lda burst_color
|
||||
and #15
|
||||
sta burst_color
|
||||
launch_key:
|
||||
cmp #32
|
||||
bne main
|
||||
jsr launch
|
||||
jmp main
|
||||
launch:
|
||||
lda #22
|
||||
sta rocket_y
|
||||
rise:
|
||||
ldx center_x
|
||||
ldy rocket_y
|
||||
lda #46
|
||||
jsr plot
|
||||
jsr delay
|
||||
ldx center_x
|
||||
ldy rocket_y
|
||||
lda #32
|
||||
jsr plot
|
||||
dec rocket_y
|
||||
lda rocket_y
|
||||
cmp #7
|
||||
bne rise
|
||||
ldx #0
|
||||
burst_loop:
|
||||
stx particle_index
|
||||
lda burst_dx,x
|
||||
clc
|
||||
adc center_x
|
||||
sta px
|
||||
lda burst_dy,x
|
||||
clc
|
||||
adc rocket_y
|
||||
tay
|
||||
ldx px
|
||||
lda #42
|
||||
jsr plot
|
||||
ldx particle_index
|
||||
inx
|
||||
cpx #12
|
||||
bne burst_loop
|
||||
jsr long_delay
|
||||
ldx #0
|
||||
clear_burst:
|
||||
stx particle_index
|
||||
lda burst_dx,x
|
||||
clc
|
||||
adc center_x
|
||||
sta px
|
||||
lda burst_dy,x
|
||||
clc
|
||||
adc rocket_y
|
||||
tay
|
||||
ldx px
|
||||
lda #32
|
||||
jsr plot
|
||||
ldx particle_index
|
||||
inx
|
||||
cpx #12
|
||||
bne clear_burst
|
||||
lda center_x
|
||||
clc
|
||||
adc #11
|
||||
cmp #35
|
||||
bcc save_center
|
||||
sbc #31
|
||||
save_center:
|
||||
sta center_x
|
||||
rts
|
||||
plot:
|
||||
sta char_value
|
||||
lda row_lo,y
|
||||
sta $fb
|
||||
lda row_hi,y
|
||||
sta $fc
|
||||
txa
|
||||
tay
|
||||
lda char_value
|
||||
sta ($fb),y
|
||||
lda $fb
|
||||
clc
|
||||
adc #<$d400
|
||||
sta $fd
|
||||
lda $fc
|
||||
adc #>$d400
|
||||
sta $fe
|
||||
lda burst_color
|
||||
sta ($fd),y
|
||||
rts
|
||||
delay:
|
||||
ldx #8
|
||||
delay_outer:
|
||||
ldy #0
|
||||
delay_inner:
|
||||
dey
|
||||
bne delay_inner
|
||||
dex
|
||||
bne delay_outer
|
||||
rts
|
||||
long_delay:
|
||||
ldx #45
|
||||
bne delay_outer
|
||||
exit:
|
||||
lda old_border
|
||||
sta $d020
|
||||
lda old_background
|
||||
sta $d021
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "FIREWORKS SPACE LAUNCH C COLOR Q QUIT"
|
||||
!byte 13,0
|
||||
burst_dx: !byte 0,1,2,$ff,$fe,0,0,1,2,$ff,$fe,0
|
||||
burst_dy: !byte $fe,$ff,0,$ff,0,2,1,1,2,1,2,$ff
|
||||
row_lo: !for i,0,24 { !byte <($0400+i*40) }
|
||||
row_hi: !for i,0,24 { !byte >($0400+i*40) }
|
||||
center_x: !byte 0
|
||||
rocket_y: !byte 0
|
||||
burst_color: !byte 0
|
||||
particle_index: !byte 0
|
||||
px: !byte 0
|
||||
char_value: !byte 0
|
||||
old_border: !byte 0
|
||||
old_background: !byte 0
|
||||
BIN
fireworks/fireworks.prg
Normal file
BIN
fireworks/fireworks.prg
Normal file
Binary file not shown.
124
jiffy-clock/jiffy-clock.asm
Normal file
124
jiffy-clock/jiffy-clock.asm
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
print_title:
|
||||
lda title,x
|
||||
beq reset
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne print_title
|
||||
reset:
|
||||
lda #0
|
||||
sta frames
|
||||
sta seconds
|
||||
sta minutes
|
||||
sta paused
|
||||
lda $a2
|
||||
sta last_jiffy
|
||||
main:
|
||||
jsr wait_frame
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #82
|
||||
beq reset
|
||||
cmp #32
|
||||
bne update
|
||||
lda paused
|
||||
eor #1
|
||||
sta paused
|
||||
update:
|
||||
lda paused
|
||||
bne display
|
||||
lda $a2
|
||||
cmp last_jiffy
|
||||
beq display
|
||||
sta last_jiffy
|
||||
inc frames
|
||||
lda frames
|
||||
cmp #60
|
||||
bne display
|
||||
lda #0
|
||||
sta frames
|
||||
inc seconds
|
||||
lda seconds
|
||||
cmp #60
|
||||
bne display
|
||||
lda #0
|
||||
sta seconds
|
||||
inc minutes
|
||||
lda minutes
|
||||
cmp #100
|
||||
bne display
|
||||
lda #0
|
||||
sta minutes
|
||||
display:
|
||||
lda minutes
|
||||
jsr split_digits
|
||||
lda tens
|
||||
ora #$30
|
||||
sta $05f0
|
||||
lda ones
|
||||
ora #$30
|
||||
sta $05f1
|
||||
lda #58
|
||||
sta $05f2
|
||||
lda seconds
|
||||
jsr split_digits
|
||||
lda tens
|
||||
ora #$30
|
||||
sta $05f3
|
||||
lda ones
|
||||
ora #$30
|
||||
sta $05f4
|
||||
jmp main
|
||||
split_digits:
|
||||
ldx #0
|
||||
subtract_ten:
|
||||
cmp #10
|
||||
bcc split_done
|
||||
sec
|
||||
sbc #10
|
||||
inx
|
||||
bne subtract_ten
|
||||
split_done:
|
||||
stx tens
|
||||
sta ones
|
||||
rts
|
||||
wait_frame:
|
||||
lda #250
|
||||
wait_raster:
|
||||
cmp $d012
|
||||
bne wait_raster
|
||||
wait_leave:
|
||||
cmp $d012
|
||||
beq wait_leave
|
||||
rts
|
||||
exit:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "JIFFY CLOCK R RESET SPACE PAUSE Q QUIT"
|
||||
!byte 13,13,13,13,13,13,13,13,13,13,0
|
||||
frames: !byte 0
|
||||
seconds: !byte 0
|
||||
minutes: !byte 0
|
||||
paused: !byte 0
|
||||
last_jiffy: !byte 0
|
||||
tens: !byte 0
|
||||
ones: !byte 0
|
||||
BIN
jiffy-clock/jiffy-clock.prg
Normal file
BIN
jiffy-clock/jiffy-clock.prg
Normal file
Binary file not shown.
189
paddle-ball/paddle-ball.asm
Normal file
189
paddle-ball/paddle-ball.asm
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
!cpu 6510
|
||||
|
||||
; Self-starting BASIC line: 10 SYS2061
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda $d020
|
||||
sta old_border
|
||||
lda $d021
|
||||
sta old_background
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
title_loop:
|
||||
lda title,x
|
||||
beq new_ball
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne title_loop
|
||||
new_ball:
|
||||
lda #20
|
||||
sta paddle_x
|
||||
lda #20
|
||||
sta ball_x
|
||||
lda #10
|
||||
sta ball_y
|
||||
lda #1
|
||||
sta dx
|
||||
sta dy
|
||||
main_loop:
|
||||
jsr erase_objects
|
||||
jsr read_key
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #65
|
||||
bne check_right
|
||||
lda paddle_x
|
||||
beq check_right
|
||||
dec paddle_x
|
||||
check_right:
|
||||
cmp #68
|
||||
bne update_ball
|
||||
lda paddle_x
|
||||
cmp #34
|
||||
beq update_ball
|
||||
inc paddle_x
|
||||
update_ball:
|
||||
lda ball_x
|
||||
clc
|
||||
adc dx
|
||||
sta ball_x
|
||||
beq bounce_x
|
||||
cmp #39
|
||||
bne move_y
|
||||
bounce_x:
|
||||
lda dx
|
||||
eor #$fe
|
||||
clc
|
||||
adc #1
|
||||
sta dx
|
||||
move_y:
|
||||
lda ball_y
|
||||
clc
|
||||
adc dy
|
||||
sta ball_y
|
||||
beq bounce_y
|
||||
cmp #22
|
||||
bne draw
|
||||
lda ball_x
|
||||
cmp paddle_x
|
||||
bcc missed
|
||||
sec
|
||||
sbc paddle_x
|
||||
cmp #6
|
||||
bcc bounce_y
|
||||
missed:
|
||||
jsr draw_objects
|
||||
wait_serve:
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne serve_key
|
||||
jmp exit
|
||||
serve_key:
|
||||
cmp #32
|
||||
bne wait_serve
|
||||
jmp new_ball
|
||||
bounce_y:
|
||||
lda dy
|
||||
eor #$fe
|
||||
clc
|
||||
adc #1
|
||||
sta dy
|
||||
draw:
|
||||
jsr draw_objects
|
||||
ldx #6
|
||||
delay_outer:
|
||||
ldy #0
|
||||
delay_inner:
|
||||
dey
|
||||
bne delay_inner
|
||||
dex
|
||||
bne delay_outer
|
||||
jmp main_loop
|
||||
erase_objects:
|
||||
lda #32
|
||||
jsr draw_ball
|
||||
ldx #5
|
||||
erase_paddle:
|
||||
txa
|
||||
pha
|
||||
clc
|
||||
adc paddle_x
|
||||
tax
|
||||
ldy #23
|
||||
lda #32
|
||||
jsr plot
|
||||
pla
|
||||
tax
|
||||
dex
|
||||
bpl erase_paddle
|
||||
rts
|
||||
draw_objects:
|
||||
lda #81
|
||||
jsr draw_ball
|
||||
ldx #5
|
||||
draw_paddle:
|
||||
txa
|
||||
pha
|
||||
clc
|
||||
adc paddle_x
|
||||
tax
|
||||
ldy #23
|
||||
lda #160
|
||||
jsr plot
|
||||
pla
|
||||
tax
|
||||
dex
|
||||
bpl draw_paddle
|
||||
rts
|
||||
draw_ball:
|
||||
pha
|
||||
ldx ball_x
|
||||
ldy ball_y
|
||||
pla
|
||||
plot:
|
||||
sta char_value
|
||||
lda row_lo,y
|
||||
sta $fb
|
||||
lda row_hi,y
|
||||
sta $fc
|
||||
txa
|
||||
tay
|
||||
lda char_value
|
||||
sta ($fb),y
|
||||
rts
|
||||
read_key:
|
||||
jsr $ffe4
|
||||
rts
|
||||
exit:
|
||||
lda old_border
|
||||
sta $d020
|
||||
lda old_background
|
||||
sta $d021
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "PADDLE BALL A/D MOVE SPACE SERVE Q QUIT"
|
||||
!byte 13,0
|
||||
row_lo: !for i,0,24 { !byte <($0400+i*40) }
|
||||
row_hi: !for i,0,24 { !byte >($0400+i*40) }
|
||||
paddle_x: !byte 0
|
||||
ball_x: !byte 0
|
||||
ball_y: !byte 0
|
||||
dx: !byte 0
|
||||
dy: !byte 0
|
||||
char_value: !byte 0
|
||||
old_border: !byte 0
|
||||
old_background: !byte 0
|
||||
BIN
paddle-ball/paddle-ball.prg
Normal file
BIN
paddle-ball/paddle-ball.prg
Normal file
Binary file not shown.
161
petscii-aquarium/petscii-aquarium.asm
Normal file
161
petscii-aquarium/petscii-aquarium.asm
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda $d020
|
||||
sta old_border
|
||||
lda $d021
|
||||
sta old_background
|
||||
lda #6
|
||||
sta $d020
|
||||
lda #14
|
||||
sta $d021
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
title_loop:
|
||||
lda title,x
|
||||
beq setup
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne title_loop
|
||||
setup:
|
||||
lda #4
|
||||
sta speed
|
||||
lda #0
|
||||
sta tick
|
||||
sta food_y
|
||||
main:
|
||||
jsr wait_frame
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #70
|
||||
bne speed_keys
|
||||
lda #3
|
||||
sta food_x
|
||||
lda #2
|
||||
sta food_y
|
||||
speed_keys:
|
||||
cmp #49
|
||||
bne speed_two
|
||||
lda #7
|
||||
sta speed
|
||||
speed_two:
|
||||
cmp #50
|
||||
bne speed_three
|
||||
lda #4
|
||||
sta speed
|
||||
speed_three:
|
||||
cmp #51
|
||||
bne animate
|
||||
lda #2
|
||||
sta speed
|
||||
animate:
|
||||
inc tick
|
||||
lda tick
|
||||
cmp speed
|
||||
bcc main
|
||||
lda #0
|
||||
sta tick
|
||||
ldx #0
|
||||
fish_loop:
|
||||
stx fish_index
|
||||
lda fish_x,x
|
||||
sta old_x
|
||||
ldy fish_y,x
|
||||
ldx old_x
|
||||
lda #32
|
||||
jsr plot
|
||||
ldx fish_index
|
||||
inc fish_x,x
|
||||
lda fish_x,x
|
||||
cmp #39
|
||||
bcc draw_fish
|
||||
lda #0
|
||||
sta fish_x,x
|
||||
draw_fish:
|
||||
sta old_x
|
||||
ldy fish_y,x
|
||||
ldx old_x
|
||||
lda #62
|
||||
jsr plot
|
||||
ldx fish_index
|
||||
inx
|
||||
cpx #5
|
||||
bne fish_loop
|
||||
lda food_y
|
||||
bne move_food
|
||||
jmp main
|
||||
move_food:
|
||||
ldx food_x
|
||||
ldy food_y
|
||||
lda #32
|
||||
jsr plot
|
||||
inc food_y
|
||||
lda food_y
|
||||
cmp #22
|
||||
bcc draw_food
|
||||
lda #0
|
||||
sta food_y
|
||||
jmp main
|
||||
draw_food:
|
||||
tay
|
||||
ldx food_x
|
||||
lda #46
|
||||
jsr plot
|
||||
jmp main
|
||||
plot:
|
||||
sta char_value
|
||||
lda row_lo,y
|
||||
sta $fb
|
||||
lda row_hi,y
|
||||
sta $fc
|
||||
txa
|
||||
tay
|
||||
lda char_value
|
||||
sta ($fb),y
|
||||
rts
|
||||
wait_frame:
|
||||
lda #250
|
||||
wait_raster:
|
||||
cmp $d012
|
||||
bne wait_raster
|
||||
wait_leave:
|
||||
cmp $d012
|
||||
beq wait_leave
|
||||
rts
|
||||
exit:
|
||||
lda old_border
|
||||
sta $d020
|
||||
lda old_background
|
||||
sta $d021
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "PETSCII AQUARIUM F FEED 1-3 SPEED Q QUIT"
|
||||
!byte 13,0
|
||||
fish_x: !byte 2,9,16,23,30
|
||||
fish_y: !byte 5,9,13,17,20
|
||||
row_lo: !for i,0,24 { !byte <($0400+i*40) }
|
||||
row_hi: !for i,0,24 { !byte >($0400+i*40) }
|
||||
speed: !byte 0
|
||||
tick: !byte 0
|
||||
food_x: !byte 0
|
||||
food_y: !byte 0
|
||||
fish_index: !byte 0
|
||||
old_x: !byte 0
|
||||
char_value: !byte 0
|
||||
old_border: !byte 0
|
||||
old_background: !byte 0
|
||||
BIN
petscii-aquarium/petscii-aquarium.prg
Normal file
BIN
petscii-aquarium/petscii-aquarium.prg
Normal file
Binary file not shown.
|
|
@ -1,140 +1,182 @@
|
|||
[
|
||||
{
|
||||
"title": "Meteor Dodge", "category": "Arcade", "language": "basic",
|
||||
"title": "Meteor Dodge", "category": "Arcade", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Dodge falling meteors and build your score.",
|
||||
"controls": [{"keys":["A"],"description":"Move left"},{"keys":["D"],"description":"Move right"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "meteor-dodge/meteor-dodge.prg", "source": "meteor-dodge/meteor-dodge.bas"
|
||||
},
|
||||
{
|
||||
"title": "Code Breaker", "category": "Logic", "language": "basic",
|
||||
"title": "Code Breaker", "category": "Logic", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Deduce a four-digit secret in ten guesses.",
|
||||
"controls": [{"keys":["0-9"],"description":"Enter a four-digit guess"},{"keys":["RETURN"],"description":"Submit the guess"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "code-breaker/code-breaker.prg", "source": "code-breaker/code-breaker.bas"
|
||||
},
|
||||
{
|
||||
"title": "Submarine Hunt", "category": "Strategy", "language": "basic",
|
||||
"title": "Submarine Hunt", "category": "Strategy", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Find three hidden submarines with limited sonar shots.",
|
||||
"controls": [{"keys":["ROW","COLUMN"],"description":"Enter sonar coordinates"},{"keys":["RETURN"],"description":"Fire a sonar shot"},{"keys":["0","0"],"description":"Quit"}],
|
||||
"path": "submarine-hunt/submarine-hunt.prg", "source": "submarine-hunt/submarine-hunt.bas"
|
||||
},
|
||||
{
|
||||
"title": "Lunar Lander", "category": "Simulation", "language": "basic",
|
||||
"title": "Lunar Lander", "category": "Simulation", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Choose engine burns and touch down gently.",
|
||||
"controls": [{"keys":["0-9"],"description":"Choose engine burn strength"},{"keys":["RETURN"],"description":"Commit the burn"},{"keys":["-1"],"description":"Quit"}],
|
||||
"path": "lunar-lander/lunar-lander.prg", "source": "lunar-lander/lunar-lander.bas"
|
||||
},
|
||||
{
|
||||
"title": "Memory Matrix", "category": "Memory", "language": "basic",
|
||||
"title": "Memory Matrix", "category": "Memory", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Repeat a growing sequence of flashing quadrants.",
|
||||
"controls": [{"keys":["1","2","3","4"],"description":"Repeat the flashed quadrant"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "memory-matrix/memory-matrix.prg", "source": "memory-matrix/memory-matrix.bas"
|
||||
},
|
||||
{
|
||||
"title": "Robot Maze", "category": "Puzzle", "language": "basic",
|
||||
"title": "Robot Maze", "category": "Puzzle", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Navigate a freshly generated maze to its exit.",
|
||||
"controls": [{"keys":["W","A","S","D"],"description":"Move the robot"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "robot-maze/robot-maze.prg", "source": "robot-maze/robot-maze.bas"
|
||||
},
|
||||
{
|
||||
"title": "Music Box", "category": "Music", "language": "basic",
|
||||
"title": "Music Box", "category": "Music", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Play the SID chip as a small computer keyboard.",
|
||||
"controls": [{"keys":["A-K"],"description":"Play notes"},{"keys":["1","2","3"],"description":"Select square, saw, or triangle wave"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "music-box/music-box.prg", "source": "music-box/music-box.bas"
|
||||
},
|
||||
{
|
||||
"title": "Pixel Painter", "category": "Creative", "language": "basic",
|
||||
"title": "Pixel Painter", "category": "Creative", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Draw character-cell pictures on the screen.",
|
||||
"controls": [{"keys":["CURSOR KEYS"],"description":"Move the drawing cursor"},{"keys":["SPACE"],"description":"Draw a pixel"},{"keys":["C"],"description":"Clear the canvas"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "pixel-painter/pixel-painter.prg", "source": "pixel-painter/pixel-painter.bas"
|
||||
},
|
||||
{
|
||||
"title": "Stock Trader", "category": "Simulation", "language": "basic",
|
||||
"title": "Stock Trader", "category": "Simulation", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Trade a volatile stock over twenty market days.",
|
||||
"controls": [{"keys":["B"],"description":"Buy shares"},{"keys":["S"],"description":"Sell shares"},{"keys":["W"],"description":"Wait for the next day"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "stock-trader/stock-trader.prg", "source": "stock-trader/stock-trader.bas"
|
||||
},
|
||||
{
|
||||
"title": "Starfield", "category": "Demo", "language": "basic",
|
||||
"title": "Starfield", "category": "Demo", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Watch and control a layered animated starfield.",
|
||||
"controls": [{"keys":["1","2","3"],"description":"Select slow, medium, or fast speed"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "starfield/starfield.prg", "source": "starfield/starfield.bas"
|
||||
},
|
||||
{
|
||||
"title": "Snake Trail", "category": "Arcade", "language": "basic",
|
||||
"title": "Snake Trail", "category": "Arcade", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Guide a growing snake through a field of food and danger.",
|
||||
"controls": [{"keys":["W","A","S","D"],"description":"Steer the snake"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "snake-trail/snake-trail.prg", "source": "snake-trail/snake-trail.bas"
|
||||
},
|
||||
{
|
||||
"title": "Dice Poker", "category": "Dice", "language": "basic",
|
||||
"title": "Dice Poker", "category": "Dice", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Keep your best dice and chase a winning poker combination.",
|
||||
"controls": [{"keys":["1-5"],"description":"Choose dice positions to keep"},{"keys":["RETURN"],"description":"Roll the remaining dice"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "dice-poker/dice-poker.prg", "source": "dice-poker/dice-poker.bas"
|
||||
},
|
||||
{
|
||||
"title": "Word Scramble", "category": "Word", "language": "basic",
|
||||
"title": "Word Scramble", "category": "Word", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Unscramble five computer-themed words from helpful clues.",
|
||||
"controls": [{"keys":["A-Z"],"description":"Type your answer"},{"keys":["RETURN"],"description":"Submit the answer"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "word-scramble/word-scramble.prg", "source": "word-scramble/word-scramble.bas"
|
||||
},
|
||||
{
|
||||
"title": "Nim Challenge", "category": "Strategy", "language": "basic",
|
||||
"title": "Nim Challenge", "category": "Strategy", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Outthink the computer in the classic game of twenty-one sticks.",
|
||||
"controls": [{"keys":["1","2","3"],"description":"Choose how many sticks to take"},{"keys":["0"],"description":"Quit"}],
|
||||
"path": "nim-challenge/nim-challenge.prg", "source": "nim-challenge/nim-challenge.bas"
|
||||
},
|
||||
{
|
||||
"title": "Reaction Timer", "category": "Reflex", "language": "basic",
|
||||
"title": "Reaction Timer", "category": "Reflex", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Test your reactions across five unpredictable timed rounds.",
|
||||
"controls": [{"keys":["ANY KEY"],"description":"React when NOW appears"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "reaction-timer/reaction-timer.prg", "source": "reaction-timer/reaction-timer.bas"
|
||||
},
|
||||
{
|
||||
"title": "Morse Trainer", "category": "Educational", "language": "basic",
|
||||
"title": "Morse Trainer", "category": "Educational", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Learn Morse letters by reading and hearing their signals.",
|
||||
"controls": [{"keys":["A-Z"],"description":"Enter the decoded letter"},{"keys":["RETURN"],"description":"Submit the answer"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "morse-trainer/morse-trainer.prg", "source": "morse-trainer/morse-trainer.bas"
|
||||
},
|
||||
{
|
||||
"title": "Kaleidoscope", "category": "Demo", "language": "basic",
|
||||
"title": "Kaleidoscope", "category": "Demo", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Grow colorful symmetrical patterns across the character screen.",
|
||||
"controls": [{"keys":["SPACE"],"description":"Start a new pattern"},{"keys":["C"],"description":"Change color"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "kaleidoscope/kaleidoscope.prg", "source": "kaleidoscope/kaleidoscope.bas"
|
||||
},
|
||||
{
|
||||
"title": "World Quiz", "category": "Trivia", "language": "basic",
|
||||
"title": "World Quiz", "category": "Trivia", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Take a seven-stop tour of world geography and landmarks.",
|
||||
"controls": [{"keys":["1","2","3"],"description":"Choose an answer"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "world-quiz/world-quiz.prg", "source": "world-quiz/world-quiz.bas"
|
||||
},
|
||||
{
|
||||
"title": "Life Lab", "category": "Simulation", "language": "basic",
|
||||
"title": "Life Lab", "category": "Simulation", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Observe generations of Conway's cellular automaton evolve.",
|
||||
"controls": [{"keys":["SPACE"],"description":"Pause or continue"},{"keys":["R"],"description":"Create a new random colony"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "life-lab/life-lab.prg", "source": "life-lab/life-lab.bas"
|
||||
},
|
||||
{
|
||||
"title": "Cipher Wheel", "category": "Utility", "language": "basic",
|
||||
"title": "Cipher Wheel", "category": "Utility", "language": "basic", "releaseDate": "2026-10-07",
|
||||
"description": "Encode and decode secret messages with a Caesar shift.",
|
||||
"controls": [{"keys":["E","D"],"description":"Choose encode or decode"},{"keys":["N"],"description":"Enter another message"},{"keys":["Q"],"description":"Quit"}],
|
||||
"path": "cipher-wheel/cipher-wheel.prg", "source": "cipher-wheel/cipher-wheel.bas"
|
||||
},
|
||||
{
|
||||
"title": "Sprite Playground", "category": "Creative", "language": "machine-code",
|
||||
"title": "Sprite Playground", "category": "Creative", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Move and recolor a hardware sprite in a responsive VIC-II playground.",
|
||||
"controls": [{"keys":["W","A","S","D"],"description":"Move the sprite"},{"keys":["C"],"description":"Change sprite color"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "sprite-playground/sprite-playground.prg", "source": "sprite-playground/sprite-playground.asm"
|
||||
},
|
||||
{
|
||||
"title": "Raster Bars", "category": "Demo", "language": "machine-code",
|
||||
"title": "Raster Bars", "category": "Demo", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Paint animated bands of color with cycle-aware VIC-II raster timing.",
|
||||
"controls": [{"keys":["1","2","3"],"description":"Select slow, medium, or fast movement"},{"keys":["C"],"description":"Change color palette"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "raster-bars/raster-bars.prg", "source": "raster-bars/raster-bars.asm"
|
||||
},
|
||||
{
|
||||
"title": "SID Arpeggiator", "category": "Music", "language": "machine-code",
|
||||
"title": "SID Arpeggiator", "category": "Music", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Shape a looping SID arpeggio with notes, waveforms, and tempo controls.",
|
||||
"controls": [{"keys":["A-K"],"description":"Select the root note"},{"keys":["1","2","3"],"description":"Select triangle, saw, or pulse wave"},{"keys":["+","-"],"description":"Increase or decrease tempo"},{"keys":["SPACE"],"description":"Pause or resume playback"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "sid-arpeggiator/sid-arpeggiator.prg", "source": "sid-arpeggiator/sid-arpeggiator.asm"
|
||||
},
|
||||
{
|
||||
"title": "Paddle Ball", "category": "Arcade", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Keep a fast-moving ball in play with a screen-edge paddle.",
|
||||
"controls": [{"keys":["A","D"],"description":"Move the paddle left or right"},{"keys":["SPACE"],"description":"Serve a new ball"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "paddle-ball/paddle-ball.prg", "source": "paddle-ball/paddle-ball.asm"
|
||||
},
|
||||
{
|
||||
"title": "Smooth Scroller", "category": "Demo", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Send a message gliding across the screen with VIC-II fine scrolling.",
|
||||
"controls": [{"keys":["+","-"],"description":"Increase or decrease scroll speed"},{"keys":["C"],"description":"Change text color"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "smooth-scroller/smooth-scroller.prg", "source": "smooth-scroller/smooth-scroller.asm"
|
||||
},
|
||||
{
|
||||
"title": "Fireworks", "category": "Demo", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Launch colorful PETSCII fireworks into a night sky.",
|
||||
"controls": [{"keys":["SPACE"],"description":"Launch a firework"},{"keys":["C"],"description":"Choose the next burst color"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "fireworks/fireworks.prg", "source": "fireworks/fireworks.asm"
|
||||
},
|
||||
{
|
||||
"title": "Jiffy Clock", "category": "Utility", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Turn the C64 jiffy timer into a large live digital clock.",
|
||||
"controls": [{"keys":["R"],"description":"Reset the elapsed time"},{"keys":["SPACE"],"description":"Pause or resume the clock"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "jiffy-clock/jiffy-clock.prg", "source": "jiffy-clock/jiffy-clock.asm"
|
||||
},
|
||||
{
|
||||
"title": "Typing Sprint", "category": "Word", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Race the clock while copying a Commodore-themed phrase.",
|
||||
"controls": [{"keys":["A-Z"],"description":"Type the highlighted letters"},{"keys":["SPACE"],"description":"Start a new sprint"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "typing-sprint/typing-sprint.prg", "source": "typing-sprint/typing-sprint.asm"
|
||||
},
|
||||
{
|
||||
"title": "PETSCII Aquarium", "category": "Simulation", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Watch a tiny animated aquarium and feed its roaming fish.",
|
||||
"controls": [{"keys":["F"],"description":"Drop fish food into the tank"},{"keys":["1","2","3"],"description":"Select animation speed"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "petscii-aquarium/petscii-aquarium.prg", "source": "petscii-aquarium/petscii-aquarium.asm"
|
||||
},
|
||||
{
|
||||
"title": "Fifteen Puzzle", "category": "Puzzle", "language": "machine-code", "releaseDate": "2026-10-16",
|
||||
"description": "Slide numbered tiles until the classic four-by-four board is restored.",
|
||||
"controls": [{"keys":["W","A","S","D"],"description":"Slide a tile toward the empty space"},{"keys":["R"],"description":"Shuffle the board again"},{"keys":["Q"],"description":"Return to BASIC"}],
|
||||
"path": "fifteen-puzzle/fifteen-puzzle.prg", "source": "fifteen-puzzle/fifteen-puzzle.asm"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,25 +16,18 @@ const sourceStatus = document.querySelector('#source-status');
|
|||
const sourceCode = document.querySelector('#source-code');
|
||||
const programBadges = document.querySelector('#program-badges');
|
||||
const programDescription = document.querySelector('#program-description');
|
||||
const programReleaseDate = document.querySelector('#program-release-date');
|
||||
const programControls = document.querySelector('#program-controls');
|
||||
const programSentinel = document.querySelector('#program-sentinel');
|
||||
let allPrograms = [];
|
||||
let filteredPrograms = [];
|
||||
let visibleProgramCount = 0;
|
||||
const PROGRAM_BATCH_SIZE = 12;
|
||||
|
||||
const categoryMeta = {
|
||||
Arcade: { icon: '🎮', color: '#d65f5f' },
|
||||
Creative: { icon: '✎', color: '#65b96d' },
|
||||
Demo: { icon: '✦', color: '#75a9dc' },
|
||||
Dice: { icon: '⚄', color: '#d8d4c4' },
|
||||
Educational: { icon: '⌁', color: '#b98a64' },
|
||||
Logic: { icon: '◆', color: '#67b6bd' },
|
||||
Memory: { icon: '◫', color: '#9b6bd6' },
|
||||
Music: { icon: '♪', color: '#d668a4' },
|
||||
Puzzle: { icon: '⌘', color: '#d6c85c' },
|
||||
Reflex: { icon: '⚡', color: '#ef725f' },
|
||||
Simulation: { icon: '⚙', color: '#6d8ed6' },
|
||||
Strategy: { icon: '♟', color: '#d98f45' },
|
||||
Trivia: { icon: '?', color: '#dfaa45' },
|
||||
Utility: { icon: '⚒', color: '#a5acb8' },
|
||||
Word: { icon: 'Aa', color: '#55b7a4' }
|
||||
Arcade: '🎮', Creative: '✎', Demo: '✦', Dice: '⚄', Educational: '⌁',
|
||||
Logic: '◆', Memory: '◫', Music: '♪', Puzzle: '⌘', Reflex: '⚡',
|
||||
Simulation: '⚙', Strategy: '♟', Trivia: '?', Utility: '⚒', Word: 'Aa'
|
||||
};
|
||||
|
||||
const languageLabels = {
|
||||
|
|
@ -52,14 +45,13 @@ function categoryFromRoute() {
|
|||
}
|
||||
|
||||
function createCategoryPill(category) {
|
||||
const meta = categoryMeta[category] || { icon: '●', color: '#a5acb8' };
|
||||
const iconName = categoryMeta[category] || '●';
|
||||
const pill = document.createElement('a');
|
||||
pill.className = 'category-pill';
|
||||
pill.href = `/category/${slugify(category)}`;
|
||||
pill.style.setProperty('--category-color', meta.color);
|
||||
const icon = document.createElement('span');
|
||||
icon.setAttribute('aria-hidden', 'true');
|
||||
icon.textContent = meta.icon;
|
||||
icon.textContent = iconName;
|
||||
const label = document.createElement('span');
|
||||
label.textContent = category;
|
||||
pill.append(icon, label);
|
||||
|
|
@ -90,21 +82,11 @@ function searchText(program) {
|
|||
return `${program.title} ${program.description} ${program.category} ${languageLabels[program.language]} ${controls.join(' ')}`.toLowerCase();
|
||||
}
|
||||
|
||||
function renderPrograms() {
|
||||
const query = searchInput.value.trim().toLowerCase();
|
||||
const selectedCategory = categoryFilter.value;
|
||||
const selectedLanguage = languageFilter.value;
|
||||
const programs = allPrograms
|
||||
.filter(program => !selectedCategory || program.category === selectedCategory)
|
||||
.filter(program => !selectedLanguage || program.language === selectedLanguage)
|
||||
.filter(program => !query || searchText(program).includes(query))
|
||||
.sort((left, right) => {
|
||||
if (sortOrder.value === 'category') return left.category.localeCompare(right.category) || left.title.localeCompare(right.title);
|
||||
return left.title.localeCompare(right.title);
|
||||
});
|
||||
function formatReleaseDate(value) {
|
||||
return new Intl.DateTimeFormat('en-GB', { day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC' }).format(new Date(`${value}T00:00:00Z`));
|
||||
}
|
||||
|
||||
programsElement.replaceChildren();
|
||||
for (const program of programs) {
|
||||
function createProgramCard(program) {
|
||||
const card = document.createElement('article');
|
||||
card.className = 'program-card';
|
||||
const programUrl = `/program/${encodeURIComponent(program.slug)}`;
|
||||
|
|
@ -124,6 +106,10 @@ function renderPrograms() {
|
|||
description.className = 'program-description';
|
||||
description.textContent = program.description;
|
||||
|
||||
const releaseDate = document.createElement('p');
|
||||
releaseDate.className = 'release-date';
|
||||
releaseDate.textContent = `Released ${formatReleaseDate(program.releaseDate)}`;
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'program-actions';
|
||||
const run = document.createElement('a');
|
||||
|
|
@ -137,11 +123,39 @@ function renderPrograms() {
|
|||
source.disabled = !program.source;
|
||||
source.addEventListener('click', () => showSource(program));
|
||||
actions.append(run, source);
|
||||
card.append(badges, title, description, actions);
|
||||
programsElement.append(card);
|
||||
}
|
||||
card.append(badges, title, description, releaseDate, actions);
|
||||
return card;
|
||||
}
|
||||
|
||||
function appendProgramBatch() {
|
||||
const nextCount = Math.min(visibleProgramCount + PROGRAM_BATCH_SIZE, filteredPrograms.length);
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (const program of filteredPrograms.slice(visibleProgramCount, nextCount)) fragment.append(createProgramCard(program));
|
||||
programsElement.append(fragment);
|
||||
visibleProgramCount = nextCount;
|
||||
programSentinel.hidden = visibleProgramCount >= filteredPrograms.length;
|
||||
status.className = 'status';
|
||||
status.textContent = `${programs.length} of ${allPrograms.length} programs shown`;
|
||||
status.textContent = `${visibleProgramCount} of ${filteredPrograms.length} matching programs shown · ${allPrograms.length} total`;
|
||||
}
|
||||
|
||||
function renderPrograms() {
|
||||
const query = searchInput.value.trim().toLowerCase();
|
||||
const selectedCategory = categoryFilter.value;
|
||||
const selectedLanguage = languageFilter.value;
|
||||
filteredPrograms = allPrograms
|
||||
.filter(program => !selectedCategory || program.category === selectedCategory)
|
||||
.filter(program => !selectedLanguage || program.language === selectedLanguage)
|
||||
.filter(program => !query || searchText(program).includes(query))
|
||||
.sort((left, right) => {
|
||||
if (sortOrder.value === 'category') return left.category.localeCompare(right.category) || left.title.localeCompare(right.title);
|
||||
if (sortOrder.value === 'newest') return right.releaseDate.localeCompare(left.releaseDate) || left.title.localeCompare(right.title);
|
||||
if (sortOrder.value === 'oldest') return left.releaseDate.localeCompare(right.releaseDate) || left.title.localeCompare(right.title);
|
||||
return left.title.localeCompare(right.title);
|
||||
});
|
||||
|
||||
programsElement.replaceChildren();
|
||||
visibleProgramCount = 0;
|
||||
appendProgramBatch();
|
||||
}
|
||||
|
||||
async function showSource(program) {
|
||||
|
|
@ -197,6 +211,7 @@ async function loadPrograms() {
|
|||
function renderProgramDetails(program) {
|
||||
programBadges.replaceChildren(createCategoryPill(program.category), createLanguageBadge(program.language));
|
||||
programDescription.textContent = program.description;
|
||||
programReleaseDate.textContent = `Released ${formatReleaseDate(program.releaseDate)}`;
|
||||
programControls.replaceChildren();
|
||||
for (const control of program.controls) {
|
||||
const row = document.createElement('div');
|
||||
|
|
@ -280,6 +295,11 @@ window.addEventListener('popstate', () => {
|
|||
}
|
||||
});
|
||||
|
||||
const programObserver = new IntersectionObserver(entries => {
|
||||
if (entries.some(entry => entry.isIntersecting) && visibleProgramCount < filteredPrograms.length) appendProgramBatch();
|
||||
}, { rootMargin: '500px 0px' });
|
||||
programObserver.observe(programSentinel);
|
||||
|
||||
function setView(view) {
|
||||
const list = view === 'list';
|
||||
programsElement.classList.toggle('list-view', list);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Explore BASIC and machine-code programs for the Commodore 64 in a browser-based WASM VICE emulator.">
|
||||
<link rel="canonical" href="https://c64.p20.dev/">
|
||||
<title>C64 Program Collection</title>
|
||||
<link rel="stylesheet" href="/styles.css">
|
||||
</head>
|
||||
|
|
@ -39,6 +40,8 @@
|
|||
<select id="sort-order">
|
||||
<option value="title">Sort: Title</option>
|
||||
<option value="category">Sort: Category</option>
|
||||
<option value="newest">Sort: Newest first</option>
|
||||
<option value="oldest">Sort: Oldest first</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="view-switcher" role="group" aria-label="View style">
|
||||
|
|
@ -49,6 +52,7 @@
|
|||
</div>
|
||||
<p id="status" class="status">Loading programs…</p>
|
||||
<div id="programs" class="program-grid" aria-live="polite"></div>
|
||||
<div id="program-sentinel" class="program-sentinel" aria-hidden="true"></div>
|
||||
</section>
|
||||
<section id="player" class="player" hidden>
|
||||
<div class="player-heading">
|
||||
|
|
@ -60,6 +64,7 @@
|
|||
<a class="secondary button" href="/">Back to library</a>
|
||||
</div>
|
||||
<p id="program-description" class="program-page-description"></p>
|
||||
<p id="program-release-date" class="release-date"></p>
|
||||
<p class="hint">Click inside the emulator to enable keyboard input and sound.</p>
|
||||
<div id="game"></div>
|
||||
<p id="emulator-error" class="error" hidden></p>
|
||||
|
|
|
|||
|
|
@ -36,14 +36,16 @@ h1 { margin: 0; font-size: clamp(2rem, 7vw, 4rem); line-height: 1; }
|
|||
.program-card:has(.card-action:hover), .program-card:has(.category-pill:hover) { transform: none; background: #29216bd9; }
|
||||
.card-badges, .program-badges { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
|
||||
.category-pill, .language-badge { display: inline-flex; gap: .4rem; align-items: center; min-height: 28px; padding: .3rem .65rem; border-radius: 999px; font-size: .72rem; font-weight: 800; letter-spacing: .04em; line-height: 1; text-transform: uppercase; }
|
||||
.category-pill { border: 1px solid color-mix(in srgb, var(--category-color) 72%, white); background: color-mix(in srgb, var(--category-color) 24%, transparent); color: color-mix(in srgb, var(--category-color) 42%, white); text-decoration: none; transition: background .15s, color .15s, transform .15s; }
|
||||
.category-pill:hover { background: color-mix(in srgb, var(--category-color) 58%, #17113b); color: #fff; transform: translateY(-1px); }
|
||||
.category-pill:focus-visible { outline: 3px solid color-mix(in srgb, var(--category-color) 55%, white); outline-offset: 2px; }
|
||||
.category-pill { border: 1px solid #b8b1e755; background: #17113b99; color: #d8d3f4; text-decoration: none; transition: background .15s, color .15s, transform .15s; }
|
||||
.category-pill:hover { border-color: var(--c64-lightest-blue); background: var(--c64-light-blue); color: #fff; transform: translateY(-1px); }
|
||||
.category-pill:focus-visible { outline: 3px solid #b8b1e788; outline-offset: 2px; }
|
||||
.language-badge { border: 1px solid #b8b1e755; background: #17113b99; color: #d8d3f4; }
|
||||
.language-machine-code { border-color: #d98f4588; background: #d98f4526; color: #f4c997; }
|
||||
.language-basic { border-color: #75a9dc77; background: #75a9dc22; color: #c8e1f7; }
|
||||
.program-title { margin: 1rem 0 0; font-size: 1.2rem; }
|
||||
.program-description { margin: .55rem 0 0; color: #d2cef0; font-size: .92rem; line-height: 1.45; }
|
||||
.release-date { margin: .7rem 0 0; color: #aaa5ce; font-size: .78rem; }
|
||||
.program-sentinel { height: 1px; }
|
||||
kbd { min-width: 1.8rem; padding: .25rem .45rem; border: 1px solid var(--c64-light-blue); border-bottom-width: 3px; border-radius: 5px; background: #e9e7f7; color: #211a59; font: 700 .72rem ui-monospace, monospace; text-align: center; }
|
||||
.program-actions { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 1rem; margin-top: auto; padding-top: 1.25rem; }
|
||||
.card-action { display: inline-flex; align-items: center; justify-content: center; gap: .45rem; min-width: 94px; min-height: 38px; margin: 0; padding: .5rem .75rem; border: 1px solid #b8b1e766; border-radius: 8px; background: var(--c64-blue); color: #eeecfa; font: inherit; font-size: .84rem; font-weight: 700; line-height: 1; text-decoration: none; cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
|
||||
|
|
@ -53,8 +55,8 @@ kbd { min-width: 1.8rem; padding: .25rem .45rem; border: 1px solid var(--c64-lig
|
|||
.play-icon { font-size: .78rem; }
|
||||
.code-icon { font: 800 .8rem ui-monospace, monospace; }
|
||||
.program-grid.list-view { grid-template-columns: 1fr; }
|
||||
.program-grid.list-view .program-card { min-height: 0; display: grid; grid-template-columns: minmax(180px, auto) minmax(150px, .8fr) minmax(220px, 1.5fr) minmax(180px, 1fr); align-items: center; gap: 1rem; }
|
||||
.program-grid.list-view .program-title, .program-grid.list-view .program-description, .program-grid.list-view .program-actions { margin: 0; padding: 0; }
|
||||
.program-grid.list-view .program-card { min-height: 0; display: grid; grid-template-columns: minmax(180px, auto) minmax(150px, .8fr) minmax(220px, 1.5fr) minmax(110px, auto) minmax(180px, 1fr); align-items: center; gap: 1rem; }
|
||||
.program-grid.list-view .program-title, .program-grid.list-view .program-description, .program-grid.list-view .release-date, .program-grid.list-view .program-actions { margin: 0; padding: 0; }
|
||||
.source-modal { width: min(900px, calc(100% - 2rem)); height: min(80vh, 760px); padding: 0; border: 1px solid #b8b1e744; border-radius: 14px; background: var(--c64-deep-blue); color: #fff; box-shadow: 0 30px 80px #000a; }
|
||||
.source-modal::backdrop { background: #090621d9; backdrop-filter: blur(5px); }
|
||||
.modal-heading { position: sticky; top: 0; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.25rem; border-bottom: 1px solid #b8b1e733; background: #17113bf5; }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ set -eu
|
|||
for source in \
|
||||
sprite-playground/sprite-playground.asm \
|
||||
raster-bars/raster-bars.asm \
|
||||
sid-arpeggiator/sid-arpeggiator.asm
|
||||
sid-arpeggiator/sid-arpeggiator.asm \
|
||||
paddle-ball/paddle-ball.asm \
|
||||
smooth-scroller/smooth-scroller.asm \
|
||||
fireworks/fireworks.asm \
|
||||
jiffy-clock/jiffy-clock.asm \
|
||||
typing-sprint/typing-sprint.asm \
|
||||
petscii-aquarium/petscii-aquarium.asm \
|
||||
fifteen-puzzle/fifteen-puzzle.asm
|
||||
do
|
||||
output="${source%.asm}.prg"
|
||||
echo "Assembling $source -> $output"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ function fetchPrograms() {
|
|||
const slugs = new Set();
|
||||
return programs.map(program => {
|
||||
if (!program.title || !program.category || !program.description) throw new Error('Every program must include title, category, and description');
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(program.releaseDate) || Number.isNaN(Date.parse(`${program.releaseDate}T00:00:00Z`))) throw new Error(`Invalid release date for ${program.title}`);
|
||||
if (!['basic', 'machine-code'].includes(program.language)) throw new Error(`Invalid language for ${program.title}`);
|
||||
if (!Array.isArray(program.controls) || !program.controls.length || program.controls.some(control => !Array.isArray(control.keys) || !control.keys.length || !control.description)) {
|
||||
throw new Error(`Invalid controls for ${program.title}`);
|
||||
|
|
@ -126,6 +127,6 @@ createServer(async (request, response) => {
|
|||
else response.end();
|
||||
}
|
||||
}).listen(port, '0.0.0.0', () => {
|
||||
console.log(`C64 BASIC runner listening on port ${port}`);
|
||||
console.log(`C64 Program Collection listening on port ${port}`);
|
||||
});
|
||||
|
||||
|
|
|
|||
128
smooth-scroller/smooth-scroller.asm
Normal file
128
smooth-scroller/smooth-scroller.asm
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda $d016
|
||||
sta old_d016
|
||||
lda $d020
|
||||
sta old_border
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
header:
|
||||
lda title,x
|
||||
beq setup
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne header
|
||||
setup:
|
||||
lda #7
|
||||
sta fine
|
||||
lda #3
|
||||
sta speed
|
||||
lda #0
|
||||
sta tick
|
||||
sta message_pos
|
||||
lda #1
|
||||
sta text_color
|
||||
main:
|
||||
jsr wait_frame
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne not_quit
|
||||
jmp exit
|
||||
not_quit:
|
||||
cmp #43
|
||||
bne slower
|
||||
lda speed
|
||||
cmp #1
|
||||
beq slower
|
||||
dec speed
|
||||
slower:
|
||||
cmp #45
|
||||
bne color_key
|
||||
lda speed
|
||||
cmp #8
|
||||
beq color_key
|
||||
inc speed
|
||||
color_key:
|
||||
cmp #67
|
||||
bne animate
|
||||
inc text_color
|
||||
lda text_color
|
||||
and #15
|
||||
sta text_color
|
||||
animate:
|
||||
inc tick
|
||||
lda tick
|
||||
cmp speed
|
||||
bcc main
|
||||
lda #0
|
||||
sta tick
|
||||
dec fine
|
||||
lda fine
|
||||
and #7
|
||||
sta fine
|
||||
lda old_d016
|
||||
and #$f8
|
||||
ora fine
|
||||
sta $d016
|
||||
lda fine
|
||||
cmp #7
|
||||
bne main
|
||||
ldx #0
|
||||
shift:
|
||||
lda $0619,x
|
||||
sta $0618,x
|
||||
inx
|
||||
cpx #39
|
||||
bne shift
|
||||
ldy message_pos
|
||||
lda message,y
|
||||
bne put_char
|
||||
ldy #0
|
||||
sty message_pos
|
||||
lda message
|
||||
put_char:
|
||||
sta $063f
|
||||
lda text_color
|
||||
sta $da3f
|
||||
inc message_pos
|
||||
jmp main
|
||||
wait_frame:
|
||||
lda #250
|
||||
wait_raster:
|
||||
cmp $d012
|
||||
bne wait_raster
|
||||
wait_leave:
|
||||
cmp $d012
|
||||
beq wait_leave
|
||||
rts
|
||||
exit:
|
||||
lda old_d016
|
||||
sta $d016
|
||||
lda old_border
|
||||
sta $d020
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "SMOOTH SCROLLER +/- SPEED C COLOR Q QUIT"
|
||||
!byte 13,13,0
|
||||
message: !scr " WELCOME TO THE C64 PROGRAM COLLECTION BUILT WITH 6502 MACHINE CODE "
|
||||
!byte 0
|
||||
fine: !byte 0
|
||||
speed: !byte 0
|
||||
tick: !byte 0
|
||||
message_pos: !byte 0
|
||||
text_color: !byte 0
|
||||
old_d016: !byte 0
|
||||
old_border: !byte 0
|
||||
BIN
smooth-scroller/smooth-scroller.prg
Normal file
BIN
smooth-scroller/smooth-scroller.prg
Normal file
Binary file not shown.
159
typing-sprint/typing-sprint.asm
Normal file
159
typing-sprint/typing-sprint.asm
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
!cpu 6510
|
||||
* = $0801
|
||||
!word basic_end
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061"
|
||||
!byte 0
|
||||
basic_end: !word 0
|
||||
|
||||
* = $080d
|
||||
start:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
title_loop:
|
||||
lda title,x
|
||||
beq ready
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne title_loop
|
||||
ready:
|
||||
ldx #0
|
||||
ready_loop:
|
||||
lda ready_text,x
|
||||
beq wait_start
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne ready_loop
|
||||
wait_start:
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne start_key
|
||||
jmp exit
|
||||
start_key:
|
||||
cmp #32
|
||||
bne wait_start
|
||||
new_round:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
prompt_loop:
|
||||
lda prompt,x
|
||||
beq setup
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne prompt_loop
|
||||
setup:
|
||||
lda #0
|
||||
sta position
|
||||
sta mistakes
|
||||
sta frames
|
||||
sta seconds
|
||||
lda $a2
|
||||
sta last_jiffy
|
||||
main:
|
||||
lda $a2
|
||||
cmp last_jiffy
|
||||
beq read_key
|
||||
sta last_jiffy
|
||||
inc frames
|
||||
lda frames
|
||||
cmp #60
|
||||
bne read_key
|
||||
lda #0
|
||||
sta frames
|
||||
inc seconds
|
||||
read_key:
|
||||
jsr $ffe4
|
||||
beq main
|
||||
cmp #81
|
||||
bne type_key
|
||||
jmp exit
|
||||
type_key:
|
||||
ldx position
|
||||
cmp target,x
|
||||
beq correct
|
||||
inc mistakes
|
||||
lda #18
|
||||
jsr $ffd2
|
||||
lda #42
|
||||
jsr $ffd2
|
||||
lda #146
|
||||
jsr $ffd2
|
||||
jmp main
|
||||
correct:
|
||||
jsr $ffd2
|
||||
inc position
|
||||
ldx position
|
||||
lda target,x
|
||||
bne main
|
||||
lda #13
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
done_loop:
|
||||
lda done_text,x
|
||||
beq show_time
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne done_loop
|
||||
show_time:
|
||||
lda seconds
|
||||
ldx #0
|
||||
time_tens:
|
||||
cmp #10
|
||||
bcc print_time
|
||||
sec
|
||||
sbc #10
|
||||
inx
|
||||
bne time_tens
|
||||
print_time:
|
||||
pha
|
||||
txa
|
||||
beq print_ones
|
||||
ora #$30
|
||||
jsr $ffd2
|
||||
print_ones:
|
||||
pla
|
||||
ora #$30
|
||||
jsr $ffd2
|
||||
ldx #0
|
||||
seconds_loop:
|
||||
lda seconds_text,x
|
||||
beq wait_again
|
||||
jsr $ffd2
|
||||
inx
|
||||
bne seconds_loop
|
||||
wait_again:
|
||||
jsr $ffe4
|
||||
cmp #81
|
||||
bne again_key
|
||||
jmp exit
|
||||
again_key:
|
||||
cmp #32
|
||||
bne wait_again
|
||||
jmp new_round
|
||||
exit:
|
||||
lda #147
|
||||
jsr $ffd2
|
||||
rts
|
||||
|
||||
title: !byte 147
|
||||
!text "TYPING SPRINT COPY THE PHRASE WITHOUT ERRORS"
|
||||
!byte 13,13,0
|
||||
ready_text: !text "SPACE STARTS A SPRINT Q QUITS"
|
||||
!byte 0
|
||||
prompt: !byte 147
|
||||
!text "TYPE: COMMODORE SIXTY FOUR"
|
||||
!byte 13,13,0
|
||||
target: !text "COMMODORE SIXTY FOUR"
|
||||
!byte 0
|
||||
done_text: !text "FINISH IN "
|
||||
!byte 0
|
||||
seconds_text: !text " SECONDS SPACE RUNS AGAIN"
|
||||
!byte 0
|
||||
position: !byte 0
|
||||
mistakes: !byte 0
|
||||
frames: !byte 0
|
||||
seconds: !byte 0
|
||||
last_jiffy: !byte 0
|
||||
BIN
typing-sprint/typing-sprint.prg
Normal file
BIN
typing-sprint/typing-sprint.prg
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue