Initial commit
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| samples/ | ||||
							
								
								
									
										44
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								README.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										122
									
								
								extract-fonts.py
									
									
									
									
									
										Normal 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})) | ||||
							
								
								
									
										
											BIN
										
									
								
								firmware/mobitec-01682-r7-bib-01369-r9.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								firmware/mobitec-01682-r7-bib-01369-r9.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								firmware/mobitec-1682-r7-bib-01979-r3.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								firmware/mobitec-1682-r7-bib-01979-r3.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user