First public commit.
This commit is contained in:
parent
2a48f52979
commit
4bac9d83ed
288 changed files with 18417 additions and 1076 deletions
1
lenser/c128/viewer/__init__.py
Normal file
1
lenser/c128/viewer/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""C128 8502/VDC viewer (assembled by xa)."""
|
||||
122
lenser/c128/viewer/assemble.py
Normal file
122
lenser/c128/viewer/assemble.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
"""Assemble the C128 VDC viewer with `xa` and build the loadable PRG.
|
||||
|
||||
The PRG loads at the C128 BASIC start ($1C01): a tiny BASIC stub (`10 SYS7200`)
|
||||
followed by the 8502 viewer (at $1C20) and, from $2000, the 640x200 bitmap.
|
||||
Running it (RUN"PIC") executes the stub, which SYSes the viewer.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
VIEWER_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
BASIC_START = 0x1C01
|
||||
ML_ORG = 0x1C20
|
||||
DATA_ORG = 0x2000 # bitmap goes here (viewer copies it to the VDC)
|
||||
|
||||
# BASIC: 10 SYS7200 ($1C20 = 7200) -- bytes as they sit from $1C01
|
||||
_STUB = bytes([0x0B, 0x1C, 0x0A, 0x00, 0x9E,
|
||||
0x37, 0x32, 0x30, 0x30, 0x00, 0x00, 0x00])
|
||||
|
||||
|
||||
class AssemblerError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def have_xa() -> bool:
|
||||
return shutil.which("xa") is not None
|
||||
|
||||
|
||||
def _xa(wrapper: str) -> bytes:
|
||||
"""Assemble a generated wrapper (xa runs in VIEWER_DIR so #includes resolve)."""
|
||||
if not have_xa():
|
||||
raise AssemblerError("The 'xa' (xa65) assembler was not found on PATH.\n"
|
||||
"Install it with: sudo apt install xa65")
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
out = os.path.join(td, "v.bin")
|
||||
fd, wrap = tempfile.mkstemp(suffix=".s", prefix="_wrap_", dir=VIEWER_DIR)
|
||||
try:
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(wrapper)
|
||||
proc = subprocess.run(["xa", "-o", out, os.path.basename(wrap)],
|
||||
capture_output=True, text=True, cwd=VIEWER_DIR)
|
||||
if proc.returncode != 0:
|
||||
raise AssemblerError(f"xa failed:\n{proc.stdout}{proc.stderr}")
|
||||
with open(out, "rb") as f:
|
||||
return f.read()
|
||||
finally:
|
||||
os.unlink(wrap)
|
||||
|
||||
|
||||
def _assemble(fgbg: int, source: str) -> bytes:
|
||||
return _xa(f"#define SRC ${DATA_ORG:04X}\n"
|
||||
f"#define FGBG ${fgbg & 0xFF:02X}\n"
|
||||
f'#include "{source}"\n')
|
||||
|
||||
|
||||
def _wrap_prg(code: bytes, data: bytes) -> bytes:
|
||||
"""Lay out load-address prefix + BASIC stub + viewer code + data @ $2000."""
|
||||
mem = bytearray()
|
||||
mem += _STUB # $1C01..
|
||||
mem += b"\x00" * (ML_ORG - BASIC_START - len(_STUB))
|
||||
mem += code # $1C20..
|
||||
if len(mem) > DATA_ORG - BASIC_START:
|
||||
raise AssemblerError("viewer code overruns the $2000 data area")
|
||||
mem += b"\x00" * (DATA_ORG - BASIC_START - len(mem))
|
||||
mem += data # $2000..
|
||||
return bytes([BASIC_START & 0xFF, BASIC_START >> 8]) + bytes(mem)
|
||||
|
||||
|
||||
def build_prg_color(attributes: bytes, fgbg: int = 0x0F) -> bytes:
|
||||
"""Return the loadable PRG for the 80x100 chunky-colour viewer.
|
||||
|
||||
`attributes` is the 8000-byte per-cell colour map (colour in the high
|
||||
nibble); the viewer fills the character matrix with a solid glyph itself.
|
||||
"""
|
||||
return _wrap_prg(_assemble(fgbg, "color.s"), attributes)
|
||||
|
||||
|
||||
def build_prg_hicolor(vdc_image: bytes, fgbg: int = 0x00) -> bytes:
|
||||
"""Return the loadable PRG for the 640x200 custom-charset viewer (font mode).
|
||||
|
||||
Used by both the `hicolor` and `mono` modes. `vdc_image` is the full VDC RAM
|
||||
image (character codes, attributes and the custom character set already laid
|
||||
out); `fgbg` carries the global background in its low nibble. The viewer
|
||||
copies the image verbatim into VDC RAM.
|
||||
"""
|
||||
return _wrap_prg(_assemble(fgbg, "hicolor.s"), vdc_image)
|
||||
|
||||
|
||||
_SS_WAITMODE = {"key": 1, "seconds": 2, "both": 3}
|
||||
|
||||
|
||||
def build_slideshow_prg(fgbg_list, advance: str = "both", seconds: int = 10,
|
||||
loop: bool = True, video: str = "pal") -> bytes:
|
||||
"""Return the bootable C128 slideshow viewer PRG (RUN\"PIC\").
|
||||
|
||||
``fgbg_list`` is one VDC R26 background byte per image (conv.meta["fgbg"]);
|
||||
the per-image pictures are separate "00".."NN" files the viewer loads. The
|
||||
viewer is code-only (no appended image) -- just the BASIC stub + the 8502
|
||||
loop + the ss_fgbg table.
|
||||
"""
|
||||
if not fgbg_list:
|
||||
raise AssemblerError("a slideshow needs at least one image")
|
||||
jiffyps = 60 if video == "ntsc" else 50
|
||||
table = ",".join(str(int(b) & 0xFF) for b in fgbg_list)
|
||||
code = _xa(
|
||||
f"#define WAITMODE {_SS_WAITMODE[advance]}\n"
|
||||
f"#define WAITSECS {max(0, int(seconds))}\n"
|
||||
f"#define JIFFYPS {jiffyps}\n"
|
||||
f"#define NIMAGES {len(fgbg_list)}\n"
|
||||
f"#define LOOPFLAG {1 if loop else 0}\n"
|
||||
'#include "slideshow.s"\n'
|
||||
"ss_fgbg:\n"
|
||||
f" .byte {table}\n")
|
||||
mem = bytearray()
|
||||
mem += _STUB # $1C01 BASIC stub (10 SYS7200)
|
||||
mem += b"\x00" * (ML_ORG - BASIC_START - len(_STUB))
|
||||
mem += code # $1C20 viewer + ss_fgbg table
|
||||
return bytes([BASIC_START & 0xFF, BASIC_START >> 8]) + bytes(mem)
|
||||
173
lenser/c128/viewer/color.s
Normal file
173
lenser/c128/viewer/color.s
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
; Commodore 128 VDC (8563) 80x100 chunky-colour image viewer.
|
||||
;
|
||||
; MAME's 8563 renders the bitmap through the character/font path- each display
|
||||
; byte is a CHARACTER CODE whose glyph is drawn with fg = attribute high nibble,
|
||||
; bg = attribute low nibble. So a per-cell SOLID colour image is produced by
|
||||
; filling the whole character matrix with $FF (a solid glyph) and giving every
|
||||
; cell its colour in the high nibble of its attribute byte.
|
||||
;
|
||||
; R9=1 (2 scan lines per char row) makes 80x100 cells- 8000 char bytes + 8000
|
||||
; attribute bytes = 16000, which fits the stock 16K VDC RAM.
|
||||
;
|
||||
; #defines from viewer/assemble.py --
|
||||
; SRC main-RAM address of the 8000 attribute bytes ($2000)
|
||||
; FGBG VDC register 26 default value (unused cells / background)
|
||||
|
||||
* = $1C20
|
||||
|
||||
src = $fb
|
||||
adlo = $fd
|
||||
adhi = $fe
|
||||
cntl = $02
|
||||
cnth = $03
|
||||
fill = $04
|
||||
|
||||
start:
|
||||
sei
|
||||
lda #$0e
|
||||
sta $ff00 ; bank in RAM $4000-$BFFF, keep I/O for the VDC
|
||||
lda #<nmi
|
||||
sta $0318
|
||||
lda #>nmi
|
||||
sta $0319 ; neutralise NMI (not masked by SEI)
|
||||
|
||||
jsr setregs
|
||||
|
||||
; fill the character matrix VDC $0000 with $FF (solid glyph), 8000 bytes
|
||||
lda #$00
|
||||
sta adlo
|
||||
sta adhi
|
||||
lda #$ff
|
||||
sta fill
|
||||
lda #<8000
|
||||
sta cntl
|
||||
lda #>8000
|
||||
sta cnth
|
||||
jsr fillblk
|
||||
|
||||
; copy 8000 attribute bytes main SRC -> VDC $2000
|
||||
lda #<SRC
|
||||
sta src
|
||||
lda #>SRC
|
||||
sta src+1
|
||||
lda #$00
|
||||
sta adlo
|
||||
lda #$20
|
||||
sta adhi
|
||||
lda #<8000
|
||||
sta cntl
|
||||
lda #>8000
|
||||
sta cnth
|
||||
jsr copyblk
|
||||
|
||||
; enable bitmap + attributes (display the picture)
|
||||
ldx #25
|
||||
lda #$c0
|
||||
jsr vdcw
|
||||
|
||||
loop:
|
||||
jmp loop
|
||||
|
||||
nmi:
|
||||
rti
|
||||
|
||||
; program the VDC display geometry + attribute base (everything except R25)
|
||||
setregs:
|
||||
ldx #9
|
||||
lda #$01
|
||||
jsr vdcw ; R9 = 1 -> 2 scan lines per char row (80x100)
|
||||
ldx #4
|
||||
lda #131
|
||||
jsr vdcw ; R4 vertical total -> (131+1)*2 = 264 lines
|
||||
ldx #5
|
||||
lda #$00
|
||||
jsr vdcw ; R5 vertical total adjust
|
||||
ldx #6
|
||||
lda #100
|
||||
jsr vdcw ; R6 vertical displayed = 100 rows (*2 = 200)
|
||||
ldx #7
|
||||
lda #116
|
||||
jsr vdcw ; R7 vsync position
|
||||
ldx #20
|
||||
lda #$20
|
||||
jsr vdcw ; R20 attribute base high = $2000
|
||||
ldx #21
|
||||
lda #$00
|
||||
jsr vdcw ; R21 attribute base low
|
||||
ldx #26
|
||||
lda #FGBG
|
||||
jsr vdcw ; R26 default fg/bg
|
||||
ldx #10
|
||||
lda #$20
|
||||
jsr vdcw ; R10 cursor off (bit5) - hide the blinking cursor
|
||||
ldx #12
|
||||
lda #$00
|
||||
jsr vdcw ; R12 display start high
|
||||
ldx #13
|
||||
lda #$00
|
||||
jsr vdcw ; R13 display start low
|
||||
rts
|
||||
|
||||
; fill cnt bytes = (fill) into VDC RAM from adhi/adlo (explicit address per byte)
|
||||
fillblk:
|
||||
ldx #18
|
||||
lda adhi
|
||||
jsr vdcw
|
||||
ldx #19
|
||||
lda adlo
|
||||
jsr vdcw
|
||||
ldx #31
|
||||
lda fill
|
||||
jsr vdcw
|
||||
inc adlo
|
||||
bne fb1
|
||||
inc adhi
|
||||
fb1:
|
||||
lda cntl
|
||||
bne fb2
|
||||
dec cnth
|
||||
fb2:
|
||||
dec cntl
|
||||
lda cntl
|
||||
ora cnth
|
||||
bne fillblk
|
||||
rts
|
||||
|
||||
; copy cnt bytes from (src) into VDC RAM from adhi/adlo (explicit address per byte)
|
||||
copyblk:
|
||||
ldx #18
|
||||
lda adhi
|
||||
jsr vdcw
|
||||
ldx #19
|
||||
lda adlo
|
||||
jsr vdcw
|
||||
ldx #31
|
||||
ldy #$00
|
||||
lda (src),y
|
||||
jsr vdcw
|
||||
inc src
|
||||
bne cb1
|
||||
inc src+1
|
||||
cb1:
|
||||
inc adlo
|
||||
bne cb2
|
||||
inc adhi
|
||||
cb2:
|
||||
lda cntl
|
||||
bne cb3
|
||||
dec cnth
|
||||
cb3:
|
||||
dec cntl
|
||||
lda cntl
|
||||
ora cnth
|
||||
bne copyblk
|
||||
rts
|
||||
|
||||
; write A to VDC register X
|
||||
vdcw:
|
||||
stx $d600
|
||||
vw:
|
||||
bit $d600
|
||||
bpl vw
|
||||
sta $d601
|
||||
rts
|
||||
92
lenser/c128/viewer/hicolor.s
Normal file
92
lenser/c128/viewer/hicolor.s
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
; Commodore 128 VDC (8563) 640x200 high-colour viewer (custom character set).
|
||||
;
|
||||
; Uses the VDC's normal 80-column CHARACTER mode (R25 left at the C128 default:
|
||||
; font + attribute, bit7=0) with a per-image custom font. Each 8x8 cell draws
|
||||
; its glyph in a per-cell INK colour (attribute low nibble) over a single GLOBAL
|
||||
; background (VDC register 26 low nibble) -> 640x200 detail with colour.
|
||||
;
|
||||
; Python lays out the whole VDC RAM image in main RAM from $2000 (codes @ $0000,
|
||||
; attributes @ $0800, character set bank 0 @ $2000, bank 1 @ $3000); this copies
|
||||
; the full 16K verbatim into the VDC's own RAM with an explicit address per byte.
|
||||
;
|
||||
; #defines from viewer/assemble.py --
|
||||
; SRC main-RAM address of the VDC image ($2000)
|
||||
; FGBG VDC register 26 value (global background in the low nibble)
|
||||
|
||||
* = $1C20
|
||||
src = $fb
|
||||
adlo = $fd
|
||||
adhi = $fe
|
||||
cntl = $02
|
||||
cnth = $03
|
||||
|
||||
start:
|
||||
sei
|
||||
lda #$0e
|
||||
sta $ff00 ; bank in RAM $4000-$BFFF, keep I/O for the VDC
|
||||
lda #<nmi
|
||||
sta $0318
|
||||
lda #>nmi
|
||||
sta $0319 ; neutralise NMI (not masked by SEI)
|
||||
|
||||
lda #<SRC ; copy $4000 (16384) bytes main SRC -> VDC $0000
|
||||
sta src
|
||||
lda #>SRC
|
||||
sta src+1
|
||||
lda #$00
|
||||
sta adlo
|
||||
sta adhi
|
||||
lda #$00
|
||||
sta cntl
|
||||
lda #$40
|
||||
sta cnth
|
||||
jsr copyblk
|
||||
|
||||
ldx #26
|
||||
lda #FGBG
|
||||
jsr vdcw ; R26 global background (low nibble)
|
||||
ldx #10
|
||||
lda #$20
|
||||
jsr vdcw ; cursor off
|
||||
; R25 deliberately untouched (C128 default = font + attribute mode)
|
||||
loop:
|
||||
jmp loop
|
||||
nmi:
|
||||
rti
|
||||
|
||||
copyblk:
|
||||
ldx #18
|
||||
lda adhi
|
||||
jsr vdcw
|
||||
ldx #19
|
||||
lda adlo
|
||||
jsr vdcw
|
||||
ldx #31
|
||||
ldy #$00
|
||||
lda (src),y
|
||||
jsr vdcw
|
||||
inc src
|
||||
bne cb1
|
||||
inc src+1
|
||||
cb1:
|
||||
inc adlo
|
||||
bne cb2
|
||||
inc adhi
|
||||
cb2:
|
||||
lda cntl
|
||||
bne cb3
|
||||
dec cnth
|
||||
cb3:
|
||||
dec cntl
|
||||
lda cntl
|
||||
ora cnth
|
||||
bne copyblk
|
||||
rts
|
||||
|
||||
vdcw:
|
||||
stx $d600
|
||||
vw:
|
||||
bit $d600
|
||||
bpl vw
|
||||
sta $d601
|
||||
rts
|
||||
211
lenser/c128/viewer/slideshow.s
Normal file
211
lenser/c128/viewer/slideshow.s
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
; Commodore 128 VDC (8563) slideshow viewer -- 640x200 high-colour mode.
|
||||
;
|
||||
; Steps through NIMAGES VDC images named "00".."NN" on the disk, each a 16K VDC
|
||||
; RAM image (codes/attributes/font, the same body the single hicolor/mono viewer
|
||||
; copies). For each slide it KERNAL-loads the file into RAM bank 0 at $4000,
|
||||
; copies the 16K verbatim into the VDC's own RAM, sets the global background
|
||||
; (R26) from the per-image ss_fgbg table, then waits (key / seconds / both)
|
||||
; before advancing. Boots via RUN"PIC" -> the BASIC stub SYSes here.
|
||||
;
|
||||
; #defines from the build wrapper --
|
||||
; WAITMODE 1 key / 2 seconds / 3 both WAITSECS timeout seconds
|
||||
; JIFFYPS 50 PAL / 60 NTSC NIMAGES image count
|
||||
; LOOPFLAG 1 wrap at end, 0 stop
|
||||
; ss_fgbg (one byte per image, VDC R26 background) is appended by the wrapper.
|
||||
|
||||
* = $1C20
|
||||
|
||||
src = $fb
|
||||
adlo = $fd
|
||||
adhi = $fe
|
||||
cntl = $02
|
||||
cnth = $03
|
||||
|
||||
start:
|
||||
lda #$0e
|
||||
sta $ff00 ; KERNAL + I/O + RAM $4000-$BFFF all visible
|
||||
lda #$00
|
||||
sta $9d ; suppress KERNAL LOAD messages
|
||||
sta ssidx
|
||||
|
||||
mainloop:
|
||||
jsr namebuild
|
||||
|
||||
; ---- C128 LOAD "NN",8,1 into RAM bank 0 at $4000 ----
|
||||
lda #$00
|
||||
ldx #$00
|
||||
jsr $ff68 ; SETBNK (load bank 0, name bank 0)
|
||||
lda #2
|
||||
ldx #<ssname
|
||||
ldy #>ssname
|
||||
jsr $ffbd ; SETNAM
|
||||
lda #1
|
||||
ldx #8
|
||||
ldy #1
|
||||
jsr $ffba ; SETLFS (secondary 1 = file's own address)
|
||||
lda #0
|
||||
jsr $ffd5 ; LOAD
|
||||
|
||||
; ---- copy 16384 bytes from $4000 -> VDC $0000 ----
|
||||
lda #$00
|
||||
sta src
|
||||
lda #$40
|
||||
sta src+1
|
||||
lda #$00
|
||||
sta adlo
|
||||
sta adhi
|
||||
sta cntl
|
||||
lda #$40
|
||||
sta cnth
|
||||
jsr copyblk
|
||||
|
||||
; ---- per-image global background (R26) + cursor off (R10) ----
|
||||
ldx ssidx
|
||||
lda ss_fgbg,x
|
||||
ldx #26
|
||||
jsr vdcw
|
||||
ldx #10
|
||||
lda #$20
|
||||
jsr vdcw
|
||||
|
||||
; ---- wait for the slide's dwell ----
|
||||
jsr ss_wait
|
||||
|
||||
; ---- advance ----
|
||||
inc ssidx
|
||||
lda ssidx
|
||||
cmp #NIMAGES
|
||||
bcc mainloop
|
||||
#if LOOPFLAG == 1
|
||||
lda #$00
|
||||
sta ssidx
|
||||
jmp mainloop
|
||||
#else
|
||||
rts
|
||||
#endif
|
||||
|
||||
; --------------------------------------------------------------------------- ;
|
||||
; wait -- key / seconds / both, KERNAL GETIN + jiffy clock ($a0-$a2, $a2 = LSB)
|
||||
; (reuses $fb-$fe as 16-bit scratch now the copy is done)
|
||||
cv_t0 = $fb
|
||||
cv_el = $fd
|
||||
|
||||
ss_wait:
|
||||
#if WAITMODE == 1
|
||||
ss_drain:
|
||||
jsr $ffe4
|
||||
bne ss_drain ; flush leftover keys
|
||||
ss_k:
|
||||
jsr $ffe4
|
||||
beq ss_k
|
||||
rts
|
||||
#endif
|
||||
#if WAITMODE == 2
|
||||
lda $a2
|
||||
sta cv_t0
|
||||
lda $a1
|
||||
sta cv_t0+1
|
||||
ss_s:
|
||||
sec
|
||||
lda $a2
|
||||
sbc cv_t0
|
||||
sta cv_el
|
||||
lda $a1
|
||||
sbc cv_t0+1
|
||||
sta cv_el+1
|
||||
lda cv_el+1
|
||||
cmp #>(WAITSECS*JIFFYPS)
|
||||
bcc ss_s
|
||||
bne ss_sd
|
||||
lda cv_el
|
||||
cmp #<(WAITSECS*JIFFYPS)
|
||||
bcc ss_s
|
||||
ss_sd:
|
||||
rts
|
||||
#endif
|
||||
#if WAITMODE == 3
|
||||
ss_drain:
|
||||
jsr $ffe4
|
||||
bne ss_drain
|
||||
lda $a2
|
||||
sta cv_t0
|
||||
lda $a1
|
||||
sta cv_t0+1
|
||||
ss_b:
|
||||
jsr $ffe4
|
||||
bne ss_bd ; any key ends the slide
|
||||
sec
|
||||
lda $a2
|
||||
sbc cv_t0
|
||||
sta cv_el
|
||||
lda $a1
|
||||
sbc cv_t0+1
|
||||
sta cv_el+1
|
||||
lda cv_el+1
|
||||
cmp #>(WAITSECS*JIFFYPS)
|
||||
bcc ss_b
|
||||
bne ss_bd
|
||||
lda cv_el
|
||||
cmp #<(WAITSECS*JIFFYPS)
|
||||
bcc ss_b
|
||||
ss_bd:
|
||||
rts
|
||||
#endif
|
||||
|
||||
; build the 2-char filename "NN" (PETSCII) from ssidx (0..99)
|
||||
namebuild:
|
||||
lda ssidx
|
||||
ldx #$2f
|
||||
sec
|
||||
nb_ten:
|
||||
inx
|
||||
sbc #10
|
||||
bcs nb_ten
|
||||
adc #10
|
||||
ora #$30
|
||||
sta ssname+1
|
||||
txa
|
||||
sta ssname
|
||||
rts
|
||||
|
||||
; copy cnth/cntl bytes from (src) to VDC RAM starting at adhi/adlo
|
||||
copyblk:
|
||||
ldx #18
|
||||
lda adhi
|
||||
jsr vdcw
|
||||
ldx #19
|
||||
lda adlo
|
||||
jsr vdcw
|
||||
ldx #31
|
||||
ldy #$00
|
||||
lda (src),y
|
||||
jsr vdcw
|
||||
inc src
|
||||
bne cb1
|
||||
inc src+1
|
||||
cb1:
|
||||
inc adlo
|
||||
bne cb2
|
||||
inc adhi
|
||||
cb2:
|
||||
lda cntl
|
||||
bne cb3
|
||||
dec cnth
|
||||
cb3:
|
||||
dec cntl
|
||||
lda cntl
|
||||
ora cnth
|
||||
bne copyblk
|
||||
rts
|
||||
|
||||
vdcw:
|
||||
stx $d600
|
||||
vw:
|
||||
bit $d600
|
||||
bpl vw
|
||||
sta $d601
|
||||
rts
|
||||
|
||||
ssidx: .byte 0
|
||||
ssname: .byte $30,$30
|
||||
; ss_fgbg table (one byte per image) appended by the build wrapper
|
||||
Loading…
Add table
Add a link
Reference in a new issue