abse/gameboy-advance

A tscircuit package created by abse.

Version
0.0.45
License
unset
Stars
0

firmware/game-console/tests/test_adaptive_scan.py

"""Compile the actual field planner and scanline dispatcher against a mock LCD."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[1]
s=(root/'doom_port.c').read_text()
a=s.index('        bool full=force_redraw');b=s.index('        f->sequence',a)
plan=s[a:b]
a=s.index('        for(unsigned i=0;i<f->region_count;i++)');b=s.index('        display_us_total',a)
draw=s[a:b]
h='''#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
struct dirty_region { uint16_t x0,y0,x1,y1; };
struct frame { uint8_t pixels[320*107]; uint32_t palette[256]; struct dirty_region regions[250]; unsigned region_count; } queue[3];
static uint8_t DG_ScreenBuffer[64000];
static uint32_t lcd[480*320];
static bool field_valid[3],force_redraw;
static unsigned producer_field;
static int adaptive_scan=1,lcd_large=1,current_slot;
void lcd_indexed_scanline(const uint8_t *p,const uint32_t *pal,unsigned y,unsigned x0,unsigned x1){
 unsigned w=lcd_large?480:320,h=lcd_large?320:240;
 assert(x0<x1 && x1<=w && y<h && ((x1-x0)*3)%4==0 && (y%3)==current_slot);
 for(unsigned x=x0;x<x1;x++)lcd[y*w+x]=pal[p[x*320/w]];
}
void lcd_frame_region(const uint8_t *p,const uint32_t *pal,unsigned a,unsigned b,unsigned c,unsigned d){assert(0);}
static void submit(uint32_t *rgb) {
 unsigned slot=producer_field;current_slot=slot; struct frame *f=&queue[slot];
'''+plan.replace('sizeof(rgb)','256*sizeof(uint32_t)')+'''

'''+draw+'''
}
int main(){
 uint32_t pal[256];for(int i=0;i<256;i++)pal[i]=i;
 for(int test=0;test<40;test++){
  if(test==20){lcd_large=0;field_valid[0]=field_valid[1]=field_valid[2]=false;}
  unsigned w=lcd_large?480:320,h=lcd_large?320:240;
  for(int i=0;i<64000;i++)if(test%3==0 || i%117==0)DG_ScreenBuffer[i]=(i*37+test*13)%256;
  if(test==6)pal[45]=999;
  submit(pal);submit(pal);submit(pal);
  for(unsigned y=0;y<h;y++)for(unsigned x=0;x<w;x++)
   assert(lcd[y*w+x]==pal[DG_ScreenBuffer[(y*200/h)*320+x*320/w]]);
  submit(pal);assert(queue[current_slot].region_count==0);
  submit(pal);assert(queue[current_slot].region_count==0);
 }
}
'''
with tempfile.TemporaryDirectory() as tmp:
 c=Path(tmp)/'test.c';c.write_text(h);exe=Path(tmp)/'test'
 subprocess.run(['cc','-O2',str(c),'-o',str(exe)],check=True)
 subprocess.run([str(exe)],check=True)
print('40 field-history cases passed: full/sparse/palette/resize, convergence, DMA alignment, parity and unchanged-frame skip')