diff options
author | Alec Murphy <alec@checksum.fail> | 2020-02-16 18:17:17 -0500 |
---|---|---|
committer | Alec Murphy <alec@checksum.fail> | 2020-02-16 18:17:17 -0500 |
commit | d4588bba4259f81a7ef00db0dd909caa7a337a5c (patch) | |
tree | 19d62add575eaf44867a3e15bb9c68bdc084792e | |
parent | a6c2e6229130cc80e15e9fa61456d809c47eabd9 (diff) |
Add files to repository
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CPU.HC | 2723 | ||||
-rw-r--r-- | Font.HC | 90 | ||||
-rw-r--r-- | Lib/Debugger.HC | 148 | ||||
-rw-r--r-- | Lib/Display.HC | 17 | ||||
-rw-r--r-- | Lib/Graphics2D.HC | 250 | ||||
-rw-r--r-- | Lib/Misc.HC | 56 | ||||
-rw-r--r-- | Lib/VMSVGA.HC | 96 | ||||
-rw-r--r-- | Lib/uPNG.HC | 1073 | ||||
-rw-r--r-- | LoROM.HC | 42 | ||||
-rw-r--r-- | Load.HC | 106 | ||||
-rw-r--r-- | MMU.HC | 436 | ||||
-rw-r--r-- | PPU.HC | 1397 | ||||
-rw-r--r-- | Run.HC | 1 | ||||
-rw-r--r-- | Snes.HC | 584 | ||||
-rw-r--r-- | font.png | bin | 0 -> 2704 bytes |
16 files changed, 7020 insertions, 0 deletions
@@ -1,3 +1,4 @@ +uncrustify.cfg .vscode/ ROMs/ SnesJs/ @@ -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 @@ -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 @@ -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/")); @@ -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 @@ -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 @@ -0,0 +1 @@ +#include "Load";
\ No newline at end of file @@ -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 Binary files differnew file mode 100644 index 0000000..21c355f --- /dev/null +++ b/font.png |