Draw a door once and show it in several rooms. Change its glyph, and every cell using that code changes together. Change one cell’s code, and only that instance changes. These routines give you both operations without a whole renderer.
Include, initialise and call
Define G at 24 private RAM bytes and include glyphs.inc. Before glyph_init, configure Graphics I with the pattern table at $0000 and names at $1800. Set G+10 to the BIOS write data port from address 7 and G+11 to its control port. The BIOS read base at address 6 is used separately by the diagnostic readback. The original caller makes all of this visible.
Call glyph_init once to blank the owned glyph and name ranges. It destroys their previous contents. Then install the original sketches and select code 1 in two cells:
Install two pictures; display the first twice
; Install two original glyphs; two name entries share code 1.
ld hl,original_glyphs
ld (G),hl
ld a,1
ld (G+2),a
ld a,2
ld (G+3),a
call glyph_install
ld a,1
ld (G+4),a
ld hl,66
ld (G+5),hl
call glyph_select
ld hl,67
ld (G+5),hl
call glyph_select
xor a
ld (COMMAND),a
ld a,239
ld (COMMAND+1),aThe complete caller includes video setup and boot structure. Its mailbox is verification transport; your project calls these entries directly.
The calling contract
All three glyph backends use little-endian words in a 24-byte context at G.
| Offset | Supply or read |
|---|---|
| 0 | Word: readable source address |
| 2, 3 | First slot, nonzero count |
| 4 | Code to select in a cell |
| 5 | Word: linear cell index |
| 7 | Output: 1 accepted, 0 rejected |
| 8 | Word: destination bytes written |
glyph_install uploads a range; glyph_replace uploads one picture and ignores count. glyph_select writes one name entry. glyph_init blanks all owned patterns and cells. Inputs 0–6 survive. Counts reset on rejection. The source must be readable, long enough and non-overlapping with destination/context; it is a trusted pointer, not an arbitrary-memory validator.
Valid slots are 0–255; cell indices are 0–767. Counts run 1–255; two calls can upload all 256 slots. Zero counts, ranges past the final slot and invalid cells reject before writing. All byte-sized codes are valid.
Make the picture into bytes
Row 10000001 lights the outer two dots: bit weights 128+1 give byte 129. Sketch eight rows like that and you have an 8×8 picture. The source sketches are original Code198x assets under MIT, not ripped game art or a copied ROM font.
One byte stores each row. Row r in slot n is base + n*8 + r; slot 1 begins eight bytes after slot 0.
The Crates opening lesson builds larger artwork from four 8×8 sketches. Its method of turning dots into byte weights applies here too, even though the upload and colour rules differ.
Respect this machine’s display
Patterns occupy VRAM $0000–$07FF, names $1800–$1AFF. The CPU reaches them through VDP ports. Two control writes select an address before data bytes flow; an interrupt changing that latch would redirect the upload. Exclude or coordinate VDP-using interrupts for the whole call. Graphics I shares one foreground/background colour byte among eight patterns; the caller sets colours at $2000 separately. Init leaves colour and sprite tables alone. The OUT helper deliberately spaces accesses for a standard MSX1 clock; accelerated CPUs need their own qualification.
Replace a picture; select a different instance
After the example, point G+0 at a new sketch, set G+2=1 and call glyph_replace. Both cells still contain code 1, so both show the new picture. To change only the first, put code 2 in G+4 and its cell index in G+5, then call glyph_select. The second cell keeps code 1.
For a cell at column 2, row 2, the row-major index is 2*32+2 = 66. Local dot (3,5) in that cell appears at screen pixel (2*8+3, 2*8+5) = (19,21). Place a point explores origin and coordinates; here the top-left screen origin makes Y increase downwards. Grid placement does not imply arbitrary-position or scaled text support.

Open the complete reusable Z80 include
Upload, replace and select without a whole game renderer
; SPDX-License-Identifier: MIT
; MSX1 Graphics I: patterns $0000..07ff, name table $1800..1aff.
; G reserves 24 RAM bytes: +0 source LE word,+2 first,+3 count (1..255),
; +4 code,+5 cell LE word(0..767),+7 status,+8 LE word bytes written.
; G+10 data write port and +11 control port must be set from BIOS before init.
; Inputs +0..6,+10..11 survive; AF/BC/DE/HL/flags destroyed; non-reentrant.
; Caller owns VDP latch exclusively for entire call, IRQs disabled or coordinated.
; Fixed conservative port delay, works at standard MSX1 clock. No DI/EI here.
; Init blanks patterns and names, not colours/sprites/registers; see caller.
glyph_init:
xor a
ld (G+7),a
ld hl,0
call glyph_address
ld de,2048
call glyph_zero
ld hl,$1800
call glyph_address
ld de,768
call glyph_zero
ld hl,2816
ld (G+8),hl
ld a,1
ld (G+7),a
ret
glyph_zero:
xor a
call glyph_out
dec de
ld a,d
or e
jr nz,glyph_zero
ret
glyph_replace:
ld a,1
jr glyph_begin
glyph_install:
ld a,(G+3)
glyph_begin:
ld b,a
xor a
ld (G+7),a
ld (G+8),a
ld (G+9),a
ld a,b
or a
ret z
ld e,a
ld d,0
ld a,(G+2)
ld l,a
ld h,0
add hl,de
ld de,257
or a
sbc hl,de
ret nc
ld a,(G+2)
ld l,a
ld h,0
add hl,hl
add hl,hl
add hl,hl
push bc
call glyph_address
pop bc
ld l,b
ld h,0
add hl,hl
add hl,hl
add hl,hl
ld (G+8),hl
ld d,h
ld e,l
ld hl,(G)
glyph_copy:
ld a,(hl)
call glyph_out
inc hl
dec de
ld a,d
or e
jr nz,glyph_copy
ld a,1
ld (G+7),a
ret
glyph_select:
xor a
ld (G+7),a
ld (G+8),a
ld (G+9),a
ld hl,(G+5)
ld de,768
or a
sbc hl,de
ret nc
ld hl,(G+5)
ld de,$1800
add hl,de
call glyph_address
ld a,(G+4)
call glyph_out
ld a,1
ld (G+7),a
ld (G+8),a
ret
glyph_address:
ld a,(G+11)
ld c,a
ld a,l
call glyph_port
ld a,h
or $40
call glyph_port
ret
glyph_out:
push af
ld a,(G+10)
ld c,a
pop af
glyph_port:
out (c),a
nop
nop
nop
nop
retPay for what changes
Changing one name costs one destination byte; replacing one picture costs 8. MSX also writes two control-port bytes to select either destination address; the glyph count does not include those control writes. A full init writes 2,816 bytes; that clears the font plus the entire owned name table. Installing every glyph just to change one is wasted work.
The assembled component takes 205 code bytes and 24 context bytes; glyph assets take 8 bytes each. These counts exclude the reset caller, video setup and diagnostic VRAM readback. They are storage and write counts, not measured frame budgets. The complete pattern allocation is 2 KB.
AF/BC/DE/HL and flags are clobbered; allow ten stack bytes including nested calls. Add space for your other code and interrupts. The context is non-re-entrant. The routines do not change interrupt state or banks, and must not race a font cache, other renderer or OS using the same resources.
A font cache, proportional layout, arbitrary-position text and live transfer queue are useful further components. PET and Aquarius ROM characters need a different strategy; an upload routine cannot make fixed ROM writable.
What was verified
All 16 calls completed on this MSX1 configuration with Asm198x 0.0.71 and Emu198x 0.39.0. Checks compare full pattern/name memory, nonzero neighbours, public inputs, accepted/rejected status and destination-write counts after each request. They include shared replacement, single-cell selection, first/last slots, largest legal uploads, zero/overflow counts, invalid cells and reset. Selected whole native frames match a separate compositor. A completed wrong-slot build fails both memory and visible-output checks.
This is an original reset caller, qualified independently on this machine. It does not establish stock-OS loading, interrupt coexistence, hardware timing.