From 335839205a0d65767cc267f484b7a7aaed1e9aac Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 25 Nov 2025 19:01:03 +0100 Subject: [PATCH] upload of the files for pgb --- .clangd | 10 + Makefile | 31 + README.md | 0 pgb | Bin 0 -> 139608 bytes src/cpu.c | 2615 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 45 + src/pgb.h | 47 + src/ppu.c | 67 ++ src/timer.c | 37 + src/window.c | 53 + 10 files changed, 2905 insertions(+) create mode 100644 .clangd create mode 100644 Makefile create mode 100644 README.md create mode 100755 pgb create mode 100644 src/cpu.c create mode 100644 src/main.c create mode 100644 src/pgb.h create mode 100644 src/ppu.c create mode 100644 src/timer.c create mode 100644 src/window.c diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c04e5e1 --- /dev/null +++ b/.clangd @@ -0,0 +1,10 @@ +CompileFlags: + Add: + - -x + - c + - -Wall + - -Wextra + - -Werror + - -Wpedantic + - -std=c99 + - -Wno-empty-translation-unit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f236ec6 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CC = cc +CFLAGS = -Wall -Wextra -Wpedantic -Werror -std=c99 -lX11 + +SRCDIR = src +OBJDIR = obj +SRC = $(wildcard $(SRCDIR)/*.c) +OBJ = $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o) + +TARGET = pgb + +debug: CFLAGS += -g3 -fsanitize=address +debug: all + +release: CFLAGS += -O3 -s +release: all + +all: $(OBJDIR) $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(CFLAGS) -o $@ $^ + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJDIR) $(TARGET) + +.PHONY: clean all debug release diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pgb b/pgb new file mode 100755 index 0000000000000000000000000000000000000000..ed42e0f8792ca1ae0f6a62b5c21b5fdec1b8781d GIT binary patch literal 139608 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 q4nIAWI<#>-+ zt*)mbrKon1>Cpn*x}`~v>S=ZdMF#r;kDXRmH9N#<(kq0x6{Snw{dgZkkQ^&|gN@Hh zBU#?dcf^WcW=;*fe2E%v#h*$n!-#Z9LuhsL?z(cz?y6gKSKXq!>Q2;Ex1eS9E$9$L z1Bv&c@fYHt3aqR?237H`xxeZh?jss+L6hbyDDPI{o4_5HSaAZOZLuF;s=`d{C)|#g zz?T@drhx>6z93Ks&W3^2Azb&D+m4!q%+z~Dzz+wuof`fGv z%A&7v&(X&hC;48m6IYgD!VPR!paLozr>5i8EgdAl(?>oXhO)czCs0bg4PROo%7LZ5 zO6@_RKC1&uLx~%151|@=J{e_hL0)Z_E60Di6os%FvU5V>d4tp2qpoYB$YPc1D zxHP>HZfGlmCy#JC0ufLXAfP5df_i3iH5`eT(W@pz!el%wiLZo%#R;gnQI?M|665n& zQ?cA5F!77=;>61{k9c|J5iid?;?*;ca{8IaWPlQh52)D{P_rvV&8~o&SjdJGC~=SA z@T6Soe0C&dAp?v3gBoEu+R>oK)Vq#^Kg=8|rx%9>^P-(FOx2}GDW7cBImqYFX4wfL&P}A@Njcirk$XtOoBI#^5H`9 zls*8DcV$^RfA)wdS)i6nVCaJ#d18#T$!1! z%uH8irYkekRWmbL&&+X*M|`RCAd(wbhOsf%^mIv($*~u8acl76B=8=%*7q#)aEar= zflRI2VZ4%@Fl2&FUY5hf#uaW za={Nn*k4hd`&E+S?~<@`Y*yudnL(F!bK<*H;R!JK*#zj)}|MFd7ag5X9;myfclCq(J`_DEudh ze@-zquzVB>_mtibR8=QF3{#dAI^zg;;+x_%n1|58WpVVE>Cx2*ej2zM)$zmdlJp49 zKIPgT_z?Sn>#;Hg50>G+6L^Rz94gI=hor!>=s@rr$tN{%UjYi=rLxCW15aTN2ERLp zh4etLc`Vdpp+R8D-6({G(=0Fzc?BalRaN(!B`k<;HQd(lVCAJJ-TfM#fLo4*I_{^?Fiv?IxSydA?!`x?iK61XuD5xkDG=V0VL8Aj3?zS}yY_y&KW`xF102WLG2{8UoT1Y&q8Ofd$`M}; zYLz;TUZl8jb+KQqYB0@BUIdrRp|0dArCiF6#}N#c?ytzEsw$*9rAFL< z7;aoI#tz}7)MIGsEB72Wc1{B~7;%$-#`-3~fAoTX0uhe7X}`mfl$tZjIi|z8&n(Af zj4GtL`AFfoDpYkZn8NEg$)|>U8;-J{-K0B^T)MjvoL}6st59g*?tlirx(O^abNAp- z_)Qhsy6*sXQUwUPcVZ&{?k1JR0+Q+0z@#~)Jbm2H7dp;gs&JM2G#0I~hqMd2DUbNVe>h4r6I#PaaLa?xG; zNB?`#+V%S2pX+CFWxaj|{6#x>gFXU(JdMt6)SavI*Z&)iHtD0){`&ucMmFm^H~8z@ z?uH$r`xN&3>OO!*Ye-6hX<{x&Huly080Tt8z;(+&tu^a$ML3ruGq-HR?y|aR$&rXc9%C!_sS=DGDXEP>@tn=tu)N)Z~gnLu-%XKqrer!>jsm zptD7xkvfIebh;>X$Exdapz}qc6DwYV+jPPxbm#gTaYbj0LO-*y1qV816ndU?N9T+} zKU+_0LnnFf64Hk~yJJzx5!(?+3Fbw`1Vk3p<&2Hn?gBxW`)*UGh7-{0bif*@5o*`; zqrv*1tutx(1{{jIRqsQOk@zviD-9RG7WK8|uS$d1Xzx7DVpKFmk?fUouk7x1+=B~br7&&sF%~pK6E>8@$Hu_(1+xAM!ryF8E9|e)2||;{L7SR@@hOaJ=CR4sTe) zP9f#vYowpsU~NU2nIrAN8xW>P>_Vx`g+~~)ZT3kVEd}Q0Y@=J9$A%S?PA-EDmw#wJA zt&S}%v;9%FKMgozS};9C)=r;s?pa|w=A2o%LprwVdKq3o4+d5bj#u8&U=P>q^WV0S zZPnh2CwFd4HXe{(ueIm>IKM4a-tR_tNb-nxxa<7>WFyCY9ePv_*X#HB^t;O2yk{dc zhXo&AwwAr{Ho2iwMzXOHJwJe(H*Lf5eqdv>eF~sm^3;|ZVC2xY+J{@|II5K8-h1(zL7m#z5c>_xPQPMdedaE2@c=hh*vZ; zXfX4gH@Q$pmRkA`4YtF)t3kv575s`cw1m@;WFLZY8(+Cyx*$aps z32ds|zsK89IiUBNroagaVrqt<&UN?|{g>T|iCq3d{kqlVCtVm2 zMLz$M@dRABSP)nVqAZ+*APt;C6--*I2Vb{jNERHLoaJeDVVUWloc3_BBRRG$x5A`4 zP=qBK8X#U=nq66rwN)&ssUv9nAXgMO0^U}#I|cey~9y)!(+QmSnH1 zhQ#Y#a$K+F?*>g78>NRL_}ITqI8-2NjFpF*T|mM%^dk8-kyu+DGz)1vBKZdAyvQL- zv~2Q5kwqR-@x7rLLJk(_XNhgU|3@oV@P{T0{;)&q89{gaMe&CSp$|@w4_29vp2@G{ zFW|p1usQ^6soMj+Yt^p(YTFa)=1Fz^0mR1rp^|C}9aK9#$NS~{0o8h|@<&wH8I}C58V&TX zRkt2g!MaAZx}>g~RKWues#TAv;H2_Ds9FxGq~NQQAOnUfZ8qnxaEvmUbmm`Xneg| zaYVN|XXQJRup_OCaqyq*gqM4sIjW1V5$LU@IM{ z+s2;|Ko!Cu${twrh4MEa@Vv@>bKO4a$;HdQ`Rp@%^UgyYa(VCM6&x&JeMxP&O>O*? zYAEZ!=Z^YIYTa#DYvblaYSpJyNZL?v&j^GWX#_Cz4D`w{UHvXiv<5fTAtbQ9zXG;T zsMVjkhG*-*2cbIe!+Xv_7<@sk`+^Eyyrh<2vSU!E>(;4;uc=LssdW#i&_1OWmH!(` z?NR=(sis3}-4kVWdq8cyWPq%ebMyyv2hrIl)V8x~VpK z$XM}bhOgRsn_B-xwXvkh`6;!GSN5rukb<97>v(WPwcsaEw^ju;pk27DWkB#zwe>V< zW(RLJ4SZ779#r9yTA>@DFt}0!H|$p{pRDr2^_SGL@2cQIw&Fpzc`cr*eX`11_!N74 z`Nyih3}XpC_{`q4=91d_fLeCnW#4t95c>5)rn_?AA9> zj{(6|ZrG!5kcPZBKq=~HE^~wboVxB#6+WVF2K^mWO-IzulDgjRY&fWH0`Zkp>mk+l zm>L2UZ#ty*eqP=BnA)*l?f<+w^cWRWC%En?5DxXzU3WkYW4HS@wd!dVT;$tBIIa)X zomRCx!a>7XIcYFNN$+^HlRmB}{WQ$o@3n z`K;S|4xL@U6tdTb&1Zerb0C9f{my6o;B)BghNY0*u#{$hnx}T1RNJ-uxw15<^3G=s zplAKybAW99og&%%6tMLtpZ)&old4|B%aPa5rMb&Jd$rg6*J!9SD{y%=pgjk)d+~I& z*ZjYPn>#OEI$8d|TJD0e;*vhBxZ(fV7M5KN4&^sgfkXKXOM$7%Z}?AYA>s(j$fZk> z6OSnW*VXlBCAiUeLc0R0uB2Aoua=e6x?ueS)-M#_?0~8dp1-J~b1JUI<}Uv5>;?Fc zb4%{U*^XI>Gr=UT58eY?YF7WQFu$FzQ1uU~mJx0@%XWoqJBL;M!>Z-w+-{cbircw= zeQ;S|(61c@iIO0=K}$UV!@RyOcS$v!RIR#in@rz$mBIyAjIbBq7x&8*7{{AVDj5j+ zD9?aL9^wD$hLgxg>$`@Kl<4qY;lkV8#VzU z`}j|*6RYo3^^5;uU;yMQ#v#z`B+wpL1A6=nMq>Q*>fo6WD8YRK>EJ$*Mn5HJRO`#i zJ9YfyDYfOO>c*SVt0cQ~QK$sHB7V^;>D)dEJE2#q{u`>{n5su?<(ZO`+Vn&vF+Cg9 zt~hBszVfJAbyTfn-=9(&a4_gOSaC*e{F?T~FY1`t0kyIY(J$u-@zal}7@xnWeZ?8I z;e@9dug8CpZ&+~@!KR94X8;T~5pqM5mz3MdX9wMCoS-%#E}=VXneo}c4;HRGezC5R ztoeT{9jF$^f4>ET23d+cI3VFUS7x}7C;0tGcxzpGC{r$I z;0_T#m;FX#FnS2u+nj3vH5`4!F>mSPtaL2Rn~tJzjrh>1%L8Ii!gX?Pzk)UFwgOX7 z@;BBVy`;80Qhr9=bm=NDV!wbMlZv;lC)F}(Wr@3{Ha${lGkWcf$`QMk$kj&-KTq;m zaBm43E0Z57H)o#nfUjXps_D|@Jlp3As-F&V{Qrgo)CzC_l-V<(1NYva27Nzg;_kG< zN$02zsHiZ9V&Eab;-nzrA}p5)GA_d3BM3ZzcaSJD3S7r1c)z(-LMS{ow?0>@0-HXA zIKU;m*?55GJJlif z>!L1`Ak+y;y3+(D<06408u+j`3H+TWdB3^GZ|*@Zon4m@71h(C-1?se{A%!(UK1+; zg1iDXB99&5?>LF|0R<>G@W+5bDy89&MBU+Eqk4~JiT{WqK~y1Lp)9dlNg_)Xr7ZELub~~kY*>sK_y-mR z{(u{)QuJ{)PAxaANR$G_8kQvnSe97BvcwveB?efQ7+_hVVp(EYhX{KT8pa^ z8O2r4HP-C~0y|gg{QD7A_s6RCU8)iFWV%L6U3Z%{r6<+ugQ~uy8up(!q2p;2IyiVl zt$LGgIjmXoJ{^a;Ppzmsql@}JsiR%@DFmUV4;O=rs96fG-lvwGQ2rD942Clp>a`V~ zve=YZwgsoeE{jlg;b|D{_+=v~Wszxs*bF%Jr2!%`P+nhJ`jo>cLYI`mFaGBwA|ofLBvWg-D4Rm*vGBkGAB(B(uIRWC2ksN4C2 z!kffVRa;UU1I^c|ok!Fb?3Ppr@yOF^#}T!;q&DE$85O)AMVCj_I@Eqjd6GJzYC*k= zYVCt+1@1rr*@J5B(@a#LerfI7xdTDaaE=l|%4laa6(NyvRNr+%)zv+rYVpjtTHdDW zg7L~IaC-K6@1BS@H0ECdp4&b+{7{L@vUENF~6N0MQMS_xz5R}jo;U+)pa#nFx-XtPcTO!k2Le|cA{>h;p$+L98jtE>OloMB)JpI|umMF$tAghc ztbR~)Gs162t@=~`UsFxH)Gcg4OI^rd|2OQ=J_sV@(CYA8b!eElriVt}xebG=EZlaa zWHUc_;+Ks`Ng4Mj&IQN0aVxl`cw5T2rGooTUgZ-Z%1gJ5+m)A6#x3$vmvOsNathDZ zP!iYVxkvu1yMVpGE7<58xe-ZBo~*lExwl7}gKBkP`#PoYizJ-hqLLf?_p8lKDu}iNw>GNnpT{nm@&`r`ghyG~Q9SIwWD%H7 zj`a8RhaE4l{(fD|*H5|po z{=xBRYgc+GIh>A0(uv8@bUNI;x2-!CZNb&PL@%!PEX+WSZjJHK8e;N=V#2n316`^jn%$oi`&pm^!#u8wjnz#2QQsZM2z2ixAmrw}vN=g>pPG^e6Y@yVgJ-<-c zGo8zvp3fJt7I#ZHdmf)0o)2Te-Ei|Rz|?zpdS-Ecs+7ymhnvslbJHy?;XAi&E9UOW z=4Zmq#ZqB$sucb?c{H8Q77F=7IyaM^&z{CA-)FN}8=Ws}3EviO4c`*p8@)CBqL$~O zrv}g(4Bh*>ul6s|5p8P|JPeKkFoZ=qT`Vo0N*8md3)#{lx{osxsYH5mWHd3{dbDdY zmGq?&iKFRMVq8c?!xV2$4#fw7$^kahiKjBN=Q070C*Ih=NPGNn=>cWSYe#huN~ z6*s2SEhHI(?cR~$)WlGN{Ys9cr}9_?@N5Z}2L_Wpz0$9~#OPR}H$I-|OQ(9{!~M9( z?#9RalY(T*hq=n&$d5Hwk5%(t*uJR{>NX3l2QS*-Av20iLF zfCWp$o%Uh#OLhSdCdSgEeWU5ba9=V`UW&CvNwq+Lc~)~@tgGEOJkmcHABf>9kR)W^ zmlz%y8#0K-JG*31I0nAwv9SKsnM^_74sI&PX*bSm=s+&uiP=0>*A6A;w-ybQn^uPR zB_M^A^mwvw{4j7S%0bA+AfNb!$c;qFs(=WUUJ|+{(VNCc zCEB}r9aknOO+XQ%U@JIC3pR+fvBVwCbD6vL%;n}=L`v!_>F|LA;b=!ocz4)JL3Dp) z3{4+8i2LwJi+f8mpXOl35`9UC$?@j-%v^TQLLpzu@5#*OGN5Cg3CkI@m?_SoCCKD> zY6$R3gqyeP+tA3kR(xnpOQ9Z!dPfSi#YBP{GJqP^WB>@@eyf%=2PF1F>xlLT_2lMp zU7nh(1}XzYYV12Ek_nKj$UK1}kV@uAkM#FL7YQLSj3~_okcWNq~KXn%g z2N2A+gwyF$`68j2Ck|Yl)!P~a9gdBU7&_@_n=J1R3@6HT_laa`1P|eqDCe1}Gc&W9 z(?t+xwzxQJ$dgQLWUa_%Et30F0MeD@9zm>ms7yH+$a};CL|0dLXDr&*-5zW2Y>h=b zJ0Qf`JKNFOj*ix@NTf5;n%&L5_iMZx-lt7g_2E{0v;~qT7Kuf=@oZE-OrSu=_NJ?H z0WXv@0nc0u4>(o~+IMs|Y*xo+BC0kllcuq zwdn8I*Vfq%=^u?k{CDAdHu#)PcWWE=yW61)Iy>4r+S{`*nmalS{G^wI378g+o_55A z(bUFHIt)ID_ku;nV41eJ$4IGGV33k%c!=5R+VQ@rLN)`{3o?O$xwzm+@yQCsFUFh) z5|xao34dxfGk-e0P{bM z3i<(mW1(tj#cY8p(LpU=cX?A~aXxomq%j41IgGuwkT6;rDncs> z4?~3-fNpA<81LU3YaL89;co4%6w~wh`T51!+3-@UvEmNnXc)q-7@oEP2vKwr%g`1I75DdEputhJ3_fA??h8%cmjWwKIw}guQ@SSS-*9^PbWcw!R>y zgK4mDWTFQ`Mw6q%GeESphD2jI65k;eqsm-kZWtz~w!mTj$tbj-5^^7g3keHt4B-H< zNg_^fCpk1an4k)dlLUYS88wsxo`4rMyKv%9kcVHgC z)z$&iv8@YT>4Y`}PM{29tzF&S(T->g+OS<5q4Yutenyy~lJG7IHckD*gYY3VKhT}b zO=r{7nNo)R8l3{=BT)hkf@+5BQopp)Nuv!?W=cE*;*PbRM}IV7P#QUm z37q2sIoZT6EF*JpJt?RZEB(Yt6Aesb!K&ml1PtvZ+NRyzmJiE~<9!IJ0j;ob;OBP1 zC5$Znm)+YbIG!nFVLTMY!9~-fL*wP1wu}LW@>2Cr?$w>NP8&YTm5$RYPaPi9(v)FA zMpSUlxS!I0j2}JL5eo$T!V|tZ?eC>h3C0t}(W8jLjRc>2CndNe?u@L4H9}behs=tebrltlM`0Tc zk0#i8W0fHowJ+Au8jZk7h()8oJi;6ut@y7K$nS)U*xBCN5uuIi_z0vR2thPdRrU

#_n~(4e8$e+ycUd;dK9OK2y^Ej5p>% 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

-c3nr}37HTT~rK14xuhF|0Y*}q&SK(b3%#w)}q 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 RC`*1r6)C?mNFb=G{V!pgC7l2O literal 0 HcmV?d00001 diff --git a/src/cpu.c b/src/cpu.c new file mode 100644 index 0000000..c8b6b00 --- /dev/null +++ b/src/cpu.c @@ -0,0 +1,2615 @@ +#include "pgb.h" +#include +#include + +#define USE_BOOTROM 0 + +static u8 memory[0x10000]; +static u8 registers[12]; +static u8 clock_queue = 0; +static u8 ime = 0; +static u8 halted = 0; + +static u8 oam_lock = 0; +static u8 vram_lock = 0; + +static FILE *log; + +#pragma region Abstracted Memory Access +static inline u8 rread8(const R8 reg) { return registers[reg]; } +static inline u16 rread16(const R16 reg) { + return ((const u16)rread8(reg * 2 + 1) & 0xFF) + + ((const u16)rread8((reg * 2) & 0xFF) << 8); +} + +static inline void rwrite8(const R8 reg, const u8 val) { registers[reg] = val; } +static inline void rwrite16(const R16 reg, const u16 val) { + rwrite8((reg * 2) + 1, val & 0xFF); + rwrite8(reg * 2, (val >> 8) & 0xFF); +} + +static inline u8 mread8(const u16 addr) { + if (addr >= 0x8000 && addr <= 0x9FFF && vram_lock) { + return 0xFF; + } else if (addr >= 0xFE00 && addr <= 0xFE9F && oam_lock) { + return 0xFF; + } else if (addr == 0xFF44) { + return 0x90; + } + return memory[addr]; +} +static inline u16 mread16(const u16 addr) { + return ((const u16)mread8(addr) & 0xFF) + + (((const u16)mread8(addr + 1) & 0xFF) << 8); +} + +static inline void mwrite8(const u16 addr, const u8 val) { + if (addr <= 0x7FFF) { + return; + } else if (addr >= 0x8000 && addr <= 0x9FFF && vram_lock) { + return; + } else if (addr >= 0xFE00 && addr <= 0xFE9F && oam_lock) { + return; + } + memory[addr] = val; +} +static inline void mwrite16(const u16 addr, const u16 val) { + mwrite8(addr, val & 0xFF); + mwrite8(addr + 1, (val >> 8) & 0xFF); +} + +static inline u8 rget(const R8 reg, const u8 bit) { + return (rread8(reg) >> bit) & 1; +} +static inline void rset(const R8 reg, const u8 bit) { + rwrite8(reg, rread8(reg) | 1 << bit); +} +static inline void rclear(const R8 reg, const u8 bit) { + rwrite8(reg, rread8(reg) & ~(1 << bit)); +} + +static inline u8 mget(const u16 addr, const u8 bit) { + return (mread8(addr) >> bit) & 1; +} +static inline void mset(const u16 addr, const u8 bit) { + mwrite8(addr, mread8(addr) | (1 << bit)); +} +static inline void mclear(const u16 addr, const u8 bit) { + mwrite8(addr, mread8(addr) & ~(1 << bit)); +} + +#pragma region Other Utilities +static inline void instruction_cost(const u8 size, const u8 clocks) { + rwrite16(R16_PC, rread16(R16_PC) + (size)); + clock_queue += (clocks); +} + +static inline void error_handler(void) { + puts("\nUnimplemented/Invalid Instruction!"); + printf("AF: 0x%X, BC: 0x%X, DE: 0x%X, HL: 0x%X, SP: 0x%X, PC: 0x%X\n", + rread16(R16_AF), rread16(R16_BC), rread16(R16_DE), rread16(R16_HL), + rread16(R16_SP), rread16(R16_PC)); + printf("Hex: 0x%X\n", mread8(rread16(R16_PC))); + printf("Oct: %o\n", mread8(rread16(R16_PC))); +} + +#pragma region Flag Helpers +#define zflag(val) (val) == 0 ? rset(R8_F, FLAG_Z) : rclear(R8_F, FLAG_Z); + +#define hflag_add(result, operand) \ + ((result) & 0x0F) < ((operand) & 0x0F) ? rset(R8_F, FLAG_H) \ + : rclear(R8_F, FLAG_H); + +#define cflag_add(result, operand) \ + (result) < (operand) ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + +#define hflag_sub(original, result) \ + ((original) & 0x0F) < ((result) & 0x0F) ? rset(R8_F, FLAG_H) \ + : rclear(R8_F, FLAG_H); + +#define cflag_sub(original, result) \ + (original) < (result) ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + +#pragma region 8 Bit Arithmetic +static inline void add(const u8 val) { + rwrite8(R8_A, rread8(R8_A) + val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + hflag_add(rread8(R8_A), val); + cflag_add(rread8(R8_A), val); +} + +static inline void adc(const u8 val) { + u8 f_save; + u8 flag = rget(R8_F, FLAG_C); + add(val); + f_save = rread8(R8_F); + add(flag); + rwrite8(R8_F, rread8(R8_F) | f_save); + zflag(rread8(R8_A)); +} + +static inline void sub(const u8 val) { + u8 original = rread8(R8_A); + rwrite8(R8_A, rread8(R8_A) - val); + zflag(rread8(R8_A)); + rset(R8_F, FLAG_N); + hflag_sub(original, rread8(R8_A)); + cflag_sub(original, rread8(R8_A)); +} + +static inline void sbc(const u8 val) { + u8 f_save; + u8 flag = rget(R8_F, FLAG_C); + sub(val); + f_save = rread8(R8_F); + sub(flag); + rwrite8(R8_F, rread8(R8_F) | f_save); + zflag(rread8(R8_A)); +} + +static inline void and (const u8 val) { + rwrite8(R8_A, rread8(R8_A) & val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rset(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); +} + +static inline void xor + (const u8 val) { + rwrite8(R8_A, rread8(R8_A) ^ val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); + } + + static inline void or + (const u8 val) { + rwrite8(R8_A, rread8(R8_A) | val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); +} + +static inline void cp(const u8 val) { + u8 result = rread8(R8_A) - val; + zflag(result); + rset(R8_F, FLAG_N); + hflag_sub(rread8(R8_A), result); + cflag_sub(rread8(R8_A), result); +} + +static inline void inc(const R8 reg) { + rwrite8(reg, rread8(reg) + 1); + zflag(rread8(reg)); + rclear(R8_F, FLAG_N); + hflag_add(rread8(reg), 1); +} + +static inline void dec(const R8 reg) { + u8 original = rread8(reg); + rwrite8(reg, rread8(reg) - 1); + zflag(rread8(reg)); + rset(R8_F, FLAG_N); + hflag_sub(original, rread8(reg)); +} + +#pragma region 16 Bit Arithmetic & Stack +static inline void add16(R16 dst, R16 reg) { + u16 o1 = rread16(dst); + u16 o2 = rread16(reg); + rwrite16(dst, o1 + o2); + rclear(R8_F, FLAG_N); + if (o1 > rread16(dst)) { + rset(R8_F, FLAG_C); + } else { + rclear(R8_F, FLAG_C); + } + if ((o1 & 0xFFF) + (o2 & 0xFFF) > 0xFFF) { + rset(R8_F, FLAG_H); + } else { + rclear(R8_F, FLAG_H); + } +} + +static inline void push(R16 reg) { + rwrite16(R16_SP, rread16(R16_SP) - 2); + mwrite16(rread16(R16_SP), rread16(reg)); +} + +static inline void pop(R16 reg) { + rwrite16(reg, mread16(rread16(R16_SP))); + rwrite16(R16_SP, rread16(R16_SP) + 2); + if (reg == R16_AF) { + rwrite8(R8_F, (rread8(R8_F) >> 4) << 4); + } +} + +#pragma region Shift, Rotate & Bit Instruction Helpers +static inline u8 rl(u8 val) { + u8 result; + result = (val << 1) | rget(R8_F, FLAG_C); + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rr(u8 val) { + u8 result; + result = (rget(R8_F, FLAG_C) << 7) | (val >> 1); + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rlc(u8 val) { + u8 result; + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val << 1) | (val >> 7); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rrc(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val << 7) | (val >> 1); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 sla(u8 val) { + u8 result; + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = val << 1; + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 sra(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val & (1 << 7)) | (val >> 1); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 swap(u8 val) { + u8 result; + result = (val >> 4) | (val << 4); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); + return result; +} +static inline u8 srl(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = val >> 1; + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline void bit(u8 val, u8 bit) { + zflag((val >> bit) & 1); + rclear(R8_F, FLAG_N); + rset(R8_F, FLAG_H); +} + +#pragma region Interrupt Helpers + +static inline void interrupt_check(void) { + if (ime) { + ime = 0; + if (mget(0xFF0F, INT_VBLANK) && mget(0xFFFF, INT_VBLANK)) { + mclear(0xFF0F, INT_VBLANK); + push(R16_PC); + rwrite16(R16_PC, 0x40); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_LCD) && mget(0xFFFF, INT_LCD)) { + mclear(0xFF0F, INT_LCD); + push(R16_PC); + rwrite16(R16_PC, 0x48); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_TIMER) && mget(0xFFFF, INT_TIMER)) { + mclear(0xFF0F, INT_TIMER); + push(R16_PC); + rwrite16(R16_PC, 0x50); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_SERIAL) && mget(0xFFFF, INT_SERIAL)) { + mclear(0xFF0F, INT_SERIAL); + push(R16_PC); + rwrite16(R16_PC, 0x58); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_JOYPAD) && mget(0xFFFF, INT_JOYPAD)) { + mclear(0xFF0F, INT_JOYPAD); + push(R16_PC); + rwrite16(R16_PC, 0x60); + instruction_cost(0, 20); + halted = 0; + } + } +} + +#pragma region External Access +void lock_oam(void) { oam_lock = 1; } +void unlock_oam(void) { oam_lock = 0; } +void lock_vram(void) { vram_lock = 1; } +void unlock_vram(void) { vram_lock = 0; } + +void create_interrupt(const INTERRUPT interrupt) { mset(0xFF0F, interrupt); } + +u8 mread8_external(const u16 addr) { return mread8(addr); } +u16 mread16_external(const u16 addr) { return mread16(addr); } +void mwrite8_external(const u16 addr, const u8 val) { mwrite8(addr, val); } +void mwrite16_external(const u16 addr, const u16 val) { mwrite16(addr, val); } + +#pragma region CPU Initialization +void cpu_init(const char *gamerom, const char *logfile) { + clock_queue = 0; + fread(memory, 0x8000, 1, fopen(gamerom, "rb")); + log = fopen(logfile, "w"); + + switch (mread8(0x147)) { + case 0: + break; + default: + puts("(Possibly) Unsupported Cartridge Type!"); + break; + } + + rwrite8(R8_A, 0x01); + rwrite8(R8_F, 0xB0); + rwrite8(R8_B, 0x00); + rwrite8(R8_C, 0x13); + rwrite8(R8_D, 0x00); + rwrite8(R8_E, 0xD8); + rwrite8(R8_H, 0x01); + rwrite8(R8_L, 0x4D); + rwrite16(R16_SP, 0xFFFE); + rwrite16(R16_PC, 0x0100); +} + +static inline void prefix_run(const u8 instr) { + switch (instr) { +#pragma region Prefix 0x00 + case 0x00: + rwrite8(R8_B, rlc(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x01: + rwrite8(R8_C, rlc(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x02: + rwrite8(R8_D, rlc(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x03: + rwrite8(R8_E, rlc(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x04: + rwrite8(R8_H, rlc(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x05: + rwrite8(R8_L, rlc(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x06: + mwrite8(rread16(R16_HL), rlc(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x07: + rwrite8(R8_A, rlc(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x08: + rwrite8(R8_B, rrc(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x09: + rwrite8(R8_C, rrc(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x0A: + rwrite8(R8_D, rrc(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x0B: + rwrite8(R8_E, rrc(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x0C: + rwrite8(R8_H, rrc(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x0D: + rwrite8(R8_L, rrc(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x0E: + mwrite8(rread16(R16_HL), rrc(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x0F: + rwrite8(R8_A, rrc(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x10 + case 0x10: + rwrite8(R8_B, rl(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x11: + rwrite8(R8_C, rl(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x12: + rwrite8(R8_D, rl(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x13: + rwrite8(R8_E, rl(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x14: + rwrite8(R8_H, rl(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x15: + rwrite8(R8_L, rl(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x16: + mwrite8(rread16(R16_HL), rl(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x17: + rwrite8(R8_A, rl(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x18: + rwrite8(R8_B, rr(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x19: + rwrite8(R8_C, rr(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x1A: + rwrite8(R8_D, rr(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x1B: + rwrite8(R8_E, rr(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x1C: + rwrite8(R8_H, rr(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x1D: + rwrite8(R8_L, rr(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x1E: + mwrite8(rread16(R16_HL), rr(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x1F: + rwrite8(R8_A, rr(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x20 + case 0x20: + rwrite8(R8_B, sla(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x21: + rwrite8(R8_C, sla(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x22: + rwrite8(R8_D, sla(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x23: + rwrite8(R8_E, sla(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x24: + rwrite8(R8_H, sla(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x25: + rwrite8(R8_L, sla(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x26: + mwrite8(rread16(R16_HL), sla(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x27: + rwrite8(R8_A, sla(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x28: + rwrite8(R8_B, sra(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x29: + rwrite8(R8_C, sra(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x2A: + rwrite8(R8_D, sra(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x2B: + rwrite8(R8_E, sra(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x2C: + rwrite8(R8_H, sra(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x2D: + rwrite8(R8_L, sra(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x2E: + mwrite8(rread16(R16_HL), sra(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x2F: + rwrite8(R8_A, sra(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x30 + case 0x30: + rwrite8(R8_B, swap(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x31: + rwrite8(R8_C, swap(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x32: + rwrite8(R8_D, swap(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x33: + rwrite8(R8_E, swap(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x34: + rwrite8(R8_H, swap(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x35: + rwrite8(R8_L, swap(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x36: + mwrite8(rread16(R16_HL), swap(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x37: + rwrite8(R8_A, swap(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x38: + rwrite8(R8_B, srl(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x39: + rwrite8(R8_C, srl(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x3A: + rwrite8(R8_D, srl(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x3B: + rwrite8(R8_E, srl(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x3C: + rwrite8(R8_H, srl(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x3D: + rwrite8(R8_L, srl(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x3E: + mwrite8(rread16(R16_HL), srl(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x3F: + rwrite8(R8_A, srl(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x40 + case 0x40: + bit(rread8(R8_B), 0); + instruction_cost(2, 8); + break; + case 0x41: + bit(rread8(R8_C), 0); + instruction_cost(2, 8); + break; + case 0x42: + bit(rread8(R8_D), 0); + instruction_cost(2, 8); + break; + case 0x43: + bit(rread8(R8_E), 0); + instruction_cost(2, 8); + break; + case 0x44: + bit(rread8(R8_H), 0); + instruction_cost(2, 8); + break; + case 0x45: + bit(rread8(R8_L), 0); + instruction_cost(2, 8); + break; + case 0x46: + bit(mread8(rread16(R16_HL)), 0); + instruction_cost(2, 12); + break; + case 0x47: + bit(rread8(R8_A), 0); + instruction_cost(2, 8); + break; + case 0x48: + bit(rread8(R8_B), 1); + instruction_cost(2, 8); + break; + case 0x49: + bit(rread8(R8_C), 1); + instruction_cost(2, 8); + break; + case 0x4A: + bit(rread8(R8_D), 1); + instruction_cost(2, 8); + break; + case 0x4B: + bit(rread8(R8_E), 1); + instruction_cost(2, 8); + break; + case 0x4C: + bit(rread8(R8_H), 1); + instruction_cost(2, 8); + break; + case 0x4D: + bit(rread8(R8_L), 1); + instruction_cost(2, 8); + break; + case 0x4E: + bit(mread8(rread16(R16_HL)), 1); + instruction_cost(2, 12); + break; + case 0x4F: + bit(rread8(R8_A), 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x50 + case 0x50: + bit(rread8(R8_B), 2); + instruction_cost(2, 8); + break; + case 0x51: + bit(rread8(R8_C), 2); + instruction_cost(2, 8); + break; + case 0x52: + bit(rread8(R8_D), 2); + instruction_cost(2, 8); + break; + case 0x53: + bit(rread8(R8_E), 2); + instruction_cost(2, 8); + break; + case 0x54: + bit(rread8(R8_H), 2); + instruction_cost(2, 8); + break; + case 0x55: + bit(rread8(R8_L), 2); + instruction_cost(2, 8); + break; + case 0x56: + bit(mread8(rread16(R16_HL)), 2); + instruction_cost(2, 12); + break; + case 0x57: + bit(rread8(R8_A), 2); + instruction_cost(2, 8); + break; + case 0x58: + bit(rread8(R8_B), 3); + instruction_cost(2, 8); + break; + case 0x59: + bit(rread8(R8_C), 3); + instruction_cost(2, 8); + break; + case 0x5A: + bit(rread8(R8_D), 3); + instruction_cost(2, 8); + break; + case 0x5B: + bit(rread8(R8_E), 3); + instruction_cost(2, 8); + break; + case 0x5C: + bit(rread8(R8_H), 3); + instruction_cost(2, 8); + break; + case 0x5D: + bit(rread8(R8_L), 3); + instruction_cost(2, 8); + break; + case 0x5E: + bit(mread8(rread16(R16_HL)), 3); + instruction_cost(2, 12); + break; + case 0x5F: + bit(rread8(R8_A), 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x60 + case 0x60: + bit(rread8(R8_B), 4); + instruction_cost(2, 8); + break; + case 0x61: + bit(rread8(R8_C), 4); + instruction_cost(2, 8); + break; + case 0x62: + bit(rread8(R8_D), 4); + instruction_cost(2, 8); + break; + case 0x63: + bit(rread8(R8_E), 4); + instruction_cost(2, 8); + break; + case 0x64: + bit(rread8(R8_H), 4); + instruction_cost(2, 8); + break; + case 0x65: + bit(rread8(R8_L), 4); + instruction_cost(2, 8); + break; + case 0x66: + bit(mread8(rread16(R16_HL)), 4); + instruction_cost(2, 12); + break; + case 0x67: + bit(rread8(R8_A), 4); + instruction_cost(2, 8); + break; + case 0x68: + bit(rread8(R8_B), 5); + instruction_cost(2, 8); + break; + case 0x69: + bit(rread8(R8_C), 5); + instruction_cost(2, 8); + break; + case 0x6A: + bit(rread8(R8_D), 5); + instruction_cost(2, 8); + break; + case 0x6B: + bit(rread8(R8_E), 5); + instruction_cost(2, 8); + break; + case 0x6C: + bit(rread8(R8_H), 5); + instruction_cost(2, 8); + break; + case 0x6D: + bit(rread8(R8_L), 5); + instruction_cost(2, 8); + break; + case 0x6E: + bit(mread8(rread16(R16_HL)), 5); + instruction_cost(2, 12); + break; + case 0x6F: + bit(rread8(R8_A), 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x70 + case 0x70: + bit(rread8(R8_B), 6); + instruction_cost(2, 8); + break; + case 0x71: + bit(rread8(R8_C), 6); + instruction_cost(2, 8); + break; + case 0x72: + bit(rread8(R8_D), 6); + instruction_cost(2, 8); + break; + case 0x73: + bit(rread8(R8_E), 6); + instruction_cost(2, 8); + break; + case 0x74: + bit(rread8(R8_H), 6); + instruction_cost(2, 8); + break; + case 0x75: + bit(rread8(R8_L), 6); + instruction_cost(2, 8); + break; + case 0x76: + bit(mread8(rread16(R16_HL)), 6); + instruction_cost(2, 12); + break; + case 0x77: + bit(rread8(R8_A), 6); + instruction_cost(2, 8); + break; + case 0x78: + bit(rread8(R8_B), 7); + instruction_cost(2, 8); + break; + case 0x79: + bit(rread8(R8_C), 7); + instruction_cost(2, 8); + break; + case 0x7A: + bit(rread8(R8_D), 7); + instruction_cost(2, 8); + break; + case 0x7B: + bit(rread8(R8_E), 7); + instruction_cost(2, 8); + break; + case 0x7C: + bit(rread8(R8_H), 7); + instruction_cost(2, 8); + break; + case 0x7D: + bit(rread8(R8_L), 7); + instruction_cost(2, 8); + break; + case 0x7E: + bit(mread8(rread16(R16_HL)), 7); + instruction_cost(2, 12); + break; + case 0x7F: + bit(rread8(R8_A), 7); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x80 + case 0x80: + rclear(R8_B, 0); + instruction_cost(2, 8); + break; + case 0x81: + rclear(R8_C, 0); + instruction_cost(2, 8); + break; + case 0x82: + rclear(R8_D, 0); + instruction_cost(2, 8); + break; + case 0x83: + rclear(R8_E, 0); + instruction_cost(2, 8); + break; + case 0x84: + rclear(R8_H, 0); + instruction_cost(2, 8); + break; + case 0x85: + rclear(R8_L, 0); + instruction_cost(2, 8); + break; + case 0x86: + mclear(rread16(R16_HL), 0); + instruction_cost(2, 16); + break; + case 0x87: + rclear(R8_A, 0); + instruction_cost(2, 8); + break; + case 0x88: + rclear(R8_B, 1); + instruction_cost(2, 8); + break; + case 0x89: + rclear(R8_C, 1); + instruction_cost(2, 8); + break; + case 0x8A: + rclear(R8_D, 1); + instruction_cost(2, 8); + break; + case 0x8B: + rclear(R8_E, 1); + instruction_cost(2, 8); + break; + case 0x8C: + rclear(R8_H, 1); + instruction_cost(2, 8); + break; + case 0x8D: + rclear(R8_L, 1); + instruction_cost(2, 8); + break; + case 0x8E: + mclear(rread16(R16_HL), 1); + instruction_cost(2, 16); + break; + case 0x8F: + rclear(R8_A, 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x90 + case 0x90: + rclear(R8_B, 2); + instruction_cost(2, 8); + break; + case 0x91: + rclear(R8_C, 2); + instruction_cost(2, 8); + break; + case 0x92: + rclear(R8_D, 2); + instruction_cost(2, 8); + break; + case 0x93: + rclear(R8_E, 2); + instruction_cost(2, 8); + break; + case 0x94: + rclear(R8_H, 2); + instruction_cost(2, 8); + break; + case 0x95: + rclear(R8_L, 2); + instruction_cost(2, 8); + break; + case 0x96: + mclear(rread16(R16_HL), 2); + instruction_cost(2, 16); + break; + case 0x97: + rclear(R8_A, 2); + instruction_cost(2, 8); + break; + case 0x98: + rclear(R8_B, 3); + instruction_cost(2, 8); + break; + case 0x99: + rclear(R8_C, 3); + instruction_cost(2, 8); + break; + case 0x9A: + rclear(R8_D, 3); + instruction_cost(2, 8); + break; + case 0x9B: + rclear(R8_E, 3); + instruction_cost(2, 8); + break; + case 0x9C: + rclear(R8_H, 3); + instruction_cost(2, 8); + break; + case 0x9D: + rclear(R8_L, 3); + instruction_cost(2, 8); + break; + case 0x9E: + mclear(rread16(R16_HL), 3); + instruction_cost(2, 16); + break; + case 0x9F: + rclear(R8_A, 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xA0 + case 0xA0: + rclear(R8_B, 4); + instruction_cost(2, 8); + break; + case 0xA1: + rclear(R8_C, 4); + instruction_cost(2, 8); + break; + case 0xA2: + rclear(R8_D, 4); + instruction_cost(2, 8); + break; + case 0xA3: + rclear(R8_E, 4); + instruction_cost(2, 8); + break; + case 0xA4: + rclear(R8_H, 4); + instruction_cost(2, 8); + break; + case 0xA5: + rclear(R8_L, 4); + instruction_cost(2, 8); + break; + case 0xA6: + mclear(rread16(R16_HL), 4); + instruction_cost(2, 16); + break; + case 0xA7: + rclear(R8_A, 4); + instruction_cost(2, 8); + break; + case 0xA8: + rclear(R8_B, 5); + instruction_cost(2, 8); + break; + case 0xA9: + rclear(R8_C, 5); + instruction_cost(2, 8); + break; + case 0xAA: + rclear(R8_D, 5); + instruction_cost(2, 8); + break; + case 0xAB: + rclear(R8_E, 5); + instruction_cost(2, 8); + break; + case 0xAC: + rclear(R8_H, 5); + instruction_cost(2, 8); + break; + case 0xAD: + rclear(R8_L, 5); + instruction_cost(2, 8); + break; + case 0xAE: + mclear(rread16(R16_HL), 5); + instruction_cost(2, 16); + break; + case 0xAF: + rclear(R8_A, 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xB0 + case 0xB0: + rclear(R8_B, 6); + instruction_cost(2, 8); + break; + case 0xB1: + rclear(R8_C, 6); + instruction_cost(2, 8); + break; + case 0xB2: + rclear(R8_D, 6); + instruction_cost(2, 8); + break; + case 0xB3: + rclear(R8_E, 6); + instruction_cost(2, 8); + break; + case 0xB4: + rclear(R8_H, 6); + instruction_cost(2, 8); + break; + case 0xB5: + rclear(R8_L, 6); + instruction_cost(2, 8); + break; + case 0xB6: + mclear(rread16(R16_HL), 6); + instruction_cost(2, 16); + break; + case 0xB7: + rclear(R8_A, 6); + instruction_cost(2, 8); + break; + case 0xB8: + rclear(R8_B, 7); + instruction_cost(2, 8); + break; + case 0xB9: + rclear(R8_C, 7); + instruction_cost(2, 8); + break; + case 0xBA: + rclear(R8_D, 7); + instruction_cost(2, 8); + break; + case 0xBB: + rclear(R8_E, 7); + instruction_cost(2, 8); + break; + case 0xBC: + rclear(R8_H, 7); + instruction_cost(2, 8); + break; + case 0xBD: + rclear(R8_L, 7); + instruction_cost(2, 8); + break; + case 0xBE: + mclear(rread16(R16_HL), 7); + instruction_cost(2, 16); + break; + case 0xBF: + rclear(R8_A, 7); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xC0 + case 0xC0: + rset(R8_B, 0); + instruction_cost(2, 8); + break; + case 0xC1: + rset(R8_C, 0); + instruction_cost(2, 8); + break; + case 0xC2: + rset(R8_D, 0); + instruction_cost(2, 8); + break; + case 0xC3: + rset(R8_E, 0); + instruction_cost(2, 8); + break; + case 0xC4: + rset(R8_H, 0); + instruction_cost(2, 8); + break; + case 0xC5: + rset(R8_L, 0); + instruction_cost(2, 8); + break; + case 0xC6: + mset(rread16(R16_HL), 0); + instruction_cost(2, 16); + break; + case 0xC7: + rset(R8_A, 0); + instruction_cost(2, 8); + break; + case 0xC8: + rset(R8_B, 1); + instruction_cost(2, 8); + break; + case 0xC9: + rset(R8_C, 1); + instruction_cost(2, 8); + break; + case 0xCA: + rset(R8_D, 1); + instruction_cost(2, 8); + break; + case 0xCB: + rset(R8_E, 1); + instruction_cost(2, 8); + break; + case 0xCC: + rset(R8_H, 1); + instruction_cost(2, 8); + break; + case 0xCD: + rset(R8_L, 1); + instruction_cost(2, 8); + break; + case 0xCE: + mset(rread16(R16_HL), 1); + instruction_cost(2, 16); + break; + case 0xCF: + rset(R8_A, 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xD0 + case 0xD0: + rset(R8_B, 2); + instruction_cost(2, 8); + break; + case 0xD1: + rset(R8_C, 2); + instruction_cost(2, 8); + break; + case 0xD2: + rset(R8_D, 2); + instruction_cost(2, 8); + break; + case 0xD3: + rset(R8_E, 2); + instruction_cost(2, 8); + break; + case 0xD4: + rset(R8_H, 2); + instruction_cost(2, 8); + break; + case 0xD5: + rset(R8_L, 2); + instruction_cost(2, 8); + break; + case 0xD6: + mset(rread16(R16_HL), 2); + instruction_cost(2, 16); + break; + case 0xD7: + rset(R8_A, 2); + instruction_cost(2, 8); + break; + case 0xD8: + rset(R8_B, 3); + instruction_cost(2, 8); + break; + case 0xD9: + rset(R8_C, 3); + instruction_cost(2, 8); + break; + case 0xDA: + rset(R8_D, 3); + instruction_cost(2, 8); + break; + case 0xDB: + rset(R8_E, 3); + instruction_cost(2, 8); + break; + case 0xDC: + rset(R8_H, 3); + instruction_cost(2, 8); + break; + case 0xDD: + rset(R8_L, 3); + instruction_cost(2, 8); + break; + case 0xDE: + mset(rread16(R16_HL), 3); + instruction_cost(2, 16); + break; + case 0xDF: + rset(R8_A, 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xE0 + case 0xE0: + rset(R8_B, 4); + instruction_cost(2, 8); + break; + case 0xE1: + rset(R8_C, 4); + instruction_cost(2, 8); + break; + case 0xE2: + rset(R8_D, 4); + instruction_cost(2, 8); + break; + case 0xE3: + rset(R8_E, 4); + instruction_cost(2, 8); + break; + case 0xE4: + rset(R8_H, 4); + instruction_cost(2, 8); + break; + case 0xE5: + rset(R8_L, 4); + instruction_cost(2, 8); + break; + case 0xE6: + mset(rread16(R16_HL), 4); + instruction_cost(2, 16); + break; + case 0xE7: + rset(R8_A, 4); + instruction_cost(2, 8); + break; + case 0xE8: + rset(R8_B, 5); + instruction_cost(2, 8); + break; + case 0xE9: + rset(R8_C, 5); + instruction_cost(2, 8); + break; + case 0xEA: + rset(R8_D, 5); + instruction_cost(2, 8); + break; + case 0xEB: + rset(R8_E, 5); + instruction_cost(2, 8); + break; + case 0xEC: + rset(R8_H, 5); + instruction_cost(2, 8); + break; + case 0xED: + rset(R8_L, 5); + instruction_cost(2, 8); + break; + case 0xEE: + mset(rread16(R16_HL), 5); + instruction_cost(2, 16); + break; + case 0xEF: + rset(R8_A, 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xF0 + case 0xF0: + rset(R8_B, 6); + instruction_cost(2, 8); + break; + case 0xF1: + rset(R8_C, 6); + instruction_cost(2, 8); + break; + case 0xF2: + rset(R8_D, 6); + instruction_cost(2, 8); + break; + case 0xF3: + rset(R8_E, 6); + instruction_cost(2, 8); + break; + case 0xF4: + rset(R8_H, 6); + instruction_cost(2, 8); + break; + case 0xF5: + rset(R8_L, 6); + instruction_cost(2, 8); + break; + case 0xF6: + mset(rread16(R16_HL), 6); + instruction_cost(2, 16); + break; + case 0xF7: + rset(R8_A, 6); + instruction_cost(2, 8); + break; + case 0xF8: + rset(R8_B, 7); + instruction_cost(2, 8); + break; + case 0xF9: + rset(R8_C, 7); + instruction_cost(2, 8); + break; + case 0xFA: + rset(R8_D, 7); + instruction_cost(2, 8); + break; + case 0xFB: + rset(R8_E, 7); + instruction_cost(2, 8); + break; + case 0xFC: + rset(R8_H, 7); + instruction_cost(2, 8); + break; + case 0xFD: + rset(R8_L, 7); + instruction_cost(2, 8); + break; + case 0xFE: + mset(rread16(R16_HL), 7); + instruction_cost(2, 16); + break; + case 0xFF: + rset(R8_A, 7); + instruction_cost(2, 8); + break; + default: + error_handler(); + puts("From the prefixed instructions."); + exit(EXIT_FAILURE); + break; + } +} + +void cpu_cycle(void) { + interrupt_check(); + if (clock_queue == 0) { + if (!halted) { + if (log != NULL) { + fprintf( + log, + "A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X " + "PC:%04X PCMEM:%02X,%02X,%02X,%02X\n", + rread8(R8_A), rread8(R8_F), rread8(R8_B), rread8(R8_C), + rread8(R8_D), rread8(R8_E), rread8(R8_H), rread8(R8_L), + rread16(R16_SP), rread16(R16_PC), mread8(rread16(R16_PC)), + mread8(rread16(R16_PC) + 1), mread8(rread16(R16_PC) + 2), + mread8(rread16(R16_PC) + 3)); + } + switch (mread8(rread16(R16_PC))) { +#pragma region 0x00 + case 0x00: + instruction_cost(1, 4); + break; + case 0x01: + rwrite16(R16_BC, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x02: + mwrite8(rread16(R16_BC), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x03: + rwrite16(R16_BC, rread16(R16_BC) + 1); + instruction_cost(1, 8); + break; + case 0x04: + inc(R8_B); + instruction_cost(1, 4); + break; + case 0x05: + dec(R8_B); + instruction_cost(1, 4); + break; + case 0x06: + rwrite8(R8_B, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x07: + rwrite8(R8_A, rlc(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; + case 0x08: + mwrite16(mread16(rread16(R16_PC) + 1), rread16(R16_SP)); + instruction_cost(3, 20); + break; + case 0x09: + add16(R16_HL, R16_BC); + instruction_cost(1, 8); + break; + case 0x0A: + rwrite8(R8_A, mread8(rread16(R16_BC))); + instruction_cost(1, 8); + break; + case 0x0B: + rwrite16(R16_BC, rread16(R16_BC) - 1); + instruction_cost(1, 8); + break; + case 0x0C: + inc(R8_C); + instruction_cost(1, 4); + break; + case 0x0D: + dec(R8_C); + instruction_cost(1, 4); + break; + case 0x0E: + rwrite8(R8_C, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x0F: + rwrite8(R8_A, rrc(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; +#pragma region 0x10 + case 0x10: + halted = 1; + instruction_cost(2, 4); + break; + case 0x11: + rwrite16(R16_DE, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x12: + mwrite8(rread16(R16_DE), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x13: + rwrite16(R16_DE, rread16(R16_DE) + 1); + instruction_cost(1, 8); + break; + case 0x14: + inc(R8_D); + instruction_cost(1, 4); + break; + case 0x15: + dec(R8_D); + instruction_cost(1, 4); + break; + case 0x16: + rwrite8(R8_D, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x17: + rwrite8(R8_A, rl(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; + case 0x18: + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0x19: + add16(R16_HL, R16_DE); + instruction_cost(1, 8); + break; + case 0x1A: + rwrite8(R8_A, mread8(rread16(R16_DE))); + instruction_cost(1, 8); + break; + case 0x1B: + rwrite16(R16_DE, rread16(R16_DE) - 1); + instruction_cost(1, 8); + break; + case 0x1C: + inc(R8_E); + instruction_cost(1, 4); + break; + case 0x1D: + dec(R8_E); + instruction_cost(1, 4); + break; + case 0x1E: + rwrite8(R8_E, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x1F: + rwrite8(R8_A, rr(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; +#pragma region 0x20 + case 0x20: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x21: + rwrite16(R16_HL, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x22: + mwrite8(rread16(R16_HL), rread8(R8_A)); + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x23: + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x24: + inc(R8_H); + instruction_cost(1, 4); + break; + case 0x25: + dec(R8_H); + instruction_cost(1, 4); + break; + case 0x26: + rwrite8(R8_H, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x27: + if (rget(R8_F, FLAG_N)) { + u8 adjustment = 0; + if (rget(R8_F, FLAG_H)) { + adjustment += 0x6; + } + if (rget(R8_F, FLAG_C)) { + adjustment += 0x60; + } else { + rclear(R8_F, FLAG_C); + } + rwrite8(R8_A, rread8(R8_A) - adjustment); + } else { + u8 adjustment = 0; + if (rget(R8_F, FLAG_H) || (rread8(R8_A) & 0xF) > 0x9) { + adjustment += 0x6; + } + if (rget(R8_F, FLAG_C) || rread8(R8_A) > 0x99) { + adjustment += 0x60; + rset(R8_F, FLAG_C); + } else { + rclear(R8_F, FLAG_C); + } + rwrite8(R8_A, rread8(R8_A) + adjustment); + } + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_H); + instruction_cost(1, 4); + break; + case 0x28: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x29: + add16(R16_HL, R16_HL); + instruction_cost(1, 8); + break; + case 0x2A: + rwrite8(R8_A, mread8(rread16(R16_HL))); + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x2B: + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x2C: + inc(R8_L); + instruction_cost(1, 4); + break; + case 0x2D: + dec(R8_L); + instruction_cost(1, 4); + break; + case 0x2E: + rwrite8(R8_L, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x2F: + rwrite8(R8_A, ~rread8(R8_A)); + rset(R8_F, FLAG_N); + rset(R8_F, FLAG_H); + instruction_cost(1, 4); + break; +#pragma region 0x30 + case 0x30: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x31: + rwrite16(R16_SP, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x32: + mwrite8(rread16(R16_HL), rread8(R8_A)); + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x33: + rwrite16(R16_SP, rread16(R16_SP) + 1); + instruction_cost(1, 8); + break; + case 0x34: + mwrite8(rread16(R16_HL), mread8(rread16(R16_HL)) + 1); + zflag(mread8(rread16(R16_HL))); + rclear(R8_F, FLAG_N); + hflag_add(mread8(rread16(R16_HL)), 1); + instruction_cost(1, 4); + break; + case 0x35: + mwrite8(rread16(R16_HL), mread8(rread16(R16_HL)) - 1); + zflag(mread8(rread16(R16_HL))); + rset(R8_F, FLAG_N); + hflag_sub(mread8(rread16(R16_HL)) + 1, mread8(rread16(R16_HL))); + instruction_cost(1, 4); + break; + case 0x36: + mwrite8(rread16(R16_HL), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0x37: + rset(R8_F, FLAG_C); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_N); + instruction_cost(1, 4); + break; + case 0x38: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x39: + add16(R16_HL, R16_SP); + instruction_cost(1, 8); + break; + case 0x3A: + rwrite8(R8_A, mread8(rread16(R16_HL))); + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x3B: + rwrite16(R16_SP, rread16(R16_SP) - 1); + instruction_cost(1, 8); + break; + case 0x3C: + inc(R8_A); + instruction_cost(1, 4); + break; + case 0x3D: + dec(R8_A); + instruction_cost(1, 4); + break; + case 0x3E: + rwrite8(R8_A, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x3F: + rget(R8_F, FLAG_C) ? rclear(R8_F, FLAG_C) : rset(R8_F, FLAG_C); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + instruction_cost(1, 4); + break; +#pragma region 0x40 + case 0x40: + rwrite8(R8_B, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x41: + rwrite8(R8_B, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x42: + rwrite8(R8_B, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x43: + rwrite8(R8_B, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x44: + rwrite8(R8_B, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x45: + rwrite8(R8_B, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x46: + rwrite8(R8_B, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x47: + rwrite8(R8_B, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x48: + rwrite8(R8_C, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x49: + rwrite8(R8_C, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x4A: + rwrite8(R8_C, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x4B: + rwrite8(R8_C, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x4C: + rwrite8(R8_C, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x4D: + rwrite8(R8_C, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x4E: + rwrite8(R8_C, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x4F: + rwrite8(R8_C, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x50 + case 0x50: + rwrite8(R8_D, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x51: + rwrite8(R8_D, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x52: + rwrite8(R8_D, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x53: + rwrite8(R8_D, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x54: + rwrite8(R8_D, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x55: + rwrite8(R8_D, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x56: + rwrite8(R8_D, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x57: + rwrite8(R8_D, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x58: + rwrite8(R8_E, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x59: + rwrite8(R8_E, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x5A: + rwrite8(R8_E, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x5B: + rwrite8(R8_E, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x5C: + rwrite8(R8_E, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x5D: + rwrite8(R8_E, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x5E: + rwrite8(R8_E, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x5F: + rwrite8(R8_E, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x60 + case 0x60: + rwrite8(R8_H, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x61: + rwrite8(R8_H, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x62: + rwrite8(R8_H, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x63: + rwrite8(R8_H, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x64: + rwrite8(R8_H, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x65: + rwrite8(R8_H, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x66: + rwrite8(R8_H, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x67: + rwrite8(R8_H, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x68: + rwrite8(R8_L, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x69: + rwrite8(R8_L, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x6A: + rwrite8(R8_L, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x6B: + rwrite8(R8_L, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x6C: + rwrite8(R8_L, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x6D: + rwrite8(R8_L, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x6E: + rwrite8(R8_L, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x6F: + rwrite8(R8_L, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x70 + case 0x70: + mwrite8(rread16(R16_HL), rread8(R8_B)); + instruction_cost(1, 8); + break; + case 0x71: + mwrite8(rread16(R16_HL), rread8(R8_C)); + instruction_cost(1, 8); + break; + case 0x72: + mwrite8(rread16(R16_HL), rread8(R8_D)); + instruction_cost(1, 8); + break; + case 0x73: + mwrite8(rread16(R16_HL), rread8(R8_E)); + instruction_cost(1, 8); + break; + case 0x74: + mwrite8(rread16(R16_HL), rread8(R8_H)); + instruction_cost(1, 8); + break; + case 0x75: + mwrite8(rread16(R16_HL), rread8(R8_L)); + instruction_cost(1, 8); + break; + case 0x76: + halted = 1; + instruction_cost(1, 4); + break; + case 0x77: + mwrite8(rread16(R16_HL), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x78: + rwrite8(R8_A, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x79: + rwrite8(R8_A, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x7A: + rwrite8(R8_A, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x7B: + rwrite8(R8_A, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x7C: + rwrite8(R8_A, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x7D: + rwrite8(R8_A, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x7E: + rwrite8(R8_A, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x7F: + rwrite8(R8_A, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x80 + case 0x80: + add(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x81: + add(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x82: + add(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x83: + add(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x84: + add(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x85: + add(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x86: + add(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x87: + add(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x88: + adc(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x89: + adc(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x8A: + adc(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x8B: + adc(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x8C: + adc(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x8D: + adc(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x8E: + adc(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x8F: + adc(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x90 + case 0x90: + sub(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x91: + sub(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x92: + sub(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x93: + sub(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x94: + sub(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x95: + sub(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x96: + sub(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x97: + sub(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x98: + sbc(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x99: + sbc(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x9A: + sbc(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x9B: + sbc(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x9C: + sbc(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x9D: + sbc(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x9E: + sbc(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x9F: + sbc(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xA0 + case 0xA0: + and(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xA1: + and(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xA2: + and(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xA3: + and(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xA4: + and(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xA5: + and(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xA6: + and(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xA7: + and(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0xA8: + xor(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xA9: + xor(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xAA: + xor(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xAB: + xor(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xAC: + xor(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xAD: + xor(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xAE: + xor(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xAF: + xor(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xB0 + case 0xB0: + or (rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xB1: + or (rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xB2: + or (rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xB3: + or (rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xB4: + or (rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xB5: + or (rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xB6: + or (mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xB7: + or (rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0xB8: + cp(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xB9: + cp(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xBA: + cp(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xBB: + cp(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xBC: + cp(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xBD: + cp(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xBE: + cp(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xBF: + cp(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xC0 + case 0xC0: + if (!rget(R8_F, FLAG_Z)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xC1: + pop(R16_BC); + instruction_cost(1, 12); + break; + case 0xC2: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xC3: + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + break; + case 0xC4: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xC5: + push(R16_BC); + instruction_cost(1, 16); + break; + case 0xC6: + add(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xC7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x00); + instruction_cost(0, 16); + break; + case 0xC8: + if (rget(R8_F, FLAG_Z)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xC9: + pop(R16_PC); + instruction_cost(0, 16); + break; + case 0xCA: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xCB: + prefix_run(mread8(rread16(R16_PC) + 1)); + break; + case 0xCC: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xCD: + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + break; + case 0xCE: + adc(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xCF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x08); + instruction_cost(0, 16); + break; +#pragma region 0xD0 + case 0xD0: + if (!rget(R8_F, FLAG_C)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xD1: + pop(R16_DE); + instruction_cost(1, 12); + break; + case 0xD2: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xD4: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xD5: + push(R16_DE); + instruction_cost(1, 16); + break; + case 0xD6: + sub(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xD7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x10); + instruction_cost(0, 16); + break; + case 0xD8: + if (rget(R8_F, FLAG_C)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xD9: + pop(R16_PC); + ime = 1; + instruction_cost(0, 16); + break; + case 0xDA: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xDC: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xDE: + sbc(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xDF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x18); + instruction_cost(0, 16); + break; +#pragma region 0xE0 + case 0xE0: + mwrite8(0xFF00 + mread8(rread16(R16_PC) + 1), rread8(R8_A)); + instruction_cost(2, 12); + break; + case 0xE1: + pop(R16_HL); + instruction_cost(1, 12); + break; + case 0xE2: + mwrite8(0xFF00 + rread8(R8_C), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0xE5: + push(R16_HL); + instruction_cost(1, 16); + break; + case 0xE6: + and(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xE7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x20); + instruction_cost(0, 16); + break; + case 0xE8: + rwrite16(R16_SP, rread16(R16_SP) + (s8)mread8(rread16(R16_PC) + 1)); + rclear(R8_F, FLAG_Z); + rclear(R8_F, FLAG_N); + cflag_add((u8)(rread16(R16_SP) & 0xFF), mread8(rread16(R16_PC) + 1)); + hflag_add((u8)(rread16(R16_SP) & 0xFF), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 16); + break; + case 0xE9: + rwrite16(R16_PC, rread16(R16_HL)); + instruction_cost(0, 4); + break; + case 0xEA: + mwrite8(mread16(rread16(R16_PC) + 1), rread8(R8_A)); + instruction_cost(3, 16); + break; + case 0xEE: + xor(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xEF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x28); + instruction_cost(0, 16); + break; +#pragma region 0xF0 + case 0xF0: + rwrite8(R8_A, mread8(0xFF00 + mread8(rread16(R16_PC) + 1))); + instruction_cost(2, 12); + break; + case 0xF1: + pop(R16_AF); + instruction_cost(1, 12); + break; + case 0xF2: + rwrite8(R8_A, mread8(0xFF00 + rread8(R8_C))); + instruction_cost(1, 8); + break; + case 0xF3: + ime = 0; + instruction_cost(1, 4); + break; + case 0xF5: + push(R16_AF); + instruction_cost(1, 16); + break; + case 0xF6: + or (mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xF7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x30); + instruction_cost(0, 16); + break; + case 0xF8: + rwrite16(R16_HL, rread16(R16_SP) + (s8)mread8(rread16(R16_PC) + 1)); + rclear(R8_F, FLAG_Z); + rclear(R8_F, FLAG_N); + hflag_add(rread8(R8_L), mread8(rread16(R16_PC) + 1)); + cflag_add(rread8(R8_L), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0xF9: + rwrite16(R16_SP, rread16(R16_HL)); + instruction_cost(1, 8); + break; + case 0xFA: + rwrite8(R8_A, mread8(mread16(rread16(R16_PC) + 1))); + instruction_cost(3, 16); + break; + case 0xFB: + ime = 1; + instruction_cost(1, 4); + break; + case 0xFE: + cp(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xFF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x38); + instruction_cost(0, 16); + break; + default: + error_handler(); + exit(EXIT_FAILURE); + break; + } + } + } + if (clock_queue > 0) { + clock_queue--; + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7975f6e --- /dev/null +++ b/src/main.c @@ -0,0 +1,45 @@ +#include "pgb.h" +#include +#include +#include +#include + +#define GB_RATE 59.73 +#define CLOCKS_PER_FRAME 70224 +#define TIME_TO_SLEEP (CLOCKS_PER_SEC / GB_RATE) + +int main(const int argc, const char **argv) { + create_window(); + if (argc > 1) { + const char *gamerom = NULL; + const char *logfile = NULL; + int i; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--file") == 0) { + gamerom = argv[++i]; + } else if (strcmp(argv[i], "--log") == 0) { + logfile = argv[++i]; + } + } + if (gamerom != NULL) { + cpu_init(gamerom, logfile); + int timer = time(NULL); + while (1) { + int i; + int start = clock(); + for (i = 0; i < CLOCKS_PER_FRAME; i++) { + cpu_cycle(); + ppu_dot(); + timer_cycle(); + } + while ((clock() - start) < TIME_TO_SLEEP) { + } + if (time(NULL) - timer >= 1) { + timer = time(NULL); + } + } + } + } + close_window(); + return EXIT_SUCCESS; +} diff --git a/src/pgb.h b/src/pgb.h new file mode 100644 index 0000000..1ce05be --- /dev/null +++ b/src/pgb.h @@ -0,0 +1,47 @@ +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed char s16; + +typedef enum R8 { R8_A, R8_F, R8_B, R8_C, R8_D, R8_E, R8_H, R8_L } R8; +typedef enum R16 { R16_AF, R16_BC, R16_DE, R16_HL, R16_SP, R16_PC } R16; +typedef enum RFLAG { + NOFLAG0, + NOFLAG1, + NOFLAG2, + NOFLAG3, + FLAG_C, + FLAG_H, + FLAG_N, + FLAG_Z +} RFLAG; + +typedef enum INTERRUPT { + INT_VBLANK, + INT_LCD, + INT_TIMER, + INT_SERIAL, + INT_JOYPAD +} INTERRUPT; + +void lock_oam(void); +void unlock_oam(void); +void lock_vram(void); +void unlock_vram(void); + +u8 mread8_external(const u16 addr); +u16 mread16_external(const u16 addr); +void mwrite8_external(const u16 addr, const u8 val); +void mwrite16_external(const u16 addr, const u16 val); + +void cpu_init(const char *gamerom, const char *logfile); +void cpu_cycle(void); +void create_interrupt(const INTERRUPT interrupt); + +void ppu_dot(void); + +void timer_cycle(void); + +void create_window(void); +void draw_pixel(u8 x, u8 y, u8 color); +void close_window(void); diff --git a/src/ppu.c b/src/ppu.c new file mode 100644 index 0000000..2f6967f --- /dev/null +++ b/src/ppu.c @@ -0,0 +1,67 @@ +#include "pgb.h" +#include +#include + +static u16 dot; +static u8 scanline; +static u8 x; +static u8 delay; + +#define get_lcdc(bit) ((mread8_external(0xFF40) >> bit) & 1) + +void fetch_bg_pixel(u8 display_x, u8 display_y) { + u8 internal_x = mread8_external(0xFF43) + display_x; + u8 internal_y = mread8_external(0xFF42) + display_y; + u16 tile_addr_offset = + mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) + (u16)(internal_x / 8) + + ((u16)(internal_y / 8)) * 32) * + 16; + u16 tile_addr = get_lcdc(4) ? (0x8000 + tile_addr_offset) + : (0x9000 + (s16)tile_addr_offset); + u8 tile_x = internal_x % 8; + u8 tile_y = internal_y % 8; + u16 tile_data = mread16_external(tile_addr + tile_y * 2); + if (tile_y < 3) { + u16 addr = 0x8000 + (mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) + + (u16)internal_x / 8 + + (u16)((internal_y + 3) / 8) * 32) * + 16); + if (mread16_external(addr + (tile_y + 3) * 2) != tile_data && get_lcdc(4)) { + printf("(u16)(%d) (u16)(%d)\n", (internal_y) / 8, (internal_y + 3) / 8); + } + } + u8 color = (tile_data >> abs(tile_x - 7)) & 1; + color += ((tile_data >> abs(tile_x - 15)) & 1) * 2; + draw_pixel(display_x, display_y, color); +} + +void ppu_dot(void) { + if (dot == 0) { + lock_oam(); + x = 0; + delay = 0; + } else if (dot == 456) { + dot = -1; + scanline++; + if (scanline == 144) { + create_interrupt(INT_VBLANK); + } + if (scanline == 154) { + scanline = 0; + } + } + if (dot >= 80 && x < 160) { + if (delay == 0) { + unlock_vram(); + fetch_bg_pixel(x, scanline); + x++; + lock_vram(); + } else { + delay--; + } + } else if (x == 160) { + unlock_vram(); + unlock_oam(); + } + dot++; +} diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..fda1ae0 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,37 @@ +#include "pgb.h" + +static u16 counter = 0; + +static u16 get_cycles_to_count(void) { + switch (mread8_external(0xFF07) & 3) { + case 0: + return 1024; + break; + case 1: + return 16; + break; + case 2: + return 64; + break; + case 3: + return 256; + break; + } + return 1024; +} + +void timer_cycle(void) { + if (mread8_external(0xFF07) & 4) { + if (counter < get_cycles_to_count()) { + counter++; + } else { + counter = 0; + if (mread8_external(0xFF05) == 0xFF) { + mwrite8_external(0xFF05, mread8_external(0xFF06)); + create_interrupt(INT_TIMER); + } else { + mwrite8_external(0xFF05, mread8_external(0xFF05) + 1); + } + } + } +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..18c0481 --- /dev/null +++ b/src/window.c @@ -0,0 +1,53 @@ +#include "pgb.h" +#include +#include +#include +#include + +static Display *display; +static Window window; +static int screen; + +void create_window(void) { + display = XOpenDisplay(NULL); + if (display == NULL) { + fprintf(stderr, "Cannot open display\n"); + exit(1); + } + + screen = DefaultScreen(display); + + window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, + 320, 288, 1, BlackPixel(display, screen), + WhitePixel(display, screen)); + + XStoreName(display, window, "PGB"); + XSelectInput(display, window, ExposureMask | KeyPressMask); + XMapWindow(display, window); +} + +void draw_pixel(u8 x, u8 y, u8 color) { + unsigned long real_color = 0xFFFFFFFF; + switch (color) { + case 0: + real_color = 0xFFFFFFFF; + break; + case 1: + real_color = 0xAAAAAAAA; + break; + case 2: + real_color = 0x55555555; + break; + case 3: + real_color = 0x00000000; + break; + } + XSetForeground(display, DefaultGC(display, screen), real_color); + XFillRectangle(display, window, DefaultGC(display, screen), x * 2, y * 2, 2, + 2); +} + +void close_window(void) { + XDestroyWindow(display, window); + XCloseDisplay(display); +}