Initial commit

This commit is contained in:
Lauri Võsandi 2023-01-21 09:53:09 +02:00
commit 2d6428446e
5 changed files with 167 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
samples/

44
README.md Normal file
View File

@ -0,0 +1,44 @@
# Intro
Mobitec flip-disc displays use MC27C1001 EPROM-s to store the
firmware (?) and fonts.
In the `firmware/` directory you can find two ROM dumps.
# Useful commands
To extract font sections:
```
dd if=mobitec-1682-r7-bib-01979-r3.bin of=mobitec-1682-r7-bib-01979-r3-fonts.bin skip=64 bs=1024
dd if=mobitec-01682-r7-bib-01369-r9.bin of=mobitec-01682-r7-bib-01369-r9-fonts.bin skip=64 bs=1024
```
For diffing font sections:
```
dhex mobitec-1682-r7-bib-01979-r3-fonts.bin mobitec-01682-r7-bib-01369-r9-fonts.bin
```
# Dump structure
The firmware seems to be located in the first 7KiB and the font data
starts at 0x10000 offset.
The font beginning marker seem to consist of a character from `a` to `z`
followed by at least 60 null characters.
They don't seem aligned or indexed in any way so easiest is just to scan
for known markers.
After the beginning marker you can find the lookup table which maps
printable ASCII characters to a glyph address.
The lookup table is 190 bytes (95x 16-bit addresses) which correspond
to ASCII characters 32 up to 126.
Not all fonts include all characters, missing glyphs are represented by address 0.
Note that the glyph address is relative to the beginning of font structure,
beginning marker inclusive.
At the glyph address first width and height of the glyph are found represented
by two 8-bit integers. The actual bitmap data follows in 8x8 pixel tiles
packed together as 8 bytes.

122
extract-fonts.py Normal file
View File

@ -0,0 +1,122 @@
import json
import cv2
import numpy as np
"""
dd if=mobitec-1682-r7-bib-01979-r3.bin of=mobitec-1682-r7-bib-01979-r3-fonts.bin skip=64 bs=1024
dd if=mobitec-01682-r7-bib-01369-r9.bin of=mobitec-01682-r7-bib-01369-r9-fonts.bin skip=64 bs=1024
dhex mobitec-1682-r7-bib-01979-r3-fonts.bin mobitec-01682-r7-bib-01369-r9-fonts.bin
"""
src = "mobitec-1682-r7-bib-01979-r3"
src = "mobitec-01682-r7-bib-01369-r9"
with open("firmware/%s.bin" % src, "rb") as fh:
buf = fh.read()[65536:]
def scan_fonts(buf):
"""
Font start markers seem to be ascii character a..z followed by at least 60 nulls
"""
font_index = buf[0]
assert font_index == 0x61
prev = 0
done = False
while not done:
needle = b"%c" % (font_index + 1)
needle += 50 * b"\x00"
try:
font_offset = buf.index(needle)
except ValueError:
break
font_body = buf[prev:]
done = True
else:
font_body = buf[prev:font_offset]
if done:
break
font_marker_length = len(needle)
font_body = font_body[font_marker_length:]
while font_body.startswith(b"\x00"):
font_body = font_body[1:]
font_marker_length += 1
yield font_index, font_body, font_marker_length
prev = font_offset
font_index += 1
meta = []
for font_index, font_body, font_marker_length in scan_fonts(buf):
print()
print("Parsing font %02x" % font_index)
# Looks like yet another marker
assert font_body[0] == 3
assert font_body[1] == 1
# Look up table consists of 95 16-bit integers
glyph_offsets = np.frombuffer(font_body[2:192], dtype=np.uint16)
prev = 0
slices = []
errors = set()
for index, glyph_offset in enumerate(glyph_offsets):
if glyph_offset != 0:
glyph_offset -= font_marker_length
print("Found %d undecoded bytes between lookup table and first glyph" % len(font_body[192:192 + glyph_offset]))
break
characters_present = ""
for index, glyph_offset in enumerate(glyph_offsets):
if glyph_offset == 0:
characters_present += "_"
# No glyph
meta.append(None)
continue
else:
# The index goes from space (32) up to ~ (126)
character = chr(index + 32)
characters_present += "+"
# Glyphs are relative to the font marker start
glyph_offset -= font_marker_length
try:
height, width = font_body[glyph_offset], font_body[glyph_offset + 1]
except IndexError:
print("Failed to look up glyph for %s", repr(character))
else:
jah = np.frombuffer((font_body[glyph_offset + 2:])[::-1], dtype=np.uint8)[::-1]
a = np.unpackbits(jah, axis=0)
try:
img = a[:width * 16].reshape((width, 16, 1))
img = np.hstack([img[:, 8:, :], img[:, :8, :]])
meta.append({
"width": width,
"height": height,
"bitmap": img.tolist()
})
img = 255 * img
slices.append(img)
except ValueError:
pass
else:
blank_image = np.zeros((1, 16, 1), np.uint8)
blank_image[::] = 0
slices.append(blank_image)
if glyph_offset <= prev:
print("Glyph offsets not ordered")
prev = glyph_offset
print("Glyphs present:", characters_present)
img = np.vstack(slices)
img = np.flip(np.swapaxes(img, 1, 0), 0)
s = img.shape * np.array(5)
img = cv2.resize(img, dsize=(s[1], s[0]), interpolation=cv2.INTER_NEAREST)
cv2.imwrite("samples/%s-%02x.png" % (src, font_index), img)
with open("samples/%s-%02x.json" % (src, font_index), "w") as fh:
fh.write(json.dumps({"glyphs": meta}))

Binary file not shown.

Binary file not shown.