Skip to content

Commit f30d04c

Browse files
authored
Merge pull request #20 from harukasan/text-zoom
Add a 2x scaled text console mode (320x240)
2 parents c10ce36 + 8ee1c90 commit f30d04c

16 files changed

Lines changed: 663 additions & 75 deletions

File tree

doc/dvi.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ Class: `DVI` (provided by picoruby-dvi mrbgem)
1919
- [DVI.wait_vsync](#dviwait_vsync)
2020

2121
`DVI` provides display output control. Two display modes are available:
22-
`DVI::TEXT_MODE` (106x37 text grid at 640x480) and `DVI::GRAPHICS_MODE`
23-
(640x480 RGB332 framebuffer).
22+
`DVI::TEXT_MODE` (text grid, 640x480 native or 320x240 scaled 2x) and
23+
`DVI::GRAPHICS_MODE` (RGB332 framebuffer, 640x480 or 320x240). Both
24+
modes select their source resolution at runtime with `set_resolution`.
2425

2526
```ruby
2627
DVI.set_mode(DVI::TEXT_MODE)
@@ -50,6 +51,9 @@ Block until the next VBlank. See
5051

5152
Class: `DVI::Text` (provided by picoruby-dvi mrbgem)
5253

54+
- [DVI::Text.cols](#dvitextcols---integer)
55+
- [DVI::Text.rows](#dvitextrows---integer)
56+
- [DVI::Text.set_resolution](#dvitextset_resolutionwidth-height)
5357
- [DVI::Text.put_char](#dvitextput_charcol-row-ch-attr)
5458
- [DVI::Text.put_string](#dvitextput_stringcol-row-str-attr)
5559
- [DVI::Text.clear](#dvitextclearattr)
@@ -62,13 +66,33 @@ Class: `DVI::Text` (provided by picoruby-dvi mrbgem)
6266
- [DVI::Text.commit](#dvitextcommit)
6367

6468
`DVI::Text` provides text mode rendering with double-buffered VRAM.
65-
Characters are placed on a 106-column, 37-row grid. The `attr` byte
66-
encodes foreground (bits 7-4) and background (bits 3-0) palette indices.
69+
Characters are placed on a runtime-sized grid: 106 columns by 37 rows at
70+
the native 640x480 resolution, or 53 columns by 18 rows at 320x240 (each
71+
pixel doubled to the 640x480 DVI output). The `attr` byte encodes
72+
foreground (bits 7-4) and background (bits 3-0) palette indices.
6773

6874
All write operations modify the back buffer. Call `commit` to swap the
6975
back buffer to the front buffer at VBlank, preventing tearing.
7076

71-
Constants: `DVI::Text::COLS` (106), `DVI::Text::ROWS` (37).
77+
Constants: `DVI::Text::COLS` (106) and `DVI::Text::ROWS` (37) are the
78+
maximum grid, equal to the 640x480 grid. Use `cols` and `rows` for the
79+
current grid.
80+
81+
#### DVI::Text.cols -> Integer
82+
83+
Return the current grid width (106 at 640x480, 53 at 320x240).
84+
85+
#### DVI::Text.rows -> Integer
86+
87+
Return the current grid height (37 at 640x480, 18 at 320x240).
88+
89+
#### DVI::Text.set_resolution(width, height)
90+
91+
Switch the text mode source resolution. Accepts 640x480 (native) or
92+
320x240 (scaled 2x to the DVI output, for demo readability). Blocks
93+
until the change is applied at VSync, so `cols` and `rows` read the new
94+
grid immediately after. Screen contents are not converted; callers
95+
should clear and redraw (the console does this via `Console#reset`).
7296

7397
#### DVI::Text.put_char(col, row, ch, attr)
7498

doc/dvi/batch-rendering.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ providing ~24,500 cycles of headroom.
1313
remain single (45 IRQs: 10 VFP + 2 VSYNC + 33 VBP). Total: 165 IRQs
1414
per frame.
1515

16+
In 2x scaled text mode a batch covers 2 source lines instead of 4: each
17+
rendered line buffer is referenced by two consecutive DVI scanline
18+
descriptors, consuming 2 line buffers per batch. See
19+
[text-mode-rendering.md](text-mode-rendering.md).
20+
1621
## Design
1722

1823
### Line buffers: 2N = 8

doc/dvi/text-mode-rendering.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
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).
2124
For 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+
2433
16-color palette maps 4-bit indices to RGB332 values (VGA-compatible
2534
defaults).
2635

@@ -110,3 +119,42 @@ Key render-loop optimizations:
110119

111120
The render function and IRQ handler are placed in SCRATCH_X to avoid
112121
flash 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).

mrbgems/picoruby-dvi/include/dvi.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ typedef struct {
3838

3939
typedef enum {
4040
DVI_MODE_GRAPHICS, // RGB332 framebuffer (resolution set by DVI_GRAPHICS_SCALE)
41-
DVI_MODE_TEXT, // text VRAM, native 640x480
41+
DVI_MODE_TEXT, // text VRAM, 640x480 native or 320x240 (2x scaled)
4242
} dvi_mode_t;
4343

4444
// Font data structure (defined in dvi_font.h)
@@ -76,6 +76,10 @@ void dvi_graphics_commit(void);
7676
dvi_text_cell_t *dvi_get_text_vram(void);
7777
int dvi_text_get_cols(void);
7878
int dvi_text_get_rows(void);
79+
// Runtime text resolution. Scale 1 = 640x480 native (106x37 grid with the
80+
// 12px font), scale 2 = 320x240 scaled 2x to the DVI output (53x18 grid).
81+
// Blocks until the VSync IRQ applies the change.
82+
void dvi_set_text_scale(int scale);
7983
void dvi_text_set_font(const dvi_font_t *font);
8084
void dvi_text_set_wide_font(const dvi_font_t *font);
8185
void dvi_text_set_bold_font(const dvi_font_t *font);

0 commit comments

Comments
 (0)