from pathlib import Path
import re,subprocess,json
R=Path(__file__).resolve().parent;T=R/'host_test';T.mkdir(exist_ok=True)
D=R/'firmware/固件/lamp_target'
h=(D/'Device/Include/stm32g030xx.h').read_text();h=re.sub(r'^#include "(?:core_cm0plus|system_stm32g0xx)\.h"[^\n]*','',h,flags=re.M)
(T/'registers.h').write_text('#include <stdint.h>\n#define __IO volatile\n#define __I volatile const\n#define __O volatile\n'+h)
preamble='''#include "registers.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#undef GPIOA
#undef GPIOB
#undef RCC
#undef SYSCFG
GPIO_TypeDef a,b;
RCC_TypeDef r;
SYSCFG_TypeDef y;
#define GPIOA (&a)
#define GPIOB (&b)
#define RCC (&r)
#define SYSCFG (&y)
#define __DSB() ((void)0)
static unsigned before_mapping;
static void apply(GPIO_TypeDef *p) {p->ODR=(p->ODR & ~(p->BSRR>>16))|(p->BSRR&65535u); assert((a.ODR&((1u<<9)|(1u<<11)))==0);}
static void before_clear(void) {
 assert(((a.MODER>>22)&3)==1 && ((a.MODER>>18)&3)==1);
 assert(((a.MODER>>24)&3)==0 && ((a.MODER>>20)&3)==0);
 assert(((a.PUPDR>>24)&3)==2 && ((a.PUPDR>>20)&3)==2);
 assert((a.ODR&((1u<<9)|(1u<<11)))==0);before_mapping++;
}
'''
test='''
int main(void) {
 unsigned cases=0;
 for(unsigned seed=0;seed<2;seed++) for(unsigned remap=0;remap<4;remap++) {
 memset(&a,seed?255:0,sizeof a);memset(&b,seed?255:0,sizeof b);memset(&r,0,sizeof r);memset(&y,0,sizeof y);
 /* Reset only the ODR image at the preloading checkpoint, representing BSRR
    writes; high prior latches are allowed and tested before any mode change. */
 y.CFGR1=0x80u|((remap&1)?SYSCFG_CFGR1_PA11_RMP:0)|((remap&2)?SYSCFG_CFGR1_PA12_RMP:0);
 board_safe_boot();
 assert(((a.MODER>>22)&3)==1);assert((a.ODR&(1u<<11))==0);
 assert(((a.MODER>>24)&3)==0);assert(((a.PUPDR>>24)&3)==2);
 assert((y.CFGR1&(SYSCFG_CFGR1_PA11_RMP|SYSCFG_CFGR1_PA12_RMP))==0);
 assert((y.CFGR1&0x80u)!=0);
 assert((r.APBENR2&RCC_APBENR2_SYSCFGEN)!=0);
 assert(((a.MODER>>8)&3)==1 && (a.ODR&(1u<<4))==0);
 assert(((a.MODER>>14)&3)==1 && (a.ODR&(1u<<7))!=0);
 assert(((b.MODER>>18)&3)==1 && (b.ODR&(1u<<9))==0);
 assert(((a.PUPDR>>0)&3)==2 && ((b.PUPDR>>0)&3)==1);
 cases++;
 }
 assert(before_mapping==8);printf("PASS: %u boot cases; both remap bits, prior GPIO zero/all-ones; physical pin16 low and pin17 input pulldown; mapping staged before switch. MMIO model only.\\n",cases);
}
'''
# Instrument BSRR's real register side effect only; no replacement boot behavior.
def emit(src):
 s=src.read_text();s=s[s.index('static void gpio_drive'):s.index('void SysTick_Handler')]
 s=s.replace('port->BSRR = high ? (1u << pin) : (1u << (pin + 16u));','port->BSRR = high ? (1u << pin) : (1u << (pin + 16u));\n    apply(port);')
 s=s.replace('    __DSB();','    apply(GPIOA); apply(GPIOB);\n    __DSB();',1)
 s=s.replace('    SYSCFG->CFGR1 &=','    before_clear();\n    SYSCFG->CFGR1 &=')
 return preamble+s+test
results={}
for n,src in [('fixed',D/'src/main.c'),('original',R/'original_main.c')]:
 p=T/(n+'.c');p.write_text(emit(src));exe=T/n
 flags=['cc','-std=c11','-Wall','-Wextra','-Werror','-Wno-unused-function','-fsanitize=address,undefined','-I'+str(T),str(p),'-o',str(exe)]
 c=subprocess.run(flags,capture_output=True,text=True);assert c.returncode==0,c.stderr
 x=subprocess.run([str(exe)],capture_output=True,text=True);(R/(n+'_host_test.log')).write_text(x.stdout+x.stderr)
 if n=='fixed':assert x.returncode==0,x.stderr
 else:assert x.returncode!=0;assert 'a.MODER>>22' in x.stderr,x.stderr
 results[n]={'returncode':x.returncode,'expected_result':'PASS' if n=='fixed' else 'REJECTED_PHYSICAL_PIN16_ASSERTION'}
 print(n,x.returncode,x.stdout.strip())
(R/'boot_test_results.json').write_text(json.dumps(results,indent=2)+'\n')
