Skip to content

Instantly share code, notes, and snippets.

@gschizas
Created March 12, 2021 01:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gschizas/aa0bd3fe23e04e11ce397d029671d72c to your computer and use it in GitHub Desktop.
Save gschizas/aa0bd3fe23e04e11ce397d029671d72c to your computer and use it in GitHub Desktop.
Convert Amstrad CPC Video Memory to an PIL Image

Some assumptions:

  • The file being read has video memory stored in the first 0x4000 (16384) bytes.
  • The screen hasn't been scrolled
  • Screen mode and border and palette inks follow the main data:
    • MODE is a single byte in position 0x4000
    • BORDER (unused) is in position 0x4001
    • INKS 0-15 follow in positions 0x4002-0x4011
#!/usr/bin/env python
# coding: utf-8
import struct
import sys
import PIL
import PIL.Image
palette_software = [
0x000000,0x000080,0x0000FF, 0x800000,0x800080,0x8000FF, 0xFF0000,0xFF0080,0xFF00FF,
0x008000,0x008080,0x0080FF, 0x808000,0x808080,0x8080FF, 0xFF8000,0xFF8080,0xFF80FF,
0x00FF00,0x00FF80,0x00FFFF, 0x80FF00,0x80FF80,0x80FFFF, 0xFFFF00,0xFFFF80,0xFFFFFF]
palette_hardware = [20, 4, 21, 28, 24, 29, 12, 5, 13, 22, 6, 23, 30, 0, 31, 14, 7, 15, 18, 2, 19, 26, 25, 27, 10, 3, 11]
standard_palette = b'\x04\x04\n\x13\x0c\x0b\x14\x15\r\x06\x1e\x1f\x07\x12\x19\x04\x17\x04\x04\n\x13\x0c\x0b\x14\x15\r\x06\x1e\x1f\x07\x12\x19\n\x07'
def read_inks(memory_data):
inks = []
for ink in memory_data[0x4002:0x4012]:
assert ink in palette_hardware
hw_index = palette_hardware.index(ink)
sw_color = palette_software[hw_index]
inks.append(sw_color)
return inks
def convert_image_from_memory(memory_data, mode=None):
image = PIL.Image.new('RGB', (640, 400), color=0xff0080)
if mode is None:
mode = memory_data[0x4000]
inks = read_inks(memory_data)
scale_x = 4 >> mode
pixels_per_byte = 2 << mode
scale_y = 2
for screen_y in range(200):
screen_row = screen_y % 8
screen_line = screen_y // 8
for screen_x in range(640 // scale_x):
screen_col = screen_x // pixels_per_byte
screen_pixel = screen_x % pixels_per_byte
memory_pos = screen_row * 0x800 + screen_line * 80 + screen_col
memory_value = memory_data[memory_pos]
if mode == 2:
current_ink = (memory_value & (128 >> screen_pixel)) >> (7 - screen_pixel)
elif mode == 1:
current_ink0 = (memory_value >> 0 & 15 & (8 >> screen_pixel)) >> (3 - screen_pixel)
current_ink1 = (memory_value >> 4 & 15 & (8 >> screen_pixel)) >> (3 - screen_pixel)
current_ink = (current_ink0 << 1) + current_ink1
elif mode == 0:
current_ink0 = (memory_value >> 0 & 15 & (2 >> screen_pixel)) >> (1 - screen_pixel)
current_ink1 = (memory_value >> 2 & 15 & (2 >> screen_pixel)) >> (1 - screen_pixel)
current_ink2 = (memory_value >> 4 & 15 & (2 >> screen_pixel)) >> (1 - screen_pixel)
current_ink3 = (memory_value >> 6 & 15 & (2 >> screen_pixel)) >> (1 - screen_pixel)
current_ink = (current_ink0 << 3) + (current_ink1 << 2) + (current_ink2 << 1) + current_ink3
try:
pixel_value = inks[current_ink]
except:
print(current_ink, file=sys.stderr)
raise
for pixel_y in range(scale_y):
for pixel_x in range(scale_x):
color = struct.unpack('>I', struct.pack('<I', pixel_value))[0] >> 8
image.putpixel((screen_x * scale_x + pixel_x, screen_y * scale_y + pixel_y), color)
#break
return image
with open(r"SCREEN.BIN", 'rb') as f:
memory_data = f.read()
image = convert_image_from_memory(memory_data)
image.save('Screen.png')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment