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

138 lines
3.4 KiB
ArmAsm

; lenser -- Atari 2600 (VCS) image viewer kernel (6502, "racing the beam").
;
; No framebuffer -- per visible scanline the kernel feeds the TIA a 40-pixel
; asymmetric playfield (left 20 + right 20, rewritten mid-line) plus a shared
; playfield colour (COLUPF) and two backgrounds -- COLUBK is set for the left
; half during HBLANK then rewritten mid-line for the right half, so each
; scanline shows 3 colours (left bg + right bg + foreground). The nine 192-byte
; data tables are page-aligned (set by the cartridge builder) so LDA tab,Y never
; crosses a page and the kernel stays cycle-exact.
;
; #defines from the wrapper -- PF0L,PF1L,PF2L,PF0R,PF1R,PF2R,BKL,BKR,PFT (table
; bases), WAITMODE (0 forever / 1 fire / 2 seconds), WAITSECS.
;
; assembled by a2600/viewer/assemble.py via xa
VSYNC = $00
VBLANK = $01
WSYNC = $02
NUSIZ0 = $04
COLUPF = $08
COLUBK = $09
CTRLPF = $0A
PF0 = $0D
PF1 = $0E
PF2 = $0F
INPT4 = $3C
FRLO = $80 ; frame counter (seconds mode)
FRHI = $81
PARITY = $82 ; interlace frame parity (IL mode)
* = $f000
start:
sei
cld
ldx #0
txa
clr:
sta $00,x
inx
bne clr ; clear $00-$FF (RAM + TIA)
ldx #$ff
txs
frame:
lda #2
sta VSYNC
sta WSYNC
sta WSYNC
sta WSYNC ; 3 VSYNC lines
lda #0
sta VSYNC
lda #2
sta VBLANK
sta CTRLPF ; reflect off (we draw both halves explicitly)
lda #0
sta CTRLPF
#if IL
; temporal interlace -- alternate the two 4K banks (frame A / frame B)
; each frame; the kernel is identical in both banks so execution
; continues seamlessly and only the data tables differ.
lda PARITY
eor #1
sta PARITY
beq selbank0
lda $1ff9 ; F8 hotspot -> select bank 1 (AD F9 1F)
jmp bankdone
selbank0:
lda $1ff8 ; F8 hotspot -> select bank 0 (AD F8 1F)
bankdone:
#endif
ldx #36
vbl:
sta WSYNC
dex
bne vbl
lda #0
sta VBLANK
; ---- visible image, 192 cycle-exact scanlines ----
ldy #0
kloop:
sta WSYNC
lda BKL,y ; left background (HBLANK)
sta COLUBK
lda PFT,y ; shared foreground
sta COLUPF
lda PF0L,y
sta PF0
lda PF1L,y
sta PF1
lda PF2L,y
sta PF2
lda PF0R,y ; right playfield PF0 (early, before mid-line)
sta PF0
lda BKR,y ; right background (lands at the half boundary)
sta COLUBK
lda PF1R,y
sta PF1
lda PF2R,y
sta PF2
iny
cpy #192
bne kloop
lda #0
sta PF0
sta PF1
sta PF2
sta COLUBK
; ---- hold / exit ----
#if WAITMODE == 1
lda INPT4
bmi over ; bit7 set = fire not pressed
jmp start ; pressed -> reset (re-display)
#endif
#if WAITMODE == 2
inc FRLO
bne fr_ok
inc FRHI
fr_ok:
lda FRHI
cmp #>(WAITSECS*60)
bcc over
lda FRLO
cmp #<(WAITSECS*60)
bcc over
jmp start
#endif
over:
lda #2
sta VBLANK
ldx #30
ovl:
sta WSYNC
dex
bne ovl
jmp frame