; ==============================================================================
; 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