-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtinyred_min.c
More file actions
47 lines (47 loc) · 10.1 KB
/
tinyred_min.c
File metadata and controls
47 lines (47 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#ifdef USE_SDL
#include <SDL.h>
#endif
typedef uint8_t B;typedef uint16_t W;typedef uint32_t D;
B *rom,v[8192],w[8192],e[32768],o[160],io[128],h[127],A,F,Bc,C,Dd,E,H,L,ie,ime,halt,keys=0xff,frm,st;W pc=0x100,sp=0xfffe;int rs,lo=1,hi,md,ramon,divc,timc,lcdc,rbanks=2,hold[8],gui,quit;B pix[160*144];struct termios old;
#ifdef USE_SDL
SDL_Window*win;SDL_Renderer*ren;SDL_Texture*tex;D fb[160*144],col[]={0xffffffff,0xffaaaaaa,0xff555555,0xff000000};
#endif
enum{Z=128,N=64,HH=32,CC=16};
W HL(){return H<<8|L;}W BC(){return Bc<<8|C;}W DE(){return Dd<<8|E;}void SHL(W x){H=x>>8;L=x;}void SBC(W x){Bc=x>>8;C=x;}void SDE(W x){Dd=x>>8;E=x;}
int bank0(){return 0;}int bank(){int b=lo%rbanks;return b?b:1;}B pal(B p,B c){return p>>(c*2)&3;}B STAT(){return 128|(io[65]&120)|(io[68]==io[69]?4:0)|(!(io[64]&128)?0:io[68]>143?1:lcdc<80?2:lcdc<252?3:0);}
B R(W a){if(a<0x4000)return rom[(bank0()*0x4000+a)%rs];if(a<0x8000)return rom[(bank()*0x4000+(a&0x3fff))%rs];if(a<0xa000)return v[a&8191];if(a<0xc000)return ramon&&hi<4?e[(hi*8192+(a&8191))&32767]:255;if(a<0xe000)return w[a&8191];if(a<0xfe00)return w[a&8191];if(a<0xfea0)return o[a&255];if(a<0xff00)return 255;if(a==0xff00){B r=io[0]&0xf0,n=15;if(!(r&16))n&=(keys>>4)&15;if(!(r&32))n&=keys&15;return 192|r|n;}if(a==0xff41)return STAT();if(a<0xff80)return io[a&127];if(a<0xffff)return h[a&127];return ie;}
void Wb(W a,B x){if(a<0x2000)ramon=(x&15)==10;else if(a<0x4000){lo=x&127;if(!lo)lo=1;}else if(a<0x6000)hi=x;else if(a<0x8000);else if(a<0xa000)v[a&8191]=x;else if(a<0xc000){if(ramon&&hi<4)e[(hi*8192+(a&8191))&32767]=x;}else if(a<0xe000)w[a&8191]=x;else if(a<0xfe00)w[a&8191]=x;else if(a<0xfea0)o[a&255]=x;else if(a>=0xff00&&a<0xff80){if(a==0xff00)io[0]=x&0x30;else if(a==0xff04)io[4]=0;else if(a==0xff41)io[65]=x&120;else if(a==0xff44)io[68]=0;else if(a==0xff46)for(int i=0;i<160;i++)o[i]=R((x<<8)+i);else io[a&127]=x;}else if(a<0xffff)h[a&127]=x;else ie=x;}
W Rw(){B l=R(pc++),h=R(pc++);return h<<8|l;}void Ww(W a,W x){Wb(a,x);Wb(a+1,x>>8);}void push(W x){Wb(--sp,x>>8);Wb(--sp,x);}W pop(){B l=R(sp++),h=R(sp++);return h<<8|l;}
B gr(int r){return r==0?Bc:r==1?C:r==2?Dd:r==3?E:r==4?H:r==5?L:r==6?R(HL()):A;}void sr(int r,B x){if(r==0)Bc=x;else if(r==1)C=x;else if(r==2)Dd=x;else if(r==3)E=x;else if(r==4)H=x;else if(r==5)L=x;else if(r==6)Wb(HL(),x);else A=x;}W rp(int r){return r==0?BC():r==1?DE():r==2?HL():sp;}void srp(int r,W x){if(r==0)SBC(x);else if(r==1)SDE(x);else if(r==2)SHL(x);else sp=x;}W rp2(int r){return r==0?BC():r==1?DE():r==2?HL():A<<8|F;}void srp2(int r,W x){if(r==0)SBC(x);else if(r==1)SDE(x);else if(r==2)SHL(x);else{A=x>>8;F=x&240;}}
void alu(int f,B x){int a=A,r,c=F&CC;if(f==0)r=a+x,F=(r&256?CC:0)|((a^x^r)&16?HH:0),A=r;else if(f==1)r=a+x+(c>0),F=(r&256?CC:0)|((a^x^r)&16?HH:0),A=r;else if(f==2)r=a-x,F=N|(r<0?CC:0)|((a^x^r)&16?HH:0),A=r;else if(f==3)r=a-x-(c>0),F=N|(r<0?CC:0)|((a^x^r)&16?HH:0),A=r;else if(f==4)A&=x,F=HH;else if(f==5)A^=x,F=0;else if(f==6)A|=x,F=0;else r=a-x,F=N|(r<0?CC:0)|((a^x^r)&16?HH:0);if(f<7&&f!=2&&f!=3)F&=~N;if(!((f==7?r:A)&255))F|=Z;}
void cb(){B op=R(pc++),r=op&7,y=op>>3&7,x=op>>6,a=gr(r),n=a,c=F&CC;if(x==0){if(y==0)n=a<<1|a>>7,F=a>>7?CC:0;else if(y==1)n=a>>1|a<<7,F=a&1?CC:0;else if(y==2)n=a<<1|(c>0),F=a>>7?CC:0;else if(y==3)n=a>>1|(c?128:0),F=a&1?CC:0;else if(y==4)n=a<<1,F=a>>7?CC:0;else if(y==5)n=a>>1|(a&128),F=a&1?CC:0;else if(y==6)n=a<<4|a>>4,F=0;else n=a>>1,F=a&1?CC:0;if(!n)F|=Z;sr(r,n);}else if(x==1)F=(a&(1<<y)?0:Z)|HH|(F&CC);else{if(x==2)n=a&~(1<<y);else n=a|1<<y;sr(r,n);}}
int cond(int c){return c==0?!(F&Z):c==1?F&Z:c==2?!(F&CC):F&CC;}
int step(){int ii=ie&io[15]&31;if(ii)halt=0;if(ime&&ii){int i=0;while(!(ii&(1<<i)))i++;ime=0;io[15]&=~(1<<i);push(pc);pc=0x40+i*8;return 5;}if(halt)return 1;B op=R(pc++),y;int r,x,c;D q;int8_t s;if(op>=0x40&&op<0x80){if(op==0x76)halt=1;else sr(op>>3&7,gr(op&7));return (op&7)==6||(op>>3&7)==6?2:1;}if(op>=0x80&&op<0xc0){alu(op>>3&7,gr(op&7));return (op&7)==6?2:1;}if((op&199)==6){sr(op>>3&7,R(pc++));return (op>>3&7)==6?3:2;}if((op&199)==4){r=op>>3&7;x=gr(r);y=x+1;sr(r,y);F=(F&CC)|(!y?Z:0)|((x&15)==15?HH:0);return r==6?3:1;}if((op&199)==5){r=op>>3&7;x=gr(r);y=x-1;sr(r,y);F=(F&CC)|N|(!y?Z:0)|((x&15)==0?HH:0);return r==6?3:1;}if((op&207)==1){srp(op>>4&3,Rw());return 3;}if((op&207)==3){r=op>>4&3;srp(r,rp(r)+1);return 2;}if((op&207)==11){r=op>>4&3;srp(r,rp(r)-1);return 2;}if((op&207)==9){q=HL()+rp(op>>4&3);F=(F&Z)|(((HL()&4095)+(rp(op>>4&3)&4095))>4095?HH:0)|(q>65535?CC:0);SHL(q);return 2;}switch(op){case 0:return 1;case 2:Wb(BC(),A);return 2;case 18:Wb(DE(),A);return 2;case 34:Wb(HL(),A);SHL(HL()+1);return 2;case 50:Wb(HL(),A);SHL(HL()-1);return 2;case 10:A=R(BC());return 2;case 26:A=R(DE());return 2;case 42:A=R(HL());SHL(HL()+1);return 2;case 58:A=R(HL());SHL(HL()-1);return 2;case 7:F=A>>7?CC:0;A=A<<1|A>>7;return 1;case 15:F=A&1?CC:0;A=A>>1|A<<7;return 1;case 23:x=F&CC;F=A>>7?CC:0;A=A<<1|(x>0);return 1;case 31:x=F&CC;F=A&1?CC:0;A=A>>1|(x?128:0);return 1;case 39:x=0;c=F&CC;if(!(F&N)){if((F&HH)||(A&15)>9)x=6;if(c||A>153)x|=96,c=CC;A+=x;}else{if(F&HH)x=6;if(c)x|=96;A-=x;}F=(F&N)|c|(!A?Z:0);return 1;case 47:A^=255;F|=N|HH;return 1;case 55:F=(F&Z)|CC;return 1;case 63:F=(F&Z)|((F&CC)?0:CC);return 1;case 8:q=Rw();Ww(q,sp);return 5;case 24:pc+=(int8_t)R(pc)+1;return 3;case 32:case 40:case 48:case 56:s=R(pc++);if(cond(op>>3&3)){pc+=s;return 3;}return 2;case 16:pc++;return 1;case 203:cb();return 2;case 195:pc=Rw();return 4;case 201:pc=pop();return 4;case 217:pc=pop();ime=1;return 4;case 233:pc=HL();return 1;case 205:q=Rw();push(pc);pc=q;return 6;case 197:case 213:case 229:case 245:push(rp2((op>>4)&3));return 4;case 193:case 209:case 225:case 241:srp2((op>>4)&3,pop());return 3;case 198:case 206:case 214:case 222:case 230:case 238:case 246:case 254:alu(op>>3&7,R(pc++));return 2;case 224:Wb(0xff00+R(pc++),A);return 3;case 226:Wb(0xff00+C,A);return 2;case 234:q=Rw();Wb(q,A);return 4;case 240:A=R(0xff00+R(pc++));return 3;case 242:A=R(0xff00+C);return 2;case 250:q=Rw();A=R(q);return 4;case 232:s=R(pc++);q=sp+s;F=((sp^s^q)&16?HH:0)|((sp^s^q)&256?CC:0);sp=q;return 4;case 248:s=R(pc++);q=sp+s;F=((sp^s^q)&16?HH:0)|((sp^s^q)&256?CC:0);SHL(q);return 3;case 249:sp=HL();return 2;case 243:ime=0;return 1;case 251:ime=1;return 1;}if((op&199)==192){if(cond(op>>3&3)){pc=pop();return 5;}return 2;}if((op&199)==194){q=Rw();if(cond(op>>3&3)){pc=q;return 4;}return 3;}if((op&199)==196){q=Rw();if(cond(op>>3&3)){push(pc);pc=q;return 6;}return 3;}if((op&199)==199){push(pc);pc=op&56;return 4;}return 1;}
void line(){int ly=io[68];if(!(io[64]&128)||ly>143)return;for(int x=0;x<160;x++){int wx=x-(io[75]-7),wy=ly-io[74],win=(io[64]&32)&&wy>=0&&wx>=0,px=win?wx:x+io[67],py=win?wy:ly+io[66],map=(win?(io[64]&64):(io[64]&8))?0x1c00:0x1800,t=v[map+((py>>3)&31)*32+((px>>3)&31)],ad=(io[64]&16)?t*16:0x1000+(int8_t)t*16,row=(py&7)*2,bit=7-(px&7),c=(v[(ad+row)&8191]>>bit&1)|((v[(ad+row+1)&8191]>>bit&1)<<1),bg=c,sh=pal(io[71],c),bs=999,bp=sh;if(!(io[64]&1))bg=sh=bp=0;if(io[64]&2)for(int i=0,n=0;i<40;i++){int sy=o[i*4]-16,sx=o[i*4+1]-8,tt=o[i*4+2],fl=o[i*4+3],sz=(io[64]&4)?16:8,xx=x-sx,yy=ly-sy;if(yy<0||yy>=sz)continue;if(n++==10)break;if(xx<0||xx>7)continue;if(fl&32)xx=7-xx;if(fl&64)yy=sz-1-yy;if(sz==16)tt&=254;int a=tt*16+yy*2,bb=7-xx,cc=(v[(a)&8191]>>bb&1)|((v[(a+1)&8191]>>bb&1)<<1);if(cc&&(!(fl&128)||!bg)&&sx<bs)bs=sx,bp=pal(io[(fl&16)?73:72],cc);}pix[ly*160+x]=bp;}}
void tick(int c){static int ps[]={1024,16,64,256},si[]={8,16,32,0};c*=4;divc+=c;while(divc>=256)divc-=256,io[4]++;if(io[7]&4){int p=ps[io[7]&3];timc+=c;while(timc>=p){timc-=p;if(++io[5]==0)io[5]=io[6],io[15]|=4;}}if(io[64]&128){lcdc+=c;while(lcdc>=456){lcdc-=456;if(++io[68]==144)io[15]|=1,frm=1;if(io[68]>153)io[68]=0;if(io[68]<144)line();}}else io[68]=lcdc=0,memset(pix,0,sizeof pix);B n=STAT();if(((n^st)&7)&&(((n&4)&&(io[65]&64))||(io[65]&si[n&3])))io[15]|=2;st=n;}
void down(int i){hold[i]=8;}void keysin(){
#ifdef USE_SDL
SDL_Event e;while(gui&&SDL_PollEvent(&e))if(e.type==SDL_QUIT)quit=1;if(gui){const Uint8*k=SDL_GetKeyboardState(0);if(k[SDL_SCANCODE_Q]||k[SDL_SCANCODE_ESCAPE])quit=1;if(k[SDL_SCANCODE_J]||k[SDL_SCANCODE_X])down(0);if(k[SDL_SCANCODE_K]||k[SDL_SCANCODE_Z])down(1);if(k[SDL_SCANCODE_SPACE])down(2);if(k[SDL_SCANCODE_RETURN]||k[SDL_SCANCODE_KP_ENTER])down(3);if(k[SDL_SCANCODE_D]||k[SDL_SCANCODE_RIGHT])down(4);if(k[SDL_SCANCODE_A]||k[SDL_SCANCODE_LEFT])down(5);if(k[SDL_SCANCODE_W]||k[SDL_SCANCODE_UP])down(6);if(k[SDL_SCANCODE_S]||k[SDL_SCANCODE_DOWN])down(7);}
#endif
char c;while(!gui&&read(0,&c,1)>0){if(c=='q'){quit=1;break;}if(c=='j'||c=='x')down(0);if(c=='k'||c=='z')down(1);if(c==' ')down(2);if(c=='\n'||c==13)down(3);if(c=='d')down(4);if(c=='a')down(5);if(c=='w')down(6);if(c=='s')down(7);}keys=255;for(int i=0;i<8;i++)if(hold[i])hold[i]--,keys&=~(1<<i);}
void show(){
#ifdef USE_SDL
if(gui){for(int i=0;i<160*144;i++)fb[i]=col[pix[i]&3];SDL_UpdateTexture(tex,0,fb,160*4);SDL_RenderClear(ren);SDL_RenderCopy(ren,tex,0,0);SDL_RenderPresent(ren);return;}
#endif
static char ramp[]=" .:-=+*#%@";printf("\033[H");for(int y=0;y<144;y+=2){for(int x=0;x<160;x+=2){int c=pix[y*160+x]+pix[y*160+x+1]+pix[(y+1)*160+x]+pix[(y+1)*160+x+1];putchar(ramp[c*9/12]);}putchar('\n');}fflush(stdout);}
int main(int ac,char**av){char*path=ac>1?av[1]:"pokemon-red.gb";FILE*f=fopen(path,"rb");if(!f)return perror(path),puts("usage: ./tinyred pokemon-red.gb"),1;fseek(f,0,2);rs=ftell(f);rewind(f);rom=malloc(rs);fread(rom,1,rs,f);fclose(f);rbanks=rs/0x4000;memset(e,255,sizeof e);A=1;F=0xb0;C=0x13;E=0xd8;H=1;L=0x4d;io[0]=0xcf;io[64]=0x91;io[71]=0xfc;io[72]=io[73]=0xff;io[38]=0xf1;
#ifdef USE_SDL
gui=ac<3;if(gui){SDL_Init(SDL_INIT_VIDEO);win=SDL_CreateWindow("tinyred",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,576,0);ren=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED);tex=SDL_CreateTexture(ren,SDL_PIXELFORMAT_ARGB8888,SDL_TEXTUREACCESS_STREAMING,160,144);}
#endif
if(!gui){tcgetattr(0,&old);struct termios raw=old;raw.c_lflag&=~(ICANON|ECHO);tcsetattr(0,0,&raw);fcntl(0,F_SETFL,O_NONBLOCK);printf("\033[2J\033[?25l");}for(int n=ac>2?atoi(av[2]):0;!quit;){frm=0;for(int z=0;!frm&&!quit&&z<17556;){int c=step();tick(c);z+=c;}keysin();if(!n||n==1)show();if(n&&!--n)break;if(!gui)usleep(16000);}if(!gui)printf("\033[?25h\033[0m\n"),tcsetattr(0,0,&old);
#ifdef USE_SDL
if(gui)SDL_Quit();
#endif
}