Skip to content

Put Text Inside a Panel

Measure counted labels and choose character or word wrapping with four reusable machine-specific implementations.

A prompt changes from READY to READY TO PLAY. It should still fit its panel. A longer message should either find room on the next line or leave the old picture intact. Those are useful rules to put in one routine instead of rediscovering them in every menu.

These components add counted-line measurement, left/centre/right alignment and bounded multi-line layout to the existing bitmap-text renderers. The drawing routines and their machine-specific display contracts stay in charge of pixels.

Implementation What the display path keeps visible
Spectrum 48K 256×192 bitmap; scanline helper and separately owned attributes
BBC Micro MODE 4 320×256 bitmap; interleaved rows, 512-byte tables, zero-page BP
Dragon 32 PMODE 4 256×192 linear bitmap; big-endian input/output words
CPC 464 MODE 1 320×200 logical pixels; packed pen indices and interleaved rows

Each entry has usable includes, a complete caller, a changed-width/spacing example, native captures and two common mistakes with verified repairs. The evidence covers these named machines and modes; another machine needs its own qualified display backend and execution checks.

Start with the cell width

An 8×8 font advances eight pixels per code, including a space or fallback glyph. Five codes need 40 pixels. A 177-pixel panel has 137 pixels left over, so centre alignment puts 68 before the label and 69 after it. Add the panel’s left edge to turn that local offset into a screen position.

The measurement routine reads a supplied byte count, without scanning for a terminator. Its word result can represent all 2,040 pixels of a 255-code line. Check the result against the panel before unsigned subtraction. A label too wide for single-line mode rejects without changing the bitmap.

This centres logical cells, not the outlines of the letters. The original font has blank columns inside its cells. Optical centring, proportional advances and kerning are different decisions; use them when they improve the result enough to justify their costs.

Give wrapping a precise policy

Block mode fits panel width div 8 cells per line. It recognises LF (byte 10), preserves spaces and wraps at character boundaries. A long word can split. It does not silently turn into a word processor.

An LF immediately after a full chunk belongs to that break. Consecutive LFs retain interior blank lines. A final LF does not add an extra trailing row. Empty input has zero lines. Single-line mode treats every byte as a glyph code instead; the counted-line measurement routine follows that same rule.

Line pitch is the distance between origins. Five 8-pixel rows at pitch 16 occupy (5-1)*16+8=72 scanlines. Counting five whole pitches would reserve an unnecessary final gap. The routine plans the complete block before drawing, so a message too tall for its rectangle does not paint half a paragraph first.

Use it in a game

Supply a panel, alignment, pitch, source pointer and byte count. Initialise the existing renderer’s font/range, fallback, mode and pens. The layout owns its 64-byte context and the renderer’s 96-byte T/48-byte BP workspaces during the call. It sets the scissor to the panel and leaves source/font/configuration stability to the caller.

For a result screen, centre a counted heading. For menu values, a right-aligned edge can be steadier. For a message panel, use block mode and clear the area you own before replacing longer text. Opaque glyph cells do not erase unused line space or interior blank lines; transparent text preserves zero-bit pixels.

The narrower custom caller shows what the inputs buy you: another message, right alignment, five rows and wider line spacing, with the same font and component allocations. Its 13-column character wrapping deliberately splits PANEL. The word-aware variant below keeps those words whole; the simpler character-wrap component retains its original rule.

Keep the alternatives cheap

Counted labels are a good fit for fixed game text. A static position can cost less than recalculating a title every frame. A pre-rendered panel can cost less CPU at the expense of another asset. Hardware character screens have their own fixed grids and character repertoires; these bitmap routines are not a reason to replace those useful paths.

The new layout code is 1,628 bytes on Z80, 1,867 on 6502 and 1,463 on 6809 with the qualified assembler. All add 64 private RAM bytes. Renderer/backend code, existing workspaces, font, video storage, BBC tables, messages and stack remain separate. Block mode makes two planning passes; drawing adds source/font reads and the existing glyph/span work. Native span/write counters explain work for a named picture, not elapsed time or a frame budget.

The supplied faults show two common disagreements: measuring four pixels while drawing eight, and treating a panel-local offset as a screen coordinate. Both complete in the emulators, both have independent full-picture expectations, and both repairs execute. A successful loader alone would prove none of that.

Coordinates and remainders and glyph-local positions connect the calculations to other drawing routines. The source contract and execution records retain include/init/call details, boundaries, tool identities and reproduction commands.

The proportional option below adds per-glyph advances. Integer enlargement and other transforms need further implementations. Choose those when the result warrants their extra code, data and drawing work. The routines here provide fixed-width layout with the explicit policies described above.

Keep words together

Choose the word-aware variant when a paragraph reads better with whole words. In seven columns, ONE THREE FIVE becomes ONE / THREE / FIVE. A word wider than the panel still splits at the cell boundary, so the routine always makes progress.

It consumes an LF or space immediately after a full row. Otherwise it uses the last space after the row’s first byte, omits that one separator and resumes after it. All other spaces stay literal; repeated spaces are not normalised. Consecutive LFs keep interior blank rows; a trailing LF adds no extra row. The complete first pass still rejects a block that is too tall before any bitmap writes.

Each machine entry above includes the alternate component, a customisation and a word-break error with a verified repair. Include either text-layout or word-wrap: their entry labels and 64-byte TL contract are the same. The unchanged 8×8 renderer, font and display backend remain in charge of pixels.

Word-aware layout adds 113 code bytes on Z80, 105 on 6502 or 101 on 6809, with no additional context allocation. It scans for boundaries and can rescan a word after a soft break. The same narrow-panel example draws 693 rather than 709 spans, because the row placement and two selected spaces change. That does not measure total speed: extra planning work still needs to be counted.

The maintained word-aware source and 320 native observations cover the four named modes. The proportional option below adds per-glyph advances; scaling and different whitespace policies remain separate work.

Give narrow letters less room

Six I glyphs and six W glyphs both occupy 48 pixels in a fixed 8-pixel grid. Our proportional font needs 24 and 36 pixels respectively at spacing 1. A small width table makes the difference explicit. Measurement sums glyph width + letter spacing; alignment, word boundaries and drawing use those same advances. The final spacing slot counts too, so the result describes logical placement rather than optical ink bounds.

Each implementation above now includes the selectable proportional component, a cropped original MIT font and matching 64-byte width table. Keep the existing renderer and machine-specific backend; reserve 80 TL bytes instead of 64. Supply a metric-table pointer at TL+64 and spacing at TL+66. Measurement now reads the string, font range/fallback and metrics: a count-only call is no longer sufficient. Widths are 1..8, extra spacing 0..2, and their sum must fit the existing eight-column glyph renderer.

This keeps hardware differences visible. BBC still needs zero-page BP and scanline tables, Dragon uses big-endian words, Spectrum attributes remain separately owned, and CPC packs four pen indices per byte. A smaller advance changes byte phase as well as the number of pixels. None acquires a new display mode or a synchronised video interval from this layout routine.

The changed-spacing caller and wrong/fixed movement example help separate useful customisation from a bug: changing spacing in the declaration changes both measurement and drawing; leaving an eight-pixel drawing step behind makes letters spread beyond their measured end. Native checks compare the complete bitmap, source/font/metrics, guards and public results. Retained replay and corruption controls support those configuration-specific observations; they are not fresh execution on another system.

Integer division and remainders explain advance sums and centring; drawing with local coordinates explains translation from glyph to panel to screen. Baseline typography, kerning, integer enlargement and transforms remain separate work. Keep fixed-width text when its smaller contract and predictable grid serve the game better.