NITETIME GOPHER 1.0 - Lake Koshkonong Node
Server: gopher.nitetime.net:70    Selector: /commodore/memviz.asm
; ==============================================================================
; C128 REAL-TIME MEMORY MAP VISUALIZER
; Architecture: 8502 Native Mode
; Assembler: ACME
; ==============================================================================

!cpu 6510

; --- ZP Variables ---
ScanPtr       = $02     ; 16-bit pointer for memory scanning
ScanPage      = $04     ; Current page being scanned (0 to 191)
DecayPtr      = $05     ; 16-bit pointer for the phosphor decay loop
TestActive    = $07     ; Flag: 1 = Run built-in test workload
PlotPtr       = $08     ; 16-bit pointer used by pixel plotting
tmp           = $0A
tmp2          = $0B
FrameCount    = $0C

; --- C128 Hardware Registers ---
MMU_CR        = $FF00   ; MMU Configuration Register
VIC_SCN       = $4000   ; Screen RAM base
VIC_BMP       = $6000   ; Bitmap base
COL_RAM       = $D800   ; Color RAM base

; ==============================================================================
; BASIC LAUNCHER (SYS 7181)
; ==============================================================================
* = $1C01
        !word .nextLine
        !word 10
        !byte $9E
        !text "7181"
        !byte 0
.nextLine:
        !word 0

; ==============================================================================
; PROGRAM ENTRY POINT ($1C0D = 7181)
; ==============================================================================
* = $1C0D

Main:
        SEI
        LDA #$0E        ; Set MMU to standard C128 runtime (RAM 0 + I/O + ROMs)
        STA MMU_CR

        ; Initialize shared-RAM stub for Bank 1 access
        JSR InitBank1Stub

        ; Generate fast math lookup tables
        JSR BuildLookupTables

        ; Initialize Display (VIC-II Bank 1, Multi-color Bitmap)
        JSR InitDisplay

        ; Create initial memory shadow snapshot
        JSR InitShadowBuffer

        CLI

        ; Default state
        LDA #0
        STA ScanPage
        STA FrameCount
        STA TestActive

        LDA #<VIC_BMP
        STA DecayPtr
        LDA #>VIC_BMP
        STA DecayPtr+1

; ------------------------------------------------------------------------------
; MAIN REAL-TIME LOOP
; ------------------------------------------------------------------------------
MainLoop:
        ; Wait for VBLANK to pace the loop
        LDA $D011
        BPL MainLoop
.waitVblank:
        LDA $D011
        BMI .waitVblank

        INC FrameCount

        ; 1. Execute Test Workload (if enabled)
        LDA TestActive
        BEQ +
        JSR RunTestWorkload
+
        ; 2. Scan memory incrementally (8 pages per frame = 2KB)
        LDX #8
.scanLoop:
        JSR ScanOnePage
        INC ScanPage
        
        ; Prevent scanning VIC-II Video Memory ($40 to $7F)
        LDA ScanPage
        CMP #$40
        BNE +
        LDA #$80        ; Skip ahead to $8000
        STA ScanPage
+       
        ; Wrap at $C0 (Limit scan to 48K: $0000 to $BFFF)
        LDA ScanPage
        CMP #$C0
        BNE +
        LDA #0
        STA ScanPage
+
        DEX
        BNE .scanLoop

        ; 3. Run incremental Phosphor Decay (768 bytes per frame)
        JSR DecayScreen

        ; 4. Check Keyboard Controls
        JSR HandleKeyboard

        JMP MainLoop

; ==============================================================================
; SCANNER SUBSYSTEM
; ==============================================================================
ScanOnePage:
        LDA #0
        STA ScanPtr
        LDA ScanPage
        STA ScanPtr+1
        LDY #0

.byteLoop:
        ; Read byte from live RAM (Bank 0)
        LDA (ScanPtr), Y
        TAX

        ; Read byte from shadow buffer (Bank 1)
        JSR PeekBank1
        
        ; Compare them
        STX tmp
        CMP tmp
        BEQ .noChange

        ; Change detected! Update shadow buffer.
        LDA tmp
        JSR PokeBank1

        ; Plot the change onto the Heatmap
        JSR PlotPixel

.noChange:
        INY
        BNE .byteLoop
        RTS

; ------------------------------------------------------------------------------
; SHARED RAM BANK 1 ACCESS STUB (Lives in $0340 - safe from bank switching)
; ------------------------------------------------------------------------------
InitBank1Stub:
        LDX #0
-       LDA Bank1Routine, X
        STA $0340, X
        INX
        CPX #(Bank1RoutineEnd - Bank1Routine)
        BNE -
        RTS

Bank1Routine:
        ; PeekBank1 (Entry $0340)
        LDA #$4E        ; Switch to RAM Bank 1 + ROMs
        STA MMU_CR
        LDA (ScanPtr), Y
        PHA
        LDA #$0E        ; Restore RAM Bank 0 + ROMs
        STA MMU_CR
        PLA
        RTS

        ; PokeBank1 (Entry $034E)
        PHA
        LDA #$4E
        STA MMU_CR
        PLA
        STA (ScanPtr), Y
        LDA #$0E
        STA MMU_CR
        RTS
Bank1RoutineEnd:

PeekBank1 = $0340
PokeBank1 = $034E

; ==============================================================================
; VISUALIZATION PLOTTER
; Maps Address (ScanPtr+1, Y) to X/Y Grid
; ==============================================================================
PlotPixel:
        TYA
        PHA
        TXA
        PHA

        ; X Coordinate = Low Byte (Y register) / 2
        TYA
        LSR
        TAX

        ; Y Coordinate = High Byte (ScanPage)
        LDY ScanPage

        ; Calculate Bitmap Address: Base(Y) + X_Offset(X)
        LDA Y_Table_Lo, Y
        CLC
        ADC X_Table, X
        STA PlotPtr
        LDA Y_Table_Hi, Y
        ADC #0
        STA PlotPtr+1

        ; Read existing bitmap byte, apply maximum heat mask, and write back
        LDY #0
        LDA (PlotPtr), Y
        ORA Heat_Mask_Table, X
        STA (PlotPtr), Y

        PLA
        TAX
        PLA
        TAY
        RTS

; ==============================================================================
; INCREMENTAL PHOSPHOR DECAY
; Decays 768 bytes of the bitmap per frame
; ==============================================================================
DecayScreen:
        LDX #3          ; 3 pages = 768 bytes
        LDY #0
.dloop:
        LDA (DecayPtr), Y
        ; Fast lookup to decrease pixel brightness
        TAX
        LDA Decay_Table, X
        STA (DecayPtr), Y

        INY
        BNE .dloop

        INC DecayPtr+1
        
        ; Wrap if we hit the end of the bitmap ($8000)
        LDA DecayPtr+1
        CMP #$80
        BNE +
        LDA #>VIC_BMP
        STA DecayPtr+1
+       
        DEX
        BNE .dloop
        RTS

; ==============================================================================
; KEYBOARD HANDLER
; ==============================================================================
HandleKeyboard:
        JSR $FFE4       ; Kernal GETIN
        BEQ .done
        
        CMP #'t'
        BNE +
        LDA TestActive
        EOR #1
        STA TestActive
        RTS
+       
        CMP #'r'
        BNE +
        JSR ClearBitmap
        RTS
+
        CMP #' '
        BNE +
.pauseWait:
        JSR $FFE4
        CMP #' '
        BNE .pauseWait
+
.done:  RTS

; ==============================================================================
; BUILT-IN TEST WORKLOAD
; Performs real memory manipulation at $8000-$80FF
; ==============================================================================
RunTestWorkload:
        ; Increment a byte to create a pulse
        INC $8000
        
        ; Shift a pattern down memory (simulating a block move / data stream)
        LDX #254
-       LDA $8000, X
        STA $8001, X
        DEX
        BNE -
        RTS

; ==============================================================================
; HARDWARE INITIALIZATION
; ==============================================================================
InitDisplay:
        ; Set VIC-II to Bank 1 ($4000-$7FFF)
        LDA $DD00
        AND #$FC
        ORA #$02
        STA $DD00

        ; Set Screen RAM to $4000, Bitmap to $6000
        LDA #$08
        STA $D018

        ; Enable Multi-Color Mode (Bit 4)
        LDA $D016
        ORA #$10
        STA $D016

        ; Enable Bitmap Mode (Bit 5)
        LDA $D011
        ORA #$20
        STA $D011

        ; Setup Palette
        LDA #$00
        STA $D020       ; Border Black
        STA $D021       ; Background Black

        ; Fill Screen Matrix with Multi-color attributes
        ; $BC = Dark Gray (Color 1), Light Gray (Color 2)
        LDA #$BC
        LDX #4
        LDA #<VIC_SCN
        STA tmp
        LDA #>VIC_SCN
        STA tmp+1
        LDY #0
        LDA #$BC
-       STA (tmp), Y
        INY
        BNE -
        INC tmp+1
        DEX
        BNE -

        ; Fill Color RAM with White (Color 3)
        LDX #4
        LDA #<COL_RAM
        STA tmp
        LDA #>COL_RAM
        STA tmp+1
        LDY #0
        LDA #$01
-       STA (tmp), Y
        INY
        BNE -
        INC tmp+1
        DEX
        BNE -

ClearBitmap:
        LDX #32         ; 8000 bytes
        LDA #<VIC_BMP
        STA tmp
        LDA #>VIC_BMP
        STA tmp+1
        LDY #0
        TYA
-       STA (tmp), Y
        INY
        BNE -
        INC tmp+1
        DEX
        BNE -
        RTS

InitShadowBuffer:
        ; Synchronizes Bank 1 to Bank 0 safely to avoid a massive startup flash
        LDA #0
        STA ScanPtr
        STA ScanPtr+1
.ishadow:
        ; Skip video memory
        LDA ScanPtr+1
        CMP #$40
        BNE +
        LDA #$80
        STA ScanPtr+1
+
        CMP #$C0
        BEQ .sdone

        LDY #0
-       LDA (ScanPtr), Y
        JSR PokeBank1
        INY
        BNE -
        
        INC ScanPtr+1
        JMP .ishadow
.sdone: RTS

; ==============================================================================
; LOOKUP TABLE GENERATORS
; ==============================================================================
BuildLookupTables:
        ; 1. Build Y Row Lookup (192 rows)
        ; Base starts at $6004 (Centered X axis by 16 pixels / 4 bytes)
        LDA #<$6004
        STA tmp
        LDA #>$6004
        STA tmp+1
        LDX #0
        LDY #0
.bYloop:
        LDA tmp
        STA Y_Table_Lo, X
        LDA tmp+1
        STA Y_Table_Hi, X
        INX
        CPX #192
        BEQ .bYdone
        INY
        CPY #8
        BNE .bYadd1
        LDY #0
        LDA tmp
        CLC
        ADC #$38        ; Move down a character row (320 - 8 = 312 bytes = $138)
        STA tmp
        LDA tmp+1
        ADC #$01
        STA tmp+1
        JMP .bYloop
.bYadd1:
        INC tmp
        BNE .bYloop
        INC tmp+1
        JMP .bYloop
.bYdone:

        ; 2. Build X Offset Table (128 values)
        LDX #0
.bXloop:
        TXA
        LSR             ; / 2
        LSR             ; / 4
        ASL             ; * 2
        ASL             ; * 4
        ASL             ; * 8  (Effectively (X/4) * 8)
        STA X_Table, X
        INX
        BPL .bXloop

        ; 3. Build Heat Mask Table (128 values)
        LDX #0
.bHloop:
        TXA
        AND #3
        CMP #0
        BEQ .p0
        CMP #1
        BEQ .p1
        CMP #2
        BEQ .p2
        LDA #$03
        BNE .hSt
.p0:    LDA #$C0
        BNE .hSt
.p1:    LDA #$30
        BNE .hSt
.p2:    LDA #$0C
.hSt:   STA Heat_Mask_Table, X
        INX
        BPL .bHloop

        ; 4. Build Fast Decay Table (256 values)
        ; Reduces every 2-bit multi-color pixel by 1 level if > 0
        LDX #0
.bDloop:
        TXA
        STA tmp
        LDA #0
        STA tmp2
        
        ; Pixel 3 (Bits 0-1)
        LDA tmp
        AND #$03
        BEQ +
        SEC
        SBC #$01
+       ORA tmp2
        STA tmp2
        
        ; Pixel 2 (Bits 2-3)
        LDA tmp
        AND #$0C
        BEQ +
        SEC
        SBC #$04
+       ORA tmp2
        STA tmp2
        
        ; Pixel 1 (Bits 4-5)
        LDA tmp
        AND #$30
        BEQ +
        SEC
        SBC #$10
+       ORA tmp2
        STA tmp2
        
        ; Pixel 0 (Bits 6-7)
        LDA tmp
        AND #$C0
        BEQ +
        SEC
        SBC #$40
+       ORA tmp2
        
        STA Decay_Table, X
        INX
        BNE .bDloop
        RTS

; ==============================================================================
; DATA STRUCTURES & TABLES ALIGNED IN RAM
; ==============================================================================
* = $2800

Y_Table_Lo:         !fill 192, 0
Y_Table_Hi:         !fill 192, 0
X_Table:            !fill 128, 0
Heat_Mask_Table:    !fill 128, 0

* = $2A00
Decay_Table:        !fill 256, 0
SIGNAL ACTIVE   //   WISCO 608   //   Gateway locked to gopher.nitetime.net