349 lines
7.9 KiB
NASM
349 lines
7.9 KiB
NASM
; -----------------------------------------------------------
|
|
; main.asm - C64 demo: giant TTG logo + credits scroller
|
|
; effects: color wash on the logo, smooth pixel scroll,
|
|
; pulsing title - all timed by polling the raster beam
|
|
; Build: acme -f cbm -o main.prg main.asm
|
|
; -----------------------------------------------------------
|
|
|
|
!to "main.prg", cbm
|
|
|
|
* = $0801
|
|
|
|
; ---- BASIC stub: 10 SYS 2064 ----
|
|
; so after LOAD"*",8,1 it's enough to type RUN
|
|
!byte $0c, $08 ; address of next line
|
|
!byte $0a, $00 ; line number: 10
|
|
!byte $9e ; SYS token
|
|
!byte $20, $32, $30, $36, $34 ; " 2064"
|
|
!byte $00 ; end of line
|
|
!byte $00, $00 ; end of program
|
|
|
|
* = $0810 ; = 2064 in decimal
|
|
|
|
; ---- hardware registers ----
|
|
BORDER = $d020 ; border color register
|
|
BG = $d021 ; background color register
|
|
RASTER = $d012 ; current raster line
|
|
XCTRL = $d016 ; horizontal fine scroll + 38/40 column mode
|
|
SCREEN = $0400 ; screen RAM (40x25 chars)
|
|
COLRAM = $d800 ; color RAM
|
|
|
|
; ---- colors ----
|
|
BLACK = 0
|
|
WHITE = 1
|
|
CYAN = 3
|
|
YELLOW = 7
|
|
LGREEN = 13
|
|
LGREY = 15
|
|
|
|
; ---- screen layout ----
|
|
BIGROW = 2 ; top row of the giant letters
|
|
BIGCOL = 6 ; left column (3*8 + 2*2 = 28 wide, centered)
|
|
TITLE_POS = 12*40 + 13 ; "TELETYPE GAMES" (14 chars, centered)
|
|
SCROLLROW = 14 ; scroller row
|
|
WWW_POS = 17*40 + 9 ; "WWW.TELETYPEGAMES.ORG" (21 chars)
|
|
CITY_POS = 19*40 + 14 ; "SZEGED, 2026" (12 chars)
|
|
|
|
; ---- timing ----
|
|
SCROLL_STEP = 2 ; pixels per frame (8 px / 4 frames = 1 char)
|
|
RAS_SCRTOP = 160 ; just above the scroller row (row 14 = lines 162-169)
|
|
RAS_SCRBOT = 171 ; just below it (rows 13 and 15 are blank, so
|
|
; a line of jitter here is invisible)
|
|
RAS_BOTTOM = 248 ; lower border: safe time for screen updates
|
|
|
|
; ---- zero page pointers ----
|
|
scrlo = $fb
|
|
scrhi = $fc
|
|
collo = $fd
|
|
colhi = $fe
|
|
|
|
start:
|
|
sei ; no KERNAL IRQ - raster polling must not jitter
|
|
|
|
lda #BLACK
|
|
sta BORDER
|
|
sta BG
|
|
|
|
; clear screen RAM, fill color RAM with white
|
|
ldx #0
|
|
clear_loop:
|
|
lda #$20 ; space
|
|
sta SCREEN,x
|
|
sta SCREEN+$100,x
|
|
sta SCREEN+$200,x
|
|
sta SCREEN+$2e8,x
|
|
lda #WHITE
|
|
sta COLRAM,x
|
|
sta COLRAM+$100,x
|
|
sta COLRAM+$200,x
|
|
sta COLRAM+$2e8,x
|
|
inx
|
|
bne clear_loop
|
|
|
|
; ---- draw the giant TTG letters from 8x8 bitmaps ----
|
|
lda #0
|
|
sta letter
|
|
letter_loop:
|
|
ldx letter
|
|
lda letter_off,x
|
|
clc
|
|
adc #<(SCREEN + BIGROW*40 + BIGCOL)
|
|
sta scrlo
|
|
lda #>(SCREEN + BIGROW*40 + BIGCOL)
|
|
adc #0
|
|
sta scrhi
|
|
lda letter_off,x
|
|
clc
|
|
adc #<(COLRAM + BIGROW*40 + BIGCOL)
|
|
sta collo
|
|
lda #>(COLRAM + BIGROW*40 + BIGCOL)
|
|
adc #0
|
|
sta colhi
|
|
|
|
lda letter ; bitmap index = letter * 8
|
|
asl
|
|
asl
|
|
asl
|
|
tax
|
|
lda #8
|
|
sta rowcount
|
|
row_loop:
|
|
lda bigfont,x
|
|
sta curbits
|
|
ldy #0
|
|
bit_loop:
|
|
asl curbits ; leftmost pixel first
|
|
bcc no_plot
|
|
lda #$a0 ; inverse space = solid block
|
|
sta (scrlo),y
|
|
lda #YELLOW
|
|
sta (collo),y
|
|
no_plot:
|
|
iny
|
|
cpy #8
|
|
bne bit_loop
|
|
lda scrlo ; advance both pointers one screen row
|
|
clc
|
|
adc #40
|
|
sta scrlo
|
|
bcc scr_nocarry
|
|
inc scrhi
|
|
scr_nocarry:
|
|
lda collo
|
|
clc
|
|
adc #40
|
|
sta collo
|
|
bcc col_nocarry
|
|
inc colhi
|
|
col_nocarry:
|
|
inx
|
|
dec rowcount
|
|
bne row_loop
|
|
|
|
inc letter
|
|
lda letter
|
|
cmp #3
|
|
bne letter_loop
|
|
|
|
; ---- static texts ----
|
|
ldx #0
|
|
title_loop:
|
|
lda txt_title,x
|
|
sta SCREEN+TITLE_POS,x
|
|
lda #WHITE
|
|
sta COLRAM+TITLE_POS,x
|
|
inx
|
|
cpx #txt_title_end - txt_title
|
|
bne title_loop
|
|
|
|
ldx #0
|
|
www_loop:
|
|
lda txt_www,x
|
|
sta SCREEN+WWW_POS,x
|
|
lda #LGREEN
|
|
sta COLRAM+WWW_POS,x
|
|
inx
|
|
cpx #txt_www_end - txt_www
|
|
bne www_loop
|
|
|
|
ldx #0
|
|
city_loop:
|
|
lda txt_city,x
|
|
sta SCREEN+CITY_POS,x
|
|
lda #LGREY
|
|
sta COLRAM+CITY_POS,x
|
|
inx
|
|
cpx #txt_city_end - txt_city
|
|
bne city_loop
|
|
|
|
; scroller row color
|
|
ldx #39
|
|
lda #CYAN
|
|
scrollcol_loop:
|
|
sta COLRAM+SCROLLROW*40,x
|
|
dex
|
|
bpl scrollcol_loop
|
|
|
|
lda #6 ; multiple of SCROLL_STEP -> clean 4-frame cycle
|
|
sta xscroll
|
|
lda #0
|
|
sta scrollidx
|
|
sta framectr
|
|
sta washphase
|
|
|
|
; ============================================================
|
|
; main loop - one iteration per frame, driven by the raster
|
|
; ============================================================
|
|
main:
|
|
; switch on fine scroll + 38-column mode for the scroller row only
|
|
wait_scrtop:
|
|
lda RASTER
|
|
cmp #RAS_SCRTOP
|
|
bne wait_scrtop
|
|
lda xscroll ; bits 0-2 = x scroll, bit 3 = 0 -> 38 columns
|
|
sta XCTRL
|
|
|
|
wait_scrbot:
|
|
lda RASTER
|
|
cmp #RAS_SCRBOT
|
|
bne wait_scrbot
|
|
lda #$c8 ; back to default: 40 columns, no scroll
|
|
sta XCTRL
|
|
|
|
; beam is in the lower border: do all updates now, invisibly
|
|
wait_bottom:
|
|
lda RASTER
|
|
cmp #RAS_BOTTOM
|
|
bne wait_bottom
|
|
|
|
inc framectr
|
|
|
|
; smooth scroll: move SCROLL_STEP pixels, shift chars on wrap
|
|
lda xscroll
|
|
sec
|
|
sbc #SCROLL_STEP
|
|
bcs xs_store ; still 0..7: just store
|
|
adc #8 ; wrap (carry clear, so this adds exactly 8)
|
|
sta xscroll
|
|
jsr shift_row
|
|
jmp xs_done
|
|
xs_store:
|
|
sta xscroll
|
|
xs_done:
|
|
|
|
lda framectr ; wash moves one column every 2nd frame
|
|
and #1
|
|
bne wash_hold
|
|
inc washphase
|
|
wash_hold:
|
|
|
|
jsr do_wash
|
|
jsr do_pulse
|
|
jmp main
|
|
|
|
; ---- shift the scroller row one char left, feed in the next char ----
|
|
shift_row:
|
|
ldx #0
|
|
sr_loop:
|
|
lda SCREEN+SCROLLROW*40+1,x
|
|
sta SCREEN+SCROLLROW*40,x
|
|
inx
|
|
cpx #39
|
|
bne sr_loop
|
|
ldx scrollidx
|
|
lda scrolltext,x
|
|
sta SCREEN+SCROLLROW*40+39
|
|
inx
|
|
cpx #scrolltext_end - scrolltext
|
|
bne sr_nowrap
|
|
ldx #0
|
|
sr_nowrap:
|
|
stx scrollidx
|
|
rts
|
|
|
|
; ---- color wash: gold gradient running across the logo columns ----
|
|
do_wash:
|
|
ldx #27 ; 28 logo columns
|
|
dw_loop:
|
|
txa
|
|
clc
|
|
adc washphase
|
|
and #15
|
|
tay
|
|
lda washtbl,y
|
|
sta COLRAM + (BIGROW+0)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+1)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+2)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+3)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+4)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+5)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+6)*40 + BIGCOL,x
|
|
sta COLRAM + (BIGROW+7)*40 + BIGCOL,x
|
|
dex
|
|
bpl dw_loop
|
|
rts
|
|
|
|
; ---- pulsing title: grey-white ramp, one step per 8 frames ----
|
|
do_pulse:
|
|
lda framectr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
and #7
|
|
tay
|
|
lda pulsetbl,y
|
|
ldx #txt_title_end - txt_title - 1
|
|
dp_loop:
|
|
sta COLRAM+TITLE_POS,x
|
|
dex
|
|
bpl dp_loop
|
|
rts
|
|
|
|
; ---- data ----
|
|
|
|
; 8x8 bitmaps of the giant letters (one byte per row, MSB = left pixel)
|
|
bigfont:
|
|
; T
|
|
!byte $ff, $ff, $3c, $3c, $3c, $3c, $3c, $3c
|
|
; T
|
|
!byte $ff, $ff, $3c, $3c, $3c, $3c, $3c, $3c
|
|
; G
|
|
!byte $7e, $c3, $c0, $c0, $cf, $c3, $c3, $7e
|
|
|
|
letter_off: ; column offset of each letter (8 wide + 2 gap)
|
|
!byte 0, 10, 20
|
|
|
|
washtbl: ; gold glow ramp (16 entries, cyclic)
|
|
!byte $09, $02, $08, $0a, $07, $07, $01, $01
|
|
!byte $01, $01, $07, $07, $0a, $08, $02, $09
|
|
|
|
pulsetbl: ; grey-white pulse (8 entries, cyclic)
|
|
!byte $0b, $0c, $0f, $01, $01, $0f, $0c, $0b
|
|
|
|
; texts in screen codes (!scr); lowercase source shows as uppercase on C64
|
|
txt_title:
|
|
!scr "teletype games"
|
|
txt_title_end:
|
|
|
|
txt_www:
|
|
!scr "www.teletypegames.org"
|
|
txt_www_end:
|
|
|
|
txt_city:
|
|
!scr "szeged, 2026"
|
|
txt_city_end:
|
|
|
|
scrolltext:
|
|
!scr "mr.zero - tasnadi zsolt / mr.one - tari balazs / "
|
|
!scr "mr.two - timar zoltan / mr.three - mezo bela / "
|
|
scrolltext_end:
|
|
|
|
; ---- variables ----
|
|
letter: !byte 0
|
|
rowcount: !byte 0
|
|
curbits: !byte 0
|
|
framectr: !byte 0
|
|
xscroll: !byte 0
|
|
washphase: !byte 0
|
|
scrollidx: !byte 0
|