RT-4D: реверс прошивки, русификация, кастомный UI, флешеры
- Полный 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>
Этот коммит содержится в:
@@ -0,0 +1,275 @@
|
||||
# RT-4D Firmware — Main Loop, UI State Machine & UI-Rewrite Hook Points
|
||||
|
||||
**Key:** `main-loop` · **Target:** `stock-fw/rt4d_stock_v3.25_abs_0x08002800.bin` (ARM Cortex-M4F Thumb, load/vaddr base `0x08002800`).
|
||||
All disassembly via capstone `CS_ARCH_ARM + CS_MODE_THUMB`.
|
||||
|
||||
This is the hook-point document for a full UI rewrite. It nails down (1) the boot→superloop path, (2) the single UI screen-state variable and the **two mirror-image dispatch tables** (draw + input) keyed on it, (3) how screens redraw and transition, and (4) the exact vaddrs and strategy to redirect rendering + navigation into custom code while keeping all lower-level radio/codeplug/serial APIs intact.
|
||||
|
||||
---
|
||||
|
||||
## 0. TL;DR — the hook
|
||||
|
||||
- **Screen-state variable:** `g_screen = *(uint8_t*)0x200008B3` (0..11). A shadow `*(uint8_t*)0x200008B2` holds the previous screen. `0x200008B3` is referenced by **55** literal-pool words across the image — it is the central UI selector.
|
||||
- **Draw dispatcher:** `ui_draw_dispatch @0x0801E1BC` — `tbb`-jumps on `g_screen` (12 entries) to the current screen's incremental redraw routine. Called every UI tick from the scheduler at `0x080207E6`.
|
||||
- **Input dispatcher:** `ui_process_key @0x0801E6EC` — pulls a keycode/keystate from the key struct at `0x20000B57`, then `tbb`-jumps on `g_screen` (12 entries, table `@0x0801E780`) to the current screen's key handler `handler(u8 keycode, u8 keystate)`. Called every UI tick from the scheduler at `0x08020816`.
|
||||
- **Hook strategy (recommended):** patch the two `bl` call sites in the scheduler — `0x080207E6` (`bl ui_draw_dispatch`) and `0x08020816` (`bl ui_process_key`) — to call your own router. Your router owns rendering + navigation and calls the stock lower-level APIs (draw_string, keys, RF, DMR, codeplug). This is a 2-instruction redirect and leaves the serial/CPS path and codeplug format completely untouched. See §5.
|
||||
|
||||
---
|
||||
|
||||
## 1. Boot → application superloop
|
||||
|
||||
### 1.1 Reset / CRT startup (library glue, not app logic)
|
||||
|
||||
```
|
||||
Reset 0x08002AC0: SystemInit(0x0801DA2D) ; then bx __main(0x080029E1)
|
||||
__main 0x080029E0: bl 0x080029E8 (__scatterload / RW+ZI init, RLE decompressor @0x08002A1E)
|
||||
bl 0x08002AA0 (__rt_entry)
|
||||
__rt_entry 0x08002AA0: sequence of ARM C-lib inits, then:
|
||||
bl 0x0802136C <-- the real application main / superloop
|
||||
```
|
||||
|
||||
The `0x08002AA0..0x08002D18` cluster is ARM compiler runtime (heap/stack setup, `bkpt 0xAB` semihosting stubs, the ADC ISR tail). The application proper is **`app_main @0x0802136C`**.
|
||||
|
||||
### 1.2 `app_main @0x0802136C` — the top-level superloop ★ HARD ENTRY POINT
|
||||
|
||||
```
|
||||
0x0802136C movs r4,#0
|
||||
0x0802136E bl 0x0801DAD0 ; low-level HW/clock/periph bring-up
|
||||
0x08021372 ldr r0,=0x08002800 ; ldr r1,=0xE000ED08 ; str r0,[r1] ; VTOR = app vector base
|
||||
0x08021378 bl 0x08021024 ; init B
|
||||
0x0802137C movs r0,#0xC8 ; bl 0x08007946 ; init C (display/boot?)
|
||||
0x08021382 bl 0x080127D4 ; init D
|
||||
0x08021386 bl 0x08003060 ; init E (codeplug/settings load)
|
||||
0x0802138A b 0x08021414 ; -> jump to loop top
|
||||
--- LOOP TOP ---
|
||||
0x0802138C if (*(u8*)0x20000C54) bl 0x0801A38C ; reboot flag -> NVIC_SystemReset
|
||||
0x08021396 modeA = *(u8*)0x20000B66 ; serial/session mode gate
|
||||
modeB = *(u8*)0x20000C12
|
||||
spiMode = *(u8*)0x20000C57
|
||||
if (modeA==0 && modeB!=2 && modeB!=4 && spiMode==0)
|
||||
0x080213B2 bl 0x080207DC ; ★ NORMAL UI TICK (radio running)
|
||||
else ... ; PC-programming / SPI-write session paths:
|
||||
0x080213C2 modeB==2 -> bl 0x08019A7C ; CPS mode handler
|
||||
0x080213E2 modeB==4 -> bl 0x0801F84C ; serial framer
|
||||
0x080213EE modeA!=0 -> bl 0x0801F84C ; bl 0x0801F540 ; drain FM100B rx (0x200082EF, 0x08006CB8)
|
||||
0x0802140C bl 0x0801F854 ; serial poll (every iteration)
|
||||
0x08021410 bl 0x0801FE50 ; housekeeping (every iteration)
|
||||
0x08021414 b 0x0802138C ; loop
|
||||
```
|
||||
|
||||
**Interpretation.** The superloop first honours a reboot request, then branches on **serial-session state**: when the radio is *not* in a PC-programming / SPI-write session (`0x20000B66==0`, `0x20000C12∉{2,4}`, `0x20000C57==0`), it runs the **normal UI tick `0x080207DC`**. Otherwise it services the CPS/serial paths. `0x0801F854` (serial poll) and `0x0801FE50` (housekeeping) run unconditionally every pass.
|
||||
|
||||
> **Boundary note (respect the CPS/codeplug contract).** The serial session vars `0x20000B66 / 0x20000C12 / 0x20000C57 / 0x20000C54` and the handlers `0x08019A7C / 0x0801F84C / 0x0801F540 / 0x0801F854` are the **CPS protocol + SPI region-write engine** (documented in RT-4D_RE_Report §4). A UI rewrite must leave this entire `else` branch and the two unconditional serial calls **exactly as-is** — that is the serial/CPS boundary. Only replace what happens *inside* the normal UI tick `0x080207DC`.
|
||||
|
||||
| vaddr | name | signature | notes |
|
||||
|---|---|---|---|
|
||||
| 0x0802136C | `app_main` | `void app_main(void) __attribute__((noreturn))` | The application superloop. Sets VTOR, runs inits, then loops. **Do not relocate** — reset path branches here. |
|
||||
| 0x0801DAD0 | `hw_init` | `void hw_init(void)` | clock/peripheral bring-up (called first) |
|
||||
| 0x08003060 | `codeplug_load_init` | `void(void)` | last init; loads settings/channels from SPI (candidate) |
|
||||
| 0x0801A38C | `nvic_system_reset` | `void(void) noreturn` | writes AIRCR `0x05FA0004` (reboot). Gated by `0x20000C54`. |
|
||||
| 0x080207DC | `ui_tick_normal` | `void(void)` | **the normal-mode UI scheduler** (see §2) |
|
||||
| 0x0801F854 | `serial_poll` | `void(void)` | runs every loop; part of CPS path — keep |
|
||||
| 0x0801FE50 | `housekeeping` | `void(void)` | runs every loop (battery/timers) |
|
||||
|
||||
---
|
||||
|
||||
## 2. `ui_tick_normal @0x080207DC` — the cooperative UI scheduler
|
||||
|
||||
This is **not** the state machine itself; it is a fixed list of periodic subsystems, several gated by down-counters so they run at different rates. The screen draw + key dispatch are two of its calls.
|
||||
|
||||
```
|
||||
0x080207DE bl 0x0801F84C ; serial framer (shared)
|
||||
0x080207E2 bl 0x0801F9C0 ; ? (reads 0x20000C8A menu-key state, 0x200029BB struct)
|
||||
0x080207E6 bl 0x0801E1BC ; ★ ui_draw_dispatch (SCREEN DRAW — hook here)
|
||||
0x080207EA bl 0x0801E050 ; ui_sidekey_dispatch (side/long-press hotkeys — see §3.3)
|
||||
0x080207EE bl 0x0801E3F8 ; build display line buffer (reads 0x200008B3)
|
||||
0x080207F2 bl 0x0801FAC8 ; ...
|
||||
0x080207F6 bl 0x0801F504
|
||||
0x080207FA bl 0x0801FDA4
|
||||
0x080207FE bl 0x0801FDDC
|
||||
0x08020802 bl 0x0801FE0C
|
||||
0x08020806 bl 0x0801DFD0
|
||||
--- period-gated tasks: counter at 0x20000BF1.. reloads to a period when it hits 0 ---
|
||||
0x0802080A if(--tick@0x20000BF1==0){reload 1; bl 0x0801E6EC(key?) ...} see note
|
||||
0x08020816 bl 0x0801E6EC ; ★ ui_process_key (SCREEN INPUT — hook here)
|
||||
0x0802081A bl 0x08020288
|
||||
... 0x20000BF2 (reload 4), 0x20000BF3 (reload 0xA), 0x20000BF5 (0xC8),
|
||||
0x20000BF6 (0x1F4), 0x20000BF8 (0x3E8): slower periodic tasks (RSSI, battery, scan, etc.)
|
||||
0x080208DC return
|
||||
```
|
||||
|
||||
Concretely, the **two dispatch calls you care about** are both unconditional every UI pass:
|
||||
- `0x080207E6 bl 0x0801E1BC` → screen **draw** dispatch
|
||||
- `0x08020816 bl 0x0801E6EC` → screen **key** dispatch
|
||||
|
||||
(Down-counter reloads observed: `0x20000BF1`→1, `0x20000BF2`→4, `0x20000BF3`→0xA, `0x20000BF5`→0xC8, `0x20000BF6`→0x1F4, `0x20000BF8`→0x3E8 — these throttle the slower periodic tasks; the two dispatchers themselves are not throttled.)
|
||||
|
||||
---
|
||||
|
||||
## 3. The UI state machine
|
||||
|
||||
### 3.1 The state variable
|
||||
|
||||
```
|
||||
g_screen = *(uint8_t*)0x200008B3 ; current screen id, 0..11 (0x0B)
|
||||
g_screen_prev = *(uint8_t*)0x200008B2 ; previous screen (used for restore/back)
|
||||
```
|
||||
|
||||
Both dispatchers guard with `cmp g_screen,#0x0C ; bhs <default>` before the `tbb`, so **valid ids are 0..11**.
|
||||
|
||||
### 3.2 The two mirror dispatch tables (draw + input)
|
||||
|
||||
Both are `tbb [pc,r0]` byte-offset jump tables indexed by `g_screen`. They are **positionally parallel**: index *i* in the draw table and index *i* in the input table are the same screen.
|
||||
|
||||
**DRAW dispatcher** `ui_draw_dispatch @0x0801E1BC`:
|
||||
```
|
||||
0x0801E1BE bl 0x080136E4 ; clear per-field dirty flags (row @0x20002512)
|
||||
0x0801E1C2 r0 = *(u8*)0x200008B3
|
||||
0x0801E1C6 cmp r0,#0x0C ; bhs 0x0801E228 (default/no-op)
|
||||
0x0801E1CA tbb [pc,r0] ; table @0x0801E1CE
|
||||
```
|
||||
|
||||
**INPUT dispatcher** `ui_process_key @0x0801E6EC` (tbb inside at `0x0801E780`):
|
||||
```
|
||||
0x0801E6EE bl 0x08005A14 ; keypad scan/debounce
|
||||
0x0801E6F4 if(*(u8*)0x20000B5A==0) return ; keystate (0x20000B57+3) == no-key -> bail
|
||||
0x0801E6FC if(*(u8*)0x20000B59==0xFF) return ; keycode (0x20000B57+2) == none -> bail
|
||||
... global lock / special-mode guards ...
|
||||
0x0801E778 r0 = *(u8*)0x200008B3
|
||||
0x0801E77C cmp r0,#0x0C ; bhs 0x0801E804 (default)
|
||||
0x0801E780 tbb [pc,r0] ; table @0x0801E784
|
||||
each case: r2=0x20000B57; r1=[r2,#3](keystate); r0=[r2,#2](keycode); bl <handler>
|
||||
0x0801E806 after dispatch: *(u8*)(0x20000B57+2) = 0xFF ; consume keycode
|
||||
```
|
||||
|
||||
The **key event struct** is at `0x20000B57`: byte +2 = keycode, byte +3 = keystate/edge (1 = press/repeat). Handlers receive `(r0=keycode, r1=keystate)`.
|
||||
|
||||
### 3.3 Screen table — id → {draw handler, input handler}
|
||||
|
||||
Verified by decoding both `tbb` tables (bytes are half-word offsets from the table base):
|
||||
|
||||
| id | draw handler | input handler | inferred screen | evidence |
|
||||
|---:|---|---|---|---|
|
||||
| 0 | `0x08013BD8` | `0x08017F60` | **Home / VFO-A main** (default landing) | most `strb g_screen,#0` "return home" sites; `0x08013BD8` reads big status struct `0x200009C3+0x42` |
|
||||
| 1 | *(default no-op `0x0801E228` / input `0x0801E804`)* | — | **blank / transient** | table byte 0x2D→no-op; input 0x40→no-op |
|
||||
| 2 | `0x08013980` | `0x08007B10` | **VFO/standby key screen** | `0x08007B10` handles MENU key (10) → sets g_screen=0xA (menu entry, §3.4); EXIT/side keys |
|
||||
| 3 | `0x08013A44` | `0x0800FE3C` | **screen w/ icon @`0x0802505C`** (freq-input / dial) | draws bitmap via `0x08008D62`; input checks `0x20000855` |
|
||||
| 4 | `0x08014978` | `0x0802074C` | **screen w/ icon @`0x08025115`** | bitmap blit; input digit `0xC` handling |
|
||||
| 5 | `0x08013940` | `0x08007784` | **numeric entry A** (0–F) | input: `cmp r4,#0xF; bl 0x08012EF8` (hex digit) |
|
||||
| 6 | `0x080139EC` | `0x08007CD0` | **numeric entry B** (0–9) | input: `cmp r4,#9; bl 0x08012F78` (dec digit) |
|
||||
| 7 | `0x08013804` | `0x08007E68`→`0x08017F60` | **alt of screen 0** | input forwards to id-0 handler `0x08017F60` |
|
||||
| 8 | `0x08013B6C` | `0x08007D…`/via `0x0801E7E4→0x0800F930` | **list/scroll screen** | draw calls `0x080085D6`; several handlers special-case `g_screen==8` (`0x0800F930`) |
|
||||
| 9 | `0x08013F7C` | *(input tbl byte 0x33→no-op region `0x0801E7EA`)* | **status/info screen** | draw reads `0x20000A3D+0x39`, `0x20000A7B` |
|
||||
| **10 (0xA)** | **`0x080142C0`** | **`0x08017294`** | **MENU system** ★ | draw = the menu render loop (facts §Display); input = full menu key handler (largest, `sub sp,#0x64`) |
|
||||
| 11 (0xB) | `0x08014104` | `0x0801D3B0` | **SMS / editor** (text) | draw blits chars at fixed cols; input handles 0–9 (`cmp r4,#9`) |
|
||||
|
||||
Notes:
|
||||
- **Standby "home" screen (id 0/7)** is the radio's idle/operating screen (frequency, channel, RX/TX, DMR-rx overlay). The full home rendering also runs through the period-gated tasks and `0x0801E3F8` (line-buffer builder) — the dispatched draw handler `0x08013BD8` does the incremental field redraw.
|
||||
- **id 1** is a genuine no-op slot (both tables route it to the shared "do nothing" tail). Treat as "transition/blank".
|
||||
- All input handlers share the uniform prototype **`void screen_key(uint8_t keycode, uint8_t keystate)`**.
|
||||
|
||||
### 3.4 How transitions happen (state writes)
|
||||
|
||||
Screen changes are plain byte stores `strb rN, [=0x200008B3]`. Enumerated immediate-write sites (subset; `movs rN,#imm` immediately before the `strb`):
|
||||
|
||||
| new id | example write sites | meaning |
|
||||
|---:|---|---|
|
||||
| 0 | `0x08003490, 0x080094AA, 0x0800AA70, 0x0800AE4C, 0x0800B200, 0x0801E120, 0x0801F06C, 0x0801F81E, …` (many) | return to Home / EXIT |
|
||||
| 1 | `0x080094AE, 0x080095DC, 0x08009760, 0x0801E5DA, 0x0801F698, …` | enter transient/blank |
|
||||
| 2 | `0x0801A2BE` | enter VFO/standby key screen |
|
||||
| 3 | `0x0800B112, 0x08018344, 0x0801A2DE` | enter freq-input/dial |
|
||||
| 4 | `0x0800BC58, 0x0801E15A` | enter screen 4 |
|
||||
| 5 | `0x08009842` | enter numeric-entry A |
|
||||
| 7 | `0x0801A66C` | enter alt-home |
|
||||
| 8 | `0x0800AF0A` | enter list/scroll |
|
||||
| 9 | `0x080094D2` | enter status/info |
|
||||
| **10 (0xA)** | **`0x0800B92A`** (from Home MENU key, §below) | **enter MENU** |
|
||||
| 11 (0xB) | `0x0800B85C` | enter SMS/editor |
|
||||
| 0x11 | `0x080094A4` | (id 17 — value out of 0..11 range; likely a sub-mode byte, not a screen; used by a specialized editor) |
|
||||
| 0x2A | `0x08009A9C` | (id 42 — same: sub-mode marker, not a screen dispatch id) |
|
||||
|
||||
**Canonical transition example — Home → Menu** (in the id-2 standby key handler `0x08007B10`, MENU key path lands in `0x0800B900`):
|
||||
```
|
||||
0x0800B91C bl 0x0801AD9C ; menu-open side effects (build top-level list)
|
||||
0x0800B926 movs r0,#0xA
|
||||
0x0800B928 ldr r1,=0x200008B3
|
||||
0x0800B92A strb r0,[r1] ; g_screen = 10 (MENU)
|
||||
```
|
||||
So **navigation = write the target id to `0x200008B3`** (optionally saving the old value to `0x200008B2` for "back"), plus per-screen enter side-effects. A custom router replicates exactly this.
|
||||
|
||||
---
|
||||
|
||||
## 4. How a screen is (re)drawn each loop
|
||||
|
||||
The draw model is **incremental / dirty-flag driven**, not full-frame:
|
||||
|
||||
1. `ui_draw_dispatch @0x0801E1BC` first calls `0x080136E4`, which walks a dirty-flag row at `0x20002512` (loop of 8+) and force-marks fields dirty on screen entry.
|
||||
2. It then `tbb`-dispatches to the current screen's redraw routine (§3.3). Each routine reads its per-field "changed?" bytes (e.g. `0x200008F5`, `0x20000914`, `0x20000A84`) and only when set calls the text/glyph primitives:
|
||||
- `draw_string @0x08008A50` — `void draw_string(u8 y_page /*r0*/, u8 x /*r1*/, const char* s /*r2*/, u8 len /*r3*/, u8 mode /*[sp+0x28]*/)`; mode 0=normal,1=inverse,2=outline. (Confirmed head: `mov sl,r0; mov r7,r1; mov r5,r2; mov fp,r3`.)
|
||||
- `0x08008D62` — bitmap/icon blitter (`draw_bitmap(mode,x,y,const u8* bmp)`)
|
||||
- `0x080089AC`, `0x080085D6`, `0x08008798` — box/line/clear helpers.
|
||||
3. The framebuffer is flushed to the LCD over SPI2 by the period-gated tasks; the ASCII glyph blitter `0x08007FB8` and wide/GBK blitter `0x08008454` pull font bitmaps from SPI flash (per facts).
|
||||
|
||||
**Consequence for a rewrite:** because draw is gated behind dirty flags and a `tbb` on `g_screen`, replacing the dispatched routine (or the dispatcher call) cleanly takes over rendering for that screen without fighting the stock partial-redraw logic — as long as your code marks the whole area dirty / clears+redraws each frame itself.
|
||||
|
||||
---
|
||||
|
||||
## 5. Recommended HOOK POINTS for a custom UI router
|
||||
|
||||
Goal: our code owns **rendering + navigation + which key does what**, while calling stock lower-level APIs (draw_string `0x08008A50`, keypad `0x08005A14`, RF/DMR/codeplug helpers, and — untouched — the serial/CPS engine). Three options, most-preferred first.
|
||||
|
||||
### Option A (recommended): redirect the two scheduler dispatch calls
|
||||
Patch the two `bl` instructions in `ui_tick_normal`:
|
||||
|
||||
| patch site | stock instr | change to |
|
||||
|---|---|---|
|
||||
| **`0x080207E6`** | `bl 0x0801E1BC` (ui_draw_dispatch) | `bl my_draw_router` |
|
||||
| **`0x08020816`** | `bl 0x0801E6EC` (ui_process_key) | `bl my_key_router` |
|
||||
|
||||
- Your `my_key_router` reads the same key struct at `0x20000B57` (+2 keycode, +3 keystate) — call stock `0x08005A14` first if you want the stock debounce, or read raw. After handling, write `0xFF` to `0x20000B57+2` to consume, exactly as stock does at `0x0801E806`.
|
||||
- Your `my_draw_router` renders via `draw_string @0x08008A50` and the blit/clear helpers, keyed on your own screen model. You may keep or ignore `g_screen@0x200008B3`.
|
||||
- **Everything else in the superloop and scheduler is preserved**, so RF, DMR RX, scan, battery, and the entire serial/CPS + SPI region-write path (`0x080213B8` else-branch, `0x0801F854`, `0x0801FE50`) keep working unchanged. This is the minimal, cleanest cut: **2 instructions**.
|
||||
- Keep `0x080207EA bl 0x0801E050` (side-key/long-press handler) if you still want stock side-key semantics, or repoint it too for full control.
|
||||
|
||||
### Option B: replace the two `tbb` jump tables (per-screen, incremental)
|
||||
Repoint individual entries in the draw table (`@0x0801E1CE`, 12 bytes) and input table (`@0x0801E784`, 12 bytes) to your own handlers, one screen at a time. Because entries are **1-byte half-word offsets from the table base**, a target must be within `+0..+0x1FE` of the base; to jump far, keep a stock case as a 2-instruction trampoline (`b.w my_handler`) inside range. This lets you migrate screens gradually while stock screens still work. More fiddly than Option A.
|
||||
|
||||
### Option C: own the screen id + provide new handlers
|
||||
Keep the dispatchers, but treat `0x200008B3` as your state var and point all 12 draw/input slots at your dispatch trampolines. Effectively Option B for all 12 at once; no advantage over Option A.
|
||||
|
||||
### Lower-level APIs to reuse (stable call targets)
|
||||
| purpose | vaddr | prototype |
|
||||
|---|---|---|
|
||||
| draw text | `0x08008A50` | `draw_string(u8 y_page, u8 x, const char* s, u8 len, u8 mode@sp+0x28)` |
|
||||
| draw icon/bitmap | `0x08008D62` | `draw_bitmap(u8 mode, u8 x, u8 y, const u8* bmp)` |
|
||||
| ascii glyph blit | `0x08007FB8` | (per facts; SPI font @0x19C000) |
|
||||
| wide/GBK glyph blit | `0x08008454` | (per facts; SPI font @0x19E000) |
|
||||
| keypad scan/debounce | `0x08005A14` | `void keypad_scan(void)` (fills `0x20000B57`) |
|
||||
| SPI codeplug read | `0x08021828` | `spi_read(dst, byteaddr, len)` — **keep format** |
|
||||
| menu-open helper | `0x0801AD9C` | builds top-level menu list (call if reusing stock menu data) |
|
||||
| reboot | `0x0801A38C` | `nvic_system_reset()` |
|
||||
|
||||
**Do NOT touch** (serial/CPS + codeplug boundary — hard constraint): the superloop else-branch `0x080213B8..0x0802140A`, `serial_poll 0x0801F854`, the framer/dispatcher `0x0801F84C / 0x08019790 / 0x080188D4`, and the SPI region-write engine. These implement the stock CPS protocol and the on-flash codeplug format; leaving them byte-identical is what keeps the stock Radtel CPS working.
|
||||
|
||||
---
|
||||
|
||||
## 6. Key RAM state variables (UI)
|
||||
|
||||
| addr | width | name | role |
|
||||
|---|---|---|---|
|
||||
| `0x200008B3` | u8 | `g_screen` | current UI screen id (0..11) — **the state machine selector** |
|
||||
| `0x200008B2` | u8 | `g_screen_prev` | previous screen (back/restore) |
|
||||
| `0x20000B57` | struct | `g_key` | key event: +2 keycode, +3 keystate(1=press) |
|
||||
| `0x20000C3D` | u8 | `g_sidekey` | side/long-press keycode consumed by `0x0801E050` |
|
||||
| `0x20000BF1..BF8` | u8/u16 | scheduler down-counters | throttle slow periodic tasks in `ui_tick_normal` |
|
||||
| `0x20002512` | u8[8+] | dirty-flag row | per-field "needs redraw" flags (cleared by `0x080136E4`) |
|
||||
| `0x20000C54` | u8 | reboot request | superloop → `nvic_system_reset` |
|
||||
| `0x20000B66 / 0x20000C12 / 0x20000C57` | u8 | serial-session mode gates | select UI vs CPS/SPI-write path (**do not repurpose**) |
|
||||
|
||||
---
|
||||
|
||||
## 7. Confidence
|
||||
|
||||
- **HIGH** — superloop (`0x0802136C`), normal-UI scheduler (`0x080207DC`), the single screen-state var `0x200008B3`, and the two mirror `tbb` dispatchers (draw `0x0801E1BC`, input `0x0801E6EC`) with their 12-entry tables. All directly disassembled and cross-checked (55 xrefs to the state var; both tbb tables decoded; the Home→Menu transition traced end-to-end).
|
||||
- **HIGH** — the recommended hook (patch `bl` at `0x080207E6` and `0x08020816`); both call sites verified in the scheduler disassembly, and the serial/CPS boundary is cleanly separated in the superloop.
|
||||
- **MEDIUM** — the *English names* assigned to screen ids 3/4/5/6/8/9/11: the dispatch structure and handler vaddrs are certain, but exact screen semantics are inferred from handler behaviour (digit ranges, bitmap vs text, forwarding) rather than from a label string on each. ids 0/7 (Home), 2 (standby-key), 10 (Menu), 11 (SMS/editor) are well-anchored.
|
||||
Ссылка в новой задаче
Block a user