11# Text Mode Rendering
22
3- Text mode renders a 106x37 character grid at native 640x480 resolution.
4- Each scanline is rendered by the DMA IRQ handler on Core 1, converting
5- text VRAM cells into 640 RGB332 pixels via inline ARM Thumb-2 assembly.
3+ Text mode renders a character grid at two runtime-selectable source
4+ resolutions: 106x37 at native 640x480, or 53x18 at 320x240 scaled 2x to
5+ the DVI output (see [ Scaled text mode] ( #scaled-text-mode-2x ) ). Each
6+ scanline is rendered by the DMA IRQ handler on Core 1, converting text
7+ VRAM cells into RGB332 pixels via inline ARM Thumb-2 assembly (native)
8+ or plain C (scaled).
69
710## Text VRAM
811
@@ -21,6 +24,12 @@ for bold (mapping into the 256-511 bold region of the narrow cache).
2124For wide characters, ` ch ` holds the linear JIS index directly (used by
2225` write_line ` for glyph re-rendering from scrollback).
2326
27+ Physical rows always use the fixed ` TEXT_VRAM_STRIDE ` (106 cells), so
28+ cell addressing is independent of the active column count. The active
29+ grid (` text_cols ` x ` text_rows ` ) bounds writes, string wrapping, and the
30+ scroll ring modulus; in scaled mode only the first 53 cells of each of
31+ the first 18 physical rows are active.
32+
243316-color palette maps 4-bit indices to RGB332 values (VGA-compatible
2534defaults).
2635
@@ -110,3 +119,42 @@ Key render-loop optimizations:
110119
111120The render function and IRQ handler are placed in SCRATCH_X to avoid
112121flash instruction fetch during rendering.
122+
123+ ## Scaled Text Mode (2x)
124+
125+ ` dvi_set_text_scale(2) ` (Ruby: ` DVI::Text.set_resolution(320, 240) ` )
126+ switches text mode to a 320x240 source resolution scaled 2x to the
127+ 640x480 DVI output. The grid becomes 53x18: 53 cells cover 318 pixels
128+ with 2 black padding pixels on the right, and 18 rows cover 234 source
129+ lines with the remaining 6 source lines (12 DVI lines) black at the
130+ bottom.
131+
132+ The scale change is requested from Core 0 and applied by the DMA IRQ in
133+ the same VSync window as the HSTX reconfiguration, mirroring the
134+ graphics scale mechanism. On application, both ring scroll offsets are
135+ clamped below the new row count (the render side by the IRQ, the write
136+ side by the blocked Core 0 caller) so the single-subtraction ring wrap
137+ stays in bounds when the grid shrinks from 37 to 18 rows.
138+
139+ Scaling reuses the 320x240 graphics mode mechanics:
140+
141+ - ** Horizontal** : pixel groups transfer 320 bytes with ` DMA_SIZE_8 ` and
142+ the HSTX expander is configured with ` ENC_N_SHIFTS = 2 ` , so each byte
143+ is TMDS-encoded twice (byte-lane replication).
144+ - ** Vertical** : a batch of 4 DVI lines covers 2 source lines; each
145+ rendered line buffer is referenced by two consecutive DVI scanline
146+ descriptors.
147+
148+ ` render_text_scanline_12wide_scaled() ` renders one 320 pixel source
149+ line. It is plain C in main SRAM (` .time_critical ` ), not SCRATCH_X asm:
150+ at half the width and half the line rate it has roughly 4x the cycle
151+ budget of the native renderer. It handles narrow and wide cells in a
152+ single loop (no narrow-only pair path, so the odd column count needs no
153+ special casing) and reads the same narrow row cache, per-position glyph
154+ bitmap, and font-byte-mask LUT as the native renderer.
155+
156+ ` prepare_batch_dma_text_scaled() ` builds the batch descriptors. It is
157+ also RAM-resident because it runs during flash-write blanking, where
158+ every source switches to ` blank_line_buf ` with the scale 2 transfer
159+ count (the native blanking branch would feed the wrong count for the
160+ scale 2 HSTX configuration).
0 commit comments