64kramsystem / 64kramsystem/ghidra-vice-connector
Character set and bitmap screen viewer
- Lingua principale
- Python
- Stelle
- 1
- Fork
- 0
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
## Summary
Decode and render the active character set or bitmap graphics from live VICE memory, saving to PNG. Most C64 games use custom character sets for maps and HUDs, or bitmap screens for title graphics. Visualising which memory range holds the active graphics data is essential for identifying and classifying those regions during disassembly.
Source: RetroDebugger (Charset View, Bitmap View).
---
## How the C64 graphics system works
Everything is controlled by VIC-II register `$D018` (`VMCSB`) and `$D011` (`SCROLY`):
### Character mode (default, most games)
```
char_base = vic_bank + ((VMCSB >> 1) & 0x07) * $0800 — 2KB charset
screen_base = vic_bank + ((VMCSB >> 4) & 0x0F) * $0400 — 1KB screen RAM
```
- Charset: 256 characters × 8 bytes = 2KB. Each byte = one row of 8 pixels.
- Screen RAM: 1000 bytes (40×25), each byte = character index.
- Color RAM: always at `$D800`–`$DBE7`, 1000 bytes, low nibble = foreground color.
- **ECM** (bit 6 of $D011): extended background color mode — only 64 chars, 4 bg colors.
- **MCM** (bit 4 of $D016): multicolor char mode — 4×8 pixels per char, 4 colors.
### Bitmap mode (bit 5 of $D011 set)
```
bitmap_base = vic_bank + ((VMCSB >> 3) & 0x01) * $2000 — 8KB bitmap
screen_base = vic_bank + ((VMCSB >> 4) & 0x0F) * $0400 — 1KB color data
```
- Bitmap: 8000 bytes (320×200 pixels, 8 pixels per byte).
- Color data: 1000 bytes; high nibble = color for `1` bits, low nibble = color for `0` bits per 8×8 cell.
- **Multicolor bitmap** (bit 4 of $D016): 160×200, 2 bits per pixel, 4 colors per cell.
---
## Implementation plan
### 1. `methods.py` — two new methods
```python
@REGISTRY.method(display='Save Charset View')
def save_charset(process: C64):
"""Render the active 256-character charset as a 16×16 grid PNG."""
vice = commands.STATE.require_vice()
io_bank = commands._get_io_bank(vice)
vic = vice.memory_get(0xD000, 0xD018, bank_id=io_bank, side_effects=False)
vmcsb = vic[0xD018 - 0xD000]
d016 = vic[0xD016 - 0xD000]
cia2 = vice.memory_get(0xDD00, 0xDD00, bank_id=io_bank, side_effects=False)
vic_bank = (3 - (cia2[0] & 0x03)) * 0x4000
char_base = vic_bank + ((vmcsb >> 1) & 0x07) * 0x0800
multicolor = bool(d016 & 0x10)
charset = vice.memory_get(char_base, char_base + 0x07FF)
log.info(f'Charset at 0x{char_base:04X} multicolor={multicolor}')
commands._render_charset(charset, multicolor, '/tmp/vice_charset.png')
log.info('Charset saved to /tmp/vice_charset.png')
@REGISTRY.method(display='Save Bitmap Screen')
def save_bitmap_screen(process: C64):
"""Render the active bitmap screen (320×200 hires or 160×200 multicolor) to PNG."""
vice = commands.STATE.require_vice()
io_bank = commands._get_io_bank(vice)
vic = vice.memory_get(0xD000, 0xD021, bank_id=io_bank, side_effects=False)
vmcsb = vic[0xD018 - 0xD000]
d011 = vic[0xD011 - 0xD000]
d016 = vic[0xD016 - 0xD000]
bg_color = vic[0xD021 - 0xD000] & 0x0F
if not (d011 & 0x20):
log.warning('VIC-II is not in bitmap mode ($D011 bit 5 = 0)')
return
cia2 = vice.memory_get(0xDD00, 0xDD00, bank_id=io_bank, side_effects=False)
vic_bank = (3 - (cia2[0] & 0x03)) * 0x4000
bitmap_base = vic_bank + ((vmcsb >> 3) & 0x01) * 0x2000
screen_base = vic_bank + ((vmcsb >> 4) & 0x0F) * 0x0400
multicolor = bool(d016 & 0x10)
bitmap = vice.memory_get(bitmap_base, bitmap_base + 0x1F3F) # 7999 bytes
colors = vice.memory_get(screen_base, screen_base + 0x03E7) # 1000 bytes
color_ram = vice.memory_get(0xD800, 0xDBE7) # 1000 bytes
log.info(f'Bitmap at 0x{bitmap_base:04X} colors at 0x{screen_base:04X} MC={multicolor}')
commands._render_bitmap(bitmap, colors, color_ram, bg_color, multicolor,
'/tmp/vice_bitmap.png')
log.info('Bitmap saved to /tmp/vice_bitmap.png')
```
### 2. `commands.py` — rendering helpers
```python
def _render_charset(charset: bytes, multicolor: bool, path: str, scale: int = 2):
# 16×16 grid of characters, each 8×8 (or 4×8 MC) pixels
from . import arch
COLS = C64_PALETTE # from methods.py or arch.py
W = 16 * 8 * scale
H = 16 * 8 * scale
pixels = bytearray(W * H * 3)
for ch in range(256):
cx = (ch % 16) * 8 * scale
cy = (ch // 16) * 8 * scale
for row in range(8):
byte = charset[ch * 8 + row]
for col in range(8):
bit = (byte >> (7 - col)) & 1
color = COLS[1] if bit else COLS[0] # white on black
for dy in range(scale):
for dx in range(scale):
px = cx + col * scale + dx
py = cy + row * scale + dy
pixels[(py * W + px) * 3:(py * W + px) * 3 + 3] = color
_write_png(path, W, H, bytes(pixels))
def _render_bitmap(bitmap, colors, color_ram, bg_color, multicolor, path, scale=2):
W = (160 if multicolor else 320) * scale
H = 200 * scale
pixels = bytearray(W * H * 3)
for cell_y in range(25):
for cell_x in range(40):
cell = cell_y * 40 + cell_x
col_byte = colors[cell]
cram_byte = color_ram[cell] & 0x0F
hi = (col_byte >> 4) & 0x0F
lo = col_byte & 0x0F
for row in range(8):
byte = bitmap[cell * 8 + row] if not multicolor else 0
# hires: bit=1 → hi color, bit=0 → lo color
# MC: 2-bit pairs → 00=bg, 01=hi, 10=lo, 11=cram
for col in range(8 if not multicolor else 4):
if multicolor:
pair = (bitmap[cell * 8 + row] >> (6 - col*2)) & 0x03
color = [C64_PALETTE[bg_color], C64_PALETTE[hi],
C64_PALETTE[lo], C64_PALETTE[cram_byte]][pair]
else:
bit = (byte >> (7 - col)) & 1
color = C64_PALETTE[hi if bit else lo]
for dy in range(scale):
for dx in range(scale * (2 if multicolor else 1)):
px = (cell_x * (4 if multicolor else 8) + col) * scale * (2 if multicolor else 1) + dx
py = (cell_y * 8 + row) * scale + dy
pixels[(py * W + px) * 3:(py * W + px) * 3 + 3] = color
_write_png(path, W, H, bytes(pixels))
```
---
## Shared infrastructure with other issues
- `_get_io_bank()` — shared with issues #21 (chip registers) and #31 (sprites)
- `_write_png()` — shared with issues #23 (screen capture) and #31 (sprites)
- `C64_PALETTE` — shared with issue #31 (sprites)
All three helpers should live in `commands.py` or a new `render.py` utility module.
---
## Files to change
- `src/main/py/src/vice/methods.py` — add `save_charset`, `save_bitmap_screen`
- `src/main/py/src/vice/commands.py` — add `_render_charset()`, `_render_bitmap()`, and shared helpers
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.