- Полный RE стока V3.25 (Cortex-M4F) + FM100B: карта памяти, протокол, codeplug, UI-архитектура - Русификация: свой CP1251-шрифт + патч рендера, перевод меню и надписей, ребренд Ru-4D V3.25 - Блюпринт переделки UI + C-тулчейн (clang thumbv7em), доказан инъекцией - Готовые флешеры: WebSerial .html и Windows .exe со вшитой прошивкой - Дамп SPI рации, стоковая прошивка, инструменты сборки Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
278 строки
19 KiB
Markdown
278 строки
19 KiB
Markdown
# RT-4D Display Subsystem — API Reference (`display` key)
|
||
|
||
Reverse-engineered drawing toolkit for the Radtel RT-4D stock firmware
|
||
(`rt4d_stock_v3.25_abs_0x08002800.bin`, ARM Cortex-M4F Thumb, load base `0x08002800`).
|
||
This is the low-level graphics API to **reuse** when rewriting the UI. All addresses are
|
||
absolute vaddrs. Signatures use ARM Thumb AAPCS (r0..r3 = args, extra args on stack).
|
||
|
||
> **TL;DR for the UI rewrite:** the LCD is a **monochrome page-addressed panel, 128×64
|
||
> px = 8 pages × 128 columns**, driven by **bit-banged serial (GPIO), with NO framebuffer** —
|
||
> every draw call clocks pixels *directly into the controller's GDDRAM*. There is nothing to
|
||
> "flush." You position a cursor with `lcd_set_pos(page,col)` and stream 8-bit vertical
|
||
> pixel-slices with `lcd_write_col(byte)`. Everything above that (chars, strings, icons,
|
||
> boxes) is built from those two. Fonts live in **external SPI data-flash**, fetched at draw
|
||
> time by `spi_flash_read`. The single most important callable is
|
||
> **`draw_string(page,x,str,len,mode) @0x08008A50`**.
|
||
|
||
---
|
||
|
||
## 1. Display hardware
|
||
|
||
### 1.1 Panel type, resolution, color format
|
||
|
||
| Property | Value | Evidence |
|
||
|---|---|---|
|
||
| Type | **Monochrome, page-addressed** LCD (ST7565/UC1701/SSD1306-class controller) | `lcd_write_col` streams **8 vertical pixels per byte, LSB=top**; page addressing in `lcd_set_pos` |
|
||
| Resolution | **128 (W) × 64 (H) px** | `draw_string` wraps X at 121 and cycles page `& 7` (8 pages × 8 = 64 rows); status icons at cols 0x15/0x7B (21/123) |
|
||
| Pages (rows of 8px) | **8** (page 0 = top) | page arg masked `& 7` in the wrap logic (`0x08008A82`) |
|
||
| Color format | **1 bpp** (bit set = pixel on). "mode" adds invert/outline (see §4) | `lcd_write_col` shifts one bit/pixel; blitter mode transforms are bit ops |
|
||
| Controller column origin | **183** (`0xB7`) — visible window is offset inside a wider GDDRAM | `lcd_set_pos`: `col_cmd = 0xB7 − x` (`rsb r1,r5,#0xB7` @`0x08014A82`) |
|
||
| Framebuffer | **NONE** — direct-to-GDDRAM bit-bang | `lcd_write_col`/`lcd_set_pos` write GPIO registers directly; no large SRAM buffer touched |
|
||
|
||
Because there is no framebuffer, drawing is **immediate** and **non-atomic** — a partial redraw
|
||
is visible mid-frame. The stock UI mitigates flicker by only repainting changed regions. A
|
||
rewrite can adopt the same discipline, or maintain its own SRAM shadow buffer and blit it with
|
||
the raw-bitmap primitive (`lcd_blit_cols`, §2).
|
||
|
||
### 1.2 The bit-bang interface (GPIO, not SPI2)
|
||
|
||
The LCD is **not** on SPI2 (SPI2 @0x40003800 is used elsewhere; the external **SPI data-flash**
|
||
that holds the fonts is itself bit-banged on GPIOB — see §5). The LCD is driven by three GPIO
|
||
"registers" resolved from `lcd_set_pos`/`lcd_write_col` literal pools:
|
||
|
||
| Symbol in this doc | Literal | Role (inferred) | Access pattern |
|
||
|---|---|---|---|
|
||
| `LCD_RS` | `0x40010000` | **Register-Select / DC** line, driven bit0 via helper `gpio_bit0(base,val) @0x08021C4C` (`bfi [r0],r1,#0,#1`). `0`=command phase, `1`=data phase | set 0 before a byte, 1 after |
|
||
| `LCD_PORT` | `0x40020400` (GPIOB) | **CLK + DATA** bit-bang port. Set-bits reg at `+0x18`, clear-bits reg at `+0x28` (Artery-clone GPIO SCR/CLR layout) | per-bit CLK low/high, DATA set/clear |
|
||
| `LCD_CS` | `0x40020000` (GPIOA) | **CS/latch** — writes GPIOA `+0x18`/`+0x28` around each byte | strobe per byte |
|
||
|
||
> **Note on the clone GPIO map.** This MCU is an STM32F407-class **Artery AT32** clone. Its GPIO
|
||
> "set" and "clear" strobe registers sit at `base+0x18` and `base+0x28` (used as `str val,[port]`
|
||
> and `str val,[port+0x10]` after the code loads `port+0x18`). Bit masks seen: `0x08` = CLK,
|
||
> `0x20` = DATA. The exact silicon pin numbers are not load-bearing for the UI rewrite (you call
|
||
> the primitives, you don't re-bang pins); they are documented here only to explain the register
|
||
> writes. `gpio_bit0 @0x08021C4C`, `gpio_clr @0x08021C56`, `gpio_test @0x08021C5C` are the generic
|
||
> helpers.
|
||
|
||
### 1.3 The two hardware primitives (bottom of the stack)
|
||
|
||
```c
|
||
// @0x08014A7C — position the GDDRAM write cursor.
|
||
// Sends 3 command bytes: (0xB7 - x_col), (0x10 | (y>>4)), (y & 0x0F) via lcd_send_cmd.
|
||
// NOTE arg order: r0 = page/x-select, r1 = the 16-bit-ish column value. See §3 for the
|
||
// exact meaning as used by the blitters: r0 = page index, r1 = column (x pixel).
|
||
void lcd_set_pos(uint8_t page, uint16_t col); // @0x08014A7C
|
||
|
||
// @0x08014AA0 — send ONE command byte (RS=0), MSB-first, 8 clocked bits. Internal to set_pos;
|
||
// 0 external callers. Effectively lcd_send_cmd(uint8_t cmd).
|
||
static void lcd_send_cmd(uint8_t cmd); // @0x08014AA0
|
||
|
||
// @0x08014B28 — write ONE data byte = a vertical strip of 8 pixels (LSB = top pixel) at the
|
||
// current cursor, then auto-advances the column. RS=1. Bits shifted LSB-first (asrs r5,#1).
|
||
void lcd_write_col(uint8_t column_bits); // @0x08014B28
|
||
```
|
||
|
||
`lcd_set_pos` internally calls `lcd_send_cmd` three times (col-high, page, col-low). `lcd_write_col`
|
||
clocks 8 data bits then strobes CS. Both use the busy-wait delay `delay_short(n) @0x08007934`
|
||
(a `subs/bne` spin — **not** a real timer) between edges.
|
||
|
||
### 1.4 Panel init / power / backlight
|
||
|
||
- **Backlight** is a **TIM PWM** on TIM1 (`0x40010000`), programmed via `tim_set_ccr @0x08021C14`
|
||
and `tim_set_arr_psc @0x08021C04`.
|
||
- `lcd_set_brightness(level) @0x08014C34` — `level` 0..4 → duty {0, 5, 0x1E, 0x64, 0xFF}. Called
|
||
from `0x08009204`.
|
||
- `backlight_on/init @0x08014BEC` and `@0x08014C0C` — enable pin + set duty.
|
||
- **Panel reset / power pins** are toggled in `lcd_power_seq @0x0801D938` (writes GPIOB `+0x18`/`+0x28`
|
||
BSRR, runs a PWM ramp via `pwm_cfg @0x0802129C`, delays via `delay_ms @0x08007946`).
|
||
- The panel **command init sequence** (contrast/segment-remap/display-on) is executed once at boot
|
||
through the same `lcd_send_cmd` path; it does not need to be re-issued by a UI rewrite that keeps
|
||
the stock boot. (Reusing `lcd_set_pos`/`lcd_write_col` after boot is sufficient.)
|
||
|
||
`delay_ms(ms) @0x08007946`, `delay_short(loops) @0x08007934` — timing helpers usable by UI code.
|
||
|
||
---
|
||
|
||
## 2. Drawing primitives (callable API)
|
||
|
||
All coordinates: **`page`** = vertical row of 8 px (0=top..7), **`x`/`col`** = pixel column
|
||
(0=left..127). Text/GBK cells are **2 pages tall (16 px)**; the compact font is 1 page tall.
|
||
|
||
| vaddr | Suggested name | Signature (AAPCS) | What it does | Evidence |
|
||
|---|---|---|---|---|
|
||
| **0x08008A50** | **draw_string** | `void draw_string(u8 page, u8 x, const char *s, u16 len, u8 mode /*[sp]*/)` | **Primary text API.** Iterates bytes: `0x01–0x7F`→ASCII glyph (+7px); `0x80–0xFE`→GBK lead byte, consumes trail, `(lead<<8)\|trail`, wide glyph (+14px); `0x00/0xFF`→space/stop. Handles X-wrap at 121 and page advance. 5th arg `mode` is read at `[sp+0x28]` inside (10-reg push). | disasm §confirmed; callers pass `mode` via `str r?,[sp]` before `bl` (e.g. `0x0800318C`, `0x0801408E`) |
|
||
| 0x08007FB8 | ascii_blit | `void ascii_blit(u8 page, u8 x, u8 ch, u8 mode)` | Draws one **7px-wide × 16px-tall** ASCII glyph. Reads **14 bytes** from SPI font `0x19C000 + (ch−0x20)*14` via `spi_flash_read`. Applies mode (invert/outline). Emits via `lcd_set_pos`+`lcd_write_col` (2 pages × 7 cols). | `mov r2,#0x19c000`; `index=(ch-0x20)*? ; 14B read`; per-col set_pos/write_col loop |
|
||
| 0x08008076 | ascii_blit_v2 | `void (u8 page,u8 x,u8 ch)` | Variant of ascii_blit using width-shaper `0x08005368` (proportional spacing table). | `0x19c000`, calls `0x08005368` |
|
||
| 0x08008454 | gbk_blit | `void gbk_blit(u8 page, u8 x, u16 gbk, u8 mode)` | Draws one **14px-wide × 16px-tall** double-byte (GB2312/GBK) glyph. `row=lead−0x81, col=trail−0x40, index=col+row*190 (−1 if trail>0x7F)`, reads **28 (0x1C) bytes** from `0x19E000 + index*28`. | `mov r2,#0x19e000`; `movs r1,#0x81`; 0x1C read |
|
||
| 0x08008530 | gbk_char_at | `void gbk_char_at(u8 page, u8 x, u16 gbk)` | Standalone directly-callable single wide-char draw (same math as gbk_blit, base `0x19E000`, 28B). | `0x19e000`, `subs #0x40`, `0x81` |
|
||
| 0x080089AC | small_char | `void small_char(u8 page, u8 x, char ch, ...)` | **Compact 5px font** renderer (1 page tall). Reads `(ch−0x20)*5` from an in-flash 5×8 table; clamps ch to 0x20..0x7F. Used for tiny status text/numbers. | `subs #0x20`, `*5` (`r7*5`), single-page write |
|
||
| **0x08008CF0** | draw_hline_seg | `void (u8 col, bool on)` | Draws a 2-px separator at pages 4 & 5 (values 0xFE/0x7F when on, else 0) — a horizontal rule/underline used under menu fields. | set_pos(page4/5), write_col(0xFE/0x7F) |
|
||
| **0x0800842C** | fill_col_run | `void fill_col_run(u8 page, u8 x, u8 count)` | Writes `count` columns of constant `0x18` at `page` — a thin horizontal bar (used for gauge ticks). | write_col(0x18) loop `r5<r4` |
|
||
| **0x08008DAA** | draw_box | `void draw_box(u8 page_origin)` | Draws a **rounded-rectangle frame** (corners 0xF8/0x1F, edges 0xFF) spanning pages `origin`..`origin+6`. Menu/popup border. | corner bytes 0xF8/0x1F, edges 0xFF |
|
||
| 0x08008D24 | blit_cols | `void blit_cols(u8 page, u8 count, const u8 *cols, bool src)` | Streams `count` raw column-bytes from `cols[]` at `page` (or zeros if `src==0`) — **generic 1-page bitmap blit / clear**. | set_pos(page), loop write_col(buf[i]) |
|
||
| 0x08008D62 | blit_rect | `void blit_rect(u8 page, u8 x, u8 pages, u8 width, const u8 *bmp /*[sp]*/)` | Streams a `pages`×`width` raw bitmap (row-major `bmp[page*width+col]`) — **multi-page bitmap/icon blit**. Use this to blit a shadow framebuffer. | `mla r0,r4,r5,r6` addressing, nested page/col loops |
|
||
| 0x0800836C | draw_logo48 (`0x080086CC`) | `void draw_logo(void)` | Draws the **48px×48px boot logo** (6 pages × 0x30 cols) from an in-flash bitmap table. | `#0x58` col base, `r4<6`, `r5<0x30` |
|
||
| 0x08008874 | draw_icon_batt | `void draw_icon(u8 page, bool on)` | Draws the battery/side icon at col `0x7B` (123). Status-bar icon. | col `r4+0x7B`, 8B table |
|
||
| 0x08008A04 | draw_icon_signal | `void draw_icon(u8 page, bool on)` | Draws a status icon at col `0x15` (21) — signal/antenna glyph. | col `0x15+r4` |
|
||
| 0x08008300 | draw_dots | `void draw_dots(u8 which)` | Draws 1–3 dotted markers (byte 0xBD) — page/step indicator dots. | `0xBD` at sp[4..b], cases 1/2/3 |
|
||
| 0x08008224 | draw_frame_corners | `void (u8 x, u8 page)` | Draws corner pixels of a highlight frame (2-px inset box). | `rsb #1`, write_col(0) corner pattern |
|
||
| 0x08008100 | big_char | `void big_char(u8 page,u8 x,u8 ch,u8 mode)` | Large-font glyph: reads **0x21 (33) bytes** from `0x?+(ch−0x20)*0x20` — a taller/wider font variant (frequency display digits). | `subs #0x20`, `*0x20`, 0x21B read, invert mode |
|
||
| 0x080081A0 | set_pixel_run | pixel/segment helper | Builds a partial column mask (`0x80>>bit`) for sub-page pixel plotting; used by line/rect fills. | `0x80 asr`, `orr` mask accumulate |
|
||
| 0x08008B90 | draw_number_row | `void (u8 page,u8 x,const u8*digits,u16 n,u8 mode)` | Draws `n` glyphs from `digits[]` with **12px** stride (`(i*3)<<2`) — fixed-pitch number/label row (calls big_char `0x08008100`). | stride `r4*3<<2`, calls `0x08008100` |
|
||
| 0x08008BC6 | draw_str_spaced | `void (u8 page,u8 x,const u8*s,u16 n,u8 mode)` | Draws `n` chars with **6px** proportional stride (calls small_char `0x080089AC`). | `+6` stride, calls `0x080089AC` |
|
||
| 0x08008BFC | draw_field | menu field value paint | Copies a field record (0x14 bytes) and renders label+value; reads struct offsets `+0x102/+0x104/+0x108/+0x10A`. | `movs r2,#0x14`, `memcpy 0x08002BEE` |
|
||
|
||
### 2.1 Screen-clear / fill
|
||
|
||
There is **no single "clear whole screen"** ROM export separate from the loop; the stock code
|
||
clears by writing zero columns. **To clear a region**, call `blit_cols(page, count, NULL, 0)`
|
||
(`@0x08008D24` with `src==0` → writes `0x00`) per page, or `blit_rect` (`@0x08008D62`) with a
|
||
zero bitmap. **To clear the full 128×64:** loop `page = 0..7`, `blit_cols(page, 128, NULL, 0)`.
|
||
`draw_string` with a run of `0x20` (space) glyphs also erases a text row.
|
||
|
||
---
|
||
|
||
## 3. Coordinate system & text metrics
|
||
|
||
```
|
||
col/x → 0 ........................................ 127 (128 px wide)
|
||
page 0 ┌───────────────── status bar (icons) ─────────────┐ y 0..7
|
||
page 1 │ │ y 8..15
|
||
page 2 │ text/GBK cells are 16 px tall = 2 pages │
|
||
... │ │
|
||
page 7 └───────────────────────────────────────────────────┘ y 56..63
|
||
```
|
||
|
||
- **Origin:** top-left. `page` grows downward, `x`/`col` grows rightward.
|
||
- **`page` (row) unit:** one page = **8 vertical pixels**. Valid 0..7. Passed as `r0` to blitters.
|
||
- **`x` (column) unit:** **pixels**, 0..127. Passed as `r1`.
|
||
- **`lcd_set_pos(page, col)`** maps `col` to the controller command `0xB7 − col` (window offset)
|
||
and `page` via `0x10 | (page>>4)` / `page & 0x0F`. The blitters call it once per (page, column)
|
||
pair, so you rarely call it directly.
|
||
|
||
**Text cell metrics:**
|
||
|
||
| Font | Draw fn | Cell W × H | Advance | Glyph src (SPI) | Bytes/glyph |
|
||
|---|---|---|---|---|---|
|
||
| ASCII (normal) | `ascii_blit` / via `draw_string` | 7 × 16 px (2 pages) | **+7 px** | `0x19C000 + (ch−0x20)*14` | 14 |
|
||
| GBK / CJK / Cyrillic | `gbk_blit` / via `draw_string` | 14 × 16 px (2 pages) | **+14 px** | `0x19E000 + index*28` | 28 |
|
||
| Compact | `small_char` | 5 × 8 px (1 page) | **+6 px** (with `draw_str_spaced`) | in-flash 5×8 table | 5 |
|
||
| Large digits | `big_char` | ~11 × 16 px | +12 px (`draw_number_row`) | in-flash *0x20 table | 33 (0x21) |
|
||
|
||
- **Row height** = 16 px = 2 pages. A 128×64 screen therefore fits **4 text rows** (pages 0-1,
|
||
2-3, 4-5, 6-7). Menus use page-4 for the highlighted line (`draw_string(4, …)` in the menu
|
||
field painter `@0x08014074`).
|
||
- **Chars per row:** 128/7 ≈ **18 ASCII** or **9 GBK** (2-byte) chars.
|
||
- **X-wrap:** `draw_string` auto-wraps to next 2-page cell when `x` passes ~121 (ASCII) / 114 (GBK).
|
||
|
||
---
|
||
|
||
## 4. Color / mono handling and the `mode` parameter
|
||
|
||
The panel is 1-bpp. "Color" = pixel on/off. The **`mode`** argument (5th arg of `draw_string`,
|
||
4th of `ascii_blit`/`gbk_blit`, read from stack `[sp+0x28]` in `draw_string`) selects a
|
||
per-glyph bit transform applied to the fetched font bytes **before** they are clocked out:
|
||
|
||
| mode | Name | Transform (in `ascii_blit @0x08007FB8`) | Visual |
|
||
|---|---|---|---|
|
||
| **0** | Normal | none | black text on white (pixel = font bit) |
|
||
| **1** | Inverse | `byte = ~byte`, plus edge-bit clears (`&0xFE`/`&0x7F` on alternating bytes) to keep a 1px gutter | white text in a black bar (selection highlight) |
|
||
| **2** | Outline | `byte \|= 0x80` on even bytes (adds a boundary line) | boxed/outlined text |
|
||
|
||
Evidence: `0x08007FDE cmp r7,#1 → invert loop (mvns)`; `0x0800801C cmp r7,#2 → orr #0x80 loop`.
|
||
`draw_string` forwards its 5th arg unchanged as this `mode`. **Selection highlighting in menus =
|
||
`mode 1`.** For a UI rewrite, pass `mode=1` to render the currently-selected line inverted.
|
||
|
||
---
|
||
|
||
## 5. Font/asset fetch (SPI data-flash)
|
||
|
||
Glyph bitmaps are **not in MCU flash** — they are read on demand from the external SPI data-flash
|
||
(the same 4 MB device dumped in `radio-spi-dump.bin`) by:
|
||
|
||
```c
|
||
// @0x08021828 (entry 0x08021826 sets r0=0 then falls through)
|
||
void spi_flash_read(void *dst, uint32_t src_addr, uint32_t len); // @0x08021828
|
||
```
|
||
|
||
- Issues SPI opcode **0x03** (read), clocks a **3-byte** address (or 4-byte if the chip capacity
|
||
byte is 0x18/0x19), streams `len` bytes into `dst`. Bit-banged on **GPIOB** (`0x40020400`
|
||
region, literals `0x8021820..0x8021AD8`). 34 direct BL callers — the glyph blitters are the
|
||
heavy users.
|
||
- **Font bases:** ASCII narrow `0x19C000` (14 B/glyph), GBK/wide `0x19E000` (28 B/glyph). Cyrillic
|
||
is present in the GBK bank at GB2312 row A7 (see `RT-4D_Russification.md`). A separate Unicode
|
||
codepoint index lives at SPI `0x3F0000`.
|
||
- **Constraint respected:** these are SPI **reads only** for the UI; they do not touch the codeplug
|
||
format or the CPS serial protocol. A UI rewrite reuses `spi_flash_read` as-is for fonts.
|
||
|
||
Related SPI HW primitives (do **not** reuse for writes in a UI rewrite — codeplug boundary):
|
||
`spi_send_byte @0x08021538`, `spi_read_bytes @0x08021580`, `spi_sector_erase @0x08021924`
|
||
(opcode 0x20, 4 KB), `spi_page_program @0x08021A70`.
|
||
|
||
---
|
||
|
||
## 6. Minimal "draw a screen from scratch" recipe
|
||
|
||
Using only reused stock entry points (no framebuffer needed):
|
||
|
||
```c
|
||
// ---- reused stock API (thumb addresses, call with bl / function pointers) ----
|
||
void draw_string (u8 page, u8 x, const char *s, u16 len, u8 mode); // 0x08008A50
|
||
void gbk_char_at (u8 page, u8 x, u16 gbk); // 0x08008530
|
||
void blit_cols (u8 page, u8 count, const u8 *cols, u8 src); // 0x08008D24 (src=0 => clear)
|
||
void draw_box (u8 page_origin); // 0x08008DAA
|
||
void lcd_set_pos (u8 page, u16 col); // 0x08014A7C
|
||
void lcd_write_col(u8 column_bits); // 0x08014B28
|
||
void lcd_set_brightness(u8 level); // 0x08014C34
|
||
|
||
void my_screen(void)
|
||
{
|
||
// 1. Clear the whole 128x64 (8 pages) — write zero columns.
|
||
for (u8 pg = 0; pg < 8; pg++)
|
||
blit_cols(pg, 128, 0, 0); // src=0 -> 0x00 fill
|
||
|
||
// 2. Title on the top text row (pages 0-1), normal video.
|
||
draw_string(0, 2, "MAIN", 4, 0);
|
||
|
||
// 3. A selected menu line on pages 4-5, inverse video (highlight).
|
||
draw_string(4, 1, "Channel 001", 11, 1); // mode 1 = inverse
|
||
|
||
// 4. A framed popup border (rounded box) starting at page 1.
|
||
draw_box(1);
|
||
|
||
// 5. Draw a raw 8px-tall icon: position cursor, stream columns.
|
||
static const u8 batt[8] = {0x3C,0x24,0x24,0x24,0x24,0x24,0x3C,0x18};
|
||
lcd_set_pos(0, 118); // page 0, x=118
|
||
for (u8 i = 0; i < 8; i++) lcd_write_col(batt[i]);
|
||
|
||
// No flush needed — pixels are already in the LCD.
|
||
}
|
||
```
|
||
|
||
**Notes for the rewrite:**
|
||
- Keep to the (page, x-pixel) model. A "row" is 2 pages (16 px); place text rows at pages 0,2,4,6.
|
||
- For flicker-free full-screen redraws, either repaint only changed regions (stock approach) or
|
||
keep your own `u8 fb[8][128]` SRAM shadow and blit it with `blit_rect @0x08008D62`.
|
||
- Use `mode=1` for the selected item; `mode=0` otherwise; `mode=2` for outlined labels.
|
||
- Cyrillic/CJK: put raw GB2312 double-byte codes (lead ≥0x80) in the string; `draw_string`
|
||
auto-routes them through `gbk_blit`. No firmware change needed (see Russification report).
|
||
- All of this is **display-only** and reuses `spi_flash_read` solely for font fetches — it never
|
||
writes the SPI codeplug regions and never touches the CPS serial protocol, satisfying the hard
|
||
constraint.
|
||
|
||
---
|
||
|
||
## 7. Confidence & open items
|
||
|
||
- **High confidence:** `draw_string`, `ascii_blit`, `gbk_blit`, `gbk_char_at`, `lcd_set_pos`,
|
||
`lcd_write_col`, `spi_flash_read`, `mode` semantics, font bases, char metrics, 128×64/8-page
|
||
geometry, no-framebuffer/direct-GDDRAM model, brightness PWM. All verified from disassembly +
|
||
literal pools + call sites.
|
||
- **Medium confidence:** exact controller part number (ST7565/UC1701/SSD1306-family inferred from
|
||
page addressing + `0xB7` column origin, not read from an ID); the precise GPIO pin numbers for
|
||
CLK/DATA/RS/CS on the Artery clone (register offsets are certain; silicon pin mapping is not
|
||
load-bearing). `big_char`/`draw_number_row` font-base literal not fully resolved (only the
|
||
33-byte stride is confirmed).
|
||
- **Not needed for the rewrite:** the boot-time panel command-init sequence (reused as-is via the
|
||
stock boot); re-deriving it would only matter for a from-scratch panel bring-up.
|