diff --git a/Assets/gamedb/gamedb_nes.txt b/Assets/gamedb/gamedb_nes.txt index b837ef1be5..ec808e4165 100644 --- a/Assets/gamedb/gamedb_nes.txt +++ b/Assets/gamedb/gamedb_nes.txt @@ -305,7 +305,6 @@ sha1:17473C223453D2D80FCB9DCFA317947287DC5C52 Xing He Zhan Shi (China) (Unl) NE sha1:B1C74236FD17FAB4AB9AA6AB28E38864C66D6255 Pocahontus (UNL) NES board=MAPPER182;PRG=256;CHR=256;WRAM=8;PAD_H=1 sha1:5FA23F88432006DCF6874EA36E9E7DA8934427BE Super Donkey Kong (Unl) NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 B Super Donkey Kong (Unl) [b1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 -sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 O Super Donkey Kong (Unl) [o1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 ;wrong vram info sha1:32D71DD6C5A8D78A918FE1B9D6D6C4A570D9652D Oeka Kids Anpanman no Hiragana Daisuki (J) NES board=MAPPER096;VRAM=32 diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 157b849a24..0e551be12f 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -709,6 +709,38 @@ + + + + + O2Hawk.cs + + + O2Hawk.cs + + + O2Hawk.cs + + + O2Hawk.cs + + + O2Hawk.cs + + + O2Hawk.cs + + + O2Hawk.cs + + + + + + + + + @@ -1607,6 +1639,13 @@ + + + + + + + diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Disassembler.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Disassembler.cs new file mode 100644 index 0000000000..bd325ae77d --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Disassembler.cs @@ -0,0 +1,494 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public sealed partial class I8048 + { + static string[] table = + { + "NEG DP+i8", // 00 + "???", // 01 + "???", // 02 + "COM DP+i8", // 03 + "LSR DP+i8", // 04 + "???", // 05 + "ROR DP+i8", // 06 + "ASR DP+i8", // 07 + "ASL DP+i8", // 08 + "ROL DP+i8", // 09 + "DEC DP+i8", // 0a + "???", // 0b + "INC DP+i8", // 0c + "TST DP+i8", // 0d + "JMP DP+i8", // 0e + "CLR DP+i8", // 0f + "PAGE 2", // 10 + "PAGE 3", // 11 + "NOP", // 12 + "SYNC", // 13 + "???", // 14 + "???", // 15 + "LBRA i16", // 16 + "LBSR i16", // 17 + "???", // 18 + "DAA", // 19 + "ORCC i8", // 1a + "???", // 1b + "ANDCC i8", // 1c + "SEX", // 1d + "EXG i8", // 1e + "TFR i8", // 1f + "BRA i8", // 20 + "BRN i8", // 21 + "BHI i8", // 22 + "BLS i8", // 23 + "BHS i8", // 24 + "BLO i8", // 25 + "BNE i8", // 26 + "BEQ i8", // 27 + "BVC i8", // 28 + "BVS i8", // 29 + "BPL i8", // 2a + "BMI i8", // 2b + "BGE i8", // 2c + "BLT i8", // 2d + "BGT i8", // 2e + "BLE i8", // 2f + "LEAX ix16", // 30 + "LEAY ix16", // 31 + "LEAS ix16", // 32 + "LEAU ix16", // 33 + "PSHS i8", // 34 + "PULS i8", // 35 + "PSHU i8", // 36 + "PULU i8", // 37 + "???", // 38 + "RTS", // 39 + "ABX", // 3a + "RTI", // 3b + "CWAI i8", // 3c + "MUL", // 3d + "???", // 3e + "SWI1", // 3f + "NEG A", // 40 + "???", // 41 + "???", // 42 + "COM A", // 43 + "LSR A", // 44 + "???", // 45 + "ROR A", // 46 + "ASR A", // 47 + "ASL A", // 48 + "ROL A", // 49 + "DEC A", // 4a + "???", // 4b + "INC A", // 4c + "TST A", // 4d + "???", // 4e + "CLR A", // 4f + "NEG B", // 50 + "???", // 51 + "???", // 52 + "COM B", // 53 + "LSR B", // 54 + "???", // 55 + "ROR B", // 56 + "ASR B", // 57 + "ASL B", // 58 + "ROL B", // 59 + "DEC B", // 5a + "???", // 5b + "INC B", // 5c + "TST B", // 5d + "???", // 5e + "CLR B", // 5f + "NEG ix16", // 60 + "???", // 61 + "???", // 62 + "COM ix16", // 63 + "LSR ix16", // 64 + "???", // 65 + "ROR ix16", // 66 + "ASR ix16", // 67 + "ASL ix16", // 68 + "ROL ix16", // 69 + "DEC ix16", // 6a + "???", // 6b + "INC ix16", // 6c + "TST ix16", // 6d + "JMP ix16", // 6e + "CLR ix16", // 6f + "NEG ex16", // 70 + "???", // 71 + "???", // 72 + "COM ex16", // 73 + "LSR ex16", // 74 + "???", // 75 + "ROR ex16", // 76 + "ASR ex16", // 77 + "ASL ex16", // 78 + "ROL ex16", // 79 + "DEC ex16", // 7a + "???", // 7b + "INC ex16", // 7c + "TST ex16", // 7d + "JMP ex16", // 7e + "CLR ex16", // 7f + "SUB A,i8", // 80 + "CMP A,i8", // 81 + "SBC A,i8", // 82 + "SUB D,i16", // 83 + "AND A,i8", // 84 + "BIT A,i8", // 85 + "LD A,i8", // 86 + "???", // 87 + "EOR A,i8", // 88 + "ADC A,i8", // 89 + "OR A,i8", // 8a + "ADD A,i8", // 8b + "CMP X,i16", // 8c + "BSR i8", // 8d + "LD X,i16", // 8e + "???", // 8f + "SUB A,DP+i8", // 90 + "CMP A,DP+i8", // 91 + "SBC A,DP+i8", // 92 + "SUB D,DP+i8", // 93 + "AND A,DP+i8", // 94 + "BIT A,DP+i8", // 95 + "LD A,DP+i8", // 96 + "ST A,DP+i8", // 97 + "EOR A,DP+i8", // 98 + "ADC A,DP+i8", // 99 + "OR A,DP+i8", // 9a + "ADD A,DP+i8", // 9b + "CMP X,DP+i8", // 9c + "JSR DP+i8", // 9d + "LD X,DP+i8", // 9e + "ST X,DP+i8", // 9f + "SUB A,ix16", // a0 + "CMP A,ix16", // a1 + "SBC A,ix16", // a2 + "SUB D,ix16", // a3 + "AND A,ix16", // a4 + "BIT A,ix16", // a5 + "LD A,ix16", // a6 + "ST A,ix16", // a7 + "EOR A,ix16", // a8 + "ADC A,ix16", // a9 + "OR A,ix16", // aa + "ADD A,ix16", // ab + "CMP X,ix16", // ac + "JSR ix16", // ad + "LD X,ix16", // ae + "ST X,ix16", // af + "SUB A,ex16", // b0 + "CMP A,ex16", // b1 + "SBC A,ex16", // b2 + "SUB D,ex16", // b3 + "AND A,ex16", // b4 + "BIT A,ex16", // b5 + "LD A,ex16", // b6 + "ST A,ex16", // b7 + "EOR A,ex16", // b8 + "ADC A,ex16", // b9 + "OR A,ex16", // ba + "ADD A,ex16", // bb + "CMP X,ex16", // bc + "JSR ex16", // bd + "LD X,ex16", // be + "ST X,ex16", // bf + "SUB B,i8", // c0 + "CMP B,i8", // c1 + "SBC B,i8", // c2 + "ADD D,i16", // c3 + "AND B,i8", // c4 + "BIT B,i8", // c5 + "LD B,i8", // c6 + "???", // c7 + "EOR B,i8", // c8 + "ADC B,i8", // c9 + "OR B,i8", // ca + "ADD B,i8", // cb + "LD D,i16", // cc + "???", // cd + "LD U,i16", // ce + "???", // cf + "SUB B,DP+i8", // d0 + "CMP B,DP+i8", // d1 + "SBC B,DP+i8", // d2 + "ADD D,DP+i8", // d3 + "AND B,DP+i8", // d4 + "BIT B,DP+i8", // d5 + "LD B,DP+i8", // d6 + "ST B,DP+i8", // d7 + "EOR B,DP+i8", // d8 + "ADC B,DP+i8", // d9 + "OR B,DP+i8", // da + "ADD B,DP+i8", // db + "LD D,DP+i8", // dc + "ST D,DP+i8", // dd + "LD U,DP+i8", // de + "ST U,DP+i8", // df + "SUB B,ix16", // e0 + "CMP B,ix16", // e1 + "SBC B,ix16", // e2 + "ADD D,ix16", // e3 + "AND B,ix16", // e4 + "BIT B,ix16", // e5 + "LD B,ix16", // e6 + "ST B,ix16", // e7 + "EOR B,ix16", // e8 + "ADC B,ix16", // e9 + "OR B,ix16", // ea + "ADD B,ix16", // eb + "LD D,ix16", // ec + "ST D,ix16", // ed + "LD U,ix16", // ee + "ST U,ix16", // ef + "SUB B,ex16", // f0 + "CMP B,ex16", // f1 + "SBC B,ex16", // f2 + "ADD D,ex16", // f3 + "AND B,ex16", // f4 + "BIT B,ex16", // f5 + "LD B,ex16", // f6 + "ST B,ex16", // f7 + "EOR B,ex16", // f8 + "ADC B,ex16", // f9 + "OR B,ex16", // fa + "ADD B,ex16", // fb + "LD D,ex16", // fc + "ST D,ex16", // fd + "LD U,ex16", // fe + "ST U,ex16", // ff + }; + + public static string Disassemble(ushort addr, Func reader, out ushort size) + { + ushort origaddr = addr; + List bytes = new List(); + bytes.Add(reader(addr++)); + + string result = table[bytes[0]]; + + if (result.Contains("i8")) + { + byte d = reader(addr++); + bytes.Add(d); + result = result.Replace("i8", string.Format("#{0:X2}h", d)); + } + else if (result.Contains("i16")) + { + byte dhi = reader(addr++); + byte dlo = reader(addr++); + bytes.Add(dhi); + bytes.Add(dlo); + result = result.Replace("i16", string.Format("#{0:X2}{1:X2}h", dhi, dlo)); + } + else if (result.Contains("ex16")) + { + byte dhi = reader(addr++); + byte dlo = reader(addr++); + bytes.Add(dhi); + bytes.Add(dlo); + result = result.Replace("ex16", "(" + string.Format("#{0:X2}{1:X2}h", dhi, dlo) + ")"); + } + else if (result.Contains("ix16")) + { + byte d = reader(addr++); + bytes.Add(d); + + string temp_reg = ""; + + switch ((d >> 5) & 3) + { + case 0: temp_reg = "X"; break; + case 1: temp_reg = "Y"; break; + case 2: temp_reg = "US"; break; + case 3: temp_reg = "SP"; break; + } + + if ((d & 0x80) == 0) + { + short tempdis = (short)(d & 0x1F); + if (tempdis >= 16) + tempdis -= 32; + + result = result.Replace("ix16", temp_reg + " + ea"); + result = result.Replace("ea", string.Format("{0:N}h", tempdis)); + } + else + { + if ((d & 0x10) == 0x10) + { + switch (d & 0xF) + { + case 0x0: + result = result.Replace("ix16", "???"); + break; + case 0x1: + result = result.Replace("ix16","(" + temp_reg + ")++"); + break; + case 0x2: + result = result.Replace("ix16", "???"); + break; + case 0x3: + result = result.Replace("ix16", "--(" + temp_reg + ")"); + break; + case 0x4: + result = result.Replace("ix16", "(" + temp_reg + ")"); + break; + case 0x5: + result = result.Replace("ix16", "(" + temp_reg + " + B)"); + break; + case 0x6: + result = result.Replace("ix16", "(" + temp_reg + " + A)"); + break; + case 0x7: + result = result.Replace("ix16", "???"); + break; + case 0x8: + byte e = reader(addr++); + bytes.Add(e); + result = result.Replace("ix16", "(" + temp_reg + " + ea)"); + result = result.Replace("ea", string.Format("{0:X2}h", e)); + break; + case 0x9: + byte f = reader(addr++); + bytes.Add(f); + byte g = reader(addr++); + bytes.Add(g); + result = result.Replace("ix16", "(" + temp_reg + " + ea)"); + result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", f, g)); + break; + case 0xA: + result = result.Replace("ix16", "???"); + break; + case 0xB: + result = result.Replace("ix16", "(" + temp_reg + " + D)"); + break; + case 0xC: + temp_reg = "PC"; + byte h = reader(addr++); + bytes.Add(h); + result = result.Replace("ix16", "(" + temp_reg + " + ea)"); + result = result.Replace("ea", string.Format("{0:X2}h", h)); + break; + case 0xD: + temp_reg = "PC"; + byte i = reader(addr++); + bytes.Add(i); + byte j = reader(addr++); + bytes.Add(j); + result = result.Replace("ix16", "(" + temp_reg + " + ea)"); + result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", i, j)); + break; + case 0xE: + result = result.Replace("ix16", "???"); + break; + case 0xF: + if (((d >> 5) & 3) == 0) + { + byte k = reader(addr++); + bytes.Add(k); + byte l = reader(addr++); + bytes.Add(l); + result = result.Replace("ix16", "(" + string.Format("{0:X2}{1:X2}h", k, l) + ")"); + } + else + { + result = result.Replace("ix16", "???"); + } + break; + } + } + else + { + switch (d & 0xF) + { + case 0x0: + result = result.Replace("ix16", temp_reg + "+"); + break; + case 0x1: + result = result.Replace("ix16", temp_reg + "++"); + break; + case 0x2: + result = result.Replace("ix16", "-" + temp_reg); + break; + case 0x3: + result = result.Replace("ix16", "--" + temp_reg); + break; + case 0x4: + result = result.Replace("ix16", temp_reg); + break; + case 0x5: + result = result.Replace("ix16", temp_reg + " + B"); + break; + case 0x6: + result = result.Replace("ix16", temp_reg + " + A"); + break; + case 0x7: + result = result.Replace("ix16", "???"); + break; + case 0x8: + byte e = reader(addr++); + bytes.Add(e); + result = result.Replace("ix16", temp_reg + " + ea"); + result = result.Replace("ea", string.Format("{0:X2}h", e)); + break; + case 0x9: + byte f = reader(addr++); + bytes.Add(f); + byte g = reader(addr++); + bytes.Add(g); + result = result.Replace("ix16", temp_reg + " + ea"); + result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", f, g)); + break; + case 0xA: + result = result.Replace("ix16", "???"); + break; + case 0xB: + result = result.Replace("ix16", temp_reg + " + D"); + break; + case 0xC: + temp_reg = "PC"; + byte h = reader(addr++); + bytes.Add(h); + result = result.Replace("ix16", temp_reg + " + ea"); + result = result.Replace("ea", string.Format("{0:X2}h", h)); + break; + case 0xD: + temp_reg = "PC"; + byte i = reader(addr++); + bytes.Add(i); + byte j = reader(addr++); + bytes.Add(j); + result = result.Replace("ix16", temp_reg + " + ea"); + result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", i, j)); + break; + case 0xE: + result = result.Replace("ix16", "???"); + break; + case 0xF: + result = result.Replace("ix16", "???"); + break; + } + } + } + } + + StringBuilder ret = new StringBuilder(); + ret.Append(string.Format("{0:X4}: ", origaddr)); + foreach (var b in bytes) + ret.Append(string.Format("{0:X2} ", b)); + while (ret.Length < 22) + ret.Append(' '); + ret.Append(result); + size = (ushort)(addr - origaddr); + return ret.ToString(); + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs new file mode 100644 index 0000000000..279b9b1d54 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs @@ -0,0 +1,285 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public partial class I8048 + { + public ulong TotalExecutedCycles; + + // variables for executing instructions + public int instr_pntr = 0; + public ushort[] cur_instr = new ushort[60]; + public int opcode_see; + + public int IRQS; + public int irq_pntr; + + ushort reg_d_ad; + ushort reg_h_ad; + ushort reg_l_ad; + + public void FetchInstruction(byte opcode) + { + opcode_see = opcode; + switch (opcode) + { + case 0x00: OP_IMP(IDLE); break; // NOP + case 0x01: ILLEGAL(); break; // ILLEGAL + case 0x02: ILLEGAL(); break; // ILLEGAL + case 0x03: OP_A_DIR(ADD8); break; // ADD A,# + case 0x04: ILLEGAL(); break; // LSR (Direct) + case 0x05: OP_IMP(EI); break; // EI + case 0x06: ILLEGAL(); break; // ROR (Direct) + case 0x07: OP_IMP(DECA); break; // DEC A + case 0x08: ILLEGAL(); break; // ASL , LSL (Direct) + case 0x09: ILLEGAL(); break; // ROL (Direct) + case 0x0A: ILLEGAL(); break; // DEC (Direct) + case 0x0B: ILLEGAL(); break; // ILLEGAL + case 0x0C: ILLEGAL(); break; // INC (Direct) + case 0x0D: ILLEGAL(); break; // TST (Direct) + case 0x0E: ILLEGAL(); break; // JMP (Direct) + case 0x0F: ILLEGAL(); break; // CLR (Direct) + case 0x10: ILLEGAL(); break; // Page 2 + case 0x11: ILLEGAL(); break; // Page 3 + case 0x12: ILLEGAL(); break; // NOP (Inherent) + case 0x13: OP_A_DIR(ADC8); break; // ADC A,# + case 0x14: CALL(0); break; // CALL + case 0x15: OP_IMP(DI); break; // DI + case 0x16: ILLEGAL(); break; // LBRA (Relative) + case 0x17: OP_IMP(INCA); break; // INC A + case 0x18: ILLEGAL(); break; // ILLEGAL + case 0x19: ILLEGAL(); break; // DAA (Inherent) + case 0x1A: ILLEGAL(); break; // ORCC (Immediate) + case 0x1B: ILLEGAL(); break; // ILLEGAL + case 0x1C: ILLEGAL(); break; // ANDCC (Immediate) + case 0x1D: ILLEGAL(); break; // SEX (Inherent) + case 0x1E: ILLEGAL(); break; // EXG (Immediate) + case 0x1F: ILLEGAL(); break; // TFR (Immediate) + case 0x20: ILLEGAL(); break; // BRA (Relative) + case 0x21: ILLEGAL(); break; // BRN (Relative) + case 0x22: ILLEGAL(); break; // BHI (Relative) + case 0x23: ILLEGAL(); break; // BLS (Relative) + case 0x24: ILLEGAL(); break; // BHS , BCC (Relative) + case 0x25: OP_IMP(EN); break; // EN + case 0x26: ILLEGAL(); break; // BNE (Relative) + case 0x27: OP_IMP(CLR); break; // CLR A + case 0x28: ILLEGAL(); break; // BVC (Relative) + case 0x29: ILLEGAL(); break; // BVS (Relative) + case 0x2A: ILLEGAL(); break; // BPL (Relative) + case 0x2B: ILLEGAL(); break; // BMI (Relative) + case 0x2C: ILLEGAL(); break; // BGE (Relative) + case 0x2D: ILLEGAL(); break; // BLT (Relative) + case 0x2E: ILLEGAL(); break; // BGT (Relative) + case 0x2F: ILLEGAL(); break; // BLE (Relative) + case 0x30: ILLEGAL(); break; // LEAX (Indexed) + case 0x31: ILLEGAL(); break; // LEAY (Indexed) + case 0x32: ILLEGAL(); break; // LEAS (Indexed) + case 0x33: ILLEGAL(); break; // LEAU (Indexed) + case 0x34: CALL(1); break; // CALL + case 0x35: OP_IMP(DN); break; // DN + case 0x36: ILLEGAL(); break; // PSHU (Immediate) + case 0x37: OP_IMP(COM); break; // COM A + case 0x38: ILLEGAL(); break; // ILLEGAL + case 0x39: ILLEGAL(); break; // RTS (Inherent) + case 0x3A: ILLEGAL(); break; // ABX (Inherent) + case 0x3B: ILLEGAL(); break; // RTI (Inherent) + case 0x3C: ILLEGAL(); break; // CWAI (Inherent) + case 0x3D: ILLEGAL(); break; // MUL (Inherent) + case 0x3E: ILLEGAL(); break; // ILLEGAL + case 0x3F: ILLEGAL(); break; // SWI (Inherent) + case 0x40: OP_A_R(OR8RAM, R0); break; // OR A,@R0 + case 0x41: OP_A_R(OR8RAM, R1); break; // OR A,@R1 + case 0x42: ILLEGAL(); break; // ILLEGAL + case 0x43: OP_A_DIR(OR8); break; // OR A,# + case 0x44: ILLEGAL(); break; // LSRA (Inherent) + case 0x45: ILLEGAL(); break; // ILLEGAL + case 0x46: ILLEGAL(); break; // RORA (Inherent) + case 0x47: OP_IMP(SWP); break; // SWP + case 0x48: OP_A_R(OR8, R0); break; // OR A,R0 + case 0x49: OP_A_R(OR8, R1); break; // OR A,R1 + case 0x4A: OP_A_R(OR8, R2); break; // OR A,R2 + case 0x4B: OP_A_R(OR8, R3); break; // OR A,R3 + case 0x4C: OP_A_R(OR8, R4); break; // OR A,R4 + case 0x4D: OP_A_R(OR8, R5); break; // OR A,R5 + case 0x4E: OP_A_R(OR8, R6); break; // OR A,R6 + case 0x4F: OP_A_R(OR8, R7); break; // OR A,R7 + case 0x50: OP_A_R(AND8RAM, R0); break; // AND A,@R0 + case 0x51: OP_A_R(AND8RAM, R1); break; // AND A,@R1 + case 0x52: ILLEGAL(); break; // ILLEGAL + case 0x53: OP_A_DIR(AND8); break; // AND A,# + case 0x54: CALL(2); break; // CALL + case 0x55: ILLEGAL(); break; // ILLEGAL + case 0x56: ILLEGAL(); break; // RORB (Inherent) + case 0x57: OP_IMP(DA); break; // DA A + case 0x58: OP_A_R(AND8, R0); break; // AND A,R0 + case 0x59: OP_A_R(AND8, R1); break; // AND A,R1 + case 0x5A: OP_A_R(AND8, R2); break; // AND A,R2 + case 0x5B: OP_A_R(AND8, R3); break; // AND A,R3 + case 0x5C: OP_A_R(AND8, R4); break; // AND A,R4 + case 0x5D: OP_A_R(AND8, R5); break; // AND A,R5 + case 0x5E: OP_A_R(AND8, R6); break; // AND A,R6 + case 0x5F: OP_A_R(AND8, R7); break; // AND A,R7 + case 0x60: OP_A_R(ADD8RAM, R0); break; // ADD A,@R0 + case 0x61: OP_A_R(ADD8RAM, R1); break; // ADD A,@R1 + case 0x62: ILLEGAL(); break; // ILLEGAL + case 0x63: ILLEGAL(); break; // COM (Indexed) + case 0x64: ILLEGAL(); break; // LSR (Indexed) + case 0x65: ILLEGAL(); break; // ILLEGAL + case 0x66: ILLEGAL(); break; // ROR (Indexed) + case 0x67: OP_IMP(RRC); break; // RRC + case 0x68: OP_A_R(ADD8, R0); break; // ADD A,R0 + case 0x69: OP_A_R(ADD8, R1); break; // ADD A,R1 + case 0x6A: OP_A_R(ADD8, R2); break; // ADD A,R2 + case 0x6B: OP_A_R(ADD8, R3); break; // ADD A,R3 + case 0x6C: OP_A_R(ADD8, R4); break; // ADD A,R4 + case 0x6D: OP_A_R(ADD8, R5); break; // ADD A,R5 + case 0x6E: OP_A_R(ADD8, R6); break; // ADD A,R6 + case 0x6F: OP_A_R(ADD8, R7); break; // ADD A,R7 + case 0x70: OP_A_R(ADC8RAM, R0); break; // ADC A,@R0 + case 0x71: OP_A_R(ADC8RAM, R1); break; // ADC A,@R1 + case 0x72: ILLEGAL(); break; // ILLEGAL + case 0x73: ILLEGAL(); break; // COM (Extended) + case 0x74: CALL(3); break; // CALL + case 0x75: ILLEGAL(); break; // ILLEGAL + case 0x76: ILLEGAL(); break; // ROR (Extended) + case 0x77: OP_IMP(ROR); break; // ROR + case 0x78: OP_A_R(ADC8, R0); break; // ADC A,R0 + case 0x79: OP_A_R(ADC8, R1); break; // ADC A,R1 + case 0x7A: OP_A_R(ADC8, R2); break; // ADC A,R2 + case 0x7B: OP_A_R(ADC8, R3); break; // ADC A,R3 + case 0x7C: OP_A_R(ADC8, R4); break; // ADC A,R4 + case 0x7D: OP_A_R(ADC8, R5); break; // ADC A,R5 + case 0x7E: OP_A_R(ADC8, R6); break; // ADC A,R6 + case 0x7F: OP_A_R(ADC8, R7); break; // ADC A,R7 + case 0x80: ILLEGAL(); break; // SUBA (Immediate) + case 0x81: ILLEGAL(); break; // CMPA (Immediate) + case 0x82: ILLEGAL(); break; // SBCA (Immediate) + case 0x83: ILLEGAL(); break; // SUBD (Immediate) + case 0x84: ILLEGAL(); break; // ANDA (Immediate) + case 0x85: OP_IMP(CL0); break; // CLR F0 + case 0x86: ILLEGAL(); break; // LDA (Immediate) + case 0x87: ILLEGAL(); break; // ILLEGAL + case 0x88: OP_PB_DIR(OR8, BUS); break; // OR BUS,# + case 0x89: OP_PB_DIR(OR8, P1); break; // OR P1,# + case 0x8A: OP_PB_DIR(OR8, P2); break; // OR P2,# + case 0x8B: ILLEGAL(); break; // ILLEGAL + case 0x8C: OP_EXP_A(OR8, P4); break; // OR P4,A + case 0x8D: OP_EXP_A(OR8, P5); break; // OR P5,A + case 0x8E: OP_EXP_A(OR8, P6); break; // OR P6,A + case 0x8F: OP_EXP_A(OR8, P7); break; // OR P7,A + case 0x90: ILLEGAL(); break; // SUBA (Direct) + case 0x91: ILLEGAL(); break; // CMPA (Direct) + case 0x92: ILLEGAL(); break; // SBCA (Direct) + case 0x93: ILLEGAL(); break; // SUBD (Direct) + case 0x94: CALL(4); break; // CALL + case 0x95: OP_IMP(CM0); break; // COM F0 + case 0x96: ILLEGAL(); break; // LDA (Direct) + case 0x97: OP_IMP(CLC); break; // CLR C + case 0x98: OP_PB_DIR(AND8, BUS); break; // AND BUS,# + case 0x99: OP_PB_DIR(AND8, P1); break; // AND P1,# + case 0x9A: OP_PB_DIR(AND8, P2); break; // AND P2,# + case 0x9B: ILLEGAL(); break; // ILLEGAL + case 0x9C: OP_EXP_A(AND8, P4); break; // AND P4,A + case 0x9D: OP_EXP_A(AND8, P5); break; // AND P5,A + case 0x9E: OP_EXP_A(AND8, P6); break; // AND P6,A + case 0x9F: OP_EXP_A(AND8, P7); break; // AND P7,A + case 0xA0: ILLEGAL(); break; // SUBA (Indexed) + case 0xA1: ILLEGAL(); break; // CMPA (Indexed) + case 0xA2: ILLEGAL(); break; // SBCA (Indexed) + case 0xA3: ILLEGAL(); break; // SUBD (Indexed) + case 0xA4: ILLEGAL(); break; // ANDA (Indexed) + case 0xA5: OP_IMP(CL1); break; // CLR F1 + case 0xA6: ILLEGAL(); break; // LDA (Indexed) + case 0xA7: OP_IMP(CMC); break; // COM C + case 0xA8: ILLEGAL(); break; // EORA (Indexed) + case 0xA9: ILLEGAL(); break; // ADCA (Indexed) + case 0xAA: ILLEGAL(); break; // ORA (Indexed) + case 0xAB: ILLEGAL(); break; // ADDA (Indexed) + case 0xAC: ILLEGAL(); break; // CMPX (Indexed) + case 0xAD: ILLEGAL(); break; // JSR (Indexed) + case 0xAE: ILLEGAL(); break; // LDX (Indexed) + case 0xAF: ILLEGAL(); break; // STX (Indexed) + case 0xB0: ILLEGAL(); break; // SUBA (Extended) + case 0xB1: ILLEGAL(); break; // CMPA (Extended) + case 0xB2: ILLEGAL(); break; // SBCA (Extended) + case 0xB3: ILLEGAL(); break; // SUBD (Extended) + case 0xB4: CALL(5); break; // CALL + case 0xB5: OP_IMP(CM1); break; // COM F1 + case 0xB6: ILLEGAL(); break; // LDA (Extended) + case 0xB7: ILLEGAL(); break; // STA (Extended) + case 0xB8: ILLEGAL(); break; // EORA (Extended) + case 0xB9: ILLEGAL(); break; // ADCA (Extended) + case 0xBA: ILLEGAL(); break; // ORA (Extended) + case 0xBB: ILLEGAL(); break; // ADDA (Extended) + case 0xBC: ILLEGAL(); break; // CMPX (Extended) + case 0xBD: ILLEGAL(); break; // JSR (Extended) + case 0xBE: ILLEGAL(); break; // LDX (Extended) + case 0xBF: ILLEGAL(); break; // STX (Extended) + case 0xC0: ILLEGAL(); break; // SUBB (Immediate) + case 0xC1: ILLEGAL(); break; // CMPB (Immediate) + case 0xC2: ILLEGAL(); break; // SBCB (Immediate) + case 0xC3: ILLEGAL(); break; // ADDD (Immediate) + case 0xC4: ILLEGAL(); break; // ANDB (Immediate) + case 0xC5: ILLEGAL(); break; // BITB (Immediate) + case 0xC6: ILLEGAL(); break; // LDB (Immediate) + case 0xC7: ILLEGAL(); break; // ILLEGAL + case 0xC8: OP_R_IMP(DEC8, R0); break; // DEC R0 + case 0xC9: OP_R_IMP(DEC8, R1); break; // DEC R1 + case 0xCA: OP_R_IMP(DEC8, R2); break; // DEC R2 + case 0xCB: OP_R_IMP(DEC8, R3); break; // DEC R3 + case 0xCC: OP_R_IMP(DEC8, R4); break; // DEC R4 + case 0xCD: OP_R_IMP(DEC8, R5); break; // DEC R5 + case 0xCE: OP_R_IMP(DEC8, R6); break; // DEC R6 + case 0xCF: OP_R_IMP(DEC8, R7); break; // DEC R7 + case 0xD0: OP_A_R(XOR8RAM, R0); break; // XOR A,@R0 + case 0xD1: OP_A_R(XOR8RAM, R1); break; // XOR A,@R1 + case 0xD2: ILLEGAL(); break; // ILLEGAL + case 0xD3: OP_A_DIR(XOR8); break; // XOR A,# + case 0xD4: CALL(6); break; // CALL + case 0xD5: ILLEGAL(); break; // BITB (Direct) + case 0xD6: ILLEGAL(); break; // LDB (Direct) + case 0xD7: ILLEGAL(); break; // STB (Direct) + case 0xD8: OP_A_R(XOR8, R0); break; // XOR A,R0 + case 0xD9: OP_A_R(XOR8, R1); break; // XOR A,R1 + case 0xDA: OP_A_R(XOR8, R2); break; // XOR A,R2 + case 0xDB: OP_A_R(XOR8, R3); break; // XOR A,R3 + case 0xDC: OP_A_R(XOR8, R4); break; // XOR A,R4 + case 0xDD: OP_A_R(XOR8, R5); break; // XOR A,R5 + case 0xDE: OP_A_R(XOR8, R6); break; // XOR A,R6 + case 0xDF: OP_A_R(XOR8, R7); break; // XOR A,R7 + case 0xE0: ILLEGAL(); break; // SUBB (Indexed) + case 0xE1: ILLEGAL(); break; // CMPB (Indexed) + case 0xE2: ILLEGAL(); break; // SBCB (Indexed) + case 0xE3: ILLEGAL(); break; // ADDD (Indexed) + case 0xE4: ILLEGAL(); break; // ANDB (Indexed) + case 0xE5: ILLEGAL(); break; // BITB (Indexed) + case 0xE6: ILLEGAL(); break; // LDB (Indexed) + case 0xE7: OP_IMP(ROL); break; // ROL + case 0xE8: ILLEGAL(); break; // EORB (Indexed) + case 0xE9: ILLEGAL(); break; // ADCB (Indexed) + case 0xEA: ILLEGAL(); break; // ORB (Indexed) + case 0xEB: ILLEGAL(); break; // ADDB (Indexed) + case 0xEC: ILLEGAL(); break; // LDD (Indexed) + case 0xED: ILLEGAL(); break; // STD (Indexed) + case 0xEE: ILLEGAL(); break; // LDU (Indexed) + case 0xEF: ILLEGAL(); break; // STU (Indexed) + case 0xF0: ILLEGAL(); break; // SUBB (Extended) + case 0xF1: ILLEGAL(); break; // CMPB (Extended) + case 0xF2: ILLEGAL(); break; // SBCB (Extended) + case 0xF3: ILLEGAL(); break; // ADDD (Extended) + case 0xF4: CALL(7); break; // CALL + case 0xF5: ILLEGAL(); break; // BITB (Extended) + case 0xF6: ILLEGAL(); break; // LDB (Extended) + case 0xF7: OP_IMP(RLC); break; // RLC + case 0xF8: ILLEGAL(); break; // EORB (Extended) + case 0xF9: ILLEGAL(); break; // ADCB (Extended) + case 0xFA: ILLEGAL(); break; // ORB (Extended) + case 0xFB: ILLEGAL(); break; // ADDB (Extended) + case 0xFC: ILLEGAL(); break; // LDD (Extended) + case 0xFD: ILLEGAL(); break; // STD (Extended) + case 0xFE: ILLEGAL(); break; // LDU (Extended) + case 0xFF: ILLEGAL(); break; // STU (Extended) + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs new file mode 100644 index 0000000000..28ffdd7633 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs @@ -0,0 +1,369 @@ +using System; + +using BizHawk.Common; + +// Intel Corp 8048 +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public sealed partial class I8048 + { + // operations that can take place in an instruction + public const ushort IDLE = 0; + public const ushort OP = 1; + public const ushort RD = 2; + public const ushort WR = 3; + public const ushort TR = 4; + public const ushort ADD16BR = 5; + public const ushort ADD8 = 6; + public const ushort ADD8RAM = 7; + public const ushort ADC8 = 8; + public const ushort ADC8RAM = 9; + public const ushort INC16 = 10; + public const ushort INC8 = 11; + public const ushort INCA = 12; + public const ushort DEC16 = 13; + public const ushort DEC8 = 14; + public const ushort DECA = 15; + public const ushort ROL = 16; + public const ushort ROR = 17; + public const ushort RLC = 18; + public const ushort RRC = 19; + public const ushort SWP = 20; + public const ushort COM = 21; + public const ushort CMC = 22; + public const ushort CM0 = 23; + public const ushort CM1 = 24; + public const ushort DA = 25; + public const ushort AND8 = 26; + public const ushort AND8RAM = 27; + public const ushort XOR8 = 28; + public const ushort XOR8RAM = 29; + public const ushort OR8 = 30; + public const ushort OR8RAM = 31; + public const ushort ASL = 32; + public const ushort ASR = 33; + public const ushort LSR = 34; + public const ushort BIT = 35; + public const ushort RD_INC = 36; + public const ushort SET_ADDR = 37; + public const ushort NEG = 38; + public const ushort TST = 39; + public const ushort CLR = 40; + public const ushort CLC = 41; + public const ushort CL0 = 42; + public const ushort CL1 = 43; + public const ushort EI = 44; + public const ushort EN = 45; + public const ushort DI = 46; + public const ushort DN = 47; + public const ushort TFR = 48; + public const ushort ADD8BR = 49; + public const ushort ABX = 50; + public const ushort JPE = 51; + public const ushort MSK = 52; + public const ushort CMP8 = 53; + public const ushort SUB16 = 54; + public const ushort ADD16 = 55; + public const ushort CMP16 = 56; + public const ushort CMP16D = 57; + public const ushort CLR_E = 63; + + public I8048() + { + Reset(); + } + + public void Reset() + { + ResetRegisters(); + ResetInterrupts(); + TotalExecutedCycles = 0; + Regs[PC] = 0xFFFE; + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + RD_INC, ALU, PC, + RD_INC, ALU2, PC, + SET_ADDR, PC, ALU, ALU2); + + IRQS = 6; + instr_pntr = irq_pntr = 0; + } + + // Memory Access + + public Func ReadMemory; + public Action WriteMemory; + public Func PeekMemory; + public Func DummyReadMemory; + + //this only calls when the first byte of an instruction is fetched. + public Action OnExecFetch; + + public void UnregisterMemoryMapper() + { + ReadMemory = null; + ReadMemory = null; + PeekMemory = null; + DummyReadMemory = null; + } + + public void SetCallbacks + ( + Func ReadMemory, + Func DummyReadMemory, + Func PeekMemory, + Action WriteMemory + ) + { + this.ReadMemory = ReadMemory; + this.DummyReadMemory = DummyReadMemory; + this.PeekMemory = PeekMemory; + this.WriteMemory = WriteMemory; + } + + //a little CDL related stuff + public delegate void DoCDLCallbackType(ushort addr, I8048.eCDLogMemFlags flags); + + public DoCDLCallbackType CDLCallback; + + public enum eCDLogMemFlags + { + FetchFirst = 1, + FetchOperand = 2, + Data = 4, + Write = 8 + }; + + // Execute instructions + public void ExecuteOne() + { + //Console.Write(opcode_see + " "); + //Console.WriteLine(Regs[PC] + " "); + switch (cur_instr[instr_pntr++]) + { + case IDLE: + // do nothing + break; + case OP: + // Read the opcode of the next instruction + if (OnExecFetch != null) OnExecFetch(PC); + if (TraceCallback != null) TraceCallback(State()); + if (CDLCallback != null) CDLCallback(PC, eCDLogMemFlags.FetchFirst); + FetchInstruction(ReadMemory(Regs[PC]++)); + instr_pntr = 0; + irq_pntr = -1; + break; + case RD: + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case RD_INC: + Read_Inc_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case TR: + TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case TFR: + TFR_Func(cur_instr[instr_pntr++]); + break; + case SET_ADDR: + reg_d_ad = cur_instr[instr_pntr++]; + reg_h_ad = cur_instr[instr_pntr++]; + reg_l_ad = cur_instr[instr_pntr++]; + + // Console.WriteLine(reg_d_ad + " " + reg_h_ad + " " + reg_l_ad); + // Console.WriteLine(Regs[reg_d_ad] + " " + Regs[reg_h_ad] + " " + Regs[reg_l_ad]); + + Regs[reg_d_ad] = (ushort)((Regs[reg_h_ad] << 8) | Regs[reg_l_ad]); + break; + case TST: + TST_Func(cur_instr[instr_pntr++]); + break; + case CLR: + CLR_Func(cur_instr[instr_pntr++]); + break; + case CLR_E: + + break; + case ADD16BR: + ADD16BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD8BR: + ADD8BR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD8: + ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADC8: + ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case CMP8: + CMP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case INC16: + INC16_Func(cur_instr[instr_pntr++]); + break; + case INC8: + INC8_Func(cur_instr[instr_pntr++]); + break; + case DEC16: + DEC16_Func(cur_instr[instr_pntr++]); + break; + case CMP16: + CMP16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case DEC8: + DEC8_Func(cur_instr[instr_pntr++]); + break; + case ROL: + ROL_Func(cur_instr[instr_pntr++]); + break; + case ROR: + ROR_Func(cur_instr[instr_pntr++]); + break; + case COM: + COM_Func(cur_instr[instr_pntr++]); + break; + case DA: + DA_Func(cur_instr[instr_pntr++]); + break; + case AND8: + AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case XOR8: + XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case OR8: + OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ASL: + ASL_Func(cur_instr[instr_pntr++]); + break; + case ASR: + ASR_Func(cur_instr[instr_pntr++]); + break; + case LSR: + LSR_Func(cur_instr[instr_pntr++]); + break; + case BIT: + BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + } + + if (++irq_pntr == IRQS) + { + // then regular IRQ + if (IRQPending && IntEn) + { + IRQPending = false; + + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } + + IRQ_(); + IRQCallback(); + instr_pntr = irq_pntr = 0; + } + // otherwise start the next instruction + else + { + PopulateCURINSTR(OP); + instr_pntr = irq_pntr = 0; + IRQS = -1; + } + } + + TotalExecutedCycles++; + } + + // tracer stuff + + public Action TraceCallback; + + public string TraceHeader + { + get { return "MC6809: PC, machine code, mnemonic, operands, registers (A, B, X, Y, US, SP, DP, CC), Cy, flags (EFHINZVC)"; } + } + + public TraceInfo State(bool disassemble = true) + { + ushort notused; + + return new TraceInfo + { + Disassembly = $"{(disassemble ? Disassemble(Regs[PC], ReadMemory, out notused) : "---")} ".PadRight(50), + RegisterInfo = string.Format( + "A:{0:X2} R0:{1:X2} R1:{2:X2} R2:{3:X2} R3:{4:X2} R4:{5:X2} R5:{6:X2} R6:{7:X2} R7:{8:X2} PSW:{9:X4} Cy:{10} {11}{12}{13}{14} {15}{16}{17}{18}", + Regs[A], + Regs[R0], + Regs[R1], + Regs[R2], + Regs[R3], + Regs[R4], + Regs[R5], + Regs[R6], + Regs[R7], + Regs[PSW], + TotalExecutedCycles, + FlagC ? "C" : "c", + FlagAC ? "A" : "a", + FlagF0 ? "F" : "f", + FlagBS ? "B" : "b", + IntEn ? "I" : "i", + F1 ? "F" : "f", + T0 ? "T" : "t", + T1 ? "T" : "t" + ) + }; + } + + /// + /// Optimization method to set cur_instr + /// + private void PopulateCURINSTR(ushort d0 = 0, ushort d1 = 0, ushort d2 = 0, ushort d3 = 0, ushort d4 = 0, ushort d5 = 0, ushort d6 = 0, ushort d7 = 0, ushort d8 = 0, + ushort d9 = 0, ushort d10 = 0, ushort d11 = 0, ushort d12 = 0, ushort d13 = 0, ushort d14 = 0, ushort d15 = 0, ushort d16 = 0, ushort d17 = 0, ushort d18 = 0, + ushort d19 = 0, ushort d20 = 0, ushort d21 = 0, ushort d22 = 0, ushort d23 = 0, ushort d24 = 0, ushort d25 = 0, ushort d26 = 0, ushort d27 = 0, ushort d28 = 0, + ushort d29 = 0, ushort d30 = 0, ushort d31 = 0, ushort d32 = 0, ushort d33 = 0, ushort d34 = 0, ushort d35 = 0, ushort d36 = 0, ushort d37 = 0, ushort d38 = 0) + { + cur_instr[0] = d0; cur_instr[1] = d1; cur_instr[2] = d2; + cur_instr[3] = d3; cur_instr[4] = d4; cur_instr[5] = d5; + cur_instr[6] = d6; cur_instr[7] = d7; cur_instr[8] = d8; + cur_instr[9] = d9; cur_instr[10] = d10; cur_instr[11] = d11; + cur_instr[12] = d12; cur_instr[13] = d13; cur_instr[14] = d14; + cur_instr[15] = d15; cur_instr[16] = d16; cur_instr[17] = d17; + cur_instr[18] = d18; cur_instr[19] = d19; cur_instr[20] = d20; + cur_instr[21] = d21; cur_instr[22] = d22; cur_instr[23] = d23; + cur_instr[24] = d24; cur_instr[25] = d25; cur_instr[26] = d26; + cur_instr[27] = d27; cur_instr[28] = d28; cur_instr[29] = d29; + cur_instr[30] = d30; cur_instr[31] = d31; cur_instr[32] = d32; + cur_instr[33] = d33; cur_instr[34] = d34; cur_instr[35] = d35; + cur_instr[36] = d36; cur_instr[37] = d37; cur_instr[38] = d38; + } + + // State Save/Load + public void SyncState(Serializer ser) + { + ser.BeginSection("MC6809"); + + ser.Sync(nameof(IntEn), ref IntEn); + ser.Sync(nameof(IRQPending), ref IRQPending); + + ser.Sync(nameof(instr_pntr), ref instr_pntr); + ser.Sync(nameof(cur_instr), ref cur_instr, false); + ser.Sync(nameof(opcode_see), ref opcode_see); + ser.Sync(nameof(IRQS), ref IRQS); + ser.Sync(nameof(irq_pntr), ref irq_pntr); + + ser.Sync(nameof(Regs), ref Regs, false); + ser.Sync(nameof(RAM), ref RAM, false); + ser.Sync(nameof(F1), ref F1); + ser.Sync(nameof(T0), ref T0); + ser.Sync(nameof(T1), ref T1); + ser.Sync(nameof(TotalExecutedCycles), ref TotalExecutedCycles); + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs new file mode 100644 index 0000000000..ca157fb333 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs @@ -0,0 +1,30 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public partial class I8048 + { + private void IRQ_() + { + Regs[ADDR] = 0xFFF8; + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + RD_INC, ALU, ADDR, + RD_INC, ALU2, ADDR, + SET_ADDR, PC, ALU, ALU2); + + IRQS = 19; + } + + public bool IRQPending; + public bool IntEn; + + public Action IRQCallback = delegate () { }; + + private void ResetInterrupts() + { + IntEn = true; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs new file mode 100644 index 0000000000..d122a47d61 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs @@ -0,0 +1,113 @@ +using System; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public partial class I8048 + { + // this contains the vectors of instrcution operations + // NOTE: This list is NOT confirmed accurate for each individual cycle + public void ILLEGAL() + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + IDLE); + + IRQS = 4; + } + + public void OP_IMP(ushort oper) + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + oper); + + IRQS = 4; + } + + public void OP_R_IMP(ushort oper, ushort reg) + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + oper, reg); + + IRQS = 4; + } + + + public void OP_A_R(ushort oper, ushort reg) + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + oper,A,reg); + + IRQS = 4; + } + + public void OP_A_DIR(ushort oper) + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + RD, ALU, PC, + INC16, PC, + IDLE, + IDLE, + IDLE, + oper, A, ALU); + + IRQS = 9; + } + + public void OP_PB_DIR(ushort oper, ushort reg) + { + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + RD, ALU, PC, + INC16, PC, + IDLE, + IDLE, + IDLE, + oper, reg, ALU); + + IRQS = 9; + } + + public void OP_EXP_A(ushort oper, ushort reg) + { + // Lower 4 bits only + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + TR, ALU, A, + IDLE, + IDLE, + MSK, ALU, + IDLE, + oper, reg, ALU); + + IRQS = 9; + } + + public void CALL(ushort dest_h) + { + // Lower 4 bits only + PopulateCURINSTR(IDLE, + IDLE, + IDLE, + TR, ALU, A, + IDLE, + IDLE, + MSK, ALU, + IDLE, + ALU); + + IRQS = 9; + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Operations.cs new file mode 100644 index 0000000000..24f54091c9 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Operations.cs @@ -0,0 +1,291 @@ +using System; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public partial class I8048 + { + public void Read_Func(ushort dest, ushort src) + { + if (CDLCallback != null) + { + if (src == PC) CDLCallback(Regs[src], eCDLogMemFlags.FetchOperand); + else CDLCallback(Regs[src], eCDLogMemFlags.Data); + } + Regs[dest] = ReadMemory(Regs[src]); + } + + public void Read_Inc_Func(ushort dest, ushort src) + { + if (CDLCallback != null) + { + if (src == PC) CDLCallback(Regs[src], eCDLogMemFlags.FetchOperand); + else CDLCallback(Regs[src], eCDLogMemFlags.Data); + } + //Console.WriteLine(dest + " " + src + " " + opcode_see); + + Regs[dest] = ReadMemory(Regs[src]); + + Regs[src]++; + } + + public void Write_Func(ushort dest, ushort src) + { + if (CDLCallback != null) CDLCallback(Regs[dest], eCDLogMemFlags.Write | eCDLogMemFlags.Data); + WriteMemory(Regs[dest], (byte)Regs[src]); + } + + public void TR_Func(ushort dest, ushort src) + { + Regs[dest] = Regs[src]; + } + + public void LD_8_Func(ushort dest, ushort src) + { + Regs[dest] = Regs[src]; + } + + public void TST_Func(ushort src) + { + + } + + public void CLR_Func(ushort src) + { + Regs[src] = 0; + + FlagC = false; + } + + // source is considered a 16 bit signed value, used for long relative branch + // no flags used + public void ADD16BR_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] + (short)Regs[src]); + } + + public void ADD8BR_Func(ushort dest, ushort src) + { + if (Regs[src] > 127) { Regs[src] |= 0xFF00; } + Regs[dest] = (ushort)(Regs[dest] + (short)Regs[src]); + } + + public void ADD8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d += Regs[src]; + + FlagC = Reg16_d.Bit(8); + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += (Regs[src] & 0xF); + + Regs[dest] = ans; + } + + public void SUB8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d -= Regs[src]; + + FlagC = Reg16_d.Bit(8); + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + + Regs[dest] = ans; + } + + // same as SUB8 but result not stored + public void CMP8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d -= Regs[src]; + + FlagC = Reg16_d.Bit(8); + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + } + + public void BIT_Func(ushort dest, ushort src) + { + ushort ans = (ushort)(Regs[dest] & Regs[src]); + } + + public void ASL_Func(ushort src) + { + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)((Regs[src] << 1) & 0xFF); + } + + public void ASR_Func(ushort src) + { + FlagC = Regs[src].Bit(0); + + ushort temp = (ushort)(Regs[src] & 0x80); // MSB doesn't change in this operation + + Regs[src] = (ushort)((Regs[src] >> 1) | temp); + } + + public void LSR_Func(ushort src) + { + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)(Regs[src] >> 1); + } + + public void COM_Func(ushort src) + { + Regs[src] = (ushort)((~Regs[src]) & 0xFF); + + FlagC = true; + } + + public void AND8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] & Regs[src]); + } + + public void OR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] | Regs[src]); + } + + public void XOR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] ^ Regs[src]); + } + + public void ROR_Func(ushort src) + { + ushort c = (ushort)(FlagC ? 0x80 : 0); + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)(c | (Regs[src] >> 1)); + } + + public void ROL_Func(ushort src) + { + ushort c = (ushort)(FlagC ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + } + + public void RRC_Func(ushort src) + { + ushort c = (ushort)(FlagC ? 0x80 : 0); + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)(c | (Regs[src] >> 1)); + } + + public void RLC_Func(ushort src) + { + ushort c = (ushort)(FlagC ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + } + + public void INC8_Func(ushort src) + { + Regs[src] = (ushort)((Regs[src] + 1) & 0xFF); + } + + public void DEC8_Func(ushort src) + { + Regs[src] = (ushort)((Regs[src] - 1) & 0xFF); + } + + public void INC16_Func(ushort src) + { + Regs[src] += 1; + } + + public void DEC16_Func(ushort src) + { + Regs[src] -= 1; + } + + public void ADC8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + int c = FlagC ? 1 : 0; + + Reg16_d += (Regs[src] + c); + + FlagC = Reg16_d.Bit(8); + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += ((Regs[src] & 0xF) + c); + + Regs[dest] = ans; + } + + public void DA_Func(ushort src) + { + int a = Regs[src]; + + byte CF = 0; + if (FlagC || ((a & 0xF) > 9)) + { + CF = 6; + } + if (FlagC || (((a >> 4) & 0xF) > 9) || ((((a >> 4) & 0xF) > 8) && ((a & 0xF) > 9))) + { + CF |= (byte)(6 << 4); + } + + a += CF; + + if ((a > 0xFF) || FlagC) + { + FlagC = true; + } + else + { + FlagC = false; + } + Regs[src] = (byte)a; + } + + public void CMP16_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + int Reg16_s = Regs[src]; + + Reg16_d -= Reg16_s; + + FlagC = Reg16_d.Bit(16); + + ushort ans = (ushort)(Reg16_d & 0xFFFF); + } + + public void EXG_Func(ushort sel) + { + + } + + public void TFR_Func(ushort sel) + { + + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/Intel8048/ReadMe.txt new file mode 100644 index 0000000000..115b568cb8 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/ReadMe.txt @@ -0,0 +1 @@ +TODO: STOP for second byte nonzero diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs new file mode 100644 index 0000000000..7db008df57 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs @@ -0,0 +1,86 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.I8048 +{ + public partial class I8048 + { + // registers + public ushort[] Regs = new ushort[21]; + + // 64 bytes of onboard ram + public ushort[] RAM = new ushort[64]; + + // The 8048 has 2 flags that can be used for conditionals + // F0 is on the PSW, F1 is seperate + public bool F1; + + // The 8048 has 2 test lines which can be used for conditionals, T0 can be used as an output + public bool T0, T1; + + public const ushort PC = 0; + public const ushort PSW = 1; + public const ushort BUS = 2; + public const ushort A = 3; + public const ushort R0 = 4; + public const ushort R1 = 5; + public const ushort R2 = 6; + public const ushort R3 = 7; + public const ushort R4 = 8; + public const ushort R5 = 9; + public const ushort R6 = 10; + public const ushort R7 = 11; + public const ushort ADDR = 12; // internal + public const ushort ALU = 13; // internal + public const ushort ALU2 = 14; // internal + public const ushort P1 = 15; + public const ushort P2 = 16; + public const ushort P4 = 17; + public const ushort P5 = 18; + public const ushort P6 = 19; + public const ushort P7 = 20; + + public bool Flag3 + { + get { return (Regs[PSW] & 0x08) != 0; } + set { Regs[PSW] = (byte)((Regs[PSW] & ~0x08) | 0x08); } + } + + public bool FlagBS + { + get { return (Regs[PSW] & 0x10) != 0; } + set { Regs[PSW] = (byte)((Regs[PSW] & ~0x10) | (value ? 0x10 : 0x00)); } + } + + public bool FlagF0 + { + get { return (Regs[PSW] & 0x20) != 0; } + set { Regs[PSW] = (byte)((Regs[PSW] & ~0x20) | (value ? 0x20 : 0x00)); } + } + + public bool FlagAC + { + get { return (Regs[PSW] & 0x40) != 0; } + set { Regs[PSW] = (byte)((Regs[PSW] & ~0x40) | (value ? 0x40 : 0x00)); } + } + + public bool FlagC + { + get { return (Regs[PSW] & 0x80) != 0; } + set { Regs[PSW] = (byte)((Regs[PSW] & ~0x80) | (value ? 0x80 : 0x00)); } + } + + private void ResetRegisters() + { + for (int i = 0; i < 21; i++) + { + Regs[i] = 0; + } + + F1 = false; + + T0 = T1 = false; + + Flag3 = true; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/MC6800/MC6800.cs b/BizHawk.Emulation.Cores/CPUs/MC6800/MC6800.cs index 69dc1b2339..84b29f164f 100644 --- a/BizHawk.Emulation.Cores/CPUs/MC6800/MC6800.cs +++ b/BizHawk.Emulation.Cores/CPUs/MC6800/MC6800.cs @@ -473,16 +473,13 @@ namespace BizHawk.Emulation.Common.Components.MC6800 // then regular IRQ else if (IRQPending && !FlagI) { - if (!FlagI) - { - IRQPending = false; + IRQPending = false; - if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } - IRQ_(); - IRQCallback(); - instr_pntr = irq_pntr = 0; - } + IRQ_(); + IRQCallback(); + instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else diff --git a/BizHawk.Emulation.Cores/CPUs/MC6809/MC6809.cs b/BizHawk.Emulation.Cores/CPUs/MC6809/MC6809.cs index 14dfc124f4..4551374220 100644 --- a/BizHawk.Emulation.Cores/CPUs/MC6809/MC6809.cs +++ b/BizHawk.Emulation.Cores/CPUs/MC6809/MC6809.cs @@ -503,16 +503,85 @@ namespace BizHawk.Emulation.Common.Components.MC6809 else { PopulateCURINSTR(CWAI); - irq_pntr = 0; + irq_pntr = instr_pntr = 0; IRQS = -1; } instr_pntr = 0; break; case SYNC: - IN_SYNC = true; - IRQS = 1; - instr_pntr = irq_pntr = 0; - PopulateCURINSTR(SYNC); + if (NMIPending) + { + NMIPending = false; + IN_SYNC = false; + + Regs[ADDR] = 0xFFFC; + PopulateCURINSTR(RD_INC, ALU, ADDR, + RD_INC, ALU2, ADDR, + SET_ADDR, PC, ALU, ALU2); + irq_pntr = -1; + IRQS = 3; + + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC NMI====", RegisterInfo = "" }); } + } + else if (FIRQPending) + { + if (!FlagF) + { + FIRQPending = false; + + Regs[ADDR] = 0xFFF6; + PopulateCURINSTR(RD_INC, ALU, ADDR, + RD_INC, ALU2, ADDR, + SET_ADDR, PC, ALU, ALU2); + irq_pntr = -1; + IRQS = 3; + + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC FIRQ====", RegisterInfo = "" }); } + } + else + { + FIRQPending = false; + IN_SYNC = false; + IRQS = 2; + instr_pntr = irq_pntr = 0; + PopulateCURINSTR(IDLE, + IDLE); + } + } + else if (IRQPending) + { + if (!FlagI) + { + IRQPending = false; + IN_SYNC = false; + + Regs[ADDR] = 0xFFF8; + PopulateCURINSTR(RD_INC, ALU, ADDR, + RD_INC, ALU2, ADDR, + SET_ADDR, PC, ALU, ALU2); + irq_pntr = -1; + IRQS = 3; + + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC IRQ====", RegisterInfo = "" }); } + } + else + { + FIRQPending = false; + IN_SYNC = false; + IRQS = 2; + instr_pntr = irq_pntr = 0; + PopulateCURINSTR(IDLE, + IDLE); + } + } + else + { + IN_SYNC = true; + IRQS = -1; + instr_pntr = irq_pntr = 0; + PopulateCURINSTR(SYNC); + } + break; } @@ -525,62 +594,31 @@ namespace BizHawk.Emulation.Common.Components.MC6809 if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====NMI====", RegisterInfo = "" }); } - IN_SYNC = false; NMI_(); NMICallback(); instr_pntr = irq_pntr = 0; } // fast IRQ has next priority - else if (FIRQPending) + else if (FIRQPending && !FlagF) { - if (!FlagF) - { - FIRQPending = false; + FIRQPending = false; - if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====FIRQ====", RegisterInfo = "" }); } + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====FIRQ====", RegisterInfo = "" }); } - IN_SYNC = false; - FIRQ_(); - FIRQCallback(); - instr_pntr = irq_pntr = 0; - } - else if (IN_SYNC) - { - FIRQPending = false; - - if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC====", RegisterInfo = "" }); } - - IN_SYNC = false; - IRQS = 1; - instr_pntr = irq_pntr = 0; - PopulateCURINSTR(IDLE); - } + FIRQ_(); + FIRQCallback(); + instr_pntr = irq_pntr = 0; } // then regular IRQ else if (IRQPending && !FlagI) { - if (!FlagI) - { - IRQPending = false; + IRQPending = false; - if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } + if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====IRQ====", RegisterInfo = "" }); } - IN_SYNC = false; - IRQ_(); - IRQCallback(); - instr_pntr = irq_pntr = 0; - } - else if (IN_SYNC) - { - IRQPending = false; - - if (TraceCallback != null) { TraceCallback(new TraceInfo { Disassembly = "====SYNC====", RegisterInfo = "" }); } - - IN_SYNC = false; - IRQS = 1; - instr_pntr = irq_pntr = 0; - PopulateCURINSTR(IDLE); - } + IRQ_(); + IRQCallback(); + instr_pntr = irq_pntr = 0; } // otherwise start the next instruction else diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs index eabaf20fdc..7751a535f5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs @@ -7,7 +7,10 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 { public partial class Atari2600 : IStatable { - public bool BinarySaveStatesPreferred => false; + public bool BinarySaveStatesPreferred + { + get { return true; } + } public void SaveStateText(TextWriter writer) { diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs new file mode 100644 index 0000000000..9a459bf8f5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs @@ -0,0 +1,220 @@ +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + // Audio Emulation + public class Audio : ISoundProvider + { + public O2Hawk Core { get; set; } + + private BlipBuffer _blip_L = new BlipBuffer(15000); + private BlipBuffer _blip_R = new BlipBuffer(15000); + + public const int NR10 = 0; + public const int NR11 = 1; + public const int NR12 = 2; + public const int NR13 = 3; + public const int NR14 = 4; + public const int NR21 = 5; + public const int NR22 = 6; + public const int NR23 = 7; + public const int NR24 = 8; + public const int NR30 = 9; + public const int NR31 = 10; + public const int NR32 = 11; + public const int NR33 = 12; + public const int NR34 = 13; + public const int NR41 = 14; + public const int NR42 = 15; + public const int NR43 = 16; + public const int NR44 = 17; + public const int NR50 = 18; + public const int NR51 = 19; + public const int NR52 = 20; + + + public byte[] Audio_Regs = new byte[21]; + + // Contol Variables + public bool AUD_CTRL_vin_L_en; + public bool AUD_CTRL_vin_R_en; + public bool AUD_CTRL_sq1_L_en; + public bool AUD_CTRL_sq2_L_en; + public bool AUD_CTRL_wave_L_en; + public bool AUD_CTRL_noise_L_en; + public bool AUD_CTRL_sq1_R_en; + public bool AUD_CTRL_sq2_R_en; + public bool AUD_CTRL_wave_R_en; + public bool AUD_CTRL_noise_R_en; + public bool AUD_CTRL_power; + public byte AUD_CTRL_vol_L; + public byte AUD_CTRL_vol_R; + + public byte sample; + + public uint master_audio_clock; + + public int latched_sample_L, latched_sample_R; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF10: ret = (byte)(Audio_Regs[NR10]); break; // NR10 (sweep) + case 0xFF11: ret = (byte)(Audio_Regs[NR11]); break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: ret = (byte)(Audio_Regs[NR12]); break; // NR12 (envelope) + case 0xFF13: ret = (byte)(Audio_Regs[NR13]); break; // NR13 (freq low) + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + + } + + public void tick() + { + + // add up components to each channel + int L_final = 0; + int R_final = 0; + + if (AUD_CTRL_sq1_L_en) { L_final += 0; } + + + if (AUD_CTRL_sq1_R_en) { R_final += 0; } + + L_final *= (AUD_CTRL_vol_L + 1) * 40; + R_final *= (AUD_CTRL_vol_R + 1) * 40; + + if (L_final != latched_sample_L) + { + _blip_L.AddDelta(master_audio_clock, L_final - latched_sample_L); + latched_sample_L = L_final; + } + + if (R_final != latched_sample_R) + { + _blip_R.AddDelta(master_audio_clock, R_final - latched_sample_R); + latched_sample_R = R_final; + } + + master_audio_clock++; + } + + public void power_off() + { + for (int i = 0; i < 0x16; i++) + { + WriteReg(0xFF10 + i, 0); + } + } + + public void Reset() + { + Audio_Regs = new byte[21]; + + master_audio_clock = 0; + + sample = 0; + + _blip_L.SetRates(4194304, 44100); + _blip_R.SetRates(4194304, 44100); + } + + public void SyncState(Serializer ser) + { + ser.Sync(nameof(Audio_Regs), ref Audio_Regs, false); + + ser.Sync(nameof(master_audio_clock), ref master_audio_clock); + + ser.Sync(nameof(sample), ref sample); + ser.Sync(nameof(latched_sample_L), ref latched_sample_L); + ser.Sync(nameof(latched_sample_R), ref latched_sample_R); + + ser.Sync(nameof(AUD_CTRL_vin_L_en), ref AUD_CTRL_vin_L_en); + ser.Sync(nameof(AUD_CTRL_vin_R_en), ref AUD_CTRL_vin_R_en); + ser.Sync(nameof(AUD_CTRL_sq1_L_en), ref AUD_CTRL_sq1_L_en); + ser.Sync(nameof(AUD_CTRL_sq2_L_en), ref AUD_CTRL_sq2_L_en); + ser.Sync(nameof(AUD_CTRL_wave_L_en), ref AUD_CTRL_wave_L_en); + ser.Sync(nameof(AUD_CTRL_noise_L_en), ref AUD_CTRL_noise_L_en); + ser.Sync(nameof(AUD_CTRL_sq1_R_en), ref AUD_CTRL_sq1_R_en); + ser.Sync(nameof(AUD_CTRL_sq2_R_en), ref AUD_CTRL_sq2_R_en); + ser.Sync(nameof(AUD_CTRL_wave_R_en), ref AUD_CTRL_wave_R_en); + ser.Sync(nameof(AUD_CTRL_noise_R_en), ref AUD_CTRL_noise_R_en); + ser.Sync(nameof(AUD_CTRL_power), ref AUD_CTRL_power); + ser.Sync(nameof(AUD_CTRL_vol_L), ref AUD_CTRL_vol_L); + ser.Sync(nameof(AUD_CTRL_vol_R), ref AUD_CTRL_vol_R); + } + + #region audio + + public bool CanProvideAsync => false; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + { + throw new InvalidOperationException("Only Sync mode is supported_"); + } + } + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + _blip_L.EndFrame(master_audio_clock); + _blip_R.EndFrame(master_audio_clock); + + nsamp = _blip_L.SamplesAvailable(); + + samples = new short[nsamp * 2]; + + if (nsamp != 0) + { + _blip_L.ReadSamplesLeft(samples, nsamp); + _blip_R.ReadSamplesRight(samples, nsamp); + } + + master_audio_clock = 0; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + _blip_L.Clear(); + _blip_R.Clear(); + master_audio_clock = 0; + } + + private void GetSamples(short[] samples) + { + + } + + public void DisposeSound() + { + _blip_L.Clear(); + _blip_R.Clear(); + _blip_L.Dispose(); + _blip_R.Dispose(); + _blip_L = null; + _blip_R = null; + } + + #endregion + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/HW_Registers.cs new file mode 100644 index 0000000000..3181a60936 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/HW_Registers.cs @@ -0,0 +1,291 @@ +using System; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk + { + public byte Read_Registers(int addr) + { + byte ret = 0; + + switch (addr) + { + // Read Input + case 0xFF00: + _islag = false; + ret = input_register; + break; + + // Serial data port + case 0xFF01: + ret = serialport.ReadReg(addr); + break; + + // Serial port control + case 0xFF02: + ret = serialport.ReadReg(addr); + break; + + // Interrupt flags + case 0xFF0F: + + break; + + // audio regs + case 0xFF10: + case 0xFF11: + case 0xFF12: + case 0xFF13: + case 0xFF14: + case 0xFF16: + case 0xFF17: + case 0xFF18: + case 0xFF19: + case 0xFF1A: + case 0xFF1B: + case 0xFF1C: + case 0xFF1D: + case 0xFF1E: + case 0xFF20: + case 0xFF21: + case 0xFF22: + case 0xFF23: + case 0xFF24: + case 0xFF25: + case 0xFF26: + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + ret = audio.ReadReg(addr); + break; + + // PPU Regs + case 0xFF40: + case 0xFF41: + case 0xFF42: + case 0xFF43: + case 0xFF44: + case 0xFF45: + case 0xFF46: + case 0xFF47: + case 0xFF48: + case 0xFF49: + case 0xFF4A: + case 0xFF4B: + ret = ppu.ReadReg(addr); + break; + + // Speed Control for GBC + case 0xFF4D: + + break; + + case 0xFF4F: // VBK + + break; + + // Bios control register. Not sure if it is readable + case 0xFF50: + ret = 0xFF; + break; + + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + + break; + + case 0xFF56: + + break; + + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + + break; + + // Speed Control for GBC + case 0xFF70: + + break; + + case 0xFF6C: + case 0xFF72: + case 0xFF73: + case 0xFF74: + case 0xFF75: + case 0xFF76: + case 0xFF77: + + break; + + // interrupt control register + case 0xFFFF: + + break; + + default: + ret = 0xFF; + break; + + } + return ret; + } + + public void Write_Registers(int addr, byte value) + { + switch (addr) + { + // select input + case 0xFF00: + + break; + + // Serial data port + case 0xFF01: + serialport.WriteReg(addr, value); + break; + + // Serial port control + case 0xFF02: + serialport.WriteReg(addr, value); + break; + + // Interrupt flags + case 0xFF0F: + + break; + + // audio regs + case 0xFF10: + case 0xFF11: + case 0xFF12: + case 0xFF13: + case 0xFF14: + case 0xFF16: + case 0xFF17: + case 0xFF18: + case 0xFF19: + case 0xFF1A: + case 0xFF1B: + case 0xFF1C: + case 0xFF1D: + case 0xFF1E: + case 0xFF20: + case 0xFF21: + case 0xFF22: + case 0xFF23: + case 0xFF24: + case 0xFF25: + case 0xFF26: + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + audio.WriteReg(addr, value); + break; + + // PPU Regs + case 0xFF40: + case 0xFF41: + case 0xFF42: + case 0xFF43: + case 0xFF44: + case 0xFF45: + case 0xFF46: + case 0xFF47: + case 0xFF48: + case 0xFF49: + case 0xFF4A: + case 0xFF4B: + ppu.WriteReg(addr, value); + break; + + // GBC compatibility register (I think) + case 0xFF4C: + + break; + + // Speed Control for GBC + case 0xFF4D: + + break; + + // VBK + case 0xFF4F: + + break; + + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs + case 0xFF50: + + break; + + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + + break; + + case 0xFF56: + + break; + + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + //if (GBC_compat) + //{ + ppu.WriteReg(addr, value); + //} + break; + + default: + Console.Write(addr); + Console.Write(" "); + Console.WriteLine(value); + break; + } + } + + public void Register_Reset() + { + input_register = 0xCF; // not reading any input + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/MapperBase.cs new file mode 100644 index 0000000000..0094f1d4da --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/MapperBase.cs @@ -0,0 +1,64 @@ +using BizHawk.Common; +using System; + +using BizHawk.Emulation.Common.Components.I8048; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public class MapperBase + { + public O2Hawk Core { get; set; } + + public virtual byte ReadMemory(ushort addr) + { + return 0; + } + + public virtual byte PeekMemory(ushort addr) + { + return 0; + } + + public virtual void WriteMemory(ushort addr, byte value) + { + } + + public virtual void PokeMemory(ushort addr, byte value) + { + } + + public virtual void SyncState(Serializer ser) + { + } + + public virtual void Dispose() + { + } + + public virtual void Initialize() + { + } + + public virtual void Mapper_Tick() + { + } + + public virtual void RTC_Get(int value, int index) + { + } + + public virtual void MapCDL(ushort addr, I8048.eCDLogMemFlags flags) + { + } + + protected void SetCDLROM(I8048.eCDLogMemFlags flags, int cdladdr) + { + Core.SetCDL(flags, "ROM", cdladdr); + } + + protected void SetCDLRAM(I8048.eCDLogMemFlags flags, int cdladdr) + { + Core.SetCDL(flags, "CartRAM", cdladdr); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/Mapper_Default.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/Mapper_Default.cs new file mode 100644 index 0000000000..657c02779f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/Mapper_Default.cs @@ -0,0 +1,80 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +using BizHawk.Emulation.Common.Components.I8048; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + // Default mapper with no bank switching + public class MapperDefault : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override void MapCDL(ushort addr, I8048.eCDLogMemFlags flags) + { + if (addr < 0x8000) + { + SetCDLROM(flags, addr); + } + else + { + if (Core.cart_RAM != null) + { + SetCDLRAM(flags, addr - 0xA000); + } + else + { + return; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/ReadMe.txt new file mode 100644 index 0000000000..0c94c8272c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Mappers/ReadMe.txt @@ -0,0 +1,3 @@ +TODO: +Official Mappers +Unofficial Mappers diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs new file mode 100644 index 0000000000..ac20bf4d07 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs @@ -0,0 +1,59 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +/* + $0400-$0FFF Cartridge (Only 2K accessible, bit 10 not mapped to cart) + $0000-$03FF BIOS +*/ + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk + { + public byte ReadMemory(ushort addr) + { + uint flags = (uint)(MemoryCallbackFlags.AccessRead); + MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus"); + addr_access = addr; + + if (addr < 0x400) + { + return _bios[addr]; + } + else + { + return mapper.ReadMemory(addr); + } + } + + public void WriteMemory(ushort addr, byte value) + { + uint flags = (uint)(MemoryCallbackFlags.AccessWrite); + MemoryCallbacks.CallMemoryCallbacks(addr, value, flags, "System Bus"); + addr_access = addr; + + if (addr < 0x400) + { + + } + else + { + mapper.WriteMemory(addr, value); + } + } + + public byte PeekMemory(ushort addr) + { + if (addr < 0x400) + { + return _bios[addr]; + } + else + { + return mapper.PeekMemory(addr); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ICodeDataLog.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ICodeDataLog.cs new file mode 100644 index 0000000000..ac77428216 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ICodeDataLog.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.I8048; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : ICodeDataLogger + { + private ICodeDataLog _cdl; + + public void SetCDL(ICodeDataLog cdl) + { + _cdl = cdl; + if (cdl == null) + this.cpu.CDLCallback = null; + else this.cpu.CDLCallback = CDLCpuCallback; + } + + public void NewCDL(ICodeDataLog cdl) + { + cdl["ROM"] = new byte[MemoryDomains["ROM"].Size]; + cdl["HRAM"] = new byte[MemoryDomains["Zero Page RAM"].Size]; + + cdl["WRAM"] = new byte[MemoryDomains["Main RAM"].Size]; + + if (MemoryDomains.Has("Cart RAM")) + { + cdl["CartRAM"] = new byte[MemoryDomains["Cart RAM"].Size]; + } + + cdl.SubType = "O2"; + cdl.SubVer = 0; + } + + [FeatureNotImplemented] + void ICodeDataLogger.DisassembleCDL(Stream s, ICodeDataLog cdl) + { + } + + public void SetCDL(I8048.eCDLogMemFlags flags, string type, int cdladdr) + { + if (type == null) return; + byte val = (byte)flags; + _cdl[type][cdladdr] |= (byte)flags; + } + + void CDLCpuCallback(ushort addr, I8048.eCDLogMemFlags flags) + { + + if (addr < 0x400) + { + + } + else + { + mapper.MapCDL(addr, flags); + return; + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IDebuggable.cs new file mode 100644 index 0000000000..45f9f3e543 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + return new Dictionary + { + /* + ["A"] = cpu.A, + ["X"] = cpu.X, + ["Y"] = cpu.Y, + ["S"] = cpu.S, + ["PC"] = cpu.PC, + ["Flag C"] = cpu.FlagC, + ["Flag Z"] = cpu.FlagZ, + ["Flag I"] = cpu.FlagI, + ["Flag D"] = cpu.FlagD, + ["Flag B"] = cpu.FlagB, + ["Flag V"] = cpu.FlagV, + ["Flag N"] = cpu.FlagN, + ["Flag T"] = cpu.FlagT + */ + }; + } + + public void SetCpuRegister(string register, int value) + { + switch (register) + { + default: + throw new InvalidOperationException(); + case "A": + //cpu.A = (byte)value; + break; + case "X": + //cpu.X = (byte)value; + break; + case "Y": + //cpu.Y = (byte)value; + break; + case "S": + //cpu.S = (byte)value; + break; + case "PC": + //cpu.PC = (ushort)value; + break; + case "Flag I": + //cpu.FlagI = value > 0; + break; + } + } + + public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" }); + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public long TotalExecutedCycles + { + get { return (long)cpu.TotalExecutedCycles; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs new file mode 100644 index 0000000000..c9452702a4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs @@ -0,0 +1,197 @@ +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : IEmulator, IVideoProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public byte controller_state; + public ushort Acc_X_state; + public ushort Acc_Y_state; + public bool in_vblank_old; + public bool in_vblank; + public bool vblank_rise; + + public bool FrameAdvance(IController controller, bool render, bool rendersound) + { + //Console.WriteLine("-----------------------FRAME-----------------------"); + + if (_tracer.Enabled) + { + cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + cpu.TraceCallback = null; + } + + _frame++; + + if (controller.IsPressed("Power")) + { + HardReset(); + } + + _islag = true; + + do_frame(controller); + + if (_islag) + { + _lagcount++; + } + + return true; + } + + public void do_frame(IController controller) + { + for (int i = 0; i < 70224; i++) + { + audio.tick(); + ppu.tick(); + ppu.DMA_tick(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(); + + if (in_vblank && !in_vblank_old) + { + // update the controller state on VBlank + GetControllerState(controller); + + // check if controller state caused interrupt + do_controller_check(); + + // send the image on VBlank + SendVideoBuffer(); + } + + in_vblank_old = in_vblank; + } + + // turn off the screen so the image doesnt persist + // but dont turn off blank_frame yet, it still needs to be true until the next VBL + // this doesn't run for GBC, some games, ex MIB the series 2, rely on the screens persistence while off to make video look smooth. + // But some GB gams, ex Battletoads, turn off the screen for a long time from the middle of the frame, so need to be cleared. + if (ppu.clear_screen) + { + for (int j = 0; j < frame_buffer.Length; j++) { frame_buffer[j] = (int)color_palette[0]; } + ppu.clear_screen = false; + } + } + + public void do_single_step() + { + audio.tick(); + ppu.tick(); + ppu.DMA_tick(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(); + } + + public void do_controller_check() + { + // check if new input changed the input register and triggered IRQ + byte contr_prev = input_register; + + input_register &= 0xF0; + if ((input_register & 0x30) == 0x20) + { + input_register |= (byte)(controller_state & 0xF); + } + else if ((input_register & 0x30) == 0x10) + { + input_register |= (byte)((controller_state & 0xF0) >> 4); + } + else if ((input_register & 0x30) == 0x00) + { + // if both polls are set, then a bit is zero if either or both pins are zero + byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); + input_register |= temp; + } + else + { + input_register |= 0xF; + } + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + controller_state = _controllerDeck.ReadPort1(controller); + + Acc_X_state = _controllerDeck.ReadAccX1(controller); + Acc_Y_state = _controllerDeck.ReadAccY1(controller); + } + + public int Frame => _frame; + + public string SystemId => "O2"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + audio.DisposeSound(); + } + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer; + + public int[] frame_buffer; + + public int[] GetVideoBuffer() + { + return frame_buffer; + } + + public void SendVideoBuffer() + { + if (ppu.blank_frame) + { + for (int i = 0; i < _vidbuffer.Length; i++) + { + _vidbuffer[i] = (int)color_palette[0]; + } + } + + for (int j = 0; j < frame_buffer.Length; j++) { frame_buffer[j] = _vidbuffer[j]; } + + ppu.blank_frame = false; + } + + public int VirtualWidth => 160; + public int VirtualHeight => 144; + public int BufferWidth => 160; + public int BufferHeight => 144; + public int BackgroundColor => unchecked((int)0xFF000000); + public int VsyncNumerator => _frameHz; + public int VsyncDenominator => 1; + + public static readonly uint[] color_palette_BW = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; + public static readonly uint[] color_palette_Gr = { 0xFFA4C505, 0xFF88A905, 0xFF1D551D, 0xFF052505 }; + + public uint[] color_palette = new uint[4]; + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IInputPollable.cs new file mode 100644 index 0000000000..252cf6418d --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : IInputPollable + { + public int LagCount + { + get { return _lagcount; } + set { _lagcount = value; } + } + + public bool IsLagFrame + { + get { return _islag; } + set { _islag = value; } + } + + public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); + + public bool _islag = true; + private int _lagcount; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IMemoryDomains.cs new file mode 100644 index 0000000000..24f66c0ec1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IMemoryDomains.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk + { + private IMemoryDomains MemoryDomains; + + public void SetupMemoryDomains() + { + var domains = new List + { + new MemoryDomainDelegate( + "Main RAM", + RAM.Length, + MemoryDomain.Endian.Little, + addr => RAM[addr], + (addr, value) => RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "System Bus", + 0X1000, + MemoryDomain.Endian.Little, + addr => PeekSystemBus(addr), + (addr, value) => PokeSystemBus(addr, value), + 1), + new MemoryDomainDelegate( + "ROM", + _rom.Length, + MemoryDomain.Endian.Little, + addr => _rom[addr], + (addr, value) => _rom[addr] = value, + 1), + new MemoryDomainDelegate( + "VRAM", + VRAM.Length, + MemoryDomain.Endian.Little, + addr => VRAM[addr], + (addr, value) => VRAM[addr] = value, + 1) + }; + + if (cart_RAM != null) + { + var CartRam = new MemoryDomainByteArray("Cart RAM", MemoryDomain.Endian.Little, cart_RAM, true, 1); + domains.Add(CartRam); + } + + MemoryDomains = new MemoryDomainList(domains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + + private byte PeekSystemBus(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFF); + return PeekMemory(addr2); + } + + private void PokeSystemBus(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFF); + WriteMemory(addr2, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs new file mode 100644 index 0000000000..130f51b497 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISaveRam.cs @@ -0,0 +1,37 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : ISaveRam + { + public byte[] CloneSaveRam() + { + if (cart_RAM != null) + { + return (byte[])cart_RAM.Clone(); + } + else + { + return null; + } + } + + public void StoreSaveRam(byte[] data) + { + if (_syncSettings.Use_SRAM) + { + Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length); + Console.WriteLine("loading SRAM here"); + } + } + + public bool SaveRamModified + { + get + { + return has_bat & _syncSettings.Use_SRAM; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISettable.cs new file mode 100644 index 0000000000..11025b254a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.ISettable.cs @@ -0,0 +1,92 @@ +using System; +using System.ComponentModel; + +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : IEmulator, IStatable, ISettable + { + public O2Settings GetSettings() + { + return _settings.Clone(); + } + + public O2SyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(O2Settings o) + { + _settings = o; + return false; + } + + public bool PutSyncSettings(O2SyncSettings o) + { + bool ret = O2SyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private O2Settings _settings = new O2Settings(); + public O2SyncSettings _syncSettings = new O2SyncSettings(); + + public class O2Settings + { + public O2Settings Clone() + { + return (O2Settings)MemberwiseClone(); + } + } + + public class O2SyncSettings + { + [JsonIgnore] + public string Port1 = O2HawkControllerDeck.DefaultControllerName; + + public enum ControllerType + { + Default, + Tilt + } + + [JsonIgnore] + private ControllerType _O2Controller; + + [DisplayName("Controller")] + [Description("Select Controller Type")] + [DefaultValue(ControllerType.Default)] + public ControllerType O2Controller + { + get { return _O2Controller; } + set + { + if (value == ControllerType.Default) { Port1 = O2HawkControllerDeck.DefaultControllerName; } + else { Port1 = "Gameboy Controller + Tilt"; } + + _O2Controller = value; + } + } + + [DisplayName("Use Existing SaveRAM")] + [Description("When true, existing SaveRAM will be loaded at boot up")] + [DefaultValue(false)] + public bool Use_SRAM { get; set; } + + public O2SyncSettings Clone() + { + return (O2SyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(O2SyncSettings x, O2SyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs new file mode 100644 index 0000000000..5d1f0b840f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs @@ -0,0 +1,91 @@ +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public partial class O2Hawk : IStatable + { + public bool BinarySaveStatesPreferred => true; + + public void SaveStateText(TextWriter writer) + { + SyncState(new Serializer(writer)); + } + + public void LoadStateText(TextReader reader) + { + SyncState(new Serializer(reader)); + } + + public void SaveStateBinary(BinaryWriter bw) + { + SyncState(new Serializer(bw)); + } + + public void LoadStateBinary(BinaryReader br) + { + SyncState(new Serializer(br)); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + return ms.ToArray(); + } + + private void SyncState(Serializer ser) + { + byte[] core = null; + if (ser.IsWriter) + { + var ms = new MemoryStream(); + ms.Close(); + core = ms.ToArray(); + } + cpu.SyncState(ser); + mapper.SyncState(ser); + ppu.SyncState(ser); + serialport.SyncState(ser); + audio.SyncState(ser); + + ser.BeginSection("Odyssey2"); + ser.Sync(nameof(core), ref core, false); + ser.Sync("Lag", ref _lagcount); + ser.Sync("Frame", ref _frame); + ser.Sync("IsLag", ref _islag); + _controllerDeck.SyncState(ser); + + ser.Sync(nameof(controller_state), ref controller_state); + ser.Sync(nameof(Acc_X_state), ref Acc_X_state); + ser.Sync(nameof(Acc_Y_state), ref Acc_Y_state); + ser.Sync(nameof(in_vblank), ref in_vblank); + ser.Sync(nameof(in_vblank_old), ref in_vblank_old); + ser.Sync(nameof(vblank_rise), ref vblank_rise); + ser.Sync(nameof(input_register), ref input_register); + + // memory domains + ser.Sync(nameof(RAM), ref RAM, false); + ser.Sync(nameof(VRAM), ref VRAM, false); + ser.Sync(nameof(OAM), ref OAM, false); + ser.Sync(nameof(_bios), ref _bios, false); + ser.Sync(nameof(RAM_Bank), ref RAM_Bank); + ser.Sync(nameof(addr_access), ref addr_access); + + ser.Sync(nameof(frame_buffer), ref frame_buffer, false); + ser.Sync(nameof(_vidbuffer), ref _vidbuffer, false); + + // probably a better way to do this + if (cart_RAM != null) + { + ser.Sync(nameof(cart_RAM), ref cart_RAM, false); + } + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs new file mode 100644 index 0000000000..39a4593eb3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs @@ -0,0 +1,144 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.I8048; + +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + [Core( + "O2Hawk", + "", + isPorted: false, + isReleased: false)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class O2Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, ISettable + { + public byte input_register; + + // memory domains + public byte[] RAM = new byte[0x80]; + + public byte[] VRAM = new byte[0x4000]; + public byte[] OAM = new byte[0xA0]; + + public int RAM_Bank; + + public byte[] _bios; + public readonly byte[] _rom; + public readonly byte[] header = new byte[0x50]; + + public byte[] cart_RAM; + public bool has_bat; + + private int _frame = 0; + + public ushort addr_access; + + public MapperBase mapper; + + private readonly ITraceable _tracer; + + public I8048 cpu; + public PPU ppu; + public Audio audio; + public SerialPort serialport; + + [CoreConstructor("O2")] + public O2Hawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + cpu = new I8048 + { + ReadMemory = ReadMemory, + WriteMemory = WriteMemory, + PeekMemory = PeekMemory, + DummyReadMemory = ReadMemory, + OnExecFetch = ExecFetch, + }; + + audio = new Audio(); + serialport = new SerialPort(); + + CoreComm = comm; + + _settings = (O2Settings)settings ?? new O2Settings(); + _syncSettings = (O2SyncSettings)syncSettings ?? new O2SyncSettings(); + _controllerDeck = new O2HawkControllerDeck(_syncSettings.Port1); + + byte[] Bios = null; + + Bios = comm.CoreFileProvider.GetFirmware("O2", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new PPU(); + + if (Bios == null) + { + throw new MissingFirmwareException("Missing Odyssey2 Bios"); + } + + _bios = Bios; + + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); + + Console.WriteLine("MD5: " + rom.HashMD5(0, rom.Length)); + Console.WriteLine("SHA1: " + rom.HashSHA1(0, rom.Length)); + _rom = rom; + Setup_Mapper(); + + _frameHz = 60; + + audio.Core = this; + ppu.Core = this; + serialport.Core = this; + + ser.Register(this); + ser.Register(audio); + ServiceProvider = ser; + + _settings = (O2Settings)settings ?? new O2Settings(); + _syncSettings = (O2SyncSettings)syncSettings ?? new O2SyncSettings(); + + _tracer = new TraceBuffer { Header = cpu.TraceHeader }; + ser.Register(_tracer); + + SetupMemoryDomains(); + HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + private readonly O2HawkControllerDeck _controllerDeck; + + public void HardReset() + { + in_vblank = true; // we start off in vblank since the LCD is off + in_vblank_old = true; + + RAM_Bank = 1; // RAM bank always starts as 1 (even writing zero still sets 1) + + Register_Reset(); + ppu.Reset(); + audio.Reset(); + serialport.Reset(); + + cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory); + + _vidbuffer = new int[VirtualWidth * VirtualHeight]; + frame_buffer = new int[VirtualWidth * VirtualHeight]; + } + + private void ExecFetch(ushort addr) + { + uint flags = (uint)(MemoryCallbackFlags.AccessRead); + MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus"); + } + + private void Setup_Mapper() + { + + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllerDeck.cs new file mode 100644 index 0000000000..83c92a6f2c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllerDeck.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public class O2HawkControllerDeck + { + public O2HawkControllerDeck(string controller1Name) + { + if (!ValidControllerTypes.ContainsKey(controller1Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller1Name); + } + + Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1); + + Definition = new ControllerDefinition + { + Name = Port1.Definition.Name, + BoolButtons = Port1.Definition.BoolButtons + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public ushort ReadAccX1(IController c) + { + return Port1.ReadAccX(c); + } + + public ushort ReadAccY1(IController c) + { + return Port1.ReadAccY(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection(nameof(Port1)); + Port1.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(O2HawkControllerDeck).Assembly + .GetTypes() + .Where(t => typeof(IPort).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToDictionary(tkey => tkey.DisplayName()); + } + + return _controllerTypes; + } + } + + public static string DefaultControllerName => typeof(StandardControls).DisplayName(); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllers.cs new file mode 100644 index 0000000000..8af8abcdd9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2HawkControllers.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + /// + /// Represents a O2 add on + /// + public interface IPort + { + byte Read(IController c); + + ushort ReadAccX(IController c); + + ushort ReadAccY(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Gameboy Controller")] + public class StandardControls : IPort + { + public StandardControls(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Gameboy Controller H", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + + if (c.IsPressed(Definition.BoolButtons[0])) + { + result -= 4; + } + if (c.IsPressed(Definition.BoolButtons[1])) + { + result -= 8; + } + if (c.IsPressed(Definition.BoolButtons[2])) + { + result -= 2; + } + if (c.IsPressed(Definition.BoolButtons[3])) + { + result -= 1; + } + if (c.IsPressed(Definition.BoolButtons[4])) + { + result -= 128; + } + if (c.IsPressed(Definition.BoolButtons[5])) + { + result -= 64; + } + if (c.IsPressed(Definition.BoolButtons[6])) + { + result -= 32; + } + if (c.IsPressed(Definition.BoolButtons[7])) + { + result -= 16; + } + + return result; + } + + public ushort ReadAccX(IController c) + { + return 0; + } + + public ushort ReadAccY(IController c) + { + return 0; + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } + + [DisplayName("Gameboy Controller + Tilt")] + public class StandardTilt : IPort + { + public StandardTilt(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Gameboy Controller + Tilt", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList(), + FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" }, + FloatRanges = { new[] { -45.0f, 0, 45.0f }, new[] { -45.0f, 0, 45.0f } } + }; + } + + public int PortNum { get; } + + public float theta, phi, theta_prev, phi_prev; + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + + if (c.IsPressed(Definition.BoolButtons[0])) + { + result -= 4; + } + if (c.IsPressed(Definition.BoolButtons[1])) + { + result -= 8; + } + if (c.IsPressed(Definition.BoolButtons[2])) + { + result -= 2; + } + if (c.IsPressed(Definition.BoolButtons[3])) + { + result -= 1; + } + if (c.IsPressed(Definition.BoolButtons[4])) + { + result -= 128; + } + if (c.IsPressed(Definition.BoolButtons[5])) + { + result -= 64; + } + if (c.IsPressed(Definition.BoolButtons[6])) + { + result -= 32; + } + if (c.IsPressed(Definition.BoolButtons[7])) + { + result -= 16; + } + + return result; + } + + // acc x is the result of rotating around body y AFTER rotating around body x + // therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases + public ushort ReadAccX(IController c) + { + theta_prev = theta; + phi_prev = phi; + + theta = (float)(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); + phi = (float)(c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0); + + float temp = (float)(Math.Cos(theta) * Math.Sin(phi)); + + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // since rotations about X have less of a moment arm compared to by, we take 1/5 of the effect as a baseline + float temp2 = (float)((phi - phi_prev) / 0.5 * 25); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) - temp2); + } + + // acc y is just the sine of the angle + // we assume that ReadAccX is called first, which updates the the states + public ushort ReadAccY(IController c) + { + float temp = (float)Math.Sin(theta); + + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // further it will be assumed that the resulting acceleration is roughly eqvuivalent to gravity + float temp2 = (float)((theta - theta_prev)/0.5 * 125); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) + temp2); + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" + }; + + public void SyncState(Serializer ser) + { + // since we need rate of change of angle, need to savestate them + ser.Sync(nameof(theta), ref theta); + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs new file mode 100644 index 0000000000..d48456cb4f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs @@ -0,0 +1,272 @@ +using System; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public class PPU + { + public O2Hawk Core { get; set; } + + public uint[] BG_palette = new uint[32]; + public uint[] OBJ_palette = new uint[32]; + + public bool HDMA_active; + public bool clear_screen; + + // register variables + public byte LCDC; + public byte STAT; + public byte scroll_y; + public byte scroll_x; + public byte LY; + public byte LY_actual; + public byte LY_inc; + public byte LYC; + public byte DMA_addr; + public byte BGP; + public byte obj_pal_0; + public byte obj_pal_1; + public byte window_y; + public byte window_x; + public bool DMA_start; + public int DMA_clock; + public int DMA_inc; + public byte DMA_byte; + + // state variables + public int cycle; + public bool LYC_INT; + public bool HBL_INT; + public bool VBL_INT; + public bool OAM_INT; + public bool LCD_was_off; + public bool stat_line; + public bool stat_line_old; + // OAM scan + public bool DMA_OAM_access; + public bool OAM_access_read; + public bool OAM_access_write; + public int OAM_scan_index; + public int SL_sprites_index; + public int[] SL_sprites = new int[40]; + public int write_sprite; + public bool no_scan; + // render + public bool VRAM_access_read; + public bool VRAM_access_write; + public int read_case; + public int internal_cycle; + public int y_tile; + public int y_scroll_offset; + public int x_tile; + public int x_scroll_offset; + public int tile_byte; + public int sprite_fetch_cycles; + public bool fetch_sprite; + public bool going_to_fetch; + public bool first_fetch; + public int sprite_fetch_counter; + public byte[] sprite_attr_list = new byte[160]; + public byte[] sprite_pixel_list = new byte[160]; + public byte[] sprite_present_list = new byte[160]; + public int temp_fetch; + public int tile_inc; + public bool pre_render; + public bool pre_render_2; + public byte[] tile_data = new byte[3]; + public byte[] tile_data_latch = new byte[3]; + public int latch_counter; + public bool latch_new_data; + public int render_counter; + public int render_offset; + public int pixel_counter; + public int pixel; + public byte[] sprite_data = new byte[2]; + public byte[] sprite_sel = new byte[2]; + public int sl_use_index; + public bool no_sprites; + public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr) + public int evaled_sprites; + public int sprite_ordered_index; + public bool blank_frame; + public bool window_latch; + public int consecutive_sprite; + public int last_eval; + + public int total_counter; + // windowing state + public int window_counter; + public bool window_pre_render; + public bool window_started; + public bool window_is_reset; + public int window_tile_inc; + public int window_y_tile; + public int window_x_tile; + public int window_y_tile_inc; + public int window_x_latch; + public int window_y_latch; + + public int hbl_countdown; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + + } + + public void tick() + { + + } + + // might be needed, not sure yet + public void latch_delay() + { + + } + + public void render(int render_cycle) + { + + } + + public void process_sprite() + { + + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public void DMA_tick() + { + + } + + public void OAM_scan(int OAM_cycle) + { + + } + + public void Reset() + { + + } + + // order sprites according to x coordinate + // note that for sprites of equal x coordinate, priority goes to first on the list + public void reorder_and_assemble_sprites() + { + + } + + public void SyncState(Serializer ser) + { + ser.Sync(nameof(BG_palette), ref BG_palette, false); + ser.Sync(nameof(OBJ_palette), ref OBJ_palette, false); + ser.Sync(nameof(HDMA_active), ref HDMA_active); + ser.Sync(nameof(clear_screen), ref clear_screen); + + ser.Sync(nameof(LCDC), ref LCDC); + ser.Sync(nameof(STAT), ref STAT); + ser.Sync(nameof(scroll_y), ref scroll_y); + ser.Sync(nameof(scroll_x), ref scroll_x); + ser.Sync(nameof(LY), ref LY); + ser.Sync(nameof(LY_actual), ref LY_actual); + ser.Sync(nameof(LY_inc), ref LY_inc); + ser.Sync(nameof(LYC), ref LYC); + ser.Sync(nameof(DMA_addr), ref DMA_addr); + ser.Sync(nameof(BGP), ref BGP); + ser.Sync(nameof(obj_pal_0), ref obj_pal_0); + ser.Sync(nameof(obj_pal_1), ref obj_pal_1); + ser.Sync(nameof(window_y), ref window_y); + ser.Sync(nameof(window_x), ref window_x); + ser.Sync(nameof(DMA_start), ref DMA_start); + ser.Sync(nameof(DMA_clock), ref DMA_clock); + ser.Sync(nameof(DMA_inc), ref DMA_inc); + ser.Sync(nameof(DMA_byte), ref DMA_byte); + + ser.Sync(nameof(cycle), ref cycle); + ser.Sync(nameof(LYC_INT), ref LYC_INT); + ser.Sync(nameof(HBL_INT), ref HBL_INT); + ser.Sync(nameof(VBL_INT), ref VBL_INT); + ser.Sync(nameof(OAM_INT), ref OAM_INT); + ser.Sync(nameof(stat_line), ref stat_line); + ser.Sync(nameof(stat_line_old), ref stat_line_old); + ser.Sync(nameof(LCD_was_off), ref LCD_was_off); + ser.Sync(nameof(OAM_scan_index), ref OAM_scan_index); + ser.Sync(nameof(SL_sprites_index), ref SL_sprites_index); + ser.Sync(nameof(SL_sprites), ref SL_sprites, false); + ser.Sync(nameof(write_sprite), ref write_sprite); + ser.Sync(nameof(no_scan), ref no_scan); + + ser.Sync(nameof(DMA_OAM_access), ref DMA_OAM_access); + ser.Sync(nameof(OAM_access_read), ref OAM_access_read); + ser.Sync(nameof(OAM_access_write), ref OAM_access_write); + ser.Sync(nameof(VRAM_access_read), ref VRAM_access_read); + ser.Sync(nameof(VRAM_access_write), ref VRAM_access_write); + + ser.Sync(nameof(read_case), ref read_case); + ser.Sync(nameof(internal_cycle), ref internal_cycle); + ser.Sync(nameof(y_tile), ref y_tile); + ser.Sync(nameof(y_scroll_offset), ref y_scroll_offset); + ser.Sync(nameof(x_tile), ref x_tile); + ser.Sync(nameof(x_scroll_offset), ref x_scroll_offset); + ser.Sync(nameof(tile_byte), ref tile_byte); + ser.Sync(nameof(sprite_fetch_cycles), ref sprite_fetch_cycles); + ser.Sync(nameof(fetch_sprite), ref fetch_sprite); + ser.Sync(nameof(going_to_fetch), ref going_to_fetch); + ser.Sync(nameof(first_fetch), ref first_fetch); + ser.Sync(nameof(sprite_fetch_counter), ref sprite_fetch_counter); + ser.Sync(nameof(sprite_attr_list), ref sprite_attr_list, false); + ser.Sync(nameof(sprite_pixel_list), ref sprite_pixel_list, false); + ser.Sync(nameof(sprite_present_list), ref sprite_present_list, false); + ser.Sync(nameof(temp_fetch), ref temp_fetch); + ser.Sync(nameof(tile_inc), ref tile_inc); + ser.Sync(nameof(pre_render), ref pre_render); + ser.Sync(nameof(pre_render_2), ref pre_render_2); + ser.Sync(nameof(tile_data), ref tile_data, false); + ser.Sync(nameof(tile_data_latch), ref tile_data_latch, false); + ser.Sync(nameof(latch_counter), ref latch_counter); + ser.Sync(nameof(latch_new_data), ref latch_new_data); + ser.Sync(nameof(render_counter), ref render_counter); + ser.Sync(nameof(render_offset), ref render_offset); + ser.Sync(nameof(pixel_counter), ref pixel_counter); + ser.Sync(nameof(pixel), ref pixel); + ser.Sync(nameof(sprite_data), ref sprite_data, false); + ser.Sync(nameof(sl_use_index), ref sl_use_index); + ser.Sync(nameof(sprite_sel), ref sprite_sel, false); + ser.Sync(nameof(no_sprites), ref no_sprites); + ser.Sync(nameof(evaled_sprites), ref evaled_sprites); + ser.Sync(nameof(SL_sprites_ordered), ref SL_sprites_ordered, false); + ser.Sync(nameof(sprite_ordered_index), ref sprite_ordered_index); + ser.Sync(nameof(blank_frame), ref blank_frame); + ser.Sync(nameof(window_latch), ref window_latch); + ser.Sync(nameof(consecutive_sprite), ref consecutive_sprite); + ser.Sync(nameof(last_eval), ref last_eval); + + ser.Sync(nameof(window_counter), ref window_counter); + ser.Sync(nameof(window_pre_render), ref window_pre_render); + ser.Sync(nameof(window_started), ref window_started); + ser.Sync(nameof(window_is_reset), ref window_is_reset); + ser.Sync(nameof(window_tile_inc), ref window_tile_inc); + ser.Sync(nameof(window_y_tile), ref window_y_tile); + ser.Sync(nameof(window_x_tile), ref window_x_tile); + ser.Sync(nameof(window_y_tile_inc), ref window_y_tile_inc); + ser.Sync(nameof(window_x_latch), ref window_x_latch); + ser.Sync(nameof(window_y_latch), ref window_y_latch); + + ser.Sync(nameof(hbl_countdown), ref hbl_countdown); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/ReadMe.txt @@ -0,0 +1 @@ +TODO: diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/SerialPort.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/SerialPort.cs new file mode 100644 index 0000000000..e67b722df3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/SerialPort.cs @@ -0,0 +1,150 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.O2Hawk +{ + public class SerialPort + { + public O2Hawk Core { get; set; } + + public byte serial_control; + public byte serial_data; + public bool serial_start; + public bool can_pulse; + public int serial_clock; + public int serial_bits; + public int clk_rate; + public byte going_out; + public byte coming_in; + + public byte ReadReg(int addr) + { + switch (addr) + { + case 0xFF01: + return serial_data; + case 0xFF02: + return serial_control; + } + + return 0xFF; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF01: + serial_data = value; + break; + + case 0xFF02: + if (((value & 0x80) > 0) && !serial_start) + { + serial_start = true; + serial_bits = 8; + if ((value & 1) > 0) + { + if (((value & 2) > 0)) + { + clk_rate = 16; + } + else + { + clk_rate = 512; + } + serial_clock = clk_rate; + can_pulse = true; + } + else + { + clk_rate = -1; + serial_clock = clk_rate; + can_pulse = false; + } + } + else if (serial_start) + { + if ((value & 1) > 0) + { + if (((value & 2) > 0)) + { + clk_rate = 16; + } + else + { + clk_rate = 512; + } + serial_clock = clk_rate; + can_pulse = true; + } + else + { + clk_rate = -1; + serial_clock = clk_rate; + can_pulse = false; + } + } + + serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1 + + break; + } + } + + + public void serial_transfer_tick() + { + if (serial_start) + { + if (serial_clock > 0) { serial_clock--; } + + if (serial_clock == 0) + { + if (serial_bits > 0) + { + byte temp = coming_in; + serial_data = (byte)((serial_data << 1) | temp); + + serial_bits--; + + if (serial_bits == 0) + { + serial_control &= 0x7F; + serial_start = false; + } + else + { + serial_clock = clk_rate; + if (clk_rate > 0) { can_pulse = true; } + } + } + } + } + } + + public void Reset() + { + serial_control = 0x7E; + serial_start = false; + serial_data = 0x00; + going_out = 0; + coming_in = 1; + } + + public void SyncState(Serializer ser) + { + ser.Sync(nameof(serial_control), ref serial_control); + ser.Sync(nameof(serial_data), ref serial_data); + ser.Sync(nameof(serial_start), ref serial_start); + ser.Sync(nameof(serial_clock), ref serial_clock); + ser.Sync(nameof(serial_bits), ref serial_bits); + ser.Sync(nameof(clk_rate), ref clk_rate); + ser.Sync(nameof(going_out), ref going_out); + ser.Sync(nameof(coming_in), ref coming_in); + ser.Sync(nameof(can_pulse), ref can_pulse); + } + } +}