NITETIME GOPHER 1.0 - Lake Koshkonong Node
Server: gopher.nitetime.net:70    Selector: /commodore/hell.asm
; ==============================================================================
; VECTOR OSCILLOSCOPE FROM HELL (Commodore 128)
; A high-speed, mutating Lissajous/Alien signal generator with phosphor trails.
; ==============================================================================

!cpu 6510
!to "hell.prg",cbm

; --- Zero Page Variables ---
zp_acc_0      = $02
zp_acc_1      = $03
zp_acc_2      = $04
zp_i_lo       = $05
zp_i_hi       = $06
zp_add_0      = $07
zp_add_1      = $08

ptr_ring_x_lo = $09
ptr_ring_x_hi = $0B
ptr_ring_y    = $0D

ring_idx      = $0F

old_x_lo      = $10
old_x_hi      = $11
old_y         = $12
new_x_lo      = $13
new_x_hi      = $14
new_y         = $15

zp_scr_lo     = $16
zp_scr_hi     = $17
zp_mul_a      = $18
zp_mul_b      = $19
sum_lo        = $1A
sum_hi        = $1B
zp_t1         = $1C
zp_t2         = $1D

; --- State / Math Variables ---
phase_x_lo    = $20
phase_x_hi    = $21
phase_y_lo    = $22
phase_y_hi    = $23
phase_m_lo    = $24
phase_m_hi    = $25

freq_x        = $26
freq_y        = $27
freq_m        = $28
amp_x         = $29
amp_y         = $2A
amp_m         = $2B

t_freq_x      = $2C
t_freq_y      = $2D
t_freq_m      = $2E
t_amp_x       = $2F
t_amp_y       = $30
t_amp_m       = $31

curr_mode     = $32
mode_timer    = $33
pts_counter   = $34

; --- Memory Map ---
; $1C01 - $1FFF : Code and Loader
; $2000 - $20FF : Sine Table
; $2100 - $21C7 : Row Address Lo
; $2200 - $22C7 : Row Address Hi
; $2300 - $2307 : Pixel Bitmasks
; $3000 - $33FF : Ring Buffer X_lo (1024 bytes)
; $3400 - $37FF : Ring Buffer X_hi (1024 bytes)
; $3800 - $3BFF : Ring Buffer Y    (1024 bytes)
; $4000 - $43E7 : Screen RAM (Color Matrix)
; $6000 - $7F3F : VIC-II Bitmap
; $8000 - $81FF : Quarter Squares Table

row_addr_lo = $2100
row_addr_hi = $2200
bit_mask    = $2300
bit_inv     = $2308
sqr_hi_0    = $8000
sqr_hi_1    = $8100

; ==============================================================================
; BASIC SYS LOADER (Loads at $1C01 in C128 mode)
; ==============================================================================
* = $1C01
!byte $0C,$1C,$0A,$00,$9E,$37,$31,$38,$31,$00,$00,$00 ; 10 SYS 7181

* = $1C0D
jmp init

; ==============================================================================
; INITIALIZATION
; ==============================================================================
init:
    sei
    ; 1. Setup MMU for pure RAM in Bank 0, but keep I/O visible
    lda #$00
    sta $FF00       ; Configuration register (RAM Bank 0, I/O at $D000)

    ; 2. Point VIC-II to Bank 1 ($4000 - $7FFF)
    lda $DD00
    and #$FC
    ora #$02
    sta $DD00

    ; 3. Setup VIC-II Hires Bitmap Mode
    lda #$3B        ; Bitmap mode, Screen ON, 25 rows
    sta $D011
    lda #$08        ; Hires (No Multi-color)
    sta $D016
    lda #$08        ; Video matrix at $4000 (offset $0000), Bitmap at $6000 (offset $2000)
    sta $D018

    ; Colors
    lda #$00
    sta $D020       ; Border Black
    sta $D021       ; Background Black

    ; 4. Clear Video Matrix to Green ($50)
    ldx #$00
    lda #$50        ; Color 5 (Green) foreground, 0 (Black) background
.clr_scr:
    sta $4000,x
    sta $4100,x
    sta $4200,x
    sta $4300,x
    inx
    bne .clr_scr

    ; 5. Clear Bitmap to Black ($00)
    lda #$00
    ldx #$00
.clr_bmp:
    sta $6000,x
    sta $6100,x
    sta $6200,x
    sta $6300,x
    sta $6400,x
    sta $6500,x
    sta $6600,x
    sta $6700,x
    sta $6800,x
    sta $6900,x
    sta $6A00,x
    sta $6B00,x
    sta $6C00,x
    sta $6D00,x
    sta $6E00,x
    sta $6F00,x
    sta $7000,x
    sta $7100,x
    sta $7200,x
    sta $7300,x
    sta $7400,x
    sta $7500,x
    sta $7600,x
    sta $7700,x
    sta $7800,x
    sta $7900,x
    sta $7A00,x
    sta $7B00,x
    sta $7C00,x
    sta $7D00,x
    sta $7E00,x
    sta $7F00,x
    inx
    bne .clr_bmp

    ; 6. Build Tables
    jsr build_row_table
    jsr build_sqr_table
    jsr build_bitmasks

    ; 7. Init State
    lda #$30
    sta ptr_ring_x_lo+1
    lda #$34
    sta ptr_ring_x_hi+1
    lda #$38
    sta ptr_ring_y+1
    lda #$00
    sta ptr_ring_x_lo
    sta ptr_ring_x_hi
    sta ptr_ring_y
    sta ring_idx
    sta phase_x_lo
    sta phase_x_hi
    sta phase_y_lo
    sta phase_y_hi
    sta phase_m_lo
    sta phase_m_hi

    ; Initial mode targets
    lda #0
    sta curr_mode
    jsr set_mode_targets
    
    ; Force immediate morph for first frame
    lda t_freq_x
    sta freq_x
    lda t_amp_x
    sta amp_x
    lda t_amp_y
    sta amp_y

; ==============================================================================
; MAIN DEMO LOOP
; ==============================================================================
main_loop:
    ldx #128         ; Process 128 points per visual frame update
.point_loop:
    stx pts_counter

    ; --- 1. ERASE OLD POINT ---
    ldy ring_idx
    lda (ptr_ring_x_lo), y
    sta old_x_lo
    lda (ptr_ring_x_hi), y
    sta old_x_hi
    lda (ptr_ring_y), y
    sta old_y
    jsr erase_point

    ; --- 2. CALCULATE NEW POINT ---
    jsr calc_point

    ; --- 3. PLOT NEW POINT ---
    jsr plot_point

    ; --- 4. SAVE TO RING BUFFER ---
    ldy ring_idx
    lda new_x_lo
    sta (ptr_ring_x_lo), y
    lda new_x_hi
    sta (ptr_ring_x_hi), y
    lda new_y
    sta (ptr_ring_y), y

    ; --- 5. ADVANCE PHASES ---
    lda phase_x_lo
    clc
    adc freq_x
    sta phase_x_lo
    bcc +
    inc phase_x_hi
+   lda phase_y_lo
    clc
    adc freq_y
    sta phase_y_lo
    bcc +
    inc phase_y_hi
+   lda phase_m_lo
    clc
    adc freq_m
    sta phase_m_lo
    bcc +
    inc phase_m_hi
+

    ; --- 6. ADVANCE RING INDEX ---
    iny
    sty ring_idx
    bne .skip_wrap
    inc ptr_ring_x_lo+1
    inc ptr_ring_x_hi+1
    inc ptr_ring_y+1
    lda ptr_ring_y+1
    cmp #$3C
    bne .skip_wrap
    lda #$30
    sta ptr_ring_x_lo+1
    lda #$34
    sta ptr_ring_x_hi+1
    lda #$38
    sta ptr_ring_y+1
.skip_wrap:

    ldx pts_counter
    dex
    bne .point_loop

    ; --- UPDATE ANIMATION / MORPH ---
    jsr morph_parameters

    dec mode_timer
    bne +
    jsr next_mode
+
    ; --- KEYBOARD SCAN ---
    lda #$7F
    sta $DC00
    lda $DC01
    and #$10        ; SPACE BAR
    bne +
    jsr next_mode
+   
    jmp main_loop

; ==============================================================================
; CALCULATE POINT
; X = 160 + (sin(px)*amp_x) + (sin(pm)*amp_m)
; Y = 100 + (sin(py)*amp_y) + (sin(pm+64)*amp_m)
; ==============================================================================
calc_point:
    ; -- Compute X --
    ldx phase_x_hi
    ldy amp_x
    jsr get_scaled_sin
    tax
    bmi .negx1
    lda #0
    sta zp_t2
    jmp .addx1
.negx1:
    lda #$FF
    sta zp_t2
.addx1:
    txa
    clc
    adc #160        ; center X
    sta new_x_lo
    lda zp_t2
    adc #0
    sta new_x_hi

    ; add X modulation
    ldx phase_m_hi
    ldy amp_m
    jsr get_scaled_sin
    tax
    bmi .negx2
    lda #0
    sta zp_t2
    jmp .addx2
.negx2:
    lda #$FF
    sta zp_t2
.addx2:
    txa
    clc
    adc new_x_lo
    sta new_x_lo
    lda zp_t2
    adc new_x_hi
    sta new_x_hi

    ; -- Compute Y --
    ldx phase_y_hi
    ldy amp_y
    jsr get_scaled_sin
    clc
    adc #100        ; center Y
    sta new_y

    ; add Y modulation (phase + 64 to create circular twisting)
    lda phase_m_hi
    clc
    adc #64
    tax
    ldy amp_m
    jsr get_scaled_sin
    clc
    adc new_y
    sta new_y
    rts

; ==============================================================================
; PLOT POINT (X: new_x_lo/hi, Y: new_y)
; ==============================================================================
plot_point:
    ldx new_y
    cpx #200
    bcs .end_plot
    lda row_addr_lo, x
    sta zp_scr_lo
    lda row_addr_hi, x
    clc
    adc new_x_hi    ; High byte addition effectively adds 256
    sta zp_scr_hi
    
    lda new_x_lo
    and #$F8
    clc
    adc zp_scr_lo
    sta zp_scr_lo
    bcc +
    inc zp_scr_hi
+   
    lda new_x_lo
    and #7
    tax
    lda bit_mask, x
    ldy #0
    ora (zp_scr_lo), y
    sta (zp_scr_lo), y
.end_plot:
    rts

; ==============================================================================
; ERASE POINT (X: old_x_lo/hi, Y: old_y)
; ==============================================================================
erase_point:
    ldx old_y
    cpx #200
    bcs .end_erase
    lda row_addr_lo, x
    sta zp_scr_lo
    lda row_addr_hi, x
    clc
    adc old_x_hi
    sta zp_scr_hi
    
    lda old_x_lo
    and #$F8
    clc
    adc zp_scr_lo
    sta zp_scr_lo
    bcc +
    inc zp_scr_hi
+   
    lda old_x_lo
    and #7
    tax
    lda bit_inv, x
    ldy #0
    and (zp_scr_lo), y
    sta (zp_scr_lo), y
.end_erase:
    rts

; ==============================================================================
; GET SCALED SINE (A = sin[X] * Y)
; Uses quarter square multiplication. Returns signed 8-bit result in A.
; ==============================================================================
get_scaled_sin:
    lda sin_table, x
    sta zp_mul_a
    sty zp_mul_b
    clc
    adc zp_mul_b
    sta sum_lo
    lda #0
    adc #0
    sta sum_hi

    sec
    lda zp_mul_a
    sbc zp_mul_b
    bcs .mul_pos
    eor #$FF
    adc #1
.mul_pos:
    tax
    ldy sum_hi
    beq .sum_sm
    ldy sum_lo
    lda sqr_hi_1, y
    sec
    sbc sqr_hi_0, x
    jmp .mul_done
.sum_sm:
    ldy sum_lo
    lda sqr_hi_0, y
    sec
    sbc sqr_hi_0, x
.mul_done:
    lsr zp_mul_b
    sec
    sbc zp_mul_b
    rts

; ==============================================================================
; MORPH PARAMETERS
; ==============================================================================
!macro morph .var, .target {
    lda .var
    cmp .target
    beq +
    bcc ++
    dec .var
    jmp +
++  inc .var
+
}

morph_parameters:
    +morph freq_x, t_freq_x
    +morph freq_y, t_freq_y
    +morph freq_m, t_freq_m
    +morph amp_x,  t_amp_x
    +morph amp_y,  t_amp_y
    +morph amp_m,  t_amp_m
    rts

next_mode:
    lda #200
    sta mode_timer
    inc curr_mode
    lda curr_mode
    cmp #6
    bcc +
    lda #0
    sta curr_mode
+   
set_mode_targets:
    lda curr_mode
    asl
    asl
    tax
    ; wait, each mode has 6 bytes. We can just do A * 6
    lda curr_mode
    sta zp_t1
    asl
    clc
    adc zp_t1
    asl
    tax
    lda mode_data, x
    sta t_freq_x
    lda mode_data+1, x
    sta t_freq_y
    lda mode_data+2, x
    sta t_amp_x
    lda mode_data+3, x
    sta t_amp_y
    lda mode_data+4, x
    sta t_freq_m
    lda mode_data+5, x
    sta t_amp_m
    rts

; FX, FY, AX, AY, FM, AM
mode_data:
!byte 3, 4, 130, 95, 0, 0     ; 0: Classic Lissajous
!byte 2, 2, 90,  90, 7, 50    ; 1: Knots
!byte 5, 6, 120, 80, 11,55    ; 2: Flower
!byte 1, 1, 15,  15, 0, 0     ; 3: Collapse (Shrinks to center)
!byte 13,17,140, 90, 23,80    ; 4: Overload (Chaotic Alien Pattern)
!byte 7, 9, 120, 90, 5, 30    ; 5: Slow Drift

; ==============================================================================
; STARTUP TABLE BUILDERS
; ==============================================================================
build_row_table:
    ldx #0
.yloop:
    txa
    pha
    lsr
    lsr
    lsr
    sta zp_t1
    lsr
    lsr
    adc zp_t1
    adc #$60
    sta zp_scr_hi
    lda zp_t1
    asl
    asl
    asl
    asl
    asl
    asl
    sta zp_scr_lo
    pla
    pha
    and #7
    clc
    adc zp_scr_lo
    sta row_addr_lo, x
    lda zp_scr_hi
    adc #0
    sta row_addr_hi, x
    pla
    tax
    inx
    cpx #200
    bne .yloop
    rts

build_sqr_table:
    lda #0
    sta zp_acc_0
    sta zp_acc_1
    sta zp_acc_2
    sta zp_i_lo
    sta zp_i_hi
    ldy #0
.sq_loop:
    lda zp_i_hi
    beq .p0
    lda zp_acc_2
    sta sqr_hi_1, y
    jmp .dst
.p0:
    lda zp_acc_2
    sta sqr_hi_0, y
.dst:
    lda zp_i_lo
    asl
    sta zp_add_0
    lda zp_i_hi
    rol
    sta zp_add_1
    inc zp_add_0
    bne +
    inc zp_add_1
+   lda zp_acc_0
    clc
    adc zp_add_0
    sta zp_acc_0
    lda zp_acc_1
    adc zp_add_1
    sta zp_acc_1
    lda zp_acc_2
    adc #0
    sta zp_acc_2
    inc zp_i_lo
    bne +
    inc zp_i_hi
+   iny
    cpy #0
    bne .sq_loop
    lda zp_i_hi
    cmp #2
    bne .sq_loop
    rts

build_bitmasks:
    ldx #0
    lda #$80
.bm:
    sta bit_mask, x
    pha
    eor #$FF
    sta bit_inv, x
    pla
    lsr
    inx
    cpx #8
    bne .bm
    rts

; ==============================================================================
; SINE TABLE (0 to 254)
; ==============================================================================
* = $2000
sin_table:
!byte 127,130,133,136,139,142,145,148,152,155,158,161,164,167,170,173
!byte 176,179,182,185,188,190,193,196,199,201,204,206,209,211,214,216
!byte 218,221,223,225,227,229,231,233,235,237,238,240,242,243,245,246
!byte 247,248,250,251,251,252,253,253,254,254,254,254,254,254,254,254
!byte 254,254,254,254,254,254,254,254,254,253,253,252,251,251,250,248
!byte 247,246,245,243,242,240,238,237,235,233,231,229,227,225,223,221
!byte 218,216,214,211,209,206,204,201,199,196,193,190,188,185,182,179
!byte 176,173,170,167,164,161,158,155,152,148,145,142,139,136,133,130
!byte 127,124,121,118,115,112,109,106,102,99,96,93,90,87,84,81
!byte 78,75,72,69,66,64,61,58,55,53,50,48,45,43,40,38
!byte 36,33,31,29,27,25,23,21,19,17,16,14,12,11,9,8
!byte 7,6,4,3,3,2,1,1,0,0,0,0,0,0,0,0
!byte 0,0,0,0,0,0,0,0,0,1,1,2,3,3,4,6
!byte 7,8,9,11,12,14,16,17,19,21,23,25,27,29,31,33
!byte 36,38,40,43,45,48,50,53,55,58,61,64,66,69,72,75
!byte 78,81,84,87,90,93,96,99,102,106,109,112,115,118,121,124
SIGNAL ACTIVE   //   WISCO 608   //   Gateway locked to gopher.nitetime.net