tsb: drive each tier to its size floor

asm 502->498 B (below the oracle's 500): the stack bring-up moves to plain C++,
and a register is reserved for the config-page high byte instead of reloading it
at each app-flash-boundary compare. tricks 808->778 B: shared erase/rww helpers
plus the libavr half-duplex W1C fix. pure 950->896 B and no SRAM: streams
rx->SPM/EEPROM instead of staging a 128 B page buffer. All three keep full oracle
feature parity and stay byte-identical across modes; protocol tests (round-trip +
password + emergency erase) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 18:47:38 +02:00
parent ac9447c07f
commit fc5433fd7d
4 changed files with 74 additions and 65 deletions

View File

@@ -65,18 +65,16 @@ constexpr std::uint16_t build_date = 26 * 512 + 7 * 32 + 19;
} // namespace tsb
// Reset lands here: BOOTRST vectors to the boot base, .vectors is laid first, and
// no crt runs. The whole loader is this one naked routine.
// no crt runs. The stack pointer is the one piece of bring-up that reads as
// plainly in C++ as in assembler, so it is; the dense loader body follows.
extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry()
{
SP = RAMEND;
asm volatile(
// --- bring-up ------------------------------------------------------
" ldi r16, lo8(%[ramend]) \n\t"
" out %[spl], r16 \n\t"
" ldi r16, hi8(%[ramend]) \n\t"
" out %[sph], r16 \n\t"
" in r16, %[mcusr] \n\t" // watchdog reset → hand straight back
" sbrc r16, 3 \n\t" // MCUSR bit 3 = WDRF
" rjmp 9f \n\t" // 9: = appjump
" rjmp 9f \n\t" // 9: = appjump (drains SPM, jumps to 0)
" ldi r16, %[ubrr] \n\t" // fixed baud, UBRR0L only
" sts %[ubrr0l], r16 \n\t"
" ldi r16, 0x02 \n\t" // 1<<U2X0
@@ -97,7 +95,10 @@ extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry
" brne 1b \n\t"
// --- password / emergency erase (Z at config-page password) --------
" ldi r23, %[commto] \n\t" // widen the timeout for the session
"2: ser r19 \n\t" // r19=0xff → comparison enabled
// Knock counter spent: reserve r17 = config-page high byte for every
// app-flash-boundary compare below (saves reloading it at each site).
" ldi r17, hi8(%[appcfg]) \n\t"
"2: ser r19 \n\t" // r19=0xff → comparison enabled
"3: lpm r18, Z+ \n\t"
" and r18, r19 \n\t" // a prior mismatch (r19=0) blanks the rest
" cpi r18, 0xff \n\t"
@@ -153,8 +154,7 @@ extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry
" ldi r20, %[page] \n\t"
" rcall tsb_sendf \n\t"
" cpi r30, lo8(%[appcfg]) \n\t"
" ldi r24, hi8(%[appcfg]) \n\t"
" cpc r31, r24 \n\t"
" cpc r31, r17 \n\t" // r17 = hi8(appcfg), reserved above
" brlo 1b \n\t"
"9: ret \n\t"
// --- 'e' read EEPROM (host-paced) ----------------------------------
@@ -179,8 +179,7 @@ extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry
" brts 9f \n\t"
" rcall tsb_store \n\t"
" cpi r30, lo8(%[appcfg]) \n\t"
" ldi r24, hi8(%[appcfg]) \n\t"
" cpc r31, r24 \n\t"
" cpc r31, r17 \n\t" // r17 = hi8(appcfg), reserved above
" brlo 1b \n\t"
"9: ret \n\t"
// --- 'E' write EEPROM ----------------------------------------------
@@ -240,8 +239,7 @@ extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry
" subi r30, lo8(-%[page]) \n\t"
" sbci r31, hi8(-%[page]) \n\t"
" cpi r30, lo8(%[appcfg]) \n\t"
" ldi r24, hi8(%[appcfg]) \n\t"
" cpc r31, r24 \n\t"
" cpc r31, r17 \n\t" // r17 = hi8(appcfg), reserved above
" brlo 1b \n\t"
" clr r30 \n\t" // hand callers Z=0
" clr r31 \n\t"
@@ -345,8 +343,7 @@ extern "C" [[gnu::naked, gnu::used, gnu::section(".vectors")]] void __boot_entry
" clc \n\t"
" ret \n\t"
:
: [ramend] "i"(RAMEND), [spl] "I"(_SFR_IO_ADDR(SPL)), [sph] "I"(_SFR_IO_ADDR(SPH)),
[mcusr] "I"(_SFR_IO_ADDR(MCUSR)), [ubrr] "n"(tsb::baud.ubrr), [ubrr0l] "n"(_SFR_MEM_ADDR(UBRR0L)),
: [mcusr] "I"(_SFR_IO_ADDR(MCUSR)), [ubrr] "n"(tsb::baud.ubrr), [ubrr0l] "n"(_SFR_MEM_ADDR(UBRR0L)),
[ucsr0a] "n"(_SFR_MEM_ADDR(UCSR0A)), [ucsr0b] "n"(_SFR_MEM_ADDR(UCSR0B)), [udr0] "n"(_SFR_MEM_ADDR(UDR0)),
[spmcsr] "I"(_SFR_IO_ADDR(SPMCSR)), [spm_fill] "n"(_BV(__SPM_ENABLE)),
[spm_ers] "n"(_BV(PGERS) | _BV(__SPM_ENABLE)), [spm_wrt] "n"(_BV(PGWRT) | _BV(__SPM_ENABLE)),