First public commit.

This commit is contained in:
The Dust Council 2026-07-03 19:35:35 -07:00
parent 2a48f52979
commit 4bac9d83ed
288 changed files with 18417 additions and 1076 deletions

View file

@ -0,0 +1 @@
from .assemble import AssemblerError, SOURCES, assemble_stub, have_xa # noqa: F401

View file

@ -0,0 +1,156 @@
"""Assemble the Atari 6502 boot viewers with `xa` (origin $2000, no load prefix)."""
from __future__ import annotations
import os
import shutil
import subprocess
import tempfile
VIEWER_DIR = os.path.dirname(os.path.abspath(__file__))
SOURCES = {
"gr15": "gr15.s",
"gr9": "gr9.s",
"gr8": "gr8.s",
"gr15dli": "gr15dli.s",
}
_cache: dict[tuple, bytes] = {}
# How long the viewer holds the picture (see atari/viewer/awyt.i).
WAIT_MODES = {"forever": 0, "key": 1, "seconds": 2}
# Slideshow advance behaviour and per-mode multi-image viewer parameters
# (source, ANTIC mode byte, GPRIOR, colour-register layout).
SS_WAITMODE = {"key": 1, "seconds": 2, "both": 3}
SLIDESHOW_PARAMS = {
"gr15": (0x0E, 0x00, 0),
"gr9": (0x0F, 0x40, 1),
"gr8": (0x0F, 0x00, 2),
}
SLIDESHOW_SOURCES = dict.fromkeys(SLIDESHOW_PARAMS, "slideshow_static.s")
SLIDESHOW_SOURCES["gr15dli"] = "slideshow_dli.s" # DLI mode, its own engine
class AssemblerError(RuntimeError):
pass
def have_xa() -> bool:
return shutil.which("xa") is not None
def assemble_stub(viewer_key: str, display: str = "forever", seconds: int = 0,
video: str = "ntsc") -> bytes:
waitmode = WAIT_MODES.get(display, 0)
rate = 50 if video == "pal" else 60
key = (viewer_key, waitmode, int(seconds), rate)
if key in _cache:
return _cache[key]
if not have_xa():
raise AssemblerError(
"The 'xa' (xa65) assembler was not found on PATH.\n"
"Install it with: sudo apt install xa65")
if not os.path.exists(os.path.join(VIEWER_DIR, SOURCES[viewer_key])):
raise AssemblerError(f"viewer source missing: {SOURCES[viewer_key]}")
# Wrapper sets the options then includes the real source; runs from VIEWER_DIR
# so the source's #include "awyt.i" resolves (xa looks relative to cwd).
wrapper = (
f"#define WAITMODE {waitmode}\n"
f"#define WAITSECS {max(0, int(seconds))}\n"
f"#define RATE {rate}\n"
f'#include "{SOURCES[viewer_key]}"\n')
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 for {viewer_key}:\n{proc.stdout}{proc.stderr}")
with open(out, "rb") as f:
raw = f.read()
finally:
os.unlink(wrap)
_cache[key] = raw
return raw
def _xa(wrapper: str, what: str) -> bytes:
"""Assemble a generated wrapper with xa (run from VIEWER_DIR so #includes
resolve); return raw bytes."""
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 for {what}:\n{proc.stdout}{proc.stderr}")
with open(out, "rb") as f:
return f.read()
finally:
os.unlink(wrap)
def build_slideshow_stub(viewer_key: str, n_images: int, base_sec: int, spi: int,
advance: str = "both", seconds: int = 10,
loop: bool = True, video: str = "ntsc") -> bytes:
"""Assemble the multi-image slideshow viewer (origin $2000, no load prefix).
``base_sec`` is the disk sector of image 0 and ``spi`` the sectors per image
(both fixed by the ATR layout); the viewer SIO-reads image i from
base_sec + i*spi. ``advance``/``seconds`` set the per-slide dwell, ``loop``
whether it wraps.
"""
if viewer_key not in SLIDESHOW_SOURCES:
raise AssemblerError(f"no Atari slideshow viewer for mode {viewer_key}")
rate = 50 if video == "pal" else 60
common = (f"#define WAITMODE {SS_WAITMODE[advance]}\n"
f"#define WAITSECS {max(0, int(seconds))}\n"
f"#define RATE {rate}\n"
f"#define NIMAGES {n_images}\n"
f"#define LOOPFLAG {1 if loop else 0}\n"
f"#define BASESEC {base_sec}\n"
f"#define SPI {spi}\n")
if viewer_key in SLIDESHOW_PARAMS: # static gr15/gr9/gr8
dlmode, gprior, colormode = SLIDESHOW_PARAMS[viewer_key]
common += (f"#define DLMODE ${dlmode:02X}\n"
f"#define GPRIOR ${gprior:02X}\n"
f"#define COLORMODE {colormode}\n")
return _xa(common + f'#include "{SLIDESHOW_SOURCES[viewer_key]}"\n',
f"slideshow_{viewer_key}")
CART_SIZE = 0x4000 # 16K Atari cartridge ROM at $8000-$BFFF
def build_cart_rom(viewer_key: str, data: bytes, display: str = "forever",
seconds: int = 0, video: str = "ntsc") -> bytes:
"""Assemble the loader + the disk viewer stub + picture data into a 16K
cartridge ROM with the Atari run/init footer at $BFFA."""
stub = assemble_stub(viewer_key, display, seconds, video)
wrapper = (
f"#define STUB_PAGES {(len(stub) + 255) // 256}\n"
f"#define DATA_PAGES {(len(data) + 255) // 256}\n"
f"#define STUB_LEN {len(stub)}\n"
'#include "cart.s"\n')
loader = _xa(wrapper, "cart")
rom = bytearray(loader + stub + bytes(data))
if len(rom) > CART_SIZE:
raise AssemblerError(
f"viewer + image = {len(rom)} bytes, over the 16K cartridge limit")
rom += bytes(CART_SIZE - len(rom))
# Atari cartridge footer at $BFFA (ROM offset $3FFA).
rom[0x3FFA] = 0x00; rom[0x3FFB] = 0x80 # CARTCS run address = $8000
rom[0x3FFC] = 0x00 # cart present
rom[0x3FFD] = 0x04 # option byte: start the cartridge
rom[0x3FFE] = 0x00; rom[0x3FFF] = 0x80 # CARTAD init address = $8000
return bytes(rom)

View file

@ -0,0 +1,48 @@
; Shared "how long to show the picture" epilogue for the Atari viewers.
; Selected at assembly time by WAITMODE (set by atari/viewer/assemble.py):
; 0 forever -- loop, just defeating attract mode
; 1 until a key -- poll CH ($2FC), then warm-start the OS
; 2 WAITSECS secs -- count RTCLOK frames (RATE per second), then warm-start
; "Exit" warm-starts the OS ($E474); on the XL that brings back a usable system.
#if WAITMODE == 0
awloop:
lda #$00
sta $4d ; defeat attract mode
jmp awloop
#endif
#if WAITMODE == 1
lda #$ff
sta $2fc ; clear CH (last-key register; $FF = no key)
awloop:
lda #$00
sta $4d
lda $2fc
cmp #$ff
beq awloop
lda #$00
sta $09 ; clear BOOT? so warmstart enters BASIC, not re-boot
jmp $e474 ; warm-start
#endif
#if WAITMODE == 2
lda #$00
sta $12
sta $13
sta $14 ; reset RTCLOK frame counter (16-bit in $13,$14)
awloop:
lda #$00
sta $4d
lda $13
cmp #>(WAITSECS*RATE)
bcc awloop
bne awdone
lda $14
cmp #<(WAITSECS*RATE)
bcc awloop
awdone:
lda #$00
sta $09 ; clear BOOT? so warmstart enters BASIC, not re-boot
jmp $e474 ; warm-start
#endif

View file

@ -0,0 +1,67 @@
; lenser -- Atari 16K cartridge loader ($8000-$BFFF).
;
; Reuses the disk viewer unchanged. The disk viewer "stub" (origin $2000, which
; begins with the 6-byte boot header and contains cont + the display list) and
; the picture data (origin $4000) are stored back-to-back in the cartridge ROM
; after this loader. We copy the stub to $2000 and the data to $4000, then jump
; to cont ($2006) -- exactly the state a disk boot would have produced.
;
; #defines set by viewer/assemble.py --
; STUB_PAGES / DATA_PAGES 256-byte page counts to copy
; STUB_LEN exact stub length (data follows it in ROM)
;
; assembled by atari/viewer/assemble.py via xa
* = $8000
S = $80
D = $82
entry:
; ---- copy the viewer stub to $2000 ----
lda #<stubsrc
sta S
lda #>stubsrc
sta S+1
lda #$00
sta D
lda #$20
sta D+1
ldx #STUB_PAGES
ldy #$00
sc:
lda (S),y
sta (D),y
iny
bne sc
inc S+1
inc D+1
dex
bne sc
; ---- copy the picture data to $4000 ----
lda #<datasrc
sta S
lda #>datasrc
sta S+1
lda #$00
sta D
lda #$40
sta D+1
ldx #DATA_PAGES
ldy #$00
dc:
lda (S),y
sta (D),y
iny
bne dc
inc S+1
inc D+1
dex
bne dc
jmp $2006 ; enter the disk viewer (cont)
stubsrc:
; viewer stub + picture data appended here by the packager
datasrc = stubsrc + STUB_LEN

View file

@ -0,0 +1,50 @@
; lenser -- Atari GR.15 (ANTIC mode E) viewer, self-booting
;
; 160x192, 4 colours chosen globally (no per-cell limit). Boots from sector 1:
; the OS loads this blob to $2000 and JSRs $2006. Appended data (from $4000):
; $4000 bitmap lines 0-101 (4080 bytes)
; $5000 bitmap lines 102-191 (3600 bytes) [split at the 4K ANTIC boundary]
; $6000 4 colour register values (value 0..3)
;
; assembled by atari/viewer/assemble.py via xa
* = $2000
boot:
.byte 0 ; flags
.byte 0 ; sector count (patched by the ATR writer)
.word $2000 ; load address
.word binit ; init address (DOSINI)
cont: ; $2006 -- OS JSRs here after loading
lda #$22
sta $22f ; SDMCTL = normal playfield + DL DMA
lda #<dlist
sta $230 ; SDLSTL
lda #>dlist
sta $231 ; SDLSTH
lda #$00
sta $26f ; GPRIOR = 0 (no GTIA mode)
; copy the 4 colour registers from $6000
lda $6000
sta $2c8 ; COLBAK (pixel value 0)
lda $6001
sta $2c4 ; COLPF0 (value 1)
lda $6002
sta $2c5 ; COLPF1 (value 2)
lda $6003
sta $2c6 ; COLPF2 (value 3)
#include "awyt.i"
binit:
rts
dlist:
.byte $70,$70,$70 ; 24 blank scan lines
.byte $4e ; LMS + mode E
.word $4000
.dsb 101,$0e ; 101 more mode E lines (102 total from $4000)
.byte $4e ; LMS + mode E
.word $5000
.dsb 89,$0e ; 89 more mode E lines (90 total from $5000)
.byte $41 ; JVB
.word dlist

View file

@ -0,0 +1,100 @@
; lenser -- Atari GR.15 + DLI viewer, self-booting
; 160x192, 4 colours rewritten every 2 scanlines by a display-list interrupt.
; Appended data from $4000 ...
; $4000/$5000 bitmap (2 bits per pixel, 4K split)
; $6000 colour table, 96 bands x 4 register values
; $6400 display list (DLI bit on the last line of each 2-line band)
;
; assembled by atari/viewer/assemble.py via xa
CP = $cb ; zero-page pointer into the colour table
* = $2000
boot:
.byte 0
.byte 0 ; sector count (patched)
.word $2000
.word binit
cont:
sei
lda #$22
sta $22f ; SDMCTL
lda #$00
sta $26f ; GPRIOR = 0
lda #$00
sta $230 ; SDLSTL = $6400 (DL shipped in the data)
lda #$64
sta $231
; DLI vector
lda #<dli
sta $200
lda #>dli
sta $201
; colour pointer starts at band 1 (band 0 set by the VBI)
lda #$04
sta CP
lda #$60
sta CP+1
lda $6000
sta $2c8
lda $6001
sta $2c4
lda $6002
sta $2c5
lda $6003
sta $2c6
ldy #<vbi
ldx #>vbi
lda #$07
jsr $e45c ; SETVBV (deferred)
lda #$c0
sta $d40e ; NMIEN = DLI + VBI
cli
#include "awyt.i"
binit:
rts
vbi:
lda #$04
sta CP
lda #$60
sta CP+1
lda $6000
sta $d01a
lda $6001
sta $d016
lda $6002
sta $d017
lda $6003
sta $d018
jmp $e462 ; XITVBV
dli:
pha
tya
pha
ldy #$00
sta $d40a ; WSYNC
lda (CP),y
sta $d01a
iny
lda (CP),y
sta $d016
iny
lda (CP),y
sta $d017
iny
lda (CP),y
sta $d018
lda CP
clc
adc #$04
sta CP
bcc nocarry
inc CP+1
nocarry:
pla
tay
pla
rti

42
lenser/atari/viewer/gr8.s Normal file
View file

@ -0,0 +1,42 @@
; lenser -- Atari GR.8 (ANTIC mode F) hi-res viewer, self-booting
; 320x192, two tones. Appended data from $4000 ...
; $4000/$5000 bitmap (1 bit per pixel, 4K split)
; $6000 two bytes, background reg then foreground reg
;
; assembled by atari/viewer/assemble.py via xa
* = $2000
boot:
.byte 0
.byte 0 ; sector count (patched)
.word $2000
.word binit
cont:
lda #$22
sta $22f ; SDMCTL
lda #<dlist
sta $230
lda #>dlist
sta $231
lda #$00
sta $26f ; GPRIOR = 0
lda $6000
sta $2c6 ; COLPF2 (background)
sta $2c8 ; COLBAK (border) = background
lda $6001
sta $2c5 ; COLPF1 (foreground luminance)
#include "awyt.i"
binit:
rts
dlist:
.byte $70,$70,$70
.byte $4f ; LMS + mode F
.word $4000
.dsb 101,$0f
.byte $4f
.word $5000
.dsb 89,$0f
.byte $41
.word dlist

39
lenser/atari/viewer/gr9.s Normal file
View file

@ -0,0 +1,39 @@
; lenser -- Atari GR.9 (GTIA 16-luminance) viewer, self-booting
; 80x192, 16 shades of one hue. Appended data from $4000 ...
; $4000/$5000 bitmap (4 bits per pixel, 4K split)
; $6000 one byte, COLBAK = hue times 16
;
; assembled by atari/viewer/assemble.py via xa
* = $2000
boot:
.byte 0
.byte 0 ; sector count (patched)
.word $2000
.word binit
cont:
lda #$22
sta $22f ; SDMCTL
lda #<dlist
sta $230
lda #>dlist
sta $231
lda #$40
sta $26f ; GPRIOR = GTIA mode 1 (GR.9)
lda $6000
sta $2c8 ; COLBAK sets the hue
#include "awyt.i"
binit:
rts
dlist:
.byte $70,$70,$70
.byte $4f ; LMS + mode F
.word $4000
.dsb 101,$0f
.byte $4f
.word $5000
.dsb 89,$0f
.byte $41
.word dlist

View file

@ -0,0 +1,248 @@
; lenser -- Atari GR.15 + DLI slideshow viewer (per-line colour).
;
; Self-booting (OS loads this to $2000, JSRs $2006). Steps through NIMAGES
; pictures stored as raw sectors -- image i at sectors BASESEC + i*SPI -- SIO-
; reading SPI sectors into $4000 (bitmap $4000/$5000, colour table $6000 of
; 96 bands x 4, display list $6400) and showing each with a display-list
; interrupt that rewrites the four colour registers every two scanlines.
;
; build-time #defines -- WAITMODE WAITSECS RATE NIMAGES LOOPFLAG BASESEC SPI.
CP = $cb ; zero-page pointer into the colour table
* = $2000
boot:
.byte 0 ; flags
.byte 0 ; sector count (patched)
.word $2000
.word binit
cont: ; $2006 -- OS JSRs here after loading the stub
sei
lda #$00
sta $26f ; GPRIOR = 0
lda #$00
sta $230 ; SDLSTL = $6400 (the loaded display list)
lda #$64
sta $231
lda #<dli
sta $200
lda #>dli
sta $201
ldy #<vbi
ldx #>vbi
lda #$07
jsr $e45c ; SETVBV (deferred VBI)
lda #$00
sta ssidx
cli
ssmain:
lda #$00
sta $d40e ; NMIEN off (no DLI/VBI while loading)
sta $22f ; SDMCTL off (blank)
jsr readimg ; SIO-load image ssidx (IRQ stays on for SIOV)
; re-init the colour pointer + band-0 colours for the new image
lda #$04
sta CP
lda #$60
sta CP+1
lda $6000
sta $2c8
lda $6001
sta $2c4
lda $6002
sta $2c5
lda $6003
sta $2c6
lda #$c0
sta $d40e ; NMIEN = DLI + VBI
lda #$22
sta $22f ; SDMCTL = playfield + DL DMA
jsr sswait
inc ssidx
lda ssidx
cmp #NIMAGES
bcc ssmain
#if LOOPFLAG == 1
lda #$00
sta ssidx
jmp ssmain
#else
sei
lda #$00
sta $d40e ; NMIEN off
lda #$40
sta $d40e ; restore VBI only (OS housekeeping)
lda #$00
sta $09
cli
jmp $e474 ; warm-start (exit)
#endif
binit:
rts
; ---- SIO read SPI sectors of image ssidx into $4000 ----
readimg:
lda #<BASESEC
sta secn
lda #>BASESEC
sta secn+1
ldx ssidx
beq rsbuf
radd:
clc
lda secn
adc #SPI
sta secn
bcc ra1
inc secn+1
ra1:
dex
bne radd
rsbuf:
lda #$00
sta $0304
lda #$40
sta $0305
lda #SPI
sta cnt
rloop:
lda #$31
sta $0300
lda #$01
sta $0301
lda #$52
sta $0302
lda #$40
sta $0303
lda #$1f
sta $0306
lda #$80
sta $0308
lda #$00
sta $0309
lda secn
sta $030a
lda secn+1
sta $030b
jsr $e459 ; SIOV
clc
lda $0304
adc #$80
sta $0304
bcc rb1
inc $0305
rb1:
inc secn
bne rb2
inc secn+1
rb2:
dec cnt
bne rloop
rts
; ---- wait (returns; defeats attract mode via $4d) ----
sswait:
#if WAITMODE == 1
lda #$ff
sta $2fc
sw1:
lda #$00
sta $4d
lda $2fc
cmp #$ff
beq sw1
rts
#endif
#if WAITMODE == 2
lda #$00
sta $12
sta $13
sta $14
sw2:
lda #$00
sta $4d
lda $13
cmp #>(WAITSECS*RATE)
bcc sw2
bne sw2d
lda $14
cmp #<(WAITSECS*RATE)
bcc sw2
sw2d:
rts
#endif
#if WAITMODE == 3
lda #$ff
sta $2fc
lda #$00
sta $12
sta $13
sta $14
sw3:
lda #$00
sta $4d
lda $2fc
cmp #$ff
bne sw3d
lda $13
cmp #>(WAITSECS*RATE)
bcc sw3
bne sw3d
lda $14
cmp #<(WAITSECS*RATE)
bcc sw3
sw3d:
rts
#endif
ssidx: .byte 0
secn: .word 0
cnt: .byte 0
; reset colour pointer + band-0 colours each frame (from the loaded $6000 table)
vbi:
lda #$04
sta CP
lda #$60
sta CP+1
lda $6000
sta $d01a
lda $6001
sta $d016
lda $6002
sta $d017
lda $6003
sta $d018
jmp $e462 ; XITVBV
dli:
pha
tya
pha
ldy #$00
sta $d40a ; WSYNC
lda (CP),y
sta $d01a
iny
lda (CP),y
sta $d016
iny
lda (CP),y
sta $d017
iny
lda (CP),y
sta $d018
lda CP
clc
adc #$04
sta CP
bcc nocarry
inc CP+1
nocarry:
pla
tay
pla
rti

View file

@ -0,0 +1,272 @@
; lenser -- Atari static-playfield slideshow viewer (GR.15 / GR.9 / GR.8).
;
; Self-booting (OS loads this to $2000, JSRs $2006). Steps through NIMAGES
; pictures stored as raw sectors -- image i at sectors BASESEC + i*SPI -- SIO-
; reading SPI sectors and showing each before advancing (key / seconds / both).
;
; DOUBLE BUFFERED so the previous slide stays on screen while the next loads (no
; blank between slides). Two RAM buffers alternate as front/back --
; buffer 0 -- bitmap $4000/$5000, colour bytes $6000
; buffer 1 -- bitmap $7000/$8000, colour bytes $9000
; both kept below $A000 so they are RAM even when the BASIC ROM is enabled. The
; display list's two LMS addresses point at the front buffer; each slide is SIO-
; read into the *back* buffer while the front (the previous picture) keeps
; displaying, then -- during a vertical blank so the change isn't torn -- the LMS
; addresses and colour registers are switched to it. ss_hi = ssbuf*$30 is the
; high-byte offset ($00 buffer 0, $30 buffer 1) added to every buffer address.
;
; The mode is fixed for the whole slideshow, chosen by build-time #defines --
; DLMODE ANTIC mode byte ($0e GR.15, $0f GR.9/GR.8)
; GPRIOR GTIA priority/mode ($00, or $40 for GR.9)
; COLORMODE colour-register layout (0 GR.15 / 1 GR.9 / 2 GR.8)
; plus WAITMODE/WAITSECS/RATE, NIMAGES, LOOPFLAG, BASESEC, SPI (see assemble.py).
colptr = $cb ; zero-page pointer to the back buffer's colours
* = $2000
boot:
.byte 0 ; flags
.byte 0 ; sector count (patched by the ATR writer)
.word $2000 ; load address
.word binit ; init address (DOSINI)
cont: ; $2006 -- OS JSRs here after loading the stub
lda #<dlist
sta $230 ; SDLSTL
lda #>dlist
sta $231 ; SDLSTH
lda #GPRIOR
sta $26f ; GPRIOR
lda #$00
sta $22f ; SDMCTL = 0 (blank before the first image only)
sta ssidx
sta ssbuf ; first slide loads into buffer 0
ssmain:
jsr sethi ; ss_hi = ssbuf * $30
jsr readimg ; SIO-load image ssidx into the back buffer
; ---- wait for a vertical blank, then flip to the back buffer ----
; (the front buffer -- the previous slide -- has stayed on screen)
lda $14
vbwait:
cmp $14
beq vbwait ; RTCLOK ticked -> we are in the vertical blank
; display-list LMS -- region 1 = base, region 2 = base + $1000
lda #$00
sta lms1
sta lms2
lda #$40
clc
adc ss_hi
sta lms1+1 ; $40 / $70
lda #$50
clc
adc ss_hi
sta lms2+1 ; $50 / $80
; colour bytes are at base + $2000
lda #$00
sta colptr
lda #$60
clc
adc ss_hi
sta colptr+1 ; $60 / $90
#if COLORMODE == 0
ldy #$00
lda (colptr),y
sta $2c8 ; COLBAK (value 0)
ldy #$01
lda (colptr),y
sta $2c4 ; COLPF0 (value 1)
ldy #$02
lda (colptr),y
sta $2c5 ; COLPF1 (value 2)
ldy #$03
lda (colptr),y
sta $2c6 ; COLPF2 (value 3)
#endif
#if COLORMODE == 1
ldy #$00
lda (colptr),y
sta $2c8 ; COLBAK = hue (GR.9)
#endif
#if COLORMODE == 2
ldy #$00
lda (colptr),y
sta $2c6 ; COLPF2 background
sta $2c8 ; COLBAK border = background
ldy #$01
lda (colptr),y
sta $2c5 ; COLPF1 foreground
#endif
lda #$22
sta $22f ; SDMCTL on (a no-op after the first slide)
jsr sswait
lda ssbuf
eor #$01
sta ssbuf ; next slide loads into the other buffer
inc ssidx
lda ssidx
cmp #NIMAGES
bcc ssmain
#if LOOPFLAG == 1
lda #$00
sta ssidx
jmp ssmain
#else
lda #$00
sta $09 ; clear BOOT? so warmstart enters BASIC
jmp $e474 ; warm-start (exit)
#endif
binit:
rts
; ss_hi = ssbuf * $30 (back-buffer high-byte offset -- $00 buffer 0, $30 buffer 1)
sethi:
ldx #$00
lda ssbuf
beq sh0
ldx #$30
sh0:
stx ss_hi
rts
; ---- SIO read SPI sectors of image ssidx into the back buffer (base+$0000) ----
readimg:
lda #<BASESEC
sta secn
lda #>BASESEC
sta secn+1
ldx ssidx
beq rsbuf
radd:
clc
lda secn
adc #SPI
sta secn
bcc ra1
inc secn+1
ra1:
dex
bne radd
rsbuf:
lda #$00
sta $0304 ; DBUFLO = $00
lda #$40
clc
adc ss_hi
sta $0305 ; DBUFHI = $40 / $70 (back buffer base)
lda #SPI
sta cnt
rloop:
lda #$31
sta $0300 ; DDEVIC = disk
lda #$01
sta $0301 ; DUNIT 1
lda #$52
sta $0302 ; DCOMND R (read)
lda #$40
sta $0303 ; DSTATS = read direction
lda #$1f
sta $0306 ; DTIMLO
lda #$80
sta $0308 ; DBYTLO = 128
lda #$00
sta $0309 ; DBYTHI
lda secn
sta $030a ; DAUX1 sector low
lda secn+1
sta $030b ; DAUX2 sector high
jsr $e459 ; SIOV
clc
lda $0304
adc #$80
sta $0304 ; buffer += 128
bcc rb1
inc $0305
rb1:
inc secn
bne rb2
inc secn+1
rb2:
dec cnt
bne rloop
rts
; ---- wait (returns; defeats attract mode via $4d) ----
sswait:
#if WAITMODE == 1
lda #$ff
sta $2fc ; clear CH
sw1:
lda #$00
sta $4d
lda $2fc
cmp #$ff
beq sw1
rts
#endif
#if WAITMODE == 2
lda #$00
sta $12
sta $13
sta $14 ; reset RTCLOK
sw2:
lda #$00
sta $4d
lda $13
cmp #>(WAITSECS*RATE)
bcc sw2
bne sw2d
lda $14
cmp #<(WAITSECS*RATE)
bcc sw2
sw2d:
rts
#endif
#if WAITMODE == 3
lda #$ff
sta $2fc
lda #$00
sta $12
sta $13
sta $14
sw3:
lda #$00
sta $4d
lda $2fc
cmp #$ff
bne sw3d ; any key ends the slide
lda $13
cmp #>(WAITSECS*RATE)
bcc sw3
bne sw3d
lda $14
cmp #<(WAITSECS*RATE)
bcc sw3
sw3d:
rts
#endif
ssidx: .byte 0
ssbuf: .byte 0 ; 0 or 1 -- which buffer the next slide loads into
ss_hi: .byte 0 ; ssbuf * $30 (back-buffer high-byte offset)
secn: .word 0
cnt: .byte 0
dlist:
.byte $70,$70,$70 ; 24 blank scan lines
.byte DLMODE+$40 ; LMS + mode
lms1:
.word $4000 ; region 1 base (patched to the front buffer)
.dsb 101,DLMODE
.byte DLMODE+$40 ; LMS + mode
lms2:
.word $5000 ; region 2 base (patched to the front buffer)
.dsb 89,DLMODE
.byte $41 ; JVB
.word dlist