aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Murphy <alec@checksum.fail>2020-02-16 18:17:17 -0500
committerAlec Murphy <alec@checksum.fail>2020-02-16 18:17:17 -0500
commitd4588bba4259f81a7ef00db0dd909caa7a337a5c (patch)
tree19d62add575eaf44867a3e15bb9c68bdc084792e
parenta6c2e6229130cc80e15e9fa61456d809c47eabd9 (diff)
Add files to repository
-rw-r--r--.gitignore1
-rw-r--r--CPU.HC2723
-rw-r--r--Font.HC90
-rw-r--r--Lib/Debugger.HC148
-rw-r--r--Lib/Display.HC17
-rw-r--r--Lib/Graphics2D.HC250
-rw-r--r--Lib/Misc.HC56
-rw-r--r--Lib/VMSVGA.HC96
-rw-r--r--Lib/uPNG.HC1073
-rw-r--r--LoROM.HC42
-rw-r--r--Load.HC106
-rw-r--r--MMU.HC436
-rw-r--r--PPU.HC1397
-rw-r--r--Run.HC1
-rw-r--r--Snes.HC584
-rw-r--r--font.pngbin0 -> 2704 bytes
16 files changed, 7020 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 864e224..94cabb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+uncrustify.cfg
.vscode/
ROMs/
SnesJs/
diff --git a/CPU.HC b/CPU.HC
new file mode 100644
index 0000000..c573d93
--- /dev/null
+++ b/CPU.HC
@@ -0,0 +1,2723 @@
+extern I32 getAdr(I32 opcode, I32 mode);
+extern U0 cpu_opcode(I32 instr, I32 adr, I32 adrh);
+
+ // indexes in register arrays
+#define DBR 0 // data bank register
+#define K 1 // program bank
+
+#define A 0
+#define X 1
+#define Y 2
+#define SP 3
+#define PC 4
+#define DPR 5 // direct page register
+
+ // addressing modes
+#define IMP 0 // or ACC
+#define IMM 1 // always 8 bit
+#define IMMm 2 // size depends on m flag
+#define IMMx 3 // size depends on x flag
+#define IMMl 4 // always 16 bit
+#define DP 5
+#define DPX 6
+#define DPY 7
+#define IDP 8
+#define IDX 9
+#define IDY 10 // for RMW and writes
+#define IDYr 11 // for reads
+#define IDL 12
+#define ILY 13
+#define SR 14
+#define ISY 15
+#define ABS 16
+#define ABX 17// for RMW and writes
+#define ABXr 18// for reads
+#define ABY 19// for RMW and writes
+#define ABYr 20// for reads
+#define ABL 21
+#define ALX 22
+#define IND 23
+#define IAX 24
+#define IAL 25
+#define REL 26
+#define RLL 27
+#define BM 28// block move
+
+ // memory handler
+ //mem = mem;
+
+ // registers
+ U16 r[2];
+ U16 br[6];
+
+ // flags
+ Bool n;
+ Bool v;
+ Bool m;
+ Bool x;
+ Bool d;
+ Bool i;
+ Bool z;
+ Bool c;
+ Bool e;
+
+ //Bool irqWanted;
+ //Bool nmiWanted;
+ Bool aboWanted;
+
+ Bool stopped;
+ Bool waiting;
+
+ //I32 cyclesLeft;
+
+
+ // modes for each instruction
+ I32 modes[259] = {
+ IMP, IDX, IMM, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, DP , DPX, DPX, ILY, IMP, ABYr,IMP, IMP, ABS, ABXr,ABX, ALX,
+ ABS, IDX, ABL, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, DPX, DPX, DPX, ILY, IMP, ABYr,IMP, IMP, ABXr,ABXr,ABX, ALX,
+ IMP, IDX, IMM, SR , BM , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, BM , DPX, DPX, ILY, IMP, ABYr,IMP, IMP, ABL, ABXr,ABX, ALX,
+ IMP, IDX, RLL, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, IND, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, DPX, DPX, DPX, ILY, IMP, ABYr,IMP, IMP, IAX, ABXr,ABX, ALX,
+ REL, IDX, RLL, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDY, IDP, ISY, DPX, DPX, DPY, ILY, IMP, ABY, IMP, IMP, ABS, ABX, ABX, ALX,
+ IMMx,IDX, IMMx,SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, DPX, DPX, DPY, ILY, IMP, ABYr,IMP, IMP, ABXr,ABXr,ABYr,ALX,
+ IMMx,IDX, IMM, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, DP , DPX, DPX, ILY, IMP, ABYr,IMP, IMP, IAL, ABXr,ABX, ALX,
+ IMMx,IDX, IMM, SR , DP , DP , DP , IDL, IMP, IMMm,IMP, IMP, ABS, ABS, ABS, ABL,
+ REL, IDYr,IDP, ISY, IMMl,DPX, DPX, ILY, IMP, ABYr,IMP, IMP, IAX, ABXr,ABX, ALX,
+ IMP, IMP, IMP // abo, nmi, irq
+ };
+
+ // cycles for each instruction
+ I32 cycles[259] = {
+ 7, 6, 7, 4, 5, 3, 5, 6, 3, 2, 2, 4, 6, 4, 6, 5,
+ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 2, 2, 6, 4, 7, 5,
+ 6, 6, 8, 4, 3, 3, 5, 6, 4, 2, 2, 5, 4, 4, 6, 5,
+ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 2, 2, 4, 4, 7, 5,
+ 6, 6, 2, 4, 7, 3, 5, 6, 3, 2, 2, 3, 3, 4, 6, 5,
+ 2, 5, 5, 7, 7, 4, 6, 6, 2, 4, 3, 2, 4, 4, 7, 5,
+ 6, 6, 6, 4, 3, 3, 5, 6, 4, 2, 2, 6, 5, 4, 6, 5,
+ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 4, 2, 6, 4, 7, 5,
+ 3, 6, 4, 4, 3, 3, 3, 6, 2, 2, 2, 3, 4, 4, 4, 5,
+ 2, 6, 5, 7, 4, 4, 4, 6, 2, 5, 2, 2, 4, 5, 5, 5,
+ 2, 6, 2, 4, 3, 3, 3, 6, 2, 2, 2, 4, 4, 4, 4, 5,
+ 2, 5, 5, 7, 4, 4, 4, 6, 2, 4, 2, 2, 4, 4, 4, 5,
+ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5,
+ 2, 5, 5, 7, 6, 4, 6, 6, 2, 4, 3, 3, 6, 4, 7, 5,
+ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5,
+ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 4, 2, 8, 4, 7, 5,
+ 7, 7, 7 // abo, nmi, irq
+ };
+
+ // function table is at bottom
+
+ U0 cpu_reset() {
+
+ r[DBR] = 0;
+ r[K] = 0;
+
+ br[A] = 0;
+ br[X] = 0;
+ br[Y] = 0;
+ br[SP] = 0;
+ br[DPR] = 0;
+
+ //if(mem_read) {
+ // read emulation mode reset vector
+ br[PC] = mem_read(0xfffc) | (mem_read(0xfffd) << 8);
+ //} else {
+ // if read function is not defined yet
+ // br[PC] = 0;
+ //}
+
+ // flags
+ n = FALSE;
+ v = FALSE;
+ m = TRUE;
+ x = TRUE;
+ d = FALSE;
+ i = FALSE;
+ z = FALSE;
+ c = FALSE;
+ e = TRUE;
+
+ // interrupts wanted
+ irqWanted = FALSE;
+ nmiWanted = FALSE;
+ aboWanted = FALSE;
+
+ // power state
+ stopped = FALSE;
+ waiting = FALSE;
+
+ // cycles left
+ cyclesLeft = 7;
+
+ }
+ cpu_reset;
+
+ U0 cpu_cycle() {
+ I32 instr;
+ I32 mode;
+ I64 adrs;
+ if(cyclesLeft == 0) {
+ if(stopped) {
+ // stopped
+ cyclesLeft = 1;
+ } else if(!waiting) {
+ // read opcode byte
+ instr = mem_read((r[K] << 16) | br[PC]++);
+ cyclesLeft = cycles[instr];
+ mode = modes[instr];
+ // test for interrupt
+ if((irqWanted && !i) || nmiWanted || aboWanted) {
+ br[PC]--;
+ if(aboWanted) {
+ aboWanted = FALSE;
+ instr = 0x100;
+ } else if(nmiWanted) {
+ nmiWanted = FALSE;
+ instr = 0x101;
+ } else {
+ // irq (level sensitive instead of edge sensitive)
+ instr = 0x102;
+ }
+ cyclesLeft = cycles[instr];
+ mode = modes[instr];
+ }
+ // execute the instruction
+ adrs = getAdr(instr, mode);
+ // TEMP: log unknown instruction
+ /*
+ if(functions[instr] == undefined) {
+ uni(adrs.u8[0], adrs.u8[1], instr);
+ } else {
+ functions[instr].call(this, adrs.u8[0], adrs.u8[1]);
+ }
+ */
+ cpu_opcode(instr, adrs.i32[0], adrs.i32[1]);
+ } else {
+ // waiting on interrupt
+ if(aboWanted || irqWanted || nmiWanted) {
+ waiting = FALSE;
+ // on next cycle, find the nmi or abort and start executing it,
+ // or continue on as a fast irq if i is 1
+ }
+ cyclesLeft = 1;
+ }
+ }
+ cyclesLeft--;
+ }
+
+ I32 cpu_getP() {
+ I32 val = 0;
+ val |= cond(n, 0x80, 0);
+ val |= cond(v, 0x40, 0);
+ val |= cond(m, 0x20, 0);
+ val |= cond(x, 0x10, 0);
+ val |= cond(d, 0x08, 0);
+ val |= cond(i, 0x04, 0);
+ val |= cond(z, 0x02, 0);
+ val |= cond(c, 0x01, 0);
+ return val;
+ }
+
+ U0 cpu_setP(I32 value) {
+ n = (value & 0x80) > 0;
+ v = (value & 0x40) > 0;
+ m = (value & 0x20) > 0;
+ x = (value & 0x10) > 0;
+ d = (value & 0x08) > 0;
+ i = (value & 0x04) > 0;
+ z = (value & 0x02) > 0;
+ c = (value & 0x01) > 0;
+ if(x) {
+ br[X] &= 0xff;
+ br[Y] &= 0xff;
+ }
+ }
+
+ U0 setZandN(I32 value, Bool byte) {
+ // sets Zero and Negative depending on 8-bit or 16-bit value
+ if(byte) {
+ z = (value & 0xff) == 0;
+ n = (value & 0x80) > 0;
+ return;
+ }
+ z = (value & 0xffff) == 0;
+ n = (value & 0x8000) > 0;
+ }
+
+ I16 getSigned(I32 value, Bool byte) {
+ // turns unsinged value 0 - 255 or 0 - 65536
+ // to signed -128 - 127 or -32768 - 32767
+ if(byte) {
+ return cond((value & 0xff) > 127, -(256 - (value & 0xff)), (value & 0xff));
+ }
+ return cond(value > 32767, -(65536 - value), value);
+ }
+
+ U0 doBranch(Bool check, I32 rel) {
+ if(check) {
+ // taken branch: 1 extra cycle
+ cyclesLeft++;
+ br[PC] += rel;
+ }
+ }
+
+ U0 pushByte(I32 value) {
+ mem_write(br[SP], value);
+ br[SP]--;
+ }
+
+ U8 pullByte() {
+ br[SP]++;
+ return mem_read(br[SP]);
+ }
+
+ U0 pushWord(I32 value) {
+ pushByte((value & 0xff00) >> 8);
+ pushByte(value & 0xff);
+ }
+
+ U16 pullWord() {
+ I32 value = pullByte();
+ value |= pullByte() << 8;
+ return value;
+ }
+
+ U16 readWord(I32 adr, I32 adrh) {
+ U16 value = mem_read(adr);
+ value |= mem_read(adrh) << 8;
+ return value;
+ }
+
+ U0 writeWord(I32 adr, I32 adrh, I32 result, Bool reversed = FALSE) {
+ if(reversed) {
+ // RMW opcodes write the high byte first
+ mem_write(adrh, (result & 0xff00) >> 8);
+ mem_write(adr, result & 0xff);
+ } else {
+ mem_write(adr, result & 0xff);
+ mem_write(adrh, (result & 0xff00) >> 8);
+ }
+ }
+
+ I32 getAdr(I32 opcode, I32 mode) {
+ no_warn opcode;
+ I64 retval;
+ I32 low;
+ I32 adr;
+ I32 rel;
+ I32 dest;
+ I32 src;
+ I32 pointer;
+ // gets the effective address (low and high), for the given adressing mode
+ switch(mode) {
+ case IMP: {
+ // implied
+ //return [0, 0];
+ retval = 0;
+ return retval;
+ }
+
+ case IMM: {
+ // immediate, always 8 bit
+ //return [(r[K] << 16) | br[PC]++, 0];
+ retval.i32[0] = (r[K] << 16) | br[PC]++;
+ retval.i32[1] = 0;
+ return retval;
+ }
+
+ case IMMm: {
+ // immediate, depending on m
+ if(m) {
+ //return [(r[K] << 16) | br[PC]++, 0];
+ retval.i32[0] = (r[K] << 16) | br[PC]++;
+ retval.i32[1] = 0;
+ return retval;
+ } else {
+ low = (r[K] << 16) | br[PC]++;
+ //return [low, (r[K] << 16) | br[PC]++];
+ retval.i32[0] = low;
+ retval.i32[1] = (r[K] << 16) | br[PC]++;
+ return retval;
+ }
+ }
+
+ case IMMx: {
+ // immediate, depending on x
+ if(x) {
+ //return [(r[K] << 16) | br[PC]++, 0];
+ retval.i32[0] = (r[K] << 16) | br[PC]++;
+ retval.i32[1] = 0;
+ return retval;
+
+ } else {
+ low = (r[K] << 16) | br[PC]++;
+ //return [low, (r[K] << 16) | br[PC]++];
+ retval.i32[0] = low;
+ retval.i32[1] = (r[K] << 16) | br[PC]++;
+ return retval;
+ }
+ }
+
+ case IMMl: {
+ // immediate, always 16 bit
+ low = (r[K] << 16) | br[PC]++;
+ //return [low, (r[K] << 16) | br[PC]++];
+ retval.i32[0] = low;
+ retval.i32[1] = (r[K] << 16) | br[PC]++;
+ return retval;
+ }
+
+ case DP: {
+ // direct page
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (br[DPR] + adr) & 0xffff,
+ (br[DPR] + adr + 1) & 0xffff
+ ];
+ */
+ retval.i32[0] = (br[DPR] + adr) & 0xffff;
+ retval.i32[1] = (br[DPR] + adr + 1) & 0xffff;
+ return retval;
+ }
+
+ case DPX: {
+ // direct page indexed on X
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (br[DPR] + adr + br[X]) & 0xffff,
+ (br[DPR] + adr + br[X] + 1) & 0xffff
+ ];
+ */
+ retval.i32[0] = (br[DPR] + adr + br[X]) & 0xffff;
+ retval.i32[1] = (br[DPR] + adr + br[X] + 1) & 0xffff;
+ return retval;
+ }
+
+ case DPY: {
+ // direct page indexed on Y
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (br[DPR] + adr + br[Y]) & 0xffff,
+ (br[DPR] + adr + br[Y] + 1) & 0xffff
+ ];
+ */
+ retval.i32[0] = (br[DPR] + adr + br[Y]) & 0xffff;
+ retval.i32[1] = (br[DPR] + adr + br[Y] + 1) & 0xffff;
+ return retval;
+ }
+
+ case IDP: {
+ // direct indirect
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read((br[DPR] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[DPR] + adr + 1) & 0xffff)
+ ) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + pointer,
+ (r[DBR] << 16) + pointer + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + pointer;
+ retval.i32[1] = (r[DBR] << 16) + pointer + 1;
+ return retval;
+
+ }
+
+ case IDX: {
+ // direct indirect indexed
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read(
+ (br[DPR] + adr + br[X]) & 0xffff
+ );
+ pointer |= (
+ mem_read((br[DPR] + adr + br[X] + 1) & 0xffff)
+ ) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + pointer,
+ (r[DBR] << 16) + pointer + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + pointer;
+ retval.i32[1] = (r[DBR] << 16) + pointer + 1;
+ return retval;
+
+ }
+
+ case IDY: {
+ // indirect direct indexed, for RMW and writes
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read((br[DPR] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[DPR] + adr + 1) & 0xffff)
+ ) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + pointer + br[Y],
+ (r[DBR] << 16) + pointer + br[Y] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + pointer + br[Y];
+ retval.i32[1] = (r[DBR] << 16) + pointer + br[Y] + 1;
+ return retval;
+
+ }
+
+ case IDYr: {
+ // indirect direct indexed, for reads (possible extra cycle)
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read((br[DPR] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[DPR] + adr + 1) & 0xffff)
+ ) << 8;
+ if(((pointer >> 8) != ((pointer + br[Y]) >> 8)) || !x) {
+ // if page is crossed, or x is 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (r[DBR] << 16) + pointer + br[Y],
+ (r[DBR] << 16) + pointer + br[Y] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + pointer + br[Y];
+ retval.i32[1] = (r[DBR] << 16) + pointer + br[Y] + 1;
+ return retval;
+
+ }
+
+ case IDL: {
+ // indirect direct long
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read((br[DPR] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[DPR] + adr + 1) & 0xffff)
+ ) << 8;
+ pointer |= (
+ mem_read((br[DPR] + adr + 2) & 0xffff)
+ ) << 16;
+ //return [pointer, pointer + 1];
+ retval.i32[0] = pointer;
+ retval.i32[1] = pointer + 1;
+ return retval;
+
+ }
+
+ case ILY: {
+ // indirect direct long indexed
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ if((br[DPR] & 0xff) != 0) {
+ // DPRl not 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ pointer = mem_read((br[DPR] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[DPR] + adr + 1) & 0xffff)
+ ) << 8;
+ pointer |= (
+ mem_read((br[DPR] + adr + 2) & 0xffff)
+ ) << 16;
+ //return [pointer + br[Y], pointer + br[Y] + 1];
+ retval.i32[0] = pointer + br[Y];
+ retval.i32[1] = pointer + br[Y] + 1;
+ return retval;
+
+ }
+
+ case SR: {
+ // stack relative
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ /*
+ return [
+ (br[SP] + adr) & 0xffff,
+ (br[SP] + adr + 1) & 0xffff,
+ ];
+ */
+ retval.i32[0] = (br[SP] + adr) & 0xffff;
+ retval.i32[1] = (br[SP] + adr + 1) & 0xffff;
+ return retval;
+
+ }
+
+ case ISY: {
+ // stack relative indexed
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ pointer = mem_read((br[SP] + adr) & 0xffff);
+ pointer |= (
+ mem_read((br[SP] + adr + 1) & 0xffff)
+ ) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + pointer + br[Y],
+ (r[DBR] << 16) + pointer + br[Y] + 1,
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + pointer + br[Y];
+ retval.i32[1] = (r[DBR] << 16) + pointer + br[Y] + 1;
+ return retval;
+
+ }
+
+ case ABS: {
+ // absolute
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ //return [(r[DBR] << 16) + adr, (r[DBR] << 16) + adr + 1];
+ retval.i32[0] = (r[DBR] << 16) + adr;
+ retval.i32[1] = (r[DBR] << 16) + adr + 1;
+ return retval;
+
+ }
+
+ case ABX: {
+ // absolute, indexed by X for RMW and writes
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + adr + br[X],
+ (r[DBR] << 16) + adr + br[X] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + adr + br[X];
+ retval.i32[1] = (r[DBR] << 16) + adr + br[X] + 1;
+ return retval;
+
+ }
+
+ case ABXr: {
+ // absolute, indexed by X for reads (possible extra cycle)
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ if(((adr >> 8) != ((adr + br[X]) >> 8)) || !x) {
+ // if page crossed or x is 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (r[DBR] << 16) + adr + br[X],
+ (r[DBR] << 16) + adr + br[X] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + adr + br[X];
+ retval.i32[1] = (r[DBR] << 16) + adr + br[X] + 1;
+ return retval;
+
+ }
+
+ case ABY: {
+ // absolute, indexed by Y for RMW and writes
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ /*
+ return [
+ (r[DBR] << 16) + adr + br[Y],
+ (r[DBR] << 16) + adr + br[Y] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + adr + br[Y];
+ retval.i32[1] = (r[DBR] << 16) + adr + br[Y] + 1;
+ return retval;
+
+ }
+
+ case ABYr: {
+ // absolute, indexed by Y for reads (possible extra cycle)
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ if(((adr >> 8) != ((adr + br[Y]) >> 8)) || !x) {
+ // if page crossed or x is 0: 1 extra cycle
+ cyclesLeft++;
+ }
+ /*
+ return [
+ (r[DBR] << 16) + adr + br[Y],
+ (r[DBR] << 16) + adr + br[Y] + 1
+ ];
+ */
+ retval.i32[0] = (r[DBR] << 16) + adr + br[Y];
+ retval.i32[1] = (r[DBR] << 16) + adr + br[Y] + 1;
+ return retval;
+
+ }
+
+ case ABL: {
+ // absoulte long
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 16;
+ //return [adr, adr + 1];
+ retval.i32[0] = adr;
+ retval.i32[1] = adr + 1;
+ return retval;
+
+ }
+
+ case ALX: {
+ // absoulte long indexed
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 16;
+ //return [adr + br[X], adr + br[X] + 1];
+ retval.i32[0] = adr + br[X];
+ retval.i32[1] = adr + br[X] + 1;
+ return retval;
+
+ }
+
+ case IND: {
+ // indirect
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ pointer = mem_read(adr);
+ pointer |= mem_read((adr + 1) & 0xffff) << 8;
+ //return [(r[K] << 16) + pointer, 0];
+ retval.i32[0] = (r[K] << 16) + pointer;
+ retval.i32[1] = 0;
+ return retval;
+
+ }
+
+ case IAX: {
+ // indirect indexed
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ pointer = mem_read(
+ (r[K] << 16) | ((adr + br[X]) & 0xffff)
+ );
+ pointer |= mem_read(
+ (r[K] << 16) | ((adr + br[X] + 1) & 0xffff)
+ ) << 8;
+ //return [(r[K] << 16) + pointer, 0];
+ retval.i32[0] = (r[K] << 16) + pointer;
+ retval.i32[1] = 0;
+ return retval;
+
+ }
+
+ case IAL: {
+ // indirect long
+ adr = mem_read((r[K] << 16) | br[PC]++);
+ adr |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ pointer = mem_read(adr);
+ pointer |= mem_read((adr + 1) & 0xffff) << 8;
+ pointer |= mem_read((adr + 2) & 0xffff) << 16;
+ //return [pointer, 0];
+ retval.i32[0] = pointer;
+ retval.i32[1] = 0;
+ return retval;
+
+ }
+
+ case REL: {
+ // relative
+ rel = mem_read((r[K] << 16) | br[PC]++);
+ //return [getSigned(rel, TRUE), 0];
+ retval.i32[0] = getSigned(rel, TRUE);
+ retval.i32[1] = 0;
+ return retval;
+
+ }
+
+ case RLL: {
+ // relative long
+ rel = mem_read((r[K] << 16) | br[PC]++);
+ rel |= mem_read((r[K] << 16) | br[PC]++) << 8;
+ //return [getSigned(rel, FALSE), 0];
+ retval.i32[0] = getSigned(rel, FALSE);
+ retval.i32[1] = 0;
+ return retval;
+
+ }
+
+ case BM: {
+ // block move
+ dest = mem_read((r[K] << 16) | br[PC]++);
+ src = mem_read((r[K] << 16) | br[PC]++);
+ //return [dest, src];
+ retval.i32[0] = dest;
+ retval.i32[1] = src;
+ return retval;
+
+ }
+ }
+ }
+
+ // instruction functions
+
+ U0 uni(I32 adr, I32 adrh, I32 instr) {
+ no_warn adr, adrh, instr;
+ // unimplemented
+ /*
+ console.log(
+ "Uninplemented instruction: " + instr.toString(16) +
+ " reading at adrl " + adr.toString(16) +
+ " and adrh " + adrh.toString(16)
+ );
+ */
+ }
+
+ U0 adc (I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr);
+ if(d) {
+ result = (br[A] & 0xf) + (value & 0xf) + cond(c, 1, 0);
+ result += cond(result > 9, 6, 0);
+ result = (
+ (br[A] & 0xf0) + (value & 0xf0) +
+ cond(result > 0xf, 0x10, 0) + (result & 0xf)
+ );
+ } else {
+ result = (br[A] & 0xff) + value + cond(c, 1, 0);
+ }
+ v = (
+ (br[A] & 0x80) == (value & 0x80) &&
+ (value & 0x80) != (result & 0x80)
+ );
+ result += cond((d && result > 0x9f), 0x60, 0);
+ c = result > 0xff;
+ setZandN(result, m);
+ br[A] = (br[A] & 0xff00) | (result & 0xff);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ if(d) {
+ result = (br[A] & 0xf) + (value & 0xf) + cond(c, 1, 0);
+ result += cond(result > 9, 6, 0);
+ result = (
+ (br[A] & 0xf0) + (value & 0xf0) +
+ cond(result > 0xf, 0x10, 0) + (result & 0xf)
+ );
+ result += cond(result > 0x9f, 0x60, 0);
+ result = (
+ (br[A] & 0xf00) + (value & 0xf00) +
+ cond(result > 0xff, 0x100, 0) + (result & 0xff)
+ );
+ result += cond(result > 0x9ff, 0x600, 0);
+ result = (
+ (br[A] & 0xf000) + (value & 0xf000) +
+ cond(result > 0xfff, 0x1000, 0) + (result & 0xfff)
+ );
+ } else {
+ result = br[A] + value + cond(c, 1, 0);
+ }
+ v = (
+ (br[A] & 0x8000) == (value & 0x8000) &&
+ (value & 0x8000) != (result & 0x8000)
+ );
+ result += cond((d && result > 0x9fff), 0x6000, 0);
+ c = result > 0xffff;
+ setZandN(result, m);
+ br[A] = result;
+ }
+ }
+
+ U0 sbc (I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr) ^ 0xff;
+ if(d) {
+ result = (br[A] & 0xf) + (value & 0xf) + cond(c, 1, 0);
+ result -= cond(result <= 0xf, 6, 0);
+ result = (
+ (br[A] & 0xf0) + (value & 0xf0) +
+ cond(result > 0xf, 0x10, 0) + (result & 0xf)
+ );
+ } else {
+ result = (br[A] & 0xff) + value + cond(c, 1, 0);
+ }
+ v = (
+ (br[A] & 0x80) == (value & 0x80) &&
+ (value & 0x80) != (result & 0x80)
+ );
+ result -= cond((d && result <= 0xff), 0x60, 0);
+ c = result > 0xff;
+ setZandN(result, m);
+ br[A] = (br[A] & 0xff00) | (result & 0xff);
+ } else {
+ value = readWord(adr, adrh) ^ 0xffff;
+ cyclesLeft++; // 1 extra cycle if m = 0
+ if(d) {
+ result = (br[A] & 0xf) + (value & 0xf) + cond(c, 1, 0);
+ result -= cond(result <= 0x0f, 6, 0);
+ result = (
+ (br[A] & 0xf0) + (value & 0xf0) +
+ cond(result > 0xf, 0x10, 0) + (result & 0xf)
+ );
+ result -= cond(result <= 0xff, 0x60, 0);
+ result = (
+ (br[A] & 0xf00) + (value & 0xf00) +
+ cond(result > 0xff, 0x100, 0) + (result & 0xff)
+ );
+ result -= cond(result <= 0xfff, 0x600, 0);
+ result = (
+ (br[A] & 0xf000) + (value & 0xf000) +
+ cond(result > 0xfff, 0x1000, 0) + (result & 0xfff)
+ );
+ } else {
+ result = br[A] + value + cond(c, 1, 0);
+ }
+ v = (
+ (br[A] & 0x8000) == (value & 0x8000) &&
+ (value & 0x8000) != (result & 0x8000)
+ );
+ result -= cond((d && result <= 0xffff), 0x6000, 0);
+ c = result > 0xffff;
+ setZandN(result, m);
+ br[A] = result;
+ }
+ }
+
+ U0 cmp (I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr) ^ 0xff;
+ result = (br[A] & 0xff) + value + 1;
+ c = result > 0xff;
+ setZandN(result, m);
+ } else {
+ value = readWord(adr, adrh) ^ 0xffff;
+ cyclesLeft++; // 1 extra cycle if m = 0
+ result = br[A] + value + 1;
+ c = result > 0xffff;
+ setZandN(result, m);
+ }
+ }
+
+ U0 cpx(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(x) {
+ value = mem_read(adr) ^ 0xff;
+ result = (br[X] & 0xff) + value + 1;
+ c = result > 0xff;
+ setZandN(result, x);
+ } else {
+ value = readWord(adr, adrh) ^ 0xffff;
+ cyclesLeft++; // 1 extra cycle if x = 0
+ result = br[X] + value + 1;
+ c = result > 0xffff;
+ setZandN(result, x);
+ }
+ }
+
+ U0 cpy(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(x) {
+ value = mem_read(adr) ^ 0xff;
+ result = (br[Y] & 0xff) + value + 1;
+ c = result > 0xff;
+ setZandN(result, x);
+ } else {
+ value = readWord(adr, adrh) ^ 0xffff;
+ cyclesLeft++; // 1 extra cycle if x = 0
+ result = br[Y] + value + 1;
+ c = result > 0xffff;
+ setZandN(result, x);
+ }
+ }
+
+ U0 dec(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ result = (mem_read(adr) - 1) & 0xff;
+ setZandN(result, m);
+ mem_write(adr, result);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ result = (value - 1) & 0xffff;
+ setZandN(result, m);
+ writeWord(adr, adrh, result, TRUE);
+ }
+ }
+
+ U0 deca(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(m) {
+ result = ((br[A] & 0xff) - 1) & 0xff;
+ setZandN(result, m);
+ br[A] = br[A] & 0xff00 | result;
+ } else {
+ br[A]--;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 dex(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(x) {
+ result = ((br[X] & 0xff) - 1) & 0xff;
+ setZandN(result, x);
+ br[X] = result;
+ } else {
+ br[X]--;
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 dey(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(x) {
+ result = ((br[Y] & 0xff) - 1) & 0xff;
+ setZandN(result, x);
+ br[Y] = result;
+ } else {
+ br[Y]--;
+ setZandN(br[Y], x);
+ }
+ }
+
+ U0 inc(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ result = (mem_read(adr) + 1) & 0xff;
+ setZandN(result, m);
+ mem_write(adr, result);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ result = (value + 1) & 0xffff;
+ setZandN(result, m);
+ writeWord(adr, adrh, result, TRUE);
+ }
+ }
+
+ U0 inca(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(m) {
+ result = ((br[A] & 0xff) + 1) & 0xff;
+ setZandN(result, m);
+ br[A] = br[A] & 0xff00 | result;
+ } else {
+ br[A]++;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 inx(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(x) {
+ result = ((br[X] & 0xff) + 1) & 0xff;
+ setZandN(result, x);
+ br[X] = result;
+ } else {
+ br[X]++;
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 iny(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 result;
+ if(x) {
+ result = ((br[Y] & 0xff) + 1) & 0xff;
+ setZandN(result, x);
+ br[Y] = result;
+ } else {
+ br[Y]++;
+ setZandN(br[Y], x);
+ }
+ }
+
+ U0 and(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ br[A] = (br[A] & 0xff00) | ((br[A] & value) & 0xff);
+ setZandN(br[A], m);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ br[A] &= value;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 eor(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ br[A] = (br[A] & 0xff00) | ((br[A] ^ value) & 0xff);
+ setZandN(br[A], m);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ br[A] ^= value;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 ora(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ br[A] = (br[A] & 0xff00) | ((br[A] | value) & 0xff);
+ setZandN(br[A], m);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ br[A] |= value;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 bit(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr);
+ result = (br[A] & 0xff) & value;
+ z = result == 0;
+ n = (value & 0x80) > 0;
+ v = (value & 0x40) > 0;
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ result = br[A] & value;
+ z = result == 0;
+ n = (value & 0x8000) > 0;
+ v = (value & 0x4000) > 0;
+ }
+ }
+
+ U0 biti(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr);
+ result = (br[A] & 0xff) & value;
+ z = result == 0;
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft++; // 1 extra cycle if m = 0
+ result = br[A] & value;
+ z = result == 0;
+ }
+ }
+
+ U0 trb(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr);
+ result = (br[A] & 0xff) & value;
+ value = (value & ~(br[A] & 0xff)) & 0xff;
+ z = result == 0;
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ result = br[A] & value;
+ value = (value & ~br[A]) & 0xffff;
+ z = result == 0;
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 tsb(I32 adr, I32 adrh) {
+ I32 value, result;
+ if(m) {
+ value = mem_read(adr);
+ result = (br[A] & 0xff) & value;
+ value = (value | (br[A] & 0xff)) & 0xff;
+ z = result == 0;
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ result = br[A] & value;
+ value = (value | br[A]) & 0xffff;
+ z = result == 0;
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 asl(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ c = (value & 0x80) > 0;
+ value <<= 1;
+ setZandN(value, m);
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ c = (value & 0x8000) > 0;
+ value <<= 1;
+ setZandN(value, m);
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 asla(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 value;
+ if(m) {
+ value = br[A] & 0xff;
+ c = (value & 0x80) > 0;
+ value <<= 1;
+ setZandN(value, m);
+ br[A] = (br[A] & 0xff00) | (value & 0xff);
+ } else {
+ c = (br[A] & 0x8000) > 0;
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ br[A] <<= 1;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 lsr(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ c = (value & 0x1) > 0;
+ value >>= 1;
+ setZandN(value, m);
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ c = (value & 0x1) > 0;
+ value >>= 1;
+ setZandN(value, m);
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 lsra(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 value;
+ if(m) {
+ value = br[A] & 0xff;
+ c = (value & 0x1) > 0;
+ value >>= 1;
+ setZandN(value, m);
+ br[A] = (br[A] & 0xff00) | (value & 0xff);
+ } else {
+ c = (br[A] & 0x1) > 0;
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ br[A] >>= 1;
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 rol(I32 adr, I32 adrh) {
+ I32 value;
+ if(m) {
+ value = mem_read(adr);
+ value = (value << 1) | cond(c, 1, 0);
+ c = (value & 0x100) > 0;
+ setZandN(value, m);
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ value = (value << 1) | cond(c, 1, 0);
+ c = (value & 0x10000) > 0;
+ setZandN(value, m);
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 rola(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 value;
+ if(m) {
+ value = br[A] & 0xff;
+ value = (value << 1) | cond(c, 1, 0);
+ c = (value & 0x100) > 0;
+ setZandN(value, m);
+ br[A] = (br[A] & 0xff00) | (value & 0xff);
+ } else {
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ value = (br[A] << 1) | cond(c, 1, 0);
+ c = (value & 0x10000) > 0;
+ setZandN(value, m);
+ br[A] = value;
+ }
+ }
+
+ U0 ror(I32 adr, I32 adrh) {
+ I32 value, carry;
+ if(m) {
+ value = mem_read(adr);
+ carry = value & 0x1;
+ value = (value >> 1) | cond(c, 0x80, 0);
+ c = carry > 0;
+ setZandN(value, m);
+ mem_write(adr, value);
+ } else {
+ value = readWord(adr, adrh);
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ carry = value & 0x1;
+ value = (value >> 1) | cond(c, 0x8000, 0);
+ c = carry > 0;
+ setZandN(value, m);
+ writeWord(adr, adrh, value, TRUE);
+ }
+ }
+
+ U0 rora(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 value, carry;
+ if(m) {
+ value = br[A] & 0xff;
+ carry = value & 0x1;
+ value = (value >> 1) | cond(c, 0x80, 0);
+ c = carry > 0;
+ setZandN(value, m);
+ br[A] = (br[A] & 0xff00) | (value & 0xff);
+ } else {
+ cyclesLeft += 2; // 2 extra cycles if m = 0
+ carry = br[A] & 0x1;
+ value = (br[A] >> 1) | cond(c, 0x8000, 0);
+ c = carry > 0;
+ setZandN(value, m);
+ br[A] = value;
+ }
+ }
+
+ U0 bcc(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(!c, adr);
+ }
+
+ U0 bcs(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(c, adr);
+ }
+
+ U0 beq(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(z, adr);
+ }
+
+ U0 bmi(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(n, adr);
+ }
+
+ U0 bne(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(!z, adr);
+ }
+
+ U0 bpl(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(!n, adr);
+ }
+
+ U0 bra(I32 adr, I32 adrh) {
+ no_warn adrh;
+ br[PC] += adr;
+ }
+
+ U0 bvc(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(!v, adr);
+ }
+
+ U0 bvs(I32 adr, I32 adrh) {
+ no_warn adrh;
+ doBranch(v, adr);
+ }
+
+ U0 brl(I32 adr, I32 adrh) {
+ no_warn adrh;
+ br[PC] += adr;
+ }
+
+ U0 jmp(I32 adr, I32 adrh) {
+ no_warn adrh;
+ br[PC] = adr & 0xffff;
+ }
+
+ U0 jml(I32 adr, I32 adrh) {
+ no_warn adrh;
+ r[K] = (adr & 0xff0000) >> 16;
+ br[PC] = adr & 0xffff;
+ }
+
+ U0 jsl(I32 adr, I32 adrh) {
+ no_warn adrh;
+ I32 pushPc = (br[PC] - 1) & 0xffff;
+ pushByte(r[K]);
+ pushWord(pushPc);
+ r[K] = (adr & 0xff0000) >> 16;
+ br[PC] = adr & 0xffff;
+ }
+
+ U0 jsr(I32 adr, I32 adrh) {
+ no_warn adrh;
+ I32 pushPc = (br[PC] - 1) & 0xffff;
+ pushWord(pushPc);
+ br[PC] = adr & 0xffff;
+ }
+
+ U0 rtl(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 pullPc = pullWord();
+ r[K] = pullByte();
+ br[PC] = pullPc + 1;
+ }
+
+ U0 rts(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 pullPc = pullWord();
+ br[PC] = pullPc + 1;
+ }
+
+ U0 brk(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 pushPc = (br[PC] + 1) & 0xffff;
+ pushByte(r[K]);
+ pushWord(pushPc);
+ pushByte(cpu_getP());
+ cyclesLeft++; // native mode: 1 extra cycle
+ i = TRUE;
+ d = FALSE;
+ r[K] = 0;
+ br[PC] = mem_read(0xffe6) | (mem_read(0xffe7) << 8);
+ }
+
+ U0 cop(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[K]);
+ pushWord(br[PC]);
+ pushByte(cpu_getP());
+ cyclesLeft++; // native mode: 1 extra cycle
+ i = TRUE;
+ d = FALSE;
+ r[K] = 0;
+ br[PC] = mem_read(0xffe4) | (mem_read(0xffe5) << 8);
+ }
+
+ U0 abo(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[K]);
+ pushWord(br[PC]);
+ pushByte(cpu_getP());
+ cyclesLeft++; // native mode: 1 extra cycle
+ i = TRUE;
+ d = FALSE;
+ r[K] = 0;
+ br[PC] = mem_read(0xffe8) | (mem_read(0xffe9) << 8);
+ }
+
+ U0 nmi(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[K]);
+ pushWord(br[PC]);
+ pushByte(cpu_getP());
+ cyclesLeft++; // native mode: 1 extra cycle
+ i = TRUE;
+ d = FALSE;
+ r[K] = 0;
+ br[PC] = mem_read(0xffea) | (mem_read(0xffeb) << 8);
+ }
+
+ U0 irq(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[K]);
+ pushWord(br[PC]);
+ pushByte(cpu_getP());
+ cyclesLeft++; // native mode: 1 extra cycle
+ i = TRUE;
+ d = FALSE;
+ r[K] = 0;
+ br[PC] = mem_read(0xffee) | (mem_read(0xffef) << 8);
+ }
+
+ U0 rti(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ cpu_setP(pullByte());
+ cyclesLeft++; // native mode: 1 extra cycle
+ I32 pullPc = pullWord();
+ r[K] = pullByte();
+ br[PC] = pullPc;
+ }
+
+ U0 clc(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ c = FALSE;
+ }
+
+ U0 cld(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ d = FALSE;
+ }
+
+ U0 cli(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ i = FALSE;
+ }
+
+ U0 clv(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ v = FALSE;
+ }
+
+ U0 sec(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ c = TRUE;
+ }
+
+ U0 sed(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ d = TRUE;
+ }
+
+ U0 sei(I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ i = TRUE;
+ }
+
+ U0 rep(I32 adr, I32 adrh) {
+ no_warn adrh;
+ I32 value = mem_read(adr);
+ cpu_setP(cpu_getP() & ~value);
+ }
+
+ U0 sep(I32 adr, I32 adrh) {
+ no_warn adrh;
+ I32 value = mem_read(adr);
+ cpu_setP(cpu_getP() | value);
+ }
+
+ U0 lda(I32 adr, I32 adrh) {
+ if(m) {
+ I32 value = mem_read(adr);
+ br[A] = (br[A] & 0xff00) | (value & 0xff);
+ setZandN(value, m);
+ } else {
+ cyclesLeft++; // m = 0: 1 extra cycle
+ br[A] = readWord(adr, adrh);
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 ldx (I32 adr, I32 adrh) {
+ if(x) {
+ br[X] = mem_read(adr);
+ setZandN(br[X], x);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ br[X] = readWord(adr, adrh);
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 ldy (I32 adr, I32 adrh) {
+ if(x) {
+ br[Y] = mem_read(adr);
+ setZandN(br[Y], x);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ br[Y] = readWord(adr, adrh);
+ setZandN(br[Y], x);
+ }
+ }
+
+ U0 sta (I32 adr, I32 adrh) {
+ if(m) {
+ mem_write(adr, br[A] & 0xff);
+ } else {
+ cyclesLeft++; // m = 0: 1 extra cycle
+ writeWord(adr, adrh, br[A]);
+ }
+ }
+
+ U0 stx (I32 adr, I32 adrh) {
+ if(x) {
+ mem_write(adr, br[X] & 0xff);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ writeWord(adr, adrh, br[X]);
+ }
+ }
+
+ U0 sty (I32 adr, I32 adrh) {
+ if(x) {
+ mem_write(adr, br[Y] & 0xff);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ writeWord(adr, adrh, br[Y]);
+ }
+ }
+
+ U0 stz (I32 adr, I32 adrh) {
+ if(m) {
+ mem_write(adr, 0);
+ } else {
+ cyclesLeft++; // m = 0: 1 extra cycle
+ writeWord(adr, adrh, 0);
+ }
+ }
+
+ U0 mvn (I32 adr, I32 adrh) {
+ r[DBR] = adr;
+ mem_write(
+ (adr << 16) | br[Y],
+ mem_read((adrh << 16) | br[X])
+ );
+ br[A]--;
+ br[X]++;
+ br[Y]++;
+ if(br[A] != 0xffff) {
+ br[PC] -= 3;
+ }
+ if(x) {
+ br[X] &= 0xff;
+ br[Y] &= 0xff;
+ }
+ }
+
+ U0 mvp (I32 adr, I32 adrh) {
+ r[DBR] = adr;
+ mem_write(
+ (adr << 16) | br[Y],
+ mem_read((adrh << 16) | br[X])
+ );
+ br[A]--;
+ br[X]--;
+ br[Y]--;
+ if(br[A] != 0xffff) {
+ br[PC] -= 3;
+ }
+ if(x) {
+ br[X] &= 0xff;
+ br[Y] &= 0xff;
+ }
+ }
+
+ U0 nop (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ // no operation
+ }
+
+ U0 wdm (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ // no operation
+ }
+
+ U0 pea (I32 adr, I32 adrh) {
+ pushWord(readWord(adr, adrh));
+ }
+
+ U0 pei (I32 adr, I32 adrh) {
+ pushWord(readWord(adr, adrh));
+ }
+
+ U0 per (I32 adr, I32 adrh) {
+ no_warn adrh;
+ pushWord((br[PC] + adr) & 0xffff);
+ }
+
+ U0 pha (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(m) {
+ pushByte(br[A] & 0xff);
+ } else {
+ cyclesLeft++; // m = 0: 1 extra cycle
+ pushWord(br[A]);
+ }
+ }
+
+ U0 phx (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ pushByte(br[X] & 0xff);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ pushWord(br[X]);
+ }
+ }
+
+ U0 phy (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ pushByte(br[Y] & 0xff);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ pushWord(br[Y]);
+ }
+ }
+
+ U0 pla (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(m) {
+ br[A] = (br[A] & 0xff00) | (pullByte() & 0xff);
+ setZandN(br[A], m);
+ } else {
+ cyclesLeft++; // m = 0: 1 extra cycle
+ br[A] = pullWord();
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 plx (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[X] = pullByte();
+ setZandN(br[X], m);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ br[X] = pullWord();
+ setZandN(br[X], m);
+ }
+ }
+
+ U0 ply (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[Y] = pullByte();
+ setZandN(br[Y], m);
+ } else {
+ cyclesLeft++; // x = 0: 1 extra cycle
+ br[Y] = pullWord();
+ setZandN(br[Y], m);
+ }
+ }
+
+ U0 phb (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[DBR]);
+ }
+
+ U0 phd (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushWord(br[DPR]);
+ }
+
+ U0 phk (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(r[K]);
+ }
+
+ U0 php (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ pushByte(cpu_getP());
+ }
+
+ U0 plb (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ r[DBR] = pullByte();
+ setZandN(r[DBR], TRUE);
+ }
+
+ U0 pld (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[DPR] = pullWord();
+ setZandN(br[DPR], FALSE);
+ }
+
+ U0 plp (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ cpu_setP(pullByte());
+ }
+
+ U0 stp (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ stopped = TRUE;
+ }
+
+ U0 wai (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ waiting = TRUE;
+ }
+
+ U0 tax (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[X] = br[A] & 0xff;
+ setZandN(br[X], x);
+ } else {
+ br[X] = br[A];
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 tay (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[Y] = br[A] & 0xff;
+ setZandN(br[Y], x);
+ } else {
+ br[Y] = br[A];
+ setZandN(br[Y], x);
+ }
+ }
+
+ U0 tsx (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[X] = br[SP] & 0xff;
+ setZandN(br[X], x);
+ } else {
+ br[X] = br[SP];
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 txa (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(m) {
+ br[A] = (br[A] & 0xff00) | (br[X] & 0xff);
+ setZandN(br[A], m);
+ } else {
+ br[A] = br[X];
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 txs (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[SP] = br[X];
+ }
+
+ U0 txy (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[Y] = br[X] & 0xff;
+ setZandN(br[Y], x);
+ } else {
+ br[Y] = br[X];
+ setZandN(br[Y], x);
+ }
+ }
+
+ U0 tya (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(m) {
+ br[A] = (br[A] & 0xff00) | (br[Y] & 0xff);
+ setZandN(br[A], m);
+ } else {
+ br[A] = br[Y];
+ setZandN(br[A], m);
+ }
+ }
+
+ U0 tyx (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ if(x) {
+ br[X] = br[Y] & 0xff;
+ setZandN(br[X], x);
+ } else {
+ br[X] = br[Y];
+ setZandN(br[X], x);
+ }
+ }
+
+ U0 tcd (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[DPR] = br[A];
+ setZandN(br[DPR], FALSE);
+ }
+
+ U0 tcs (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[SP] = br[A];
+ }
+
+ U0 tdc (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[A] = br[DPR];
+ setZandN(br[A], FALSE);
+ }
+
+ U0 tsc (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ br[A] = br[SP];
+ setZandN(br[A], FALSE);
+ }
+
+ U0 xba (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 low, high;
+ low = br[A] & 0xff;
+ high = (br[A] & 0xff00) >> 8;
+ br[A] = (low << 8) | high;
+ setZandN(br[A], TRUE);
+ }
+
+ U0 xce (I32 adr, I32 adrh) {
+ no_warn adr, adrh;
+ I32 temp = c;
+ c = e;
+ e = temp;
+ if(e) {
+ m = TRUE;
+ x = TRUE;
+ }
+ if(x) {
+ br[X] &= 0xff;
+ br[Y] &= 0xff;
+ }
+ }
+
+ // function table
+ U64 functions[259] = {
+ &brk, &ora, &cop, &ora, &tsb, &ora, &asl, &ora, &php, &ora, &asla,&phd, &tsb, &ora, &asl, &ora,
+ &bpl, &ora, &ora, &ora, &trb, &ora, &asl, &ora, &clc, &ora, &inca,&tcs, &trb, &ora, &asl, &ora,
+ &jsr, &and, &jsl, &and, &bit, &and, &rol, &and, &plp, &and, &rola,&pld, &bit, &and, &rol, &and,
+ &bmi, &and, &and, &and, &bit, &and, &rol, &and, &sec, &and, &deca,&tsc, &bit, &and, &rol, &and,
+ &rti, &eor, &wdm, &eor, &mvp, &eor, &lsr, &eor, &pha, &eor, &lsra,&phk, &jmp, &eor, &lsr, &eor,
+ &bvc, &eor, &eor, &eor, &mvn, &eor, &lsr, &eor, &cli, &eor, &phy, &tcd, &jml, &eor, &lsr, &eor,
+ &rts, &adc, &per, &adc, &stz, &adc, &ror, &adc, &pla, &adc, &rora,&rtl, &jmp, &adc, &ror, &adc,
+ &bvs, &adc, &adc, &adc, &stz, &adc, &ror, &adc, &sei, &adc, &ply, &tdc, &jmp, &adc, &ror, &adc,
+ &bra, &sta, &brl, &sta, &sty, &sta, &stx, &sta, &dey, &biti,&txa, &phb, &sty, &sta, &stx, &sta,
+ &bcc, &sta, &sta, &sta, &sty, &sta, &stx, &sta, &tya, &sta, &txs, &txy, &stz, &sta, &stz, &sta,
+ &ldy, &lda, &ldx, &lda, &ldy, &lda, &ldx, &lda, &tay, &lda, &tax, &plb, &ldy, &lda, &ldx, &lda,
+ &bcs, &lda, &lda, &lda, &ldy, &lda, &ldx, &lda, &clv, &lda, &tsx, &tyx, &ldy, &lda, &ldx, &lda,
+ &cpy, &cmp, &rep, &cmp, &cpy, &cmp, &dec, &cmp, &iny, &cmp, &dex, &wai, &cpy, &cmp, &dec, &cmp,
+ &bne, &cmp, &cmp, &cmp, &pei, &cmp, &dec, &cmp, &cld, &cmp, &phx, &stp, &jml, &cmp, &dec, &cmp,
+ &cpx, &sbc, &sep, &sbc, &cpx, &sbc, &inc, &sbc, &inx, &sbc, &nop, &xba, &cpx, &sbc, &inc, &sbc,
+ &beq, &sbc, &sbc, &sbc, &pea, &sbc, &inc, &sbc, &sed, &sbc, &plx, &xce, &jsr, &sbc, &inc, &sbc,
+ &abo, &nmi, &irq // abo, nmi, irq
+ };
+
+ U0 cpu_opcode(I32 instr, I32 adr, I32 adrh)
+ {
+ switch (instr)
+ {
+
+ case 0:
+ brk(adr, adrh);
+ break;
+ case 1:
+ ora(adr, adrh);
+ break;
+ case 2:
+ cop(adr, adrh);
+ break;
+ case 3:
+ ora(adr, adrh);
+ break;
+ case 4:
+ tsb(adr, adrh);
+ break;
+ case 5:
+ ora(adr, adrh);
+ break;
+ case 6:
+ asl(adr, adrh);
+ break;
+ case 7:
+ ora(adr, adrh);
+ break;
+ case 8:
+ php(adr, adrh);
+ break;
+ case 9:
+ ora(adr, adrh);
+ break;
+ case 10:
+ asla(adr, adrh);
+ break;
+ case 11:
+ phd(adr, adrh);
+ break;
+ case 12:
+ tsb(adr, adrh);
+ break;
+ case 13:
+ ora(adr, adrh);
+ break;
+ case 14:
+ asl(adr, adrh);
+ break;
+ case 15:
+ ora(adr, adrh);
+ break;
+ case 16:
+ bpl(adr, adrh);
+ break;
+ case 17:
+ ora(adr, adrh);
+ break;
+ case 18:
+ ora(adr, adrh);
+ break;
+ case 19:
+ ora(adr, adrh);
+ break;
+ case 20:
+ trb(adr, adrh);
+ break;
+ case 21:
+ ora(adr, adrh);
+ break;
+ case 22:
+ asl(adr, adrh);
+ break;
+ case 23:
+ ora(adr, adrh);
+ break;
+ case 24:
+ clc(adr, adrh);
+ break;
+ case 25:
+ ora(adr, adrh);
+ break;
+ case 26:
+ inca(adr, adrh);
+ break;
+ case 27:
+ tcs(adr, adrh);
+ break;
+ case 28:
+ trb(adr, adrh);
+ break;
+ case 29:
+ ora(adr, adrh);
+ break;
+ case 30:
+ asl(adr, adrh);
+ break;
+ case 31:
+ ora(adr, adrh);
+ break;
+ case 32:
+ jsr(adr, adrh);
+ break;
+ case 33:
+ and(adr, adrh);
+ break;
+ case 34:
+ jsl(adr, adrh);
+ break;
+ case 35:
+ and(adr, adrh);
+ break;
+ case 36:
+ bit(adr, adrh);
+ break;
+ case 37:
+ and(adr, adrh);
+ break;
+ case 38:
+ rol(adr, adrh);
+ break;
+ case 39:
+ and(adr, adrh);
+ break;
+ case 40:
+ plp(adr, adrh);
+ break;
+ case 41:
+ and(adr, adrh);
+ break;
+ case 42:
+ rola(adr, adrh);
+ break;
+ case 43:
+ pld(adr, adrh);
+ break;
+ case 44:
+ bit(adr, adrh);
+ break;
+ case 45:
+ and(adr, adrh);
+ break;
+ case 46:
+ rol(adr, adrh);
+ break;
+ case 47:
+ and(adr, adrh);
+ break;
+ case 48:
+ bmi(adr, adrh);
+ break;
+ case 49:
+ and(adr, adrh);
+ break;
+ case 50:
+ and(adr, adrh);
+ break;
+ case 51:
+ and(adr, adrh);
+ break;
+ case 52:
+ bit(adr, adrh);
+ break;
+ case 53:
+ and(adr, adrh);
+ break;
+ case 54:
+ rol(adr, adrh);
+ break;
+ case 55:
+ and(adr, adrh);
+ break;
+ case 56:
+ sec(adr, adrh);
+ break;
+ case 57:
+ and(adr, adrh);
+ break;
+ case 58:
+ deca(adr, adrh);
+ break;
+ case 59:
+ tsc(adr, adrh);
+ break;
+ case 60:
+ bit(adr, adrh);
+ break;
+ case 61:
+ and(adr, adrh);
+ break;
+ case 62:
+ rol(adr, adrh);
+ break;
+ case 63:
+ and(adr, adrh);
+ break;
+ case 64:
+ rti(adr, adrh);
+ break;
+ case 65:
+ eor(adr, adrh);
+ break;
+ case 66:
+ wdm(adr, adrh);
+ break;
+ case 67:
+ eor(adr, adrh);
+ break;
+ case 68:
+ mvp(adr, adrh);
+ break;
+ case 69:
+ eor(adr, adrh);
+ break;
+ case 70:
+ lsr(adr, adrh);
+ break;
+ case 71:
+ eor(adr, adrh);
+ break;
+ case 72:
+ pha(adr, adrh);
+ break;
+ case 73:
+ eor(adr, adrh);
+ break;
+ case 74:
+ lsra(adr, adrh);
+ break;
+ case 75:
+ phk(adr, adrh);
+ break;
+ case 76:
+ jmp(adr, adrh);
+ break;
+ case 77:
+ eor(adr, adrh);
+ break;
+ case 78:
+ lsr(adr, adrh);
+ break;
+ case 79:
+ eor(adr, adrh);
+ break;
+ case 80:
+ bvc(adr, adrh);
+ break;
+ case 81:
+ eor(adr, adrh);
+ break;
+ case 82:
+ eor(adr, adrh);
+ break;
+ case 83:
+ eor(adr, adrh);
+ break;
+ case 84:
+ mvn(adr, adrh);
+ break;
+ case 85:
+ eor(adr, adrh);
+ break;
+ case 86:
+ lsr(adr, adrh);
+ break;
+ case 87:
+ eor(adr, adrh);
+ break;
+ case 88:
+ cli(adr, adrh);
+ break;
+ case 89:
+ eor(adr, adrh);
+ break;
+ case 90:
+ phy(adr, adrh);
+ break;
+ case 91:
+ tcd(adr, adrh);
+ break;
+ case 92:
+ jml(adr, adrh);
+ break;
+ case 93:
+ eor(adr, adrh);
+ break;
+ case 94:
+ lsr(adr, adrh);
+ break;
+ case 95:
+ eor(adr, adrh);
+ break;
+ case 96:
+ rts(adr, adrh);
+ break;
+ case 97:
+ adc(adr, adrh);
+ break;
+ case 98:
+ per(adr, adrh);
+ break;
+ case 99:
+ adc(adr, adrh);
+ break;
+ case 100:
+ stz(adr, adrh);
+ break;
+ case 101:
+ adc(adr, adrh);
+ break;
+ case 102:
+ ror(adr, adrh);
+ break;
+ case 103:
+ adc(adr, adrh);
+ break;
+ case 104:
+ pla(adr, adrh);
+ break;
+ case 105:
+ adc(adr, adrh);
+ break;
+ case 106:
+ rora(adr, adrh);
+ break;
+ case 107:
+ rtl(adr, adrh);
+ break;
+ case 108:
+ jmp(adr, adrh);
+ break;
+ case 109:
+ adc(adr, adrh);
+ break;
+ case 110:
+ ror(adr, adrh);
+ break;
+ case 111:
+ adc(adr, adrh);
+ break;
+ case 112:
+ bvs(adr, adrh);
+ break;
+ case 113:
+ adc(adr, adrh);
+ break;
+ case 114:
+ adc(adr, adrh);
+ break;
+ case 115:
+ adc(adr, adrh);
+ break;
+ case 116:
+ stz(adr, adrh);
+ break;
+ case 117:
+ adc(adr, adrh);
+ break;
+ case 118:
+ ror(adr, adrh);
+ break;
+ case 119:
+ adc(adr, adrh);
+ break;
+ case 120:
+ sei(adr, adrh);
+ break;
+ case 121:
+ adc(adr, adrh);
+ break;
+ case 122:
+ ply(adr, adrh);
+ break;
+ case 123:
+ tdc(adr, adrh);
+ break;
+ case 124:
+ jmp(adr, adrh);
+ break;
+ case 125:
+ adc(adr, adrh);
+ break;
+ case 126:
+ ror(adr, adrh);
+ break;
+ case 127:
+ adc(adr, adrh);
+ break;
+ case 128:
+ bra(adr, adrh);
+ break;
+ case 129:
+ sta(adr, adrh);
+ break;
+ case 130:
+ brl(adr, adrh);
+ break;
+ case 131:
+ sta(adr, adrh);
+ break;
+ case 132:
+ sty(adr, adrh);
+ break;
+ case 133:
+ sta(adr, adrh);
+ break;
+ case 134:
+ stx(adr, adrh);
+ break;
+ case 135:
+ sta(adr, adrh);
+ break;
+ case 136:
+ dey(adr, adrh);
+ break;
+ case 137:
+ biti(adr, adrh);
+ break;
+ case 138:
+ txa(adr, adrh);
+ break;
+ case 139:
+ phb(adr, adrh);
+ break;
+ case 140:
+ sty(adr, adrh);
+ break;
+ case 141:
+ sta(adr, adrh);
+ break;
+ case 142:
+ stx(adr, adrh);
+ break;
+ case 143:
+ sta(adr, adrh);
+ break;
+ case 144:
+ bcc(adr, adrh);
+ break;
+ case 145:
+ sta(adr, adrh);
+ break;
+ case 146:
+ sta(adr, adrh);
+ break;
+ case 147:
+ sta(adr, adrh);
+ break;
+ case 148:
+ sty(adr, adrh);
+ break;
+ case 149:
+ sta(adr, adrh);
+ break;
+ case 150:
+ stx(adr, adrh);
+ break;
+ case 151:
+ sta(adr, adrh);
+ break;
+ case 152:
+ tya(adr, adrh);
+ break;
+ case 153:
+ sta(adr, adrh);
+ break;
+ case 154:
+ txs(adr, adrh);
+ break;
+ case 155:
+ txy(adr, adrh);
+ break;
+ case 156:
+ stz(adr, adrh);
+ break;
+ case 157:
+ sta(adr, adrh);
+ break;
+ case 158:
+ stz(adr, adrh);
+ break;
+ case 159:
+ sta(adr, adrh);
+ break;
+ case 160:
+ ldy(adr, adrh);
+ break;
+ case 161:
+ lda(adr, adrh);
+ break;
+ case 162:
+ ldx(adr, adrh);
+ break;
+ case 163:
+ lda(adr, adrh);
+ break;
+ case 164:
+ ldy(adr, adrh);
+ break;
+ case 165:
+ lda(adr, adrh);
+ break;
+ case 166:
+ ldx(adr, adrh);
+ break;
+ case 167:
+ lda(adr, adrh);
+ break;
+ case 168:
+ tay(adr, adrh);
+ break;
+ case 169:
+ lda(adr, adrh);
+ break;
+ case 170:
+ tax(adr, adrh);
+ break;
+ case 171:
+ plb(adr, adrh);
+ break;
+ case 172:
+ ldy(adr, adrh);
+ break;
+ case 173:
+ lda(adr, adrh);
+ break;
+ case 174:
+ ldx(adr, adrh);
+ break;
+ case 175:
+ lda(adr, adrh);
+ break;
+ case 176:
+ bcs(adr, adrh);
+ break;
+ case 177:
+ lda(adr, adrh);
+ break;
+ case 178:
+ lda(adr, adrh);
+ break;
+ case 179:
+ lda(adr, adrh);
+ break;
+ case 180:
+ ldy(adr, adrh);
+ break;
+ case 181:
+ lda(adr, adrh);
+ break;
+ case 182:
+ ldx(adr, adrh);
+ break;
+ case 183:
+ lda(adr, adrh);
+ break;
+ case 184:
+ clv(adr, adrh);
+ break;
+ case 185:
+ lda(adr, adrh);
+ break;
+ case 186:
+ tsx(adr, adrh);
+ break;
+ case 187:
+ tyx(adr, adrh);
+ break;
+ case 188:
+ ldy(adr, adrh);
+ break;
+ case 189:
+ lda(adr, adrh);
+ break;
+ case 190:
+ ldx(adr, adrh);
+ break;
+ case 191:
+ lda(adr, adrh);
+ break;
+ case 192:
+ cpy(adr, adrh);
+ break;
+ case 193:
+ cmp(adr, adrh);
+ break;
+ case 194:
+ rep(adr, adrh);
+ break;
+ case 195:
+ cmp(adr, adrh);
+ break;
+ case 196:
+ cpy(adr, adrh);
+ break;
+ case 197:
+ cmp(adr, adrh);
+ break;
+ case 198:
+ dec(adr, adrh);
+ break;
+ case 199:
+ cmp(adr, adrh);
+ break;
+ case 200:
+ iny(adr, adrh);
+ break;
+ case 201:
+ cmp(adr, adrh);
+ break;
+ case 202:
+ dex(adr, adrh);
+ break;
+ case 203:
+ wai(adr, adrh);
+ break;
+ case 204:
+ cpy(adr, adrh);
+ break;
+ case 205:
+ cmp(adr, adrh);
+ break;
+ case 206:
+ dec(adr, adrh);
+ break;
+ case 207:
+ cmp(adr, adrh);
+ break;
+ case 208:
+ bne(adr, adrh);
+ break;
+ case 209:
+ cmp(adr, adrh);
+ break;
+ case 210:
+ cmp(adr, adrh);
+ break;
+ case 211:
+ cmp(adr, adrh);
+ break;
+ case 212:
+ pei(adr, adrh);
+ break;
+ case 213:
+ cmp(adr, adrh);
+ break;
+ case 214:
+ dec(adr, adrh);
+ break;
+ case 215:
+ cmp(adr, adrh);
+ break;
+ case 216:
+ cld(adr, adrh);
+ break;
+ case 217:
+ cmp(adr, adrh);
+ break;
+ case 218:
+ phx(adr, adrh);
+ break;
+ case 219:
+ stp(adr, adrh);
+ break;
+ case 220:
+ jml(adr, adrh);
+ break;
+ case 221:
+ cmp(adr, adrh);
+ break;
+ case 222:
+ dec(adr, adrh);
+ break;
+ case 223:
+ cmp(adr, adrh);
+ break;
+ case 224:
+ cpx(adr, adrh);
+ break;
+ case 225:
+ sbc(adr, adrh);
+ break;
+ case 226:
+ sep(adr, adrh);
+ break;
+ case 227:
+ sbc(adr, adrh);
+ break;
+ case 228:
+ cpx(adr, adrh);
+ break;
+ case 229:
+ sbc(adr, adrh);
+ break;
+ case 230:
+ inc(adr, adrh);
+ break;
+ case 231:
+ sbc(adr, adrh);
+ break;
+ case 232:
+ inx(adr, adrh);
+ break;
+ case 233:
+ sbc(adr, adrh);
+ break;
+ case 234:
+ nop(adr, adrh);
+ break;
+ case 235:
+ xba(adr, adrh);
+ break;
+ case 236:
+ cpx(adr, adrh);
+ break;
+ case 237:
+ sbc(adr, adrh);
+ break;
+ case 238:
+ inc(adr, adrh);
+ break;
+ case 239:
+ sbc(adr, adrh);
+ break;
+ case 240:
+ beq(adr, adrh);
+ break;
+ case 241:
+ sbc(adr, adrh);
+ break;
+ case 242:
+ sbc(adr, adrh);
+ break;
+ case 243:
+ sbc(adr, adrh);
+ break;
+ case 244:
+ pea(adr, adrh);
+ break;
+ case 245:
+ sbc(adr, adrh);
+ break;
+ case 246:
+ inc(adr, adrh);
+ break;
+ case 247:
+ sbc(adr, adrh);
+ break;
+ case 248:
+ sed(adr, adrh);
+ break;
+ case 249:
+ sbc(adr, adrh);
+ break;
+ case 250:
+ plx(adr, adrh);
+ break;
+ case 251:
+ xce(adr, adrh);
+ break;
+ case 252:
+ jsr(adr, adrh);
+ break;
+ case 253:
+ sbc(adr, adrh);
+ break;
+ case 254:
+ inc(adr, adrh);
+ break;
+ case 255:
+ sbc(adr, adrh);
+ break;
+ case 256:
+ abo(adr, adrh);
+ break;
+ case 257:
+ nmi(adr, adrh);
+ break;
+ case 258:
+ irq(adr, adrh);
+ break;
+ default:
+ PressAKey;
+ break;
+
+
+ }
+
+ }
+
+U0 regs()
+{
+ "PC: 0x%04X\tSP: 0x%04X\tA: 0x%04X\tX: 0x%04X\tY: 0x%04X\n", br[PC], br[SP], br[A], br[X], br[Y];
+ "\n";
+} \ No newline at end of file
diff --git a/Font.HC b/Font.HC
new file mode 100644
index 0000000..c6a9a5b
--- /dev/null
+++ b/Font.HC
@@ -0,0 +1,90 @@
+extern I64 frames;
+
+Context2D *font_ctx = LoadPNG("font.png",,,2);
+Context2D *font_ch = NewContext2D(16, 16, display.bpp);
+
+U0 FontChar2D(Context2D *ctx, I64 ch, I64 x, I64 y)
+{
+ ch-=32;
+ Fill2D(font_ch, font_ch->alpha_color);
+ Blot2D(font_ch, -(16*(ch%8)), -(16*(ch/8)), font_ctx);
+ Blot2D(ctx, x, y, font_ch);
+}
+
+U0 Font2D(Context2D *ctx, I64 x, I64 y, U8 *fmt,...)
+{
+ U8 *buf;
+ if (argc)
+ {
+ buf=StrPrintJoin(NULL, fmt, argc, argv);
+ }
+ else
+ {
+ buf=StrNew(fmt);
+ }
+ U8 *str=buf;
+ while (*str)
+ {
+ FontChar2D(ctx, *str++, x, y);
+ x += 16;
+ }
+ Free(buf);
+}
+
+U0 PrintPlot2D(Context2D *ctx, I64 x, I64 y, U32 color)
+{
+ U64 i = ctx->fb;
+ I64 pos = (svga.width*y)*svga.bpp/8;
+ pos += x*svga.bpp/8;
+ (i+pos)(U8*)[0]=color.u8[1];
+ (i+pos)(U8*)[1]=color.u8[2];
+ (i+pos)(U8*)[2]=color.u8[3];
+}
+
+U0 PrintChar2D(Context2D *ctx, I64 x, I64 y, I64 ch, U32 fg=0xFFFFFF00, U32 bg=BLACK)
+{
+ U64 *char=sys_font_std;
+ I64 xx,yy;
+ I64 ii=0;
+ for (yy=0; yy<8; yy++)
+ {
+ xx=0;
+ for (xx=0; xx<8; xx++)
+ {
+ if (char[ch].u8[ii] & 1<<xx)
+ {
+ PrintPlot2D(ctx, x+xx, y+yy, fg);
+ }
+ else
+ {
+ PrintPlot2D(ctx, x+xx, y+yy, bg);
+ }
+ }
+ ii++;
+ if (ii>8)
+ {
+ ch++;
+ ii=0;
+ }
+ }
+}
+
+U0 Print2D(Context2D *ctx, I64 x, I64 y, U32 fg=0xFFFFFFFF, U32 bg=0, U8 *fmt,...)
+{
+ U8 *buf;
+ if (argc)
+ {
+ buf=StrPrintJoin(NULL, fmt, argc, argv);
+ }
+ else
+ {
+ buf=StrNew(fmt);
+ }
+ U8 *str=buf;
+ while (*str)
+ {
+ PrintChar2D(ctx, x, y, *str++, fg, bg);
+ x += 8;
+ }
+ Free(buf);
+} \ No newline at end of file
diff --git a/Lib/Debugger.HC b/Lib/Debugger.HC
new file mode 100644
index 0000000..358a280
--- /dev/null
+++ b/Lib/Debugger.HC
@@ -0,0 +1,148 @@
+tmpdoc = DocNew;
+origdoc = Fs->put_doc;
+Fs->put_doc = tmpdoc;
+
+U0 EDbgPlot2D(I64 x, I64 y, U32 color)
+{
+ U64 i = svga.fb;
+ I64 pos = (svga.width*y)*svga.bpp/8;
+ pos += x*svga.bpp/8;
+ (i+pos)(U8*)[0]=color.u8[1];
+ (i+pos)(U8*)[1]=color.u8[2];
+ (i+pos)(U8*)[2]=color.u8[3];
+}
+
+U0 EDbgChar2D(I64 x, I64 y, I64 ch, U32 fg=0xFFFFFF00, U32 bg=BLACK)
+{
+ U64 *char=sys_font_std;
+ I64 xx,yy;
+ I64 ii=0;
+ for (yy=0; yy<8; yy++)
+ {
+ xx=0;
+ for (xx=0; xx<8; xx++)
+ {
+ if (char[ch].u8[ii] & 1<<xx)
+ {
+ EDbgPlot2D(x+xx, y+yy, fg);
+ }
+ else
+ {
+ EDbgPlot2D(x+xx, y+yy, bg);
+ }
+ }
+ ii++;
+ if (ii>8)
+ {
+ ch++;
+ ii=0;
+ }
+ }
+}
+
+U0 EDbgPutChar(I64 ch)
+{
+ if (!IsSingleUser) { return; };
+ text.raw_flags &=~RWF_SHOW_DOLLAR;
+ if (ch>'~' && ch!=219)
+ {
+ ch=' ';
+ }
+ I64 i,row,col;
+ I64 raw_pos;
+ I64 raw_y;
+ U8 *ptr,*ptr1,*ptr2;
+
+ if (!(text.raw_flags&RWF_SHOW_DOLLAR)) {
+ if (ch=='$$') {
+ if (text.raw_flags&RWF_IN_DOLLAR) {
+ text.raw_flags&=~RWF_IN_DOLLAR;
+ if (!(text.raw_flags & RWF_LAST_DOLLAR)) {
+ text.raw_flags&=~RWF_LAST_DOLLAR;
+ return;
+ }
+ } else {
+ text.raw_flags|=RWF_IN_DOLLAR|RWF_LAST_DOLLAR;
+ return;
+ }
+ }
+ text.raw_flags&=~RWF_LAST_DOLLAR;
+ if (text.raw_flags&RWF_IN_DOLLAR)
+ return;
+ }
+ if (ch=='\t') {
+ EDbgPutChar(CH_SPACE);
+ while (text.raw_col & 7)
+ EDbgPutChar(CH_SPACE);
+ } else if (ch==CH_BACKSPACE) {
+ text.raw_col--;
+ EDbgPutChar(CH_SPACE);
+ text.raw_col--;
+ } else if (ch=='\n') {
+ EDbgPutChar(CH_SPACE);
+ while (text.raw_col % text.cols)
+ EDbgPutChar(CH_SPACE);
+
+ } else if (Bt(char_bmp_displayable,ch)) {
+ row=text.raw_col/text.cols%text.rows;
+ col=text.raw_col%text.cols;
+ if (text.raw_flags&RWF_SCROLL && text.raw_col && !row && !col) {
+ for (raw_y=0; raw_y<(text.rows*8)-8; raw_y++)
+ {
+ MemCpy(svga.fb+(raw_y*svga.width*(svga.bpp/8)),
+ svga.fb+((raw_y+8)*svga.width*(svga.bpp/8)),
+ (text.cols*8)*(svga.bpp/8));
+ }
+ for (raw_y=(text.rows*8)-8; raw_y<(text.rows*8); raw_y++)
+ {
+ MemSet(svga.fb+(raw_y*svga.width*(svga.bpp/8)),
+ 0,
+ (text.cols*8)*(svga.bpp/8));
+ }
+ text.raw_col-=text.cols;
+ row=text.rows-1;
+ }
+ EDbgChar2D(col*8, row*8, ch);
+ text.raw_col++;
+ }
+}
+
+Bool EKDRawPutKey(I64 ch,I64)
+{
+ if (IsRaw) {
+ EDbgPutChar(ch);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+Bool EKDRawPutS(U8 *st)
+{
+ I64 ch;
+ if (IsRaw) {
+ while (ch=*st++)
+ EDbgPutChar(ch);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+U0 RawDrNull(CTask *task=NULL)
+{
+}
+
+CKeyDevEntry *tmp_kde = keydev.put_key_head;
+while (tmp_kde->put_s != &KDRawPutS)
+{
+ tmp_kde = tmp_kde->next;
+}
+tmp_kde->put_key = &EKDRawPutKey;
+tmp_kde->put_s = &EKDRawPutS;
+
+MemCpy(&RawDr, &RawDrNull, sizeof(RawDrNull));
+
+Bool Debug = FALSE;
+U0 resume() { Debug=FALSE; G2; };
+
+Fs->put_doc = origdoc;
+DocDel(tmpdoc); \ No newline at end of file
diff --git a/Lib/Display.HC b/Lib/Display.HC
new file mode 100644
index 0000000..86811c7
--- /dev/null
+++ b/Lib/Display.HC
@@ -0,0 +1,17 @@
+class Display
+{
+ I64 width;
+ I64 height;
+ I64 bpp;
+};
+
+Display display;
+
+U0 DisplayInit()
+{
+ display.width = 640;
+ display.height = 480;
+ display.bpp = 32;
+}
+
+DisplayInit; \ No newline at end of file
diff --git a/Lib/Graphics2D.HC b/Lib/Graphics2D.HC
new file mode 100644
index 0000000..2c9b345
--- /dev/null
+++ b/Lib/Graphics2D.HC
@@ -0,0 +1,250 @@
+class Context2D
+{
+I64 width;
+I64 height;
+I64 bpp;
+I64 alpha_color;
+U8 *fb;
+};
+
+Context2D *NewContext2D(I64 width, I64 height, I64 bpp=display.bpp)
+{ //Create new Context2D.
+ switch (bpp)
+ {
+ case 32:
+ case 24:
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ Context2D *ctx = CAlloc(sizeof(Context2D));
+ ctx->width = width;
+ ctx->height = height;
+ ctx->bpp = bpp;
+ ctx->alpha_color = 0x00FF0000;
+ ctx->fb = CAlloc((width*height)*bpp/8);
+ return ctx;
+}
+
+U0 DelContext2D(Context2D *ctx)
+{
+ if (!ctx)
+ {
+ return;
+ }
+ Free(ctx->fb);
+ Free(ctx);
+}
+
+U0 Fill2D(Context2D *ctx, U32 color=U32_MAX)
+{ //Fill a Context2D with color.
+ if (color==U32_MAX) color=ctx->alpha_color;
+ U64 i = ctx->fb;
+ I64 pos = 0;
+ while (pos<(ctx->width*ctx->height)*ctx->bpp/8)
+ {
+ (i+pos)(U8*)[0]=color.u8[1];
+ (i+pos)(U8*)[1]=color.u8[2];
+ (i+pos)(U8*)[2]=color.u8[3];
+ switch (ctx->bpp)
+ {
+ case 32:
+ pos+=4;
+ break;
+ case 24:
+ pos+=3;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+U32 Peek2D(Context2D *ctx, I64 x, I64 y)
+{ //Return RGB value for pixel.
+ U32 color=0;
+ if (x<0 || x>ctx->width-1 || y<0 || y>ctx->height-1)
+ {
+ return 0xFFFFFFFF;
+ }
+ U64 i = ctx->fb;
+ I64 pos = (ctx->width*y)*(ctx->bpp/8);
+ pos += x*(ctx->bpp/8);
+ color.u8[1]=(i+pos)(U8*)[0];
+ color.u8[2]=(i+pos)(U8*)[1];
+ color.u8[3]=(i+pos)(U8*)[2];
+ return color;
+}
+
+U0 Plot2D(Context2D *ctx, I64 x, I64 y, U32 color)
+{ //Plot a pixel.
+ if (x<0 || x>ctx->width-1 || y<0 || y>ctx->height-1)
+ {
+ return;
+ }
+ U64 i = ctx->fb;
+ I64 pos = (ctx->width*y)*(ctx->bpp/8);
+ pos += x*(ctx->bpp/8);
+ (i+pos)(U8*)[0]=color.u8[1];
+ (i+pos)(U8*)[1]=color.u8[2];
+ (i+pos)(U8*)[2]=color.u8[3];
+}
+
+U0 HLine2D(Context2D *ctx, I64 x, I64 y, I64 x2, U32 color)
+{ //Draw a horizontal line.
+ if (x2<x)
+ {
+ return;
+ }
+ while (x<x2+1)
+ {
+ Plot2D(ctx, x, y, color);
+ x++;
+ }
+}
+
+U0 VLine2D(Context2D *ctx, I64 x, I64 y, I64 y2, U32 color)
+{ //Draw a vertical line.
+ if (y2<y)
+ {
+ return;
+ }
+ while (y<y2+1)
+ {
+ Plot2D(ctx, x, y, color);
+ y++;
+ }
+}
+
+U0 Blot2D(Context2D *dst, I64 x, I64 y, Context2D *src, U32 color=NULL)
+{
+ if (src==NULL || dst==NULL)
+ {
+ return;
+ }
+ U8 *chk_alpha_ptr;
+ U32 chk_alpha;
+ U64 i=dst->fb;
+ U64 j=src->fb;
+ I64 xx,yy;
+ I64 dx,sx;
+ i+=(x*dst->bpp/8)+((dst->width*y)*dst->bpp/8);
+ for (yy=0; yy<src->height; yy++)
+ {
+ dx=0;
+ sx=0;
+ for (xx=0; xx<src->width; xx++)
+ {
+ chk_alpha_ptr=j+dx+((src->width*yy)*src->bpp/8);
+ chk_alpha=0;
+ chk_alpha.u8[1]=chk_alpha_ptr[0];
+ chk_alpha.u8[2]=chk_alpha_ptr[1];
+ chk_alpha.u8[3]=chk_alpha_ptr[2];
+
+ if ((chk_alpha!=src->alpha_color || src->alpha_color==-1) &&
+ x+xx>-1 && y+yy>-1 &&
+ x+xx<dst->width && y+yy<dst->height)
+ {
+ if (!color)
+ {
+ MemCpy(i+sx+((dst->width*yy)*dst->bpp/8), j+dx+((src->width*yy)*src->bpp/8), 3);
+ }
+ else
+ {
+ MemSet(i+sx+((dst->width*yy)*dst->bpp/8), color.u8[1], 1);
+ MemSet(i+sx+((dst->width*yy)*dst->bpp/8)+1, color.u8[2], 1);
+ MemSet(i+sx+((dst->width*yy)*dst->bpp/8)+2, color.u8[3], 1);
+ }
+ }
+ switch (dst->bpp)
+ {
+ case 32:
+ dx+=4;
+ break;
+ case 24:
+ dx+=3;
+ break;
+ default:
+ break;
+ }
+ switch (src->bpp)
+ {
+ case 32:
+ sx+=4;
+ break;
+ case 24:
+ sx+=3;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+U0 CopyRect2D(Context2D *ctx, I64 x, I64 y, Context2D *rect)
+{//Copy rect with clipping.
+
+ U8 *ctx_pos=ctx->fb;
+ U8 *rect_pos=rect->fb;
+ I64 rect_row=0;
+ I64 rect_y_ofs=0;
+ I64 rect_x_ofs=0;
+ I64 clip_y=0;
+ U8 *rect_line;
+
+ //Handle horizontal clipping left
+ while (x<0) { rect_x_ofs++; x++; }
+
+ //Handle vertical clipping top
+ while (y<0)
+ {
+ rect_pos+=(rect->width)*(display.bpp/8);
+ rect_y_ofs++; y++;
+ }
+
+ // default, clip line to copy as width-left off screen
+ rect_line=rect->width-rect_x_ofs;
+
+ if (-rect_x_ofs+x+rect->width>=ctx->width)
+ {
+ rect_line-=((-rect_x_ofs+x+rect->width)-ctx->width);
+ }
+
+ rect_pos+=(rect_x_ofs)*(display.bpp/8);
+ clip_y = y;
+ while (rect_row<(rect->height-rect_y_ofs) && clip_y<ctx->height)
+ {
+ MemCpy(ctx_pos+(y*((ctx->width)*(display.bpp/8)))+x*(display.bpp/8),rect_pos,(rect_line)*(display.bpp/8));
+ ctx_pos+=(ctx->width)*(display.bpp/8);
+ rect_pos+=(rect->width)*(display.bpp/8);
+ clip_y++;
+ rect_row++;
+ }
+}
+
+U0 Rect2D(Context2D *ctx, I64 x, I64 y, I64 w, I64 h, U32 color)
+{ //Draw a rectangle fill.
+ Context2D *tmpctx=NewContext2D(Max(4, w), Max(4, h), display.bpp);
+ Fill2D(tmpctx, color);
+ //Blot2D(ctx, x, y, tmpctx);
+ CopyRect2D(ctx, x, y, tmpctx);
+ DelContext2D(tmpctx);
+}
+
+Context2D *sys_fb = CAlloc(sizeof(Context2D));
+
+U0 SysFrameBufferInit()
+{ //Init values for Context2D alias to system framebuffer.
+ sys_fb->width = display.width;
+ sys_fb->height = display.height;
+ sys_fb->bpp = display.bpp;
+ sys_fb->alpha_color = -1;
+ sys_fb->fb = svga.fb;
+}
+
+U0 Flip2D(Context2D *ctx)
+{
+ MemCpy(sys_fb->fb, ctx->fb, (display.width*display.height)*display.bpp/8);
+} \ No newline at end of file
diff --git a/Lib/Misc.HC b/Lib/Misc.HC
new file mode 100644
index 0000000..91afb9a
--- /dev/null
+++ b/Lib/Misc.HC
@@ -0,0 +1,56 @@
+extern I64 frames;
+extern U0 Print2D(Context2D *ctx, I64 x, I64 y, U32 fg=0xFFFFFFFF, U32 bg=0, U8 *fmt,...);
+
+Bool cond(I64 val, I64 if_true, I64 if_false)
+{
+ if (val)
+ {
+ return if_true;
+ }
+ else
+ {
+ return if_false;
+ }
+}
+
+Bool KeyDown(I64 sc)
+{
+ return Bt(kbd.down_bitmap, sc);
+}
+
+U64 append(U64 str)
+{
+ return str+StrLen(str);
+}
+
+
+I64 fps_frames = frames;
+I64 fps_jiffies = cnts.jiffies;
+
+U0 UpdateTOSMenuBar(Context2D *ctx)
+{
+ if (cnts.jiffies>=fps_jiffies+1000)
+ {
+ winmgr.fps = frames-fps_frames;
+ fps_frames = frames;
+ fps_jiffies = cnts.jiffies;
+ }
+ U8 *st;
+ CCPU *c;
+ I64 i;
+ *wall->top_line=NULL;
+ WallPaper(Fs);
+ Rect2D(ctx, 0, 0, 640, 8, 0x0000a800);
+ Print2D(ctx, 0, 0,,0x0000a800, wall->top_line);
+ WinCalcIdles;
+ for (i=0;i<mp_cnt;i++) {
+ c=&cpu_structs[i];
+ if (i&1)
+ Print2D(ctx, 8*44+(8*(i*2)), 0,,0x0000a800, "%2tf",100.0*(1.0-c->idle_factor));
+ else
+ Print2D(ctx, 8*44+(8*(i*2)), 0,0xffff5700,0x0000a800, "%2tf",100.0*(1.0-c->idle_factor));
+ }
+ st=ScanCode2KeyName(kbd.last_down_scan_code);
+ Print2D(ctx,640-(18*8),0,0xffff5700,0x0000a800,"%18ts",st);
+ Free(st);
+} \ No newline at end of file
diff --git a/Lib/VMSVGA.HC b/Lib/VMSVGA.HC
new file mode 100644
index 0000000..b5f34c3
--- /dev/null
+++ b/Lib/VMSVGA.HC
@@ -0,0 +1,96 @@
+class CVMSVGAInfo
+{
+U16 io_base;
+U16 width;
+U16 height;
+U16 bpp;
+U64 fb;
+};
+
+#define VBE_IOPORT_INDEX 0x01CE
+#define VBE_IOPORT_DATA 0x01CF
+#define VBE_XRES 1
+#define VBE_YRES 2
+#define VBE_BPP 3
+#define VBE_ENABLE 4
+
+#define VMSR_FB 13
+
+CVMSVGAInfo svga;
+MemSet(&svga, 0, sizeof(CVMSVGAInfo));
+
+U16 VBE_RegRead(U16 index)
+{
+ OutU16(VBE_IOPORT_INDEX, index);
+ return InU16(VBE_IOPORT_DATA);
+}
+
+U0 VBE_RegWrite(U16 index, U16 val)
+{
+ OutU16(VBE_IOPORT_INDEX, index);
+ OutU16(VBE_IOPORT_DATA, val);
+}
+
+U32 VMSVGA_RegRead(I64 index)
+{
+ OutU32(svga.io_base, index);
+ return InU32(svga.io_base+1);
+}
+
+U0 VMSVGA_RegWrite(I64 index, U32 val)
+{
+ OutU32(svga.io_base, index);
+ OutU32(svga.io_base+1, val);
+}
+
+U0 GetVideoRegVals()
+{
+ svga.width = VBE_RegRead(VBE_XRES);
+ svga.height = VBE_RegRead(VBE_YRES);
+ svga.bpp = VBE_RegRead(VBE_BPP);
+ svga.fb = VMSVGA_RegRead(VMSR_FB);
+}
+
+U0 SetVideoRegVals()
+{
+ VBE_RegWrite(VBE_ENABLE, 0);
+ VBE_RegWrite(VBE_XRES, svga.width);
+ VBE_RegWrite(VBE_YRES, svga.height);
+ VBE_RegWrite(VBE_BPP, svga.bpp);
+ VBE_RegWrite(VBE_ENABLE, 1);
+}
+
+U0 VMSVGA_Start(I64 w=640,I64 h=480,I64 bpp=32)
+{
+ switch (bpp)
+ {
+ case 32:
+ break;
+ default:
+ "\nInvalid bpp. (must be 32)\n";
+ return;
+ break;
+ }
+ I64 j;
+ //Scan for device
+ j=PCIClassFind(0x030000,0);
+ if (j<0)
+ {
+ "\nVMSVGA device not found.\n";
+ return;
+ }
+ svga.io_base=PCIReadU16(j.u8[2],
+ j.u8[1],j.u8[0],0x10) & ~(0x0F);
+ if (!svga.io_base)
+ {
+ "\nError reading base I/O address.\n";
+ return;
+ }
+ GetVideoRegVals;
+ svga.width=w;
+ svga.height=h;
+ svga.bpp=bpp;
+ LBts(&sys_winmgr_task->task_flags,TASKf_SUSPENDED);
+ //Raw(ON);
+ SetVideoRegVals;
+} \ No newline at end of file
diff --git a/Lib/uPNG.HC b/Lib/uPNG.HC
new file mode 100644
index 0000000..745b1d0
--- /dev/null
+++ b/Lib/uPNG.HC
@@ -0,0 +1,1073 @@
+extern Bool cond(I64 val, I64 if_true, I64 if_false);
+
+CDoc *tmpdoc = DocNew;
+CDoc *origdoc = Fs->put_doc;
+Fs->put_doc = tmpdoc;
+
+/*
+ uPNG -- derived from LodePNG version 20100808
+
+ Copyright (c) 2005-2010 Lode Vandevenne
+ Copyright (c) 2010 Sean Middleditch
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ Converted to HolyC by Alec Murphy
+ */
+
+#define UPNG_EOK 0
+#define UPNG_ENOMEM 1
+#define UPNG_ENOTFOUND 2
+#define UPNG_ENOTPNG 3
+#define UPNG_EMALFORMED 4
+#define UPNG_EUNSUPPORTED 5
+#define UPNG_EUNINTERLACED 6
+#define UPNG_EUNFORMAT 7
+#define UPNG_EPARAM 8
+
+#define UPNG_BADFORMAT 0
+#define UPNG_RGB8 1
+#define UPNG_RGB16 2
+#define UPNG_RGBA8 3
+#define UPNG_RGBA16 4
+#define UPNG_LUMINANCE1 5
+#define UPNG_LUMINANCE2 6
+#define UPNG_LUMINANCE4 7
+#define UPNG_LUMINANCE8 8
+#define UPNG_LUMINANCE_ALPHA1 9
+#define UPNG_LUMINANCE_ALPHA2 10
+#define UPNG_LUMINANCE_ALPHA4 11
+#define UPNG_LUMINANCE_ALPHA8 12
+
+#define UPNG_ERROR -1
+#define UPNG_DECODED 0
+#define UPNG_HEADER 1
+#define UPNG_NEW 2
+
+#define UPNG_LUM 0
+#define UPNG_RGB 2
+#define UPNG_LUMA 4
+#define UPNG_RGBA 6
+
+class upng_source {
+U8* buffer;
+U64 size;
+U8 owning;
+};
+
+class upng_t {
+U64 width;
+U64 height;
+I32 color_type;
+U64 color_depth;
+I64 format;
+U8* buffer;
+U64 size;
+I64 error;
+U64 error_line;
+I64 state;
+upng_source source;
+};
+
+class huffman_tree {
+U64* tree2d;
+U64 maxbitlen;
+U64 numcodes;
+};
+
+I64 upng_get_error(upng_t* upng)
+{
+ return upng->error;
+}
+U64 upng_get_error_line(upng_t* upng)
+{
+ return upng->error_line;
+}
+U64 upng_get_width(upng_t* upng)
+{
+ return upng->width;
+}
+U64 upng_get_height(upng_t* upng)
+{
+ return upng->height;
+}
+U64 upng_get_components(upng_t* upng)
+{
+ switch (upng->color_type) {
+ case UPNG_LUM:
+ return 1;
+ case UPNG_RGB:
+ return 3;
+ case UPNG_LUMA:
+ return 2;
+ case UPNG_RGBA:
+ return 4;
+ default:
+ return 0;
+ }
+}
+U64 upng_get_bitdepth(upng_t* upng)
+{
+ return upng->color_depth;
+}
+U64 upng_get_bpp(upng_t* upng)
+{
+ return upng_get_bitdepth(upng) * upng_get_components(upng);
+}
+U64 upng_get_pixelsize(upng_t* upng)
+{
+ U64 bits = upng_get_bitdepth(upng) * upng_get_components(upng);
+ bits += bits % 8;
+ return bits;
+}
+I64 upng_get_format(upng_t* upng)
+{
+ return upng->format;
+}
+U8* upng_get_buffer(upng_t* upng)
+{
+ return upng->buffer;
+}
+U64 upng_get_size(upng_t* upng)
+{
+ return upng->size;
+}
+
+
+U64 LENGTH_BASE[29] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258
+};
+U64 LENGTH_EXTRA[29] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+ 5, 5, 5, 0
+};
+U64 DISTANCE_BASE[30] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+U64 DISTANCE_EXTRA[30] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13
+};
+U64 CLCL[19]
+ = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+U64 FIXED_DEFLATE_CODE_TREE[288 * 2] = {
+ 289, 370, 290, 307, 546, 291, 561, 292, 293, 300, 294, 297, 295, 296, 0, 1,
+ 2, 3, 298, 299, 4, 5, 6, 7, 301, 304, 302, 303, 8, 9, 10, 11, 305, 306, 12,
+ 13, 14, 15, 308, 339, 309, 324, 310, 317, 311, 314, 312, 313, 16, 17, 18,
+ 19, 315, 316, 20, 21, 22, 23, 318, 321, 319, 320, 24, 25, 26, 27, 322, 323,
+ 28, 29, 30, 31, 325, 332, 326, 329, 327, 328, 32, 33, 34, 35, 330, 331, 36,
+ 37, 38, 39, 333, 336, 334, 335, 40, 41, 42, 43, 337, 338, 44, 45, 46, 47,
+ 340, 355, 341, 348, 342, 345, 343, 344, 48, 49, 50, 51, 346, 347, 52, 53,
+ 54, 55, 349, 352, 350, 351, 56, 57, 58, 59, 353, 354, 60, 61, 62, 63, 356,
+ 363, 357, 360, 358, 359, 64, 65, 66, 67, 361, 362, 68, 69, 70, 71, 364,
+ 367, 365, 366, 72, 73, 74, 75, 368, 369, 76, 77, 78, 79, 371, 434, 372,
+ 403, 373, 388, 374, 381, 375, 378, 376, 377, 80, 81, 82, 83, 379, 380, 84,
+ 85, 86, 87, 382, 385, 383, 384, 88, 89, 90, 91, 386, 387, 92, 93, 94, 95,
+ 389, 396, 390, 393, 391, 392, 96, 97, 98, 99, 394, 395, 100, 101, 102, 103,
+ 397, 400, 398, 399, 104, 105, 106, 107, 401, 402, 108, 109, 110, 111, 404,
+ 419, 405, 412, 406, 409, 407, 408, 112, 113, 114, 115, 410, 411, 116, 117,
+ 118, 119, 413, 416, 414, 415, 120, 121, 122, 123, 417, 418, 124, 125, 126,
+ 127, 420, 427, 421, 424, 422, 423, 128, 129, 130, 131, 425, 426, 132, 133,
+ 134, 135, 428, 431, 429, 430, 136, 137, 138, 139, 432, 433, 140, 141, 142,
+ 143, 435, 483, 436, 452, 568, 437, 438, 445, 439, 442, 440, 441, 144, 145,
+ 146, 147, 443, 444, 148, 149, 150, 151, 446, 449, 447, 448, 152, 153, 154,
+ 155, 450, 451, 156, 157, 158, 159, 453, 468, 454, 461, 455, 458, 456, 457,
+ 160, 161, 162, 163, 459, 460, 164, 165, 166, 167, 462, 465, 463, 464, 168,
+ 169, 170, 171, 466, 467, 172, 173, 174, 175, 469, 476, 470, 473, 471, 472,
+ 176, 177, 178, 179, 474, 475, 180, 181, 182, 183, 477, 480, 478, 479, 184,
+ 185, 186, 187, 481, 482, 188, 189, 190, 191, 484, 515, 485, 500, 486, 493,
+ 487, 490, 488, 489, 192, 193, 194, 195, 491, 492, 196, 197, 198, 199, 494,
+ 497, 495, 496, 200, 201, 202, 203, 498, 499, 204, 205, 206, 207, 501, 508,
+ 502, 505, 503, 504, 208, 209, 210, 211, 506, 507, 212, 213, 214, 215, 509,
+ 512, 510, 511, 216, 217, 218, 219, 513, 514, 220, 221, 222, 223, 516, 531,
+ 517, 524, 518, 521, 519, 520, 224, 225, 226, 227, 522, 523, 228, 229, 230,
+ 231, 525, 528, 526, 527, 232, 233, 234, 235, 529, 530, 236, 237, 238, 239,
+ 532, 539, 533, 536, 534, 535, 240, 241, 242, 243, 537, 538, 244, 245, 246,
+ 247, 540, 543, 541, 542, 248, 249, 250, 251, 544, 545, 252, 253, 254, 255,
+ 547, 554, 548, 551, 549, 550, 256, 257, 258, 259, 552, 553, 260, 261, 262,
+ 263, 555, 558, 556, 557, 264, 265, 266, 267, 559, 560, 268, 269, 270, 271,
+ 562, 565, 563, 564, 272, 273, 274, 275, 566, 567, 276, 277, 278, 279, 569,
+ 572, 570, 571, 280, 281, 282, 283, 573, 574, 284, 285, 286, 287, 0, 0
+};
+U64 FIXED_DISTANCE_TREE[32 * 2] = {
+ 33, 48, 34, 41, 35, 38, 36, 37, 0, 1, 2, 3, 39, 40, 4, 5, 6, 7, 42, 45, 43,
+ 44, 8, 9, 10, 11, 46, 47, 12, 13, 14, 15, 49, 56, 50, 53, 51, 52, 16, 17,
+ 18, 19, 54, 55, 20, 21, 22, 23, 57, 60, 58, 59, 24, 25, 26, 27, 61, 62, 28,
+ 29, 30, 31, 0, 0
+};
+U8 read_bit(U64 *bitpointer, U8 *bitstream)
+{
+ U8 result = ((bitstream[(*bitpointer) >> 3] >> ((*bitpointer) & 0x7)) & 1)(U8);
+ (*bitpointer)++;
+ return result;
+}
+U64 read_bits(U64 *bitpointer, U8 *bitstream, U64 nbits)
+{
+ U64 result = 0, i;
+ for (i = 0; i < nbits; i++)
+ result |= (read_bit(bitpointer, bitstream) (U64)) << i;
+ return result;
+}
+U0 huffman_tree_init(huffman_tree* tree, U64* buffer, U64 numcodes, U64 maxbitlen)
+{
+ tree->tree2d = buffer;
+ tree->numcodes = numcodes;
+ tree->maxbitlen = maxbitlen;
+}
+U0 huffman_tree_create_lengths(upng_t* upng, huffman_tree* tree, U64 *bitlen)
+{
+ U64 tree1d[288];
+ U64 blcount[15];
+ U64 nextcode[15 +1];
+ U64 bits, n, i;
+ U64 nodefilled = 0;
+ U64 treepos = 0;
+ MemSet(blcount, 0, sizeof(blcount));
+ MemSet(nextcode, 0, sizeof(nextcode));
+ for (bits = 0; bits < tree->numcodes; bits++) {
+ blcount[bitlen[bits]]++;
+ }
+ for (bits = 1; bits <= tree->maxbitlen; bits++) {
+ nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
+ }
+ for (n = 0; n < tree->numcodes; n++) {
+ if (bitlen[n] != 0) {
+ tree1d[n] = nextcode[bitlen[n]]++;
+ }
+ }
+ for (n = 0; n < tree->numcodes * 2; n++) {
+ tree->tree2d[n] = 32767;
+ }
+ for (n = 0; n < tree->numcodes; n++) {
+ for (i = 0; i < bitlen[n]; i++) {
+ U8 bit = ((tree1d[n] >> (bitlen[n] - i - 1)) & 1)(U8);
+ if (treepos > tree->numcodes - 2) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 245; } while (0);
+ return;
+ }
+ if (tree->tree2d[2 * treepos + bit] == 32767) {
+ if (i + 1 == bitlen[n]) {
+ tree->tree2d[2 * treepos + bit] = n;
+ treepos = 0;
+ } else {
+ nodefilled++;
+ tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes;
+ treepos = nodefilled;
+ }
+ } else {
+ treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes;
+ }
+ }
+ }
+ for (n = 0; n < tree->numcodes * 2; n++) {
+ if (tree->tree2d[n] == 32767) {
+ tree->tree2d[n] = 0;
+ }
+ }
+}
+U64 huffman_decode_symbol(upng_t *upng, U8 *in, U64 *bp, huffman_tree* codetree, U64 inlength)
+{
+ U64 treepos = 0, ct;
+ U8 bit;
+ while (1) {
+ if (((*bp) & 0x07) == 0 && ((*bp) >> 3) > inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 278; } while (0);
+ return 0;
+ }
+ bit = read_bit(bp, in);
+ ct = codetree->tree2d[(treepos << 1) | bit];
+ if (ct < codetree->numcodes) {
+ return ct;
+ }
+ treepos = ct - codetree->numcodes;
+ if (treepos >= codetree->numcodes) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 291; } while (0);
+ return 0;
+ }
+ }
+}
+U0 get_tree_inflate_dynamic(upng_t* upng, huffman_tree* codetree, huffman_tree* codetreeD, huffman_tree* codelengthcodetree, U8 *in, U64 *bp, U64 inlength)
+{
+ U64 codelengthcode[19];
+ U64 bitlen[288];
+ U64 bitlenD[32];
+ U64 replength;
+ U64 code;
+ U64 value;
+ U64 n, hlit, hdist, hclen, i;
+ if ((*bp) >> 3 >= inlength - 2) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 308; } while (0);
+ return;
+ }
+ MemSet(bitlen, 0, sizeof(bitlen));
+ MemSet(bitlenD, 0, sizeof(bitlenD));
+ hlit = read_bits(bp, in, 5) + 257;
+ hdist = read_bits(bp, in, 5) + 1;
+ hclen = read_bits(bp, in, 4) + 4;
+ for (i = 0; i < 19; i++) {
+ if (i < hclen) {
+ codelengthcode[CLCL[i]] = read_bits(bp, in, 3);
+ } else {
+ codelengthcode[CLCL[i]] = 0;
+ }
+ }
+ huffman_tree_create_lengths(upng, codelengthcodetree, codelengthcode);
+ if (upng->error != UPNG_EOK) {
+ return;
+ }
+ i = 0;
+ while (i < hlit + hdist) {
+ code = huffman_decode_symbol(upng, in, bp, codelengthcodetree, inlength);
+ if (upng->error != UPNG_EOK) {
+ break;
+ }
+ if (code <= 15) {
+ if (i < hlit) {
+ bitlen[i] = code;
+ } else {
+ bitlenD[i - hlit] = code;
+ }
+ i++;
+ } else if (code == 16) {
+ replength = 3;
+ if ((*bp) >> 3 >= inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 356; } while (0);
+ break;
+ }
+ replength += read_bits(bp, in, 2);
+ if ((i - 1) < hlit) {
+ value = bitlen[i - 1];
+ } else {
+ value = bitlenD[i - hlit - 1];
+ }
+ for (n = 0; n < replength; n++) {
+ if (i >= hlit + hdist) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 372; } while (0);
+ break;
+ }
+ if (i < hlit) {
+ bitlen[i] = value;
+ } else {
+ bitlenD[i - hlit] = value;
+ }
+ i++;
+ }
+ } else if (code == 17) {
+ replength = 3;
+ if ((*bp) >> 3 >= inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 386; } while (0);
+ break;
+ }
+ replength += read_bits(bp, in, 3);
+ for (n = 0; n < replength; n++) {
+ if (i >= hlit + hdist) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 397; } while (0);
+ break;
+ }
+ if (i < hlit) {
+ bitlen[i] = 0;
+ } else {
+ bitlenD[i - hlit] = 0;
+ }
+ i++;
+ }
+ } else if (code == 18) {
+ replength = 11;
+ if ((*bp) >> 3 >= inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 412; } while (0);
+ break;
+ }
+ replength += read_bits(bp, in, 7);
+ for (n = 0; n < replength; n++) {
+ if (i >= hlit + hdist) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 422; } while (0);
+ break;
+ }
+ if (i < hlit)
+ bitlen[i] = 0;
+ else
+ bitlenD[i - hlit] = 0;
+ i++;
+ }
+ } else {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 433; } while (0);
+ break;
+ }
+ }
+ if (upng->error == UPNG_EOK && bitlen[256] == 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 439; } while (0);
+ }
+ if (upng->error == UPNG_EOK) {
+ huffman_tree_create_lengths(upng, codetree, bitlen);
+ }
+ if (upng->error == UPNG_EOK) {
+ huffman_tree_create_lengths(upng, codetreeD, bitlenD);
+ }
+}
+U0 inflate_huffman(upng_t* upng, U8* out, U64 outsize, U8 *in, U64 *bp, U64 *pos, U64 inlength, U64 btype)
+{
+ U64 codetree_buffer[(288 * 2)];
+ U64 codetreeD_buffer[(32 * 2)];
+ U64 done = 0;
+ U64 code;
+ U64 length;
+ U64 codelengthcodetree_buffer[(32 * 2)];
+ U64 codeD, distance, numextrabitsD;
+ U64 start, forward, backward, numextrabits;
+
+ huffman_tree codetree;
+ huffman_tree codetreeD;
+ if (btype == 1) {
+ huffman_tree_init(&codetree, FIXED_DEFLATE_CODE_TREE(U64*), 288, 15);
+ huffman_tree_init(&codetreeD, FIXED_DISTANCE_TREE(U64*), 32, 15);
+ } else if (btype == 2) {
+ huffman_tree codelengthcodetree;
+ huffman_tree_init(&codetree, codetree_buffer, 288, 15);
+ huffman_tree_init(&codetreeD, codetreeD_buffer, 32, 15);
+ huffman_tree_init(&codelengthcodetree, codelengthcodetree_buffer, 19, 7);
+ get_tree_inflate_dynamic(upng, &codetree, &codetreeD, &codelengthcodetree, in, bp, inlength);
+ }
+ while (done == 0) {
+ code = huffman_decode_symbol(upng, in, bp, &codetree, inlength);
+ if (upng->error != UPNG_EOK) {
+ return;
+ }
+ if (code == 256) {
+ done = 1;
+ } else if (code <= 255) {
+ if ((*pos) >= outsize) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 489; } while (0);
+ return;
+ }
+ out[(*pos)++] = (code)(U8);
+ } else if (code >= 257 && code <= 285) {
+ length = LENGTH_BASE[code - 257];
+ numextrabits = LENGTH_EXTRA[code - 257];
+ if (((*bp) >> 3) >= inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 506; } while (0);
+ return;
+ }
+ length += read_bits(bp, in, numextrabits);
+ codeD = huffman_decode_symbol(upng, in, bp, &codetreeD, inlength);
+ if (upng->error != UPNG_EOK) {
+ return;
+ }
+ if (codeD > 29) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 519; } while (0);
+ return;
+ }
+ distance = DISTANCE_BASE[codeD];
+ numextrabitsD = DISTANCE_EXTRA[codeD];
+ if (((*bp) >> 3) >= inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 530; } while (0);
+ return;
+ }
+ distance += read_bits(bp, in, numextrabitsD);
+ start = (*pos);
+ backward = start - distance;
+ if ((*pos) + length >= outsize) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 541; } while (0);
+ return;
+ }
+ for (forward = 0; forward < length; forward++) {
+ out[(*pos)++] = out[backward];
+ backward++;
+ if (backward >= start) {
+ backward = start - distance;
+ }
+ }
+ }
+ }
+}
+U0 inflate_uncompressed(upng_t* upng, U8* out, U64 outsize, U8 *in, U64 *bp, U64 *pos, U64 inlength)
+{
+ U64 p;
+ U64 len, nlen, n;
+ while (((*bp) & 0x7) != 0) {
+ (*bp)++;
+ }
+ p = (*bp) / 8;
+ if (p >= inlength - 4) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 570; } while (0);
+ return;
+ }
+ len = in[p] + 256 * in[p + 1];
+ p += 2;
+ nlen = in[p] + 256 * in[p + 1];
+ p += 2;
+ if (len + nlen != 65535) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 581; } while (0);
+ return;
+ }
+ if ((*pos) + len >= outsize) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 586; } while (0);
+ return;
+ }
+ if (p + len > inlength) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 592; } while (0);
+ return;
+ }
+ for (n = 0; n < len; n++) {
+ out[(*pos)++] = in[p++];
+ }
+ (*bp) = p * 8;
+}
+I64 uz_inflate_data(upng_t* upng, U8* out, U64 outsize, U8 *in, U64 insize, U64 inpos)
+{
+ U64 bp = 0;
+ U64 pos = 0;
+ U64 done = 0;
+ while (done == 0) {
+ U64 btype;
+ if ((bp >> 3) >= insize) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 616; } while (0);
+ return upng->error;
+ }
+ done = read_bit(&bp, &in[inpos]);
+ btype = read_bit(&bp, &in[inpos]) | (read_bit(&bp, &in[inpos]) << 1);
+ if (btype == 3) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 626; } while (0);
+ return upng->error;
+ } else if (btype == 0) {
+ inflate_uncompressed(upng, out, outsize, &in[inpos], &bp, &pos, insize);
+ } else {
+ inflate_huffman(upng, out, outsize, &in[inpos], &bp, &pos, insize, btype);
+ }
+ if (upng->error != UPNG_EOK) {
+ return upng->error;
+ }
+ }
+ return upng->error;
+}
+I64 uz_inflate(upng_t* upng, U8 *out, U64 outsize, U8 *in, U64 insize)
+{
+ if (insize < 2) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 647; } while (0);
+ return upng->error;
+ }
+ if ((in[0] * 256 + in[1]) % 31 != 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 653; } while (0);
+ return upng->error;
+ }
+ if ((in[0] & 15) != 8 || ((in[0] >> 4) & 15) > 7) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 659; } while (0);
+ return upng->error;
+ }
+ if (((in[1] >> 5) & 1) != 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 665; } while (0);
+ return upng->error;
+ }
+ uz_inflate_data(upng, out, outsize, in, insize, 2);
+ return upng->error;
+}
+I64 paeth_predictor(I64 a, I64 b, I64 c)
+{
+ I64 p = a + b - c;
+ I64 pa = cond((p > a),p - a,a - p);
+ I64 pb = cond((p > b),p - b,b - p);
+ I64 pc = cond((p > c),p - c,c - p);
+ if (pa <= pb && pa <= pc)
+ return a;
+ else if (pb <= pc)
+ return b;
+ else
+ return c;
+}
+U0 unfilter_scanline(upng_t* upng, U8 *recon, U8 *scanline, U8 *precon, U64 bytewidth, U8 filterType, U64 length)
+{
+ U64 i;
+ switch (filterType) {
+ case 0:
+ for (i = 0; i < length; i++)
+ recon[i] = scanline[i];
+ break;
+ case 1:
+ for (i = 0; i < bytewidth; i++)
+ recon[i] = scanline[i];
+ for (i = bytewidth; i < length; i++)
+ recon[i] = scanline[i] + recon[i - bytewidth];
+ break;
+ case 2:
+ if (precon)
+ for (i = 0; i < length; i++)
+ recon[i] = scanline[i] + precon[i];
+ else
+ for (i = 0; i < length; i++)
+ recon[i] = scanline[i];
+ break;
+ case 3:
+ if (precon) {
+ for (i = 0; i < bytewidth; i++)
+ recon[i] = scanline[i] + precon[i] / 2;
+ for (i = bytewidth; i < length; i++)
+ recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
+ } else {
+ for (i = 0; i < bytewidth; i++)
+ recon[i] = scanline[i];
+ for (i = bytewidth; i < length; i++)
+ recon[i] = scanline[i] + recon[i - bytewidth] / 2;
+ }
+ break;
+ case 4:
+ if (precon) {
+ for (i = 0; i < bytewidth; i++)
+ recon[i] = (scanline[i] + paeth_predictor(0, precon[i], 0))(U8);
+ for (i = bytewidth; i < length; i++)
+ recon[i] = (scanline[i] + paeth_predictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]))(U8);
+ } else {
+ for (i = 0; i < bytewidth; i++)
+ recon[i] = scanline[i];
+ for (i = bytewidth; i < length; i++)
+ recon[i] = (scanline[i] + paeth_predictor(recon[i - bytewidth], 0, 0))(U8);
+ }
+ break;
+ default:
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 748; } while (0);
+ break;
+ }
+}
+U0 unfilter(upng_t* upng, U8 *out, U8 *in, U64 w, U64 h, U64 bpp)
+{
+ U64 y;
+ U8 *prevline = 0;
+ U64 bytewidth = (bpp + 7) / 8;
+ U64 linebytes = (w * bpp + 7) / 8;
+ for (y = 0; y < h; y++) {
+ U64 outindex = linebytes * y;
+ U64 inindex = (1 + linebytes) * y;
+ U8 filterType = in[inindex];
+ unfilter_scanline(upng, &out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes);
+ if (upng->error != UPNG_EOK) {
+ return;
+ }
+ prevline = &out[outindex];
+ }
+}
+U0 remove_padding_bits(U8 *out, U8 *in, U64 olinebits, U64 ilinebits, U64 h)
+{
+ U64 y;
+ U64 diff = ilinebits - olinebits;
+ U64 obp = 0, ibp = 0;
+ U8 bit;
+ for (y = 0; y < h; y++) {
+ U64 x;
+ for (x = 0; x < olinebits; x++) {
+ bit = ((in[(ibp) >> 3] >> (7 - ((ibp) & 0x7))) & 1)(U8);
+ ibp++;
+ if (bit == 0)
+ out[(obp) >> 3] &= (~(1 << (7 - ((obp) & 0x7))))(U8);
+ else
+ out[(obp) >> 3] |= (1 << (7 - ((obp) & 0x7)));
+ ++obp;
+ }
+ ibp += diff;
+ }
+}
+U0 post_process_scanlines(upng_t* upng, U8 *out, U8 *in, upng_t* info_png)
+{
+ U64 bpp = upng_get_bpp(info_png);
+ U64 w = info_png->width;
+ U64 h = info_png->height;
+ if (bpp == 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 818; } while (0);
+ return;
+ }
+ if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) {
+ unfilter(upng, in, in, w, h, bpp);
+ if (upng->error != UPNG_EOK) {
+ return;
+ }
+ remove_padding_bits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
+ } else {
+ unfilter(upng, out, in, w, h, bpp);
+ }
+}
+I64 determine_format(upng_t* upng) {
+ switch (upng->color_type) {
+ case UPNG_LUM:
+ switch (upng->color_depth) {
+ case 1:
+ return UPNG_LUMINANCE1;
+ case 2:
+ return UPNG_LUMINANCE2;
+ case 4:
+ return UPNG_LUMINANCE4;
+ case 8:
+ return UPNG_LUMINANCE8;
+ default:
+ return UPNG_BADFORMAT;
+ }
+ case UPNG_RGB:
+ switch (upng->color_depth) {
+ case 8:
+ return UPNG_RGB8;
+ case 16:
+ return UPNG_RGB16;
+ default:
+ return UPNG_BADFORMAT;
+ }
+ case UPNG_LUMA:
+ switch (upng->color_depth) {
+ case 1:
+ return UPNG_LUMINANCE_ALPHA1;
+ case 2:
+ return UPNG_LUMINANCE_ALPHA2;
+ case 4:
+ return UPNG_LUMINANCE_ALPHA4;
+ case 8:
+ return UPNG_LUMINANCE_ALPHA8;
+ default:
+ return UPNG_BADFORMAT;
+ }
+ case UPNG_RGBA:
+ switch (upng->color_depth) {
+ case 8:
+ return UPNG_RGBA8;
+ case 16:
+ return UPNG_RGBA16;
+ default:
+ return UPNG_BADFORMAT;
+ }
+ default:
+ return UPNG_BADFORMAT;
+ }
+}
+U0 upng_free_source(upng_t* upng)
+{
+ if (upng->source.owning != 0) {
+ Free(upng->source.buffer);
+ }
+ upng->source.buffer = NULL;
+ upng->source.size = 0;
+ upng->source.owning = 0;
+}
+I64 upng_header(upng_t* upng)
+{
+ if (upng->error != UPNG_EOK) {
+ return upng->error;
+ }
+ if (upng->state != UPNG_NEW) {
+ return upng->error;
+ }
+ if (upng->source.size < 29) {
+ do { (upng)->error = (UPNG_ENOTPNG); (upng)->error_line = 912; } while (0);
+ return upng->error;
+ }
+ if (upng->source.buffer[0] != 137 || upng->source.buffer[1] != 80 || upng->source.buffer[2] != 78 || upng->source.buffer[3] != 71 || upng->source.buffer[4] != 13 || upng->source.buffer[5] != 10 || upng->source.buffer[6] != 26 || upng->source.buffer[7] != 10) {
+ do { (upng)->error = (UPNG_ENOTPNG); (upng)->error_line = 918; } while (0);
+ return upng->error;
+ }
+ if ((((((upng->source.buffer + 12)[0]) & 0xFF) << 24) | ((((upng->source.buffer + 12)[1]) & 0xFF) << 16) | ((((upng->source.buffer + 12)[2]) & 0xFF) << 8) | (((upng->source.buffer + 12)[3]) & 0xFF)) != (((('I') & 0xFF) << 24) | ((('H') & 0xFF) << 16) | ((('D') & 0xFF) << 8) | (('R') & 0xFF))) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 924; } while (0);
+ return upng->error;
+ }
+ upng->width = (((((upng->source.buffer + 16)[0]) & 0xFF) << 24) | ((((upng->source.buffer + 16)[1]) & 0xFF) << 16) | ((((upng->source.buffer + 16)[2]) & 0xFF) << 8) | (((upng->source.buffer + 16)[3]) & 0xFF));
+ upng->height = (((((upng->source.buffer + 20)[0]) & 0xFF) << 24) | ((((upng->source.buffer + 20)[1]) & 0xFF) << 16) | ((((upng->source.buffer + 20)[2]) & 0xFF) << 8) | (((upng->source.buffer + 20)[3]) & 0xFF));
+ upng->color_depth = upng->source.buffer[24];
+ upng->color_type = upng->source.buffer[25](I32);
+ upng->format = determine_format(upng);
+ if (upng->format == UPNG_BADFORMAT) {
+ do { (upng)->error = (UPNG_EUNFORMAT); (upng)->error_line = 937; } while (0);
+ return upng->error;
+ }
+ if (upng->source.buffer[26] != 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 943; } while (0);
+ return upng->error;
+ }
+ if (upng->source.buffer[27] != 0) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 949; } while (0);
+ return upng->error;
+ }
+ if (upng->source.buffer[28] != 0) {
+ do { (upng)->error = (UPNG_EUNINTERLACED); (upng)->error_line = 955; } while (0);
+ return upng->error;
+ }
+ upng->state = UPNG_HEADER;
+ return upng->error;
+}
+I64 upng_decode(upng_t* upng)
+{
+ U8 *chunk;
+ U8* compressed;
+ U8* inflated;
+ U64 compressed_size = 0, compressed_index = 0;
+ U64 inflated_size;
+ I64 error;
+ U64 length;
+ U8 *data;
+
+ if (upng->error != UPNG_EOK) {
+ return upng->error;
+ }
+ upng_header(upng);
+ if (upng->error != UPNG_EOK) {
+ return upng->error;
+ }
+ if (upng->state != UPNG_HEADER) {
+ return upng->error;
+ }
+ if (upng->buffer != 0) {
+ Free(upng->buffer);
+ upng->buffer = 0;
+ upng->size = 0;
+ }
+ chunk = upng->source.buffer + 33;
+ while (chunk < upng->source.buffer + upng->source.size) {
+ if ((chunk - upng->source.buffer + 12)(U64) > upng->source.size) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 1007; } while (0);
+ return upng->error;
+ }
+ length = (((((chunk)[0]) & 0xFF) << 24) | ((((chunk)[1]) & 0xFF) << 16) | ((((chunk)[2]) & 0xFF) << 8) | (((chunk)[3]) & 0xFF));
+ if (length > 0x7fffffff) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 1014; } while (0);
+ return upng->error;
+ }
+ if ((chunk - upng->source.buffer + length + 12)(U64) > upng->source.size) {
+ do { (upng)->error = (UPNG_EMALFORMED); (upng)->error_line = 1020; } while (0);
+ return upng->error;
+ }
+ data = chunk + 8;
+ if (((((((chunk) + 4)[0]) & 0xFF) << 24) | (((((chunk) + 4)[1]) & 0xFF) << 16) | (((((chunk) + 4)[2]) & 0xFF) << 8) | ((((chunk) + 4)[3]) & 0xFF)) == (((('I') & 0xFF) << 24) | ((('D') & 0xFF) << 16) | ((('A') & 0xFF) << 8) | (('T') & 0xFF))) {
+ compressed_size += length;
+ } else if (((((((chunk) + 4)[0]) & 0xFF) << 24) | (((((chunk) + 4)[1]) & 0xFF) << 16) | (((((chunk) + 4)[2]) & 0xFF) << 8) | ((((chunk) + 4)[3]) & 0xFF)) == (((('I') & 0xFF) << 24) | ((('E') & 0xFF) << 16) | ((('N') & 0xFF) << 8) | (('D') & 0xFF))) {
+ break;
+ } else if ((((chunk)[4] & 32) == 0)) {
+ do { (upng)->error = (UPNG_EUNSUPPORTED); (upng)->error_line = 1033; } while (0);
+ return upng->error;
+ }
+ chunk += (((((chunk)[0]) & 0xFF) << 24) | ((((chunk)[1]) & 0xFF) << 16) | ((((chunk)[2]) & 0xFF) << 8) | (((chunk)[3]) & 0xFF)) + 12;
+ }
+ compressed = MAlloc(compressed_size) (U8*);
+ if (compressed == NULL) {
+ do { (upng)->error = (UPNG_ENOMEM); (upng)->error_line = 1043; } while (0);
+ return upng->error;
+ }
+ chunk = upng->source.buffer + 33;
+ while (chunk < upng->source.buffer + upng->source.size) {
+ length = (((((chunk)[0]) & 0xFF) << 24) | ((((chunk)[1]) & 0xFF) << 16) | ((((chunk)[2]) & 0xFF) << 8) | (((chunk)[3]) & 0xFF));
+ data = chunk + 8;
+ if (((((((chunk) + 4)[0]) & 0xFF) << 24) | (((((chunk) + 4)[1]) & 0xFF) << 16) | (((((chunk) + 4)[2]) & 0xFF) << 8) | ((((chunk) + 4)[3]) & 0xFF)) == (((('I') & 0xFF) << 24) | ((('D') & 0xFF) << 16) | ((('A') & 0xFF) << 8) | (('T') & 0xFF))) {
+ MemCpy(compressed + compressed_index, data, length);
+ compressed_index += length;
+ } else if (((((((chunk) + 4)[0]) & 0xFF) << 24) | (((((chunk) + 4)[1]) & 0xFF) << 16) | (((((chunk) + 4)[2]) & 0xFF) << 8) | ((((chunk) + 4)[3]) & 0xFF)) == (((('I') & 0xFF) << 24) | ((('E') & 0xFF) << 16) | ((('N') & 0xFF) << 8) | (('D') & 0xFF))) {
+ break;
+ }
+ chunk += (((((chunk)[0]) & 0xFF) << 24) | ((((chunk)[1]) & 0xFF) << 16) | ((((chunk)[2]) & 0xFF) << 8) | (((chunk)[3]) & 0xFF)) + 12;
+ }
+ inflated_size = ((upng->width * (upng->height * upng_get_bpp(upng) + 7)) / 8) + upng->height;
+ inflated = MAlloc(inflated_size) (U8*);
+ if (inflated == NULL) {
+ Free(compressed);
+ do { (upng)->error = (UPNG_ENOMEM); (upng)->error_line = 1073; } while (0);
+ return upng->error;
+ }
+ error = uz_inflate(upng, inflated, inflated_size, compressed, compressed_size);
+ if (error != UPNG_EOK) {
+ Free(compressed);
+ Free(inflated);
+ return upng->error;
+ }
+ Free(compressed);
+ upng->size = (upng->height * upng->width * upng_get_bpp(upng) + 7) / 8;
+ upng->buffer = MAlloc(upng->size) (U8*);
+ if (upng->buffer == NULL) {
+ Free(inflated);
+ upng->size = 0;
+ do { (upng)->error = (UPNG_ENOMEM); (upng)->error_line = 1094; } while (0);
+ return upng->error;
+ }
+ post_process_scanlines(upng, upng->buffer, inflated, upng);
+ Free(inflated);
+ if (upng->error != UPNG_EOK) {
+ Free(upng->buffer);
+ upng->buffer = NULL;
+ upng->size = 0;
+ } else {
+ upng->state = UPNG_DECODED;
+ }
+ upng_free_source(upng);
+ return upng->error;
+}
+upng_t* upng_new()
+{
+ upng_t* upng;
+ upng = MAlloc(sizeof(upng_t))(upng_t*);
+ if (upng == NULL) {
+ return NULL;
+ }
+ upng->buffer = NULL;
+ upng->size = 0;
+ upng->width = upng->height = 0;
+ upng->color_type = UPNG_RGBA;
+ upng->color_depth = 8;
+ upng->format = UPNG_RGBA8;
+ upng->state = UPNG_NEW;
+ upng->error = UPNG_EOK;
+ upng->error_line = 0;
+ upng->source.buffer = NULL;
+ upng->source.size = 0;
+ upng->source.owning = 0;
+ return upng;
+}
+upng_t* upng_new_from_bytes(U8* buffer, U64 size)
+{
+ upng_t* upng = upng_new;
+ if (upng == NULL) {
+ return NULL;
+ }
+ upng->source.buffer = buffer;
+ upng->source.size = size;
+ upng->source.owning = 0;
+ return upng;
+}
+U0 upng_Free(upng_t* upng)
+{
+ if (upng->buffer != NULL) {
+ Free(upng->buffer);
+ }
+ upng_free_source(upng);
+ Free(upng);
+}
+upng_t* upng_new_from_file(U8 *filename)
+{
+ upng_t* upng = upng_new;
+ I64 size;
+ U8 *file = FileRead(filename,&size);
+ if (file == NULL) {
+ do { (upng)->error = (UPNG_ENOTFOUND); (upng)->error_line = 1174; } while (0);
+ return upng;
+ }
+ upng_Free(upng);
+ upng = upng_new_from_bytes(file,size);
+ upng->source.owning=1;
+ return upng;
+}
+
+Context2D *PNGtoContext2D(upng_t* upng, I64 alpha_threshold=NULL, I64 alpha_color)
+{
+ Context2D *ctx=NULL;
+ I64 i,j;
+ I64 w,h,bpp;
+ U8 *ptr;
+ U32 color=0;
+
+ if (upng_decode(upng))
+ {
+ upng_Free(upng);
+ return ctx;
+ }
+
+ w=upng_get_width(upng);
+ h=upng_get_height(upng);
+ bpp=upng_get_bpp(upng)/8; // **BYTES** per pixel.
+
+ ctx=NewContext2D(w, h, display.bpp);
+
+ switch(upng->format)
+ {
+ case UPNG_RGBA8:
+ case UPNG_RGBA16:
+ ctx->alpha_color = alpha_color;
+ Fill2D(ctx, alpha_color); // Alpha channel.
+ break;
+ default:
+ Fill2D(ctx);
+ break;
+ }
+
+ ptr=upng_get_buffer(upng);
+
+ for (i=0; i<h; i++)
+ {
+ for (j=0; j<w; j++,ptr+=bpp)
+ {
+ switch(upng->format)
+ {
+ case UPNG_RGBA8:
+ if (ptr[3]>alpha_threshold)
+ {
+ color.u8[1] = ptr[2];
+ color.u8[2] = ptr[1];
+ color.u8[3] = ptr[0];
+ Plot2D(ctx, j, i, color);
+ }
+ break;
+ case UPNG_RGBA16:
+ if ((ptr+6)(U16*)>alpha_threshold)
+ {
+ /* TODO: 16bpp support.
+ color.u8[1] = ptr[0];
+ color.u8[2] = ptr[1];
+ color.u8[3] = ptr[2];
+ Plot2D(ctx, j, i, color);
+ */
+ }
+ break;
+ case UPNG_RGB8:
+ color.u8[1] = ptr[2];
+ color.u8[2] = ptr[1];
+ color.u8[3] = ptr[0];
+ Plot2D(ctx, j, i, color);
+ break;
+ case UPNG_RGB16:
+ // TODO: 16bpp support.
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ upng_Free(upng);
+ return ctx;
+}
+
+Context2D *LoadPNG(U8 *filename, I64 alpha_threshold=NULL, I64 alpha_color=0x00FF0000, I64 scale=1)
+{ // Load (8bpp) RGB/RGBA PNG file into new Context2D.
+ I64 ox, oy;
+ I64 sx, sy;
+ U32 color;
+ if (!scale) return NULL;
+ Context2D *orig;
+ Context2D *scaled;
+ upng_t* upng;
+ upng = upng_new_from_file(filename);
+ if (scale==1) return PNGtoContext2D(upng, alpha_threshold, alpha_color);
+ orig = PNGtoContext2D(upng, alpha_threshold, alpha_color);
+ scaled = NewContext2D(orig->width*scale, orig->height*scale, orig->bpp);
+ sy = 0;
+ for (oy=0; oy<orig->height; oy++)
+ {
+ sx = 0;
+ for (ox=0; ox<orig->width; ox++)
+ {
+ color = Peek2D(orig, ox, oy);
+ Rect2D(scaled, sx, sy, scale, scale, color);
+ sx += scale;
+ }
+ sy += scale;
+ }
+ Free(orig);
+ return scaled;
+}
+
+Fs->put_doc = origdoc;
+DocDel(tmpdoc); \ No newline at end of file
diff --git a/LoROM.HC b/LoROM.HC
new file mode 100644
index 0000000..42f6b69
--- /dev/null
+++ b/LoROM.HC
@@ -0,0 +1,42 @@
+U0 parseHeader(U8 *rom, ROMHeader *header)
+{
+ I64 i;
+ header->name = CAlloc(24);
+ MemCpy(header->name, rom+0x7FC0, 20);
+ i = StrLen(header->name)-1;
+ while (header->name[i]==' ') { header->name[i]=NULL; i--; }
+ header->type = rom[0x7fd5] & 0xf;
+ header->speed = rom[0x7fd5] >> 4;
+ header->chips = rom[0x7fd6];
+ header->romSize = 0x400 << rom[0x7fd7];
+ header->ramSize = 0x400 << rom[0x7fd8];
+ header->hasSram = header->chips > 0;
+ header->banks = header->romSize / 0x8000;
+ header->sramSize = cond(header->hasSram, header->ramSize, 0);
+ if (header->hasSram) sram = MAlloc(header->ramSize);
+}
+
+U0 LoROM(U8 *filename)
+{
+
+ // LoROM loader
+ MemSet(&header, NULL, sizeof(ROMHeader));
+ I64 rom_length;
+ rom = FileRead(filename, &rom_length);
+
+ // skip copier header
+ if (rom_length%1024)
+ {
+ rom += 512;
+ rom_length -= 512;
+ }
+
+ parseHeader(rom, &header);
+
+ if (header.romSize < rom_length)
+ {
+ "Incorrect romSize?\n";
+ return;
+ }
+
+} \ No newline at end of file
diff --git a/Load.HC b/Load.HC
new file mode 100644
index 0000000..51b0753
--- /dev/null
+++ b/Load.HC
@@ -0,0 +1,106 @@
+#include "Lib/VMSVGA";
+#include "Lib/Display";
+#include "Lib/Graphics2D";
+#include "Lib/uPNG";
+
+#include "Lib/Misc";
+
+#include "Lib/Debugger";
+
+Context2D *screen_ctx = NewContext2D(640, 480, 32);
+Context2D *pixel_ctx = NewContext2D(512, 480, 32);
+
+#include "Font";
+#include "Snes";
+#include "LoROM";
+
+#include "MMU";
+#include "CPU";
+#include "PPU";
+
+U0 cpu_task()
+{
+ while (1) { if (!Debug && !KeyDown(SC_F1)) cycle(0); };
+}
+
+U0 gfx_task()
+{
+ while (1)
+ {
+ if (yPos>238 && !Debug)
+ {
+ setPixels(pixel_ctx->fb);
+ CopyRect2D(screen_ctx, (screen_ctx->width/2)-(pixel_ctx->width/2), 0, pixel_ctx);
+ UpdateTOSMenuBar(screen_ctx);
+ Flip2D(screen_ctx);
+ }
+ }
+}
+
+U0 input_loop()
+{
+ while (1)
+ {
+ WinMsUpdate;
+ KbdMsHndlr(0, 0);
+ if (KeyDown(SC_ESC))
+ {
+ FifoI64Flush(kbd.down_bitmap);
+ Debug = TRUE;
+ Dbg;
+ }
+ if (KeyDown(Char2ScanCode('`')))
+ {
+ cpu_reset;
+ ppu_reset;
+ }
+ Joypad1Update;
+ Sleep(1);
+ }
+}
+
+U0 LoadCart(U8 *filename)
+{
+ // FIXME: Add sanity checks
+ LoROM(filename);
+ U8 *info_string=CAlloc(1024);
+
+ StrPrint(append(info_string), "Loaded ROM: \"%s\"; \n", header.name);
+ StrPrint(append(info_string), "Type: LoROM\n");
+ StrPrint(append(info_string), "Banks: %d; Sram size: 0x%04X\n", header.banks, header.sramSize);
+ PopUpOk(info_string);
+
+ VMSVGA_Start;
+ SysFrameBufferInit;
+
+ cpu_reset;
+
+ Spawn(&cpu_task,,,1);
+ Spawn(&gfx_task,,,2);
+
+ input_loop;
+ Free(info_string);
+}
+
+U0 Bahamut(U8 *filename)
+{
+ if (IsDir(filename))
+ {
+ PopUpOk("That's a directory...");
+ return;
+ }
+ CDirEntry *file = FilesFind(filename);
+ if (file)
+ {
+ DirTreeDel(file);
+ LoadCart(filename);
+ return;
+ }
+ else
+ {
+ PopUpOk("File not found...");
+ return;
+ }
+}
+
+while (1) Bahamut(PopUpPickFile("T:/ROMs/"));
diff --git a/MMU.HC b/MMU.HC
new file mode 100644
index 0000000..3cad9fc
--- /dev/null
+++ b/MMU.HC
@@ -0,0 +1,436 @@
+extern U16 br[6];
+
+extern I32 ppu_read(I32 adr);
+extern U0 ppu_write(I32 adr, I32 value);
+
+I64 cart_read(I64 bank, I64 adr)
+{
+ //"bank: %d, addr: %08X\n", bank, adr;
+ if(adr < 0x8000) {
+ if(bank >= 0x70 && bank < 0x7e && header.hasSram) {
+ // sram
+ return sram[
+ (((bank - 0x70) << 15) | (adr & 0x7fff)) & (header.sramSize - 1)
+ ];
+ return NULL;
+ }
+ }
+
+ return rom[((bank & (header.banks - 1)) << 15) | (adr & 0x7fff)];
+}
+
+U0 cart_write(I64 bank, I64 adr, I64 value)
+{
+ if(adr < 0x8000 && bank >= 0x70 && bank < 0x7e && header.hasSram) {
+ sram[
+ (((bank - 0x70) << 15) | (adr & 0x7fff)) & (header.sramSize - 1)
+ ] = value;
+ }
+}
+
+I64 readReg(I64 adr)
+{
+ I64 val;
+ switch(adr) {
+ case 0x4210: {
+ val = 0x1;
+ val |= cond(inNmi, 0x80, 0);
+ val |= openBus & 0x70;
+ inNmi = FALSE;
+ return val;
+ }
+ case 0x4211: {
+ val = cond(inIrq, 0x80, 0);
+ val |= openBus & 0x7f;
+ inIrq = FALSE;
+ irqWanted = FALSE;
+ return val;
+ }
+ case 0x4212: {
+ val = cond(autoJoyBusy, 0x1, 0);
+ val |= cond(inHblank, 0x40, 0);
+ val |= cond(inVblank, 0x80, 0);
+ val |= openBus & 0x3e;
+ return val;
+ }
+ case 0x4213: {
+ // IO read register
+ return cond(ppuLatch, 0x80, 0);
+ }
+ case 0x4214: {
+ return divResult & 0xff;
+ }
+ case 0x4215: {
+ return (divResult & 0xff00) >> 8;
+ }
+ case 0x4216: {
+ return mulResult & 0xff;
+ }
+ case 0x4217: {
+ return (mulResult & 0xff00) >> 8;
+ }
+ case 0x4218: {
+ return joypad1AutoRead & 0xff;
+ }
+ case 0x4219: {
+ return (joypad1AutoRead & 0xff00) >> 8;
+ }
+ case 0x421a: {
+ return joypad2AutoRead & 0xff;
+ }
+ case 0x421b: {
+ return (joypad2AutoRead & 0xff00) >> 8;
+ }
+ case 0x421c:
+ case 0x421d:
+ case 0x421e:
+ case 0x421f: {
+ // joypads 3 and 4 not emulated
+ return 0;
+ }
+ }
+
+ if(adr >= 0x4300 && adr < 0x4380) {
+ I64 channel = (adr & 0xf0) >> 4;
+ switch(adr & 0xff0f) {
+ case 0x4300: {
+ val = dmaMode[channel];
+ val |= cond(dmaFixed[channel], 0x8, 0);
+ val |= cond(dmaDec[channel], 0x10, 0);
+ val |= cond(hdmaInd[channel], 0x40, 0);
+ val |= cond(dmaFromB[channel], 0x80, 0);
+ return val;
+ }
+ case 0x4301: {
+ return dmaBadr[channel];
+ }
+ case 0x4302: {
+ return dmaAadr[channel] & 0xff;
+ }
+ case 0x4303: {
+ return (dmaAadr[channel] & 0xff00) >> 8;
+ }
+ case 0x4304: {
+ return dmaAadrBank[channel];
+ }
+ case 0x4305: {
+ return dmaSize[channel] & 0xff;
+ }
+ case 0x4306: {
+ return (dmaSize[channel] & 0xff00) >> 8;
+ }
+ case 0x4307: {
+ return hdmaIndBank[channel];
+ }
+ case 0x4308: {
+ return hdmaTableAdr[channel] & 0xff;
+ }
+ case 0x4309: {
+ return (hdmaTableAdr[channel] & 0xff00) >> 8;
+ }
+ case 0x430a: {
+ return hdmaRepCount[channel];
+ }
+ }
+ }
+
+ return openBus;
+}
+
+U0 writeReg(I64 adr, I64 value) {
+ I64 channel;
+ switch(adr) {
+ case 0x4200: {
+ autoJoyRead = (value & 0x1) > 0;
+ hIrqEnabled = (value & 0x10) > 0;
+ vIrqEnabled = (value & 0x20) > 0;
+ nmiEnabled = (value & 0x80) > 0;
+ return;
+ }
+ case 0x4201: {
+ // IO port
+ if(ppuLatch && (value & 0x80) == 0) {
+ latchedHpos = xPos >> 2;
+ latchedVpos = yPos;
+ countersLatched = TRUE;
+ }
+ ppuLatch = (value & 0x80) > 0;
+ return;
+ }
+ case 0x4202: {
+ multiplyA = value;
+ return;
+ }
+ case 0x4203: {
+ mulResult = multiplyA * value;
+ return;
+ }
+ case 0x4204: {
+ divA = (divA & 0xff00) | value;
+ return;
+ }
+ case 0x4205: {
+ divA = (divA & 0xff) | (value << 8);
+ return;
+ }
+ case 0x4206: {
+ divResult = 0xffff;
+ mulResult = divA;
+ if(value != 0) {
+ divResult = (divA / value) & 0xffff;
+ mulResult = divA % value;
+ }
+ return;
+ }
+ case 0x4207: {
+ hTimer = (hTimer & 0x100) | value;
+ return;
+ }
+ case 0x4208: {
+ hTimer = (hTimer & 0xff) | ((value & 0x1) << 8);
+ return;
+ }
+ case 0x4209: {
+ vTimer = (vTimer & 0x100) | value;
+ return;
+ }
+ case 0x420a: {
+ vTimer = (vTimer & 0xff) | ((value & 0x1) << 8);
+ return;
+ }
+ case 0x420b: {
+ // enable dma
+ dmaActive[0] = (value & 0x1) > 0;
+ dmaActive[1] = (value & 0x2) > 0;
+ dmaActive[2] = (value & 0x4) > 0;
+ dmaActive[3] = (value & 0x8) > 0;
+ dmaActive[4] = (value & 0x10) > 0;
+ dmaActive[5] = (value & 0x20) > 0;
+ dmaActive[6] = (value & 0x40) > 0;
+ dmaActive[7] = (value & 0x80) > 0;
+ dmaBusy = value > 0;
+ dmaTimer += cond(dmaBusy, 8, 0);
+ return;
+ }
+ case 0x420c: {
+ hdmaActive[0] = (value & 0x1) > 0;
+ hdmaActive[1] = (value & 0x2) > 0;
+ hdmaActive[2] = (value & 0x4) > 0;
+ hdmaActive[3] = (value & 0x8) > 0;
+ hdmaActive[4] = (value & 0x10) > 0;
+ hdmaActive[5] = (value & 0x20) > 0;
+ hdmaActive[6] = (value & 0x40) > 0;
+ hdmaActive[7] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x420d: {
+ fastMem = (value & 0x1) > 0;
+ return;
+ }
+ }
+
+ if(adr >= 0x4300 && adr < 0x4380) {
+ channel = (adr & 0xf0) >> 4;
+ switch(adr & 0xff0f) {
+ case 0x4300: {
+ dmaMode[channel] = value & 0x7;
+ dmaFixed[channel] = (value & 0x08) > 0;
+ dmaDec[channel] = (value & 0x10) > 0;
+ hdmaInd[channel] = (value & 0x40) > 0;
+ dmaFromB[channel] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x4301: {
+ dmaBadr[channel] = value;
+ return;
+ }
+ case 0x4302: {
+ dmaAadr[channel] = (dmaAadr[channel] & 0xff00) | value;
+ return;
+ }
+ case 0x4303: {
+ dmaAadr[channel] = (dmaAadr[channel] & 0xff) | (value << 8);
+ return;
+ }
+ case 0x4304: {
+ dmaAadrBank[channel] = value;
+ return;
+ }
+ case 0x4305: {
+ dmaSize[channel] = (dmaSize[channel] & 0xff00) | value;
+ return;
+ }
+ case 0x4306: {
+ dmaSize[channel] = (dmaSize[channel] & 0xff) | (value << 8);
+ return;
+ }
+ case 0x4307: {
+ hdmaIndBank[channel] = value;
+ return;
+ }
+ case 0x4308: {
+ hdmaTableAdr[channel] = (
+ hdmaTableAdr[channel] & 0xff00
+ ) | value;
+ return;
+ }
+ case 0x4309: {
+ hdmaTableAdr[channel] = (
+ hdmaTableAdr[channel] & 0xff
+ ) | (value << 8);
+ return;
+ }
+ case 0x430a: {
+ hdmaRepCount[channel] = value;
+ return;
+ }
+ }
+ }
+}
+
+Bool flip_fake_spc = FALSE; // FIXME: Implement sound emulation
+
+I64 readBBus(I64 adr) {
+ I64 val;
+ if(adr > 0x33 && adr < 0x40) {
+ return ppu_read(adr);
+ }
+ if(adr >= 0x40 && adr < 0x80) {
+ // catch up the apu, then do the read
+ catchUpApu;
+ // FIXME: Implement sound emulation
+ if (flip_fake_spc) return br[0];
+ flip_fake_spc = !flip_fake_spc;
+ return 0xBBAA;
+ //return spcWritePorts[adr & 0x3];
+ }
+ if(adr == 0x80) {
+ val = ram[ramAdr++];
+ ramAdr &= 0x1ffff;
+ return val;
+ }
+ return openBus; // rest not readable
+}
+
+U0 writeBBus(I64 adr, I64 value) {
+ if(adr < 0x34) {
+ ppu_write(adr, value);
+ return;
+ }
+ if(adr >= 0x40 && adr < 0x80) {
+ // catch up the apu, then do the write
+ catchUpApu;
+ spcReadPorts[adr & 0x3] = value;
+ return;
+ }
+ switch(adr) {
+ case 0x80: {
+ ram[ramAdr++] = value;
+ ramAdr &= 0x1ffff;
+ return;
+ }
+ case 0x81: {
+ ramAdr = (ramAdr & 0x1ff00) | value;
+ return;
+ }
+ case 0x82: {
+ ramAdr = (ramAdr & 0x100ff) | (value << 8);
+ return;
+ }
+ case 0x83: {
+ ramAdr = (ramAdr & 0x0ffff) | ((value & 1) << 16);
+ return;
+ }
+ }
+ return;
+}
+
+I64 mem_rread(I64 adr)
+{
+ I64 val;
+ adr &= 0xffffff;
+ I64 bank = adr >> 16;
+ adr &= 0xffff;
+
+ if(bank == 0x7e || bank == 0x7f) {
+ // banks 7e and 7f
+ return ram[((bank & 0x1) << 16) | adr];
+ return NULL;
+ }
+ if(adr < 0x8000 && (bank < 0x40 || (bank >= 0x80 && bank < 0xc0))) {
+ // banks 00-3f, 80-bf, $0000-$7fff
+ if(adr < 0x2000) {
+ return ram[adr & 0x1fff];
+ return NULL;
+ }
+ if(adr >= 0x2100 && adr < 0x2200) {
+ return readBBus(adr & 0xff);
+ return NULL;
+ }
+ // old-style controller reads
+ if(adr == 0x4016) {
+ val = joypad1Val & 0x1;
+ joypad1Val >>= 1;
+ joypad1Val |= 0x8000;
+ return val;
+ }
+ if(adr == 0x4017) {
+ val = joypad2Val & 0x1;
+ joypad2Val >>= 1;
+ joypad2Val |= 0x8000;
+ return val;
+ }
+ if(adr >= 0x4200 && adr < 0x4400) {
+ return readReg(adr);
+ }
+
+ return openBus; // not readable
+ }
+ return cart_read(bank, adr);
+}
+
+I64 mem_read(I64 adr, Bool dma=FALSE)
+{
+ if(!dma) {
+ cpuMemOps++;
+ cpuCyclesLeft += getAccessTime(adr);
+ }
+
+ I64 val = mem_rread(adr);
+ openBus = val;
+ return val;
+}
+
+U0 mem_write(I64 adr, I64 value, Bool dma = FALSE) {
+ if(!dma) {
+ cpuMemOps++;
+ cpuCyclesLeft += getAccessTime(adr);
+ }
+
+ openBus = value;
+ adr &= 0xffffff;
+ //log("Written $" + getByteRep(value) + " to $" + getLongRep(adr));
+ I64 bank = adr >> 16;
+ adr &= 0xffff;
+ if(bank == 0x7e || bank == 0x7f) {
+ // banks 7e and 7f
+ ram[((bank & 0x1) << 16) | adr] = value;
+ }
+ if(adr < 0x8000 && (bank < 0x40 || (bank >= 0x80 && bank < 0xc0))) {
+ // banks 00-3f, 80-bf, $0000-$7fff
+ if(adr < 0x2000) {
+ ram[adr & 0x1fff] = value;
+ }
+ if(adr >= 0x2100 && adr < 0x2200) {
+ writeBBus(adr & 0xff, value);
+ }
+ if(adr == 0x4016) {
+ joypadStrobe = (value & 0x1) > 0;
+ }
+ if(adr >= 0x4200 && adr < 0x4400) {
+ writeReg(adr, value);
+ }
+
+ }
+ cart_write(bank, adr, value);
+} \ No newline at end of file
diff --git a/PPU.HC b/PPU.HC
new file mode 100644
index 0000000..72ed008
--- /dev/null
+++ b/PPU.HC
@@ -0,0 +1,1397 @@
+ U16 *vram = MAlloc(0x8000*2);
+
+ U16 *cgram = MAlloc(0x100*2);
+
+ U16 *oam = MAlloc(0x100*2);
+ U16 *highOam = MAlloc(0x10*2);
+
+ U8 *spriteLineBuffer = MAlloc(256);
+ U8 *spritePrioBuffer = MAlloc(256);
+
+ I32 *mode7Xcoords = MAlloc(256*4);
+ I32 *mode7Ycoords = MAlloc(256*4);
+
+ U16 *pixelOutput = MAlloc((512*3*240)*2);
+
+ I32 layersPerMode[120] = {
+ 4, 0, 1, 4, 0, 1, 4, 2, 3, 4, 2, 3,
+ 4, 0, 1, 4, 0, 1, 4, 2, 4, 2, 5, 5,
+ 4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
+ 4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
+ 4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
+ 4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5,
+ 4, 0, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5,
+ 4, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5, 5,
+ 2, 4, 0, 1, 4, 0, 1, 4, 2, 4, 5, 5,
+ 4, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5, 5
+ };
+
+ I32 prioPerMode[120] = {
+ 3, 1, 1, 2, 0, 0, 1, 1, 1, 0, 0, 0,
+ 3, 1, 1, 2, 0, 0, 1, 1, 0, 0, 5, 5,
+ 3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
+ 3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
+ 3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
+ 3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5,
+ 3, 1, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5,
+ 3, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5,
+ 1, 3, 1, 1, 2, 0, 0, 1, 0, 0, 5, 5,
+ 3, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5, 5
+ };
+
+ I32 bitPerMode[40] = {
+ 2, 2, 2, 2,
+ 4, 4, 2, 5,
+ 4, 4, 5, 5,
+ 8, 4, 5, 5,
+ 8, 2, 5, 5,
+ 4, 2, 5, 5,
+ 4, 5, 5, 5,
+ 8, 5, 5, 5,
+ 4, 4, 2, 5,
+ 8, 7, 5, 5
+ };
+
+ I32 layercountPerMode[10] = {12, 10, 8, 8, 8, 8, 6, 5, 10, 7};
+
+ F64 brightnessMults[16] = {
+ 0.1, 0.5, 1.1, 1.6, 2.2, 2.7, 3.3, 3.8, 4.4, 4.9, 5.5, 6, 6.6, 7.1, 7.6, 8.2
+ ;
+
+ I32 spriteTileOffsets[64] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 112, 113, 114, 115, 116, 117, 118, 119
+ };
+
+ I32 spriteSizes[16] = {
+ 1, 1, 1, 2, 2, 4, 2, 2,
+ 2, 4, 8, 4, 8, 8, 4, 4
+ };
+
+
+U0 ppu_reset()
+{
+ I32 ii;
+ /*
+ clearArray(vram);
+ clearArray(cgram);
+ clearArray(oam);
+ clearArray(highOam);
+
+ clearArray(spriteLineBuffer);
+ clearArray(spritePrioBuffer);
+
+ clearArray(pixelOutput);
+
+ clearArray(mode7Xcoords);
+ clearArray(mode7Ycoords);
+ */
+
+ MemSet(vram, NULL, 0x8000*2);
+ MemSet(cgram, NULL, 0x100*2);
+ MemSet(oam, NULL, 0x100*2);
+ MemSet(highOam, NULL,0x10*2);
+ MemSet(spriteLineBuffer, NULL, 256);
+ MemSet(spritePrioBuffer, NULL, 256);
+
+ MemSet(mode7Xcoords, NULL, 256*4);
+ MemSet(mode7Ycoords, NULL, 256*4);
+
+ MemSet(pixelOutput, NULL, ((512*3*240)*2));
+
+ cgramAdr = 0;
+ cgramSecond = FALSE;
+ cgramBuffer = 0;
+
+ vramInc = 0;
+ vramRemap = 0;
+ vramIncOnHigh = FALSE;
+ vramAdr = 0;
+ vramReadBuffer = 0;
+
+ for (ii=0;ii<4;ii++)
+ {
+ tilemapWider[ii]=FALSE;
+ tilemapHigher[ii]=FALSE;
+ tilemapAdr[ii]=0;
+ tileAdr[ii]=0;
+ }
+
+ for (ii=0;ii<5;ii++)
+ {
+ bgHoff[ii]=0;
+ bgVoff[ii]=0;
+ }
+ offPrev1 = 0;
+ offPrev2 = 0;
+
+ mode = 0;
+ layer3Prio = FALSE;
+ for (ii=0;ii<4;ii++)
+ {
+ bigTiles[ii]=FALSE;
+ }
+
+ for (ii=0;ii<5;ii++)
+ {
+ mosaicEnabled[ii]=FALSE;
+ }
+ mosaicSize = 1;
+ mosaicStartLine = 1;
+
+ for (ii=0;ii<5;ii++)
+ {
+ mainScreenEnabled[ii]=FALSE;
+ subScreenEnabled[ii]=FALSE;
+ }
+
+ forcedBlank = TRUE;
+ brightness = 0;
+
+ oamAdr = 0;
+ oamRegAdr = 0;
+ oamInHigh = FALSE;
+ oamRegInHigh = FALSE;
+ objPriority = FALSE;
+ oamSecond = FALSE;
+ oamBuffer = FALSE;
+
+ sprAdr1 = 0;
+ sprAdr2 = 0;
+ objSize = 0;
+
+ rangeOver = FALSE;
+ timeOver = FALSE;
+
+ mode7ExBg = FALSE;
+ pseudoHires = FALSE;
+ overscan = FALSE;
+ objInterlace = FALSE;
+ interlace = FALSE;
+
+ frameOverscan = FALSE;
+ frameInterlace = FALSE;
+ evenFrame = FALSE;
+
+ latchedHpos = 0;
+ latchedVpos = 0;
+ latchHsecond = FALSE;
+ latchVsecond = FALSE;
+ countersLatched = FALSE;
+
+ mode7Hoff = 0;
+ mode7Voff = 0;
+ mode7A = 0;
+ mode7B = 0;
+ mode7C = 0;
+ mode7D = 0;
+ mode7X = 0;
+ mode7Y = 0;
+ mode7Prev = 0;
+ multResult = 0;
+
+ mode7LargeField = FALSE;
+ mode7Char0fill = FALSE;
+ mode7FlipX = FALSE;
+ mode7FlipY = FALSE;
+
+ for (ii=0;ii<6;ii++)
+ {
+ window1Inversed[ii]=FALSE;
+ window1Enabled[ii]=FALSE;
+ window2Inversed[ii]=FALSE;
+ window2Enabled[ii]=FALSE;
+ windowMaskLogic[ii]=0;
+ }
+
+ window1Left = 0;
+ window1Right = 0;
+ window2Left = 0;
+ window2Right = 0;
+
+ for (ii=0;ii<5;ii++)
+ {
+ mainScreenWindow[ii]=FALSE;
+ subScreenWindow[ii]=FALSE;
+ }
+
+ colorClip = 0;
+ preventMath = 0;
+ addSub = FALSE;
+ directColor = FALSE;
+
+ subtractColors = FALSE;
+ halfColors = FALSE;
+
+ for (ii=0;ii<6;ii++)
+ {
+ mathEnabled[ii]=FALSE;
+ }
+ fixedColorB = 0;
+ fixedColorG = 0;
+ fixedColorR = 0;
+
+ for (ii=0;ii<4;ii++)
+ {
+ tilemapBuffer[ii]=0;
+ tileBufferP1[ii]=0;
+ tileBufferP2[ii]=0;
+ tileBufferP3[ii]=0;
+ tileBufferP4[ii]=0;
+ lastTileFetchedX[ii]=-1;
+ lastTileFetchedY[ii]=-1;
+ }
+ optHorBuffer[0] = 0;
+ optHorBuffer[1] = 0;
+ optVerBuffer[0] = 0;
+ optVerBuffer[1] = 0;
+ lastOrigTileX[0] = -1;
+ lastOrigTileX[1] = -1;
+}
+
+ppu_reset;
+
+ // TODO: better mode 2/4/6 offset-per-tile (especially mode 6), color math
+ // when subscreen is visible (especially how to handle the subscreen pixels),
+ // mosaic with hires/interlace, mosaic on mode 7, rectangular sprites,
+ // oddities with sprite X-position being -256, mosaic with offset-per-tile,
+ // offset-per-tile with interlace
+
+ U0 setPixels(U8 *arr) {
+ // arr = 32bpp framebuffer
+ I32 i, x, y, ind, r, g, b, addY;
+
+ /*
+ if(!frameOverscan) {
+ // clear the top 8 and bottom 8 lines to transarent
+ for(i = 0; i < 512*16; i++) {
+ x = i % 512;
+ y = (i >> 9);
+ ind = (y * 512 + x) * 4;
+ arr[ind + 3] = 0;
+ }
+ for(i = 0; i < 512*16; i++) {
+ x = i % 512;
+ y = (i >> 9);
+ ind = ((y + 464) * 512 + x) * 4;
+ arr[ind + 3] = 0;
+ }
+ }
+ */
+
+ addY = cond(frameOverscan, 0, 14);
+
+ for(i = 512; i < 512 * cond(frameOverscan, 240, 225); i++) {
+ x = i % 512;
+ y = (i >> 9) * 2;
+ ind = ((y + addY) * 512 + x) * 4;
+ r = pixelOutput[i * 3];
+ g = pixelOutput[i * 3 + 1];
+ b = pixelOutput[i * 3 + 2];
+ if(!frameInterlace || evenFrame) {
+ arr[ind] = b;
+ arr[ind + 1] = g;
+ arr[ind + 2] = r;
+ arr[ind + 3] = 255;
+ }
+ ind += 512 * 4;
+ if(!frameInterlace || !evenFrame) {
+ arr[ind] = b;
+ arr[ind + 1] = g;
+ arr[ind + 2] = r;
+ arr[ind + 3] = 255;
+ }
+ }
+
+}
+
+U0 evaluateSprites(I32 line) {
+ I32 spriteCount, sliverCount, index, i, j, k, x, y, tile, ex, big, size, sprRow;
+ I32 adr, tileRow, tileColumn, tileNum, tileP1, tileP2, shift, tileData, color, xInd;
+ spriteCount = 0;
+ sliverCount = 0;
+ // search through oam, backwards
+ index = cond(objPriority, ((oamAdr & 0xfe) - 2) & 0xff, 254);
+ for(i = 0; i < 128; i++) {
+ x = oam[index] & 0xff;
+ y = (oam[index] & 0xff00) >> 8;
+ tile = oam[index + 1] & 0xff;
+ ex = (oam[index + 1] & 0xff00) >> 8;
+ x |= (highOam[index >> 4] >> (index & 0xf) & 0x1) << 8;
+ big = (highOam[index >> 4] >> (index & 0xf) & 0x2) > 0;
+ x = cond(x > 255, -(512 - x), x);
+
+ // check for being on this line
+ size = spriteSizes[objSize + (cond(big, 8, 0))];
+ sprRow = line - y;
+ if(sprRow < 0 || sprRow >= size * (cond(objInterlace, 4, 8))) {
+ // check if it is a sprite from the top of the screen
+ sprRow = line + (256 - y);
+ }
+ if(
+ sprRow >= 0 && sprRow < size * (cond(objInterlace, 4, 8)) &&
+ x > -(size * 8)
+ ) {
+ // in range, show it
+ if(spriteCount == 32) {
+ // this would be the 33th sprite, exit the loop
+ rangeOver = TRUE;
+ break;
+ }
+ sprRow = cond(objInterlace, sprRow * 2 + (
+ cond(evenFrame, 1, 0)
+ ), sprRow);
+ // fetch the tile(s)
+ adr = sprAdr1 + (cond((ex & 0x1) > 0, sprAdr2, 0));
+ sprRow = cond(((ex & 0x80) > 0), (size * 8) - 1 - sprRow, sprRow);
+ tileRow = sprRow >> 3;
+ sprRow &= 0x7;
+ for(k = 0; k < size; k++) {
+ if((x + k * 8) > -7 && (x + k * 8) < 256) {
+ if(sliverCount == 34) {
+ sliverCount = 35;
+ break; // exit tile fetch loop, maximum slivers
+ }
+ tileColumn = cond(((ex & 0x40) > 0), size - 1 - k, k);
+ tileNum = tile + spriteTileOffsets[
+ tileRow * 8 + tileColumn
+ ];
+ tileNum &= 0xff;
+ tileP1 = vram[
+ (adr + tileNum * 16 + sprRow) & 0x7fff
+ ];
+ tileP2 = vram[
+ (adr + tileNum * 16 + sprRow + 8) & 0x7fff
+ ];
+ // and draw it in the line buffer
+ for(j = 0; j < 8; j++) {
+ shift = cond(((ex & 0x40) > 0), j, 7 - j);
+ tileData = (tileP1 >> shift) & 0x1;
+ tileData |= ((tileP1 >> (8 + shift)) & 0x1) << 1;
+ tileData |= ((tileP2 >> shift) & 0x1) << 2;
+ tileData |= ((tileP2 >> (8 + shift)) & 0x1) << 3;
+ color = tileData + 16 * ((ex & 0xe) >> 1);
+ xInd = x + k * 8 + j;
+ if(tileData > 0 && xInd < 256 && xInd >= 0) {
+ spriteLineBuffer[xInd] = 0x80 + color;
+ spritePrioBuffer[xInd] = (ex & 0x30) >> 4;
+ }
+ }
+ sliverCount++;
+ }
+ }
+ if(sliverCount == 35) {
+ // we exited the tile fetch loop because we reached max slivers
+ // se we can stop evaluating sprites
+ timeOver = TRUE;
+ break;
+ }
+
+ spriteCount++;
+ }
+
+ index = (index - 2) & 0xff;
+ }
+ }
+
+U0 generateMode7Coords(I32 y) {
+ I32 i, rY, clippedH, clippedV, lineStartX, lineStartY;
+ rY = cond(mode7FlipY, 255 - y, y);
+
+ clippedH = mode7Hoff - mode7X;
+ clippedH = cond((clippedH & 0x2000) > 0, (clippedH | ~0x3ff), (clippedH & 0x3ff));
+ clippedV = mode7Voff - mode7Y;
+ clippedV = cond((clippedV & 0x2000) > 0, (clippedV | ~0x3ff), (clippedV & 0x3ff));
+
+ lineStartX = (
+ ((mode7A * clippedH) & ~63) +
+ ((mode7B * rY) & ~63) + ((mode7B * clippedV) & ~63) +
+ (mode7X << 8)
+ );
+ lineStartY = (
+ ((mode7C * clippedH) & ~63) +
+ ((mode7D * rY) & ~63) + ((mode7D * clippedV) & ~63) +
+ (mode7Y << 8)
+ );
+
+ mode7Xcoords[0] = lineStartX;
+ mode7Ycoords[0] = lineStartY;
+
+ for(i = 1; i < 256; i++) {
+ mode7Xcoords[i] = mode7Xcoords[i - 1] + mode7A;
+ mode7Ycoords[i] = mode7Ycoords[i - 1] + mode7C;
+ }
+ }
+
+Bool getWindowState(I32 x, I32 l) {
+ Bool test, w1test, w2test;
+ if(!window1Enabled[l] && !window2Enabled[l]) {
+ return FALSE;
+ }
+ if(window1Enabled[l] && !window2Enabled[l]) {
+ test = x >= window1Left && x <= window1Right;
+ return cond(window1Inversed[l], !test, test);
+ }
+ if(!window1Enabled[l] && window2Enabled[l]) {
+ test = x >= window2Left && x <= window2Right;
+ return cond(window2Inversed[l], !test, test);
+ }
+ // both window enabled
+ w1test = x >= window1Left && x <= window1Right;
+ w1test = cond(window1Inversed[l], !w1test, w1test);
+ w2test = x >= window2Left && x <= window2Right;
+ w2test = cond(window2Inversed[l], !w2test, w2test);
+ switch(windowMaskLogic[l]) {
+ case 0: {
+ return w1test || w2test;
+ }
+ case 1: {
+ return w1test && w2test;
+ }
+ case 2: {
+ return w1test != w2test;
+ }
+ case 3: {
+ return w1test == w2test;
+ }
+ }
+}
+
+Bool getMathEnabled(I32 x, I32 l, I32 pal) {
+ if(
+ preventMath == 3 ||
+ (preventMath == 2 && getWindowState(x, 5)) ||
+ (preventMath == 1 && !getWindowState(x, 5))
+ ) {
+ return FALSE;
+ }
+ if(mathEnabled[l] && (l != 4 || pal >= 0xc0)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+U0 fetchTileInBuffer(I32 x, I32 y, I32 l, I32 offset) {
+ I32 rx, ry, adr, yFlip, xFlip, yRow, tileNum, bits;
+ Bool useXbig;
+ rx = x;
+ ry = y;
+ useXbig = bigTiles[l] | mode == 5 | mode == 6;
+ x >>= cond(useXbig, 1, 0);
+ y >>= cond(bigTiles[l], 1, 0);
+
+ adr = tilemapAdr[l] + (
+ ((y & 0xff) >> 3) << 5 | ((x & 0xff) >> 3)
+ );
+ adr += cond(((x & 0x100) > 0 && tilemapWider[l]), 1024, 0);
+ adr += cond(((y & 0x100) > 0 && tilemapHigher[l]), (
+ cond(tilemapWider[l], 2048, 1024)
+ ), 0);
+ tilemapBuffer[l] = vram[adr & 0x7fff];
+ if(offset) {
+ // for offset-per-tile, we only nees the tilemap byte,
+ // don't fetch the tiles themselves
+ return;
+ }
+ yFlip = (tilemapBuffer[l] & 0x8000) > 0;
+ xFlip = (tilemapBuffer[l] & 0x4000) > 0;
+ yRow = cond(yFlip, 7 - (ry & 0x7), (ry & 0x7));
+ tileNum = tilemapBuffer[l] & 0x3ff;
+
+ tileNum += cond(useXbig && (rx & 0x8) == cond(xFlip, 0, 8), 1, 0);
+ tileNum += cond(bigTiles[l] && (ry & 0x8) == cond(yFlip, 0, 8), 0x10, 0);
+
+ bits = bitPerMode[mode * 4 + l];
+
+ tileBufferP1[l] = vram[
+ (tileAdr[l] + tileNum * 4 * bits + yRow) & 0x7fff
+ ];
+ if(bits > 2) {
+ tileBufferP2[l] = vram[
+ (tileAdr[l] + tileNum * 4 * bits + yRow + 8) & 0x7fff
+ ];
+ }
+ if(bits > 4) {
+ tileBufferP3[l] = vram[
+ (tileAdr[l] + tileNum * 4 * bits + yRow + 16) & 0x7fff
+ ];
+ tileBufferP4[l] = vram[
+ (tileAdr[l] + tileNum * 4 * bits + yRow + 24) & 0x7fff
+ ];
+ }
+}
+
+I32 getMode7Pixel(I32 x, I32 y, I32 l, I32 p) {
+ I32 pixelData, rX, px, py, tileX, tileY, tileByte;
+ Bool pixelIsTransparent;
+ pixelData = tilemapBuffer[0];
+ if(x != lastTileFetchedX[0] || y != lastTileFetchedY[0]) {
+ rX = cond(mode7FlipX, 255 - x, x);
+
+ px = mode7Xcoords[rX] >> 8;
+ py = mode7Ycoords[rX] >> 8;
+
+ pixelIsTransparent = FALSE;
+
+ if(mode7LargeField && (px < 0 || px >= 1024 || py < 0 || py >= 1024)) {
+ if(mode7Char0fill) {
+ // always use tile 0
+ px &= 0x7;
+ py &= 0x7;
+ } else {
+ // act as transparent
+ pixelIsTransparent = TRUE;
+ }
+ }
+ // fetch the right tilemap byte
+ tileX = (px & 0x3f8) >> 3;
+ tileY = (py & 0x3f8) >> 3;
+
+ tileByte = vram[(tileY * 128 + tileX)] & 0xff;
+ // fetch the tile
+ pixelData = vram[tileByte * 64 + (py & 0x7) * 8 + (px & 0x7)];
+ pixelData >>= 8;
+ pixelData = cond(pixelIsTransparent, 0, pixelData);
+ tilemapBuffer[0] = pixelData;
+ lastTileFetchedX[0] = x;
+ lastTileFetchedY[0] = y;
+ }
+
+ if(l == 1 && (pixelData >> 7) != p) {
+ // wrong priority
+ return 0;
+ } else if(l == 1) {
+ return pixelData & 0x7f;
+ }
+
+ return pixelData;
+ }
+
+
+
+ I32 getPixelForLayer(I32 x, I32 y, I32 l, I32 p) {
+ I32 mapWord, paletteNum, xShift, bits, mul, tileData;
+ if(l > 3) {
+ if(spritePrioBuffer[x] != p) {
+ return 0;
+ }
+ return spriteLineBuffer[x];
+ }
+
+ if(mode == 7) {
+ return getMode7Pixel(x, y, l, p);
+ }
+
+ if(
+ (x >> 3) != lastTileFetchedX[l] ||
+ y != lastTileFetchedY[l]
+ ) {
+ fetchTileInBuffer(x, y, l, FALSE);
+ lastTileFetchedX[l] = (x >> 3);
+ lastTileFetchedY[l] = y;
+ }
+
+ mapWord = tilemapBuffer[l];
+ if(((mapWord & 0x2000) >> 13) != p) {
+ // not the right priority
+ return 0;
+ }
+ paletteNum = (mapWord & 0x1c00) >> 10;
+ xShift = cond((mapWord & 0x4000) > 0, (x & 0x7), 7 - (x & 0x7));
+
+ paletteNum += cond(mode == 0, l * 8, 0);
+
+ bits = bitPerMode[mode * 4 + l];
+ mul = 4;
+ tileData = (tileBufferP1[l] >> xShift) & 0x1;
+ tileData |= ((tileBufferP1[l] >> (8 + xShift)) & 0x1) << 1;
+
+ if(bits > 2) {
+ mul = 16;
+ tileData |= ((tileBufferP2[l] >> xShift) & 0x1) << 2;
+ tileData |= ((tileBufferP2[l] >> (8 + xShift)) & 0x1) << 3;
+ }
+
+ if(bits > 4) {
+ mul = 256;
+ tileData |= ((tileBufferP3[l] >> xShift) & 0x1) << 4;
+ tileData |= ((tileBufferP3[l] >> (8 + xShift)) & 0x1) << 5;
+ tileData |= ((tileBufferP4[l] >> xShift) & 0x1) << 6;
+ tileData |= ((tileBufferP4[l] >> (8 + xShift)) & 0x1) << 7;
+ }
+
+ return cond(tileData > 0, (paletteNum * mul + tileData), 0);
+}
+
+U0 getColor(Bool sub, I32 x, I32 y, I32* lay) {
+ I32 modeIndex, count, j, pixel, layer, lx, ly, optX, andVal, tileStartX, add, color, r, g, b;
+
+ modeIndex = cond(layer3Prio && mode == 1, 96, 12 * mode);
+ modeIndex = cond(mode7ExBg && mode == 7, 108, modeIndex);
+ count = layercountPerMode[mode];
+
+ pixel = 0;
+ layer = 5;
+ if(interlace && (mode == 5 || mode == 6)) {
+ y = y * 2 + cond(evenFrame, 1, 0);
+ }
+ for(j = 0; j < count; j++) {
+ lx = x;
+ ly = y;
+ layer = layersPerMode[modeIndex + j];
+ if(
+ (
+ !sub && mainScreenEnabled[layer] &&
+ (!mainScreenWindow[layer] || !getWindowState(lx, layer))
+ ) || (
+ sub && subScreenEnabled[layer] &&
+ (!subScreenWindow[layer] || !getWindowState(lx, layer))
+ )
+ ) {
+ if(mosaicEnabled[layer]) {
+ lx -= lx % mosaicSize;
+ ly -= (ly - mosaicStartLine) % mosaicSize;
+ }
+ lx += cond(mode == 7, 0, bgHoff[layer]);
+ ly += cond(mode == 7, 0, bgVoff[layer]);
+ optX = lx - bgHoff[layer];
+ if((mode == 5 || mode == 6) && layer < 4) {
+ lx = lx * 2 + cond(sub, 0, 1);
+ optX = optX * 2 + cond(sub, 0, 1);
+ }
+
+ // origLx = lx;
+
+ if((mode == 2 || mode == 4 || mode == 6) && layer < 2) {
+ andVal = cond(layer == 0, 0x2000, 0x4000);
+ if(x == 0) {
+ lastOrigTileX[layer] = lx >> 3;
+ }
+ // where the relevant tile started
+ // TODO: lx can be above 0xffff (e.g. if scroll is 0xffff, and x > 0)
+ tileStartX = optX - (lx - (lx & 0xfff8));
+ if((lx >> 3) != lastOrigTileX[layer] && x > 0) {
+ // we are fetching a new tile for the layer, get a new OPT-tile
+ // if(logging && y == 32 && (mode == 2 || mode == 4 || mode == 6) && layer == 0) {
+ // log("at X = " + x + ", lx: " + getWordRep(lx) + ", fetched new tile for OPT");
+ // }
+ fetchTileInBuffer(
+ bgHoff[2] + ((tileStartX - 1) & 0x1f8),
+ bgVoff[2], 2, TRUE
+ );
+ optHorBuffer[layer] = tilemapBuffer[2];
+ if(mode == 4) {
+ if((optHorBuffer[layer] & 0x8000) > 0) {
+ optVerBuffer[layer] = optHorBuffer[layer];
+ optHorBuffer[layer] = 0;
+ } else {
+ optVerBuffer[layer] = 0;
+ }
+ } else {
+ fetchTileInBuffer(
+ bgHoff[2] + ((tileStartX - 1) & 0x1f8),
+ bgVoff[2] + 8, 2, TRUE
+ );
+ optVerBuffer[layer] = tilemapBuffer[2];
+ }
+ lastOrigTileX[layer] = lx >> 3;
+ }
+ if((optHorBuffer[layer] & andVal) > 0) {
+ //origLx = lx;
+ add = ((tileStartX + 7) & 0x1f8);
+ lx = (lx & 0x7) + ((optHorBuffer[layer] + add) & 0x1ff8);
+ }
+ if((optVerBuffer[layer] & andVal) > 0) {
+ ly = (optVerBuffer[layer] & 0x1fff) + (ly - bgVoff[layer]);
+ }
+ }
+ // if(logging && y == 32 && (mode == 2 || mode == 4 || mode == 6) && layer == 0) {
+ // log("at X = " + x + ", lx: " + getWordRep(lx) + ", ly: " + getWordRep(ly) + ", optHB: " + getWordRep(optHorBuffer[layer]) + ", orig lx: " + getWordRep(origLx));
+ // }
+
+ pixel = getPixelForLayer(
+ lx, ly,
+ layer,
+ prioPerMode[modeIndex + j]
+ );
+ }
+ if((pixel & 0xff) > 0) {
+ break;
+ }
+ }
+ layer = cond(j == count, 5, layer);
+ color = cgram[pixel & 0xff];
+ if(
+ directColor && layer < 4 &&
+ bitPerMode[mode * 4 + layer] == 8
+ ) {
+ r = ((pixel & 0x7) << 2) | ((pixel & 0x100) >> 7);
+ g = ((pixel & 0x38) >> 1) | ((pixel & 0x200) >> 8);
+ b = ((pixel & 0xc0) >> 3) | ((pixel & 0x400) >> 8);
+ color = (b << 10) | (g << 5) | r;
+ }
+
+ lay[0] = color;
+ lay[1] = layer;
+ lay[2] = pixel;
+}
+
+ U0 renderLine(I32 line) {
+ I32 r1, g1, b1, r2, g2, b2, i, bMult, color;
+ I32 colLay[3];
+ I32 secondLay[3];
+ if(line == 225 && overscan) {
+ frameOverscan = TRUE;
+ }
+ if(line == 0) {
+ // pre-render line
+ rangeOver = FALSE;
+ timeOver = FALSE;
+ frameOverscan = FALSE;
+ frameInterlace = FALSE;
+ MemSet(spriteLineBuffer, NULL, 256);
+ if(!forcedBlank) {
+ evaluateSprites(0);
+ }
+ } else if(line == (cond(frameOverscan,240, 225))) {
+ // beginning of Vblank
+ if(!forcedBlank) {
+ oamAdr = oamRegAdr;
+ oamInHigh = oamRegInHigh;
+ }
+ frameInterlace = interlace;
+ evenFrame = !evenFrame;
+ } else if(line > 0 && line < (cond(frameOverscan, 240, 225))) {
+ // visible line
+ if(line == 1) {
+ mosaicStartLine = 1;
+ }
+ if(mode == 7) {
+ generateMode7Coords(line);
+ }
+
+ lastTileFetchedX[0] = -1;
+ lastTileFetchedX[1] = -1;
+ lastTileFetchedX[2] = -1;
+ lastTileFetchedX[3] = -1;
+ lastTileFetchedY[0] = -1;
+ lastTileFetchedY[1] = -1;
+ lastTileFetchedY[2] = -1;
+ lastTileFetchedY[3] = -1;
+ optHorBuffer[0] = 0;
+ optHorBuffer[1] = 0;
+ optVerBuffer[0] = 0;
+ optVerBuffer[1] = 0;
+ lastOrigTileX[0] = -1;
+ lastOrigTileX[1] = -1;
+ bMult = brightnessMults[brightness];
+ i = 0;
+ while(i < 256) {
+ // for each pixel
+
+ r1 = 0;
+ g1 = 0;
+ b1 = 0;
+ r2 = 0;
+ g2 = 0;
+ b2 = 0;
+
+ if(!forcedBlank) {
+
+ getColor(FALSE, i, line, &colLay);
+ color = colLay[0];
+
+ r2 = color & 0x1f;
+ g2 = (color & 0x3e0) >> 5;
+ b2 = (color & 0x7c00) >> 10;
+
+ // TODO: docs day that this clips before math, but it seems to simply
+ // always clip the pixels to black?
+ if(
+ colorClip == 3 ||
+ (colorClip == 2 && getWindowState(i, 5)) ||
+ (colorClip == 1 && !getWindowState(i, 5))
+ ) {
+ r2 = 0;
+ g2 = 0;
+ b2 = 0;
+ }
+
+ secondLay[0] = 0;
+ secondLay[1] = 5;
+ secondLay[2] = 0;
+ if(
+ mode == 5 || mode == 6 || pseudoHires ||
+ (getMathEnabled(i, colLay[1], colLay[2]) && addSub)
+ ) {
+ getColor(TRUE, i, line, &secondLay);
+ r1 = secondLay[0] & 0x1f;
+ g1 = (secondLay[0] & 0x3e0) >> 5;
+ b1 = (secondLay[0] & 0x7c00) >> 10;
+ }
+
+ if(getMathEnabled(i, colLay[1], colLay[2])) {
+ if(subtractColors) {
+ r2 -= cond((addSub && secondLay[1] < 5), r1, fixedColorR);
+ g2 -= cond((addSub && secondLay[1] < 5), g1, fixedColorG);
+ b2 -= cond((addSub && secondLay[1] < 5), b1, fixedColorB);
+ } else {
+ r2 += cond((addSub && secondLay[1] < 5), r1, fixedColorR);
+ g2 += cond((addSub && secondLay[1] < 5), g1, fixedColorG);
+ b2 += cond((addSub && secondLay[1] < 5), b1, fixedColorB);
+ }
+ // TODO: docs say that halfing should not happen if adding the
+ // direct color, but that makes some effects in the SNES character
+ // test look wrong
+ if(halfColors && (secondLay[1] < 5 || !addSub)) {
+ r2 >>= 1;
+ g2 >>= 1;
+ b2 >>= 1;
+ }
+ r2 = cond(r2 > 31, 31, r2);
+ r2 = cond(r2 < 0, 0, r2);
+ g2 = cond(g2 > 31, 31, g2);
+ g2 = cond(g2 < 0, 0, g2);
+ b2 = cond(b2 > 31, 31, b2);
+ b2 = cond(b2 < 0, 0, b2);
+ }
+
+ if(!(mode == 5 || mode == 6 || pseudoHires)) {
+ r1 = r2;
+ g1 = g2;
+ b1 = b2;
+ }
+
+ }
+ pixelOutput[line * 1536 + 6 * i] = (r1 * bMult) & 0xff;
+ pixelOutput[line * 1536 + 6 * i + 1] = (g1 * bMult) & 0xff;
+ pixelOutput[line * 1536 + 6 * i + 2] = (b1 * bMult) & 0xff;
+ pixelOutput[line * 1536 + 6 * i + 3] = (r2 * bMult) & 0xff;
+ pixelOutput[line * 1536 + 6 * i + 4] = (g2 * bMult) & 0xff;
+ pixelOutput[line * 1536 + 6 * i + 5] = (b2 * bMult) & 0xff;
+
+ i++;
+
+ }
+ MemSet(spriteLineBuffer, NULL, 256);
+ if(!forcedBlank) {
+ evaluateSprites(line);
+ }
+ }
+ }
+
+ I32 getVramRemap() {
+ I32 adr;
+ adr = vramAdr & 0x7fff;
+ if(vramRemap == 1) {
+ adr = (adr & 0xff00) | ((adr & 0xe0) >> 5) | ((adr & 0x1f) << 3);
+ } else if(vramRemap == 2) {
+ adr = (adr & 0xfe00) | ((adr & 0x1c0) >> 6) | ((adr & 0x3f) << 3);
+ } else if(vramRemap == 3) {
+ adr = (adr & 0xfc00) | ((adr & 0x380) >> 7) | ((adr & 0x7f) << 3);
+ }
+ return adr;
+ }
+
+I32 get13Signed(I32 val) {
+ if((val & 0x1000) > 0) {
+ return -(8192 - (val & 0xfff));
+ }
+ return (val & 0xfff);
+ }
+
+I32 get16Signed(I32 val) {
+ if((val & 0x8000) > 0) {
+ return -(65536 - val);
+ }
+ return val;
+ }
+
+I32 getMultResult(I32 a, I32 b) {
+ b = cond(b < 0, 65536 + b, b);
+ b >>= 8;
+ b = cond(((b & 0x80) > 0), -(256 - b), b);
+ I32 _ans = a * b;
+ if(_ans < 0) {
+ return 16777216 + _ans;
+ }
+ return _ans;
+ }
+
+I32 ppu_read(I32 adr) {
+ I32 val;
+ switch(adr) {
+ case 0x34: {
+ return multResult & 0xff;
+ }
+ case 0x35: {
+ return (multResult & 0xff00) >> 8;
+ }
+ case 0x36: {
+ return (multResult & 0xff0000) >> 16;
+ }
+ case 0x37: {
+ // TODO: docs say this should only happen if bit 7 of the IO port is
+ // set, but always doing it makes The Legend of Zelda: A Link to the
+ // Past work
+ //if(ppuLatch) {
+ latchedHpos = xPos >> 2;
+ latchedVpos = yPos;
+ //}
+ countersLatched = TRUE;
+ return openBus;
+ }
+ case 0x38: {
+ if(!oamSecond) {
+ if(oamInHigh) {
+ val = highOam[oamAdr & 0xf] & 0xff;
+ } else {
+ val = oam[oamAdr] & 0xff;
+ }
+ oamSecond = TRUE;
+ } else {
+ if(oamInHigh) {
+ val = highOam[oamAdr & 0xf] >> 8;
+ } else {
+ val = oam[oamAdr] >> 8;
+ }
+ oamAdr++;
+ oamAdr &= 0xff;
+ oamInHigh = cond((
+ oamAdr == 0
+ ), !oamInHigh, oamInHigh);
+ oamSecond = FALSE;
+ }
+ return val;
+ }
+ case 0x39: {
+ val = vramReadBuffer;
+ vramReadBuffer = vram[getVramRemap()];
+ if(!vramIncOnHigh) {
+ vramAdr += vramInc;
+ vramAdr &= 0xffff;
+ }
+ return val & 0xff;
+ }
+ case 0x3a: {
+ val = vramReadBuffer;
+ vramReadBuffer = vram[getVramRemap()];
+ if(vramIncOnHigh) {
+ vramAdr += vramInc;
+ vramAdr &= 0xffff;
+ }
+ return (val & 0xff00) >> 8;
+ }
+ case 0x3b: {
+
+ if(!cgramSecond) {
+ val = cgram[cgramAdr] & 0xff;
+ cgramSecond = TRUE;
+ } else {
+ val = cgram[cgramAdr++] >> 8;
+ cgramAdr &= 0xff;
+ cgramSecond = FALSE;
+ }
+ return val;
+ }
+ case 0x3c: {
+
+ if(!latchHsecond) {
+ val = latchedHpos & 0xff;
+ latchHsecond = TRUE;
+ } else {
+ val = (latchedHpos & 0xff00) >> 8;
+ latchHsecond = FALSE;
+ }
+ return val;
+ }
+ case 0x3d: {
+
+ if(!latchVsecond) {
+ val = latchedVpos & 0xff;
+ latchVsecond = TRUE;
+ } else {
+ val = (latchedVpos & 0xff00) >> 8;
+ latchVsecond = FALSE;
+ }
+ return val;
+ }
+ case 0x3e: {
+ val = cond(timeOver, 0x80, 0);
+ val |= cond(rangeOver, 0x40, 0);
+ return val | 0x1;
+ }
+ case 0x3f: {
+ val = cond(evenFrame, 0x80, 0);
+ val |= cond(countersLatched, 0x40, 0);
+ if(ppuLatch) {
+ countersLatched = FALSE;
+ }
+ latchHsecond = FALSE;
+ latchVsecond = FALSE;
+ /* PAL: val |= 0x10; */
+ return val | 0x2;
+ }
+ }
+ return openBus;
+}
+
+U0 ppu_write(I32 adr, I32 value) {
+ I32 incVal, _adr;
+ switch(adr) {
+ case 0x00: {
+ forcedBlank = (value & 0x80) > 0;
+ brightness = value & 0xf;
+ return;
+ }
+ case 0x01: {
+ sprAdr1 = (value & 0x7) << 13;
+ sprAdr2 = ((value & 0x18) + 8) << 9;
+ objSize = (value & 0xe0) >> 5;
+ return;
+ }
+ case 0x02: {
+ oamAdr = value;
+ oamRegAdr = oamAdr;
+ oamInHigh = oamRegInHigh;
+ oamSecond = FALSE;
+ return;
+ }
+ case 0x03: {
+ oamInHigh = (value & 0x1) > 0;
+ objPriority = (value & 0x80) > 0;
+ oamAdr = oamRegAdr;
+ oamRegInHigh = oamInHigh;
+ oamSecond = FALSE;
+ return;
+ }
+ case 0x04: {
+ if(!oamSecond) {
+ if(oamInHigh) {
+ highOam[
+ oamAdr & 0xf
+ ] = (highOam[oamAdr & 0xf] & 0xff00) | value;
+ } else {
+ oamBuffer = (oamBuffer & 0xff00) | value;
+ }
+ oamSecond = TRUE;
+ } else {
+ if(oamInHigh) {
+ highOam[
+ oamAdr & 0xf
+ ] = (highOam[oamAdr & 0xf] & 0xff) | (value << 8);
+ } else {
+ oamBuffer = (oamBuffer & 0xff) | (value << 8);
+ oam[oamAdr] = oamBuffer;
+ }
+ oamAdr++;
+ oamAdr &= 0xff;
+ oamInHigh = cond((
+ oamAdr == 0
+ ), !oamInHigh, oamInHigh);
+ oamSecond = FALSE;
+ }
+ return;
+ }
+ case 0x05: {
+ mode = value & 0x7;
+ layer3Prio = (value & 0x08) > 0;
+ bigTiles[0] = (value & 0x10) > 0;
+ bigTiles[1] = (value & 0x20) > 0;
+ bigTiles[2] = (value & 0x40) > 0;
+ bigTiles[3] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x06: {
+ mosaicEnabled[0] = (value & 0x1) > 0;
+ mosaicEnabled[1] = (value & 0x2) > 0;
+ mosaicEnabled[2] = (value & 0x4) > 0;
+ mosaicEnabled[3] = (value & 0x8) > 0;
+ mosaicSize = ((value & 0xf0) >> 4) + 1;
+ mosaicStartLine = yPos;
+ return;
+ }
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a: {
+ tilemapWider[adr - 7] = (value & 0x1) > 0;
+ tilemapHigher[adr - 7] = (value & 0x2) > 0;
+ tilemapAdr[adr - 7] = (value & 0xfc) << 8;
+ return;
+ }
+ case 0x0b: {
+ tileAdr[0] = (value & 0xf) << 12;
+ tileAdr[1] = (value & 0xf0) << 8;
+ return;
+ }
+ case 0x0c: {
+ tileAdr[2] = (value & 0xf) << 12;
+ tileAdr[3] = (value & 0xf0) << 8;
+ return;
+ }
+ case 0x0d: {
+ mode7Hoff = get13Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ // fall through to also set normal layer bgHoff
+ }
+ case 0x0f:
+ case 0x11:
+ case 0x13: {
+ bgHoff[
+ (adr - 0xd) >> 1
+ ] = (value << 8) | (offPrev1 & 0xf8) | (offPrev2 & 0x7);
+ offPrev1 = value;
+ offPrev2 = value;
+ return;
+ }
+ case 0x0e: {
+ mode7Voff = get13Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ // fall through to also set normal layer bgVoff
+ }
+ case 0x10:
+ case 0x12:
+ case 0x14: {
+ bgVoff[
+ (adr - 0xe) >> 1
+ ] = (value << 8) | (offPrev1 & 0xff);
+ offPrev1 = value;
+ return;
+ }
+ case 0x15: {
+ incVal = value & 0x3;
+ if(incVal == 0) {
+ vramInc = 1;
+ } else if(incVal == 1) {
+ vramInc = 32;
+ } else {
+ vramInc = 128;
+ }
+ vramRemap = (value & 0xc0) >> 2;
+ vramIncOnHigh = (value & 0x80) > 0;
+ return;
+ }
+ case 0x16: {
+ vramAdr = (vramAdr & 0xff00) | value;
+ vramReadBuffer = vram[getVramRemap()];
+ return;
+ }
+ case 0x17: {
+ vramAdr = (vramAdr & 0xff) | (value << 8);
+ vramReadBuffer = vram[getVramRemap()];
+ return;
+ }
+ case 0x18: {
+ _adr = getVramRemap();
+ vram[_adr] = (vram[_adr] & 0xff00) | value;
+ if(!vramIncOnHigh) {
+ vramAdr += vramInc;
+ vramAdr &= 0xffff;
+ }
+ return;
+ }
+ case 0x19: {
+ _adr = getVramRemap();
+ vram[_adr] = (vram[_adr] & 0xff) | (value << 8);
+ if(vramIncOnHigh) {
+ vramAdr += vramInc;
+ vramAdr &= 0xffff;
+ }
+ return;
+ }
+ case 0x1a: {
+ mode7LargeField = (value & 0x80) > 0;
+ mode7Char0fill = (value & 0x40) > 0;
+ mode7FlipY = (value & 0x2) > 0;
+ mode7FlipX = (value & 0x1) > 0;
+ return;
+ }
+ case 0x1b: {
+ mode7A = get16Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ multResult = getMultResult(mode7A, mode7B);
+ return;
+ }
+ case 0x1c: {
+ mode7B = get16Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ multResult = getMultResult(mode7A, mode7B);
+ return;
+ }
+ case 0x1d: {
+ mode7C = get16Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ return;
+ }
+ case 0x1e: {
+ mode7D = get16Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ return;
+ }
+ case 0x1f: {
+ mode7X = get13Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ return;
+ }
+ case 0x20: {
+ mode7Y = get13Signed((value << 8) | mode7Prev);
+ mode7Prev = value;
+ return;
+ }
+ case 0x21: {
+ cgramAdr = value;
+ cgramSecond = FALSE;
+ return;
+ }
+ case 0x22: {
+ if(!cgramSecond) {
+ cgramBuffer = (cgramBuffer & 0xff00) | value;
+ cgramSecond = TRUE;
+ } else {
+ cgramBuffer = (cgramBuffer & 0xff) | (value << 8);
+ cgram[cgramAdr++] = cgramBuffer;
+ cgramAdr &= 0xff;
+ cgramSecond = FALSE;
+ }
+ return;
+ }
+ case 0x23: {
+ window1Inversed[0] = (value & 0x01) > 0;
+ window1Enabled[0] = (value & 0x02) > 0;
+ window2Inversed[0] = (value & 0x04) > 0;
+ window2Enabled[0] = (value & 0x08) > 0;
+ window1Inversed[1] = (value & 0x10) > 0;
+ window1Enabled[1] = (value & 0x20) > 0;
+ window2Inversed[1] = (value & 0x40) > 0;
+ window2Enabled[1] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x24: {
+ window1Inversed[2] = (value & 0x01) > 0;
+ window1Enabled[2] = (value & 0x02) > 0;
+ window2Inversed[2] = (value & 0x04) > 0;
+ window2Enabled[2] = (value & 0x08) > 0;
+ window1Inversed[3] = (value & 0x10) > 0;
+ window1Enabled[3] = (value & 0x20) > 0;
+ window2Inversed[3] = (value & 0x40) > 0;
+ window2Enabled[3] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x25: {
+ window1Inversed[4] = (value & 0x01) > 0;
+ window1Enabled[4] = (value & 0x02) > 0;
+ window2Inversed[4] = (value & 0x04) > 0;
+ window2Enabled[4] = (value & 0x08) > 0;
+ window1Inversed[5] = (value & 0x10) > 0;
+ window1Enabled[5] = (value & 0x20) > 0;
+ window2Inversed[5] = (value & 0x40) > 0;
+ window2Enabled[5] = (value & 0x80) > 0;
+ return;
+ }
+ case 0x26: {
+ window1Left = value;
+ return;
+ }
+ case 0x27: {
+ window1Right = value;
+ return;
+ }
+ case 0x28: {
+ window2Left = value;
+ return;
+ }
+ case 0x29: {
+ window2Right = value;
+ return;
+ }
+ case 0x2a: {
+ windowMaskLogic[0] = value & 0x3;
+ windowMaskLogic[1] = (value & 0xc) >> 2;
+ windowMaskLogic[2] = (value & 0x30) >> 4;
+ windowMaskLogic[3] = (value & 0xc0) >> 6;
+ return;
+ }
+ case 0x2b: {
+ windowMaskLogic[4] = value & 0x3;
+ windowMaskLogic[5] = (value & 0xc) >> 2;
+ return;
+ }
+ case 0x2c: {
+ mainScreenEnabled[0] = (value & 0x1) > 0;
+ mainScreenEnabled[1] = (value & 0x2) > 0;
+ mainScreenEnabled[2] = (value & 0x4) > 0;
+ mainScreenEnabled[3] = (value & 0x8) > 0;
+ mainScreenEnabled[4] = (value & 0x10) > 0;
+ return;
+ }
+ case 0x2d: {
+ subScreenEnabled[0] = (value & 0x1) > 0;
+ subScreenEnabled[1] = (value & 0x2) > 0;
+ subScreenEnabled[2] = (value & 0x4) > 0;
+ subScreenEnabled[3] = (value & 0x8) > 0;
+ subScreenEnabled[4] = (value & 0x10) > 0;
+ return;
+ }
+ case 0x2e: {
+ mainScreenWindow[0] = (value & 0x1) > 0;
+ mainScreenWindow[1] = (value & 0x2) > 0;
+ mainScreenWindow[2] = (value & 0x4) > 0;
+ mainScreenWindow[3] = (value & 0x8) > 0;
+ mainScreenWindow[4] = (value & 0x10) > 0;
+ return;
+ }
+ case 0x2f: {
+ subScreenWindow[0] = (value & 0x1) > 0;
+ subScreenWindow[1] = (value & 0x2) > 0;
+ subScreenWindow[2] = (value & 0x4) > 0;
+ subScreenWindow[3] = (value & 0x8) > 0;
+ subScreenWindow[4] = (value & 0x10) > 0;
+ return;
+ }
+ case 0x30: {
+ colorClip = (value & 0xc0) >> 6;
+ preventMath = (value & 0x30) >> 4;
+ addSub = (value & 0x2) > 0;
+ directColor = (value & 0x1) > 0;
+ return;
+ }
+ case 0x31: {
+ subtractColors = (value & 0x80) > 0;
+ halfColors = (value & 0x40) > 0;
+ mathEnabled[0] = (value & 0x1) > 0;
+ mathEnabled[1] = (value & 0x2) > 0;
+ mathEnabled[2] = (value & 0x4) > 0;
+ mathEnabled[3] = (value & 0x8) > 0;
+ mathEnabled[4] = (value & 0x10) > 0;
+ mathEnabled[5] = (value & 0x20) > 0;
+ return;
+ }
+ case 0x32: {
+ if((value & 0x80) > 0) {
+ fixedColorB = value & 0x1f;
+ }
+ if((value & 0x40) > 0) {
+ fixedColorG = value & 0x1f;
+ }
+ if((value & 0x20) > 0) {
+ fixedColorR = value & 0x1f;
+ }
+ return;
+ }
+ case 0x33: {
+ mode7ExBg = (value & 0x40) > 0;
+ pseudoHires = (value & 0x08) > 0;
+ overscan = (value & 0x04) > 0;
+ objInterlace = (value & 0x02) > 0;
+ interlace = (value & 0x01) > 0;
+ return;
+ }
+ }
+ } \ No newline at end of file
diff --git a/Run.HC b/Run.HC
new file mode 100644
index 0000000..18109b3
--- /dev/null
+++ b/Run.HC
@@ -0,0 +1 @@
+#include "Load"; \ No newline at end of file
diff --git a/Snes.HC b/Snes.HC
new file mode 100644
index 0000000..72518a5
--- /dev/null
+++ b/Snes.HC
@@ -0,0 +1,584 @@
+extern I64 mem_read(I64 adr, Bool dma=FALSE);
+extern U0 mem_write(I64 adr, I64 value, Bool dma = FALSE);
+extern I64 readBBus(I64 adr);
+extern U0 writeBBus(I64 adr, I64 value);
+extern U0 cpu_cycle();
+extern U0 renderLine(I32 line);
+
+class ROMHeader
+{
+ U8 *name;
+ I64 type;
+ I64 speed;
+ I64 chips;
+ I64 romSize;
+ I64 ramSize;
+ Bool hasSram;
+ I64 banks;
+ I64 sramSize;
+};
+ROMHeader header;
+
+ U8 *ram = MAlloc(0x20000);
+ U8 *sram;
+ U8 *rom;
+
+
+ // ppu variables
+ I64 cgramAdr;
+ Bool cgramSecond;
+ I64 cgramBuffer = 0;
+ I64 vramInc;
+ I64 vramRemap;
+ Bool vramIncOnHigh;
+ I64 vramAdr;
+ I64 vramReadBuffer;
+ Bool tilemapWider[4];
+ Bool tilemapHigher[4];
+ I64 tilemapAdr[4];
+ I64 tileAdr[4];
+ I64 bgHoff[5];
+ I64 bgVoff[5];
+ I64 offPrev1;
+ I64 offPrev2;
+ I64 mode;
+ Bool layer3Prio;
+ Bool bigTiles[4];
+ Bool mosaicEnabled[5];
+ I64 mosaicSize;
+ I64 mosaicStartLine;
+ Bool mainScreenEnabled[5];
+ Bool subScreenEnabled[5];
+ Bool forcedBlank;
+ I64 brightness;
+ I64 oamAdr;
+ I64 oamRegAdr;
+ Bool oamInHigh;
+ Bool oamRegInHigh;
+ Bool objPriority;
+ Bool oamSecond;
+ U16 oamBuffer;
+ I64 sprAdr1;
+ I64 sprAdr2;
+ I64 objSize;
+ Bool rangeOver;
+ Bool timeOver;
+ Bool mode7ExBg;
+ Bool pseudoHires;
+ Bool overscan;
+ Bool objInterlace;
+ Bool interlace;
+ Bool frameOverscan;
+ Bool frameInterlace;
+ Bool evenFrame;
+ I64 latchedHpos;
+ I64 latchedVpos;
+ Bool latchHsecond;
+ Bool latchVsecond;
+ Bool countersLatched;
+ I64 mode7Hoff;
+ I64 mode7Voff;
+ I64 mode7A;
+ I64 mode7B;
+ I64 mode7C;
+ I64 mode7D;
+ I64 mode7X;
+ I64 mode7Y;
+ I64 mode7Prev;
+ I64 multResult;
+ Bool mode7LargeField;
+ Bool mode7Char0fill;
+ Bool mode7FlipX;
+ Bool mode7FlipY;
+ Bool window1Inversed[6];
+ Bool window1Enabled[6];
+ Bool window2Inversed[6];
+ Bool window2Enabled[6];
+ I64 windowMaskLogic[6];
+ I64 window1Left;
+ I64 window1Right;
+ I64 window2Left;
+ I64 window2Right;
+ Bool mainScreenWindow[6];
+ Bool subScreenWindow[6];
+ I64 colorClip;
+ I64 preventMath;
+ Bool addSub;
+ Bool directColor;
+ Bool subtractColors;
+ Bool halfColors;
+ Bool mathEnabled[6];
+ I64 fixedColorB;
+ I64 fixedColorG;
+ I64 fixedColorR;
+ I64 tilemapBuffer[4];
+ I64 tileBufferP1[4];
+ I64 tileBufferP2[4];
+ I64 tileBufferP3[4];
+ I64 tileBufferP4[4];
+ I64 lastTileFetchedX[4];
+ I64 lastTileFetchedY[4];
+ I64 optHorBuffer[2];
+ I64 optVerBuffer[2];
+ I64 lastOrigTileX[2];
+
+ I64 dmaOffs[32] = {
+ 0, 0, 0, 0,
+ 0, 1, 0, 1,
+ 0, 0, 0, 0,
+ 0, 0, 1, 1,
+ 0, 1, 2, 3,
+ 0, 1, 0, 1,
+ 0, 0, 0, 0,
+ 0, 0, 1, 1
+ };
+
+ I64 dmaOffLengths[8] = {1, 2, 2, 4, 4, 4, 2, 4};
+
+ I64 apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60);
+
+ U8 spcWritePorts[4];
+ U8 spcReadPorts[6];
+
+ // for dma
+ U8 dmaBadr[8];
+ U16 dmaAadr[8];
+ U8 dmaAadrBank[8];
+ U16 dmaSize[8];
+ U8 hdmaIndBank[8];
+ U16 hdmaTableAdr[8];
+ U8 hdmaRepCount[8];
+
+ I64 xPos = 0;
+ I64 yPos = 0;
+ I64 frames = 0;
+
+ I64 cpuCyclesLeft = 5 * 8 + 12; // reset: 5 read cycles + 2 IO cycles
+ I64 cpuMemOps = 0;
+ I64 apuCatchCycles = 0;
+
+ I64 cyclesLeft; // moved from CPU.HC
+ Bool irqWanted; // moved from CPU.HC
+ Bool nmiWanted; // moved from CPU.HC
+
+ // for cpu-ports
+ I64 ramAdr = 0;
+
+ Bool hIrqEnabled = FALSE;
+ Bool vIrqEnabled = FALSE;
+ Bool nmiEnabled = FALSE;
+ I64 hTimer = 0x1ff;
+ I64 vTimer = 0x1ff;
+ Bool inNmi = FALSE;
+ Bool inIrq = FALSE;
+ Bool inHblank = FALSE;
+ Bool inVblank = FALSE;
+
+ Bool autoJoyRead = FALSE;
+ Bool autoJoyBusy = FALSE;
+ I64 autoJoyTimer = 0;
+ Bool ppuLatch = FALSE;
+
+ I64 joypad1Val = 0;
+ I64 joypad2Val = 0;
+ I64 joypad1AutoRead = 0;
+ I64 joypad2AutoRead = 0;
+ Bool joypadStrobe = FALSE;
+ I64 joypad1State = 0; // current button state
+ I64 joypad2State = 0; // current button state
+
+ I64 multiplyA = 0xff;
+ I64 divA = 0xffff;
+ I64 divResult = 0x101;
+ I64 mulResult = 0xfe01;
+
+ Bool fastMem = FALSE;
+
+ // dma and hdma
+ I64 dmaTimer = 0;
+ I64 hdmaTimer = 0;
+ Bool dmaBusy = FALSE;
+ Bool dmaActive[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+ Bool hdmaActive[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+
+ I64 dmaMode[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ Bool dmaFixed[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+ Bool dmaDec[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+ Bool hdmaInd[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+ Bool dmaFromB[8] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
+
+ Bool hdmaDoTransfer[8] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+ Bool hdmaTerminated[8] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+ I64 dmaOffIndex = 0;
+
+ I64 openBus = 0;
+
+ I64 getAccessTime(I64 adr) {
+ adr &= 0xffffff;
+ I64 bank = adr >> 16;
+ adr &= 0xffff;
+ if(bank >= 0x40 && bank < 0x80) {
+ // banks 0x40-0x7f, all slow
+ return 8;
+ }
+ if(bank >= 0xc0) {
+ // banks 0xc0-0xff, depends on speed
+ return cond(fastMem, 6, 8);
+ }
+ // banks 0x00-0x3f and 0x80-0xbf
+ if(adr < 0x2000) {
+ return 8; // slow
+ }
+ if(adr < 0x4000) {
+ return 6; // fast
+ }
+ if(adr < 0x4200) {
+ return 12; // extra slow
+ }
+ if(adr < 0x6000) {
+ return 6; // fast
+ }
+ if(adr < 0x8000) {
+ return 8; // slow
+ }
+ // 0x8000-0xffff, depends on fastrom setting if in banks 0x80+
+ return cond((fastMem && bank >= 0x80), 6, 8);
+
+ }
+
+
+U0 catchUpApu() {
+ I64 i;
+ I64 catchUpCycles = apuCatchCycles & 0xffffffff;
+ for(i = 0; i < catchUpCycles; i++) {
+ //apu_cycle;
+ }
+ apuCatchCycles -= catchUpCycles;
+}
+
+U0 handleDma() {
+ I64 i, tableOff;
+ if(dmaTimer > 0) {
+ dmaTimer -= 2;
+ return;
+ }
+ // loop over each dma channel to find the first active one
+ for(i = 0; i < 8; i++) {
+ if(dmaActive[i]) {
+ break;
+ }
+ }
+ if(i == 8) {
+ // no active channel left, dma is done
+ dmaBusy = FALSE;
+ dmaOffIndex = 0;
+ //log("Finished DMA");
+ return;
+ }
+ tableOff = dmaMode[i] * 4 + dmaOffIndex++;
+ dmaOffIndex &= 0x3;
+ if(dmaFromB[i]) {
+ mem_write(
+ (dmaAadrBank[i] << 16) | dmaAadr[i],
+ readBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff
+ ), TRUE
+ );
+ } else {
+ writeBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff,
+ mem_read((dmaAadrBank[i] << 16) | dmaAadr[i], TRUE)
+ );
+ }
+ dmaTimer += 6;
+ // because this run through the function itself also costs 2 master cycles,
+ // we have to wait 6 more to get to 8 per byte transferred
+ if(!dmaFixed[i]) {
+ if(dmaDec[i]) {
+ dmaAadr[i]--;
+ } else {
+ dmaAadr[i]++;
+ }
+ }
+ dmaSize[i]--;
+ if(dmaSize[i] == 0) {
+ dmaOffIndex = 0;
+ dmaActive[i] = FALSE;
+ dmaTimer += 8; // 8 extra cycles overhead per channel
+ }
+ }
+
+ U0 initHdma() {
+ hdmaTimer = 18;
+ I64 i;
+ for(i = 0; i < 8; i++) {
+ if(hdmaActive[i]) {
+ // terminate DMA if it was busy for this channel
+ dmaActive[i] = FALSE;
+
+ hdmaTableAdr[i] = dmaAadr[i];
+ hdmaRepCount[i] = mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ );
+ hdmaTimer += 8;
+ if(hdmaInd[i]) {
+ dmaSize[i] = mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ );
+ dmaSize[i] |= mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ ) << 8;
+ hdmaTimer += 16;
+ }
+ hdmaDoTransfer[i] = TRUE;
+ } else {
+ hdmaDoTransfer[i] = FALSE;
+ }
+ hdmaTerminated[i] = FALSE;
+ }
+ }
+
+ U0 handleHdma() {
+ I64 i, j, tableOff;
+ hdmaTimer = 18;
+ for(i = 0; i < 8; i++) {
+ if(hdmaActive[i] && !hdmaTerminated[i]) {
+ // terminate dma if it is busy on this channel
+ dmaActive[i] = FALSE;
+ hdmaTimer += 8;
+ if(hdmaDoTransfer[i]) {
+ for(j = 0; j < dmaOffLengths[dmaMode[i]]; j++) {
+ tableOff = dmaMode[i] * 4 + j;
+ hdmaTimer += 8;
+ if(hdmaInd[i]) {
+ if(dmaFromB[i]) {
+ mem_write(
+ (hdmaIndBank[i] << 16) | dmaSize[i],
+ readBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff
+ ), TRUE
+ );
+ } else {
+ writeBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff,
+ mem_read((hdmaIndBank[i] << 16) | dmaSize[i], TRUE)
+ );
+ }
+ dmaSize[i]++;
+ } else {
+ if(dmaFromB[i]) {
+ mem_write(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i],
+ readBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff
+ ), TRUE
+ );
+ } else {
+ writeBBus(
+ (dmaBadr[i] + dmaOffs[tableOff]) & 0xff,
+ mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i], TRUE
+ )
+ );
+ }
+ hdmaTableAdr[i]++;
+ }
+ }
+ }
+ hdmaRepCount[i]--;
+ hdmaDoTransfer[i] = (hdmaRepCount[i] & 0x80) > 0;
+ if((hdmaRepCount[i] & 0x7f) == 0) {
+ hdmaRepCount[i] = mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ );
+ if(hdmaInd[i]) {
+ dmaSize[i] = mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ );
+ dmaSize[i] |= mem_read(
+ (dmaAadrBank[i] << 16) | hdmaTableAdr[i]++, TRUE
+ ) << 8;
+ hdmaTimer += 16;
+ }
+ if(hdmaRepCount[i] == 0) {
+ hdmaTerminated[i] = TRUE;
+ }
+ hdmaDoTransfer[i] = TRUE;
+ }
+ }
+ }
+ }
+
+U0 doAutoJoyRead() {
+ I64 i, bit;
+ joypad1AutoRead = 0;
+ joypad2AutoRead = 0;
+ joypad1Val = joypad1State;
+ joypad2Val = joypad2State;
+ for(i = 0; i < 16; i++) {
+ bit = joypad1Val & 0x1;
+ joypad1Val >>= 1;
+ joypad1Val |= 0x8000;
+ joypad1AutoRead |= (bit << (15 - i));
+ bit = joypad2Val & 0x1;
+ joypad2Val >>= 1;
+ joypad2Val |= 0x8000;
+ joypad2AutoRead |= (bit << (15 - i));
+ }
+}
+
+U0 cpuCycle() {
+ if(cpuCyclesLeft == 0) {
+ cyclesLeft = 0;
+ cpuMemOps = 0;
+ cpu_cycle;
+ cpuCyclesLeft += (cyclesLeft + 1 - cpuMemOps) * 6;
+ }
+ cpuCyclesLeft -= 2;
+}
+
+
+U0 cycle(Bool noPpu)
+{
+ apuCatchCycles += (apuCyclesPerMaster * 2);
+
+ if(joypadStrobe) {
+ joypad1Val = joypad1State;
+ joypad2Val = joypad2State;
+ }
+
+ if(hdmaTimer > 0) {
+ hdmaTimer -= 2;
+ } else if(dmaBusy) {
+ handleDma;
+ } else if (xPos < 536 || xPos >= 576) {
+ // the cpu is paused for 40 cycles starting around dot 536
+ cpuCycle;
+ }
+
+ if(yPos == vTimer && vIrqEnabled) {
+ if(!hIrqEnabled) {
+ // only v irq enabed
+ if(xPos == 0) {
+ inIrq = TRUE;
+ irqWanted = TRUE;
+ }
+ } else {
+ // v and h irq enabled
+ if(xPos == (hTimer * 4)) {
+ inIrq = TRUE;
+ irqWanted = TRUE;
+ }
+ }
+ } else if (
+ xPos == (hTimer * 4)
+ && hIrqEnabled && !vIrqEnabled
+ ) {
+ // only h irq enabled
+ inIrq = TRUE;
+ irqWanted = TRUE;
+ }
+
+ if(xPos == 1024) {
+ // start of hblank
+ inHblank = TRUE;
+ if(!inVblank) {
+ handleHdma;
+ }
+ } else if(xPos == 0) {
+ // end of hblank
+ inHblank = FALSE;
+ } else if(xPos == 512 && !noPpu) {
+ // render line at cycle 512 for better support
+
+ renderLine(yPos);
+ }
+
+ //renderLine(yPos);
+
+ if(yPos == cond(frameOverscan, 240, 225) && xPos == 0) {
+ // start of vblank
+ inNmi = TRUE;
+ inVblank = TRUE;
+ if(autoJoyRead) {
+ autoJoyBusy = TRUE;
+ autoJoyTimer = 4224;
+ doAutoJoyRead;
+ }
+ if(nmiEnabled) {
+ nmiWanted = TRUE;
+ }
+ } else if(yPos == 0 && xPos == 0) {
+ // end of vblank
+ inNmi = FALSE;
+ inVblank = FALSE;
+ initHdma;
+ }
+
+ if(autoJoyBusy) {
+ autoJoyTimer -= 2; // loop only runs every second master cycle
+ if(autoJoyTimer == 0) {
+ autoJoyBusy = FALSE;
+ }
+ }
+
+ // TODO: in non-intelace mode, line 240 on every odd frame is 1360 cycles
+ // and in interlace mode, every even frame is 263 lines
+ xPos += 2;
+ if(xPos == 1364) {
+ xPos = 0;
+ yPos++;
+ if(yPos == 262) {
+ // when finishing a frame, also catch up the apu
+ catchUpApu;
+ yPos = 0;
+ frames++;
+ }
+ }
+ }
+
+#define JPAD_Y 0
+#define JPAD_B 1
+#define JPAD_SELECT 2
+#define JPAD_START 3
+#define JPAD_UP 4
+#define JPAD_DOWN 5
+#define JPAD_LEFT 6
+#define JPAD_RIGHT 7
+#define JPAD_A 8
+#define JPAD_X 9
+#define JPAD_L 10
+#define JPAD_R 11
+
+U0 setPad1Button(I64 num, I64 sc)
+{
+ switch (KeyDown(sc))
+ {
+ case 0:
+ joypad1State &= (~(1 << num)) & 0xfff;
+ break;
+ case 1:
+ joypad1State |= (1 << num);
+ break;
+ }
+}
+
+U0 Joypad1Update()
+{
+ setPad1Button(JPAD_Y, Char2ScanCode('z'));
+ setPad1Button(JPAD_B, Char2ScanCode('a'));
+ setPad1Button(JPAD_X, Char2ScanCode('s'));
+ setPad1Button(JPAD_A, Char2ScanCode('x'));
+ setPad1Button(JPAD_L, Char2ScanCode('d'));
+ setPad1Button(JPAD_R, Char2ScanCode('c'));
+ setPad1Button(JPAD_SELECT, SC_SHIFT);
+ setPad1Button(JPAD_START, SC_ENTER);
+ setPad1Button(JPAD_UP, SC_CURSOR_UP);
+ setPad1Button(JPAD_DOWN, SC_CURSOR_DOWN);
+ setPad1Button(JPAD_LEFT, SC_CURSOR_LEFT);
+ setPad1Button(JPAD_RIGHT, SC_CURSOR_RIGHT);
+} \ No newline at end of file
diff --git a/font.png b/font.png
new file mode 100644
index 0000000..21c355f
--- /dev/null
+++ b/font.png
Binary files differ