Displaying any image on an MSX, loading from a ROM

This article is basically just a note that I can come back to in case I forget some details.

The tool on this page (Japanese) takes an image file and adds dithering and stuff: https://nazo.main.jp/prog/retropc/gcmsx.html

The output is something that can be BLOAD’ed straight to VRAM memory using BLOAD’s S parameter. Didn’t even know that option existed!

This means we can easily convert this to a ROM by adding a loader that pokes everything into VRAM. We just need to get rid of the BSAVE header at the start (or ignore it in the loader), which looks like this according to http://www.faq.msxnet.org/suffix.html#BIN:

byte 0  : ID byte #FE
byte 1+2: start-address
byte 3+4: end-address
byte 5+6: execution-address

So we just cut off 8 bytes at the beginning, e.g. by doing:

tail -c +8 msx_20231111232339933.SC2 > foo.bin

And the loader in assembly (z80asm-flavor) could look like this:

SetVdpWrite: macro high low ; from http://map.grauw.nl/articles/vdp_tut.php
	ld a,low
	out (0x99),a
	ld a,high+0x40
	out (0x99),a
endm

vpoke: macro value
; 	ld a,value ; not needed in this implementation
	out (0x98),a
; 	nop ; nops not needed in this implementation
; 	nop
; 	nop
endm

	org 0x4000
	db "AB" ; magic number
	dw entry_point
	db 00,00,00,00,00,00,00,00,00,00,00,00 ; ignored

entry_point:
	ld a,2
	call 0x005f
	SetVdpWrite 00 00
	ld hl,data
loop:
	ld a,(hl)
	vpoke a
	inc hl
	ld a,h
	cp end>>8
	jr nz,loop
	ld a,l
	cp end&0xff
	jr nz,loop
inf:
	jr inf
data:
	incbin "foo.bin" ; read data from file foo.bin
end:
	ds 0x8000-$ ; fill remainder of 16 KB chunk with 0s

Of course, this approach is quite wasteful; we need almost 16 KB of memory to display any image, even if it’s mostly empty.

Converting paths to circles in Inkscape

Or alternatively: how to get svg2shenzhen to recognize your drill paths as drill holes

(My) rationale: there is an Inkscape extension called svg2shenzhen. This extension creates Gerber and KiCad files that can be used to create printed circuit boards, from standard SVG files. Also, this extension has a funny name. Older, DIY printed circuit boards are just a high-DPI bitmap. Using Inkscape and this extension, you can trace the bitmap and then convert it to Gerber. However, most PCBs (especially old PCBs) need to have holes drilled. The drill locates are just circles in the bitmap, and after tracing, they’re just paths. The svg2shenzhen extension (at the time of this writing) detects circles in a certain layer as locations that need to be drilled, but not paths.

I’m not an Inkscape expert, but AFAIK Inkscape (at the time of this writing) doesn’t have a built-in tool to convert (mostly) circular paths to circles. So I wrote a simple extension that does this! It works fine on Linux. Not so sure about Windows.

Extensions are made of only two files, a file that describes the extension, and the extension code (which is in Python in many cases). These two files just have to be placed into the location shown in Edit -> Preferences -> System -> User extensions, which in my case is ~/.config/inkscape/extensions/.

Here are the two files, you can copy them into a text editor like Kate or gedit or Notepad, what have you, and save them into the above directory. I recommend keeping my file names, path2circle.inx and path2circle.py. Note, some skeleton code in path2circle.py was generated by ChatGPT, though it was quite wrong. That’s where some of the verbose comments and extraneous code came from.

path2circle.inx:

<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension 
    xmlns="http://www.inkscape.org/namespace/inkscape/extension">
    <name>Path2Circle</name>
    <id>user.path2circle</id>
    <effect>
        <object-type>all</object-type>
        <effects-menu>
            <submenu name="Custom"/>
        </effects-menu>
    </effect>
    <script>
        <command location="inx" interpreter="python">path2circle.py</command>
    </script>
</inkscape-extension>

path2circle.py:

import inkex
from inkex import Circle

class Path2Circle(inkex.EffectExtension):
    def effect(self):
        # Iterate through all the selected objects in the SVG
        for node in self.svg.selection:
            # Check if the object is a path (or any other object type)
            if node.tag.endswith("path"):
                # Get the bounding box of the object
                x, y, width, height = self.get_object_dimensions(node)
                x = x.minimum
                y = y.minimum
                # with open('/path/to/debug/directory/debug_output.txt', 'a') as f:
                    # print(f"Object Dimensions: x={x}, y={y}, width={width}, height={height}", file=f)
                layer = self.svg.get_current_layer()
                diameter = min(width, height)
                layer.add(self.add_circle(x, y, diameter/2))

    def add_circle(self, x, y, radius):
        """Add a circle at the given location"""
        elem = Circle()
        elem.center = (x+radius, y+radius)
        elem.radius = radius
        return elem

    def get_object_dimensions(self, object_node):
        # Get the bounding box of the object
        bbox = object_node.bounding_box()

        # Extract the bounding box coordinates
        x = bbox.x
        y = bbox.y
        width = bbox.width
        height = bbox.height

        return x, y, width, height

if __name__ == '__main__':
    Path2Circle().run()

To use the extension, you probably first need to restart Inkscape. (You do not need to restart Inkscape after changing the extension, however.) Select all the paths you’d like to convert, and then hit Extensions -> Custom -> Path2Circle. Note: the extension doesn’t actually care if the paths even remotely look like circles, so make sure to select the correct paths. You can easily modify the extension to calculate the radius differently, or e.g. replace paths with other objects, such as squares, rectangle, or ellipses. Let me know if you need help doing that.