8bitlenser/lenser/viewer/slideshow.s
2026-07-03 19:35:35 -07:00

238 lines
7.4 KiB
ArmAsm

; lenser -- slideshow viewer (self-contained code; pictures are separate files)
;
; Boots first on the disk (LOAD"*",8,1 then RUN) and steps through NIMAGES
; picture files named "00".."NN", each a PRG that KERNAL-loads to $2000 --
; $2000 bitmap 8000 (always)
; $3F40 screen 1000 (always -> copied to the buffer's video matrix)
; $4328 colram 1000 (multicolor only -> $D800)
; $4710 background 1 (multicolor only -> $D021)
; The per-image byte in ss_modes selects hires(0) vs multicolor(1) setup. mono
; uses the hires path. WAITMODE (viewer/wait.i) selects key / seconds / both.
;
; DOUBLE BUFFERED so the previous slide stays on screen while the next loads
; (no blank between slides). Two VIC banks alternate as front/back buffer:
; buffer 0 -- VIC bank 0, bitmap $2000, video matrix $0400 ($DD00 bits %11)
; buffer 1 -- VIC bank 1, bitmap $6000, video matrix $4400 ($DD00 bits %10)
; Both use $D018=$18 (matrix at bank+$0400, bitmap at bank+$2000); only the
; $DD00 bank bits differ, so the swap is a single write. Each slide is KERNAL-
; loaded into the *back* buffer (secondary address 0, so the file's $2000 header
; is ignored and it lands at the buffer's base) while the front buffer -- the
; previous picture -- stays displayed; then a bank flip makes it the front.
; ss_hi = ss_buf*$40 is the high-byte offset ($00 buffer 0, $40 buffer 1) added
; to every buffer-relative address. Colour RAM ($D800) is shared, so for a
; multicolor slide it is copied at the swap (a hires slide needs none, and swaps
; perfectly cleanly).
;
; assembled by viewer/assemble.py via xa; the ss_modes table is appended after
; this file by the generated wrapper, and NIMAGES / LOOPFLAG / WAITMODE etc. are
; #defined there.
; BASIC autostart, SYS 2061
* = $0801
.word basicend
.word 10
.byte $9e
.byte "2061"
.byte 0
basicend:
.word 0 ; ML begins at $080D
SRC = $fb
DST = $fd
start:
lda #$00
sta $9d ; suppress KERNAL LOAD messages
sta ss_idx ; start at the first image
sta ss_buf ; first slide loads into buffer 0
sta $d020 ; border black (once)
lda #$0b
sta $d011 ; display off -- the only blank, before slide 0
mainloop:
jsr name_build ; ss_name = "NN" from ss_idx
jsr setup_hi ; ss_hi = ss_buf * $40
lda #$ff
sta $cc ; cursor off (blink can't corrupt $D800)
; ---- KERNAL LOAD "NN" into the BACK buffer ($2000 or $6000) ----
; secondary address 0 -> the file's $2000 header is ignored and the data
; is loaded to the address passed in .X/.Y, so one data file can serve
; either buffer. The front buffer stays displayed throughout.
lda #2
ldx #<ss_name
ldy #>ss_name
jsr $ffbd ; SETNAM
lda #1
ldx #8
ldy #0 ; SA 0 -> load to .X/.Y address
jsr $ffba ; SETLFS
ldx #$00 ; load address low = $00
lda #$20
clc
adc ss_hi
tay ; load address high = $20 + ss_hi
lda #0 ; 0 = load (not verify)
jsr $ffd5 ; LOAD
; ---- copy screen RAM (base+$1F40) -> this buffer's video matrix ----
; (into the still-hidden back buffer, so it is invisible)
lda #$40
sta SRC
lda #$3f
clc
adc ss_hi
sta SRC+1 ; SRC = $3F40 / $7F40
lda #$00
sta DST
lda #$04
clc
adc ss_hi
sta DST+1 ; DST = $0400 / $4400
jsr copy1024
; ---- per-image mode -- 0 = hires/mono, 1 = multicolor ----
ldx ss_idx
lda ss_modes,x
beq ss_hires
; multicolor -- colour RAM (base+$2328) -> $D800, background from base+$2710
lda #$28
sta SRC
lda #$43
clc
adc ss_hi
sta SRC+1 ; SRC = $4328 / $8328
lda #$00
sta DST
lda #$d8
sta DST+1
jsr copy1024
lda #$10
sta SRC
lda #$47
clc
adc ss_hi
sta SRC+1 ; SRC = $4710 / $8710
ldy #$00
lda (SRC),y
sta $d021 ; background colour
jsr flip_bank ; make the back buffer the front
lda #$d8
sta $d016 ; multicolor on
jmp ss_on
ss_hires:
jsr flip_bank ; make the back buffer the front
lda #$c8
sta $d016 ; hires (multicolor off)
ss_on:
lda #$18
sta $d018 ; matrix bank+$0400, bitmap bank+$2000
lda #$3b
sta $d011 ; bitmap mode, display on
lda ss_buf
eor #$01
sta ss_buf ; next slide loads into the other buffer
#include "wait.i"
; ---- advance to the next image ----
inc ss_idx
lda ss_idx
cmp #NIMAGES
bcc ss_go ; still more images
#if LOOPFLAG == 1
lda #$00
sta ss_idx ; wrap around forever
ss_go:
jmp mainloop
#else
jmp ss_end ; done -> restore and return to BASIC
ss_go:
jmp mainloop
ss_end:
#endif
; ---- restore text mode and return to BASIC ----
lda $dd00
ora #$03
sta $dd00 ; VIC bank 0
lda #$1b
sta $d011
lda #$c8
sta $d016
lda #$15
sta $d018
lda #$0e
sta $d020 ; default border (light blue)
lda #$06
sta $d021 ; default background (blue)
lda #$00
sta $cc
jsr $e544 ; clear screen
rts
; ss_hi = ss_buf * $40 (high-byte offset for the current back buffer)
setup_hi:
ldy #$00
lda ss_buf
beq sh_set
ldy #$40
sh_set:
sty ss_hi
rts
; point VIC at the current buffer's bank (buffer 0 -> bank 0, buffer 1 -> bank 1)
flip_bank:
lda ss_buf
bne fb_one
lda $dd00
ora #$03
sta $dd00 ; bank 0 ($0000-$3FFF)
rts
fb_one:
lda $dd00
and #$fc
ora #$02
sta $dd00 ; bank 1 ($4000-$7FFF)
rts
; build the 2-char filename "NN" from ss_idx (0..99)
name_build:
lda ss_idx
ldx #$2f
sec
ss_ten:
inx
sbc #10
bcs ss_ten
adc #10 ; remainder 0..9 (carry was clear on exit)
ora #$30
sta ss_name+1 ; ones digit (PETSCII)
txa
sta ss_name ; tens digit (PETSCII)
rts
; copy 1024 bytes from (SRC) to (DST)
copy1024:
ldx #4
ldy #0
cploop:
lda (SRC),y
sta (DST),y
iny
bne cploop
inc SRC+1
inc DST+1
dex
bne cploop
rts
ss_idx: .byte 0
ss_buf: .byte 0 ; 0 or 1 -- which buffer the next slide loads into
ss_hi: .byte 0 ; ss_buf * $40 (buffer high-byte offset)
ss_name: .byte $30,$30 ; "00", rebuilt each slide from ss_idx
; ss_modes table (.byte per image) appended by viewer/assemble.py's wrapper