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