Teletype Games Plus/4 demo: a TED colour-wash title card in 6502 assembly, on the served plus4 pipeline
ci/woodpecker/manual/plus4 Pipeline was successful
ci/woodpecker/manual/plus4 Pipeline was successful
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
.claude
|
||||||
|
*.prg
|
||||||
|
.version
|
||||||
|
*-*.metadata.json
|
||||||
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,2 @@
|
|||||||
|
# The CI pipeline is served by the update server: GET /build/config?platform=plus4
|
||||||
|
platform: plus4
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# -----------------------------------------
|
||||||
|
# Makefile – Plus/4 project builder (ACME + VICE)
|
||||||
|
# -----------------------------------------
|
||||||
|
|
||||||
|
PROJECT = plus4-demo
|
||||||
|
|
||||||
|
ASM = acme
|
||||||
|
EMU = xplus4
|
||||||
|
SRC = main.asm
|
||||||
|
SRCS = $(wildcard *.asm)
|
||||||
|
TARGET ?= $(PROJECT).prg
|
||||||
|
|
||||||
|
METADATA = metadata.json
|
||||||
|
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
|
||||||
|
# CI/CD variables
|
||||||
|
VERSION_FILE = .version
|
||||||
|
|
||||||
|
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 (xplus4)."
|
||||||
|
@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
|
||||||
|
|
||||||
|
.PHONY: all deps build run clear rebuild rebuildrun
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Plus/4 Demo — Teletype Games title card
|
||||||
|
|
||||||
|
A Commodore Plus/4 program written in 6502 assembly (built with the ACME assembler): the smallest complete example of a Plus/4 title that goes through the Teletype Games build pipeline. It clears the screen, paints the Teletype Games title card, runs a gold colour wash across the title in sync with the raster beam, blinks a prompt, and hands control back to BASIC on any key.
|
||||||
|
|
||||||
|
The Plus/4 sibling of the [C64 demo](https://git.teletypegames.org/games/c64-demo): same assembler, same Makefile, same pipeline shape — only the machine differs.
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| any key | return to BASIC (colours restored, screen cleared) |
|
||||||
|
|
||||||
|
Keys are read straight from the keyboard latch (`$FD30` / `$FF08`), the KERNAL IRQ is switched off while the demo runs.
|
||||||
|
|
||||||
|
## Build and run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make build # produce plus4-demo.prg
|
||||||
|
make run # build + launch in VICE (xplus4)
|
||||||
|
make rebuildrun # clean rebuild + run
|
||||||
|
make deps # on macOS: install acme + vice via Homebrew
|
||||||
|
```
|
||||||
|
|
||||||
|
Loading the `.prg` on a real or emulated Plus/4:
|
||||||
|
|
||||||
|
```
|
||||||
|
LOAD"*",8,1
|
||||||
|
RUN
|
||||||
|
```
|
||||||
|
|
||||||
|
Headless smoke test with VICE (runs for a few emulated seconds, then writes a screenshot):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
xplus4 +sound -warp -limitcycles 30000000 -exitscreenshot shot.png -autostartprgmode 1 plus4-demo.prg
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
- **BASIC stub** at `$1001`: `10 SYS 4109`, so `RUN` starts the code at `$100D`.
|
||||||
|
- **Screen** is written directly: screen RAM at `$0C00`, colour RAM at `$0800`. A Plus/4 colour byte is `luminance << 4 | hue`, which is what the wash exploits — the same gold hue at eight luminances, peaking in white.
|
||||||
|
- **Timing**: no interrupts; the main loop polls the TED raster register (`$FF1D`) and does one pass per frame.
|
||||||
|
- **Text**: the `+prtext` macro copies a fixed screen-code string to a row/column with one colour. Strings are written in lowercase ASCII, which ACME's `!scr` maps to the uppercase screen codes of the default character set.
|
||||||
|
- **Exit**: the saved border and background colours are restored, interrupts re-enabled, and the screen cleared through the KERNAL (`$FFD2`) before `RTS` returns to BASIC.
|
||||||
|
|
||||||
|
## Memory map
|
||||||
|
|
||||||
|
```
|
||||||
|
$1001 .. $100C BASIC stub (10 SYS 4109)
|
||||||
|
$100D .. ... machine code + texts + variables
|
||||||
|
$0C00 .. $0FE7 screen RAM (written directly)
|
||||||
|
$0800 .. $0BE7 colour RAM
|
||||||
|
$FF15 / $FF19 background / border colour (TED)
|
||||||
|
$FF1D raster line (frame sync)
|
||||||
|
$FD30 / $FF08 keyboard column select / row latch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pipeline
|
||||||
|
|
||||||
|
`.woodpecker.yaml` is a one-line marker (`platform: plus4`); the pipeline itself is served by the update server. See [plus4-build](https://git.teletypegames.org/build/plus4-build) for the toolchain and the platform page on the wiki: https://wiki.teletypegames.org/development/plus4
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
; -----------------------------------------------------------
|
||||||
|
; main.asm - Plus/4 demo: the Teletype Games title card
|
||||||
|
; a colour wash runs across the title, the prompt blinks,
|
||||||
|
; any key returns to BASIC
|
||||||
|
; all timed by polling the TED raster beam
|
||||||
|
;
|
||||||
|
; Build: acme -f cbm -o plus4-demo.prg main.asm
|
||||||
|
; Run: xplus4 plus4-demo.prg
|
||||||
|
; -----------------------------------------------------------
|
||||||
|
|
||||||
|
!cpu 6502
|
||||||
|
!to "plus4-demo.prg", cbm
|
||||||
|
|
||||||
|
; ---- TED registers ----
|
||||||
|
TED_BG = $ff15 ; background colour
|
||||||
|
TED_BORD = $ff19 ; border colour
|
||||||
|
TED_RASTL = $ff1d ; raster line, low 8 bits
|
||||||
|
TED_KBD = $ff08 ; keyboard row latch (write: latch, read: rows)
|
||||||
|
KBD_COLS = $fd30 ; keyboard column select (0 = selected)
|
||||||
|
|
||||||
|
SCREEN = $0c00 ; screen RAM (40x25 chars)
|
||||||
|
COLRAM = $0800 ; colour RAM: luminance << 4 | hue
|
||||||
|
|
||||||
|
CHROUT = $ffd2 ; KERNAL: print a character
|
||||||
|
|
||||||
|
; ---- colours (luminance in the high nibble) ----
|
||||||
|
BLACK = $00
|
||||||
|
DARK_BLUE = $16
|
||||||
|
CYAN = $53
|
||||||
|
WHITE = $71
|
||||||
|
GOLD = $47
|
||||||
|
SYNC_LINE = $cc ; raster line the frame loop waits for
|
||||||
|
|
||||||
|
; ---- layout ----
|
||||||
|
TITLE_ROW = 8
|
||||||
|
TITLE_COL = 13
|
||||||
|
PROMPT_ROW = 20
|
||||||
|
PROMPT_COL = 13
|
||||||
|
|
||||||
|
; print a fixed text with one colour
|
||||||
|
!macro prtext .row, .col, .color, .src, .len {
|
||||||
|
ldx #0
|
||||||
|
- lda .src, x
|
||||||
|
sta SCREEN + .row * 40 + .col, x
|
||||||
|
lda #.color
|
||||||
|
sta COLRAM + .row * 40 + .col, x
|
||||||
|
inx
|
||||||
|
cpx #.len
|
||||||
|
bne -
|
||||||
|
}
|
||||||
|
|
||||||
|
* = $1001
|
||||||
|
|
||||||
|
; ---- BASIC stub: 10 SYS 4109 ----
|
||||||
|
; so after LOAD"*",8,1 it's enough to type RUN
|
||||||
|
!byte $0b, $10 ; address of next line
|
||||||
|
!byte $0a, $00 ; line number: 10
|
||||||
|
!byte $9e ; SYS token
|
||||||
|
!text "4109" ; = $100d
|
||||||
|
!byte $00 ; end of line
|
||||||
|
!byte $00, $00 ; end of program
|
||||||
|
|
||||||
|
start: ; = $100d
|
||||||
|
lda #$93 ; clear the screen through the KERNAL: this also
|
||||||
|
jsr CHROUT ; parks the cursor before the IRQ is stopped
|
||||||
|
sei ; no KERNAL IRQ - raster polling must not jitter
|
||||||
|
|
||||||
|
lda TED_BG
|
||||||
|
sta saved_bg
|
||||||
|
lda TED_BORD
|
||||||
|
sta saved_bord
|
||||||
|
lda #DARK_BLUE
|
||||||
|
sta TED_BG
|
||||||
|
lda #BLACK
|
||||||
|
sta TED_BORD
|
||||||
|
|
||||||
|
+prtext TITLE_ROW, TITLE_COL, WHITE, txt_title, TITLE_LEN
|
||||||
|
+prtext 10, 14, CYAN, txt_sub, SUB_LEN
|
||||||
|
+prtext 14, 8, GOLD, txt_line1, LINE1_LEN
|
||||||
|
+prtext 15, 6, GOLD, txt_line2, LINE2_LEN
|
||||||
|
+prtext PROMPT_ROW, PROMPT_COL, WHITE, txt_prompt, PROMPT_LEN
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
sta framectr
|
||||||
|
|
||||||
|
- jsr check_key ; the RETURN that started us may still be down
|
||||||
|
bne -
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; main loop - one pass per frame
|
||||||
|
; ============================================================
|
||||||
|
main:
|
||||||
|
jsr wait_frame
|
||||||
|
inc framectr
|
||||||
|
|
||||||
|
; colour wash: the ramp slides one cell every second frame
|
||||||
|
lda framectr
|
||||||
|
lsr
|
||||||
|
sta wash_base
|
||||||
|
ldx #0
|
||||||
|
- txa
|
||||||
|
clc
|
||||||
|
adc wash_base
|
||||||
|
and #$0f
|
||||||
|
tay
|
||||||
|
lda ramp, y
|
||||||
|
sta COLRAM + TITLE_ROW * 40 + TITLE_COL, x
|
||||||
|
inx
|
||||||
|
cpx #TITLE_LEN
|
||||||
|
bne -
|
||||||
|
|
||||||
|
; blinking prompt: hidden while bit 5 of the frame counter is set
|
||||||
|
lda framectr
|
||||||
|
and #$20
|
||||||
|
beq +
|
||||||
|
lda #DARK_BLUE
|
||||||
|
bne ++
|
||||||
|
+ lda #WHITE
|
||||||
|
++ ldx #0
|
||||||
|
- sta COLRAM + PROMPT_ROW * 40 + PROMPT_COL, x
|
||||||
|
inx
|
||||||
|
cpx #PROMPT_LEN
|
||||||
|
bne -
|
||||||
|
|
||||||
|
jsr check_key
|
||||||
|
beq main
|
||||||
|
|
||||||
|
; ---- back to BASIC ----
|
||||||
|
lda saved_bg
|
||||||
|
sta TED_BG
|
||||||
|
lda saved_bord
|
||||||
|
sta TED_BORD
|
||||||
|
cli
|
||||||
|
lda #$93
|
||||||
|
jsr CHROUT
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; wait_frame - wait for the raster beam to pass SYNC_LINE once
|
||||||
|
; ============================================================
|
||||||
|
wait_frame:
|
||||||
|
- lda TED_RASTL
|
||||||
|
cmp #SYNC_LINE
|
||||||
|
bne -
|
||||||
|
- lda TED_RASTL
|
||||||
|
cmp #SYNC_LINE
|
||||||
|
beq -
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; check_key - Z set when no key is pressed
|
||||||
|
; all columns selected, joysticks deselected, rows latched
|
||||||
|
; ============================================================
|
||||||
|
check_key:
|
||||||
|
lda #$00
|
||||||
|
sta KBD_COLS
|
||||||
|
lda #$ff
|
||||||
|
sta TED_KBD
|
||||||
|
lda TED_KBD
|
||||||
|
cmp #$ff
|
||||||
|
rts
|
||||||
|
|
||||||
|
; ============================================================
|
||||||
|
; data
|
||||||
|
; ============================================================
|
||||||
|
ramp: ; gold rising to white and back, 16 steps
|
||||||
|
!byte $17, $27, $37, $47, $57, $67, $77, $71
|
||||||
|
!byte $71, $77, $67, $57, $47, $37, $27, $17
|
||||||
|
|
||||||
|
txt_title: !scr "teletype games"
|
||||||
|
TITLE_LEN = * - txt_title
|
||||||
|
txt_sub: !scr "plus/4 demo"
|
||||||
|
SUB_LEN = * - txt_sub
|
||||||
|
txt_line1: !scr "6502 assembly with acme"
|
||||||
|
LINE1_LEN = * - txt_line1
|
||||||
|
txt_line2: !scr "released through warpengine"
|
||||||
|
LINE2_LEN = * - txt_line2
|
||||||
|
txt_prompt: !scr "press any key"
|
||||||
|
PROMPT_LEN = * - txt_prompt
|
||||||
|
|
||||||
|
framectr: !byte 0
|
||||||
|
wash_base: !byte 0
|
||||||
|
saved_bg: !byte 0
|
||||||
|
saved_bord: !byte 0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "plus4-demo",
|
||||||
|
"title": "Teletype Games Plus/4 Demo",
|
||||||
|
"author": "Teletype Games",
|
||||||
|
"desc": "A Commodore Plus/4 title card in 6502 assembly: the Teletype Games title with a gold colour wash driven by the TED raster beam, a blinking prompt, and a clean return to BASIC on any key.",
|
||||||
|
"site": "https://git.teletypegames.org/demos/plus4-demo",
|
||||||
|
"repo": "https://git.teletypegames.org/demos/plus4-demo",
|
||||||
|
"license": "MIT",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user