From 8ff899b731685031969b30d563ddd9811e757035 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:12:31 -0400 Subject: [PATCH 01/28] Create ReadMe.txt --- BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt new file mode 100644 index 0000000000..115b568cb8 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt @@ -0,0 +1 @@ +TODO: STOP for second byte nonzero From 56d06b33579e4300260675cebc7fc5ea44d4984a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:13:56 -0400 Subject: [PATCH 02/28] LR35902 CPU commit --- .../CPUs/LR35902/Execute.cs | 552 ++++++++++++++++ .../CPUs/LR35902/Interrupts.cs | 66 ++ .../CPUs/LR35902/LR35902.cs | 459 ++++++++++++++ .../CPUs/LR35902/NewDisassembler.cs | 587 ++++++++++++++++++ .../CPUs/LR35902/Operations.cs | 467 ++++++++++++++ .../CPUs/LR35902/Registers.cs | 73 +++ .../CPUs/LR35902/Tables_Direct.cs | 498 +++++++++++++++ .../CPUs/LR35902/Tables_Indirect.cs | 339 ++++++++++ 8 files changed, 3041 insertions(+) create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs create mode 100644 BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs new file mode 100644 index 0000000000..921ee5f19f --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -0,0 +1,552 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + private int totalExecutedCycles; + public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + + private int EI_pending; + private bool interrupts_enabled; + + // variables for executing instructions + public int instr_pntr = 0; + public ushort[] cur_instr; + public int opcode; + public bool CB_prefix; + public bool halted; + public bool stopped; + public bool jammed; + public int LY; + + public void FetchInstruction(byte opcode) + { + if (!CB_prefix) + { + switch (opcode) + { + case 0x00: NOP_(); break; // NOP + case 0x01: LD_IND_16(C, B, PCl, PCh); break; // LD BC, nn + case 0x02: LD_8_IND(C, B, A); break; // LD (BC), A + case 0x03: INC_16(C, B); break; // INC BC + case 0x04: INT_OP(INC8, B); break; // INC B + case 0x05: INT_OP(DEC8, B); break; // DEC B + case 0x06: LD_IND_8_INC(B, PCl, PCh); break; // LD B, n + case 0x07: INT_OP(RLC, Aim); break; // RLCA + case 0x08: LD_R_IM(SPl, SPh, PCl, PCh); break; // LD (imm), SP + case 0x09: ADD_16(L, H, C, B); break; // ADD HL, BC + case 0x0A: REG_OP_IND(TR, A, C, B); break; // LD A, (BC) + case 0x0B: DEC_16(C, B); break; // DEC BC + case 0x0C: INT_OP(INC8, C); break; // INC C + case 0x0D: INT_OP(DEC8, C); break; // DEC C + case 0x0E: LD_IND_8_INC(C, PCl, PCh); break; // LD C, n + case 0x0F: INT_OP(RRC, Aim); break; // RRCA + case 0x10: STOP_(); break; // STOP + case 0x11: LD_IND_16(E, D, PCl, PCh); break; // LD DE, nn + case 0x12: LD_8_IND(E, D, A); break; // LD (DE), A + case 0x13: INC_16(E, D); break; // INC DE + case 0x14: INT_OP(INC8, D); break; // INC D + case 0x15: INT_OP(DEC8, D); break; // DEC D + case 0x16: LD_IND_8_INC(D, PCl, PCh); break; // LD D, n + case 0x17: INT_OP(RL, Aim); break; // RLA + case 0x18: JR_COND(true); break; // JR, r8 + case 0x19: ADD_16(L, H, E, D); break; // ADD HL, DE + case 0x1A: REG_OP_IND(TR, A, E, D); break; // LD A, (DE) + case 0x1B: DEC_16(E, D); break; // DEC DE + case 0x1C: INT_OP(INC8, E); break; // INC E + case 0x1D: INT_OP(DEC8, E); break; // DEC E + case 0x1E: LD_IND_8_INC(E, PCl, PCh); break; // LD E, n + case 0x1F: INT_OP(RR, Aim); break; // RRA + case 0x20: JR_COND(!FlagZ); break; // JR NZ, r8 + case 0x21: LD_IND_16(L, H, PCl, PCh); break; // LD HL, nn + case 0x22: LD_8_IND_INC(L, H, A); break; // LD (HL+), A + case 0x23: INC_16(L, H); break; // INC HL + case 0x24: INT_OP(INC8, H); break; // INC H + case 0x25: INT_OP(DEC8, H); break; // DEC H + case 0x26: LD_IND_8_INC(H, PCl, PCh); break; // LD H, n + case 0x27: INT_OP(DA, A); break; // DAA + case 0x28: JR_COND(FlagZ); break; // JR Z, r8 + case 0x29: ADD_16(L, H, L, H); break; // ADD HL, HL + case 0x2A: LD_IND_8_INC(A, L, H); break; // LD A, (HL+) + case 0x2B: DEC_16(L, H); break; // DEC HL + case 0x2C: INT_OP(INC8, L); break; // INC L + case 0x2D: INT_OP(DEC8, L); break; // DEC L + case 0x2E: LD_IND_8_INC(L, PCl, PCh); break; // LD L, n + case 0x2F: INT_OP(CPL, A); break; // CPL + case 0x30: JR_COND(!FlagC); break; // JR NC, r8 + case 0x31: LD_IND_16(SPl, SPh, PCl, PCh); break; // LD SP, nn + case 0x32: LD_8_IND_DEC(L, H, A); break; // LD (HL-), A + case 0x33: INC_16(SPl, SPh); break; // INC SP + case 0x34: INC_8_IND(L, H); break; // INC (HL) + case 0x35: DEC_8_IND(L, H); break; // DEC (HL) + case 0x36: LD_8_IND_IND(L, H, PCl, PCh); break; // LD (HL), n + case 0x37: INT_OP(SCF, A); break; // SCF + case 0x38: JR_COND(FlagC); break; // JR C, r8 + case 0x39: ADD_16(L, H, SPl, SPh); break; // ADD HL, SP + case 0x3A: LD_IND_8_DEC(A, L, H); break; // LD A, (HL-) + case 0x3B: DEC_16(SPl, SPh); break; // DEC SP + case 0x3C: INT_OP(INC8, A); break; // INC A + case 0x3D: INT_OP(DEC8, A); break; // DEC A + case 0x3E: LD_IND_8_INC(A, PCl, PCh); break; // LD A, n + case 0x3F: INT_OP(CCF, A); break; // CCF + case 0x40: REG_OP(TR, B, B); break; // LD B, B + case 0x41: REG_OP(TR, B, C); break; // LD B, C + case 0x42: REG_OP(TR, B, D); break; // LD B, D + case 0x43: REG_OP(TR, B, E); break; // LD B, E + case 0x44: REG_OP(TR, B, H); break; // LD B, H + case 0x45: REG_OP(TR, B, L); break; // LD B, L + case 0x46: REG_OP_IND(TR, B, L, H); break; // LD B, (HL) + case 0x47: REG_OP(TR, B, A); break; // LD B, A + case 0x48: REG_OP(TR, C, B); break; // LD C, B + case 0x49: REG_OP(TR, C, C); break; // LD C, C + case 0x4A: REG_OP(TR, C, D); break; // LD C, D + case 0x4B: REG_OP(TR, C, E); break; // LD C, E + case 0x4C: REG_OP(TR, C, H); break; // LD C, H + case 0x4D: REG_OP(TR, C, L); break; // LD C, L + case 0x4E: REG_OP_IND(TR, C, L, H); break; // LD C, (HL) + case 0x4F: REG_OP(TR, C, A); break; // LD C, A + case 0x50: REG_OP(TR, D, B); break; // LD D, B + case 0x51: REG_OP(TR, D, C); break; // LD D, C + case 0x52: REG_OP(TR, D, D); break; // LD D, D + case 0x53: REG_OP(TR, D, E); break; // LD D, E + case 0x54: REG_OP(TR, D, H); break; // LD D, H + case 0x55: REG_OP(TR, D, L); break; // LD D, L + case 0x56: REG_OP_IND(TR, D, L, H); break; // LD D, (HL) + case 0x57: REG_OP(TR, D, A); break; // LD D, A + case 0x58: REG_OP(TR, E, B); break; // LD E, B + case 0x59: REG_OP(TR, E, C); break; // LD E, C + case 0x5A: REG_OP(TR, E, D); break; // LD E, D + case 0x5B: REG_OP(TR, E, E); break; // LD E, E + case 0x5C: REG_OP(TR, E, H); break; // LD E, H + case 0x5D: REG_OP(TR, E, L); break; // LD E, L + case 0x5E: REG_OP_IND(TR, E, L, H); break; // LD E, (HL) + case 0x5F: REG_OP(TR, E, A); break; // LD E, A + case 0x60: REG_OP(TR, H, B); break; // LD H, B + case 0x61: REG_OP(TR, H, C); break; // LD H, C + case 0x62: REG_OP(TR, H, D); break; // LD H, D + case 0x63: REG_OP(TR, H, E); break; // LD H, E + case 0x64: REG_OP(TR, H, H); break; // LD H, H + case 0x65: REG_OP(TR, H, L); break; // LD H, L + case 0x66: REG_OP_IND(TR, H, L, H); break; // LD H, (HL) + case 0x67: REG_OP(TR, H, A); break; // LD H, A + case 0x68: REG_OP(TR, L, B); break; // LD L, B + case 0x69: REG_OP(TR, L, C); break; // LD L, C + case 0x6A: REG_OP(TR, L, D); break; // LD L, D + case 0x6B: REG_OP(TR, L, E); break; // LD L, E + case 0x6C: REG_OP(TR, L, H); break; // LD L, H + case 0x6D: REG_OP(TR, L, L); break; // LD L, L + case 0x6E: REG_OP_IND(TR, L, L, H); break; // LD L, (HL) + case 0x6F: REG_OP(TR, L, A); break; // LD L, A + case 0x70: LD_8_IND(L, H, B); break; // LD (HL), B + case 0x71: LD_8_IND(L, H, C); break; // LD (HL), C + case 0x72: LD_8_IND(L, H, D); break; // LD (HL), D + case 0x73: LD_8_IND(L, H, E); break; // LD (HL), E + case 0x74: LD_8_IND(L, H, H); break; // LD (HL), H + case 0x75: LD_8_IND(L, H, L); break; // LD (HL), L + case 0x76: HALT_(); break; // HALT + case 0x77: LD_8_IND(L, H, A); break; // LD (HL), A + case 0x78: REG_OP(TR, A, B); break; // LD A, B + case 0x79: REG_OP(TR, A, C); break; // LD A, C + case 0x7A: REG_OP(TR, A, D); break; // LD A, D + case 0x7B: REG_OP(TR, A, E); break; // LD A, E + case 0x7C: REG_OP(TR, A, H); break; // LD A, H + case 0x7D: REG_OP(TR, A, L); break; // LD A, L + case 0x7E: REG_OP_IND(TR, A, L, H); break; // LD A, (HL) + case 0x7F: REG_OP(TR, A, A); break; // LD A, A + case 0x80: REG_OP(ADD8, A, B); break; // ADD A, B + case 0x81: REG_OP(ADD8, A, C); break; // ADD A, C + case 0x82: REG_OP(ADD8, A, D); break; // ADD A, D + case 0x83: REG_OP(ADD8, A, E); break; // ADD A, E + case 0x84: REG_OP(ADD8, A, H); break; // ADD A, H + case 0x85: REG_OP(ADD8, A, L); break; // ADD A, L + case 0x86: REG_OP_IND(ADD8, A, L, H); break; // ADD A, (HL) + case 0x87: REG_OP(ADD8, A, A); break; // ADD A, A + case 0x88: REG_OP(ADC8, A, B); break; // ADC A, B + case 0x89: REG_OP(ADC8, A, C); break; // ADC A, C + case 0x8A: REG_OP(ADC8, A, D); break; // ADC A, D + case 0x8B: REG_OP(ADC8, A, E); break; // ADC A, E + case 0x8C: REG_OP(ADC8, A, H); break; // ADC A, H + case 0x8D: REG_OP(ADC8, A, L); break; // ADC A, L + case 0x8E: REG_OP_IND(ADC8, A, L, H); break; // ADC A, (HL) + case 0x8F: REG_OP(ADC8, A, A); break; // ADC A, A + case 0x90: REG_OP(SUB8, A, B); break; // SUB A, B + case 0x91: REG_OP(SUB8, A, C); break; // SUB A, C + case 0x92: REG_OP(SUB8, A, D); break; // SUB A, D + case 0x93: REG_OP(SUB8, A, E); break; // SUB A, E + case 0x94: REG_OP(SUB8, A, H); break; // SUB A, H + case 0x95: REG_OP(SUB8, A, L); break; // SUB A, L + case 0x96: REG_OP_IND(SUB8, A, L, H); break; // SUB A, (HL) + case 0x97: REG_OP(SUB8, A, A); break; // SUB A, A + case 0x98: REG_OP(SBC8, A, B); break; // SBC A, B + case 0x99: REG_OP(SBC8, A, C); break; // SBC A, C + case 0x9A: REG_OP(SBC8, A, D); break; // SBC A, D + case 0x9B: REG_OP(SBC8, A, E); break; // SBC A, E + case 0x9C: REG_OP(SBC8, A, H); break; // SBC A, H + case 0x9D: REG_OP(SBC8, A, L); break; // SBC A, L + case 0x9E: REG_OP_IND(SBC8, A, L, H); break; // SBC A, (HL) + case 0x9F: REG_OP(SBC8, A, A); break; // SBC A, A + case 0xA0: REG_OP(AND8, A, B); break; // AND A, B + case 0xA1: REG_OP(AND8, A, C); break; // AND A, C + case 0xA2: REG_OP(AND8, A, D); break; // AND A, D + case 0xA3: REG_OP(AND8, A, E); break; // AND A, E + case 0xA4: REG_OP(AND8, A, H); break; // AND A, H + case 0xA5: REG_OP(AND8, A, L); break; // AND A, L + case 0xA6: REG_OP_IND(AND8, A, L, H); break; // AND A, (HL) + case 0xA7: REG_OP(AND8, A, A); break; // AND A, A + case 0xA8: REG_OP(XOR8, A, B); break; // XOR A, B + case 0xA9: REG_OP(XOR8, A, C); break; // XOR A, C + case 0xAA: REG_OP(XOR8, A, D); break; // XOR A, D + case 0xAB: REG_OP(XOR8, A, E); break; // XOR A, E + case 0xAC: REG_OP(XOR8, A, H); break; // XOR A, H + case 0xAD: REG_OP(XOR8, A, L); break; // XOR A, L + case 0xAE: REG_OP_IND(XOR8, A, L, H); break; // XOR A, (HL) + case 0xAF: REG_OP(XOR8, A, A); break; // XOR A, A + case 0xB0: REG_OP(OR8, A, B); break; // OR A, B + case 0xB1: REG_OP(OR8, A, C); break; // OR A, C + case 0xB2: REG_OP(OR8, A, D); break; // OR A, D + case 0xB3: REG_OP(OR8, A, E); break; // OR A, E + case 0xB4: REG_OP(OR8, A, H); break; // OR A, H + case 0xB5: REG_OP(OR8, A, L); break; // OR A, L + case 0xB6: REG_OP_IND(OR8, A, L, H); break; // OR A, (HL) + case 0xB7: REG_OP(OR8, A, A); break; // OR A, A + case 0xB8: REG_OP(CP8, A, B); break; // CP A, B + case 0xB9: REG_OP(CP8, A, C); break; // CP A, C + case 0xBA: REG_OP(CP8, A, D); break; // CP A, D + case 0xBB: REG_OP(CP8, A, E); break; // CP A, E + case 0xBC: REG_OP(CP8, A, H); break; // CP A, H + case 0xBD: REG_OP(CP8, A, L); break; // CP A, L + case 0xBE: REG_OP_IND(CP8, A, L, H); break; // CP A, (HL) + case 0xBF: REG_OP(CP8, A, A); break; // CP A, A + case 0xC0: RET_COND(!FlagZ); break; // Ret NZ + case 0xC1: POP_(C, B); break; // POP BC + case 0xC2: JP_COND(!FlagZ); break; // JP NZ + case 0xC3: JP_COND(true); break; // JP + case 0xC4: CALL_COND(!FlagZ); break; // CALL NZ + case 0xC5: PUSH_(C, B); break; // PUSH BC + case 0xC6: REG_OP_IND_INC(ADD8, A, PCl, PCh); break; // ADD A, n + case 0xC7: RST_(0); break; // RST 0 + case 0xC8: RET_COND(FlagZ); break; // RET Z + case 0xC9: RET_(); break; // RET + case 0xCA: JP_COND(FlagZ); break; // JP Z + case 0xCB: PREFIX_(); break; // PREFIX + case 0xCC: CALL_COND(FlagZ); break; // CALL Z + case 0xCD: CALL_COND(true); break; // CALL + case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n + case 0xCF: RST_(0x08); break; // RST 0x08 + case 0xD0: RET_COND(!FlagC); break; // Ret NC + case 0xD1: POP_(E, D); break; // POP DE + case 0xD2: JP_COND(!FlagC); break; // JP NC + case 0xD3: JAM_(); break; // JAM + case 0xD4: CALL_COND(!FlagC); break; // CALL NC + case 0xD5: PUSH_(E, D); break; // PUSH DE + case 0xD6: REG_OP_IND_INC(SUB8, A, PCl, PCh); break; // SUB A, n + case 0xD7: RST_(0x10); break; // RST 0x10 + case 0xD8: RET_COND(FlagC); break; // RET C + case 0xD9: RETI_(); break; // RETI + case 0xDA: JP_COND(FlagC); break; // JP C + case 0xDB: JAM_(); break; // JAM + case 0xDC: CALL_COND(FlagC); break; // CALL C + case 0xDD: JAM_(); break; // JAM + case 0xDE: REG_OP_IND_INC(SBC8, A, PCl, PCh); break; // SBC A, n + case 0xDF: RST_(0x18); break; // RST 0x18 + case 0xE0: LD_FF_IND_8(PCl, PCh, A); break; // LD(n), A + case 0xE1: POP_(L, H); break; // POP HL + case 0xE2: LD_FFC_IND_8(PCl, PCh, A); break; // LD(C), A + case 0xE3: JAM_(); break; // JAM + case 0xE4: JAM_(); break; // JAM + case 0xE5: PUSH_(L, H); break; // PUSH HL + case 0xE6: REG_OP_IND_INC(AND8, A, PCl, PCh); break; // AND A, n + case 0xE7: RST_(0x20); break; // RST 0x20 + case 0xE8: ADD_SP(); break; // ADD SP,n + case 0xE9: JP_HL(); break; // JP (HL) + case 0xEA: LD_FF_IND_16(PCl, PCh, A); break; // LD(nn), A + case 0xEB: JAM_(); break; // JAM + case 0xEC: JAM_(); break; // JAM + case 0xED: JAM_(); break; // JAM + case 0xEE: REG_OP_IND_INC(XOR8, A, PCl, PCh); break; // XOR A, n + case 0xEF: RST_(0x28); break; // RST 0x28 + case 0xF0: LD_8_IND_FF(A, PCl, PCh); break; // A, LD(n) + case 0xF1: POP_(F, A); break; // POP AF + case 0xF2: LD_8_IND_FFC(A, PCl, PCh); break; // A, LD(C) + case 0xF3: DI_(); break; // DI + case 0xF4: JAM_(); break; // JAM + case 0xF5: PUSH_(F, A); break; // PUSH AF + case 0xF6: REG_OP_IND_INC(OR8, A, PCl, PCh); break; // OR A, n + case 0xF7: RST_(0x30); break; // RST 0x30 + case 0xF8: LD_HL_SPn(); break; // LD HL, SP+n + case 0xF9: LD_SP_HL(); break; // LD, SP, HL + case 0xFA: LD_16_IND_FF(A, PCl, PCh); break; // A, LD(nn) + case 0xFB: EI_(); break; // EI + case 0xFC: JAM_(); break; // JAM + case 0xFD: JAM_(); break; // JAM + case 0xFE: REG_OP_IND_INC(CP8, A, PCl, PCh); break; // XOR A, n + case 0xFF: RST_(0x38); break; // RST 0x38 + } + } + else + { + CB_prefix = false; + switch (opcode) + { + case 0x00: INT_OP(RLC, B); break; // RLC B + case 0x01: INT_OP(RLC, C); break; // RLC C + case 0x02: INT_OP(RLC, D); break; // RLC D + case 0x03: INT_OP(RLC, E); break; // RLC E + case 0x04: INT_OP(RLC, H); break; // RLC H + case 0x05: INT_OP(RLC, L); break; // RLC L + case 0x06: INT_OP_IND(RLC, L, H); break; // RLC (HL) + case 0x07: INT_OP(RLC, A); break; // RLC A + case 0x08: INT_OP(RRC, B); break; // RRC B + case 0x09: INT_OP(RRC, C); break; // RRC C + case 0x0A: INT_OP(RRC, D); break; // RRC D + case 0x0B: INT_OP(RRC, E); break; // RRC E + case 0x0C: INT_OP(RRC, H); break; // RRC H + case 0x0D: INT_OP(RRC, L); break; // RRC L + case 0x0E: INT_OP_IND(RRC, L, H); break; // RRC (HL) + case 0x0F: INT_OP(RRC, A); break; // RRC A + case 0x10: INT_OP(RL, B); break; // RL B + case 0x11: INT_OP(RL, C); break; // RL C + case 0x12: INT_OP(RL, D); break; // RL D + case 0x13: INT_OP(RL, E); break; // RL E + case 0x14: INT_OP(RL, H); break; // RL H + case 0x15: INT_OP(RL, L); break; // RL L + case 0x16: INT_OP_IND(RL, L, H); break; // RL (HL) + case 0x17: INT_OP(RL, A); break; // RL A + case 0x18: INT_OP(RR, B); break; // RR B + case 0x19: INT_OP(RR, C); break; // RR C + case 0x1A: INT_OP(RR, D); break; // RR D + case 0x1B: INT_OP(RR, E); break; // RR E + case 0x1C: INT_OP(RR, H); break; // RR H + case 0x1D: INT_OP(RR, L); break; // RR L + case 0x1E: INT_OP_IND(RR, L, H); break; // RR (HL) + case 0x1F: INT_OP(RR, A); break; // RR A + case 0x20: INT_OP(SLA, B); break; // SLA B + case 0x21: INT_OP(SLA, C); break; // SLA C + case 0x22: INT_OP(SLA, D); break; // SLA D + case 0x23: INT_OP(SLA, E); break; // SLA E + case 0x24: INT_OP(SLA, H); break; // SLA H + case 0x25: INT_OP(SLA, L); break; // SLA L + case 0x26: INT_OP_IND(SLA, L, H); break; // SLA (HL) + case 0x27: INT_OP(SLA, A); break; // SLA A + case 0x28: INT_OP(SRA, B); break; // SRA B + case 0x29: INT_OP(SRA, C); break; // SRA C + case 0x2A: INT_OP(SRA, D); break; // SRA D + case 0x2B: INT_OP(SRA, E); break; // SRA E + case 0x2C: INT_OP(SRA, H); break; // SRA H + case 0x2D: INT_OP(SRA, L); break; // SRA L + case 0x2E: INT_OP_IND(SRA, L, H); break; // SRA (HL) + case 0x2F: INT_OP(SRA, A); break; // SRA A + case 0x30: INT_OP(SWAP, B); break; // SWAP B + case 0x31: INT_OP(SWAP, C); break; // SWAP C + case 0x32: INT_OP(SWAP, D); break; // SWAP D + case 0x33: INT_OP(SWAP, E); break; // SWAP E + case 0x34: INT_OP(SWAP, H); break; // SWAP H + case 0x35: INT_OP(SWAP, L); break; // SWAP L + case 0x36: INT_OP_IND(SWAP, L, H); break; // SWAP (HL) + case 0x37: INT_OP(SWAP, A); break; // SWAP A + case 0x38: INT_OP(SRL, B); break; // SRL B + case 0x39: INT_OP(SRL, C); break; // SRL C + case 0x3A: INT_OP(SRL, D); break; // SRL D + case 0x3B: INT_OP(SRL, E); break; // SRL E + case 0x3C: INT_OP(SRL, H); break; // SRL H + case 0x3D: INT_OP(SRL, L); break; // SRL L + case 0x3E: INT_OP_IND(SRL, L, H); break; // SRL (HL) + case 0x3F: INT_OP(SRL, A); break; // SRL A + case 0x40: BIT_OP(BIT, 0, B); break; // BIT 0, B + case 0x41: BIT_OP(BIT, 0, C); break; // BIT 0, C + case 0x42: BIT_OP(BIT, 0, D); break; // BIT 0, D + case 0x43: BIT_OP(BIT, 0, E); break; // BIT 0, E + case 0x44: BIT_OP(BIT, 0, H); break; // BIT 0, H + case 0x45: BIT_OP(BIT, 0, L); break; // BIT 0, L + case 0x46: BIT_TE_IND(BIT, 0, L, H); break; // BIT 0, (HL) + case 0x47: BIT_OP(BIT, 0, A); break; // BIT 0, A + case 0x48: BIT_OP(BIT, 1, B); break; // BIT 1, B + case 0x49: BIT_OP(BIT, 1, C); break; // BIT 1, C + case 0x4A: BIT_OP(BIT, 1, D); break; // BIT 1, D + case 0x4B: BIT_OP(BIT, 1, E); break; // BIT 1, E + case 0x4C: BIT_OP(BIT, 1, H); break; // BIT 1, H + case 0x4D: BIT_OP(BIT, 1, L); break; // BIT 1, L + case 0x4E: BIT_TE_IND(BIT, 1, L, H); break; // BIT 1, (HL) + case 0x4F: BIT_OP(BIT, 1, A); break; // BIT 1, A + case 0x50: BIT_OP(BIT, 2, B); break; // BIT 2, B + case 0x51: BIT_OP(BIT, 2, C); break; // BIT 2, C + case 0x52: BIT_OP(BIT, 2, D); break; // BIT 2, D + case 0x53: BIT_OP(BIT, 2, E); break; // BIT 2, E + case 0x54: BIT_OP(BIT, 2, H); break; // BIT 2, H + case 0x55: BIT_OP(BIT, 2, L); break; // BIT 2, L + case 0x56: BIT_TE_IND(BIT, 2, L, H); break; // BIT 2, (HL) + case 0x57: BIT_OP(BIT, 2, A); break; // BIT 2, A + case 0x58: BIT_OP(BIT, 3, B); break; // BIT 3, B + case 0x59: BIT_OP(BIT, 3, C); break; // BIT 3, C + case 0x5A: BIT_OP(BIT, 3, D); break; // BIT 3, D + case 0x5B: BIT_OP(BIT, 3, E); break; // BIT 3, E + case 0x5C: BIT_OP(BIT, 3, H); break; // BIT 3, H + case 0x5D: BIT_OP(BIT, 3, L); break; // BIT 3, L + case 0x5E: BIT_TE_IND(BIT, 3, L, H); break; // BIT 3, (HL) + case 0x5F: BIT_OP(BIT, 3, A); break; // BIT 3, A + case 0x60: BIT_OP(BIT, 4, B); break; // BIT 4, B + case 0x61: BIT_OP(BIT, 4, C); break; // BIT 4, C + case 0x62: BIT_OP(BIT, 4, D); break; // BIT 4, D + case 0x63: BIT_OP(BIT, 4, E); break; // BIT 4, E + case 0x64: BIT_OP(BIT, 4, H); break; // BIT 4, H + case 0x65: BIT_OP(BIT, 4, L); break; // BIT 4, L + case 0x66: BIT_TE_IND(BIT, 4, L, H); break; // BIT 4, (HL) + case 0x67: BIT_OP(BIT, 4, A); break; // BIT 4, A + case 0x68: BIT_OP(BIT, 5, B); break; // BIT 5, B + case 0x69: BIT_OP(BIT, 5, C); break; // BIT 5, C + case 0x6A: BIT_OP(BIT, 5, D); break; // BIT 5, D + case 0x6B: BIT_OP(BIT, 5, E); break; // BIT 5, E + case 0x6C: BIT_OP(BIT, 5, H); break; // BIT 5, H + case 0x6D: BIT_OP(BIT, 5, L); break; // BIT 5, L + case 0x6E: BIT_TE_IND(BIT, 5, L, H); break; // BIT 5, (HL) + case 0x6F: BIT_OP(BIT, 5, A); break; // BIT 5, A + case 0x70: BIT_OP(BIT, 6, B); break; // BIT 6, B + case 0x71: BIT_OP(BIT, 6, C); break; // BIT 6, C + case 0x72: BIT_OP(BIT, 6, D); break; // BIT 6, D + case 0x73: BIT_OP(BIT, 6, E); break; // BIT 6, E + case 0x74: BIT_OP(BIT, 6, H); break; // BIT 6, H + case 0x75: BIT_OP(BIT, 6, L); break; // BIT 6, L + case 0x76: BIT_TE_IND(BIT, 6, L, H); break; // BIT 6, (HL) + case 0x77: BIT_OP(BIT, 6, A); break; // BIT 6, A + case 0x78: BIT_OP(BIT, 7, B); break; // BIT 7, B + case 0x79: BIT_OP(BIT, 7, C); break; // BIT 7, C + case 0x7A: BIT_OP(BIT, 7, D); break; // BIT 7, D + case 0x7B: BIT_OP(BIT, 7, E); break; // BIT 7, E + case 0x7C: BIT_OP(BIT, 7, H); break; // BIT 7, H + case 0x7D: BIT_OP(BIT, 7, L); break; // BIT 7, L + case 0x7E: BIT_TE_IND(BIT, 7, L, H); break; // BIT 7, (HL) + case 0x7F: BIT_OP(BIT, 7, A); break; // BIT 7, A + case 0x80: BIT_OP(RES, 0, B); break; // RES 0, B + case 0x81: BIT_OP(RES, 0, C); break; // RES 0, C + case 0x82: BIT_OP(RES, 0, D); break; // RES 0, D + case 0x83: BIT_OP(RES, 0, E); break; // RES 0, E + case 0x84: BIT_OP(RES, 0, H); break; // RES 0, H + case 0x85: BIT_OP(RES, 0, L); break; // RES 0, L + case 0x86: BIT_OP_IND(RES, 0, L, H); break; // RES 0, (HL) + case 0x87: BIT_OP(RES, 0, A); break; // RES 0, A + case 0x88: BIT_OP(RES, 1, B); break; // RES 1, B + case 0x89: BIT_OP(RES, 1, C); break; // RES 1, C + case 0x8A: BIT_OP(RES, 1, D); break; // RES 1, D + case 0x8B: BIT_OP(RES, 1, E); break; // RES 1, E + case 0x8C: BIT_OP(RES, 1, H); break; // RES 1, H + case 0x8D: BIT_OP(RES, 1, L); break; // RES 1, L + case 0x8E: BIT_OP_IND(RES, 1, L, H); break; // RES 1, (HL) + case 0x8F: BIT_OP(RES, 1, A); break; // RES 1, A + case 0x90: BIT_OP(RES, 2, B); break; // RES 2, B + case 0x91: BIT_OP(RES, 2, C); break; // RES 2, C + case 0x92: BIT_OP(RES, 2, D); break; // RES 2, D + case 0x93: BIT_OP(RES, 2, E); break; // RES 2, E + case 0x94: BIT_OP(RES, 2, H); break; // RES 2, H + case 0x95: BIT_OP(RES, 2, L); break; // RES 2, L + case 0x96: BIT_OP_IND(RES, 2, L, H); break; // RES 2, (HL) + case 0x97: BIT_OP(RES, 2, A); break; // RES 2, A + case 0x98: BIT_OP(RES, 3, B); break; // RES 3, B + case 0x99: BIT_OP(RES, 3, C); break; // RES 3, C + case 0x9A: BIT_OP(RES, 3, D); break; // RES 3, D + case 0x9B: BIT_OP(RES, 3, E); break; // RES 3, E + case 0x9C: BIT_OP(RES, 3, H); break; // RES 3, H + case 0x9D: BIT_OP(RES, 3, L); break; // RES 3, L + case 0x9E: BIT_OP_IND(RES, 3, L, H); break; // RES 3, (HL) + case 0x9F: BIT_OP(RES, 3, A); break; // RES 3, A + case 0xA0: BIT_OP(RES, 4, B); break; // RES 4, B + case 0xA1: BIT_OP(RES, 4, C); break; // RES 4, C + case 0xA2: BIT_OP(RES, 4, D); break; // RES 4, D + case 0xA3: BIT_OP(RES, 4, E); break; // RES 4, E + case 0xA4: BIT_OP(RES, 4, H); break; // RES 4, H + case 0xA5: BIT_OP(RES, 4, L); break; // RES 4, L + case 0xA6: BIT_OP_IND(RES, 4, L, H); break; // RES 4, (HL) + case 0xA7: BIT_OP(RES, 4, A); break; // RES 4, A + case 0xA8: BIT_OP(RES, 5, B); break; // RES 5, B + case 0xA9: BIT_OP(RES, 5, C); break; // RES 5, C + case 0xAA: BIT_OP(RES, 5, D); break; // RES 5, D + case 0xAB: BIT_OP(RES, 5, E); break; // RES 5, E + case 0xAC: BIT_OP(RES, 5, H); break; // RES 5, H + case 0xAD: BIT_OP(RES, 5, L); break; // RES 5, L + case 0xAE: BIT_OP_IND(RES, 5, L, H); break; // RES 5, (HL) + case 0xAF: BIT_OP(RES, 5, A); break; // RES 5, A + case 0xB0: BIT_OP(RES, 6, B); break; // RES 6, B + case 0xB1: BIT_OP(RES, 6, C); break; // RES 6, C + case 0xB2: BIT_OP(RES, 6, D); break; // RES 6, D + case 0xB3: BIT_OP(RES, 6, E); break; // RES 6, E + case 0xB4: BIT_OP(RES, 6, H); break; // RES 6, H + case 0xB5: BIT_OP(RES, 6, L); break; // RES 6, L + case 0xB6: BIT_OP_IND(RES, 6, L, H); break; // RES 6, (HL) + case 0xB7: BIT_OP(RES, 6, A); break; // RES 6, A + case 0xB8: BIT_OP(RES, 7, B); break; // RES 7, B + case 0xB9: BIT_OP(RES, 7, C); break; // RES 7, C + case 0xBA: BIT_OP(RES, 7, D); break; // RES 7, D + case 0xBB: BIT_OP(RES, 7, E); break; // RES 7, E + case 0xBC: BIT_OP(RES, 7, H); break; // RES 7, H + case 0xBD: BIT_OP(RES, 7, L); break; // RES 7, L + case 0xBE: BIT_OP_IND(RES, 7, L, H); break; // RES 7, (HL) + case 0xBF: BIT_OP(RES, 7, A); break; // RES 7, A + case 0xC0: BIT_OP(SET, 0, B); break; // SET 0, B + case 0xC1: BIT_OP(SET, 0, C); break; // SET 0, C + case 0xC2: BIT_OP(SET, 0, D); break; // SET 0, D + case 0xC3: BIT_OP(SET, 0, E); break; // SET 0, E + case 0xC4: BIT_OP(SET, 0, H); break; // SET 0, H + case 0xC5: BIT_OP(SET, 0, L); break; // SET 0, L + case 0xC6: BIT_OP_IND(SET, 0, L, H); break; // SET 0, (HL) + case 0xC7: BIT_OP(SET, 0, A); break; // SET 0, A + case 0xC8: BIT_OP(SET, 1, B); break; // SET 1, B + case 0xC9: BIT_OP(SET, 1, C); break; // SET 1, C + case 0xCA: BIT_OP(SET, 1, D); break; // SET 1, D + case 0xCB: BIT_OP(SET, 1, E); break; // SET 1, E + case 0xCC: BIT_OP(SET, 1, H); break; // SET 1, H + case 0xCD: BIT_OP(SET, 1, L); break; // SET 1, L + case 0xCE: BIT_OP_IND(SET, 1, L, H); break; // SET 1, (HL) + case 0xCF: BIT_OP(SET, 1, A); break; // SET 1, A + case 0xD0: BIT_OP(SET, 2, B); break; // SET 2, B + case 0xD1: BIT_OP(SET, 2, C); break; // SET 2, C + case 0xD2: BIT_OP(SET, 2, D); break; // SET 2, D + case 0xD3: BIT_OP(SET, 2, E); break; // SET 2, E + case 0xD4: BIT_OP(SET, 2, H); break; // SET 2, H + case 0xD5: BIT_OP(SET, 2, L); break; // SET 2, L + case 0xD6: BIT_OP_IND(SET, 2, L, H); break; // SET 2, (HL) + case 0xD7: BIT_OP(SET, 2, A); break; // SET 2, A + case 0xD8: BIT_OP(SET, 3, B); break; // SET 3, B + case 0xD9: BIT_OP(SET, 3, C); break; // SET 3, C + case 0xDA: BIT_OP(SET, 3, D); break; // SET 3, D + case 0xDB: BIT_OP(SET, 3, E); break; // SET 3, E + case 0xDC: BIT_OP(SET, 3, H); break; // SET 3, H + case 0xDD: BIT_OP(SET, 3, L); break; // SET 3, L + case 0xDE: BIT_OP_IND(SET, 3, L, H); break; // SET 3, (HL) + case 0xDF: BIT_OP(SET, 3, A); break; // SET 3, A + case 0xE0: BIT_OP(SET, 4, B); break; // SET 4, B + case 0xE1: BIT_OP(SET, 4, C); break; // SET 4, C + case 0xE2: BIT_OP(SET, 4, D); break; // SET 4, D + case 0xE3: BIT_OP(SET, 4, E); break; // SET 4, E + case 0xE4: BIT_OP(SET, 4, H); break; // SET 4, H + case 0xE5: BIT_OP(SET, 4, L); break; // SET 4, L + case 0xE6: BIT_OP_IND(SET, 4, L, H); break; // SET 4, (HL) + case 0xE7: BIT_OP(SET, 4, A); break; // SET 4, A + case 0xE8: BIT_OP(SET, 5, B); break; // SET 5, B + case 0xE9: BIT_OP(SET, 5, C); break; // SET 5, C + case 0xEA: BIT_OP(SET, 5, D); break; // SET 5, D + case 0xEB: BIT_OP(SET, 5, E); break; // SET 5, E + case 0xEC: BIT_OP(SET, 5, H); break; // SET 5, H + case 0xED: BIT_OP(SET, 5, L); break; // SET 5, L + case 0xEE: BIT_OP_IND(SET, 5, L, H); break; // SET 5, (HL) + case 0xEF: BIT_OP(SET, 5, A); break; // SET 5, A + case 0xF0: BIT_OP(SET, 6, B); break; // SET 6, B + case 0xF1: BIT_OP(SET, 6, C); break; // SET 6, C + case 0xF2: BIT_OP(SET, 6, D); break; // SET 6, D + case 0xF3: BIT_OP(SET, 6, E); break; // SET 6, E + case 0xF4: BIT_OP(SET, 6, H); break; // SET 6, H + case 0xF5: BIT_OP(SET, 6, L); break; // SET 6, L + case 0xF6: BIT_OP_IND(SET, 6, L, H); break; // SET 6, (HL) + case 0xF7: BIT_OP(SET, 6, A); break; // SET 6, A + case 0xF8: BIT_OP(SET, 7, B); break; // SET 7, B + case 0xF9: BIT_OP(SET, 7, C); break; // SET 7, C + case 0xFA: BIT_OP(SET, 7, D); break; // SET 7, D + case 0xFB: BIT_OP(SET, 7, E); break; // SET 7, E + case 0xFC: BIT_OP(SET, 7, H); break; // SET 7, H + case 0xFD: BIT_OP(SET, 7, L); break; // SET 7, L + case 0xFE: BIT_OP_IND(SET, 7, L, H); break; // SET 7, (HL) + case 0xFF: BIT_OP(SET, 7, A); break; // SET 7, A + } + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs new file mode 100644 index 0000000000..ec93ae031e --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -0,0 +1,66 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + private bool iff1; + public bool IFF1 { get { return iff1; } set { iff1 = value; } } + + private bool iff2; + public bool IFF2 { get { return iff2; } set { iff2 = value; } } + + private bool nonMaskableInterrupt; + public bool NonMaskableInterrupt + { + get { return nonMaskableInterrupt; } + set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; } + } + + private bool nonMaskableInterruptPending; + public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } } + + private int interruptMode; + public int InterruptMode + { + get { return interruptMode; } + set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } + } + + private void INTERRUPT_(ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, PCl, + IDLE, + ASGN, PCl, INT_vectors[src], + IDLE, + ASGN, PCh, 0, + IDLE, + OP }; + } + + private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; + + private void ResetInterrupts() + { + IFF1 = false; + IFF2 = false; + NonMaskableInterrupt = false; + NonMaskableInterruptPending = false; + InterruptMode = 1; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs new file mode 100644 index 0000000000..f1aa8bf317 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -0,0 +1,459 @@ +using System; +using System.Globalization; +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +// GameBoy CPU (Sharp LR35902) +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public sealed partial class LR35902 + { + // 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 ADD16 = 5; + public const ushort ADD8 = 6; + public const ushort SUB8 = 7; + public const ushort ADC8 = 8; + public const ushort SBC8 = 9; + public const ushort INC16 = 10; + public const ushort INC8 = 11; + public const ushort DEC16 = 12; + public const ushort DEC8 = 13; + public const ushort RLC = 14; + public const ushort RL = 15; + public const ushort RRC = 16; + public const ushort RR = 17; + public const ushort CPL = 18; + public const ushort DA = 19; + public const ushort SCF = 20; + public const ushort CCF = 21; + public const ushort AND8 = 22; + public const ushort XOR8 = 23; + public const ushort OR8 = 24; + public const ushort CP8 = 25; + public const ushort SLA = 26; + public const ushort SRA = 27; + public const ushort SRL = 28; + public const ushort SWAP = 29; + public const ushort BIT = 30; + public const ushort RES = 31; + public const ushort SET = 32; + public const ushort EI = 33; + public const ushort DI = 34; + public const ushort HALT = 35; + public const ushort STOP = 36; + public const ushort PREFIX = 37; + public const ushort ASGN = 38; + public const ushort ADDS = 39; // signed 16 bit operation used in 2 instructions + public const ushort OP_G = 40; // glitchy opcode read performed by halt when interrupts disabled + public const ushort JAM = 41; // all undocumented opcodes jam the machine + public const ushort RD_F = 42; // special read case to pop value into F + public const ushort EI_RETI = 43; // reti has no delay in interrupt enable + + public LR35902() + { + Reset(); + } + + public void Reset() + { + ResetRegisters(); + ResetInterrupts(); + TotalExecutedCycles = 0; + cur_instr = new ushort[] { OP }; + } + + // 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; + } + + // Execute instructions + public void ExecuteOne(ref byte interrupt_src, byte interrupt_enable) + { + switch (cur_instr[instr_pntr++]) + { + case IDLE: + // do nothing + break; + case OP: + // Read the opcode of the next instruction + if (EI_pending > 0 && !CB_prefix) + { + EI_pending--; + if (EI_pending == 0) + { + interrupts_enabled = true; + } + } + + if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + { + interrupts_enabled = false; + + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====IRQ====", + RegisterInfo = "" + }); + } + + // call interrupt processor with the appropriate source + // lowest bit set is highest priority + ushort priority = 0; + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { priority = 0; interrupt_src -= 1; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { priority = 1; interrupt_src -= 2; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { priority = 2; interrupt_src -= 4; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { priority = 3; interrupt_src -= 8; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { priority = 4; interrupt_src -= 16; } + else { /*Console.WriteLine("No source"); }*/throw new Exception("Interrupt without Source"); } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + INTERRUPT_(priority); + } + else + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + } + instr_pntr = 0; + break; + case RD: + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case TR: + TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD16: + ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD8: + ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SUB8: + SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADC8: + ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SBC8: + SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case INC16: + INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case INC8: + INC8_Func(cur_instr[instr_pntr++]); + break; + case DEC16: + DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case DEC8: + DEC8_Func(cur_instr[instr_pntr++]); + break; + case RLC: + RLC_Func(cur_instr[instr_pntr++]); + break; + case RL: + RL_Func(cur_instr[instr_pntr++]); + break; + case RRC: + RRC_Func(cur_instr[instr_pntr++]); + break; + case RR: + RR_Func(cur_instr[instr_pntr++]); + break; + case CPL: + CPL_Func(cur_instr[instr_pntr++]); + break; + case DA: + DA_Func(cur_instr[instr_pntr++]); + break; + case SCF: + SCF_Func(cur_instr[instr_pntr++]); + break; + case CCF: + CCF_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 CP8: + CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SLA: + SLA_Func(cur_instr[instr_pntr++]); + break; + case SRA: + SRA_Func(cur_instr[instr_pntr++]); + break; + case SRL: + SRL_Func(cur_instr[instr_pntr++]); + break; + case SWAP: + SWAP_Func(cur_instr[instr_pntr++]); + break; + case BIT: + BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case RES: + RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SET: + SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case EI: + EI_pending = 2; + break; + case DI: + interrupts_enabled = false; + EI_pending = 0; + break; + case HALT: + halted = true; + + if (EI_pending > 0 && !CB_prefix) + { + EI_pending--; + if (EI_pending == 0) + { + interrupts_enabled = true; + } + } + + // if the I flag is asserted at the time of halt, don't halt + + if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + { + interrupts_enabled = false; + + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====IRQ====", + RegisterInfo = "" + }); + } + halted = false; + // call interrupt processor with the appropriate source + // lowest bit set is highest priority + // call interrupt processor with the appropriate source + // lowest bit set is highest priority + ushort priority = 0; + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { priority = 0; interrupt_src -= 1; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { priority = 1; interrupt_src -= 2; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { priority = 2; interrupt_src -= 4; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { priority = 3; interrupt_src -= 8; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { priority = 4; interrupt_src -= 16; } + else { /*Console.WriteLine("No source"); }*/throw new Exception("Interrupt without Source"); } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + instr_pntr = 0; + INTERRUPT_(priority); + } + else if (FlagI) + { + // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-halted====", + RegisterInfo = "" + }); + } + halted = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT }; + } + break; + case STOP: + stopped = true; + + if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though + { + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-stop====", + RegisterInfo = "" + }); + } + + stopped = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + STOP }; + } + break; + case PREFIX: + CB_prefix = true; + break; + case ASGN: + ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADDS: + ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case OP_G: + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + + FetchInstruction(ReadMemory(RegPC)); // note no increment + + instr_pntr = 0; + break; + case JAM: + jammed = true; + instr_pntr--; + break; + case RD_F: + Read_Func_F(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case EI_RETI: + EI_pending = 1; + break; + } + totalExecutedCycles++; + } + + // tracer stuff + + public Action TraceCallback; + + public string TraceHeader + { + get { return "LR35902: PC, machine code, mnemonic, operands, registers (A, F, B, C, D, E, H, L, SP), Cy, flags (ZNHCI)"; } + } + + public TraceInfo State(bool disassemble = true) + { + ushort notused; + + return new TraceInfo + { + Disassembly = string.Format( + "{0} ", + disassemble ? Disassemble(RegPC, ReadMemory, out notused) : "---").PadRight(40), + RegisterInfo = string.Format( + "A:{0:X2} F:{1:X2} B:{2:X2} C:{3:X2} D:{4:X2} E:{5:X2} H:{6:X2} L:{7:X2} SP:{8:X2} Cy:{9} LY:{10} {11}{12}{13}{14}{15}{16}", + Regs[A], + Regs[F], + Regs[B], + Regs[C], + Regs[D], + Regs[E], + Regs[H], + Regs[L], + Regs[SPl] | (Regs[SPh] << 8), + TotalExecutedCycles, + LY, + FlagZ ? "Z" : "z", + FlagN ? "N" : "n", + FlagH ? "H" : "h", + FlagC ? "C" : "c", + FlagI ? "I" : "i", + interrupts_enabled ? "E" : "e") + }; + } + // State Save/Load + + public void SyncState(Serializer ser) + { + ser.BeginSection("LR35902"); + ser.Sync("Regs", ref Regs, false); + ser.Sync("IRQ", ref interrupts_enabled); + ser.Sync("NMI", ref nonMaskableInterrupt); + ser.Sync("NMIPending", ref nonMaskableInterruptPending); + ser.Sync("IM", ref interruptMode); + ser.Sync("IFF1", ref iff1); + ser.Sync("IFF2", ref iff2); + ser.Sync("Halted", ref halted); + ser.Sync("ExecutedCycles", ref totalExecutedCycles); + ser.Sync("EI_pending", ref EI_pending); + + ser.Sync("instruction_pointer", ref instr_pntr); + ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("CB Preifx", ref CB_prefix); + ser.Sync("Stopped", ref stopped); + ser.Sync("opcode", ref opcode); + ser.Sync("jammped", ref jammed); + ser.Sync("LY", ref LY); + ser.Sync("FlagI", ref FlagI); + + ser.EndSection(); + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs new file mode 100644 index 0000000000..4bbb453301 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + // adapted from the information at http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html + public sealed partial class LR35902 + { + static string[] table = + { + "NOP", // 00 + "LD BC,d16", // 01 + "LD (BC),A", // 02 + "INC BC", // 03 + "INC B", // 04 + "DEC B", // 05 + "LD B,d8", // 06 + "RLCA", // 07 + "LD (a16),SP", // 08 + "ADD HL,BC", // 09 + "LD A,(BC)", // 0a + "DEC BC", // 0b + "INC C", // 0c + "DEC C", // 0d + "LD C,d8", // 0e + "RRCA", // 0f + "STOP 0", // 10 + "LD DE,d16", // 11 + "LD (DE),A", // 12 + "INC DE", // 13 + "INC D", // 14 + "DEC D", // 15 + "LD D,d8", // 16 + "RLA", // 17 + "JR r8", // 18 + "ADD HL,DE", // 19 + "LD A,(DE)", // 1a + "DEC DE", // 1b + "INC E", // 1c + "DEC E", // 1d + "LD E,d8", // 1e + "RRA", // 1f + "JR NZ,r8", // 20 + "LD HL,d16", // 21 + "LD (HL+),A", // 22 + "INC HL", // 23 + "INC H", // 24 + "DEC H", // 25 + "LD H,d8", // 26 + "DAA", // 27 + "JR Z,r8", // 28 + "ADD HL,HL", // 29 + "LD A,(HL+)", // 2a + "DEC HL", // 2b + "INC L", // 2c + "DEC L", // 2d + "LD L,d8", // 2e + "CPL", // 2f + "JR NC,r8", // 30 + "LD SP,d16", // 31 + "LD (HL-),A", // 32 + "INC SP", // 33 + "INC (HL)", // 34 + "DEC (HL)", // 35 + "LD (HL),d8", // 36 + "SCF", // 37 + "JR C,r8", // 38 + "ADD HL,SP", // 39 + "LD A,(HL-)", // 3a + "DEC SP", // 3b + "INC A", // 3c + "DEC A", // 3d + "LD A,d8", // 3e + "CCF", // 3f + "LD B,B", // 40 + "LD B,C", // 41 + "LD B,D", // 42 + "LD B,E", // 43 + "LD B,H", // 44 + "LD B,L", // 45 + "LD B,(HL)", // 46 + "LD B,A", // 47 + "LD C,B", // 48 + "LD C,C", // 49 + "LD C,D", // 4a + "LD C,E", // 4b + "LD C,H", // 4c + "LD C,L", // 4d + "LD C,(HL)", // 4e + "LD C,A", // 4f + "LD D,B", // 50 + "LD D,C", // 51 + "LD D,D", // 52 + "LD D,E", // 53 + "LD D,H", // 54 + "LD D,L", // 55 + "LD D,(HL)", // 56 + "LD D,A", // 57 + "LD E,B", // 58 + "LD E,C", // 59 + "LD E,D", // 5a + "LD E,E", // 5b + "LD E,H", // 5c + "LD E,L", // 5d + "LD E,(HL)", // 5e + "LD E,A", // 5f + "LD H,B", // 60 + "LD H,C", // 61 + "LD H,D", // 62 + "LD H,E", // 63 + "LD H,H", // 64 + "LD H,L", // 65 + "LD H,(HL)", // 66 + "LD H,A", // 67 + "LD L,B", // 68 + "LD L,C", // 69 + "LD L,D", // 6a + "LD L,E", // 6b + "LD L,H", // 6c + "LD L,L", // 6d + "LD L,(HL)", // 6e + "LD L,A", // 6f + "LD (HL),B", // 70 + "LD (HL),C", // 71 + "LD (HL),D", // 72 + "LD (HL),E", // 73 + "LD (HL),H", // 74 + "LD (HL),L", // 75 + "HALT", // 76 + "LD (HL),A", // 77 + "LD A,B", // 78 + "LD A,C", // 79 + "LD A,D", // 7a + "LD A,E", // 7b + "LD A,H", // 7c + "LD A,L", // 7d + "LD A,(HL)", // 7e + "LD A,A", // 7f + "ADD A,B", // 80 + "ADD A,C", // 81 + "ADD A,D", // 82 + "ADD A,E", // 83 + "ADD A,H", // 84 + "ADD A,L", // 85 + "ADD A,(HL)", // 86 + "ADD A,A", // 87 + "ADC A,B", // 88 + "ADC A,C", // 89 + "ADC A,D", // 8a + "ADC A,E", // 8b + "ADC A,H", // 8c + "ADC A,L", // 8d + "ADC A,(HL)", // 8e + "ADC A,A", // 8f + "SUB B", // 90 + "SUB C", // 91 + "SUB D", // 92 + "SUB E", // 93 + "SUB H", // 94 + "SUB L", // 95 + "SUB (HL)", // 96 + "SUB A", // 97 + "SBC A,B", // 98 + "SBC A,C", // 99 + "SBC A,D", // 9a + "SBC A,E", // 9b + "SBC A,H", // 9c + "SBC A,L", // 9d + "SBC A,(HL)", // 9e + "SBC A,A", // 9f + "AND B", // a0 + "AND C", // a1 + "AND D", // a2 + "AND E", // a3 + "AND H", // a4 + "AND L", // a5 + "AND (HL)", // a6 + "AND A", // a7 + "XOR B", // a8 + "XOR C", // a9 + "XOR D", // aa + "XOR E", // ab + "XOR H", // ac + "XOR L", // ad + "XOR (HL)", // ae + "XOR A", // af + "OR B", // b0 + "OR C", // b1 + "OR D", // b2 + "OR E", // b3 + "OR H", // b4 + "OR L", // b5 + "OR (HL)", // b6 + "OR A", // b7 + "CP B", // b8 + "CP C", // b9 + "CP D", // ba + "CP E", // bb + "CP H", // bc + "CP L", // bd + "CP (HL)", // be + "CP A", // bf + "RET NZ", // c0 + "POP BC", // c1 + "JP NZ,a16", // c2 + "JP a16", // c3 + "CALL NZ,a16", // c4 + "PUSH BC", // c5 + "ADD A,d8", // c6 + "RST 00H", // c7 + "RET Z", // c8 + "RET", // c9 + "JP Z,a16", // ca + "PREFIX CB", // cb + "CALL Z,a16", // cc + "CALL a16", // cd + "ADC A,d8", // ce + "RST 08H", // cf + "RET NC", // d0 + "POP DE", // d1 + "JP NC,a16", // d2 + "???", // d3 + "CALL NC,a16", // d4 + "PUSH DE", // d5 + "SUB d8", // d6 + "RST 10H", // d7 + "RET C", // d8 + "RETI", // d9 + "JP C,a16", // da + "???", // db + "CALL C,a16", // dc + "???", // dd + "SBC A,d8", // de + "RST 18H", // df + "LDH (a8),A", // e0 + "POP HL", // e1 + "LD (C),A", // e2 + "???", // e3 + "???", // e4 + "PUSH HL", // e5 + "AND d8", // e6 + "RST 20H", // e7 + "ADD SP,r8", // e8 + "JP (HL)", // e9 + "LD (a16),A", // ea + "???", // eb + "???", // ec + "???", // ed + "XOR d8", // ee + "RST 28H", // ef + "LDH A,(a8)", // f0 + "POP AF", // f1 + "LD A,(C)", // f2 + "DI", // f3 + "???", // f4 + "PUSH AF", // f5 + "OR d8", // f6 + "RST 30H", // f7 + "LD HL,SP+r8", // f8 + "LD SP,HL", // f9 + "LD A,(a16)", // fa + "EI ", // fb + "???", // fc + "???", // fd + "CP d8", // fe + "RST 38H", // ff + "RLC B", // 00 + "RLC C", // 01 + "RLC D", // 02 + "RLC E", // 03 + "RLC H", // 04 + "RLC L", // 05 + "RLC (HL)", // 06 + "RLC A", // 07 + "RRC B", // 08 + "RRC C", // 09 + "RRC D", // 0a + "RRC E", // 0b + "RRC H", // 0c + "RRC L", // 0d + "RRC (HL)", // 0e + "RRC A", // 0f + "RL B", // 10 + "RL C", // 11 + "RL D", // 12 + "RL E", // 13 + "RL H", // 14 + "RL L", // 15 + "RL (HL)", // 16 + "RL A", // 17 + "RR B", // 18 + "RR C", // 19 + "RR D", // 1a + "RR E", // 1b + "RR H", // 1c + "RR L", // 1d + "RR (HL)", // 1e + "RR A", // 1f + "SLA B", // 20 + "SLA C", // 21 + "SLA D", // 22 + "SLA E", // 23 + "SLA H", // 24 + "SLA L", // 25 + "SLA (HL)", // 26 + "SLA A", // 27 + "SRA B", // 28 + "SRA C", // 29 + "SRA D", // 2a + "SRA E", // 2b + "SRA H", // 2c + "SRA L", // 2d + "SRA (HL)", // 2e + "SRA A", // 2f + "SWAP B", // 30 + "SWAP C", // 31 + "SWAP D", // 32 + "SWAP E", // 33 + "SWAP H", // 34 + "SWAP L", // 35 + "SWAP (HL)", // 36 + "SWAP A", // 37 + "SRL B", // 38 + "SRL C", // 39 + "SRL D", // 3a + "SRL E", // 3b + "SRL H", // 3c + "SRL L", // 3d + "SRL (HL)", // 3e + "SRL A", // 3f + "BIT 0,B", // 40 + "BIT 0,C", // 41 + "BIT 0,D", // 42 + "BIT 0,E", // 43 + "BIT 0,H", // 44 + "BIT 0,L", // 45 + "BIT 0,(HL)", // 46 + "BIT 0,A", // 47 + "BIT 1,B", // 48 + "BIT 1,C", // 49 + "BIT 1,D", // 4a + "BIT 1,E", // 4b + "BIT 1,H", // 4c + "BIT 1,L", // 4d + "BIT 1,(HL)", // 4e + "BIT 1,A", // 4f + "BIT 2,B", // 50 + "BIT 2,C", // 51 + "BIT 2,D", // 52 + "BIT 2,E", // 53 + "BIT 2,H", // 54 + "BIT 2,L", // 55 + "BIT 2,(HL)", // 56 + "BIT 2,A", // 57 + "BIT 3,B", // 58 + "BIT 3,C", // 59 + "BIT 3,D", // 5a + "BIT 3,E", // 5b + "BIT 3,H", // 5c + "BIT 3,L", // 5d + "BIT 3,(HL)", // 5e + "BIT 3,A", // 5f + "BIT 4,B", // 60 + "BIT 4,C", // 61 + "BIT 4,D", // 62 + "BIT 4,E", // 63 + "BIT 4,H", // 64 + "BIT 4,L", // 65 + "BIT 4,(HL)", // 66 + "BIT 4,A", // 67 + "BIT 5,B", // 68 + "BIT 5,C", // 69 + "BIT 5,D", // 6a + "BIT 5,E", // 6b + "BIT 5,H", // 6c + "BIT 5,L", // 6d + "BIT 5,(HL)", // 6e + "BIT 5,A", // 6f + "BIT 6,B", // 70 + "BIT 6,C", // 71 + "BIT 6,D", // 72 + "BIT 6,E", // 73 + "BIT 6,H", // 74 + "BIT 6,L", // 75 + "BIT 6,(HL)", // 76 + "BIT 6,A", // 77 + "BIT 7,B", // 78 + "BIT 7,C", // 79 + "BIT 7,D", // 7a + "BIT 7,E", // 7b + "BIT 7,H", // 7c + "BIT 7,L", // 7d + "BIT 7,(HL)", // 7e + "BIT 7,A", // 7f + "RES 0,B", // 80 + "RES 0,C", // 81 + "RES 0,D", // 82 + "RES 0,E", // 83 + "RES 0,H", // 84 + "RES 0,L", // 85 + "RES 0,(HL)", // 86 + "RES 0,A", // 87 + "RES 1,B", // 88 + "RES 1,C", // 89 + "RES 1,D", // 8a + "RES 1,E", // 8b + "RES 1,H", // 8c + "RES 1,L", // 8d + "RES 1,(HL)", // 8e + "RES 1,A", // 8f + "RES 2,B", // 90 + "RES 2,C", // 91 + "RES 2,D", // 92 + "RES 2,E", // 93 + "RES 2,H", // 94 + "RES 2,L", // 95 + "RES 2,(HL)", // 96 + "RES 2,A", // 97 + "RES 3,B", // 98 + "RES 3,C", // 99 + "RES 3,D", // 9a + "RES 3,E", // 9b + "RES 3,H", // 9c + "RES 3,L", // 9d + "RES 3,(HL)", // 9e + "RES 3,A", // 9f + "RES 4,B", // a0 + "RES 4,C", // a1 + "RES 4,D", // a2 + "RES 4,E", // a3 + "RES 4,H", // a4 + "RES 4,L", // a5 + "RES 4,(HL)", // a6 + "RES 4,A", // a7 + "RES 5,B", // a8 + "RES 5,C", // a9 + "RES 5,D", // aa + "RES 5,E", // ab + "RES 5,H", // ac + "RES 5,L", // ad + "RES 5,(HL)", // ae + "RES 5,A", // af + "RES 6,B", // b0 + "RES 6,C", // b1 + "RES 6,D", // b2 + "RES 6,E", // b3 + "RES 6,H", // b4 + "RES 6,L", // b5 + "RES 6,(HL)", // b6 + "RES 6,A", // b7 + "RES 7,B", // b8 + "RES 7,C", // b9 + "RES 7,D", // ba + "RES 7,E", // bb + "RES 7,H", // bc + "RES 7,L", // bd + "RES 7,(HL)", // be + "RES 7,A", // bf + "SET 0,B", // c0 + "SET 0,C", // c1 + "SET 0,D", // c2 + "SET 0,E", // c3 + "SET 0,H", // c4 + "SET 0,L", // c5 + "SET 0,(HL)", // c6 + "SET 0,A", // c7 + "SET 1,B", // c8 + "SET 1,C", // c9 + "SET 1,D", // ca + "SET 1,E", // cb + "SET 1,H", // cc + "SET 1,L", // cd + "SET 1,(HL)", // ce + "SET 1,A", // cf + "SET 2,B", // d0 + "SET 2,C", // d1 + "SET 2,D", // d2 + "SET 2,E", // d3 + "SET 2,H", // d4 + "SET 2,L", // d5 + "SET 2,(HL)", // d6 + "SET 2,A", // d7 + "SET 3,B", // d8 + "SET 3,C", // d9 + "SET 3,D", // da + "SET 3,E", // db + "SET 3,H", // dc + "SET 3,L", // dd + "SET 3,(HL)", // de + "SET 3,A", // df + "SET 4,B", // e0 + "SET 4,C", // e1 + "SET 4,D", // e2 + "SET 4,E", // e3 + "SET 4,H", // e4 + "SET 4,L", // e5 + "SET 4,(HL)", // e6 + "SET 4,A", // e7 + "SET 5,B", // e8 + "SET 5,C", // e9 + "SET 5,D", // ea + "SET 5,E", // eb + "SET 5,H", // ec + "SET 5,L", // ed + "SET 5,(HL)", // ee + "SET 5,A", // ef + "SET 6,B", // f0 + "SET 6,C", // f1 + "SET 6,D", // f2 + "SET 6,E", // f3 + "SET 6,H", // f4 + "SET 6,L", // f5 + "SET 6,(HL)", // f6 + "SET 6,A", // f7 + "SET 7,B", // f8 + "SET 7,C", // f9 + "SET 7,D", // fa + "SET 7,E", // fb + "SET 7,H", // fc + "SET 7,L", // fd + "SET 7,(HL)", // fe + "SET 7,A", // 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 (bytes[0] == 0xcb) + { + bytes.Add(reader(addr++)); + result = table[bytes[1] + 256]; + } + + if (result.Contains("d8")) + { + byte d = reader(addr++); + bytes.Add(d); + result = result.Replace("d8", string.Format("#{0:X2}h", d)); + } + else if (result.Contains("d16")) + { + byte dlo = reader(addr++); + byte dhi = reader(addr++); + bytes.Add(dlo); + bytes.Add(dhi); + result = result.Replace("d16", string.Format("#{0:X2}{1:X2}h", dhi, dlo)); + } + else if (result.Contains("a16")) + { + byte dlo = reader(addr++); + byte dhi = reader(addr++); + bytes.Add(dlo); + bytes.Add(dhi); + result = result.Replace("a16", string.Format("#{0:X2}{1:X2}h", dhi, dlo)); + } + else if (result.Contains("a8")) + { + byte d = reader(addr++); + bytes.Add(d); + result = result.Replace("a8", string.Format("#FF{0:X2}h", d)); + } + else if (result.Contains("r8")) + { + byte d = reader(addr++); + bytes.Add(d); + int offs = d; + if (offs >= 128) + offs -= 256; + result = result.Replace("r8", string.Format("{0:X4}h", (ushort)(addr + offs))); + } + 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 < 17) + ret.Append(' '); + ret.Append(result); + size = (ushort)(addr - origaddr); + return ret.ToString(); + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs new file mode 100644 index 0000000000..582c2cd437 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs @@ -0,0 +1,467 @@ +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + public void Read_Func(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + } + + // speical read for POP AF that always clears the lower 4 bits of F + public void Read_Func_F(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = (ushort)(ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)) & 0xF0); + } + + public void Write_Func(ushort dest_l, ushort dest_h, ushort src) + { + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h]) << 8), (byte)Regs[src]); + } + + public void TR_Func(ushort dest, ushort src) + { + Regs[dest] = Regs[src]; + } + + public void ADD16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + int Reg16_d = Regs[dest_l] | (Regs[dest_h] << 8); + int Reg16_s = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d += Reg16_s; + + FlagC = Reg16_d.Bit(16); + + ushort ans_l = (ushort)(Reg16_d & 0xFF); + ushort ans_h = (ushort)((Reg16_d & 0xFF00) >> 8); + + // redo for half carry flag + Reg16_d = Regs[dest_l] | ((Regs[dest_h] & 0x0F) << 8); + Reg16_s = Regs[src_l] | ((Regs[src_h] & 0x0F) << 8); + + Reg16_d += Reg16_s; + + FlagH = Reg16_d.Bit(12); + FlagN = false; + + Regs[dest_l] = ans_l; + Regs[dest_h] = ans_h; + } + + public void ADD8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d += Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + + FlagN = false; + + 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); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[dest] = ans; + } + + public void BIT_Func(ushort bit, ushort src) + { + FlagZ = !Regs[src].Bit(bit); + FlagH = true; + FlagN = false; + } + + public void SET_Func(ushort bit, ushort src) + { + Regs[src] |= (ushort)(1 << bit); + } + + public void RES_Func(ushort bit, ushort src) + { + Regs[src] &= (ushort)(0xFF - (1 << bit)); + } + + public void ASGN_Func(ushort src, ushort val) + { + Regs[src] = val; + } + + public void SWAP_Func(ushort src) + { + ushort temp = (ushort)((Regs[src] << 4) & 0xF0); + Regs[src] = (ushort)(temp | (Regs[src] >> 4)); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + FlagC = false; + } + + public void SLA_Func(ushort src) + { + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)((Regs[src] << 1) & 0xFF); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void SRA_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); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void SRL_Func(ushort src) + { + FlagC = Regs[src].Bit(0) ? true : false; + + Regs[src] = (ushort)(Regs[src] >> 1); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void CPL_Func(ushort src) + { + Regs[src] = (ushort)((~Regs[src]) & 0xFF); + + FlagH = true; + FlagN = true; + } + + public void CCF_Func(ushort src) + { + FlagC = !FlagC; + FlagH = false; + FlagN = false; + } + + public void SCF_Func(ushort src) + { + FlagC = true; + FlagH = false; + FlagN = false; + } + + public void AND8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] & Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = true; + FlagN = false; + } + + public void OR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] | Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = false; + FlagN = false; + } + + public void XOR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] ^ Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = false; + FlagN = false; + } + + public void CP8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d -= Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + + FlagN = true; + } + + public void RRC_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)((FlagC ? 0x80 : 0) | (Regs[src] >> 1)); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RR_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(FlagC ? 0x80 : 0); + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)(c | (Regs[src] >> 1)); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RLC_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(Regs[src].Bit(7) ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RL_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(FlagC ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void INC8_Func(ushort src) + { + int Reg16_d = Regs[src]; + Reg16_d += 1; + + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[src] & 0xF; + Reg16_d += 1; + + FlagH = Reg16_d.Bit(4); + FlagN = false; + + Regs[src] = ans; + } + + public void DEC8_Func(ushort src) + { + int Reg16_d = Regs[src]; + Reg16_d -= 1; + + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[src] & 0xF; + Reg16_d -= 1; + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[src] = ans; + } + + public void INC16_Func(ushort src_l, ushort src_h) + { + int Reg16_d = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d += 1; + + Regs[src_l] = (ushort)(Reg16_d & 0xFF); + Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8); + } + + public void DEC16_Func(ushort src_l, ushort src_h) + { + int Reg16_d = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d -= 1; + + Regs[src_l] = (ushort)(Reg16_d & 0xFF); + Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8); + } + + 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); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += ((Regs[src] & 0xF) + c); + + FlagH = Reg16_d.Bit(4); + FlagN = false; + + Regs[dest] = ans; + } + + public void SBC8_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); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= ((Regs[src] & 0xF) + c); + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[dest] = ans; + } + + // DA code courtesy of AWJ: http://forums.nesdev.com/viewtopic.php?f=20&t=15944 + public void DA_Func(ushort src) + { + byte a = (byte)Regs[src]; + + if (!FlagN) + { // after an addition, adjust if (half-)carry occurred or if result is out of bounds + if (FlagC || a > 0x99) { a += 0x60; FlagC = true; } + if (FlagH || (a & 0x0f) > 0x09) { a += 0x6; } + } + else + { // after a subtraction, only adjust if (half-)carry occurred + if (FlagC) { a -= 0x60; } + if (FlagH) { a -= 0x6; } + } + + a &= 0xFF; + + Regs[src] = a; + + FlagZ = a == 0; + FlagH = false; + } + + // used for signed operations + public void ADDS_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + int Reg16_d = Regs[dest_l]; + int Reg16_s = Regs[src_l]; + + Reg16_d += Reg16_s; + + ushort temp = 0; + + // since this is signed addition, calculate the high byte carry appropriately + if (Reg16_s.Bit(7)) + { + if (((Reg16_d & 0xFF) >= Regs[dest_l])) + { + temp = 0xFF; + } + else + { + temp = 0; + } + } + else + { + temp = (ushort)(Reg16_d.Bit(8) ? 1 : 0); + } + + ushort ans_l = (ushort)(Reg16_d & 0xFF); + + // JR operations do not effect flags + if (dest_l != PCl) + { + FlagC = Reg16_d.Bit(8); + + // redo for half carry flag + Reg16_d = Regs[dest_l] & 0xF; + Reg16_d += Regs[src_l] & 0xF; + + FlagH = Reg16_d.Bit(4); + FlagN = false; + FlagZ = false; + } + + Regs[dest_l] = ans_l; + Regs[dest_h] += temp; + Regs[dest_h] &= 0xFF; + + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs new file mode 100644 index 0000000000..8456f6a157 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs @@ -0,0 +1,73 @@ +using System.Runtime.InteropServices; +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + // registers + + public static ushort PCl = 0; + public static ushort PCh = 1; + public static ushort SPl = 2; + public static ushort SPh = 3; + public static ushort A = 4; + public static ushort F = 5; + public static ushort B = 6; + public static ushort C = 7; + public static ushort D = 8; + public static ushort E = 9; + public static ushort H = 10; + public static ushort L = 11; + public static ushort W = 12; + public static ushort Z = 13; + public static ushort Aim = 14; // use this indicator for RLCA etc., since the Z flag is reset on those + + public ushort[] Regs = new ushort[14]; + + public bool FlagI; + + public bool FlagC + { + get { return (Regs[5] & 0x10) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x10) | (value ? 0x10 : 0x00)); } + } + + public bool FlagH + { + get { return (Regs[5] & 0x20) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x20) | (value ? 0x20 : 0x00)); } + } + + public bool FlagN + { + get { return (Regs[5] & 0x40) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x40) | (value ? 0x40 : 0x00)); } + } + + public bool FlagZ + { + get { return (Regs[5] & 0x80) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x80) | (value ? 0x80 : 0x00)); } + } + + public ushort RegPC + { + get { return (ushort)(Regs[0] | (Regs[1] << 8)); } + set + { + Regs[0] = (ushort)(value & 0xFF); + Regs[1] = (ushort)((value >> 8) & 0xFF); + } + } + + private void ResetRegisters() + { + for (int i=0; i < 14; i++) + { + Regs[i] = 0; + } + } + + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs new file mode 100644 index 0000000000..5979671599 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -0,0 +1,498 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + // this contains the vectors of instrcution operations + // NOTE: This list is NOT confirmed accurate for each individual cycle + + private void NOP_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP }; + } + + private void INC_16(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {INC16, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void DEC_16(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {DEC16, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {ADD16, dest_l, dest_h, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void REG_OP(ushort operation, ushort dest, ushort src) + { + cur_instr = new ushort[] + {operation, dest, src, + IDLE, + IDLE, + OP }; + } + + private void STOP_() + { + cur_instr = new ushort[] + {RD, Z, PCl, PCh, + INC16, PCl, PCh, + IDLE, + STOP }; + } + + private void HALT_() + { + if (!FlagI) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT }; + } + else + { + // if interrupts are disabled, + // a glitchy decrement to the program counter happens + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP_G}; + } + + } + + private void JR_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + ASGN, Z, 0, + IDLE, + ADDS, PCl, PCh, W, Z, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void JP_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + TR, PCl, W, + IDLE, + TR, PCh, Z, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void RET_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void RETI_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + EI_RETI, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void RET_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + } + + private void CALL_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + INC16, PCl, PCh, + IDLE, + IDLE, + RD, Z, PCl, PCh, + INC16, PCl, PCh, + IDLE, + DEC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + IDLE, + DEC16, SPl, SPh, + WR, SPl, SPh, PCl, + IDLE, + TR, PCl, W, + TR, PCh, Z, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void INT_OP(ushort operation, ushort src) + { + cur_instr = new ushort[] + {operation, src, + IDLE, + IDLE, + OP }; + } + + private void BIT_OP(ushort operation, ushort bit, ushort src) + { + cur_instr = new ushort[] + {operation, bit, src, + IDLE, + IDLE, + OP }; + } + + private void PUSH_(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, src_h, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, src_l, + IDLE, + IDLE, + IDLE, + OP }; + } + + // NOTE: this is the only instruction that can write to F + // but the bottom 4 bits of F are always 0, so instead of putting a special check for every read op + // let's just put a special operation here specifically for F + private void POP_(ushort src_l, ushort src_h) + { + if (src_l != F) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, src_l, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, src_h, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD_F, src_l, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, src_h, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + OP }; + } + } + + private void RST_(ushort n) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + WR, SPl, SPh, PCh, + DEC16, SPl, SPh, + IDLE, + IDLE, + WR, SPl, SPh, PCl, + IDLE, + ASGN, PCh, 0, + ASGN, PCl, n, + OP }; + } + + private void PREFIX_() + { + cur_instr = new ushort[] + {PREFIX, + IDLE, + IDLE, + OP }; + } + + private void DI_() + { + cur_instr = new ushort[] + {DI, + IDLE, + IDLE, + OP }; + } + + private void EI_() + { + cur_instr = new ushort[] + {EI, + IDLE, + IDLE, + OP }; + } + + private void JP_HL() + { + cur_instr = new ushort[] + {TR, PCl, L, + IDLE, + TR, PCh, H, + OP }; + } + + private void ADD_SP() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + ASGN, Z, 0, + IDLE, + ADDS, SPl, SPh, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_SP_HL() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + TR, SPl, L, + IDLE, + TR, SPh, H, + IDLE, + OP }; + } + + private void LD_HL_SPn() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + TR, H, SPh, + TR, L, SPl, + ASGN, Z, 0, + ADDS, L, H, W, Z, + OP }; + } + + private void JAM_() + { + cur_instr = new ushort[] + {JAM, + IDLE, + IDLE, + IDLE }; + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs new file mode 100644 index 0000000000..59b3ed63bc --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs @@ -0,0 +1,339 @@ +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + private void INT_OP_IND(ushort operation, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, bit, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void BIT_TE_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, bit, Z, + IDLE, + OP }; + } + + private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, dest, Z, + INC16, src_l, src_h, + OP }; + } + + private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, dest, Z, + IDLE, + OP }; + } + + private void LD_R_IM(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + WR, W, Z, dest_l, + IDLE, + INC16, W, Z, + IDLE, + WR, W, Z, dest_h, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_INC(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + INC16, dest_l, dest_h, + IDLE, + OP }; + } + + private void LD_8_IND_DEC(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + DEC16, dest_l, dest_h, + IDLE, + OP }; + } + + private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + WR, dest_l, dest_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + OP }; + } + + private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest, src_l, src_h, + IDLE, + DEC16, src_l, src_h, + IDLE, + OP }; + } + + private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest_l, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, dest_h, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + OP }; + } + + private void INC_8_IND(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC8, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void DEC_8_IND(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + DEC8, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void LD_8_IND_FF(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + INC16, src_l, src_h, + IDLE, + ASGN, Z , 0xFF, + RD, dest, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FF_IND_8(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, dest_l, dest_h, + INC16, dest_l, dest_h, + IDLE, + ASGN, Z , 0xFF, + WR, W, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_FFC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + ASGN, Z , 0xFF, + RD, dest, C, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FFC_IND_8(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + ASGN, Z , 0xFF, + WR, C, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_16_IND_FF(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, dest, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FF_IND_16(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, dest_l, dest_h, + IDLE, + INC16, dest_l, dest_h, + IDLE, + RD, Z, dest_l, dest_h, + IDLE, + INC16, dest_l, dest_h, + IDLE, + WR, W, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + } +} From 6f73e1de1fdac1c250f4d524400c3d0999ea7feb Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:16:24 -0400 Subject: [PATCH 03/28] Create ReadMe.txt --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt @@ -0,0 +1 @@ +TODO: From 6237a284fa3582d9ad980f1253afabb8eb336970 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:18:28 -0400 Subject: [PATCH 04/28] Add files via upload --- .../Consoles/Nintendo/GBHawk/Audio.cs | 213 ++++ .../Nintendo/GBHawk/GBHawk.IDebuggable.cs | 77 ++ .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 156 +++ .../Nintendo/GBHawk/GBHawk.IInputPollable.cs | 24 + .../Nintendo/GBHawk/GBHawk.IMemoryDomains.cs | 56 ++ .../Nintendo/GBHawk/GBHawk.ISaveRam.cs | 26 + .../Nintendo/GBHawk/GBHawk.ISettable.cs | 77 ++ .../Nintendo/GBHawk/GBHawk.IStatable.cs | 102 ++ .../Consoles/Nintendo/GBHawk/GBHawk.cs | 223 +++++ .../Nintendo/GBHawk/GBHawkControllerDeck.cs | 76 ++ .../Nintendo/GBHawk/GBHawkControllers.cs | 68 ++ .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 283 ++++++ .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 164 ++++ .../Consoles/Nintendo/GBHawk/PPU.cs | 922 ++++++++++++++++++ .../Consoles/Nintendo/GBHawk/Timer.cs | 175 ++++ 15 files changed, 2642 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs new file mode 100644 index 0000000000..01a3078e1c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -0,0 +1,213 @@ +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Audio Emulation + public class Audio : ISoundProvider + { + public GBHawk Core { get; set; } + + 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, + 0x3F, 0x00, 0xFF, 0xBF, + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, + 0xFF, 0x00, 0x00, 0xBF, + 0x00, 0x00, 0x70}; + + public byte[] Audio_Regs = new byte[21]; + + public byte[] Wave_RAM = new byte [16]; + + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) + case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) + case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) + case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) + case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) + case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) + case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) + case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) + case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) + case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) + case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) + case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) + case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (sweep) + case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (sweep) + case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (sweep) + case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (sweep) + case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (sweep) + case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (sweep) + case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (sweep) + + // wave ram table + 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 = Wave_RAM[addr & 0x0F]; + break; + + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF10: Audio_Regs[NR10] = value; break; // NR10 (sweep) + case 0xFF11: Audio_Regs[NR11] = value; break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: Audio_Regs[NR12] = value; break; // NR12 (envelope) + case 0xFF13: Audio_Regs[NR13] = value; break; // NR13 (freq low) + case 0xFF14: Audio_Regs[NR14] = value; break; // NR14 (freq hi) + case 0xFF16: Audio_Regs[NR21] = value; break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: Audio_Regs[NR22] = value; break; // NR22 (envelope) + case 0xFF18: Audio_Regs[NR23] = value; break; // NR23 (freq low) + case 0xFF19: Audio_Regs[NR24] = value; break; // NR24 (freq hi) + case 0xFF1A: Audio_Regs[NR30] = value; break; // NR30 (on/off) + case 0xFF1B: Audio_Regs[NR31] = value; break; // NR31 (length) + case 0xFF1C: Audio_Regs[NR32] = value; break; // NR32 (level output) + case 0xFF1D: Audio_Regs[NR33] = value; break; // NR33 (freq low) + case 0xFF1E: Audio_Regs[NR34] = value; break; // NR34 (freq hi) + case 0xFF20: Audio_Regs[NR41] = value; break; // NR41 (sweep) + case 0xFF21: Audio_Regs[NR42] = value; break; // NR42 (sweep) + case 0xFF22: Audio_Regs[NR43] = value; break; // NR43 (sweep) + case 0xFF23: Audio_Regs[NR44] = value; break; // NR44 (sweep) + case 0xFF24: Audio_Regs[NR50] = value; break; // NR50 (sweep) + case 0xFF25: Audio_Regs[NR51] = value; break; // NR51 (sweep) + case 0xFF26: Audio_Regs[NR52] = value; break; // NR52 (sweep) + + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + + } + } + + public void tick() + { + + } + + public void reset() + { + Wave_RAM = new byte[16]; + + Audio_Regs = new byte[21]; + } + + public void SyncState(Serializer ser) + { + ser.Sync("Audio_Regs", ref Audio_Regs, false); + ser.Sync("Wave_Ram", ref Wave_RAM, false); + + } + + #region audio + + public bool CanProvideAsync => false; + + public int _spf; + public int AudioClocks; + + 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) + { + short[] ret = new short[_spf * 2]; + nsamp = _spf; + GetSamples(ret); + samples = ret; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + AudioClocks = 0; + } + + // Exposing this as GetSamplesAsync would allow this to provide async sound + // However, it does nothing special for async sound so I don't see a point + private void GetSamples(short[] samples) + { + + } + + #endregion + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs new file mode 100644 index 0000000000..d738c63fe4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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(); + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public int TotalExecutedCycles + { + get { return cpu.TotalExecutedCycles; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs new file mode 100644 index 0000000000..77fe21cc28 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -0,0 +1,156 @@ +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IVideoProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public byte controller_state; + public byte controller_state_old; + public bool in_vblank_old; + public bool in_vblank; + public bool vblank_rise; + + public void 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")) + { + // it seems that theMachine.Reset() doesn't clear ram, etc + // this should leave hsram intact but clear most other things + HardReset(); + } + + _islag = true; + + GetControllerState(controller); + + do_frame(); + + if (_islag) + { + _lagcount++; + } + } + + public void do_frame() + { + // gameboy frames can be variable lengths + // we want to end a frame when VBlank turns from false to true + int ticker = 0; + while (!vblank_rise && (ticker < 100000)) + { + audio.tick(); + timer.tick_1(); + ppu.tick(); + + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + + timer.tick_2(); + + + if (in_vblank && !in_vblank_old) + { + vblank_rise = true; + } + ticker++; + in_vblank_old = in_vblank; + } + + vblank_rise = false; + } + + public void RunCPUCycle() + { + + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + controller_state = _controllerDeck.ReadPort1(controller); + + // set interrupt flag if a pin went from high to low + if (controller_state < controller_state_old) + { + if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } + REG_FF0F |= 0x10; + } + + controller_state_old = controller_state; + } + + public void serial_transfer() + { + if (serial_control.Bit(7) && !serial_start_old) + { + serial_start_old = true; + + // transfer out on byte of data + // needs to be modelled + } + } + + public int Frame => _frame; + + public string SystemId => "GB"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + + } + + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer; + + public int[] GetVideoBuffer() + { + return _vidbuffer; + } + + 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 = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs new file mode 100644 index 0000000000..00c5112275 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs new file mode 100644 index 0000000000..971b3c3c2d --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + 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( + "Zero Page RAM", + ZP_RAM.Length, + MemoryDomain.Endian.Little, + addr => ZP_RAM[addr], + (addr, value) => ZP_RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "System Bus", + 0X10000, + MemoryDomain.Endian.Little, + addr => PeekSystemBus(addr), + (addr, value) => PokeSystemBus(addr, value), + 1) + }; + + MemoryDomains = new MemoryDomainList(domains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + + private byte PeekSystemBus(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + return ReadMemory(addr2); + } + + private void PokeSystemBus(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + WriteMemory(addr2, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs new file mode 100644 index 0000000000..d1fa270c30 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -0,0 +1,26 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : ISaveRam + { + public byte[] CloneSaveRam() + { + return (byte[])_sram.Clone(); + } + + public void StoreSaveRam(byte[] data) + { + Buffer.BlockCopy(data, 0, _sram, 0, data.Length); + } + + public bool SaveRamModified + { + get + { + return false; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs new file mode 100644 index 0000000000..55d2cc130e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -0,0 +1,77 @@ +using System; +using System.ComponentModel; + +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IStatable, ISettable + { + public GBSettings GetSettings() + { + return _settings.Clone(); + } + + public GBSyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(GBSettings o) + { + _settings = o; + return false; + } + + public bool PutSyncSettings(GBSyncSettings o) + { + bool ret = GBSyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private GBSettings _settings = new GBSettings(); + public GBSyncSettings _syncSettings = new GBSyncSettings(); + + public class GBSettings + { + public GBSettings Clone() + { + return (GBSettings)MemberwiseClone(); + } + } + + public class GBSyncSettings + { + private string _port1 = GBHawkControllerDeck.DefaultControllerName; + + [JsonIgnore] + public string Port1 + { + get { return _port1; } + set + { + if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port1 = value; + } + } + + public GBSyncSettings Clone() + { + return (GBSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GBSyncSettings x, GBSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs new file mode 100644 index 0000000000..c228de6e1e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -0,0 +1,102 @@ +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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); + timer.SyncState(ser); + ppu.SyncState(ser); + audio.SyncState(ser); + + ser.BeginSection("Gameboy"); + ser.Sync("core", ref core, false); + ser.Sync("Lag", ref _lagcount); + ser.Sync("Frame", ref _frame); + ser.Sync("IsLag", ref _islag); + _controllerDeck.SyncState(ser); + + ser.Sync("controller_state", ref controller_state); + ser.Sync("controller_state_old", ref controller_state_old); + ser.Sync("in_vblank", ref in_vblank); + ser.Sync("in_vblank_old", ref in_vblank_old); + ser.Sync("vblank_rise", ref vblank_rise); + ser.Sync("GB_bios_register", ref GB_bios_register); + ser.Sync("input_register", ref input_register); + + ser.Sync("serial_control", ref serial_control); + ser.Sync("serial_data_out", ref serial_data_out); + ser.Sync("serial_data_in", ref serial_data_in); + ser.Sync("serial_start_old", ref serial_start_old); + + ser.Sync("REG_FFFF", ref REG_FFFF); + ser.Sync("REG_FF0F", ref REG_FF0F); + ser.Sync("enable_VBL", ref enable_VBL); + ser.Sync("enable_LCDC", ref enable_PRS); + ser.Sync("enable_TIMO", ref enable_TIMO); + ser.Sync("enable_SER", ref enable_SER); + ser.Sync("enable_STAT", ref enable_STAT); + + // memory domains + ser.Sync("RAM", ref RAM, false); + ser.Sync("ZP_RAM", ref ZP_RAM, false); + ser.Sync("CHR_RAM", ref CHR_RAM, false); + ser.Sync("BG_map_1", ref BG_map_1, false); + ser.Sync("BG_map_2", ref BG_map_2, false); + ser.Sync("OAM", ref OAM, false); + // probably a better way to do this + if (cart_RAM != null) + { + ser.Sync("cart_RAM", ref cart_RAM, false); + } + + + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs new file mode 100644 index 0000000000..356d4617eb --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -0,0 +1,223 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + [Core( + "GBHawk", + "", + isPorted: false, + isReleased: true)] + [ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))] + public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, + ISettable + { + // this register controls whether or not the GB BIOS is mapped into memory + public byte GB_bios_register; + + public byte input_register; + + public byte serial_control; + public byte serial_data_out; + public byte serial_data_in; + public bool serial_start_old; + + // The unused bits in this register are still read/writable + public byte REG_FFFF; + // The unused bits in this register (interrupt flags) are always set + public byte REG_FF0F = 0xE0; + public bool enable_VBL; + public bool enable_STAT; + public bool enable_TIMO; + public bool enable_SER; + public bool enable_PRS; + + + // memory domains + public byte[] RAM = new byte[0x2000]; + public byte[] ZP_RAM = new byte[0x80]; + public byte[] CHR_RAM = new byte[0x1800]; + public byte[] BG_map_1 = new byte[0x400]; + public byte[] BG_map_2 = new byte[0x400]; + public byte[] OAM = new byte[0xA0]; + + public readonly byte[] _rom; + public readonly byte[] _bios; + public readonly byte[] _sram = new byte[2048]; + public readonly byte[] header = new byte[0x50]; + + public byte[] cart_RAM; + + private int _frame = 0; + + public MapperBase mapper; + + private readonly ITraceable _tracer; + + public LR35902 cpu; + public PPU ppu; + public Timer timer; + public Audio audio; + + [CoreConstructor("GB")] + public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + cpu = new LR35902 + { + ReadMemory = ReadMemory, + WriteMemory = WriteMemory, + PeekMemory = ReadMemory, + DummyReadMemory = ReadMemory, + OnExecFetch = ExecFetch + }; + ppu = new PPU(); + timer = new Timer(); + audio = new Audio(); + + CoreComm = comm; + + _settings = (GBSettings)settings ?? new GBSettings(); + _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); + _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); + + byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", false, "BIOS Not Found, Cannot Load"); + _bios = Bios; + + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); + + string hash_md5 = null; + hash_md5 = "md5:" + rom.HashMD5(0, rom.Length); + Console.WriteLine(hash_md5); + + _rom = rom; + Setup_Mapper(); + + _frameHz = 60; + + timer.Core = this; + audio.Core = this; + ppu.Core = this; + + ser.Register(this); + ser.Register(audio); + ServiceProvider = ser; + + _tracer = new TraceBuffer { Header = cpu.TraceHeader }; + ser.Register(_tracer); + + SetupMemoryDomains(); + HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + private readonly GBHawkControllerDeck _controllerDeck; + + private void HardReset() + { + GB_bios_register = 0; // bios enable + in_vblank = true; // we start off in vblank since the LCD is off + in_vblank_old = true; + + Register_Reset(); + timer.Reset(); + ppu.Reset(); + + cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); + + _vidbuffer = new int[VirtualWidth * VirtualHeight]; + } + + private void ExecFetch(ushort addr) + { + MemoryCallbacks.CallExecutes(addr); + } + + private void Setup_Mapper() + { + // setup up mapper based on header entry + switch (header[0x47]) + { + case 0x0: mapper = new MapperDefault(); break; + case 0x1: mapper = new MapperMBC1(); break; + case 0x2: mapper = new MapperMBC1(); break; + case 0x3: mapper = new MapperMBC1(); break; + case 0x5: mapper = new MapperMBC2(); break; + case 0x6: mapper = new MapperMBC2(); break; + case 0x8: mapper = new MapperDefault(); break; + case 0x9: mapper = new MapperDefault(); break; + case 0xB: mapper = new MapperMMM01(); break; + case 0xC: mapper = new MapperMMM01(); break; + case 0xD: mapper = new MapperMMM01(); break; + case 0xF: mapper = new MapperMBC3(); break; + case 0x10: mapper = new MapperMBC3(); break; + case 0x11: mapper = new MapperMBC3(); break; + case 0x12: mapper = new MapperMBC3(); break; + case 0x13: mapper = new MapperMBC3(); break; + case 0x19: mapper = new MapperMBC5(); break; + case 0x1A: mapper = new MapperMBC5(); break; + case 0x1B: mapper = new MapperMBC5(); break; + case 0x1C: mapper = new MapperMBC5(); break; + case 0x1D: mapper = new MapperMBC5(); break; + case 0x1E: mapper = new MapperMBC5(); break; + case 0x20: mapper = new MapperMBC6(); break; + case 0x22: mapper = new MapperMBC7(); break; + case 0xFC: mapper = new MapperCamera(); break; + case 0xFD: mapper = new MapperTAMA5(); break; + case 0xFE: mapper = new MapperHuC3(); break; + case 0xFF: mapper = new MapperHuC1(); break; + + case 0x4: + case 0x7: + case 0xA: + case 0xE: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x1F: + case 0x21: + default: + // mapper not implemented + throw new Exception("Mapper not implemented"); + break; + + } + + Console.Write("Mapper: "); + Console.WriteLine(header[0x47]); + + cart_RAM = null; + + switch (header[0x49]) + { + case 1: + cart_RAM = new byte[0x800]; + break; + case 2: + cart_RAM = new byte[0x2000]; + break; + case 3: + cart_RAM = new byte[0x8000]; + break; + case 4: + cart_RAM = new byte[0x20000]; + break; + case 5: + cart_RAM = new byte[0x10000]; + break; + + } + + mapper.Core = this; + mapper.Initialize(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs new file mode 100644 index 0000000000..1939f1b4c1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs @@ -0,0 +1,76 @@ +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.GBHawk +{ + public class GBHawkControllerDeck + { + public GBHawkControllerDeck(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 + .Concat(new[] + { + "Power", + "Reset", + }) + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Port1"); + Port1.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(GBHawkControllerDeck).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/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs new file mode 100644 index 0000000000..e322e4e252 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + /// + /// Represents a GB add on + /// + public interface IPort + { + byte Read(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Standard controls")] + public class StandardControls : IPort + { + public StandardControls(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Game Boy", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + for (int i = 0; i < 8; i++) + { + if (c.IsPressed(Definition.BoolButtons[i])) + { + result -= (byte)(1 << i); + } + } + + return result; + } + + private static readonly string[] BaseDefinition = + { + "Right", "Left", "Up", "Down", "A", "B", "Select", "Start" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs new file mode 100644 index 0000000000..a55e91798e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -0,0 +1,283 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte Read_Registers(int addr) + { + byte ret = 0; + + switch (addr) + { + // Read Input + case 0xFF00: + _islag = false; + + 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) == 0x30) + { + // 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; + } + ret = input_register; + break; + + // Serial data port + case 0xFF01: + ret = serial_data_in; + break; + + // Serial port control + case 0xFF02: + ret = serial_control; + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + ret = timer.ReadReg(addr); + break; + + // Interrupt flags + case 0xFF0F: + ret = REG_FF0F; + 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; + + // Bios control register. Not sure if it is readable + case 0xFF50: + ret = 0xFF; + break; + + // interrupt control register + case 0xFFFF: + ret = REG_FFFF; + break; + + default: + ret = 0xFF; + break; + + } + return ret; + } + + public void Write_Registers(int addr, byte value) + { + switch (addr) + { + // select input + case 0xFF00: + input_register = (byte)(0xC0 | (value & 0x3F)); // top 2 bits always 1 + break; + + // Serial data port + case 0xFF01: + serial_data_out = value; + serial_data_in = serial_data_out; + break; + + // Serial port control + case 0xFF02: + serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1 + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + timer.WriteReg(addr, value); + break; + + // Interrupt flags + case 0xFF0F: + REG_FF0F = (byte)(0xE0 | value); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + 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; + + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs + case 0xFF50: + //Console.WriteLine(value); + if (GB_bios_register != 1) + { + GB_bios_register = value; + } + break; + + // interrupt control register + case 0xFFFF: + REG_FFFF = value; + enable_VBL = REG_FFFF.Bit(0); + enable_STAT = REG_FFFF.Bit(1); + enable_TIMO = REG_FFFF.Bit(2); + enable_SER = REG_FFFF.Bit(3); + enable_PRS = REG_FFFF.Bit(4); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + break; + } + } + + public void Register_Reset() + { + input_register = 0xCF; // not reading any input + serial_control = 0x7E; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs new file mode 100644 index 0000000000..2b1a3bdb0a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -0,0 +1,164 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + + +/* + $FFFF Interrupt Enable Flag + $FF80-$FFFE Zero Page - 127 bytes + $FF00-$FF7F Hardware I/O Registers + $FEA0-$FEFF Unusable Memory + $FE00-$FE9F OAM - Object Attribute Memory + $E000-$FDFF Echo RAM - Reserved, Do Not Use + $D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only) + $C000-$CFFF Internal RAM - Bank 0 (fixed) + $A000-$BFFF Cartridge RAM (If Available) + $9C00-$9FFF BG Map Data 2 + $9800-$9BFF BG Map Data 1 + $8000-$97FF Character RAM + $4000-$7FFF Cartridge ROM - Switchable Banks 1-xx + $0150-$3FFF Cartridge ROM - Bank 0 (fixed) + $0100-$014F Cartridge Header Area + $0000-$00FF Restart and Interrupt Vectors +*/ + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte ReadMemory(ushort addr) + { + MemoryCallbacks.CallReads(addr); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr < 0x8000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0x9800) + { + return CHR_RAM[addr - 0x8000]; + } + else if (addr < 0x9C00) + { + return BG_map_1[addr - 0x9800]; + } + else if (addr < 0xA000) + { + return BG_map_2[addr - 0x9C00]; + } + else if (addr < 0xC000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0xE000) + { + return RAM[addr - 0xC000]; + } + else if (addr < 0xFE00) + { + return RAM[addr - 0xE000]; + } + else if (addr < 0xFEA0 && ppu.OAM_access) + { + return OAM[addr - 0xFE00]; + } + else if (addr < 0xFF00) + { + // unmapped memory, returns 0xFF + return 0xFF; + } + else if (addr < 0xFF80) + { + return Read_Registers(addr); + } + else if (addr < 0xFFFF) + { + return ZP_RAM[addr - 0xFF80]; + } + else + { + return Read_Registers(addr); + } + + } + + public void WriteMemory(ushort addr, byte value) + { + MemoryCallbacks.CallWrites(addr); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + // Can't write to BIOS region + } + else + { + mapper.WriteMemory(addr, value); + } + } + else if (addr < 0x8000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0x9800) + { + CHR_RAM[addr - 0x8000] = value; + } + else if (addr < 0x9C00) + { + BG_map_1[addr - 0x9800] = value; + } + else if (addr < 0xA000) + { + BG_map_2[addr - 0x9C00] = value; + } + else if (addr < 0xC000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0xE000) + { + RAM[addr - 0xC000] = value; + } + else if (addr < 0xFE00) + { + RAM[addr - 0xE000] = value; + } + else if (addr < 0xFEA0 && ppu.OAM_access) + { + OAM[addr - 0xFE00] = value; + } + else if (addr < 0xFF00) + { + // unmapped, writing has no effect + } + else if (addr < 0xFF80) + { + Write_Registers(addr, value); + } + else if (addr < 0xFFFF) + { + ZP_RAM[addr - 0xFF80] = value; + } + else + { + Write_Registers(addr, value); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs new file mode 100644 index 0000000000..e1290075c4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -0,0 +1,922 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class PPU + { + public GBHawk Core { get; set; } + + //public byte BGP_l; + + // register variables + public byte LCDC; + public byte STAT; + public byte scroll_y; + public byte scroll_x; + public byte LY; + 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; + public bool hbl_set_once; + // OAM scan + public bool OAM_access; + public int OAM_scan_index; + public int SL_sprites_index; + public int[] SL_sprites = new int[40]; + public int write_sprite; + // render + public bool VRAM_access; + 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 int temp_fetch; + public int tile_inc; + public bool pre_render; + public byte[] tile_data = new byte[2]; + public byte[] tile_data_latch = new byte[2]; + 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 sprite_fetch_index; + public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr) + public int index_used; + public int sprite_ordered_index; + public int bottom_index; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: /*ret = DMA_addr; */ break; // DMA (not readable?) + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + LCDC = value; + break; + case 0xFF41: // STAT + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LY != LYC) { STAT &= 0xFB; } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public void tick() + { + // tick DMA + if (DMA_start) + { + if (DMA_clock >= 4) + { + OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + if ((DMA_inc % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + } + else + { + Core.OAM[DMA_inc] = DMA_byte; + } + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock==648) + { + DMA_start = false; + OAM_access = true; + } + } + + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 8)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (cycle == 4) + { + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + if (cycle >= 4 && cycle < 84) + { + // here OAM scanning is performed + OAM_scan(cycle - 4); + } + else if (cycle >= 84 && LY < 144) + { + // render the screen and handle hblank + render(cycle - 84); + } + } + + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted + if ((cycle == 4) && (LY != 0)) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + + if (cycle==456) + { + cycle = 0; + LY+=LY_inc; + + if (LY==0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + } + + Core.cpu.LY = LY; + + if (LY==144) + { + Core.in_vblank = true; + } + } + } + else + { + // screen disable sets STAT as though it were vblank, but there is no Stat IRQ asserted + STAT &= 0xFC; + STAT |= 0x01; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + + } + + // might be needed, not sure yet + public void latch_delay() + { + //BGP_l = BGP; + } + + public void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access = false; + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + // (sprite Y - 16) equals LY, we have a sprite + if ((Core.OAM[OAM_scan_index * 4] - 16) <= LY && + ((Core.OAM[OAM_scan_index * 4] - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = Core.OAM[OAM_scan_index * 4]; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + SL_sprites[SL_sprites_index * 4 + write_sprite] = Core.OAM[OAM_scan_index * 4 + write_sprite]; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access = false; + VRAM_access = false; + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = 0; + sl_use_index = 0; + index_used = 0; + bottom_index = 0; + sprite_ordered_index = 0; + fetch_sprite = false; + no_sprites = false; + + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + if (SL_sprites_index == 0) + { + no_sprites = true; + } + } + + if (!pre_render && !fetch_sprite) + { + // start by fetching all the sprites that need to be fetched + if (!no_sprites) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < SL_sprites[i * 4 + 1]) && + !index_used.Bit(i)) + { + fetch_sprite = true; + sprite_fetch_index = 0; + } + } + } + + if (!fetch_sprite) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + pixel = (BGP >> (pixel * 2)) & 3; + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int i = bottom_index; + int s_pixel = 0; + int sprite_attr = 0; + + while (i < sprite_ordered_index) + { + if (SL_sprites_ordered[i * 4] == pixel_counter) + { + bottom_index++; + if (bottom_index == SL_sprites_index) { no_sprites = true; } + } + else if (!have_sprite) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = sprite_data[0] + sprite_data[1]; + sprite_attr = SL_sprites_ordered[i * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_sprite = true; + } + } + i++; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + if (s_pixel != 0) { use_sprite = true; } + } + else if (pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)GBHawk.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_set_once = true; + } + } + render_counter++; + } + } + + if (!fetch_sprite) + { + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + + } + else + { + if (!pre_render) + { + tile_inc++; + } + read_case = 1; + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = -1; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + break; + + case 6: // read from tile graphics (for the window) + break; + + case 7: // read from tile graphics (for the window) + break; + + case 8: // done reading, we are now in phase 0 + + OAM_access = true; + VRAM_access = true; + + STAT &= 0xFC; + STAT |= 0x00; + pre_render = true; + if (hbl_set_once) + { + if (STAT.Bit(3)) { HBL_INT = true; } + hbl_set_once = false; + } + + break; + } + + internal_cycle++; + } + + if (fetch_sprite) + { + if (sprite_fetch_index < SL_sprites_index) + { + if (pixel_counter != 0) { + if ((pixel_counter == (SL_sprites[sprite_fetch_index * 4 + 1] - 8)) && + //(pixel_counter < SL_sprites[sprite_fetch_index * 4 + 1]) && + !index_used.Bit(sprite_fetch_index)) + { + sl_use_index = sprite_fetch_index; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[sprite_fetch_index * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[sprite_fetch_index * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + sprite_fetch_index++; + if (sprite_fetch_index == SL_sprites_index) { fetch_sprite = false; } + } + else + { + // whan pixel counter is 0, we want to scan all the points before 0 as well + // certainly non-physical but good enough for now + for (int j = -7; j < 1; j++) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((j == (SL_sprites[i * 4 + 1] - 8)) && + !index_used.Bit(i)) + { + sl_use_index = i; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[i * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[i * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + } + } + fetch_sprite = false; + } + } + } + } + + public void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0; + BGP = 0; + obj_pal_0 = 0; + obj_pal_1 = 0; + window_y = 0; + window_x = 0; + LY_inc = 1; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + } + + public void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + public void SyncState(Serializer ser) + { + ser.Sync("LCDC", ref LCDC); + ser.Sync("STAT", ref STAT); + ser.Sync("scroll_y", ref scroll_y); + ser.Sync("scroll_x", ref scroll_x); + ser.Sync("LY", ref LY); + ser.Sync("LYinc", ref LY_inc); + ser.Sync("LYC", ref LYC); + ser.Sync("DMA_addr", ref DMA_addr); + ser.Sync("BGP", ref BGP); + ser.Sync("obj_pal_0", ref obj_pal_0); + ser.Sync("obj_pal_1", ref obj_pal_1); + ser.Sync("window_y", ref window_y); + ser.Sync("window_x", ref window_x); + ser.Sync("DMA_start", ref DMA_start); + ser.Sync("DMA_clock", ref DMA_clock); + ser.Sync("DMA_inc", ref DMA_inc); + ser.Sync("DMA_byte", ref DMA_byte); + + ser.Sync("LYC_INT", ref LYC_INT); + ser.Sync("HBL_INT", ref HBL_INT); + ser.Sync("VBL_INT", ref VBL_INT); + ser.Sync("OAM_INT", ref OAM_INT); + ser.Sync("stat_line", ref stat_line); + ser.Sync("stat_line_old", ref stat_line_old); + ser.Sync("hbl_set_once", ref hbl_set_once); + ser.Sync("LCD_was_off", ref LCD_was_off); + ser.Sync("OAM_access", ref OAM_access); + ser.Sync("OAM_scan_index", ref OAM_scan_index); + ser.Sync("SL_sprites_index", ref SL_sprites_index); + ser.Sync("SL_sprites", ref SL_sprites, false); + ser.Sync("write_sprite", ref write_sprite); + + ser.Sync("VRAM_access", ref VRAM_access); + ser.Sync("read_case", ref read_case); + ser.Sync("internal_cycle", ref internal_cycle); + ser.Sync("y_tile", ref y_tile); + ser.Sync("y_scroll_offset", ref y_scroll_offset); + ser.Sync("x_tile", ref x_tile); + ser.Sync("x_scroll_offset", ref x_scroll_offset); + ser.Sync("tile_byte", ref tile_byte); + ser.Sync("sprite_fetch_cycles", ref sprite_fetch_cycles); + ser.Sync("fetch_sprite", ref fetch_sprite); + ser.Sync("temp_fetch", ref temp_fetch); + ser.Sync("tile_inc", ref tile_inc); + ser.Sync("pre_render", ref pre_render); + ser.Sync("tile_data", ref tile_data, false); + ser.Sync("tile_data_latch", ref tile_data_latch, false); + ser.Sync("latch_counter", ref latch_counter); + ser.Sync("latch_new_data", ref latch_new_data); + ser.Sync("render_counter", ref render_counter); + ser.Sync("render_offset", ref render_offset); + ser.Sync("pixel_counter", ref pixel_counter); + ser.Sync("pixel", ref pixel); + ser.Sync("sprite_data", ref sprite_data, false); + ser.Sync("sl_use_index", ref sl_use_index); + ser.Sync("sprite_sel", ref sprite_sel, false); + ser.Sync("no_sprites", ref no_sprites); + ser.Sync("sprite_fetch_index", ref sprite_fetch_index); + ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); + ser.Sync("index_used", ref index_used); + ser.Sync("sprite_ordered_index", ref sprite_ordered_index); + ser.Sync("bottom_index", ref bottom_index); + + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs new file mode 100644 index 0000000000..817c28201e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -0,0 +1,175 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Timer Emulation + public class Timer + { + public GBHawk Core { get; set; } + + public ushort divider_reg; + public byte timer_reload; + public byte timer; + public byte timer_old; + public byte timer_control; + public byte pending_reload; + public byte write_ignore; + public bool old_state; + public bool state; + public bool reload_block; + public bool TMA_coincidence; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF04: ret = (byte)(divider_reg >> 8); break; // DIV register + case 0xFF05: ret = timer; break; // TIMA (Timer Counter) + case 0xFF06: ret = timer_reload; break; // TMA (Timer Modulo) + case 0xFF07: ret = timer_control; break; // TAC (Timer Control) + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + // DIV register + case 0xFF04: + divider_reg = 0; + break; + + // TIMA (Timer Counter) + case 0xFF05: + if (write_ignore == 0) + { + timer_old = timer; + timer = value; + reload_block = true; + } + break; + + // TMA (Timer Modulo) + case 0xFF06: + timer_reload = value; + if (TMA_coincidence) + { + timer = timer_reload; + timer_old = timer; + } + break; + + // TAC (Timer Control) + case 0xFF07: + timer_control = (byte)((timer_control & 0xf8) | (value & 0x7)); // only bottom 3 bits function + break; + } + } + + public void tick_1() + { + if (write_ignore > 0) + { + write_ignore--; + if (write_ignore==0) + { + TMA_coincidence = false; + } + } + + if (pending_reload > 0) + { + pending_reload--; + if (pending_reload == 0 && !reload_block) + { + timer = timer_reload; + timer_old = timer; + write_ignore = 4; + TMA_coincidence = true; + + // set interrupts + if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x04; + } + } + } + + public void tick_2() + { + divider_reg++; + + // pick a bit to test based on the current value of timer control + switch (timer_control & 3) + { + case 0: + state = divider_reg.Bit(9); + break; + case 1: + state = divider_reg.Bit(3); + break; + case 2: + state = divider_reg.Bit(5); + break; + case 3: + state = divider_reg.Bit(7); + break; + } + + // And it with the state of the timer on/off bit + state &= timer_control.Bit(2); + + // this procedure allows several glitchy timer ticks, since it only measures falling edge of the state + // so things like turning the timer off and resetting the divider will tick the timer + if (old_state && !state) + { + timer_old = timer; + timer++; + + // if overflow, set the interrupt flag and reload the timer (4 clocks later) + if (timer < timer_old) + { + pending_reload = 4; + reload_block = false; + } + } + + old_state = state; + } + + public void Reset() + { + divider_reg = 0; + timer_reload = 0; + timer = 0; + timer_old = 0; + timer_control = 0xF8; + pending_reload = 0; + write_ignore = 0; + old_state = false; + state = false; + reload_block = false; + TMA_coincidence = false; + } + + public void SyncState(Serializer ser) + { + ser.Sync("divider_reg", ref divider_reg); + ser.Sync("timer_reload", ref timer_reload); + ser.Sync("timer", ref timer); + ser.Sync("timer_old", ref timer_old); + ser.Sync("timer_control", ref timer_control); + ser.Sync("pending_reload", ref pending_reload); + ser.Sync("write_ignore", ref write_ignore); + ser.Sync("old_state", ref old_state); + ser.Sync("state", ref state); + ser.Sync("reload_block", ref reload_block); + ser.Sync("TMA_coincidence", ref TMA_coincidence); + } + } +} \ No newline at end of file From f7a016adaa8759bf75d8020e447f1b279c0157fe Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:20:09 -0400 Subject: [PATCH 05/28] Create ReadMe.txt --- .../Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt new file mode 100644 index 0000000000..0c94c8272c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt @@ -0,0 +1,3 @@ +TODO: +Official Mappers +Unofficial Mappers From 8dab8dc3687069aa42626078ef9545bb516b2da8 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 29 Aug 2017 09:20:56 -0400 Subject: [PATCH 06/28] Add files via upload --- .../Nintendo/GBHawk/Mappers/MapperBase.cs | 40 ++++++ .../Nintendo/GBHawk/Mappers/Mapper_Camera.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_Default.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_HuC1.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_HuC3.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC1.cs | 130 ++++++++++++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC2.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC3.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC5.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC6.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC7.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MMM01.cs | 59 ++++++++ .../Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs | 59 ++++++++ 13 files changed, 819 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs new file mode 100644 index 0000000000..0cd2cc11ea --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs @@ -0,0 +1,40 @@ +using BizHawk.Common; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class MapperBase + { + public GBHawk 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() + { + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs new file mode 100644 index 0000000000..b6ad35bb1b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperCamera : 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 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/Nintendo/GBHawk/Mappers/Mapper_Default.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs new file mode 100644 index 0000000000..2df4c92454 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // 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 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/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs new file mode 100644 index 0000000000..cef7f2bbdc --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC1 : 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 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/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs new file mode 100644 index 0000000000..0daf88475a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC3 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs new file mode 100644 index 0000000000..1224b9b954 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs @@ -0,0 +1,130 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC1 : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed + return Core._rom[addr]; + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0; + } + + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x1F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank &= 0xE0; + ROM_bank |= value; + } + else if (addr < 0x6000) + { + if (sel_mode) + { + RAM_bank = value & 0x3; + } + else + { + ROM_bank &= 0x1F; + ROM_bank |= ((value & 3) << 5); + } + } + else + { + sel_mode = (value & 1) > 0; + + if (sel_mode) + { + ROM_bank &= 0x1F; + } + else + { + RAM_bank = 0; + } + } + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("sel_mode", ref sel_mode); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs new file mode 100644 index 0000000000..b131098538 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC2 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs new file mode 100644 index 0000000000..18a7f013c7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC3 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs new file mode 100644 index 0000000000..529f48ca4a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC5 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs new file mode 100644 index 0000000000..5cbdd331ff --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC6 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs new file mode 100644 index 0000000000..24d957c653 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC7 : 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 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/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs new file mode 100644 index 0000000000..0a45601998 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMMM01 : 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 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/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs new file mode 100644 index 0000000000..dc6c6d8d9f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperTAMA5 : 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 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); + } + } +} From 666e284e03296887a49b1e442ab558313a8e045b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Sep 2017 17:36:26 -0400 Subject: [PATCH 07/28] PPU add windowing --- .../Consoles/Nintendo/GBHawk/PPU.cs | 181 +++++++++++++++++- 1 file changed, 176 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index e1290075c4..b088b237a0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -78,6 +78,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int sprite_ordered_index; public int bottom_index; + // windowing state + public int window_counter; + public bool window_pre_render; + public bool window_started; + public int window_tile_inc; + public int window_y_tile; + public int window_x_tile; + public int window_y_tile_inc; + public byte ReadReg(int addr) { byte ret = 0; @@ -318,6 +327,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY_inc = 1; Core.in_vblank = false; VBL_INT = false; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; } Core.cpu.LY = LY; @@ -325,6 +341,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LY==144) { Core.in_vblank = true; + } } } @@ -460,6 +477,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk fetch_sprite = false; no_sprites = false; + window_pre_render = false; + if (window_started && LCDC.Bit(5)) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + } + } + window_started = false; + // calculate the row number of the tiles to be fetched y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; @@ -468,8 +497,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk no_sprites = true; } } + + // before anything else, we have to check if windowing is in effect + if (LCDC.Bit(5) && !window_started && LY >= window_y && pixel_counter >= window_x - 7) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(window_y); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.WriteLine(scroll_y); + */ + if (pixel_counter == 0 && window_x <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + window_pre_render = true; + } + else + { + // otherwise, just restart the whole process as if starting BG again + window_pre_render = true; + read_case = 4; + window_counter = 0; + } - if (!pre_render && !fetch_sprite) + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x - 7)) / 8); + window_tile_inc = 0; + window_started = true; + } + + if (!pre_render && !fetch_sprite && !window_pre_render) { // start by fetching all the sprites that need to be fetched if (!no_sprites) @@ -600,11 +660,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { + read_case = 1; if (!pre_render) { tile_inc++; - } - read_case = 1; + if (window_pre_render) + { + read_case = 4; + } + } } break; @@ -687,18 +751,110 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - read_case = 0; + if (window_started) + { + read_case = 4; + } + else + { + read_case = 0; + } + latch_new_data = true; } + if (window_started) { window_counter++; } + break; case 4: // read from window data + if ((window_counter % 2) == 0) + { + + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + if (!window_pre_render) + { + window_tile_inc++; + } + read_case = 6; + } + window_counter++; break; case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 7; + } + window_counter++; break; case 7: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + window_pre_render = false; + render_offset = 0; + render_counter = -1; + latch_counter = 0; + read_case = 4; + } + else + { + read_case = 3; + } + + } + window_counter++; break; case 8: // done reading, we are now in phase 0 @@ -794,7 +950,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk stat_line = false; stat_line_old = false; - } + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + } public void process_sprite() { @@ -917,6 +1081,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("sprite_ordered_index", ref sprite_ordered_index); ser.Sync("bottom_index", ref bottom_index); + ser.Sync("window_counter", ref window_counter); + ser.Sync("window_pre_render", ref window_pre_render); + ser.Sync("window_started", ref window_started); + ser.Sync("window_tile_inc", ref window_tile_inc); + ser.Sync("window_y_tile", ref window_y_tile); + ser.Sync("window_x_tile", ref window_x_tile); + ser.Sync("window_y_tile_inc", ref window_y_tile_inc); } } } From f0306c10df4c0fd9b03cc65e58e798e38a9f5891 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Sep 2017 09:32:59 -0400 Subject: [PATCH 08/28] Update PPU.cs --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index b088b237a0..fdb381ae9e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -485,6 +485,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { window_y_tile_inc = 0; window_y_tile++; + window_y_tile %= 32; } } window_started = false; From 465d1ea3a18ca7c606817d72b8d0a82421ab0775 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Sep 2017 19:38:18 -0400 Subject: [PATCH 09/28] Add files via upload --- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 24 +- .../Consoles/Nintendo/GBHawk/PPU.cs | 241 +++++++++++++----- 2 files changed, 185 insertions(+), 80 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 2b1a3bdb0a..da99274751 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -49,15 +49,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0x9800) { - return CHR_RAM[addr - 0x8000]; + if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } + else { return 0xFF; } } else if (addr < 0x9C00) { - return BG_map_1[addr - 0x9800]; + if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } + else { return 0xFF; } } else if (addr < 0xA000) { - return BG_map_2[addr - 0x9C00]; + if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + else { return 0xFF; } } else if (addr < 0xC000) { @@ -71,9 +74,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { return RAM[addr - 0xE000]; } - else if (addr < 0xFEA0 && ppu.OAM_access) + else if (addr < 0xFEA0) { - return OAM[addr - 0xFE00]; + if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } + else { return 0xFF; } } else if (addr < 0xFF00) { @@ -117,15 +121,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0x9800) { - CHR_RAM[addr - 0x8000] = value; + if (ppu.VRAM_access_write) { CHR_RAM[addr - 0x8000] = value; } } else if (addr < 0x9C00) { - BG_map_1[addr - 0x9800] = value; + if (ppu.VRAM_access_write) { BG_map_1[addr - 0x9800] = value; } } else if (addr < 0xA000) { - BG_map_2[addr - 0x9C00] = value; + if (ppu.VRAM_access_write) { BG_map_2[addr - 0x9C00] = value; } } else if (addr < 0xC000) { @@ -139,9 +143,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { RAM[addr - 0xE000] = value; } - else if (addr < 0xFEA0 && ppu.OAM_access) + else if (addr < 0xFEA0) { - OAM[addr - 0xFE00] = value; + if (ppu.OAM_access_write) { OAM[addr - 0xFE00] = value; } } else if (addr < 0xFF00) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index fdb381ae9e..d413c74adc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -39,15 +39,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool LCD_was_off; public bool stat_line; public bool stat_line_old; - public bool hbl_set_once; + public int hbl_countdown; // OAM scan - public bool 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; + public bool VRAM_access_read; + public bool VRAM_access_write; public int read_case; public int internal_cycle; public int y_tile; @@ -166,7 +169,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (DMA_clock >= 4) { - OAM_access = false; + OAM_access_read = false; if ((DMA_clock % 4) == 1) { // the cpu can't access memory during this time, but we still need the ppu to be able to. @@ -194,13 +197,51 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (DMA_clock==648) { DMA_start = false; - OAM_access = true; + OAM_access_read = true; } } - + // the ppu only does anything if it is turned on via bit 7 of LCDC if (LCDC.Bit(7)) { + // start the next scanline + if (cycle == 456) + { + cycle = 0; + LY += LY_inc; + + no_scan = false; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + } + + Core.cpu.LY = LY; + + if (LY == 144) + { + Core.in_vblank = true; + + } + } + // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -211,6 +252,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // we exit vblank into mode 0 for 4 cycles // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; } // the VBL stat is continuously asserted @@ -256,26 +303,88 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (!Core.in_vblank) { - if (cycle == 4) + if (no_scan) { - // here mode 2 will be set to true and interrupts fired if enabled - STAT &= 0xFC; - STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } - - HBL_INT = false; - } + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? - if (cycle >= 4 && cycle < 84) - { - // here OAM scanning is performed - OAM_scan(cycle - 4); + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } } - else if (cycle >= 84 && LY < 144) + else { - // render the screen and handle hblank - render(cycle - 84); + if (cycle < 80) + { + if (cycle == 4) + { + // apparently, writes can make it to OAm one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if (cycle >= 80 && LY < 144) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } } + } @@ -316,34 +425,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle++; - - if (cycle==456) - { - cycle = 0; - LY+=LY_inc; - - if (LY==0 && LY_inc == 0) - { - LY_inc = 1; - Core.in_vblank = false; - VBL_INT = false; - - // special note here, the y coordiate of the window is kept if the window is deactivated - // meaning it will pick up where it left off if re-enabled later - // so we don't reset it in the scanline loop - window_y_tile = 0; - window_y_tile_inc = 0; - window_started = false; - } - - Core.cpu.LY = LY; - - if (LY==144) - { - Core.in_vblank = true; - - } - } } else { @@ -391,7 +472,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { - OAM_access = false; + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; @@ -458,12 +540,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; - OAM_access = false; - VRAM_access = false; OAM_scan_index = 0; read_case = 0; internal_cycle = 0; @@ -526,6 +606,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x - 7)) / 8); + window_tile_inc = 0; window_started = true; } @@ -576,7 +657,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); t_index = 7 - t_index; - + sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); @@ -633,7 +714,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (pixel_counter == 160) { read_case = 8; - hbl_set_once = true; + hbl_countdown = 4; } } render_counter++; @@ -772,7 +853,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } else { @@ -859,19 +942,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 8: // done reading, we are now in phase 0 - - OAM_access = true; - VRAM_access = true; - STAT &= 0xFC; - STAT |= 0x00; pre_render = true; - if (hbl_set_once) + + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) { - if (STAT.Bit(3)) { HBL_INT = true; } - hbl_set_once = false; + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } } - + break; } @@ -942,6 +1034,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y = 0; window_x = 0; LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; cycle = 0; LYC_INT = false; @@ -1043,15 +1140,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("OAM_INT", ref OAM_INT); ser.Sync("stat_line", ref stat_line); ser.Sync("stat_line_old", ref stat_line_old); - ser.Sync("hbl_set_once", ref hbl_set_once); - ser.Sync("LCD_was_off", ref LCD_was_off); - ser.Sync("OAM_access", ref OAM_access); + ser.Sync("hbl_countdown", ref hbl_countdown); + ser.Sync("LCD_was_off", ref LCD_was_off); ser.Sync("OAM_scan_index", ref OAM_scan_index); ser.Sync("SL_sprites_index", ref SL_sprites_index); ser.Sync("SL_sprites", ref SL_sprites, false); ser.Sync("write_sprite", ref write_sprite); + ser.Sync("no_scan", ref no_scan); + + ser.Sync("OAM_access_read", ref OAM_access_read); + ser.Sync("OAM_access_write", ref OAM_access_write); + ser.Sync("VRAM_access_read", ref VRAM_access_read); + ser.Sync("VRAM_access_write", ref VRAM_access_write); - ser.Sync("VRAM_access", ref VRAM_access); ser.Sync("read_case", ref read_case); ser.Sync("internal_cycle", ref internal_cycle); ser.Sync("y_tile", ref y_tile); From ab3a246412f58ffb0eaf9102647ba8a5b939f143 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 8 Sep 2017 09:37:39 -0400 Subject: [PATCH 10/28] Add files via upload --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index d413c74adc..4fe8a674eb 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -209,13 +209,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { cycle = 0; LY += LY_inc; - + //Console.WriteLine(Core.cpu.TotalExecutedCycles); no_scan = false; // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case if (LY_inc == 1) { - LYC_INT = false; + //LYC_INT = false; STAT &= 0xFB; } @@ -1134,6 +1134,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("DMA_inc", ref DMA_inc); ser.Sync("DMA_byte", ref DMA_byte); + ser.Sync("cycle", ref cycle); ser.Sync("LYC_INT", ref LYC_INT); ser.Sync("HBL_INT", ref HBL_INT); ser.Sync("VBL_INT", ref VBL_INT); From f42b8e2d7fcfae82bbbe2e6f4ddd8e8776d13c6e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 9 Sep 2017 15:44:41 -0400 Subject: [PATCH 11/28] Add files via upload --- .../Consoles/Nintendo/GBHawk/PPU.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 4fe8a674eb..9f1742c09e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -602,8 +602,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // otherwise, just restart the whole process as if starting BG again window_pre_render = true; read_case = 4; - window_counter = 0; } + window_counter = 0; window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x - 7)) / 8); @@ -833,29 +833,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (window_started) - { - read_case = 4; - } - else - { - read_case = 0; - } - + read_case = 0; latch_new_data = true; } - if (window_started) { window_counter++; } - break; case 4: // read from window data if ((window_counter % 2) == 0) { - temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; - tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - } else { @@ -863,19 +850,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { window_tile_inc++; } - read_case = 6; + read_case = 5; } window_counter++; break; - case 6: // read from tile graphics (for the window) + case 5: // read from tile graphics (for the window) if ((window_counter % 2) == 0) { y_scroll_offset = (window_y_tile_inc) % 8; if (LCDC.Bit(4)) { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } else { @@ -884,18 +873,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte -= 256; } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } - } else { - read_case = 7; + read_case = 6; } window_counter++; break; - case 7: // read from tile graphics (for the window) + case 6: // read from tile graphics (for the window) if ((window_counter % 2) == 0) { y_scroll_offset = (window_y_tile_inc) % 8; @@ -934,18 +923,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - read_case = 3; + read_case = 7; } - } window_counter++; break; + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + case 8: // done reading, we are now in phase 0 pre_render = true; - // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here if (hbl_countdown > 0) { @@ -966,7 +966,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; } - internal_cycle++; } From 7749e1407e4f25f38521f4c91eeae8eac805dd60 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 9 Nov 2017 09:51:39 -0500 Subject: [PATCH 12/28] GBHawk Updates and Sync --- Audio.cs | 213 ++++ BizHawk.Client.Common/Global.cs | 3 +- BizHawk.Client.Common/RomLoader.cs | 4 +- .../BizHawk.Emulation.Cores.csproj | 36 + .../Nintendo/GBHawk/GBHawk.IDebuggable.cs | 2 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 2 +- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 4 +- GBHawk.IDebuggable.cs | 77 ++ GBHawk.IEmulator.cs | 156 +++ GBHawk.IInputPollable.cs | 24 + GBHawk.IMemoryDomains.cs | 56 ++ GBHawk.ISaveRam.cs | 26 + GBHawk.ISettable.cs | 77 ++ GBHawk.IStatable.cs | 102 ++ GBHawk.cs | 223 +++++ GBHawkControllerDeck.cs | 76 ++ GBHawkControllers.cs | 68 ++ HW_Registers.cs | 283 ++++++ Mappers/MapperBase.cs | 40 + Mappers/Mapper_Camera.cs | 59 ++ Mappers/Mapper_Default.cs | 59 ++ Mappers/Mapper_HuC1.cs | 59 ++ Mappers/Mapper_HuC3.cs | 59 ++ Mappers/Mapper_MBC1.cs | 130 +++ Mappers/Mapper_MBC2.cs | 59 ++ Mappers/Mapper_MBC3.cs | 59 ++ Mappers/Mapper_MBC5.cs | 59 ++ Mappers/Mapper_MBC6.cs | 59 ++ Mappers/Mapper_MBC7.cs | 59 ++ Mappers/Mapper_MMM01.cs | 59 ++ Mappers/Mapper_TAMA5.cs | 59 ++ Mappers/ReadMe.txt | 3 + MemoryMap.cs | 164 ++++ PPU.cs | 922 ++++++++++++++++++ ReadMe.txt | 1 + Timer.cs | 175 ++++ 36 files changed, 3510 insertions(+), 6 deletions(-) create mode 100644 Audio.cs create mode 100644 GBHawk.IDebuggable.cs create mode 100644 GBHawk.IEmulator.cs create mode 100644 GBHawk.IInputPollable.cs create mode 100644 GBHawk.IMemoryDomains.cs create mode 100644 GBHawk.ISaveRam.cs create mode 100644 GBHawk.ISettable.cs create mode 100644 GBHawk.IStatable.cs create mode 100644 GBHawk.cs create mode 100644 GBHawkControllerDeck.cs create mode 100644 GBHawkControllers.cs create mode 100644 HW_Registers.cs create mode 100644 Mappers/MapperBase.cs create mode 100644 Mappers/Mapper_Camera.cs create mode 100644 Mappers/Mapper_Default.cs create mode 100644 Mappers/Mapper_HuC1.cs create mode 100644 Mappers/Mapper_HuC3.cs create mode 100644 Mappers/Mapper_MBC1.cs create mode 100644 Mappers/Mapper_MBC2.cs create mode 100644 Mappers/Mapper_MBC3.cs create mode 100644 Mappers/Mapper_MBC5.cs create mode 100644 Mappers/Mapper_MBC6.cs create mode 100644 Mappers/Mapper_MBC7.cs create mode 100644 Mappers/Mapper_MMM01.cs create mode 100644 Mappers/Mapper_TAMA5.cs create mode 100644 Mappers/ReadMe.txt create mode 100644 MemoryMap.cs create mode 100644 PPU.cs create mode 100644 ReadMe.txt create mode 100644 Timer.cs diff --git a/Audio.cs b/Audio.cs new file mode 100644 index 0000000000..7fcaaca85f --- /dev/null +++ b/Audio.cs @@ -0,0 +1,213 @@ +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Audio Emulation + public class Audio : ISoundProvider + { + public GBHawk Core { get; set; } + + 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, + 0x3F, 0x00, 0xFF, 0xBF, + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, + 0xFF, 0x00, 0x00, 0xBF, + 0x00, 0x00, 0x70}; + + public byte[] Audio_Regs = new byte[21]; + + public byte[] Wave_RAM = new byte [16]; + + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) + case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) + case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) + case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) + case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) + case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) + case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) + case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) + case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) + case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) + case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) + case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) + case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (sweep) + case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (sweep) + case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (sweep) + case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (sweep) + case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (sweep) + case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (sweep) + case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (sweep) + + // wave ram table + 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 = Wave_RAM[addr & 0x0F]; + break; + + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF10: Audio_Regs[NR10] = value; break; // NR10 (sweep) + case 0xFF11: Audio_Regs[NR11] = value; break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: Audio_Regs[NR12] = value; break; // NR12 (envelope) + case 0xFF13: Audio_Regs[NR13] = value; break; // NR13 (freq low) + case 0xFF14: Audio_Regs[NR14] = value; break; // NR14 (freq hi) + case 0xFF16: Audio_Regs[NR21] = value; break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: Audio_Regs[NR22] = value; break; // NR22 (envelope) + case 0xFF18: Audio_Regs[NR23] = value; break; // NR23 (freq low) + case 0xFF19: Audio_Regs[NR24] = value; break; // NR24 (freq hi) + case 0xFF1A: Audio_Regs[NR30] = value; break; // NR30 (on/off) + case 0xFF1B: Audio_Regs[NR31] = value; break; // NR31 (length) + case 0xFF1C: Audio_Regs[NR32] = value; break; // NR32 (level output) + case 0xFF1D: Audio_Regs[NR33] = value; break; // NR33 (freq low) + case 0xFF1E: Audio_Regs[NR34] = value; break; // NR34 (freq hi) + case 0xFF20: Audio_Regs[NR41] = value; break; // NR41 (sweep) + case 0xFF21: Audio_Regs[NR42] = value; break; // NR42 (sweep) + case 0xFF22: Audio_Regs[NR43] = value; break; // NR43 (sweep) + case 0xFF23: Audio_Regs[NR44] = value; break; // NR44 (sweep) + case 0xFF24: Audio_Regs[NR50] = value; break; // NR50 (sweep) + case 0xFF25: Audio_Regs[NR51] = value; break; // NR51 (sweep) + case 0xFF26: Audio_Regs[NR52] = value; break; // NR52 (sweep) + + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + + } + } + + public void tick() + { + + } + + public void reset() + { + Wave_RAM = new byte[16]; + + Audio_Regs = new byte[21]; + } + + public void SyncState(Serializer ser) + { + ser.Sync("Audio_Regs", ref Audio_Regs, false); + ser.Sync("Wave_Ram", ref Wave_RAM, false); + + } + + #region audio + + public bool CanProvideAsync => false; + + public int _spf; + public int AudioClocks; + + 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) + { + short[] ret = new short[_spf * 2]; + nsamp = _spf; + GetSamples(ret); + samples = ret; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + AudioClocks = 0; + } + + // Exposing this as GetSamplesAsync would allow this to provide async sound + // However, it does nothing special for async sound so I don't see a point + private void GetSamples(short[] samples) + { + + } + + #endregion + } +} \ No newline at end of file diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index 42e9a75a89..b592f1696b 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -112,11 +112,12 @@ namespace BizHawk.Client.Common case "SNES": return SystemInfo.SNES; case "GB": + /* if ((Emulator as IGameboyCommon).IsCGBMode()) { return SystemInfo.GBC; } - + */ return SystemInfo.GB; case "A26": return SystemInfo.Atari2600; diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index d20e74a845..d89deccc64 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -13,6 +13,7 @@ using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Consoles.Sega.gpgx; using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; using BizHawk.Emulation.Cores.Nintendo.SNES; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.Saturn; @@ -938,7 +939,8 @@ namespace BizHawk.Client.Common case "GBC": if (!Global.Config.GB_AsSGB) { - core = CoreInventory.Instance["GB", "Gambatte"]; + core = CoreInventory.Instance["GB", "GBHawk"]; + //core = CoreInventory.Instance["GB", "Gambatte"]; } else { diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 0acda8f08e..b5a847cb21 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -581,6 +581,34 @@ VBANext.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + N64.cs @@ -1161,6 +1189,14 @@ + + + + + + + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs index d738c63fe4..778b17acfc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(); + public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" }); public bool CanStep(StepType type) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 356d4617eb..9015a4fe1d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -136,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private void ExecFetch(ushort addr) { - MemoryCallbacks.CallExecutes(addr); + MemoryCallbacks.CallExecutes(addr, "System Bus"); } private void Setup_Mapper() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index da99274751..fe6bb017ce 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public byte ReadMemory(ushort addr) { - MemoryCallbacks.CallReads(addr); + MemoryCallbacks.CallReads(addr, "System Bus"); if (addr < 0x100) { @@ -101,7 +101,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void WriteMemory(ushort addr, byte value) { - MemoryCallbacks.CallWrites(addr); + MemoryCallbacks.CallWrites(addr, "System Bus"); if (addr < 0x100) { diff --git a/GBHawk.IDebuggable.cs b/GBHawk.IDebuggable.cs new file mode 100644 index 0000000000..543cecf962 --- /dev/null +++ b/GBHawk.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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(); + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public int TotalExecutedCycles + { + get { return cpu.TotalExecutedCycles; } + } + } +} diff --git a/GBHawk.IEmulator.cs b/GBHawk.IEmulator.cs new file mode 100644 index 0000000000..189feb6af7 --- /dev/null +++ b/GBHawk.IEmulator.cs @@ -0,0 +1,156 @@ +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IVideoProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public byte controller_state; + public byte controller_state_old; + public bool in_vblank_old; + public bool in_vblank; + public bool vblank_rise; + + public void 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")) + { + // it seems that theMachine.Reset() doesn't clear ram, etc + // this should leave hsram intact but clear most other things + HardReset(); + } + + _islag = true; + + GetControllerState(controller); + + do_frame(); + + if (_islag) + { + _lagcount++; + } + } + + public void do_frame() + { + // gameboy frames can be variable lengths + // we want to end a frame when VBlank turns from false to true + int ticker = 0; + while (!vblank_rise && (ticker < 100000)) + { + audio.tick(); + timer.tick_1(); + ppu.tick(); + + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + + timer.tick_2(); + + + if (in_vblank && !in_vblank_old) + { + vblank_rise = true; + } + ticker++; + in_vblank_old = in_vblank; + } + + vblank_rise = false; + } + + public void RunCPUCycle() + { + + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + controller_state = _controllerDeck.ReadPort1(controller); + + // set interrupt flag if a pin went from high to low + if (controller_state < controller_state_old) + { + if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } + REG_FF0F |= 0x10; + } + + controller_state_old = controller_state; + } + + public void serial_transfer() + { + if (serial_control.Bit(7) && !serial_start_old) + { + serial_start_old = true; + + // transfer out on byte of data + // needs to be modelled + } + } + + public int Frame => _frame; + + public string SystemId => "GB"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + + } + + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer; + + public int[] GetVideoBuffer() + { + return _vidbuffer; + } + + 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 = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; + + #endregion + } +} diff --git a/GBHawk.IInputPollable.cs b/GBHawk.IInputPollable.cs new file mode 100644 index 0000000000..30af4780c6 --- /dev/null +++ b/GBHawk.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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/GBHawk.IMemoryDomains.cs b/GBHawk.IMemoryDomains.cs new file mode 100644 index 0000000000..aa9ecf8b2e --- /dev/null +++ b/GBHawk.IMemoryDomains.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + 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( + "Zero Page RAM", + ZP_RAM.Length, + MemoryDomain.Endian.Little, + addr => ZP_RAM[addr], + (addr, value) => ZP_RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "System Bus", + 0X10000, + MemoryDomain.Endian.Little, + addr => PeekSystemBus(addr), + (addr, value) => PokeSystemBus(addr, value), + 1) + }; + + MemoryDomains = new MemoryDomainList(domains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + + private byte PeekSystemBus(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + return ReadMemory(addr2); + } + + private void PokeSystemBus(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + WriteMemory(addr2, value); + } + } +} diff --git a/GBHawk.ISaveRam.cs b/GBHawk.ISaveRam.cs new file mode 100644 index 0000000000..5936f9543c --- /dev/null +++ b/GBHawk.ISaveRam.cs @@ -0,0 +1,26 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : ISaveRam + { + public byte[] CloneSaveRam() + { + return (byte[])_sram.Clone(); + } + + public void StoreSaveRam(byte[] data) + { + Buffer.BlockCopy(data, 0, _sram, 0, data.Length); + } + + public bool SaveRamModified + { + get + { + return false; + } + } + } +} diff --git a/GBHawk.ISettable.cs b/GBHawk.ISettable.cs new file mode 100644 index 0000000000..75a267bf62 --- /dev/null +++ b/GBHawk.ISettable.cs @@ -0,0 +1,77 @@ +using System; +using System.ComponentModel; + +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IStatable, ISettable + { + public GBSettings GetSettings() + { + return _settings.Clone(); + } + + public GBSyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(GBSettings o) + { + _settings = o; + return false; + } + + public bool PutSyncSettings(GBSyncSettings o) + { + bool ret = GBSyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private GBSettings _settings = new GBSettings(); + public GBSyncSettings _syncSettings = new GBSyncSettings(); + + public class GBSettings + { + public GBSettings Clone() + { + return (GBSettings)MemberwiseClone(); + } + } + + public class GBSyncSettings + { + private string _port1 = GBHawkControllerDeck.DefaultControllerName; + + [JsonIgnore] + public string Port1 + { + get { return _port1; } + set + { + if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port1 = value; + } + } + + public GBSyncSettings Clone() + { + return (GBSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GBSyncSettings x, GBSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/GBHawk.IStatable.cs b/GBHawk.IStatable.cs new file mode 100644 index 0000000000..a50da0305a --- /dev/null +++ b/GBHawk.IStatable.cs @@ -0,0 +1,102 @@ +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : 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); + timer.SyncState(ser); + ppu.SyncState(ser); + audio.SyncState(ser); + + ser.BeginSection("Gameboy"); + ser.Sync("core", ref core, false); + ser.Sync("Lag", ref _lagcount); + ser.Sync("Frame", ref _frame); + ser.Sync("IsLag", ref _islag); + _controllerDeck.SyncState(ser); + + ser.Sync("controller_state", ref controller_state); + ser.Sync("controller_state_old", ref controller_state_old); + ser.Sync("in_vblank", ref in_vblank); + ser.Sync("in_vblank_old", ref in_vblank_old); + ser.Sync("vblank_rise", ref vblank_rise); + ser.Sync("GB_bios_register", ref GB_bios_register); + ser.Sync("input_register", ref input_register); + + ser.Sync("serial_control", ref serial_control); + ser.Sync("serial_data_out", ref serial_data_out); + ser.Sync("serial_data_in", ref serial_data_in); + ser.Sync("serial_start_old", ref serial_start_old); + + ser.Sync("REG_FFFF", ref REG_FFFF); + ser.Sync("REG_FF0F", ref REG_FF0F); + ser.Sync("enable_VBL", ref enable_VBL); + ser.Sync("enable_LCDC", ref enable_PRS); + ser.Sync("enable_TIMO", ref enable_TIMO); + ser.Sync("enable_SER", ref enable_SER); + ser.Sync("enable_STAT", ref enable_STAT); + + // memory domains + ser.Sync("RAM", ref RAM, false); + ser.Sync("ZP_RAM", ref ZP_RAM, false); + ser.Sync("CHR_RAM", ref CHR_RAM, false); + ser.Sync("BG_map_1", ref BG_map_1, false); + ser.Sync("BG_map_2", ref BG_map_2, false); + ser.Sync("OAM", ref OAM, false); + // probably a better way to do this + if (cart_RAM != null) + { + ser.Sync("cart_RAM", ref cart_RAM, false); + } + + + + ser.EndSection(); + } + } +} diff --git a/GBHawk.cs b/GBHawk.cs new file mode 100644 index 0000000000..8a4ff534bf --- /dev/null +++ b/GBHawk.cs @@ -0,0 +1,223 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + [Core( + "GBHawk", + "", + isPorted: false, + isReleased: true)] + [ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))] + public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, + ISettable + { + // this register controls whether or not the GB BIOS is mapped into memory + public byte GB_bios_register; + + public byte input_register; + + public byte serial_control; + public byte serial_data_out; + public byte serial_data_in; + public bool serial_start_old; + + // The unused bits in this register are still read/writable + public byte REG_FFFF; + // The unused bits in this register (interrupt flags) are always set + public byte REG_FF0F = 0xE0; + public bool enable_VBL; + public bool enable_STAT; + public bool enable_TIMO; + public bool enable_SER; + public bool enable_PRS; + + + // memory domains + public byte[] RAM = new byte[0x2000]; + public byte[] ZP_RAM = new byte[0x80]; + public byte[] CHR_RAM = new byte[0x1800]; + public byte[] BG_map_1 = new byte[0x400]; + public byte[] BG_map_2 = new byte[0x400]; + public byte[] OAM = new byte[0xA0]; + + public readonly byte[] _rom; + public readonly byte[] _bios; + public readonly byte[] _sram = new byte[2048]; + public readonly byte[] header = new byte[0x50]; + + public byte[] cart_RAM; + + private int _frame = 0; + + public MapperBase mapper; + + private readonly ITraceable _tracer; + + public LR35902 cpu; + public PPU ppu; + public Timer timer; + public Audio audio; + + [CoreConstructor("GB")] + public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + cpu = new LR35902 + { + ReadMemory = ReadMemory, + WriteMemory = WriteMemory, + PeekMemory = ReadMemory, + DummyReadMemory = ReadMemory, + OnExecFetch = ExecFetch + }; + ppu = new PPU(); + timer = new Timer(); + audio = new Audio(); + + CoreComm = comm; + + _settings = (GBSettings)settings ?? new GBSettings(); + _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); + _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); + + byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", false, "BIOS Not Found, Cannot Load"); + _bios = Bios; + + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); + + string hash_md5 = null; + hash_md5 = "md5:" + rom.HashMD5(0, rom.Length); + Console.WriteLine(hash_md5); + + _rom = rom; + Setup_Mapper(); + + _frameHz = 60; + + timer.Core = this; + audio.Core = this; + ppu.Core = this; + + ser.Register(this); + ser.Register(audio); + ServiceProvider = ser; + + _tracer = new TraceBuffer { Header = cpu.TraceHeader }; + ser.Register(_tracer); + + SetupMemoryDomains(); + HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + private readonly GBHawkControllerDeck _controllerDeck; + + private void HardReset() + { + GB_bios_register = 0; // bios enable + in_vblank = true; // we start off in vblank since the LCD is off + in_vblank_old = true; + + Register_Reset(); + timer.Reset(); + ppu.Reset(); + + cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); + + _vidbuffer = new int[VirtualWidth * VirtualHeight]; + } + + private void ExecFetch(ushort addr) + { + MemoryCallbacks.CallExecutes(addr); + } + + private void Setup_Mapper() + { + // setup up mapper based on header entry + switch (header[0x47]) + { + case 0x0: mapper = new MapperDefault(); break; + case 0x1: mapper = new MapperMBC1(); break; + case 0x2: mapper = new MapperMBC1(); break; + case 0x3: mapper = new MapperMBC1(); break; + case 0x5: mapper = new MapperMBC2(); break; + case 0x6: mapper = new MapperMBC2(); break; + case 0x8: mapper = new MapperDefault(); break; + case 0x9: mapper = new MapperDefault(); break; + case 0xB: mapper = new MapperMMM01(); break; + case 0xC: mapper = new MapperMMM01(); break; + case 0xD: mapper = new MapperMMM01(); break; + case 0xF: mapper = new MapperMBC3(); break; + case 0x10: mapper = new MapperMBC3(); break; + case 0x11: mapper = new MapperMBC3(); break; + case 0x12: mapper = new MapperMBC3(); break; + case 0x13: mapper = new MapperMBC3(); break; + case 0x19: mapper = new MapperMBC5(); break; + case 0x1A: mapper = new MapperMBC5(); break; + case 0x1B: mapper = new MapperMBC5(); break; + case 0x1C: mapper = new MapperMBC5(); break; + case 0x1D: mapper = new MapperMBC5(); break; + case 0x1E: mapper = new MapperMBC5(); break; + case 0x20: mapper = new MapperMBC6(); break; + case 0x22: mapper = new MapperMBC7(); break; + case 0xFC: mapper = new MapperCamera(); break; + case 0xFD: mapper = new MapperTAMA5(); break; + case 0xFE: mapper = new MapperHuC3(); break; + case 0xFF: mapper = new MapperHuC1(); break; + + case 0x4: + case 0x7: + case 0xA: + case 0xE: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x1F: + case 0x21: + default: + // mapper not implemented + throw new Exception("Mapper not implemented"); + break; + + } + + Console.Write("Mapper: "); + Console.WriteLine(header[0x47]); + + cart_RAM = null; + + switch (header[0x49]) + { + case 1: + cart_RAM = new byte[0x800]; + break; + case 2: + cart_RAM = new byte[0x2000]; + break; + case 3: + cart_RAM = new byte[0x8000]; + break; + case 4: + cart_RAM = new byte[0x20000]; + break; + case 5: + cart_RAM = new byte[0x10000]; + break; + + } + + mapper.Core = this; + mapper.Initialize(); + } + } +} diff --git a/GBHawkControllerDeck.cs b/GBHawkControllerDeck.cs new file mode 100644 index 0000000000..464c4bfdcb --- /dev/null +++ b/GBHawkControllerDeck.cs @@ -0,0 +1,76 @@ +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.GBHawk +{ + public class GBHawkControllerDeck + { + public GBHawkControllerDeck(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 + .Concat(new[] + { + "Power", + "Reset", + }) + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Port1"); + Port1.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(GBHawkControllerDeck).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/GBHawkControllers.cs b/GBHawkControllers.cs new file mode 100644 index 0000000000..af504834c7 --- /dev/null +++ b/GBHawkControllers.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + /// + /// Represents a GB add on + /// + public interface IPort + { + byte Read(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Standard controls")] + public class StandardControls : IPort + { + public StandardControls(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Game Boy", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + for (int i = 0; i < 8; i++) + { + if (c.IsPressed(Definition.BoolButtons[i])) + { + result -= (byte)(1 << i); + } + } + + return result; + } + + private static readonly string[] BaseDefinition = + { + "Right", "Left", "Up", "Down", "A", "B", "Select", "Start" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } +} \ No newline at end of file diff --git a/HW_Registers.cs b/HW_Registers.cs new file mode 100644 index 0000000000..3e71185e35 --- /dev/null +++ b/HW_Registers.cs @@ -0,0 +1,283 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte Read_Registers(int addr) + { + byte ret = 0; + + switch (addr) + { + // Read Input + case 0xFF00: + _islag = false; + + 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) == 0x30) + { + // 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; + } + ret = input_register; + break; + + // Serial data port + case 0xFF01: + ret = serial_data_in; + break; + + // Serial port control + case 0xFF02: + ret = serial_control; + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + ret = timer.ReadReg(addr); + break; + + // Interrupt flags + case 0xFF0F: + ret = REG_FF0F; + 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; + + // Bios control register. Not sure if it is readable + case 0xFF50: + ret = 0xFF; + break; + + // interrupt control register + case 0xFFFF: + ret = REG_FFFF; + break; + + default: + ret = 0xFF; + break; + + } + return ret; + } + + public void Write_Registers(int addr, byte value) + { + switch (addr) + { + // select input + case 0xFF00: + input_register = (byte)(0xC0 | (value & 0x3F)); // top 2 bits always 1 + break; + + // Serial data port + case 0xFF01: + serial_data_out = value; + serial_data_in = serial_data_out; + break; + + // Serial port control + case 0xFF02: + serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1 + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + timer.WriteReg(addr, value); + break; + + // Interrupt flags + case 0xFF0F: + REG_FF0F = (byte)(0xE0 | value); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + 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; + + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs + case 0xFF50: + //Console.WriteLine(value); + if (GB_bios_register != 1) + { + GB_bios_register = value; + } + break; + + // interrupt control register + case 0xFFFF: + REG_FFFF = value; + enable_VBL = REG_FFFF.Bit(0); + enable_STAT = REG_FFFF.Bit(1); + enable_TIMO = REG_FFFF.Bit(2); + enable_SER = REG_FFFF.Bit(3); + enable_PRS = REG_FFFF.Bit(4); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + break; + } + } + + public void Register_Reset() + { + input_register = 0xCF; // not reading any input + serial_control = 0x7E; + } + } +} diff --git a/Mappers/MapperBase.cs b/Mappers/MapperBase.cs new file mode 100644 index 0000000000..f7aeb7ddca --- /dev/null +++ b/Mappers/MapperBase.cs @@ -0,0 +1,40 @@ +using BizHawk.Common; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class MapperBase + { + public GBHawk 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() + { + } + } +} diff --git a/Mappers/Mapper_Camera.cs b/Mappers/Mapper_Camera.cs new file mode 100644 index 0000000000..e247725259 --- /dev/null +++ b/Mappers/Mapper_Camera.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperCamera : 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 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/Mappers/Mapper_Default.cs b/Mappers/Mapper_Default.cs new file mode 100644 index 0000000000..609a696040 --- /dev/null +++ b/Mappers/Mapper_Default.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // 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 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/Mappers/Mapper_HuC1.cs b/Mappers/Mapper_HuC1.cs new file mode 100644 index 0000000000..0e33db1474 --- /dev/null +++ b/Mappers/Mapper_HuC1.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC1 : 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 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/Mappers/Mapper_HuC3.cs b/Mappers/Mapper_HuC3.cs new file mode 100644 index 0000000000..ff0e5cc6d1 --- /dev/null +++ b/Mappers/Mapper_HuC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC3 : 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 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/Mappers/Mapper_MBC1.cs b/Mappers/Mapper_MBC1.cs new file mode 100644 index 0000000000..2a8bed26be --- /dev/null +++ b/Mappers/Mapper_MBC1.cs @@ -0,0 +1,130 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC1 : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed + return Core._rom[addr]; + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0; + } + + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x1F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank &= 0xE0; + ROM_bank |= value; + } + else if (addr < 0x6000) + { + if (sel_mode) + { + RAM_bank = value & 0x3; + } + else + { + ROM_bank &= 0x1F; + ROM_bank |= ((value & 3) << 5); + } + } + else + { + sel_mode = (value & 1) > 0; + + if (sel_mode) + { + ROM_bank &= 0x1F; + } + else + { + RAM_bank = 0; + } + } + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("sel_mode", ref sel_mode); + } + } +} diff --git a/Mappers/Mapper_MBC2.cs b/Mappers/Mapper_MBC2.cs new file mode 100644 index 0000000000..3130aaa469 --- /dev/null +++ b/Mappers/Mapper_MBC2.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC2 : 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 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/Mappers/Mapper_MBC3.cs b/Mappers/Mapper_MBC3.cs new file mode 100644 index 0000000000..961f1769e4 --- /dev/null +++ b/Mappers/Mapper_MBC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC3 : 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 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/Mappers/Mapper_MBC5.cs b/Mappers/Mapper_MBC5.cs new file mode 100644 index 0000000000..312c9dc108 --- /dev/null +++ b/Mappers/Mapper_MBC5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC5 : 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 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/Mappers/Mapper_MBC6.cs b/Mappers/Mapper_MBC6.cs new file mode 100644 index 0000000000..7f9624adb9 --- /dev/null +++ b/Mappers/Mapper_MBC6.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC6 : 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 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/Mappers/Mapper_MBC7.cs b/Mappers/Mapper_MBC7.cs new file mode 100644 index 0000000000..40c9513b8b --- /dev/null +++ b/Mappers/Mapper_MBC7.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC7 : 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 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/Mappers/Mapper_MMM01.cs b/Mappers/Mapper_MMM01.cs new file mode 100644 index 0000000000..86c07e20ca --- /dev/null +++ b/Mappers/Mapper_MMM01.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMMM01 : 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 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/Mappers/Mapper_TAMA5.cs b/Mappers/Mapper_TAMA5.cs new file mode 100644 index 0000000000..eed5909336 --- /dev/null +++ b/Mappers/Mapper_TAMA5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperTAMA5 : 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 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/Mappers/ReadMe.txt b/Mappers/ReadMe.txt new file mode 100644 index 0000000000..0c94c8272c --- /dev/null +++ b/Mappers/ReadMe.txt @@ -0,0 +1,3 @@ +TODO: +Official Mappers +Unofficial Mappers diff --git a/MemoryMap.cs b/MemoryMap.cs new file mode 100644 index 0000000000..8357ebe073 --- /dev/null +++ b/MemoryMap.cs @@ -0,0 +1,164 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + + +/* + $FFFF Interrupt Enable Flag + $FF80-$FFFE Zero Page - 127 bytes + $FF00-$FF7F Hardware I/O Registers + $FEA0-$FEFF Unusable Memory + $FE00-$FE9F OAM - Object Attribute Memory + $E000-$FDFF Echo RAM - Reserved, Do Not Use + $D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only) + $C000-$CFFF Internal RAM - Bank 0 (fixed) + $A000-$BFFF Cartridge RAM (If Available) + $9C00-$9FFF BG Map Data 2 + $9800-$9BFF BG Map Data 1 + $8000-$97FF Character RAM + $4000-$7FFF Cartridge ROM - Switchable Banks 1-xx + $0150-$3FFF Cartridge ROM - Bank 0 (fixed) + $0100-$014F Cartridge Header Area + $0000-$00FF Restart and Interrupt Vectors +*/ + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte ReadMemory(ushort addr) + { + MemoryCallbacks.CallReads(addr); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr < 0x8000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0x9800) + { + return CHR_RAM[addr - 0x8000]; + } + else if (addr < 0x9C00) + { + return BG_map_1[addr - 0x9800]; + } + else if (addr < 0xA000) + { + return BG_map_2[addr - 0x9C00]; + } + else if (addr < 0xC000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0xE000) + { + return RAM[addr - 0xC000]; + } + else if (addr < 0xFE00) + { + return RAM[addr - 0xE000]; + } + else if (addr < 0xFEA0 && ppu.OAM_access) + { + return OAM[addr - 0xFE00]; + } + else if (addr < 0xFF00) + { + // unmapped memory, returns 0xFF + return 0xFF; + } + else if (addr < 0xFF80) + { + return Read_Registers(addr); + } + else if (addr < 0xFFFF) + { + return ZP_RAM[addr - 0xFF80]; + } + else + { + return Read_Registers(addr); + } + + } + + public void WriteMemory(ushort addr, byte value) + { + MemoryCallbacks.CallWrites(addr); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + // Can't write to BIOS region + } + else + { + mapper.WriteMemory(addr, value); + } + } + else if (addr < 0x8000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0x9800) + { + CHR_RAM[addr - 0x8000] = value; + } + else if (addr < 0x9C00) + { + BG_map_1[addr - 0x9800] = value; + } + else if (addr < 0xA000) + { + BG_map_2[addr - 0x9C00] = value; + } + else if (addr < 0xC000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0xE000) + { + RAM[addr - 0xC000] = value; + } + else if (addr < 0xFE00) + { + RAM[addr - 0xE000] = value; + } + else if (addr < 0xFEA0 && ppu.OAM_access) + { + OAM[addr - 0xFE00] = value; + } + else if (addr < 0xFF00) + { + // unmapped, writing has no effect + } + else if (addr < 0xFF80) + { + Write_Registers(addr, value); + } + else if (addr < 0xFFFF) + { + ZP_RAM[addr - 0xFF80] = value; + } + else + { + Write_Registers(addr, value); + } + } + } +} diff --git a/PPU.cs b/PPU.cs new file mode 100644 index 0000000000..9170cf1128 --- /dev/null +++ b/PPU.cs @@ -0,0 +1,922 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class PPU + { + public GBHawk Core { get; set; } + + //public byte BGP_l; + + // register variables + public byte LCDC; + public byte STAT; + public byte scroll_y; + public byte scroll_x; + public byte LY; + 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; + public bool hbl_set_once; + // OAM scan + public bool OAM_access; + public int OAM_scan_index; + public int SL_sprites_index; + public int[] SL_sprites = new int[40]; + public int write_sprite; + // render + public bool VRAM_access; + 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 int temp_fetch; + public int tile_inc; + public bool pre_render; + public byte[] tile_data = new byte[2]; + public byte[] tile_data_latch = new byte[2]; + 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 sprite_fetch_index; + public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr) + public int index_used; + public int sprite_ordered_index; + public int bottom_index; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: /*ret = DMA_addr; */ break; // DMA (not readable?) + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + LCDC = value; + break; + case 0xFF41: // STAT + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LY != LYC) { STAT &= 0xFB; } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public void tick() + { + // tick DMA + if (DMA_start) + { + if (DMA_clock >= 4) + { + OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + if ((DMA_inc % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + } + else + { + Core.OAM[DMA_inc] = DMA_byte; + } + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock==648) + { + DMA_start = false; + OAM_access = true; + } + } + + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 8)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (cycle == 4) + { + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + if (cycle >= 4 && cycle < 84) + { + // here OAM scanning is performed + OAM_scan(cycle - 4); + } + else if (cycle >= 84 && LY < 144) + { + // render the screen and handle hblank + render(cycle - 84); + } + } + + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted + if ((cycle == 4) && (LY != 0)) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + + if (cycle==456) + { + cycle = 0; + LY+=LY_inc; + + if (LY==0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + } + + Core.cpu.LY = LY; + + if (LY==144) + { + Core.in_vblank = true; + } + } + } + else + { + // screen disable sets STAT as though it were vblank, but there is no Stat IRQ asserted + STAT &= 0xFC; + STAT |= 0x01; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + + } + + // might be needed, not sure yet + public void latch_delay() + { + //BGP_l = BGP; + } + + public void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access = false; + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + // (sprite Y - 16) equals LY, we have a sprite + if ((Core.OAM[OAM_scan_index * 4] - 16) <= LY && + ((Core.OAM[OAM_scan_index * 4] - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = Core.OAM[OAM_scan_index * 4]; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + SL_sprites[SL_sprites_index * 4 + write_sprite] = Core.OAM[OAM_scan_index * 4 + write_sprite]; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access = false; + VRAM_access = false; + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = 0; + sl_use_index = 0; + index_used = 0; + bottom_index = 0; + sprite_ordered_index = 0; + fetch_sprite = false; + no_sprites = false; + + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + if (SL_sprites_index == 0) + { + no_sprites = true; + } + } + + if (!pre_render && !fetch_sprite) + { + // start by fetching all the sprites that need to be fetched + if (!no_sprites) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < SL_sprites[i * 4 + 1]) && + !index_used.Bit(i)) + { + fetch_sprite = true; + sprite_fetch_index = 0; + } + } + } + + if (!fetch_sprite) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + pixel = (BGP >> (pixel * 2)) & 3; + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int i = bottom_index; + int s_pixel = 0; + int sprite_attr = 0; + + while (i < sprite_ordered_index) + { + if (SL_sprites_ordered[i * 4] == pixel_counter) + { + bottom_index++; + if (bottom_index == SL_sprites_index) { no_sprites = true; } + } + else if (!have_sprite) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = sprite_data[0] + sprite_data[1]; + sprite_attr = SL_sprites_ordered[i * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_sprite = true; + } + } + i++; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + if (s_pixel != 0) { use_sprite = true; } + } + else if (pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)GBHawk.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_set_once = true; + } + } + render_counter++; + } + } + + if (!fetch_sprite) + { + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + + } + else + { + if (!pre_render) + { + tile_inc++; + } + read_case = 1; + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = -1; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + break; + + case 6: // read from tile graphics (for the window) + break; + + case 7: // read from tile graphics (for the window) + break; + + case 8: // done reading, we are now in phase 0 + + OAM_access = true; + VRAM_access = true; + + STAT &= 0xFC; + STAT |= 0x00; + pre_render = true; + if (hbl_set_once) + { + if (STAT.Bit(3)) { HBL_INT = true; } + hbl_set_once = false; + } + + break; + } + + internal_cycle++; + } + + if (fetch_sprite) + { + if (sprite_fetch_index < SL_sprites_index) + { + if (pixel_counter != 0) { + if ((pixel_counter == (SL_sprites[sprite_fetch_index * 4 + 1] - 8)) && + //(pixel_counter < SL_sprites[sprite_fetch_index * 4 + 1]) && + !index_used.Bit(sprite_fetch_index)) + { + sl_use_index = sprite_fetch_index; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[sprite_fetch_index * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[sprite_fetch_index * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + sprite_fetch_index++; + if (sprite_fetch_index == SL_sprites_index) { fetch_sprite = false; } + } + else + { + // whan pixel counter is 0, we want to scan all the points before 0 as well + // certainly non-physical but good enough for now + for (int j = -7; j < 1; j++) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((j == (SL_sprites[i * 4 + 1] - 8)) && + !index_used.Bit(i)) + { + sl_use_index = i; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[i * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[i * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + } + } + fetch_sprite = false; + } + } + } + } + + public void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0; + BGP = 0; + obj_pal_0 = 0; + obj_pal_1 = 0; + window_y = 0; + window_x = 0; + LY_inc = 1; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + } + + public void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + public void SyncState(Serializer ser) + { + ser.Sync("LCDC", ref LCDC); + ser.Sync("STAT", ref STAT); + ser.Sync("scroll_y", ref scroll_y); + ser.Sync("scroll_x", ref scroll_x); + ser.Sync("LY", ref LY); + ser.Sync("LYinc", ref LY_inc); + ser.Sync("LYC", ref LYC); + ser.Sync("DMA_addr", ref DMA_addr); + ser.Sync("BGP", ref BGP); + ser.Sync("obj_pal_0", ref obj_pal_0); + ser.Sync("obj_pal_1", ref obj_pal_1); + ser.Sync("window_y", ref window_y); + ser.Sync("window_x", ref window_x); + ser.Sync("DMA_start", ref DMA_start); + ser.Sync("DMA_clock", ref DMA_clock); + ser.Sync("DMA_inc", ref DMA_inc); + ser.Sync("DMA_byte", ref DMA_byte); + + ser.Sync("LYC_INT", ref LYC_INT); + ser.Sync("HBL_INT", ref HBL_INT); + ser.Sync("VBL_INT", ref VBL_INT); + ser.Sync("OAM_INT", ref OAM_INT); + ser.Sync("stat_line", ref stat_line); + ser.Sync("stat_line_old", ref stat_line_old); + ser.Sync("hbl_set_once", ref hbl_set_once); + ser.Sync("LCD_was_off", ref LCD_was_off); + ser.Sync("OAM_access", ref OAM_access); + ser.Sync("OAM_scan_index", ref OAM_scan_index); + ser.Sync("SL_sprites_index", ref SL_sprites_index); + ser.Sync("SL_sprites", ref SL_sprites, false); + ser.Sync("write_sprite", ref write_sprite); + + ser.Sync("VRAM_access", ref VRAM_access); + ser.Sync("read_case", ref read_case); + ser.Sync("internal_cycle", ref internal_cycle); + ser.Sync("y_tile", ref y_tile); + ser.Sync("y_scroll_offset", ref y_scroll_offset); + ser.Sync("x_tile", ref x_tile); + ser.Sync("x_scroll_offset", ref x_scroll_offset); + ser.Sync("tile_byte", ref tile_byte); + ser.Sync("sprite_fetch_cycles", ref sprite_fetch_cycles); + ser.Sync("fetch_sprite", ref fetch_sprite); + ser.Sync("temp_fetch", ref temp_fetch); + ser.Sync("tile_inc", ref tile_inc); + ser.Sync("pre_render", ref pre_render); + ser.Sync("tile_data", ref tile_data, false); + ser.Sync("tile_data_latch", ref tile_data_latch, false); + ser.Sync("latch_counter", ref latch_counter); + ser.Sync("latch_new_data", ref latch_new_data); + ser.Sync("render_counter", ref render_counter); + ser.Sync("render_offset", ref render_offset); + ser.Sync("pixel_counter", ref pixel_counter); + ser.Sync("pixel", ref pixel); + ser.Sync("sprite_data", ref sprite_data, false); + ser.Sync("sl_use_index", ref sl_use_index); + ser.Sync("sprite_sel", ref sprite_sel, false); + ser.Sync("no_sprites", ref no_sprites); + ser.Sync("sprite_fetch_index", ref sprite_fetch_index); + ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); + ser.Sync("index_used", ref index_used); + ser.Sync("sprite_ordered_index", ref sprite_ordered_index); + ser.Sync("bottom_index", ref bottom_index); + + } + } +} diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1 @@ +TODO: diff --git a/Timer.cs b/Timer.cs new file mode 100644 index 0000000000..b243081d70 --- /dev/null +++ b/Timer.cs @@ -0,0 +1,175 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Timer Emulation + public class Timer + { + public GBHawk Core { get; set; } + + public ushort divider_reg; + public byte timer_reload; + public byte timer; + public byte timer_old; + public byte timer_control; + public byte pending_reload; + public byte write_ignore; + public bool old_state; + public bool state; + public bool reload_block; + public bool TMA_coincidence; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF04: ret = (byte)(divider_reg >> 8); break; // DIV register + case 0xFF05: ret = timer; break; // TIMA (Timer Counter) + case 0xFF06: ret = timer_reload; break; // TMA (Timer Modulo) + case 0xFF07: ret = timer_control; break; // TAC (Timer Control) + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + // DIV register + case 0xFF04: + divider_reg = 0; + break; + + // TIMA (Timer Counter) + case 0xFF05: + if (write_ignore == 0) + { + timer_old = timer; + timer = value; + reload_block = true; + } + break; + + // TMA (Timer Modulo) + case 0xFF06: + timer_reload = value; + if (TMA_coincidence) + { + timer = timer_reload; + timer_old = timer; + } + break; + + // TAC (Timer Control) + case 0xFF07: + timer_control = (byte)((timer_control & 0xf8) | (value & 0x7)); // only bottom 3 bits function + break; + } + } + + public void tick_1() + { + if (write_ignore > 0) + { + write_ignore--; + if (write_ignore==0) + { + TMA_coincidence = false; + } + } + + if (pending_reload > 0) + { + pending_reload--; + if (pending_reload == 0 && !reload_block) + { + timer = timer_reload; + timer_old = timer; + write_ignore = 4; + TMA_coincidence = true; + + // set interrupts + if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x04; + } + } + } + + public void tick_2() + { + divider_reg++; + + // pick a bit to test based on the current value of timer control + switch (timer_control & 3) + { + case 0: + state = divider_reg.Bit(9); + break; + case 1: + state = divider_reg.Bit(3); + break; + case 2: + state = divider_reg.Bit(5); + break; + case 3: + state = divider_reg.Bit(7); + break; + } + + // And it with the state of the timer on/off bit + state &= timer_control.Bit(2); + + // this procedure allows several glitchy timer ticks, since it only measures falling edge of the state + // so things like turning the timer off and resetting the divider will tick the timer + if (old_state && !state) + { + timer_old = timer; + timer++; + + // if overflow, set the interrupt flag and reload the timer (4 clocks later) + if (timer < timer_old) + { + pending_reload = 4; + reload_block = false; + } + } + + old_state = state; + } + + public void Reset() + { + divider_reg = 0; + timer_reload = 0; + timer = 0; + timer_old = 0; + timer_control = 0xF8; + pending_reload = 0; + write_ignore = 0; + old_state = false; + state = false; + reload_block = false; + TMA_coincidence = false; + } + + public void SyncState(Serializer ser) + { + ser.Sync("divider_reg", ref divider_reg); + ser.Sync("timer_reload", ref timer_reload); + ser.Sync("timer", ref timer); + ser.Sync("timer_old", ref timer_old); + ser.Sync("timer_control", ref timer_control); + ser.Sync("pending_reload", ref pending_reload); + ser.Sync("write_ignore", ref write_ignore); + ser.Sync("old_state", ref old_state); + ser.Sync("state", ref state); + ser.Sync("reload_block", ref reload_block); + ser.Sync("TMA_coincidence", ref TMA_coincidence); + } + } +} \ No newline at end of file From ee824dc308a92e6ead445646e5c2a876e5eece11 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 9 Nov 2017 09:55:24 -0500 Subject: [PATCH 13/28] Oops --- Audio.cs | 213 --------- GBHawk.IDebuggable.cs | 77 ---- GBHawk.IEmulator.cs | 156 ------- GBHawk.IInputPollable.cs | 24 - GBHawk.IMemoryDomains.cs | 56 --- GBHawk.ISaveRam.cs | 26 -- GBHawk.ISettable.cs | 77 ---- GBHawk.IStatable.cs | 102 ----- GBHawk.cs | 223 --------- GBHawkControllerDeck.cs | 76 ---- GBHawkControllers.cs | 68 --- HW_Registers.cs | 283 ------------ Mappers/MapperBase.cs | 40 -- Mappers/Mapper_Camera.cs | 59 --- Mappers/Mapper_Default.cs | 59 --- Mappers/Mapper_HuC1.cs | 59 --- Mappers/Mapper_HuC3.cs | 59 --- Mappers/Mapper_MBC1.cs | 130 ------ Mappers/Mapper_MBC2.cs | 59 --- Mappers/Mapper_MBC3.cs | 59 --- Mappers/Mapper_MBC5.cs | 59 --- Mappers/Mapper_MBC6.cs | 59 --- Mappers/Mapper_MBC7.cs | 59 --- Mappers/Mapper_MMM01.cs | 59 --- Mappers/Mapper_TAMA5.cs | 59 --- Mappers/ReadMe.txt | 3 - MemoryMap.cs | 164 ------- PPU.cs | 922 -------------------------------------- Timer.cs | 175 -------- 29 files changed, 3464 deletions(-) delete mode 100644 Audio.cs delete mode 100644 GBHawk.IDebuggable.cs delete mode 100644 GBHawk.IEmulator.cs delete mode 100644 GBHawk.IInputPollable.cs delete mode 100644 GBHawk.IMemoryDomains.cs delete mode 100644 GBHawk.ISaveRam.cs delete mode 100644 GBHawk.ISettable.cs delete mode 100644 GBHawk.IStatable.cs delete mode 100644 GBHawk.cs delete mode 100644 GBHawkControllerDeck.cs delete mode 100644 GBHawkControllers.cs delete mode 100644 HW_Registers.cs delete mode 100644 Mappers/MapperBase.cs delete mode 100644 Mappers/Mapper_Camera.cs delete mode 100644 Mappers/Mapper_Default.cs delete mode 100644 Mappers/Mapper_HuC1.cs delete mode 100644 Mappers/Mapper_HuC3.cs delete mode 100644 Mappers/Mapper_MBC1.cs delete mode 100644 Mappers/Mapper_MBC2.cs delete mode 100644 Mappers/Mapper_MBC3.cs delete mode 100644 Mappers/Mapper_MBC5.cs delete mode 100644 Mappers/Mapper_MBC6.cs delete mode 100644 Mappers/Mapper_MBC7.cs delete mode 100644 Mappers/Mapper_MMM01.cs delete mode 100644 Mappers/Mapper_TAMA5.cs delete mode 100644 Mappers/ReadMe.txt delete mode 100644 MemoryMap.cs delete mode 100644 PPU.cs delete mode 100644 Timer.cs diff --git a/Audio.cs b/Audio.cs deleted file mode 100644 index 7fcaaca85f..0000000000 --- a/Audio.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; - -using BizHawk.Common; -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Audio Emulation - public class Audio : ISoundProvider - { - public GBHawk Core { get; set; } - - 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, - 0x3F, 0x00, 0xFF, 0xBF, - 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, - 0xFF, 0x00, 0x00, 0xBF, - 0x00, 0x00, 0x70}; - - public byte[] Audio_Regs = new byte[21]; - - public byte[] Wave_RAM = new byte [16]; - - - public byte ReadReg(int addr) - { - byte ret = 0; - - switch (addr) - { - case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) - case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) - case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) - case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) - case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) - case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) - case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) - case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) - case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) - case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) - case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) - case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) - case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) - case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) - case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (sweep) - case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (sweep) - case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (sweep) - case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (sweep) - case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (sweep) - case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (sweep) - case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (sweep) - - // wave ram table - 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 = Wave_RAM[addr & 0x0F]; - break; - - } - - return ret; - } - - public void WriteReg(int addr, byte value) - { - switch (addr) - { - case 0xFF10: Audio_Regs[NR10] = value; break; // NR10 (sweep) - case 0xFF11: Audio_Regs[NR11] = value; break; // NR11 (sound length / wave pattern duty %) - case 0xFF12: Audio_Regs[NR12] = value; break; // NR12 (envelope) - case 0xFF13: Audio_Regs[NR13] = value; break; // NR13 (freq low) - case 0xFF14: Audio_Regs[NR14] = value; break; // NR14 (freq hi) - case 0xFF16: Audio_Regs[NR21] = value; break; // NR21 (sound length / wave pattern duty %) - case 0xFF17: Audio_Regs[NR22] = value; break; // NR22 (envelope) - case 0xFF18: Audio_Regs[NR23] = value; break; // NR23 (freq low) - case 0xFF19: Audio_Regs[NR24] = value; break; // NR24 (freq hi) - case 0xFF1A: Audio_Regs[NR30] = value; break; // NR30 (on/off) - case 0xFF1B: Audio_Regs[NR31] = value; break; // NR31 (length) - case 0xFF1C: Audio_Regs[NR32] = value; break; // NR32 (level output) - case 0xFF1D: Audio_Regs[NR33] = value; break; // NR33 (freq low) - case 0xFF1E: Audio_Regs[NR34] = value; break; // NR34 (freq hi) - case 0xFF20: Audio_Regs[NR41] = value; break; // NR41 (sweep) - case 0xFF21: Audio_Regs[NR42] = value; break; // NR42 (sweep) - case 0xFF22: Audio_Regs[NR43] = value; break; // NR43 (sweep) - case 0xFF23: Audio_Regs[NR44] = value; break; // NR44 (sweep) - case 0xFF24: Audio_Regs[NR50] = value; break; // NR50 (sweep) - case 0xFF25: Audio_Regs[NR51] = value; break; // NR51 (sweep) - case 0xFF26: Audio_Regs[NR52] = value; break; // NR52 (sweep) - - // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; - - } - } - - public void tick() - { - - } - - public void reset() - { - Wave_RAM = new byte[16]; - - Audio_Regs = new byte[21]; - } - - public void SyncState(Serializer ser) - { - ser.Sync("Audio_Regs", ref Audio_Regs, false); - ser.Sync("Wave_Ram", ref Wave_RAM, false); - - } - - #region audio - - public bool CanProvideAsync => false; - - public int _spf; - public int AudioClocks; - - 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) - { - short[] ret = new short[_spf * 2]; - nsamp = _spf; - GetSamples(ret); - samples = ret; - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - AudioClocks = 0; - } - - // Exposing this as GetSamplesAsync would allow this to provide async sound - // However, it does nothing special for async sound so I don't see a point - private void GetSamples(short[] samples) - { - - } - - #endregion - } -} \ No newline at end of file diff --git a/GBHawk.IDebuggable.cs b/GBHawk.IDebuggable.cs deleted file mode 100644 index 543cecf962..0000000000 --- a/GBHawk.IDebuggable.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; - -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : 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(); - - public bool CanStep(StepType type) - { - return false; - } - - [FeatureNotImplemented] - public void Step(StepType type) - { - throw new NotImplementedException(); - } - - public int TotalExecutedCycles - { - get { return cpu.TotalExecutedCycles; } - } - } -} diff --git a/GBHawk.IEmulator.cs b/GBHawk.IEmulator.cs deleted file mode 100644 index 189feb6af7..0000000000 --- a/GBHawk.IEmulator.cs +++ /dev/null @@ -1,156 +0,0 @@ -using BizHawk.Common.NumberExtensions; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : IEmulator, IVideoProvider - { - public IEmulatorServiceProvider ServiceProvider { get; } - - public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; - - public byte controller_state; - public byte controller_state_old; - public bool in_vblank_old; - public bool in_vblank; - public bool vblank_rise; - - public void 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")) - { - // it seems that theMachine.Reset() doesn't clear ram, etc - // this should leave hsram intact but clear most other things - HardReset(); - } - - _islag = true; - - GetControllerState(controller); - - do_frame(); - - if (_islag) - { - _lagcount++; - } - } - - public void do_frame() - { - // gameboy frames can be variable lengths - // we want to end a frame when VBlank turns from false to true - int ticker = 0; - while (!vblank_rise && (ticker < 100000)) - { - audio.tick(); - timer.tick_1(); - ppu.tick(); - - cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); - - timer.tick_2(); - - - if (in_vblank && !in_vblank_old) - { - vblank_rise = true; - } - ticker++; - in_vblank_old = in_vblank; - } - - vblank_rise = false; - } - - public void RunCPUCycle() - { - - } - - public void GetControllerState(IController controller) - { - InputCallbacks.Call(); - controller_state = _controllerDeck.ReadPort1(controller); - - // set interrupt flag if a pin went from high to low - if (controller_state < controller_state_old) - { - if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } - REG_FF0F |= 0x10; - } - - controller_state_old = controller_state; - } - - public void serial_transfer() - { - if (serial_control.Bit(7) && !serial_start_old) - { - serial_start_old = true; - - // transfer out on byte of data - // needs to be modelled - } - } - - public int Frame => _frame; - - public string SystemId => "GB"; - - public bool DeterministicEmulation { get; set; } - - public void ResetCounters() - { - _frame = 0; - _lagcount = 0; - _islag = false; - } - - public CoreComm CoreComm { get; } - - public void Dispose() - { - - } - - - #region Video provider - - public int _frameHz = 60; - - public int[] _vidbuffer; - - public int[] GetVideoBuffer() - { - return _vidbuffer; - } - - 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 = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; - - #endregion - } -} diff --git a/GBHawk.IInputPollable.cs b/GBHawk.IInputPollable.cs deleted file mode 100644 index 30af4780c6..0000000000 --- a/GBHawk.IInputPollable.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : 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/GBHawk.IMemoryDomains.cs b/GBHawk.IMemoryDomains.cs deleted file mode 100644 index aa9ecf8b2e..0000000000 --- a/GBHawk.IMemoryDomains.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk - { - 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( - "Zero Page RAM", - ZP_RAM.Length, - MemoryDomain.Endian.Little, - addr => ZP_RAM[addr], - (addr, value) => ZP_RAM[addr] = value, - 1), - new MemoryDomainDelegate( - "System Bus", - 0X10000, - MemoryDomain.Endian.Little, - addr => PeekSystemBus(addr), - (addr, value) => PokeSystemBus(addr, value), - 1) - }; - - MemoryDomains = new MemoryDomainList(domains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); - } - - private byte PeekSystemBus(long addr) - { - ushort addr2 = (ushort)(addr & 0xFFFF); - return ReadMemory(addr2); - } - - private void PokeSystemBus(long addr, byte value) - { - ushort addr2 = (ushort)(addr & 0xFFFF); - WriteMemory(addr2, value); - } - } -} diff --git a/GBHawk.ISaveRam.cs b/GBHawk.ISaveRam.cs deleted file mode 100644 index 5936f9543c..0000000000 --- a/GBHawk.ISaveRam.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : ISaveRam - { - public byte[] CloneSaveRam() - { - return (byte[])_sram.Clone(); - } - - public void StoreSaveRam(byte[] data) - { - Buffer.BlockCopy(data, 0, _sram, 0, data.Length); - } - - public bool SaveRamModified - { - get - { - return false; - } - } - } -} diff --git a/GBHawk.ISettable.cs b/GBHawk.ISettable.cs deleted file mode 100644 index 75a267bf62..0000000000 --- a/GBHawk.ISettable.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.ComponentModel; - -using Newtonsoft.Json; - -using BizHawk.Common; -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : IEmulator, IStatable, ISettable - { - public GBSettings GetSettings() - { - return _settings.Clone(); - } - - public GBSyncSettings GetSyncSettings() - { - return _syncSettings.Clone(); - } - - public bool PutSettings(GBSettings o) - { - _settings = o; - return false; - } - - public bool PutSyncSettings(GBSyncSettings o) - { - bool ret = GBSyncSettings.NeedsReboot(_syncSettings, o); - _syncSettings = o; - return ret; - } - - private GBSettings _settings = new GBSettings(); - public GBSyncSettings _syncSettings = new GBSyncSettings(); - - public class GBSettings - { - public GBSettings Clone() - { - return (GBSettings)MemberwiseClone(); - } - } - - public class GBSyncSettings - { - private string _port1 = GBHawkControllerDeck.DefaultControllerName; - - [JsonIgnore] - public string Port1 - { - get { return _port1; } - set - { - if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) - { - throw new InvalidOperationException("Invalid controller type: " + value); - } - - _port1 = value; - } - } - - public GBSyncSettings Clone() - { - return (GBSyncSettings)MemberwiseClone(); - } - - public static bool NeedsReboot(GBSyncSettings x, GBSyncSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - } - } -} diff --git a/GBHawk.IStatable.cs b/GBHawk.IStatable.cs deleted file mode 100644 index a50da0305a..0000000000 --- a/GBHawk.IStatable.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.IO; - -using BizHawk.Common; -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk : 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); - timer.SyncState(ser); - ppu.SyncState(ser); - audio.SyncState(ser); - - ser.BeginSection("Gameboy"); - ser.Sync("core", ref core, false); - ser.Sync("Lag", ref _lagcount); - ser.Sync("Frame", ref _frame); - ser.Sync("IsLag", ref _islag); - _controllerDeck.SyncState(ser); - - ser.Sync("controller_state", ref controller_state); - ser.Sync("controller_state_old", ref controller_state_old); - ser.Sync("in_vblank", ref in_vblank); - ser.Sync("in_vblank_old", ref in_vblank_old); - ser.Sync("vblank_rise", ref vblank_rise); - ser.Sync("GB_bios_register", ref GB_bios_register); - ser.Sync("input_register", ref input_register); - - ser.Sync("serial_control", ref serial_control); - ser.Sync("serial_data_out", ref serial_data_out); - ser.Sync("serial_data_in", ref serial_data_in); - ser.Sync("serial_start_old", ref serial_start_old); - - ser.Sync("REG_FFFF", ref REG_FFFF); - ser.Sync("REG_FF0F", ref REG_FF0F); - ser.Sync("enable_VBL", ref enable_VBL); - ser.Sync("enable_LCDC", ref enable_PRS); - ser.Sync("enable_TIMO", ref enable_TIMO); - ser.Sync("enable_SER", ref enable_SER); - ser.Sync("enable_STAT", ref enable_STAT); - - // memory domains - ser.Sync("RAM", ref RAM, false); - ser.Sync("ZP_RAM", ref ZP_RAM, false); - ser.Sync("CHR_RAM", ref CHR_RAM, false); - ser.Sync("BG_map_1", ref BG_map_1, false); - ser.Sync("BG_map_2", ref BG_map_2, false); - ser.Sync("OAM", ref OAM, false); - // probably a better way to do this - if (cart_RAM != null) - { - ser.Sync("cart_RAM", ref cart_RAM, false); - } - - - - ser.EndSection(); - } - } -} diff --git a/GBHawk.cs b/GBHawk.cs deleted file mode 100644 index 8a4ff534bf..0000000000 --- a/GBHawk.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; - -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Common.Components.LR35902; -using BizHawk.Common.NumberExtensions; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - [Core( - "GBHawk", - "", - isPorted: false, - isReleased: true)] - [ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))] - public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, - ISettable - { - // this register controls whether or not the GB BIOS is mapped into memory - public byte GB_bios_register; - - public byte input_register; - - public byte serial_control; - public byte serial_data_out; - public byte serial_data_in; - public bool serial_start_old; - - // The unused bits in this register are still read/writable - public byte REG_FFFF; - // The unused bits in this register (interrupt flags) are always set - public byte REG_FF0F = 0xE0; - public bool enable_VBL; - public bool enable_STAT; - public bool enable_TIMO; - public bool enable_SER; - public bool enable_PRS; - - - // memory domains - public byte[] RAM = new byte[0x2000]; - public byte[] ZP_RAM = new byte[0x80]; - public byte[] CHR_RAM = new byte[0x1800]; - public byte[] BG_map_1 = new byte[0x400]; - public byte[] BG_map_2 = new byte[0x400]; - public byte[] OAM = new byte[0xA0]; - - public readonly byte[] _rom; - public readonly byte[] _bios; - public readonly byte[] _sram = new byte[2048]; - public readonly byte[] header = new byte[0x50]; - - public byte[] cart_RAM; - - private int _frame = 0; - - public MapperBase mapper; - - private readonly ITraceable _tracer; - - public LR35902 cpu; - public PPU ppu; - public Timer timer; - public Audio audio; - - [CoreConstructor("GB")] - public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) - { - var ser = new BasicServiceProvider(this); - - cpu = new LR35902 - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = ReadMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; - ppu = new PPU(); - timer = new Timer(); - audio = new Audio(); - - CoreComm = comm; - - _settings = (GBSettings)settings ?? new GBSettings(); - _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); - _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); - - byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", false, "BIOS Not Found, Cannot Load"); - _bios = Bios; - - Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); - - string hash_md5 = null; - hash_md5 = "md5:" + rom.HashMD5(0, rom.Length); - Console.WriteLine(hash_md5); - - _rom = rom; - Setup_Mapper(); - - _frameHz = 60; - - timer.Core = this; - audio.Core = this; - ppu.Core = this; - - ser.Register(this); - ser.Register(audio); - ServiceProvider = ser; - - _tracer = new TraceBuffer { Header = cpu.TraceHeader }; - ser.Register(_tracer); - - SetupMemoryDomains(); - HardReset(); - } - - public DisplayType Region => DisplayType.NTSC; - - private readonly GBHawkControllerDeck _controllerDeck; - - private void HardReset() - { - GB_bios_register = 0; // bios enable - in_vblank = true; // we start off in vblank since the LCD is off - in_vblank_old = true; - - Register_Reset(); - timer.Reset(); - ppu.Reset(); - - cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); - - _vidbuffer = new int[VirtualWidth * VirtualHeight]; - } - - private void ExecFetch(ushort addr) - { - MemoryCallbacks.CallExecutes(addr); - } - - private void Setup_Mapper() - { - // setup up mapper based on header entry - switch (header[0x47]) - { - case 0x0: mapper = new MapperDefault(); break; - case 0x1: mapper = new MapperMBC1(); break; - case 0x2: mapper = new MapperMBC1(); break; - case 0x3: mapper = new MapperMBC1(); break; - case 0x5: mapper = new MapperMBC2(); break; - case 0x6: mapper = new MapperMBC2(); break; - case 0x8: mapper = new MapperDefault(); break; - case 0x9: mapper = new MapperDefault(); break; - case 0xB: mapper = new MapperMMM01(); break; - case 0xC: mapper = new MapperMMM01(); break; - case 0xD: mapper = new MapperMMM01(); break; - case 0xF: mapper = new MapperMBC3(); break; - case 0x10: mapper = new MapperMBC3(); break; - case 0x11: mapper = new MapperMBC3(); break; - case 0x12: mapper = new MapperMBC3(); break; - case 0x13: mapper = new MapperMBC3(); break; - case 0x19: mapper = new MapperMBC5(); break; - case 0x1A: mapper = new MapperMBC5(); break; - case 0x1B: mapper = new MapperMBC5(); break; - case 0x1C: mapper = new MapperMBC5(); break; - case 0x1D: mapper = new MapperMBC5(); break; - case 0x1E: mapper = new MapperMBC5(); break; - case 0x20: mapper = new MapperMBC6(); break; - case 0x22: mapper = new MapperMBC7(); break; - case 0xFC: mapper = new MapperCamera(); break; - case 0xFD: mapper = new MapperTAMA5(); break; - case 0xFE: mapper = new MapperHuC3(); break; - case 0xFF: mapper = new MapperHuC1(); break; - - case 0x4: - case 0x7: - case 0xA: - case 0xE: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x1F: - case 0x21: - default: - // mapper not implemented - throw new Exception("Mapper not implemented"); - break; - - } - - Console.Write("Mapper: "); - Console.WriteLine(header[0x47]); - - cart_RAM = null; - - switch (header[0x49]) - { - case 1: - cart_RAM = new byte[0x800]; - break; - case 2: - cart_RAM = new byte[0x2000]; - break; - case 3: - cart_RAM = new byte[0x8000]; - break; - case 4: - cart_RAM = new byte[0x20000]; - break; - case 5: - cart_RAM = new byte[0x10000]; - break; - - } - - mapper.Core = this; - mapper.Initialize(); - } - } -} diff --git a/GBHawkControllerDeck.cs b/GBHawkControllerDeck.cs deleted file mode 100644 index 464c4bfdcb..0000000000 --- a/GBHawkControllerDeck.cs +++ /dev/null @@ -1,76 +0,0 @@ -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.GBHawk -{ - public class GBHawkControllerDeck - { - public GBHawkControllerDeck(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 - .Concat(new[] - { - "Power", - "Reset", - }) - .ToList() - }; - - Definition.FloatControls.AddRange(Port1.Definition.FloatControls); - - Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); - } - - public byte ReadPort1(IController c) - { - return Port1.Read(c); - } - - public ControllerDefinition Definition { get; } - - public void SyncState(Serializer ser) - { - ser.BeginSection("Port1"); - Port1.SyncState(ser); - ser.EndSection(); - } - - private readonly IPort Port1; - - private static Dictionary _controllerTypes; - - public static Dictionary ValidControllerTypes - { - get - { - if (_controllerTypes == null) - { - _controllerTypes = typeof(GBHawkControllerDeck).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/GBHawkControllers.cs b/GBHawkControllers.cs deleted file mode 100644 index af504834c7..0000000000 --- a/GBHawkControllers.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; - -using BizHawk.Common; -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - /// - /// Represents a GB add on - /// - public interface IPort - { - byte Read(IController c); - - ControllerDefinition Definition { get; } - - void SyncState(Serializer ser); - - int PortNum { get; } - } - - [DisplayName("Standard controls")] - public class StandardControls : IPort - { - public StandardControls(int portNum) - { - PortNum = portNum; - Definition = new ControllerDefinition - { - Name = "Game Boy", - BoolButtons = BaseDefinition - .Select(b => "P" + PortNum + " " + b) - .ToList() - }; - } - - public int PortNum { get; } - - public ControllerDefinition Definition { get; } - - public byte Read(IController c) - { - byte result = 0xFF; - for (int i = 0; i < 8; i++) - { - if (c.IsPressed(Definition.BoolButtons[i])) - { - result -= (byte)(1 << i); - } - } - - return result; - } - - private static readonly string[] BaseDefinition = - { - "Right", "Left", "Up", "Down", "A", "B", "Select", "Start" - }; - - public void SyncState(Serializer ser) - { - //nothing - } - } -} \ No newline at end of file diff --git a/HW_Registers.cs b/HW_Registers.cs deleted file mode 100644 index 3e71185e35..0000000000 --- a/HW_Registers.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; -using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk - { - public byte Read_Registers(int addr) - { - byte ret = 0; - - switch (addr) - { - // Read Input - case 0xFF00: - _islag = false; - - 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) == 0x30) - { - // 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; - } - ret = input_register; - break; - - // Serial data port - case 0xFF01: - ret = serial_data_in; - break; - - // Serial port control - case 0xFF02: - ret = serial_control; - break; - - // Timer Registers - case 0xFF04: - case 0xFF05: - case 0xFF06: - case 0xFF07: - ret = timer.ReadReg(addr); - break; - - // Interrupt flags - case 0xFF0F: - ret = REG_FF0F; - 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; - - // Bios control register. Not sure if it is readable - case 0xFF50: - ret = 0xFF; - break; - - // interrupt control register - case 0xFFFF: - ret = REG_FFFF; - break; - - default: - ret = 0xFF; - break; - - } - return ret; - } - - public void Write_Registers(int addr, byte value) - { - switch (addr) - { - // select input - case 0xFF00: - input_register = (byte)(0xC0 | (value & 0x3F)); // top 2 bits always 1 - break; - - // Serial data port - case 0xFF01: - serial_data_out = value; - serial_data_in = serial_data_out; - break; - - // Serial port control - case 0xFF02: - serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1 - break; - - // Timer Registers - case 0xFF04: - case 0xFF05: - case 0xFF06: - case 0xFF07: - timer.WriteReg(addr, value); - break; - - // Interrupt flags - case 0xFF0F: - REG_FF0F = (byte)(0xE0 | value); - - // check if enabling any of the bits triggered an IRQ - for (int i = 0; i < 5; i++) - { - if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) - { - cpu.FlagI = true; - } - } - - // if no bits are in common between flags and enables, de-assert the IRQ - if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } - - 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; - - // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs - case 0xFF50: - //Console.WriteLine(value); - if (GB_bios_register != 1) - { - GB_bios_register = value; - } - break; - - // interrupt control register - case 0xFFFF: - REG_FFFF = value; - enable_VBL = REG_FFFF.Bit(0); - enable_STAT = REG_FFFF.Bit(1); - enable_TIMO = REG_FFFF.Bit(2); - enable_SER = REG_FFFF.Bit(3); - enable_PRS = REG_FFFF.Bit(4); - - // check if enabling any of the bits triggered an IRQ - for (int i = 0; i < 5; i++) - { - if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) - { - cpu.FlagI = true; - } - } - - // if no bits are in common between flags and enables, de-assert the IRQ - if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } - - break; - } - } - - public void Register_Reset() - { - input_register = 0xCF; // not reading any input - serial_control = 0x7E; - } - } -} diff --git a/Mappers/MapperBase.cs b/Mappers/MapperBase.cs deleted file mode 100644 index f7aeb7ddca..0000000000 --- a/Mappers/MapperBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using BizHawk.Common; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public class MapperBase - { - public GBHawk 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() - { - } - } -} diff --git a/Mappers/Mapper_Camera.cs b/Mappers/Mapper_Camera.cs deleted file mode 100644 index e247725259..0000000000 --- a/Mappers/Mapper_Camera.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperCamera : 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 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/Mappers/Mapper_Default.cs b/Mappers/Mapper_Default.cs deleted file mode 100644 index 609a696040..0000000000 --- a/Mappers/Mapper_Default.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // 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 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/Mappers/Mapper_HuC1.cs b/Mappers/Mapper_HuC1.cs deleted file mode 100644 index 0e33db1474..0000000000 --- a/Mappers/Mapper_HuC1.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperHuC1 : 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 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/Mappers/Mapper_HuC3.cs b/Mappers/Mapper_HuC3.cs deleted file mode 100644 index ff0e5cc6d1..0000000000 --- a/Mappers/Mapper_HuC3.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperHuC3 : 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 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/Mappers/Mapper_MBC1.cs b/Mappers/Mapper_MBC1.cs deleted file mode 100644 index 2a8bed26be..0000000000 --- a/Mappers/Mapper_MBC1.cs +++ /dev/null @@ -1,130 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // MBC1 with bank switching and RAM - public class MapperMBC1 : MapperBase - { - public int ROM_bank; - public int RAM_bank; - public bool RAM_enable; - public bool sel_mode; - - public override void Initialize() - { - ROM_bank = 1; - RAM_bank = 0; - RAM_enable = false; - sel_mode = false; - } - - public override byte ReadMemory(ushort addr) - { - if (addr < 0x4000) - { - // lowest bank is fixed - return Core._rom[addr]; - } - else if (addr < 0x8000) - { - return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; - } - else - { - if (Core.cart_RAM != null) - { - if (RAM_enable) - { - return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; - } - else - { - return 0; - } - - } - else - { - return 0; - } - } - } - - public override byte PeekMemory(ushort addr) - { - return ReadMemory(addr); - } - - public override void WriteMemory(ushort addr, byte value) - { - if (addr < 0x8000) - { - if (addr < 0x2000) - { - RAM_enable = ((value & 0xA) == 0xA) ? true : false; - } - else if (addr < 0x4000) - { - value &= 0x1F; - - // writing zero gets translated to 1 - if (value == 0) { value = 1; } - - ROM_bank &= 0xE0; - ROM_bank |= value; - } - else if (addr < 0x6000) - { - if (sel_mode) - { - RAM_bank = value & 0x3; - } - else - { - ROM_bank &= 0x1F; - ROM_bank |= ((value & 3) << 5); - } - } - else - { - sel_mode = (value & 1) > 0; - - if (sel_mode) - { - ROM_bank &= 0x1F; - } - else - { - RAM_bank = 0; - } - } - } - else - { - if (Core.cart_RAM != null) - { - if (RAM_enable) - { - Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; - } - - } - } - } - - public override void PokeMemory(ushort addr, byte value) - { - WriteMemory(addr, value); - } - - public override void SyncState(Serializer ser) - { - ser.Sync("ROM_Bank", ref ROM_bank); - ser.Sync("RAM_Bank", ref RAM_bank); - ser.Sync("RAM_enable", ref RAM_enable); - ser.Sync("sel_mode", ref sel_mode); - } - } -} diff --git a/Mappers/Mapper_MBC2.cs b/Mappers/Mapper_MBC2.cs deleted file mode 100644 index 3130aaa469..0000000000 --- a/Mappers/Mapper_MBC2.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMBC2 : 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 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/Mappers/Mapper_MBC3.cs b/Mappers/Mapper_MBC3.cs deleted file mode 100644 index 961f1769e4..0000000000 --- a/Mappers/Mapper_MBC3.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMBC3 : 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 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/Mappers/Mapper_MBC5.cs b/Mappers/Mapper_MBC5.cs deleted file mode 100644 index 312c9dc108..0000000000 --- a/Mappers/Mapper_MBC5.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMBC5 : 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 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/Mappers/Mapper_MBC6.cs b/Mappers/Mapper_MBC6.cs deleted file mode 100644 index 7f9624adb9..0000000000 --- a/Mappers/Mapper_MBC6.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMBC6 : 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 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/Mappers/Mapper_MBC7.cs b/Mappers/Mapper_MBC7.cs deleted file mode 100644 index 40c9513b8b..0000000000 --- a/Mappers/Mapper_MBC7.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMBC7 : 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 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/Mappers/Mapper_MMM01.cs b/Mappers/Mapper_MMM01.cs deleted file mode 100644 index 86c07e20ca..0000000000 --- a/Mappers/Mapper_MMM01.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperMMM01 : 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 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/Mappers/Mapper_TAMA5.cs b/Mappers/Mapper_TAMA5.cs deleted file mode 100644 index eed5909336..0000000000 --- a/Mappers/Mapper_TAMA5.cs +++ /dev/null @@ -1,59 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Default mapper with no bank switching - public class MapperTAMA5 : 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 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/Mappers/ReadMe.txt b/Mappers/ReadMe.txt deleted file mode 100644 index 0c94c8272c..0000000000 --- a/Mappers/ReadMe.txt +++ /dev/null @@ -1,3 +0,0 @@ -TODO: -Official Mappers -Unofficial Mappers diff --git a/MemoryMap.cs b/MemoryMap.cs deleted file mode 100644 index 8357ebe073..0000000000 --- a/MemoryMap.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; - -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; - - -/* - $FFFF Interrupt Enable Flag - $FF80-$FFFE Zero Page - 127 bytes - $FF00-$FF7F Hardware I/O Registers - $FEA0-$FEFF Unusable Memory - $FE00-$FE9F OAM - Object Attribute Memory - $E000-$FDFF Echo RAM - Reserved, Do Not Use - $D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only) - $C000-$CFFF Internal RAM - Bank 0 (fixed) - $A000-$BFFF Cartridge RAM (If Available) - $9C00-$9FFF BG Map Data 2 - $9800-$9BFF BG Map Data 1 - $8000-$97FF Character RAM - $4000-$7FFF Cartridge ROM - Switchable Banks 1-xx - $0150-$3FFF Cartridge ROM - Bank 0 (fixed) - $0100-$014F Cartridge Header Area - $0000-$00FF Restart and Interrupt Vectors -*/ - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public partial class GBHawk - { - public byte ReadMemory(ushort addr) - { - MemoryCallbacks.CallReads(addr); - - if (addr < 0x100) - { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) - { - return _bios[addr]; // Return BIOS - } - else - { - return mapper.ReadMemory(addr); - } - } - else if (addr < 0x8000) - { - return mapper.ReadMemory(addr); - } - else if (addr < 0x9800) - { - return CHR_RAM[addr - 0x8000]; - } - else if (addr < 0x9C00) - { - return BG_map_1[addr - 0x9800]; - } - else if (addr < 0xA000) - { - return BG_map_2[addr - 0x9C00]; - } - else if (addr < 0xC000) - { - return mapper.ReadMemory(addr); - } - else if (addr < 0xE000) - { - return RAM[addr - 0xC000]; - } - else if (addr < 0xFE00) - { - return RAM[addr - 0xE000]; - } - else if (addr < 0xFEA0 && ppu.OAM_access) - { - return OAM[addr - 0xFE00]; - } - else if (addr < 0xFF00) - { - // unmapped memory, returns 0xFF - return 0xFF; - } - else if (addr < 0xFF80) - { - return Read_Registers(addr); - } - else if (addr < 0xFFFF) - { - return ZP_RAM[addr - 0xFF80]; - } - else - { - return Read_Registers(addr); - } - - } - - public void WriteMemory(ushort addr, byte value) - { - MemoryCallbacks.CallWrites(addr); - - if (addr < 0x100) - { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) - { - // Can't write to BIOS region - } - else - { - mapper.WriteMemory(addr, value); - } - } - else if (addr < 0x8000) - { - mapper.WriteMemory(addr, value); - } - else if (addr < 0x9800) - { - CHR_RAM[addr - 0x8000] = value; - } - else if (addr < 0x9C00) - { - BG_map_1[addr - 0x9800] = value; - } - else if (addr < 0xA000) - { - BG_map_2[addr - 0x9C00] = value; - } - else if (addr < 0xC000) - { - mapper.WriteMemory(addr, value); - } - else if (addr < 0xE000) - { - RAM[addr - 0xC000] = value; - } - else if (addr < 0xFE00) - { - RAM[addr - 0xE000] = value; - } - else if (addr < 0xFEA0 && ppu.OAM_access) - { - OAM[addr - 0xFE00] = value; - } - else if (addr < 0xFF00) - { - // unmapped, writing has no effect - } - else if (addr < 0xFF80) - { - Write_Registers(addr, value); - } - else if (addr < 0xFFFF) - { - ZP_RAM[addr - 0xFF80] = value; - } - else - { - Write_Registers(addr, value); - } - } - } -} diff --git a/PPU.cs b/PPU.cs deleted file mode 100644 index 9170cf1128..0000000000 --- a/PPU.cs +++ /dev/null @@ -1,922 +0,0 @@ -using System; -using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - public class PPU - { - public GBHawk Core { get; set; } - - //public byte BGP_l; - - // register variables - public byte LCDC; - public byte STAT; - public byte scroll_y; - public byte scroll_x; - public byte LY; - 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; - public bool hbl_set_once; - // OAM scan - public bool OAM_access; - public int OAM_scan_index; - public int SL_sprites_index; - public int[] SL_sprites = new int[40]; - public int write_sprite; - // render - public bool VRAM_access; - 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 int temp_fetch; - public int tile_inc; - public bool pre_render; - public byte[] tile_data = new byte[2]; - public byte[] tile_data_latch = new byte[2]; - 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 sprite_fetch_index; - public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr) - public int index_used; - public int sprite_ordered_index; - public int bottom_index; - - public byte ReadReg(int addr) - { - byte ret = 0; - - switch (addr) - { - case 0xFF40: ret = LCDC; break; // LCDC - case 0xFF41: ret = STAT; break; // STAT - case 0xFF42: ret = scroll_y; break; // SCY - case 0xFF43: ret = scroll_x; break; // SCX - case 0xFF44: ret = LY; break; // LY - case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: /*ret = DMA_addr; */ break; // DMA (not readable?) - case 0xFF47: ret = BGP; break; // BGP - case 0xFF48: ret = obj_pal_0; break; // OBP0 - case 0xFF49: ret = obj_pal_1; break; // OBP1 - case 0xFF4A: ret = window_y; break; // WY - case 0xFF4B: ret = window_x; break; // WX - } - - return ret; - } - - public void WriteReg(int addr, byte value) - { - switch (addr) - { - case 0xFF40: // LCDC - LCDC = value; - break; - case 0xFF41: // STAT - STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); - break; - case 0xFF42: // SCY - scroll_y = value; - break; - case 0xFF43: // SCX - scroll_x = value; - // calculate the column number of the tile to start with - x_tile = (int)Math.Floor((float)(scroll_x) / 8); - break; - case 0xFF44: // LY - LY = 0; /*reset*/ - break; - case 0xFF45: // LYC - LYC = value; - if (LY != LYC) { STAT &= 0xFB; } - break; - case 0xFF46: // DMA - DMA_addr = value; - DMA_start = true; - DMA_clock = 0; - DMA_inc = 0; - break; - case 0xFF47: // BGP - BGP = value; - break; - case 0xFF48: // OBP0 - obj_pal_0 = value; - break; - case 0xFF49: // OBP1 - obj_pal_1 = value; - break; - case 0xFF4A: // WY - window_y = value; - break; - case 0xFF4B: // WX - window_x = value; - break; - } - } - - public void tick() - { - // tick DMA - if (DMA_start) - { - if (DMA_clock >= 4) - { - OAM_access = false; - if ((DMA_clock % 4) == 1) - { - // the cpu can't access memory during this time, but we still need the ppu to be able to. - DMA_start = false; - DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); - DMA_start = true; - } - else if ((DMA_clock % 4) == 3) - { - if ((DMA_inc % 4) == 3) - { - Core.OAM[DMA_inc] = DMA_byte; - } - else - { - Core.OAM[DMA_inc] = DMA_byte; - } - - if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } - } - } - - DMA_clock++; - - if (DMA_clock==648) - { - DMA_start = false; - OAM_access = true; - } - } - - // the ppu only does anything if it is turned on via bit 7 of LCDC - if (LCDC.Bit(7)) - { - // exit vblank if LCD went from off to on - if (LCD_was_off) - { - //VBL_INT = false; - Core.in_vblank = false; - LCD_was_off = false; - - // we exit vblank into mode 0 for 4 cycles - // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 - STAT &= 0xFC; - } - - // the VBL stat is continuously asserted - if ((LY >= 144)) - { - if (STAT.Bit(4)) - { - if ((cycle >= 4) && (LY == 144)) - { - VBL_INT = true; - } - else if (LY > 144) - { - VBL_INT = true; - } - } - - if ((cycle == 4) && (LY == 144)) { - - HBL_INT = false; - - // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled - STAT &= 0xFC; - STAT |= 0x01; - - if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x01; - } - - if ((LY >= 144) && (cycle == 4)) - { - // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if ((LY == 153) && (cycle == 8)) - { - LY = 0; - LY_inc = 0; - Core.cpu.LY = LY; - } - } - - if (!Core.in_vblank) - { - if (cycle == 4) - { - // here mode 2 will be set to true and interrupts fired if enabled - STAT &= 0xFC; - STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } - - HBL_INT = false; - } - - if (cycle >= 4 && cycle < 84) - { - // here OAM scanning is performed - OAM_scan(cycle - 4); - } - else if (cycle >= 84 && LY < 144) - { - // render the screen and handle hblank - render(cycle - 84); - } - } - - - if ((LY_inc == 0)) - { - if (cycle == 12) - { - LYC_INT = false; - STAT &= 0xFB; - - // Special case of LY = LYC - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - - // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if (cycle == 92) { OAM_INT = false; } - } - - // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) - { - LYC_INT = false; - STAT &= 0xFB; - - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - } - - cycle++; - - if (cycle==456) - { - cycle = 0; - LY+=LY_inc; - - if (LY==0 && LY_inc == 0) - { - LY_inc = 1; - Core.in_vblank = false; - VBL_INT = false; - } - - Core.cpu.LY = LY; - - if (LY==144) - { - Core.in_vblank = true; - } - } - } - else - { - // screen disable sets STAT as though it were vblank, but there is no Stat IRQ asserted - STAT &= 0xFC; - STAT |= 0x01; - - VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; - - Core.in_vblank = true; - - LCD_was_off = true; - - LY = 0; - Core.cpu.LY = LY; - - cycle = 0; - } - - // assert the STAT IRQ line if the line went from zero to 1 - stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; - - if (stat_line && !stat_line_old) - { - if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x02; - } - - stat_line_old = stat_line; - - // process latch delays - //latch_delay(); - - } - - // might be needed, not sure yet - public void latch_delay() - { - //BGP_l = BGP; - } - - public void OAM_scan(int OAM_cycle) - { - // we are now in STAT mode 2 - // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? - if (OAM_cycle == 0) - { - OAM_access = false; - OAM_scan_index = 0; - SL_sprites_index = 0; - write_sprite = 0; - } - - // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw - // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close - if (OAM_cycle < 10) - { - // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) - SL_sprites[OAM_cycle * 4] = 0; - SL_sprites[OAM_cycle * 4 + 1] = 0; - SL_sprites[OAM_cycle * 4 + 2] = 0; - SL_sprites[OAM_cycle * 4 + 3] = 0; - } - else - { - if (write_sprite == 0) - { - if (OAM_scan_index < 40) - { - // (sprite Y - 16) equals LY, we have a sprite - if ((Core.OAM[OAM_scan_index * 4] - 16) <= LY && - ((Core.OAM[OAM_scan_index * 4] - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) - { - // always pick the first 10 in range sprites - if (SL_sprites_index < 10) - { - SL_sprites[SL_sprites_index * 4] = Core.OAM[OAM_scan_index * 4]; - - write_sprite = 1; - } - else - { - // if we already have 10 sprites, there's nothing to do, increment the index - OAM_scan_index++; - } - } - else - { - OAM_scan_index++; - } - } - } - else - { - SL_sprites[SL_sprites_index * 4 + write_sprite] = Core.OAM[OAM_scan_index * 4 + write_sprite]; - write_sprite++; - - if (write_sprite == 4) - { - write_sprite = 0; - SL_sprites_index++; - OAM_scan_index++; - } - } - } - } - - public void render(int render_cycle) - { - // we are now in STAT mode 3 - // NOTE: presumably the first necessary sprite is fetched at sprite evaulation - // i.e. just keeping track of the lowest x-value sprite - if (render_cycle == 0) - { - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; - - OAM_access = false; - VRAM_access = false; - OAM_scan_index = 0; - read_case = 0; - internal_cycle = 0; - pre_render = true; - tile_inc = 0; - pixel_counter = 0; - sl_use_index = 0; - index_used = 0; - bottom_index = 0; - sprite_ordered_index = 0; - fetch_sprite = false; - no_sprites = false; - - // calculate the row number of the tiles to be fetched - y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; - - if (SL_sprites_index == 0) - { - no_sprites = true; - } - } - - if (!pre_render && !fetch_sprite) - { - // start by fetching all the sprites that need to be fetched - if (!no_sprites) - { - for (int i = 0; i < SL_sprites_index; i++) - { - if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && - (pixel_counter < SL_sprites[i * 4 + 1]) && - !index_used.Bit(i)) - { - fetch_sprite = true; - sprite_fetch_index = 0; - } - } - } - - if (!fetch_sprite) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - pixel = (BGP >> (pixel * 2)) & 3; - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int i = bottom_index; - int s_pixel = 0; - int sprite_attr = 0; - - while (i < sprite_ordered_index) - { - if (SL_sprites_ordered[i * 4] == pixel_counter) - { - bottom_index++; - if (bottom_index == SL_sprites_index) { no_sprites = true; } - } - else if (!have_sprite) - { - // we can use the current sprite, so pick out a pixel for it - int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); - - t_index = 7 - t_index; - - sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); - sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); - - s_pixel = sprite_data[0] + sprite_data[1]; - sprite_attr = SL_sprites_ordered[i * 4 + 3]; - - // pixel color of 0 is transparent, so if this is the case we dont have a pixel - if (s_pixel != 0) - { - have_sprite = true; - } - } - i++; - } - - if (have_sprite) - { - bool use_sprite = false; - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - if (s_pixel != 0) { use_sprite = true; } - } - else if (pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - } - - if (use_sprite) - { - if (sprite_attr.Bit(4)) - { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - Core._vidbuffer[LY * 160 + pixel_counter] = (int)GBHawk.color_palette[pixel]; - pixel_counter++; - - if (pixel_counter == 160) - { - read_case = 8; - hbl_set_once = true; - } - } - render_counter++; - } - } - - if (!fetch_sprite) - { - if (latch_new_data) - { - latch_new_data = false; - tile_data_latch[0] = tile_data[0]; - tile_data_latch[1] = tile_data[1]; - } - - switch (read_case) - { - case 0: // read a background tile - if ((internal_cycle % 2) == 0) - { - - temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; - tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - - } - else - { - if (!pre_render) - { - tile_inc++; - } - read_case = 1; - } - break; - - case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7)) - { - tile_byte -= 256; - } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; - } - - } - else - { - read_case = 2; - } - break; - - case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - // if LCDC somehow changed between the two reads, make sure we have a positive number - if (tile_byte < 0) - { - tile_byte += 256; - } - - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7) && tile_byte > 0) - { - tile_byte -= 256; - } - - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - - } - else - { - if (pre_render) - { - // here we set up rendering - pre_render = false; - render_offset = scroll_x % 8; - render_counter = -1; - latch_counter = 0; - read_case = 0; - } - else - { - read_case = 3; - } - - } - break; - - case 3: // read from sprite data - if ((internal_cycle % 2) == 0) - { - // nothing to do if not fetching - } - else - { - read_case = 0; - latch_new_data = true; - } - break; - - case 4: // read from window data - break; - - case 6: // read from tile graphics (for the window) - break; - - case 7: // read from tile graphics (for the window) - break; - - case 8: // done reading, we are now in phase 0 - - OAM_access = true; - VRAM_access = true; - - STAT &= 0xFC; - STAT |= 0x00; - pre_render = true; - if (hbl_set_once) - { - if (STAT.Bit(3)) { HBL_INT = true; } - hbl_set_once = false; - } - - break; - } - - internal_cycle++; - } - - if (fetch_sprite) - { - if (sprite_fetch_index < SL_sprites_index) - { - if (pixel_counter != 0) { - if ((pixel_counter == (SL_sprites[sprite_fetch_index * 4 + 1] - 8)) && - //(pixel_counter < SL_sprites[sprite_fetch_index * 4 + 1]) && - !index_used.Bit(sprite_fetch_index)) - { - sl_use_index = sprite_fetch_index; - process_sprite(); - SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[sprite_fetch_index * 4 + 1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; - SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[sprite_fetch_index * 4 + 3]; - sprite_ordered_index++; - index_used |= (1 << sl_use_index); - } - sprite_fetch_index++; - if (sprite_fetch_index == SL_sprites_index) { fetch_sprite = false; } - } - else - { - // whan pixel counter is 0, we want to scan all the points before 0 as well - // certainly non-physical but good enough for now - for (int j = -7; j < 1; j++) - { - for (int i = 0; i < SL_sprites_index; i++) - { - if ((j == (SL_sprites[i * 4 + 1] - 8)) && - !index_used.Bit(i)) - { - sl_use_index = i; - process_sprite(); - SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[i * 4 + 1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; - SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[i * 4 + 3]; - sprite_ordered_index++; - index_used |= (1 << sl_use_index); - } - } - } - fetch_sprite = false; - } - } - } - } - - public void Reset() - { - LCDC = 0; - STAT = 0x80; - scroll_y = 0; - scroll_x = 0; - LY = 0; - LYC = 0; - DMA_addr = 0; - BGP = 0; - obj_pal_0 = 0; - obj_pal_1 = 0; - window_y = 0; - window_x = 0; - LY_inc = 1; - - cycle = 0; - LYC_INT = false; - HBL_INT = false; - VBL_INT = false; - OAM_INT = false; - - stat_line = false; - stat_line_old = false; - } - - public void process_sprite() - { - int y; - - if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 15 - y; - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 7 - y; - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } - else - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } - - if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) - { - int b0, b1, b2, b3, b4, b5, b6, b7 = 0; - for (int i = 0; i < 2; i++) - { - b0 = (sprite_sel[i] & 0x01) << 7; - b1 = (sprite_sel[i] & 0x02) << 5; - b2 = (sprite_sel[i] & 0x04) << 3; - b3 = (sprite_sel[i] & 0x08) << 1; - b4 = (sprite_sel[i] & 0x10) >> 1; - b5 = (sprite_sel[i] & 0x20) >> 3; - b6 = (sprite_sel[i] & 0x40) >> 5; - b7 = (sprite_sel[i] & 0x80) >> 7; - - sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); - } - } - } - - public void SyncState(Serializer ser) - { - ser.Sync("LCDC", ref LCDC); - ser.Sync("STAT", ref STAT); - ser.Sync("scroll_y", ref scroll_y); - ser.Sync("scroll_x", ref scroll_x); - ser.Sync("LY", ref LY); - ser.Sync("LYinc", ref LY_inc); - ser.Sync("LYC", ref LYC); - ser.Sync("DMA_addr", ref DMA_addr); - ser.Sync("BGP", ref BGP); - ser.Sync("obj_pal_0", ref obj_pal_0); - ser.Sync("obj_pal_1", ref obj_pal_1); - ser.Sync("window_y", ref window_y); - ser.Sync("window_x", ref window_x); - ser.Sync("DMA_start", ref DMA_start); - ser.Sync("DMA_clock", ref DMA_clock); - ser.Sync("DMA_inc", ref DMA_inc); - ser.Sync("DMA_byte", ref DMA_byte); - - ser.Sync("LYC_INT", ref LYC_INT); - ser.Sync("HBL_INT", ref HBL_INT); - ser.Sync("VBL_INT", ref VBL_INT); - ser.Sync("OAM_INT", ref OAM_INT); - ser.Sync("stat_line", ref stat_line); - ser.Sync("stat_line_old", ref stat_line_old); - ser.Sync("hbl_set_once", ref hbl_set_once); - ser.Sync("LCD_was_off", ref LCD_was_off); - ser.Sync("OAM_access", ref OAM_access); - ser.Sync("OAM_scan_index", ref OAM_scan_index); - ser.Sync("SL_sprites_index", ref SL_sprites_index); - ser.Sync("SL_sprites", ref SL_sprites, false); - ser.Sync("write_sprite", ref write_sprite); - - ser.Sync("VRAM_access", ref VRAM_access); - ser.Sync("read_case", ref read_case); - ser.Sync("internal_cycle", ref internal_cycle); - ser.Sync("y_tile", ref y_tile); - ser.Sync("y_scroll_offset", ref y_scroll_offset); - ser.Sync("x_tile", ref x_tile); - ser.Sync("x_scroll_offset", ref x_scroll_offset); - ser.Sync("tile_byte", ref tile_byte); - ser.Sync("sprite_fetch_cycles", ref sprite_fetch_cycles); - ser.Sync("fetch_sprite", ref fetch_sprite); - ser.Sync("temp_fetch", ref temp_fetch); - ser.Sync("tile_inc", ref tile_inc); - ser.Sync("pre_render", ref pre_render); - ser.Sync("tile_data", ref tile_data, false); - ser.Sync("tile_data_latch", ref tile_data_latch, false); - ser.Sync("latch_counter", ref latch_counter); - ser.Sync("latch_new_data", ref latch_new_data); - ser.Sync("render_counter", ref render_counter); - ser.Sync("render_offset", ref render_offset); - ser.Sync("pixel_counter", ref pixel_counter); - ser.Sync("pixel", ref pixel); - ser.Sync("sprite_data", ref sprite_data, false); - ser.Sync("sl_use_index", ref sl_use_index); - ser.Sync("sprite_sel", ref sprite_sel, false); - ser.Sync("no_sprites", ref no_sprites); - ser.Sync("sprite_fetch_index", ref sprite_fetch_index); - ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); - ser.Sync("index_used", ref index_used); - ser.Sync("sprite_ordered_index", ref sprite_ordered_index); - ser.Sync("bottom_index", ref bottom_index); - - } - } -} diff --git a/Timer.cs b/Timer.cs deleted file mode 100644 index b243081d70..0000000000 --- a/Timer.cs +++ /dev/null @@ -1,175 +0,0 @@ -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using System; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk -{ - // Timer Emulation - public class Timer - { - public GBHawk Core { get; set; } - - public ushort divider_reg; - public byte timer_reload; - public byte timer; - public byte timer_old; - public byte timer_control; - public byte pending_reload; - public byte write_ignore; - public bool old_state; - public bool state; - public bool reload_block; - public bool TMA_coincidence; - - public byte ReadReg(int addr) - { - byte ret = 0; - - switch (addr) - { - case 0xFF04: ret = (byte)(divider_reg >> 8); break; // DIV register - case 0xFF05: ret = timer; break; // TIMA (Timer Counter) - case 0xFF06: ret = timer_reload; break; // TMA (Timer Modulo) - case 0xFF07: ret = timer_control; break; // TAC (Timer Control) - } - - return ret; - } - - public void WriteReg(int addr, byte value) - { - switch (addr) - { - // DIV register - case 0xFF04: - divider_reg = 0; - break; - - // TIMA (Timer Counter) - case 0xFF05: - if (write_ignore == 0) - { - timer_old = timer; - timer = value; - reload_block = true; - } - break; - - // TMA (Timer Modulo) - case 0xFF06: - timer_reload = value; - if (TMA_coincidence) - { - timer = timer_reload; - timer_old = timer; - } - break; - - // TAC (Timer Control) - case 0xFF07: - timer_control = (byte)((timer_control & 0xf8) | (value & 0x7)); // only bottom 3 bits function - break; - } - } - - public void tick_1() - { - if (write_ignore > 0) - { - write_ignore--; - if (write_ignore==0) - { - TMA_coincidence = false; - } - } - - if (pending_reload > 0) - { - pending_reload--; - if (pending_reload == 0 && !reload_block) - { - timer = timer_reload; - timer_old = timer; - write_ignore = 4; - TMA_coincidence = true; - - // set interrupts - if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x04; - } - } - } - - public void tick_2() - { - divider_reg++; - - // pick a bit to test based on the current value of timer control - switch (timer_control & 3) - { - case 0: - state = divider_reg.Bit(9); - break; - case 1: - state = divider_reg.Bit(3); - break; - case 2: - state = divider_reg.Bit(5); - break; - case 3: - state = divider_reg.Bit(7); - break; - } - - // And it with the state of the timer on/off bit - state &= timer_control.Bit(2); - - // this procedure allows several glitchy timer ticks, since it only measures falling edge of the state - // so things like turning the timer off and resetting the divider will tick the timer - if (old_state && !state) - { - timer_old = timer; - timer++; - - // if overflow, set the interrupt flag and reload the timer (4 clocks later) - if (timer < timer_old) - { - pending_reload = 4; - reload_block = false; - } - } - - old_state = state; - } - - public void Reset() - { - divider_reg = 0; - timer_reload = 0; - timer = 0; - timer_old = 0; - timer_control = 0xF8; - pending_reload = 0; - write_ignore = 0; - old_state = false; - state = false; - reload_block = false; - TMA_coincidence = false; - } - - public void SyncState(Serializer ser) - { - ser.Sync("divider_reg", ref divider_reg); - ser.Sync("timer_reload", ref timer_reload); - ser.Sync("timer", ref timer); - ser.Sync("timer_old", ref timer_old); - ser.Sync("timer_control", ref timer_control); - ser.Sync("pending_reload", ref pending_reload); - ser.Sync("write_ignore", ref write_ignore); - ser.Sync("old_state", ref old_state); - ser.Sync("state", ref state); - ser.Sync("reload_block", ref reload_block); - ser.Sync("TMA_coincidence", ref TMA_coincidence); - } - } -} \ No newline at end of file From 4cf9ef3f3a8035d0e4f48394004c06bd9df24e20 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 10 Nov 2017 19:07:36 -0500 Subject: [PATCH 14/28] GBHawk: MBC1 updates and multi-cart support --- .../BizHawk.Emulation.Cores.csproj | 9 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 10 ++ .../Nintendo/GBHawk/Mappers/Mapper_MBC1.cs | 39 +++-- .../GBHawk/Mappers/Mapper_MBC1_Multi.cs | 151 ++++++++++++++++++ .../Consoles/Nintendo/GBHawk/PPU.cs | 6 +- 5 files changed, 200 insertions(+), 15 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b5a847cb21..e2ea538512 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -581,7 +581,7 @@ VBANext.cs - + @@ -598,6 +598,7 @@ + @@ -780,7 +781,7 @@ - + @@ -1189,7 +1190,7 @@ - + @@ -1358,4 +1359,4 @@ --> - + \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 9015a4fe1d..471b2a6b09 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -191,6 +191,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } + // special case for multi cart mappers + if ((_rom.HashMD5(0,_rom.Length) == "97122B9B183AAB4079C8D36A4CE6E9C1") || + (_rom.HashMD5(0, _rom.Length) == "9FB9C42CF52DCFDCFBAD5E61AE1B5777") || + (_rom.HashMD5(0, _rom.Length) == "CF1F58AB72112716D3C615A553B2F481") + ) + { + Console.WriteLine("Using Multi-Cart Mapper"); + mapper = new MapperMBC1Multi(); + } + Console.Write("Mapper: "); Console.WriteLine(header[0x47]); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs index 1224b9b954..2bdbff79f6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs @@ -11,6 +11,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int RAM_bank; public bool RAM_enable; public bool sel_mode; + public int ROM_mask; + public int RAM_mask; public override void Initialize() { @@ -18,14 +20,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk RAM_bank = 0; RAM_enable = false; sel_mode = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } } public override byte ReadMemory(ushort addr) { if (addr < 0x4000) { - // lowest bank is fixed - return Core._rom[addr]; + // lowest bank is fixed, but is still effected by mode + if (sel_mode) + { + return Core._rom[(ROM_bank & 0x60) * 0x4000 + addr]; + } + else + { + return Core._rom[addr]; + } } else if (addr < 0x8000) { @@ -35,15 +51,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (Core.cart_RAM != null) { - if (RAM_enable) + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) { return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; } else { - return 0; + return 0xFF; } - + } else { @@ -74,17 +90,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ROM_bank &= 0xE0; ROM_bank |= value; + ROM_bank &= ROM_mask; } else if (addr < 0x6000) { - if (sel_mode) + if (sel_mode && Core.cart_RAM != null) { - RAM_bank = value & 0x3; + RAM_bank = value & 3; + RAM_bank &= RAM_mask; } else { ROM_bank &= 0x1F; ROM_bank |= ((value & 3) << 5); + ROM_bank &= ROM_mask; } } else @@ -94,6 +113,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (sel_mode) { ROM_bank &= 0x1F; + ROM_bank &= ROM_mask; } else { @@ -105,11 +125,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (Core.cart_RAM != null) { - if (RAM_enable) + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) { Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; } - } } } @@ -122,7 +141,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void SyncState(Serializer ser) { ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); ser.Sync("RAM_enable", ref RAM_enable); ser.Sync("sel_mode", ref sel_mode); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs new file mode 100644 index 0000000000..d72112aa40 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs @@ -0,0 +1,151 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC1Multi : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + public int ROM_mask; + public int RAM_mask; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + ROM_mask = (Core._rom.Length / 0x4000 * 2) - 1; // due to how mapping workd, we want a 1 bit higher mask + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed, but is still effected by mode + if (sel_mode) + { + return Core._rom[((ROM_bank & 0x60) >> 1) * 0x4000 + addr]; + } + else + { + return Core._rom[addr]; + } + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + (((ROM_bank & 0x60) >> 1) | (ROM_bank & 0xF)) * 0x4000]; + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x1F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank &= 0xE0; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + if (sel_mode && Core.cart_RAM != null) + { + RAM_bank = value & 3; + RAM_bank &= RAM_mask; + } + else + { + ROM_bank &= 0x1F; + ROM_bank |= ((value & 3) << 5); + ROM_bank &= ROM_mask; + } + } + else + { + sel_mode = (value & 1) > 0; + + if (sel_mode && Core.cart_RAM != null) + { + ROM_bank &= 0x1F; + ROM_bank &= ROM_mask; + } + else + { + RAM_bank = 0; + } + } + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("sel_mode", ref sel_mode); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 9f1742c09e..8bc8cee50d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -170,6 +170,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (DMA_clock >= 4) { OAM_access_read = false; + OAM_access_write = false; if ((DMA_clock % 4) == 1) { // the cpu can't access memory during this time, but we still need the ppu to be able to. @@ -194,10 +195,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk DMA_clock++; - if (DMA_clock==648) + if (DMA_clock == 648) { - DMA_start = false; OAM_access_read = true; + OAM_access_write = true; + DMA_start = false; } } From c18298c8a5915abc5abefb3fb2420259e464cead Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 11 Nov 2017 09:07:58 -0500 Subject: [PATCH 15/28] GameBoy: Disable BIOS in Gambatte. Use GBHawk for GameBoy, Gambatte for CGB. --- BizHawk.Client.Common/RomLoader.cs | 7 ++++--- .../Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs | 2 +- .../Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs | 5 ----- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 10 +--------- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index d89deccc64..6a9c8b68d7 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -936,11 +936,12 @@ namespace BizHawk.Client.Common break; case "GB": + core = CoreInventory.Instance["GB", "GBHawk"]; + break; case "GBC": if (!Global.Config.GB_AsSGB) - { - core = CoreInventory.Instance["GB", "GBHawk"]; - //core = CoreInventory.Instance["GB", "Gambatte"]; + { + core = CoreInventory.Instance["GBC", "Gambatte"]; } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs index 2bdbff79f6..c61133bdcd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs @@ -110,7 +110,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { sel_mode = (value & 1) > 0; - if (sel_mode) + if (sel_mode && Core.cart_RAM != null) { ROM_bank &= 0x1F; ROM_bank &= ROM_mask; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index fb41292935..7e2ba05432 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -80,11 +80,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public class GambatteSyncSettings { - [DisplayName("Enable BIOS: WARNING: File must exist!")] - [Description("Boots game using system BIOS. Should be used for TASing")] - [DefaultValue(false)] - public bool EnableBIOS { get; set; } - public enum ConsoleModeType { Auto, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index fcfa9a0486..f4e67dae32 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -99,16 +99,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy break; } - if (_syncSettings.EnableBIOS && BiosRom == null) - { - throw new MissingFirmwareException("Boot Rom not found"); - } - // to disable BIOS loading into gambatte, just set bios_length to 0 - if (!_syncSettings.EnableBIOS) - { - bios_length = 0; - } + bios_length = 0; if (_syncSettings.GBACGB) { From ff815dec656512cccf49b497cf3fb7c95488e144 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 14 Nov 2017 11:59:32 -0500 Subject: [PATCH 16/28] GBAHawk start audio --- .../Consoles/Nintendo/GBHawk/Audio.cs | 407 +++++++++++++++--- 1 file changed, 359 insertions(+), 48 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 01a3078e1c..a0d5dfc8ab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -44,6 +44,52 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte[] Wave_RAM = new byte [16]; + struct AUD_Object + { + // channel controls + public byte swp_period; + public bool negate; + public byte shift; + public byte duty; + public byte length; + public byte st_vol; + public bool env_add; + public byte per; + public ushort frq; + public bool trigger; + public bool len_en; + public bool DAC_pow; + public byte vol_code; + public byte clk_shft; + public bool wdth_md; + public byte div_code; + + // channel states + public byte length_counter; + } + + struct CTRL_Object + { + public bool vin_L_en; + public bool vin_R_en; + public byte vol_L; + public byte vol_R; + public bool sq1_L_en; + public bool sq2_L_en; + public bool wave_L_en; + public bool noise_L_en; + public bool sq1_R_en; + public bool sq2_R_en; + public bool wave_R_en; + public bool noise_R_en; + public bool power; + } + + AUD_Object SQ1, SQ2, WAVE, NOISE; + + CTRL_Object AUD_CTRL; + + public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; public byte ReadReg(int addr) { @@ -65,13 +111,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) - case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (sweep) - case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (sweep) - case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (sweep) - case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (sweep) - case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (sweep) - case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (sweep) - case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (sweep) + case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (length) + case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (envelope) + case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (shift) + case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) + case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) + case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) + case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (ctrl) // wave ram table case 0xFF30: @@ -100,50 +146,201 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void WriteReg(int addr, byte value) { - switch (addr) + // while power is on, everything is writable + if (AUD_CTRL.power) { - case 0xFF10: Audio_Regs[NR10] = value; break; // NR10 (sweep) - case 0xFF11: Audio_Regs[NR11] = value; break; // NR11 (sound length / wave pattern duty %) - case 0xFF12: Audio_Regs[NR12] = value; break; // NR12 (envelope) - case 0xFF13: Audio_Regs[NR13] = value; break; // NR13 (freq low) - case 0xFF14: Audio_Regs[NR14] = value; break; // NR14 (freq hi) - case 0xFF16: Audio_Regs[NR21] = value; break; // NR21 (sound length / wave pattern duty %) - case 0xFF17: Audio_Regs[NR22] = value; break; // NR22 (envelope) - case 0xFF18: Audio_Regs[NR23] = value; break; // NR23 (freq low) - case 0xFF19: Audio_Regs[NR24] = value; break; // NR24 (freq hi) - case 0xFF1A: Audio_Regs[NR30] = value; break; // NR30 (on/off) - case 0xFF1B: Audio_Regs[NR31] = value; break; // NR31 (length) - case 0xFF1C: Audio_Regs[NR32] = value; break; // NR32 (level output) - case 0xFF1D: Audio_Regs[NR33] = value; break; // NR33 (freq low) - case 0xFF1E: Audio_Regs[NR34] = value; break; // NR34 (freq hi) - case 0xFF20: Audio_Regs[NR41] = value; break; // NR41 (sweep) - case 0xFF21: Audio_Regs[NR42] = value; break; // NR42 (sweep) - case 0xFF22: Audio_Regs[NR43] = value; break; // NR43 (sweep) - case 0xFF23: Audio_Regs[NR44] = value; break; // NR44 (sweep) - case 0xFF24: Audio_Regs[NR50] = value; break; // NR50 (sweep) - case 0xFF25: Audio_Regs[NR51] = value; break; // NR51 (sweep) - case 0xFF26: Audio_Regs[NR52] = value; break; // NR52 (sweep) + switch (addr) + { + case 0xFF10: // NR10 (sweep) + Audio_Regs[NR10] = value; + SQ1.swp_period = (byte)((value & 0x70) >> 4); + SQ1.negate = (value & 8) > 0; + SQ1.shift = (byte)(value & 7); + break; + case 0xFF11: // NR11 (sound length / wave pattern duty %) + Audio_Regs[NR11] = value; + SQ1.duty = (byte)((value & 0xC0) >> 6); + SQ1.length = (byte)(64 - value & 0x3F); + break; + case 0xFF12: // NR12 (envelope) + Audio_Regs[NR12] = value; + SQ1.st_vol = (byte)((value & 0xF0) >> 4); + SQ1.env_add = (value & 8) > 0; + SQ1.per = (byte)(value & 7); + break; + case 0xFF13: // NR13 (freq low) + Audio_Regs[NR13] = value; + SQ1.frq &= 0x700; + SQ1.frq |= value; + break; + case 0xFF14: // NR14 (freq hi) + Audio_Regs[NR14] = value; + SQ1.trigger = (value & 0x80) > 0; + SQ1.len_en = (value & 0x40) > 0; + SQ1.frq &= 0xFF; + SQ1.frq |= (ushort)((value & 7) << 8); + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + Audio_Regs[NR21] = value; + SQ2.duty = (byte)((value & 0xC0) >> 6); + SQ2.length = (byte)(64 - value & 0x3F); + break; + case 0xFF17: // NR22 (envelope) + Audio_Regs[NR22] = value; + SQ2.st_vol = (byte)((value & 0xF0) >> 4); + SQ2.env_add = (value & 8) > 0; + SQ2.per = (byte)(value & 7); + break; + case 0xFF18: // NR23 (freq low) + Audio_Regs[NR23] = value; + SQ2.frq &= 0x700; + SQ2.frq |= value; + break; + case 0xFF19: // NR24 (freq hi) + Audio_Regs[NR24] = value; + SQ2.trigger = (value & 0x80) > 0; + SQ2.len_en = (value & 0x40) > 0; + SQ2.frq &= 0xFF; + SQ2.frq |= (ushort)((value & 7) << 8); + break; + case 0xFF1A: // NR30 (on/off) + Audio_Regs[NR30] = value; + WAVE.DAC_pow = (value & 0x80) > 0; + break; + case 0xFF1B: // NR31 (length) + Audio_Regs[NR31] = value; + WAVE.length = (byte)(256 - value); + break; + case 0xFF1C: // NR32 (level output) + Audio_Regs[NR32] = value; + WAVE.vol_code = (byte)((value & 0x60) >> 5); + break; + case 0xFF1D: // NR33 (freq low) + Audio_Regs[NR33] = value; + WAVE.frq &= 0x700; + WAVE.frq |= value; + break; + case 0xFF1E: // NR34 (freq hi) + Audio_Regs[NR34] = value; + WAVE.trigger = (value & 0x80) > 0; + WAVE.len_en = (value & 0x40) > 0; + WAVE.frq &= 0xFF; + WAVE.frq |= (ushort)((value & 7) << 8); + break; + case 0xFF20: // NR41 (length) + Audio_Regs[NR41] = value; + NOISE.length = (byte)(64 - value & 0x3F); + break; + case 0xFF21: // NR42 (envelope) + Audio_Regs[NR42] = value; + NOISE.st_vol = (byte)((value & 0xF0) >> 4); + NOISE.env_add = (value & 8) > 0; + NOISE.per = (byte)(value & 7); + break; + case 0xFF22: // NR43 (shift) + Audio_Regs[NR43] = value; + NOISE.clk_shft = (byte)((value & 0xF0) >> 4); + NOISE.wdth_md = (value & 8) > 0; + NOISE.div_code = (byte)(value & 7); + break; + case 0xFF23: // NR44 (trigger) + Audio_Regs[NR44] = value; + WAVE.trigger = (value & 0x80) > 0; + WAVE.len_en = (value & 0x40) > 0; + break; + case 0xFF24: // NR50 (ctrl) + Audio_Regs[NR50] = value; + AUD_CTRL.vin_L_en = (value & 0x80) > 0; + AUD_CTRL.vol_L = (byte)((value & 0x70) >> 4); + AUD_CTRL.vin_R_en = (value & 8) > 0; + AUD_CTRL.vol_R = (byte)(value & 7); + break; + case 0xFF25: // NR51 (ctrl) + Audio_Regs[NR51] = value; + AUD_CTRL.noise_L_en = (value & 0x80) > 0; + AUD_CTRL.wave_L_en = (value & 0x40) > 0; + AUD_CTRL.sq2_L_en = (value & 0x20) > 0; + AUD_CTRL.sq1_L_en = (value & 0x10) > 0; + AUD_CTRL.noise_R_en = (value & 8) > 0; + AUD_CTRL.wave_R_en = (value & 4) > 0; + AUD_CTRL.sq2_R_en = (value & 2) > 0; + AUD_CTRL.sq1_R_en = (value & 1) > 0; + break; + case 0xFF26: // NR52 (ctrl) + Audio_Regs[NR52] &= 0x7F; + Audio_Regs[NR52] |= (byte)(value & 0x80); + AUD_CTRL.power = (value & 0x80) > 0; - // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; + if (!AUD_CTRL.power) + { + power_off(); + } + break; + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + } + } + // when power is off, only length counters and waveRAM are effected by writes + else + { + switch (addr) + { + case 0xFF11: // NR11 (sound length / wave pattern duty %) + SQ1.length = (byte)(64 - value & 0x3F); + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + SQ2.length = (byte)(64 - value & 0x3F); + break; + case 0xFF1B: // NR31 (length) + WAVE.length = (byte)(256 - value); + break; + case 0xFF20: // NR41 (length) + NOISE.length = (byte)(64 - value & 0x3F); + break; + case 0xFF26: // NR52 (ctrl) + Audio_Regs[NR52] &= 0x7F; + Audio_Regs[NR52] |= (byte)(value & 0x80); + AUD_CTRL.power = (value & 0x80) > 0; + break; + + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + } } } @@ -152,11 +349,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } + public void power_off() + { + for (int i = 0; i < 20; i++) + { + Audio_Regs[i] = 0; + } + + sequencer_len = 0; + sequencer_vol = 0; + sequencer_swp = 0; + sequencer_tick = 0; + } public void reset() { Wave_RAM = new byte[16]; Audio_Regs = new byte[21]; + + SQ1 = new AUD_Object(); + SQ2 = new AUD_Object(); + WAVE = new AUD_Object(); + NOISE = new AUD_Object(); + + AUD_CTRL = new CTRL_Object(); } public void SyncState(Serializer ser) @@ -164,6 +380,101 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("Audio_Regs", ref Audio_Regs, false); ser.Sync("Wave_Ram", ref Wave_RAM, false); + // some aspecta of the channel states are not derived from the Regs + ser.Sync("SQ1.length_counter", ref SQ1.length_counter); + ser.Sync("SQ2.length_counter", ref SQ2.length_counter); + ser.Sync("WAVE.length_counter", ref WAVE.length_counter); + ser.Sync("NOISE.length_counter", ref NOISE.length_counter); + + // get derived state + if (ser.IsReader) + { + sync_channels(); + } + + + + } + + public void sync_channels() + { + + SQ1.swp_period = (byte)((Audio_Regs[NR10] & 0x70) >> 4); + SQ1.negate = (Audio_Regs[NR10] & 8) > 0; + SQ1.shift = (byte)(Audio_Regs[NR10] & 7); + + SQ1.duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); + SQ1.length = (byte)(64 - Audio_Regs[NR11] & 0x3F); + + SQ1.st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); + SQ1.env_add = (Audio_Regs[NR12] & 8) > 0; + SQ1.per = (byte)(Audio_Regs[NR12] & 7); + + SQ1.frq &= 0x700; + SQ1.frq |= Audio_Regs[NR13]; + + SQ1.trigger = (Audio_Regs[NR14] & 0x80) > 0; + SQ1.len_en = (Audio_Regs[NR14] & 0x40) > 0; + SQ1.frq &= 0xFF; + SQ1.frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); + + SQ2.duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); + SQ2.length = (byte)(64 - Audio_Regs[NR21] & 0x3F); + + SQ2.st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); + SQ2.env_add = (Audio_Regs[NR22] & 8) > 0; + SQ2.per = (byte)(Audio_Regs[NR22] & 7); + + SQ2.frq &= 0x700; + SQ2.frq |= Audio_Regs[NR23]; + + SQ2.trigger = (Audio_Regs[NR24] & 0x80) > 0; + SQ2.len_en = (Audio_Regs[NR24] & 0x40) > 0; + SQ2.frq &= 0xFF; + SQ2.frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); + + WAVE.DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; + + WAVE.length = (byte)(256 - Audio_Regs[NR31]); + + WAVE.vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); + + WAVE.frq &= 0x700; + WAVE.frq |= Audio_Regs[NR33]; + + WAVE.trigger = (Audio_Regs[NR34] & 0x80) > 0; + WAVE.len_en = (Audio_Regs[NR34] & 0x40) > 0; + WAVE.frq &= 0xFF; + WAVE.frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); + + NOISE.length = (byte)(64 - Audio_Regs[NR41] & 0x3F); + + NOISE.st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); + NOISE.env_add = (Audio_Regs[NR42] & 8) > 0; + NOISE.per = (byte)(Audio_Regs[NR42] & 7); + + NOISE.clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); + NOISE.wdth_md = (Audio_Regs[NR43] & 8) > 0; + NOISE.div_code = (byte)(Audio_Regs[NR43] & 7); + + WAVE.trigger = (Audio_Regs[NR44] & 0x80) > 0; + WAVE.len_en = (Audio_Regs[NR44] & 0x40) > 0; + + AUD_CTRL.vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; + AUD_CTRL.vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); + AUD_CTRL.vin_R_en = (Audio_Regs[NR50] & 8) > 0; + AUD_CTRL.vol_R = (byte)(Audio_Regs[NR50] & 7); + + AUD_CTRL.noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL.wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; + AUD_CTRL.sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; + AUD_CTRL.sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; + AUD_CTRL.noise_R_en = (Audio_Regs[NR51] & 8) > 0; + AUD_CTRL.wave_R_en = (Audio_Regs[NR51] & 4) > 0; + AUD_CTRL.sq2_R_en = (Audio_Regs[NR51] & 2) > 0; + AUD_CTRL.sq1_R_en = (Audio_Regs[NR51] & 1) > 0; + + AUD_CTRL.power = (Audio_Regs[NR51] & 0x80) > 0; } #region audio From 574a78ee8643d4d00ce1f874bebec86b895fa6fd Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 14 Nov 2017 14:43:58 -0500 Subject: [PATCH 17/28] GBHawk: mapper and audio updates --- .../Consoles/Nintendo/GBHawk/Audio.cs | 121 +++++++++++++++++- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 65 +++++----- .../Nintendo/GBHawk/Mappers/Mapper_MBC2.cs | 58 ++++++--- 3 files changed, 197 insertions(+), 47 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index a0d5dfc8ab..dbdaeeeed6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -12,6 +12,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public GBHawk Core { get; set; } + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 0}; + public const int NR10 = 0; public const int NR11 = 1; public const int NR12 = 2; @@ -55,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte st_vol; public bool env_add; public byte per; - public ushort frq; + public int frq; public bool trigger; public bool len_en; public bool DAC_pow; @@ -66,6 +71,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // channel states public byte length_counter; + public byte volume_state; + public int frq_shadow; + public int internal_cntr; + public byte duty_counter; + public bool enable; + + // channel non-states + public int output; } struct CTRL_Object @@ -346,7 +359,113 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void tick() { + // calculate square1's output + if (SQ1.enable) + { + SQ1.internal_cntr++; + if (SQ1.internal_cntr == (2048 - SQ1.frq_shadow) * 4) + { + SQ1.internal_cntr = 0; + SQ1.duty_counter++; + SQ1.duty_counter &= 7; + SQ1.output = DUTY_CYCLES[SQ1.duty * 8 + SQ1.duty_counter]; + SQ1.output *= SQ1.volume_state; + } + } + + // calculate square2's output + if (SQ2.enable) + { + SQ2.internal_cntr++; + if (SQ2.internal_cntr == (2048 - SQ2.frq) * 4) + { + SQ2.internal_cntr = 0; + SQ2.duty_counter++; + SQ2.duty_counter &= 7; + + SQ2.output = DUTY_CYCLES[SQ2.duty * 8 + SQ2.duty_counter]; + SQ2.output *= SQ2.volume_state; + } + } + + // calculate wave output + + // calculate noise output + + + + + + // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) + sequencer_tick++; + + if (sequencer_tick==8192) + { + sequencer_tick = 0; + + sequencer_vol++; sequencer_vol &= 0x7; + sequencer_len++; sequencer_len &= 0x7; + sequencer_swp++; sequencer_swp &= 0x7; + + // clock the lengths + if ((sequencer_len == 1) || (sequencer_len == 3) || (sequencer_len == 5) || (sequencer_len == 7)) + { + if (SQ1.len_en && SQ1.length_counter > 0) { SQ1.length_counter--; if (SQ1.length_counter == 0) { SQ1.enable = false; } } + if (SQ2.len_en && SQ2.length_counter > 0) { SQ2.length_counter--; if (SQ2.length_counter == 0) { SQ2.enable = false; } } + if (WAVE.len_en && WAVE.length_counter > 0) { WAVE.length_counter--; if (WAVE.length_counter == 0) { WAVE.enable = false; } } + if (NOISE.len_en && NOISE.length_counter > 0) { NOISE.length_counter--; if (NOISE.length_counter == 0) { NOISE.enable = false; } } + } + + // clock the sweep + if ((sequencer_swp == 3) || (sequencer_swp == 7)) + { + if (((SQ1.swp_period > 0) || (SQ1.shift > 0)) && (SQ1.swp_period > 0)) + { + int shadow_frq = SQ1.frq_shadow; + shadow_frq = shadow_frq >> SQ1.shift; + if (SQ1.negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1.frq_shadow; + + // disable channel if overflow + if ((uint) shadow_frq > 2047) + { + SQ1.enable = false; + } + else + { + shadow_frq &= 0x7FF; + SQ1.frq = shadow_frq; + SQ1.frq_shadow = shadow_frq; + + // note that we also write back the frequency to the actual register + Audio_Regs[NR13] = (byte)(SQ1.frq & 0xFF); + Audio_Regs[NR14] &= 0xF8; + Audio_Regs[NR14] |= (byte)((SQ1.frq >> 8) & 7); + + // after writing, we repeat the process and do another overflow check + shadow_frq = SQ1.frq_shadow; + shadow_frq = shadow_frq >> SQ1.shift; + if (SQ1.negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1.frq_shadow; + + if ((uint)shadow_frq > 2047) + { + SQ1.enable = false; + } + } + } + } + + // clock the volume envelope + if (sequencer_vol == 0) + { + if (SQ1.per > 0) { if (SQ1.env_add) { SQ1.volume_state++; } else { SQ1.volume_state--; } } + if (SQ2.per > 0) { if (SQ2.env_add) { SQ2.volume_state++; } else { SQ2.volume_state--; } } + if (WAVE.per > 0) { if (WAVE.env_add) { WAVE.volume_state++; } else { WAVE.volume_state--; } } + if (NOISE.per > 0) { if (NOISE.env_add) { NOISE.volume_state++; } else { NOISE.volume_state--; } } + } + } } public void power_off() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 471b2a6b09..fd1d50b131 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -142,36 +142,38 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private void Setup_Mapper() { // setup up mapper based on header entry + string mppr; + switch (header[0x47]) { - case 0x0: mapper = new MapperDefault(); break; - case 0x1: mapper = new MapperMBC1(); break; - case 0x2: mapper = new MapperMBC1(); break; - case 0x3: mapper = new MapperMBC1(); break; - case 0x5: mapper = new MapperMBC2(); break; - case 0x6: mapper = new MapperMBC2(); break; - case 0x8: mapper = new MapperDefault(); break; - case 0x9: mapper = new MapperDefault(); break; - case 0xB: mapper = new MapperMMM01(); break; - case 0xC: mapper = new MapperMMM01(); break; - case 0xD: mapper = new MapperMMM01(); break; - case 0xF: mapper = new MapperMBC3(); break; - case 0x10: mapper = new MapperMBC3(); break; - case 0x11: mapper = new MapperMBC3(); break; - case 0x12: mapper = new MapperMBC3(); break; - case 0x13: mapper = new MapperMBC3(); break; - case 0x19: mapper = new MapperMBC5(); break; - case 0x1A: mapper = new MapperMBC5(); break; - case 0x1B: mapper = new MapperMBC5(); break; - case 0x1C: mapper = new MapperMBC5(); break; - case 0x1D: mapper = new MapperMBC5(); break; - case 0x1E: mapper = new MapperMBC5(); break; - case 0x20: mapper = new MapperMBC6(); break; - case 0x22: mapper = new MapperMBC7(); break; - case 0xFC: mapper = new MapperCamera(); break; - case 0xFD: mapper = new MapperTAMA5(); break; - case 0xFE: mapper = new MapperHuC3(); break; - case 0xFF: mapper = new MapperHuC1(); break; + case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x9: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; + case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; break; + case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; + case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; + case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; + case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; case 0x4: case 0x7: @@ -202,7 +204,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } Console.Write("Mapper: "); - Console.WriteLine(header[0x47]); + Console.WriteLine(mppr); cart_RAM = null; @@ -223,7 +225,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 5: cart_RAM = new byte[0x10000]; break; + } + // mbc2 carts have built in RAM + if (mppr == "MBC2") + { + cart_RAM = new byte[0x200]; } mapper.Core = this; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs index b131098538..6327d00491 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs @@ -4,30 +4,39 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // MBC1 with bank switching and RAM public class MapperMBC2 : MapperBase { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public int ROM_mask; + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else if ((addr >= 0xA000) && (addr < 0xA200)) + { + return Core.cart_RAM[addr - 0xA000]; + } else { - if (Core.cart_RAM != null) - { - return Core.cart_RAM[addr - 0xA000]; - } - else - { - return 0; - } + return 0xFF; } } @@ -38,22 +47,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x8000) + if (addr < 0x2000) { - // no mapping hardware available + RAM_enable = (addr & 0x100) > 0; } - else + else if (addr < 0x4000) { - if (Core.cart_RAM != null) + if ((addr & 0x100) > 0) { - Core.cart_RAM[addr - 0xA000] = value; + ROM_bank = value & 0xF; } } + else if ((addr >= 0xA000) && (addr < 0xA200)) + { + if (RAM_enable) + { + Core.cart_RAM[addr - 0xA000] = (byte)(value & 0xF); + } + } } public override void PokeMemory(ushort addr, byte value) { WriteMemory(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_enable", ref RAM_enable); + } } -} +} \ No newline at end of file From 67d4f0bb7dc88166a511049236d3d1aaf2afa8ca Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 14 Nov 2017 17:52:35 -0500 Subject: [PATCH 18/28] GBHawk: Starting to get sound --- .../Consoles/Nintendo/GBHawk/Audio.cs | 230 ++++++++++++++++-- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 1 + 2 files changed, 217 insertions(+), 14 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index dbdaeeeed6..c02a30c65d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -17,6 +17,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0}; + public static int[] DIVISOR = new int[] {8, 16, 32, 48, 64, 80, 96, 112}; + + public const int NR10 = 0; public const int NR11 = 1; public const int NR12 = 2; @@ -76,6 +79,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int internal_cntr; public byte duty_counter; public bool enable; + public int noise_LFSR; + public int wave_counter; + public int vol_per; // channel non-states public int output; @@ -104,6 +110,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + public int master_audio_clock; + public byte ReadReg(int addr) { byte ret = 0; @@ -192,6 +200,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1.len_en = (value & 0x40) > 0; SQ1.frq &= 0xFF; SQ1.frq |= (ushort)((value & 7) << 8); + + if (SQ1.trigger) + { + SQ1.enable = true; + if (SQ1.length_counter == 0) { SQ1.length_counter = 64; } + SQ1.internal_cntr = 0; + SQ1.volume_state = SQ1.st_vol; + SQ1.vol_per = 0; + } break; case 0xFF16: // NR21 (sound length / wave pattern duty %) Audio_Regs[NR21] = value; @@ -215,6 +232,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ2.len_en = (value & 0x40) > 0; SQ2.frq &= 0xFF; SQ2.frq |= (ushort)((value & 7) << 8); + + if (SQ2.trigger) + { + SQ2.enable = true; + if (SQ2.length_counter == 0) { SQ2.length_counter = 64; } + SQ2.internal_cntr = 0; + SQ2.volume_state = SQ2.st_vol; + SQ2.vol_per = 0; + } break; case 0xFF1A: // NR30 (on/off) Audio_Regs[NR30] = value; @@ -239,6 +265,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WAVE.len_en = (value & 0x40) > 0; WAVE.frq &= 0xFF; WAVE.frq |= (ushort)((value & 7) << 8); + + if (WAVE.trigger) + { + WAVE.enable = true; + if (WAVE.length_counter == 0) { WAVE.length_counter = 64; } + WAVE.internal_cntr = 0; + WAVE.volume_state = WAVE.st_vol; + WAVE.vol_per = 0; + WAVE.wave_counter = 0; + } break; case 0xFF20: // NR41 (length) Audio_Regs[NR41] = value; @@ -258,8 +294,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF23: // NR44 (trigger) Audio_Regs[NR44] = value; - WAVE.trigger = (value & 0x80) > 0; - WAVE.len_en = (value & 0x40) > 0; + NOISE.trigger = (value & 0x80) > 0; + NOISE.len_en = (value & 0x40) > 0; + + if (NOISE.trigger) + { + NOISE.enable = true; + if (NOISE.length_counter == 0) { NOISE.length_counter = 64; } + NOISE.internal_cntr = 0; + NOISE.volume_state = NOISE.st_vol; + NOISE.vol_per = 0; + NOISE.wave_counter = 0; + NOISE.noise_LFSR = 0x7FFF; + } break; case 0xFF24: // NR50 (ctrl) Audio_Regs[NR50] = value; @@ -390,12 +437,110 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // calculate wave output + if (WAVE.enable) + { + WAVE.internal_cntr++; + if (WAVE.internal_cntr == (2048 - WAVE.frq) * 2) + { + WAVE.wave_counter++; + WAVE.wave_counter &= 0x1F; + + byte sample = Wave_RAM[WAVE.wave_counter >> 1]; + + if ((WAVE.wave_counter & 1) == 0) + { + sample = (byte)(sample >> 4); + } + + if (WAVE.vol_code == 0) + { + sample = (byte)((sample & 0xF) >> 4); + } + else if (WAVE.vol_code == 1) + { + sample = (byte)(sample & 0xF); + } + else if (WAVE.vol_code == 2) + { + sample = (byte)((sample & 0xF) >> 1); + } + else + { + sample = (byte)((sample & 0xF) >> 2); + } + + WAVE.output = sample; + + if (!WAVE.DAC_pow) + { + WAVE.output = 0; + } + } + } + // calculate noise output + if (NOISE.enable) + { + NOISE.internal_cntr++; + if (NOISE.internal_cntr == DIVISOR[NOISE.div_code] << NOISE.shift) + { + NOISE.internal_cntr = 0; + int bit_lfsr = (NOISE.noise_LFSR & 1) ^ ((NOISE.noise_LFSR & 2) >> 1); + NOISE.noise_LFSR = (NOISE.noise_LFSR >> 1) & 0x3FFF; + NOISE.noise_LFSR |= (bit_lfsr << 14); + if (NOISE.wdth_md) + { + NOISE.noise_LFSR = NOISE.noise_LFSR & 0x7FBF; + NOISE.noise_LFSR |= (bit_lfsr << 6); + } + NOISE.output = NOISE.noise_LFSR & 1; + NOISE.output *= NOISE.volume_state; + } + } + // add up components to each channel + int L_final = 0; + int R_final = 0; + + if (AUD_CTRL.sq1_L_en) { L_final += SQ1.output; } + if (AUD_CTRL.sq2_L_en) { L_final += SQ2.output; } + if (AUD_CTRL.wave_L_en) { L_final += WAVE.output; } + if (AUD_CTRL.noise_L_en) { L_final += NOISE.output; } + + if (AUD_CTRL.sq1_R_en) { R_final += SQ1.output; } + if (AUD_CTRL.sq2_R_en) { R_final += SQ2.output; } + if (AUD_CTRL.wave_R_en) { R_final += WAVE.output; } + if (AUD_CTRL.noise_R_en) { R_final += NOISE.output; } + + L_final *= (AUD_CTRL.vol_L + 1); + R_final *= (AUD_CTRL.vol_R + 1); + + // send out an actual sample every 94 cycles + master_audio_clock++; + if (master_audio_clock == 94) + { + master_audio_clock = 0; + if (AudioClocks < 1500) + { + AudioSamples[AudioClocks] = (short)L_final; + /* + Console.Write(SQ1.output); + Console.Write(" "); + Console.Write(SQ2.output); + Console.Write(" "); + Console.Write(WAVE.output); + Console.Write(" "); + Console.WriteLine(NOISE.output); + */ + AudioClocks++; + AudioSamples[AudioClocks] = (short)R_final; + AudioClocks++; + } + } // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) sequencer_tick++; @@ -460,10 +605,46 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // clock the volume envelope if (sequencer_vol == 0) { - if (SQ1.per > 0) { if (SQ1.env_add) { SQ1.volume_state++; } else { SQ1.volume_state--; } } - if (SQ2.per > 0) { if (SQ2.env_add) { SQ2.volume_state++; } else { SQ2.volume_state--; } } - if (WAVE.per > 0) { if (WAVE.env_add) { WAVE.volume_state++; } else { WAVE.volume_state--; } } - if (NOISE.per > 0) { if (NOISE.env_add) { NOISE.volume_state++; } else { NOISE.volume_state--; } } + if (SQ1.per > 0) + { + SQ1.vol_per++; + if (SQ1.vol_per == SQ1.per) + { + SQ1.vol_per = 0; + if (SQ1.env_add) { SQ1.volume_state++; } + else { SQ1.volume_state--; } + } + } + if (SQ2.per > 0) + { + SQ2.vol_per++; + if (SQ2.vol_per == SQ2.per) + { + SQ2.vol_per = 0; + if (SQ2.env_add) { SQ2.volume_state++; } + else { SQ2.volume_state--; } + } + } + if (WAVE.per > 0) + { + WAVE.vol_per++; + if (WAVE.vol_per == WAVE.per) + { + WAVE.vol_per = 0; + if (WAVE.env_add) { WAVE.volume_state++; } + else { WAVE.volume_state--; } + } + } + if (NOISE.per > 0) + { + NOISE.vol_per++; + if (NOISE.vol_per == NOISE.per) + { + NOISE.vol_per = 0; + if (NOISE.env_add) { NOISE.volume_state++; } + else { NOISE.volume_state--; } + } + } } } } @@ -479,8 +660,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk sequencer_vol = 0; sequencer_swp = 0; sequencer_tick = 0; + master_audio_clock = 0; } - public void reset() + public void Reset() { Wave_RAM = new byte[16]; @@ -490,8 +672,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ2 = new AUD_Object(); WAVE = new AUD_Object(); NOISE = new AUD_Object(); - AUD_CTRL = new CTRL_Object(); + + AudioClocks = 0; + master_audio_clock = 0; + + sequencer_len = 0; + sequencer_swp = 0; + sequencer_vol = 0; + sequencer_tick = 0; } public void SyncState(Serializer ser) @@ -505,6 +694,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("WAVE.length_counter", ref WAVE.length_counter); ser.Sync("NOISE.length_counter", ref NOISE.length_counter); + ser.Sync("sequencer_len", ref sequencer_len); + ser.Sync("sequencer_vol", ref sequencer_vol); + ser.Sync("sequencer_swp", ref sequencer_swp); + ser.Sync("sequencer_tick", ref sequencer_tick); + + ser.Sync("master_audio_clock", ref master_audio_clock); + // get derived state if (ser.IsReader) { @@ -602,6 +798,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int _spf; public int AudioClocks; + public short[] AudioSamples = new short[1500]; public void SetSyncMode(SyncSoundMode mode) { @@ -615,10 +812,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void GetSamplesSync(out short[] samples, out int nsamp) { - short[] ret = new short[_spf * 2]; - nsamp = _spf; - GetSamples(ret); - samples = ret; + nsamp = AudioClocks / 2; + short[] temp_samp = new short[AudioClocks]; + + for (int i = 0; i < AudioClocks; i++) + { + temp_samp[i] = AudioSamples[i]; + } + Console.WriteLine(AudioClocks); + samples = temp_samp; + + AudioClocks = 0; } public void GetSamplesAsync(short[] samples) @@ -631,8 +835,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk AudioClocks = 0; } - // Exposing this as GetSamplesAsync would allow this to provide async sound - // However, it does nothing special for async sound so I don't see a point private void GetSamples(short[] samples) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index fd1d50b131..1c36b13680 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -128,6 +128,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Register_Reset(); timer.Reset(); ppu.Reset(); + audio.Reset(); cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); From 27601653e528e87e13969130773237f0463b6217 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 15 Nov 2017 12:17:58 -0500 Subject: [PATCH 19/28] GBHawk: Audio work --- .../Consoles/Nintendo/GBHawk/Audio.cs | 711 +++++++++--------- 1 file changed, 376 insertions(+), 335 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index c02a30c65d..27215e435b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -52,61 +52,54 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte[] Wave_RAM = new byte [16]; - struct AUD_Object - { - // channel controls - public byte swp_period; - public bool negate; - public byte shift; - public byte duty; - public byte length; - public byte st_vol; - public bool env_add; - public byte per; - public int frq; - public bool trigger; - public bool len_en; - public bool DAC_pow; - public byte vol_code; - public byte clk_shft; - public bool wdth_md; - public byte div_code; - // channel states - public byte length_counter; - public byte volume_state; - public int frq_shadow; - public int internal_cntr; - public byte duty_counter; - public bool enable; - public int noise_LFSR; - public int wave_counter; - public int vol_per; + // Audio Variables + // derived + public bool WAVE_DAC_pow; + public bool NOISE_wdth_md; + public bool SQ1_negate; + public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; + public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; + public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; + public byte WAVE_vol_code; + public byte NOISE_clk_shft; + public byte NOISE_div_code; + public byte SQ1_shift; + public byte SQ1_duty, SQ2_duty; + public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol; + public byte SQ1_per, SQ2_per, NOISE_per; + public byte SQ1_swp_prd; + public int SQ1_frq, SQ2_frq, WAVE_frq; + public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; + // state + public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; + public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; + public byte SQ1_duty_cntr, SQ2_duty_cntr; + public byte WAVE_wave_cntr; + public int SQ1_frq_shadow; + public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr; + public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; + public int SQ1_intl_swp_cnt; + public int NOISE_LFSR; + public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr; + // computed + public int SQ1_output, SQ2_output, WAVE_output, NOISE_output; - // channel non-states - public int output; - } + // 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; - struct CTRL_Object - { - public bool vin_L_en; - public bool vin_R_en; - public byte vol_L; - public byte vol_R; - public bool sq1_L_en; - public bool sq2_L_en; - public bool wave_L_en; - public bool noise_L_en; - public bool sq1_R_en; - public bool sq2_R_en; - public bool wave_R_en; - public bool noise_R_en; - public bool power; - } - - AUD_Object SQ1, SQ2, WAVE, NOISE; - - CTRL_Object AUD_CTRL; public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; @@ -138,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) - case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (ctrl) + case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); Console.WriteLine(Audio_Regs[NR52] & 0xF); break; // NR52 (ctrl) // wave ram table case 0xFF30: @@ -168,170 +161,184 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void WriteReg(int addr, byte value) { // while power is on, everything is writable - if (AUD_CTRL.power) + if (AUD_CTRL_power) { switch (addr) { case 0xFF10: // NR10 (sweep) Audio_Regs[NR10] = value; - SQ1.swp_period = (byte)((value & 0x70) >> 4); - SQ1.negate = (value & 8) > 0; - SQ1.shift = (byte)(value & 7); + SQ1_swp_prd = (byte)((value & 0x70) >> 4); + SQ1_negate = (value & 8) > 0; + SQ1_shift = (byte)(value & 7); + if (SQ1_swp_prd == 0) { SQ1_swp_prd = 8; } break; case 0xFF11: // NR11 (sound length / wave pattern duty %) Audio_Regs[NR11] = value; - SQ1.duty = (byte)((value & 0xC0) >> 6); - SQ1.length = (byte)(64 - value & 0x3F); + SQ1_duty = (byte)((value & 0xC0) >> 6); + SQ1_length = (ushort)(64 - value & 0x3F); + SQ1_len_cntr = SQ1_length; break; case 0xFF12: // NR12 (envelope) Audio_Regs[NR12] = value; - SQ1.st_vol = (byte)((value & 0xF0) >> 4); - SQ1.env_add = (value & 8) > 0; - SQ1.per = (byte)(value & 7); + SQ1_st_vol = (byte)((value & 0xF0) >> 4); + SQ1_env_add = (value & 8) > 0; + SQ1_per = (byte)(value & 7); + if (SQ1_per == 0) { SQ1_per = 8; } break; case 0xFF13: // NR13 (freq low) Audio_Regs[NR13] = value; - SQ1.frq &= 0x700; - SQ1.frq |= value; + SQ1_frq &= 0x700; + SQ1_frq |= value; break; case 0xFF14: // NR14 (freq hi) Audio_Regs[NR14] = value; - SQ1.trigger = (value & 0x80) > 0; - SQ1.len_en = (value & 0x40) > 0; - SQ1.frq &= 0xFF; - SQ1.frq |= (ushort)((value & 7) << 8); + SQ1_trigger = (value & 0x80) > 0; + SQ1_len_en = (value & 0x40) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((value & 7) << 8); - if (SQ1.trigger) + if (SQ1_trigger) { - SQ1.enable = true; - if (SQ1.length_counter == 0) { SQ1.length_counter = 64; } - SQ1.internal_cntr = 0; - SQ1.volume_state = SQ1.st_vol; - SQ1.vol_per = 0; + SQ1_enable = true; + Audio_Regs[NR52] |= 1; + if (SQ1_len_cntr == 0) { SQ1_len_cntr = 64; } + SQ1_intl_cntr = 0; + SQ1_vol_state = SQ1_st_vol; + SQ1_vol_per = 0; + SQ1_frq_shadow = SQ1_frq; + if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } } break; case 0xFF16: // NR21 (sound length / wave pattern duty %) Audio_Regs[NR21] = value; - SQ2.duty = (byte)((value & 0xC0) >> 6); - SQ2.length = (byte)(64 - value & 0x3F); + SQ2_duty = (byte)((value & 0xC0) >> 6); + SQ2_length = (ushort)(64 - value & 0x3F); + SQ2_len_cntr = SQ2_length; break; case 0xFF17: // NR22 (envelope) Audio_Regs[NR22] = value; - SQ2.st_vol = (byte)((value & 0xF0) >> 4); - SQ2.env_add = (value & 8) > 0; - SQ2.per = (byte)(value & 7); + SQ2_st_vol = (byte)((value & 0xF0) >> 4); + SQ2_env_add = (value & 8) > 0; + SQ2_per = (byte)(value & 7); + //if (SQ2_per == 0) { SQ2_per = 8; } break; case 0xFF18: // NR23 (freq low) Audio_Regs[NR23] = value; - SQ2.frq &= 0x700; - SQ2.frq |= value; + SQ2_frq &= 0x700; + SQ2_frq |= value; break; case 0xFF19: // NR24 (freq hi) Audio_Regs[NR24] = value; - SQ2.trigger = (value & 0x80) > 0; - SQ2.len_en = (value & 0x40) > 0; - SQ2.frq &= 0xFF; - SQ2.frq |= (ushort)((value & 7) << 8); + SQ2_trigger = (value & 0x80) > 0; + SQ2_len_en = (value & 0x40) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((value & 7) << 8); - if (SQ2.trigger) + if (SQ2_trigger) { - SQ2.enable = true; - if (SQ2.length_counter == 0) { SQ2.length_counter = 64; } - SQ2.internal_cntr = 0; - SQ2.volume_state = SQ2.st_vol; - SQ2.vol_per = 0; + SQ2_enable = true; + Audio_Regs[NR52] |= 2; + if (SQ2_len_cntr == 0) { SQ2_len_cntr = 64; } + SQ2_intl_cntr = 0; + SQ2_vol_state = SQ2_st_vol; + SQ2_vol_per = 0; + if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } } break; case 0xFF1A: // NR30 (on/off) Audio_Regs[NR30] = value; - WAVE.DAC_pow = (value & 0x80) > 0; + WAVE_DAC_pow = (value & 0x80) > 0; break; case 0xFF1B: // NR31 (length) Audio_Regs[NR31] = value; - WAVE.length = (byte)(256 - value); + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; break; case 0xFF1C: // NR32 (level output) Audio_Regs[NR32] = value; - WAVE.vol_code = (byte)((value & 0x60) >> 5); + WAVE_vol_code = (byte)((value & 0x60) >> 5); break; case 0xFF1D: // NR33 (freq low) Audio_Regs[NR33] = value; - WAVE.frq &= 0x700; - WAVE.frq |= value; + WAVE_frq &= 0x700; + WAVE_frq |= value; break; case 0xFF1E: // NR34 (freq hi) Audio_Regs[NR34] = value; - WAVE.trigger = (value & 0x80) > 0; - WAVE.len_en = (value & 0x40) > 0; - WAVE.frq &= 0xFF; - WAVE.frq |= (ushort)((value & 7) << 8); + WAVE_trigger = (value & 0x80) > 0; + WAVE_len_en = (value & 0x40) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((value & 7) << 8); - if (WAVE.trigger) + if (WAVE_trigger) { - WAVE.enable = true; - if (WAVE.length_counter == 0) { WAVE.length_counter = 64; } - WAVE.internal_cntr = 0; - WAVE.volume_state = WAVE.st_vol; - WAVE.vol_per = 0; - WAVE.wave_counter = 0; + WAVE_enable = true; + Audio_Regs[NR52] |= 4; + if (WAVE_len_cntr == 0) { WAVE_len_cntr = 256; } + WAVE_intl_cntr = 0; + WAVE_wave_cntr = 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } } break; case 0xFF20: // NR41 (length) Audio_Regs[NR41] = value; - NOISE.length = (byte)(64 - value & 0x3F); + NOISE_length = (ushort)(64 - value & 0x3F); + NOISE_len_cntr = NOISE_length; break; case 0xFF21: // NR42 (envelope) Audio_Regs[NR42] = value; - NOISE.st_vol = (byte)((value & 0xF0) >> 4); - NOISE.env_add = (value & 8) > 0; - NOISE.per = (byte)(value & 7); + NOISE_st_vol = (byte)((value & 0xF0) >> 4); + NOISE_env_add = (value & 8) > 0; + NOISE_per = (byte)(value & 7); + //if (NOISE_per == 0) { NOISE_per = 8; } break; case 0xFF22: // NR43 (shift) Audio_Regs[NR43] = value; - NOISE.clk_shft = (byte)((value & 0xF0) >> 4); - NOISE.wdth_md = (value & 8) > 0; - NOISE.div_code = (byte)(value & 7); + NOISE_clk_shft = (byte)((value & 0xF0) >> 4); + NOISE_wdth_md = (value & 8) > 0; + NOISE_div_code = (byte)(value & 7); break; case 0xFF23: // NR44 (trigger) Audio_Regs[NR44] = value; - NOISE.trigger = (value & 0x80) > 0; - NOISE.len_en = (value & 0x40) > 0; + NOISE_trigger = (value & 0x80) > 0; + NOISE_len_en = (value & 0x40) > 0; - if (NOISE.trigger) + if (NOISE_trigger) { - NOISE.enable = true; - if (NOISE.length_counter == 0) { NOISE.length_counter = 64; } - NOISE.internal_cntr = 0; - NOISE.volume_state = NOISE.st_vol; - NOISE.vol_per = 0; - NOISE.wave_counter = 0; - NOISE.noise_LFSR = 0x7FFF; + NOISE_enable = true; + Audio_Regs[NR52] |= 8; + if (NOISE_len_cntr == 0) { NOISE_len_cntr = 64; } + NOISE_intl_cntr = 0; + NOISE_vol_state = NOISE_st_vol; + NOISE_vol_per = 0; + NOISE_LFSR = 0x7FFF; + if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } } break; case 0xFF24: // NR50 (ctrl) Audio_Regs[NR50] = value; - AUD_CTRL.vin_L_en = (value & 0x80) > 0; - AUD_CTRL.vol_L = (byte)((value & 0x70) >> 4); - AUD_CTRL.vin_R_en = (value & 8) > 0; - AUD_CTRL.vol_R = (byte)(value & 7); + AUD_CTRL_vin_L_en = (value & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); + AUD_CTRL_vin_R_en = (value & 8) > 0; + AUD_CTRL_vol_R = (byte)(value & 7); break; case 0xFF25: // NR51 (ctrl) Audio_Regs[NR51] = value; - AUD_CTRL.noise_L_en = (value & 0x80) > 0; - AUD_CTRL.wave_L_en = (value & 0x40) > 0; - AUD_CTRL.sq2_L_en = (value & 0x20) > 0; - AUD_CTRL.sq1_L_en = (value & 0x10) > 0; - AUD_CTRL.noise_R_en = (value & 8) > 0; - AUD_CTRL.wave_R_en = (value & 4) > 0; - AUD_CTRL.sq2_R_en = (value & 2) > 0; - AUD_CTRL.sq1_R_en = (value & 1) > 0; + AUD_CTRL_noise_L_en = (value & 0x80) > 0; + AUD_CTRL_wave_L_en = (value & 0x40) > 0; + AUD_CTRL_sq2_L_en = (value & 0x20) > 0; + AUD_CTRL_sq1_L_en = (value & 0x10) > 0; + AUD_CTRL_noise_R_en = (value & 8) > 0; + AUD_CTRL_wave_R_en = (value & 4) > 0; + AUD_CTRL_sq2_R_en = (value & 2) > 0; + AUD_CTRL_sq1_R_en = (value & 1) > 0; break; case 0xFF26: // NR52 (ctrl) Audio_Regs[NR52] &= 0x7F; Audio_Regs[NR52] |= (byte)(value & 0x80); - AUD_CTRL.power = (value & 0x80) > 0; + AUD_CTRL_power = (value & 0x80) > 0; - if (!AUD_CTRL.power) + if (!AUD_CTRL_power) { power_off(); } @@ -364,21 +371,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk switch (addr) { case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1.length = (byte)(64 - value & 0x3F); + SQ1_length = (ushort)(64 - value & 0x3F); break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2.length = (byte)(64 - value & 0x3F); + SQ2_length = (ushort)(64 - value & 0x3F); break; case 0xFF1B: // NR31 (length) - WAVE.length = (byte)(256 - value); + WAVE_length = (ushort)(256 - value); break; case 0xFF20: // NR41 (length) - NOISE.length = (byte)(64 - value & 0x3F); + NOISE_length = (ushort)(64 - value & 0x3F); break; case 0xFF26: // NR52 (ctrl) Audio_Regs[NR52] &= 0x7F; Audio_Regs[NR52] |= (byte)(value & 0x80); - AUD_CTRL.power = (value & 0x80) > 0; + AUD_CTRL_power = (value & 0x80) > 0; break; // wave ram table @@ -407,60 +414,61 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void tick() { // calculate square1's output - if (SQ1.enable) + if (SQ1_enable) { - SQ1.internal_cntr++; - if (SQ1.internal_cntr == (2048 - SQ1.frq_shadow) * 4) + SQ1_intl_cntr++; + if (SQ1_intl_cntr >= (2048 - SQ1_frq_shadow) * 4) { - SQ1.internal_cntr = 0; - SQ1.duty_counter++; - SQ1.duty_counter &= 7; + SQ1_intl_cntr = 0; + SQ1_duty_cntr++; + SQ1_duty_cntr &= 7; - SQ1.output = DUTY_CYCLES[SQ1.duty * 8 + SQ1.duty_counter]; - SQ1.output *= SQ1.volume_state; + SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; + SQ1_output *= SQ1_vol_state; } } // calculate square2's output - if (SQ2.enable) + if (SQ2_enable) { - SQ2.internal_cntr++; - if (SQ2.internal_cntr == (2048 - SQ2.frq) * 4) + SQ2_intl_cntr++; + if (SQ2_intl_cntr >= (2048 - SQ2_frq) * 4) { - SQ2.internal_cntr = 0; - SQ2.duty_counter++; - SQ2.duty_counter &= 7; + SQ2_intl_cntr = 0; + SQ2_duty_cntr++; + SQ2_duty_cntr &= 7; - SQ2.output = DUTY_CYCLES[SQ2.duty * 8 + SQ2.duty_counter]; - SQ2.output *= SQ2.volume_state; + SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; + SQ2_output *= SQ2_vol_state; } } // calculate wave output - if (WAVE.enable) + if (WAVE_enable) { - WAVE.internal_cntr++; - if (WAVE.internal_cntr == (2048 - WAVE.frq) * 2) + WAVE_intl_cntr++; + if (WAVE_intl_cntr >= (2048 - WAVE_frq) * 2) { - WAVE.wave_counter++; - WAVE.wave_counter &= 0x1F; + WAVE_intl_cntr = 0; + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; - byte sample = Wave_RAM[WAVE.wave_counter >> 1]; + byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; - if ((WAVE.wave_counter & 1) == 0) + if ((WAVE_wave_cntr & 1) == 0) { sample = (byte)(sample >> 4); } - if (WAVE.vol_code == 0) + if (WAVE_vol_code == 0) { sample = (byte)((sample & 0xF) >> 4); } - else if (WAVE.vol_code == 1) + else if (WAVE_vol_code == 1) { sample = (byte)(sample & 0xF); } - else if (WAVE.vol_code == 2) + else if (WAVE_vol_code == 2) { sample = (byte)((sample & 0xF) >> 1); } @@ -469,36 +477,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk sample = (byte)((sample & 0xF) >> 2); } - WAVE.output = sample; + WAVE_output = sample; - if (!WAVE.DAC_pow) + if (!WAVE_DAC_pow) { - WAVE.output = 0; + WAVE_output = 0; } } } // calculate noise output - if (NOISE.enable) + if (NOISE_enable) { - NOISE.internal_cntr++; - if (NOISE.internal_cntr == DIVISOR[NOISE.div_code] << NOISE.shift) + NOISE_intl_cntr++; + if (NOISE_intl_cntr >= (DIVISOR[NOISE_div_code] << NOISE_clk_shft)) { - NOISE.internal_cntr = 0; - int bit_lfsr = (NOISE.noise_LFSR & 1) ^ ((NOISE.noise_LFSR & 2) >> 1); + NOISE_intl_cntr = 0; + int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); - NOISE.noise_LFSR = (NOISE.noise_LFSR >> 1) & 0x3FFF; - NOISE.noise_LFSR |= (bit_lfsr << 14); + NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; + NOISE_LFSR |= (bit_lfsr << 14); - if (NOISE.wdth_md) + if (NOISE_wdth_md) { - NOISE.noise_LFSR = NOISE.noise_LFSR & 0x7FBF; - NOISE.noise_LFSR |= (bit_lfsr << 6); + NOISE_LFSR = NOISE_LFSR & 0x7FBF; + NOISE_LFSR |= (bit_lfsr << 6); } - NOISE.output = NOISE.noise_LFSR & 1; - NOISE.output *= NOISE.volume_state; + NOISE_output = NOISE_LFSR & 1; + NOISE_output *= NOISE_vol_state; } } @@ -506,18 +514,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk int L_final = 0; int R_final = 0; - if (AUD_CTRL.sq1_L_en) { L_final += SQ1.output; } - if (AUD_CTRL.sq2_L_en) { L_final += SQ2.output; } - if (AUD_CTRL.wave_L_en) { L_final += WAVE.output; } - if (AUD_CTRL.noise_L_en) { L_final += NOISE.output; } + if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } + if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } + if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } + if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } - if (AUD_CTRL.sq1_R_en) { R_final += SQ1.output; } - if (AUD_CTRL.sq2_R_en) { R_final += SQ2.output; } - if (AUD_CTRL.wave_R_en) { R_final += WAVE.output; } - if (AUD_CTRL.noise_R_en) { R_final += NOISE.output; } + if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } + if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } + if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } + if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } - L_final *= (AUD_CTRL.vol_L + 1); - R_final *= (AUD_CTRL.vol_R + 1); + L_final *= (AUD_CTRL_vol_L + 1); + R_final *= (AUD_CTRL_vol_R + 1); // send out an actual sample every 94 cycles master_audio_clock++; @@ -526,18 +534,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk master_audio_clock = 0; if (AudioClocks < 1500) { - AudioSamples[AudioClocks] = (short)L_final; + AudioSamples[AudioClocks] = (short)(L_final * 4); /* - Console.Write(SQ1.output); - Console.Write(" "); - Console.Write(SQ2.output); - Console.Write(" "); - Console.Write(WAVE.output); - Console.Write(" "); - Console.WriteLine(NOISE.output); + Console_Write(SQ1_output); + Console_Write(" "); + Console_Write(SQ2_output); + Console_Write(" "); + Console_Write(WAVE_output); + Console_Write(" "); + Console_WriteLine(NOISE_output); */ AudioClocks++; - AudioSamples[AudioClocks] = (short)R_final; + AudioSamples[AudioClocks] = (short)(R_final * 4); AudioClocks++; } } @@ -556,47 +564,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // clock the lengths if ((sequencer_len == 1) || (sequencer_len == 3) || (sequencer_len == 5) || (sequencer_len == 7)) { - if (SQ1.len_en && SQ1.length_counter > 0) { SQ1.length_counter--; if (SQ1.length_counter == 0) { SQ1.enable = false; } } - if (SQ2.len_en && SQ2.length_counter > 0) { SQ2.length_counter--; if (SQ2.length_counter == 0) { SQ2.enable = false; } } - if (WAVE.len_en && WAVE.length_counter > 0) { WAVE.length_counter--; if (WAVE.length_counter == 0) { WAVE.enable = false; } } - if (NOISE.len_en && NOISE.length_counter > 0) { NOISE.length_counter--; if (NOISE.length_counter == 0) { NOISE.enable = false; } } + if (SQ1_len_en && SQ1_len_cntr > 0) + { + SQ1_len_cntr--; + if (SQ1_len_cntr == 0) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } + } + if (SQ2_len_en && SQ2_len_cntr > 0) + { + SQ2_len_cntr--; + if (SQ2_len_cntr == 0) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } + } + if (WAVE_len_en && WAVE_len_cntr > 0) + { + WAVE_len_cntr--; + if (WAVE_len_cntr == 0) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } + } + if (NOISE_len_en && NOISE_len_cntr > 0) + { + NOISE_len_cntr--; + if (NOISE_len_cntr == 0) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } + } } // clock the sweep if ((sequencer_swp == 3) || (sequencer_swp == 7)) { - if (((SQ1.swp_period > 0) || (SQ1.shift > 0)) && (SQ1.swp_period > 0)) + SQ1_intl_swp_cnt++; + if (SQ1_intl_swp_cnt >= SQ1_swp_prd) { - int shadow_frq = SQ1.frq_shadow; - shadow_frq = shadow_frq >> SQ1.shift; - if (SQ1.negate) { shadow_frq = -shadow_frq; } - shadow_frq += SQ1.frq_shadow; + SQ1_intl_swp_cnt = 0; - // disable channel if overflow - if ((uint) shadow_frq > 2047) + if ((SQ1_swp_prd > 0) && (SQ1_shift > 0)) { - SQ1.enable = false; - } - else - { - shadow_frq &= 0x7FF; - SQ1.frq = shadow_frq; - SQ1.frq_shadow = shadow_frq; - - // note that we also write back the frequency to the actual register - Audio_Regs[NR13] = (byte)(SQ1.frq & 0xFF); - Audio_Regs[NR14] &= 0xF8; - Audio_Regs[NR14] |= (byte)((SQ1.frq >> 8) & 7); - - // after writing, we repeat the process and do another overflow check - shadow_frq = SQ1.frq_shadow; - shadow_frq = shadow_frq >> SQ1.shift; - if (SQ1.negate) { shadow_frq = -shadow_frq; } - shadow_frq += SQ1.frq_shadow; + int shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + // disable channel if overflow if ((uint)shadow_frq > 2047) { - SQ1.enable = false; + SQ1_enable = false; + Audio_Regs[NR52] &= 0xFE; + } + else + { + shadow_frq &= 0x7FF; + SQ1_frq = shadow_frq; + SQ1_frq_shadow = shadow_frq; + + // note that we also write back the frequency to the actual register + Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF); + Audio_Regs[NR14] &= 0xF8; + Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7); + + // after writing, we repeat the process and do another overflow check + shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + if ((uint)shadow_frq > 2047) + { + SQ1_enable = false; + Audio_Regs[NR52] &= 0xFE; + } } } } @@ -605,44 +637,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // clock the volume envelope if (sequencer_vol == 0) { - if (SQ1.per > 0) + if (SQ1_per > 0) { - SQ1.vol_per++; - if (SQ1.vol_per == SQ1.per) + SQ1_vol_per++; + if (SQ1_vol_per >= SQ1_per) { - SQ1.vol_per = 0; - if (SQ1.env_add) { SQ1.volume_state++; } - else { SQ1.volume_state--; } + SQ1_vol_per = 0; + if (SQ1_env_add && (SQ1_vol_state < 15)) { SQ1_vol_state++; } + else if (SQ1_vol_state > 0) { SQ1_vol_state--; } } } - if (SQ2.per > 0) + if (SQ2_per > 0) { - SQ2.vol_per++; - if (SQ2.vol_per == SQ2.per) + SQ2_vol_per++; + if (SQ2_vol_per >= SQ2_per) { - SQ2.vol_per = 0; - if (SQ2.env_add) { SQ2.volume_state++; } - else { SQ2.volume_state--; } + SQ2_vol_per = 0; + if (SQ2_env_add && (SQ2_vol_state < 15)) { SQ2_vol_state++; } + else if (SQ2_vol_state > 0) { SQ2_vol_state--; } } } - if (WAVE.per > 0) + if (NOISE_per > 0) { - WAVE.vol_per++; - if (WAVE.vol_per == WAVE.per) + NOISE_vol_per++; + if (NOISE_vol_per >= NOISE_per) { - WAVE.vol_per = 0; - if (WAVE.env_add) { WAVE.volume_state++; } - else { WAVE.volume_state--; } - } - } - if (NOISE.per > 0) - { - NOISE.vol_per++; - if (NOISE.vol_per == NOISE.per) - { - NOISE.vol_per = 0; - if (NOISE.env_add) { NOISE.volume_state++; } - else { NOISE.volume_state--; } + NOISE_vol_per = 0; + if (NOISE_env_add && (NOISE_vol_state < 15)) { NOISE_vol_state++; } + else if (NOISE_vol_state > 0) { NOISE_vol_state--; } } } } @@ -651,10 +673,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void power_off() { - for (int i = 0; i < 20; i++) + for (int i = 0; i < 21; i++) { Audio_Regs[i] = 0; } + SQ1_enable = false; + SQ2_enable = false; + WAVE_enable = false; + NOISE_enable = false; sequencer_len = 0; sequencer_vol = 0; @@ -668,12 +694,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Audio_Regs = new byte[21]; - SQ1 = new AUD_Object(); - SQ2 = new AUD_Object(); - WAVE = new AUD_Object(); - NOISE = new AUD_Object(); - AUD_CTRL = new CTRL_Object(); - AudioClocks = 0; master_audio_clock = 0; @@ -688,11 +708,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("Audio_Regs", ref Audio_Regs, false); ser.Sync("Wave_Ram", ref Wave_RAM, false); - // some aspecta of the channel states are not derived from the Regs - ser.Sync("SQ1.length_counter", ref SQ1.length_counter); - ser.Sync("SQ2.length_counter", ref SQ2.length_counter); - ser.Sync("WAVE.length_counter", ref WAVE.length_counter); - ser.Sync("NOISE.length_counter", ref NOISE.length_counter); + // save state variables + ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); + ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); + ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); + ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); + ser.Sync("SQ1_enable", ref SQ1_enable); + ser.Sync("SQ2_enable", ref SQ2_enable); + ser.Sync("WAVE_enable", ref WAVE_enable); + ser.Sync("NOISE_enable", ref NOISE_enable); + ser.Sync("SQ1_vol_state", ref SQ1_vol_state); + ser.Sync("SQ2_vol_state", ref SQ2_vol_state); + ser.Sync("NOISE_vol_state", ref NOISE_vol_state); + ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); + ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); + ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); + ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); + ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); + ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); + ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); + ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); + ser.Sync("SQ1_vol_per", ref SQ1_vol_per); + ser.Sync("SQ2_vol_per", ref SQ2_vol_per); + ser.Sync("NOISE_vol_per", ref NOISE_vol_per); + ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); + ser.Sync("NOISE_LFSR", ref NOISE_LFSR); + ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); + ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); + ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); + ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); + ser.Sync("sequencer_len", ref sequencer_len); ser.Sync("sequencer_vol", ref sequencer_vol); @@ -706,97 +751,93 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { sync_channels(); } - - - } public void sync_channels() { - SQ1.swp_period = (byte)((Audio_Regs[NR10] & 0x70) >> 4); - SQ1.negate = (Audio_Regs[NR10] & 8) > 0; - SQ1.shift = (byte)(Audio_Regs[NR10] & 7); + SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); + SQ1_negate = (Audio_Regs[NR10] & 8) > 0; + SQ1_shift = (byte)(Audio_Regs[NR10] & 7); - SQ1.duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); - SQ1.length = (byte)(64 - Audio_Regs[NR11] & 0x3F); + SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); + SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); - SQ1.st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); - SQ1.env_add = (Audio_Regs[NR12] & 8) > 0; - SQ1.per = (byte)(Audio_Regs[NR12] & 7); + SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); + SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; + SQ1_per = (byte)(Audio_Regs[NR12] & 7); - SQ1.frq &= 0x700; - SQ1.frq |= Audio_Regs[NR13]; + SQ1_frq &= 0x700; + SQ1_frq |= Audio_Regs[NR13]; - SQ1.trigger = (Audio_Regs[NR14] & 0x80) > 0; - SQ1.len_en = (Audio_Regs[NR14] & 0x40) > 0; - SQ1.frq &= 0xFF; - SQ1.frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); + SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; + SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); - SQ2.duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); - SQ2.length = (byte)(64 - Audio_Regs[NR21] & 0x3F); + SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); + SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); - SQ2.st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); - SQ2.env_add = (Audio_Regs[NR22] & 8) > 0; - SQ2.per = (byte)(Audio_Regs[NR22] & 7); + SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); + SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; + SQ2_per = (byte)(Audio_Regs[NR22] & 7); - SQ2.frq &= 0x700; - SQ2.frq |= Audio_Regs[NR23]; + SQ2_frq &= 0x700; + SQ2_frq |= Audio_Regs[NR23]; - SQ2.trigger = (Audio_Regs[NR24] & 0x80) > 0; - SQ2.len_en = (Audio_Regs[NR24] & 0x40) > 0; - SQ2.frq &= 0xFF; - SQ2.frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); + SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; + SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); - WAVE.DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; + WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; - WAVE.length = (byte)(256 - Audio_Regs[NR31]); + WAVE_length = (ushort)(256 - Audio_Regs[NR31]); - WAVE.vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); + WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); - WAVE.frq &= 0x700; - WAVE.frq |= Audio_Regs[NR33]; + WAVE_frq &= 0x700; + WAVE_frq |= Audio_Regs[NR33]; - WAVE.trigger = (Audio_Regs[NR34] & 0x80) > 0; - WAVE.len_en = (Audio_Regs[NR34] & 0x40) > 0; - WAVE.frq &= 0xFF; - WAVE.frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); + WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); - NOISE.length = (byte)(64 - Audio_Regs[NR41] & 0x3F); + NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); - NOISE.st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); - NOISE.env_add = (Audio_Regs[NR42] & 8) > 0; - NOISE.per = (byte)(Audio_Regs[NR42] & 7); + NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); + NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; + NOISE_per = (byte)(Audio_Regs[NR42] & 7); - NOISE.clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); - NOISE.wdth_md = (Audio_Regs[NR43] & 8) > 0; - NOISE.div_code = (byte)(Audio_Regs[NR43] & 7); + NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); + NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; + NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); - WAVE.trigger = (Audio_Regs[NR44] & 0x80) > 0; - WAVE.len_en = (Audio_Regs[NR44] & 0x40) > 0; + WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; - AUD_CTRL.vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; - AUD_CTRL.vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); - AUD_CTRL.vin_R_en = (Audio_Regs[NR50] & 8) > 0; - AUD_CTRL.vol_R = (byte)(Audio_Regs[NR50] & 7); + AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); + AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; + AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); - AUD_CTRL.noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; - AUD_CTRL.wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; - AUD_CTRL.sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; - AUD_CTRL.sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; - AUD_CTRL.noise_R_en = (Audio_Regs[NR51] & 8) > 0; - AUD_CTRL.wave_R_en = (Audio_Regs[NR51] & 4) > 0; - AUD_CTRL.sq2_R_en = (Audio_Regs[NR51] & 2) > 0; - AUD_CTRL.sq1_R_en = (Audio_Regs[NR51] & 1) > 0; + AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; + AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; + AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; + AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; + AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; + AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; + AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; - AUD_CTRL.power = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; } #region audio public bool CanProvideAsync => false; - public int _spf; public int AudioClocks; public short[] AudioSamples = new short[1500]; @@ -804,7 +845,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (mode != SyncSoundMode.Sync) { - throw new InvalidOperationException("Only Sync mode is supported."); + throw new InvalidOperationException("Only Sync mode is supported_"); } } @@ -819,7 +860,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { temp_samp[i] = AudioSamples[i]; } - Console.WriteLine(AudioClocks); + samples = temp_samp; AudioClocks = 0; From c9bfd57dd12c96fea856396426fdeb6d999cdfdb Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 15 Nov 2017 16:13:00 -0500 Subject: [PATCH 20/28] GBHawk: Start passing tests --- .../Consoles/Nintendo/GBHawk/Audio.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 27215e435b..44b5a136ab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -131,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) - case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); Console.WriteLine(Audio_Regs[NR52] & 0xF); break; // NR52 (ctrl) + case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (ctrl) // wave ram table case 0xFF30: @@ -175,7 +175,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF11: // NR11 (sound length / wave pattern duty %) Audio_Regs[NR11] = value; SQ1_duty = (byte)((value & 0xC0) >> 6); - SQ1_length = (ushort)(64 - value & 0x3F); + SQ1_length = (ushort)(64 - (value & 0x3F)); SQ1_len_cntr = SQ1_length; break; case 0xFF12: // NR12 (envelope) @@ -184,6 +184,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1_env_add = (value & 8) > 0; SQ1_per = (byte)(value & 7); if (SQ1_per == 0) { SQ1_per = 8; } + if ((value & 0xF8) == 0) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } break; case 0xFF13: // NR13 (freq low) Audio_Regs[NR13] = value; @@ -212,7 +213,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF16: // NR21 (sound length / wave pattern duty %) Audio_Regs[NR21] = value; SQ2_duty = (byte)((value & 0xC0) >> 6); - SQ2_length = (ushort)(64 - value & 0x3F); + SQ2_length = (ushort)(64 - (value & 0x3F)); SQ2_len_cntr = SQ2_length; break; case 0xFF17: // NR22 (envelope) @@ -221,6 +222,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ2_env_add = (value & 8) > 0; SQ2_per = (byte)(value & 7); //if (SQ2_per == 0) { SQ2_per = 8; } + if ((value & 0xF8) == 0) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } break; case 0xFF18: // NR23 (freq low) Audio_Regs[NR23] = value; @@ -248,6 +250,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF1A: // NR30 (on/off) Audio_Regs[NR30] = value; WAVE_DAC_pow = (value & 0x80) > 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } break; case 0xFF1B: // NR31 (length) Audio_Regs[NR31] = value; @@ -282,7 +285,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF20: // NR41 (length) Audio_Regs[NR41] = value; - NOISE_length = (ushort)(64 - value & 0x3F); + NOISE_length = (ushort)(64 - (value & 0x3F)); NOISE_len_cntr = NOISE_length; break; case 0xFF21: // NR42 (envelope) @@ -291,6 +294,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk NOISE_env_add = (value & 8) > 0; NOISE_per = (byte)(value & 7); //if (NOISE_per == 0) { NOISE_per = 8; } + if ((value & 0xF8) == 0) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } break; case 0xFF22: // NR43 (shift) Audio_Regs[NR43] = value; From 21dca406b6d54caadabf98eff617eed0a8320d7c Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 15 Nov 2017 20:44:47 -0500 Subject: [PATCH 21/28] GBHawk: Pass sound test 3 --- .../Consoles/Nintendo/GBHawk/Audio.cs | 79 ++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 44b5a136ab..fc25cf33cf 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -194,21 +194,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF14: // NR14 (freq hi) Audio_Regs[NR14] = value; SQ1_trigger = (value & 0x80) > 0; - SQ1_len_en = (value & 0x40) > 0; SQ1_frq &= 0xFF; SQ1_frq |= (ushort)((value & 7) << 8); + if (((sequencer_len & 1) > 0)) + { + if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) + { + SQ1_len_cntr--; + if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } + } + } + if (SQ1_trigger) { SQ1_enable = true; Audio_Regs[NR52] |= 1; - if (SQ1_len_cntr == 0) { SQ1_len_cntr = 64; } + if (SQ1_len_cntr == 0) + { + SQ1_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } + } SQ1_intl_cntr = 0; SQ1_vol_state = SQ1_st_vol; SQ1_vol_per = 0; SQ1_frq_shadow = SQ1_frq; if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } } + + + SQ1_len_en = (value & 0x40) > 0; + break; case 0xFF16: // NR21 (sound length / wave pattern duty %) Audio_Regs[NR21] = value; @@ -232,20 +248,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF19: // NR24 (freq hi) Audio_Regs[NR24] = value; SQ2_trigger = (value & 0x80) > 0; - SQ2_len_en = (value & 0x40) > 0; SQ2_frq &= 0xFF; SQ2_frq |= (ushort)((value & 7) << 8); + if ((sequencer_len & 1) > 0) + { + if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) + { + SQ2_len_cntr--; + if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } + } + } + if (SQ2_trigger) { SQ2_enable = true; Audio_Regs[NR52] |= 2; - if (SQ2_len_cntr == 0) { SQ2_len_cntr = 64; } + if (SQ2_len_cntr == 0) + { + SQ2_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } + } SQ2_intl_cntr = 0; SQ2_vol_state = SQ2_st_vol; SQ2_vol_per = 0; if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } } + + SQ2_len_en = (value & 0x40) > 0; + break; case 0xFF1A: // NR30 (on/off) Audio_Regs[NR30] = value; @@ -269,19 +300,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF1E: // NR34 (freq hi) Audio_Regs[NR34] = value; WAVE_trigger = (value & 0x80) > 0; - WAVE_len_en = (value & 0x40) > 0; WAVE_frq &= 0xFF; WAVE_frq |= (ushort)((value & 7) << 8); + if ((sequencer_len & 1) > 0) + { + if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) + { + WAVE_len_cntr--; + if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } + } + } + if (WAVE_trigger) { WAVE_enable = true; Audio_Regs[NR52] |= 4; - if (WAVE_len_cntr == 0) { WAVE_len_cntr = 256; } + if (WAVE_len_cntr == 0) + { + WAVE_len_cntr = 256; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } + } WAVE_intl_cntr = 0; WAVE_wave_cntr = 0; if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } } + + WAVE_len_en = (value & 0x40) > 0; + break; case 0xFF20: // NR41 (length) Audio_Regs[NR41] = value; @@ -305,19 +351,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF23: // NR44 (trigger) Audio_Regs[NR44] = value; NOISE_trigger = (value & 0x80) > 0; - NOISE_len_en = (value & 0x40) > 0; + + if ((sequencer_len & 1) > 0) + { + if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) + { + NOISE_len_cntr--; + if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } + } + } if (NOISE_trigger) { NOISE_enable = true; Audio_Regs[NR52] |= 8; - if (NOISE_len_cntr == 0) { NOISE_len_cntr = 64; } + if (NOISE_len_cntr == 0) + { + NOISE_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } + } NOISE_intl_cntr = 0; NOISE_vol_state = NOISE_st_vol; NOISE_vol_per = 0; NOISE_LFSR = 0x7FFF; if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } } + + NOISE_len_en = (value & 0x40) > 0; + break; case 0xFF24: // NR50 (ctrl) Audio_Regs[NR50] = value; @@ -566,7 +627,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk sequencer_swp++; sequencer_swp &= 0x7; // clock the lengths - if ((sequencer_len == 1) || (sequencer_len == 3) || (sequencer_len == 5) || (sequencer_len == 7)) + if ((sequencer_len & 1) > 0) { if (SQ1_len_en && SQ1_len_cntr > 0) { From 93addbe7250aee09c107910a6a7760608b6eb53e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 18 Nov 2017 11:43:58 -0500 Subject: [PATCH 22/28] GBHawk: More audio work --- .../Consoles/Nintendo/GBHawk/Audio.cs | 1823 +++++++++-------- 1 file changed, 923 insertions(+), 900 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index fc25cf33cf..d5112c1c9b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -1,951 +1,974 @@ -using System; - -using BizHawk.Common; -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { // Audio Emulation - public class Audio : ISoundProvider - { - public GBHawk Core { get; set; } - - public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 0}; - - public static int[] DIVISOR = new int[] {8, 16, 32, 48, 64, 80, 96, 112}; - - - 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, - 0x3F, 0x00, 0xFF, 0xBF, - 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, - 0xFF, 0x00, 0x00, 0xBF, - 0x00, 0x00, 0x70}; - - public byte[] Audio_Regs = new byte[21]; - - public byte[] Wave_RAM = new byte [16]; + public class Audio : ISoundProvider + { + public GBHawk Core { get; set; } + + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 0}; + + public static int[] DIVISOR = new int[] { 8, 16, 32, 48, 64, 80, 96, 112 }; + + + 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, + 0x3F, 0x00, 0xFF, 0xBF, + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, + 0xFF, 0x00, 0x00, 0xBF, + 0x00, 0x00, 0x70}; + + public byte[] Audio_Regs = new byte[21]; + + public byte[] Wave_RAM = new byte[16]; // Audio Variables // derived - public bool WAVE_DAC_pow; - public bool NOISE_wdth_md; - public bool SQ1_negate; - public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; - public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; - public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; - public byte WAVE_vol_code; - public byte NOISE_clk_shft; - public byte NOISE_div_code; - public byte SQ1_shift; - public byte SQ1_duty, SQ2_duty; - public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol; - public byte SQ1_per, SQ2_per, NOISE_per; - public byte SQ1_swp_prd; - public int SQ1_frq, SQ2_frq, WAVE_frq; - public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; + public bool WAVE_DAC_pow; + public bool NOISE_wdth_md; + public bool SQ1_negate; + public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; + public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; + public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; + public byte WAVE_vol_code; + public byte NOISE_clk_shft; + public byte NOISE_div_code; + public byte SQ1_shift; + public byte SQ1_duty, SQ2_duty; + public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol; + public byte SQ1_per, SQ2_per, NOISE_per; + public byte SQ1_swp_prd; + public int SQ1_frq, SQ2_frq, WAVE_frq; + public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; // state - public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; - public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; - public byte SQ1_duty_cntr, SQ2_duty_cntr; - public byte WAVE_wave_cntr; - public int SQ1_frq_shadow; - public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr; - public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; - public int SQ1_intl_swp_cnt; - public int NOISE_LFSR; - public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr; + public bool SQ1_swp_enable; + public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; + public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; + public byte SQ1_duty_cntr, SQ2_duty_cntr; + public byte WAVE_wave_cntr; + public int SQ1_frq_shadow; + public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr; + public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; + public int SQ1_intl_swp_cnt; + public int NOISE_LFSR; + public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr; // computed - public int SQ1_output, SQ2_output, WAVE_output, NOISE_output; + public int SQ1_output, SQ2_output, WAVE_output, NOISE_output; // 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 int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; - - public int master_audio_clock; - - public byte ReadReg(int addr) - { - byte ret = 0; - - switch (addr) - { - case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) - case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) - case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) - case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) - case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) - case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) - case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) - case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) - case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) - case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) - case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) - case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) - case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) - case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) - case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (length) - case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (envelope) - case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (shift) - case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) - case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) - case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) - case 0xFF26: ret = (byte)(Audio_Regs[NR52] | unused_bits[NR52]); break; // NR52 (ctrl) + 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 int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + + public int master_audio_clock; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) + case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) + case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) + case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) + case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) + case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) + case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) + case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) + case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) + case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) + case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) + case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) + case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (length) + case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (envelope) + case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (shift) + case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) + case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) + case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) + case 0xFF26: ret = (byte)(Read_NR52() | unused_bits[NR52]); break; // NR52 (ctrl) // wave ram table - 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 = Wave_RAM[addr & 0x0F]; - break; - - } - - return ret; - } - - public void WriteReg(int addr, byte value) + 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 = Wave_RAM[addr & 0x0F]; + break; + } + + return ret; + } + + public void WriteReg(int addr, byte value) { // while power is on, everything is writable - if (AUD_CTRL_power) - { - switch (addr) - { + if (AUD_CTRL_power) + { + switch (addr) + { case 0xFF10: // NR10 (sweep) - Audio_Regs[NR10] = value; - SQ1_swp_prd = (byte)((value & 0x70) >> 4); - SQ1_negate = (value & 8) > 0; - SQ1_shift = (byte)(value & 7); - if (SQ1_swp_prd == 0) { SQ1_swp_prd = 8; } - break; + Audio_Regs[NR10] = value; + SQ1_swp_prd = (byte)((value & 0x70) >> 4); + SQ1_negate = (value & 8) > 0; + SQ1_shift = (byte)(value & 7); + break; case 0xFF11: // NR11 (sound length / wave pattern duty %) - Audio_Regs[NR11] = value; - SQ1_duty = (byte)((value & 0xC0) >> 6); - SQ1_length = (ushort)(64 - (value & 0x3F)); - SQ1_len_cntr = SQ1_length; - break; + Audio_Regs[NR11] = value; + SQ1_duty = (byte)((value & 0xC0) >> 6); + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + break; case 0xFF12: // NR12 (envelope) - Audio_Regs[NR12] = value; - SQ1_st_vol = (byte)((value & 0xF0) >> 4); - SQ1_env_add = (value & 8) > 0; - SQ1_per = (byte)(value & 7); - if (SQ1_per == 0) { SQ1_per = 8; } - if ((value & 0xF8) == 0) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } - break; + Audio_Regs[NR12] = value; + SQ1_st_vol = (byte)((value & 0xF0) >> 4); + SQ1_env_add = (value & 8) > 0; + SQ1_per = (byte)(value & 7); + if (SQ1_per == 0) { SQ1_per = 8; } + if ((value & 0xF8) == 0) { SQ1_enable = SQ1_swp_enable = false; } + break; case 0xFF13: // NR13 (freq low) - Audio_Regs[NR13] = value; - SQ1_frq &= 0x700; - SQ1_frq |= value; - break; + Audio_Regs[NR13] = value; + SQ1_frq &= 0x700; + SQ1_frq |= value; + break; case 0xFF14: // NR14 (freq hi) - Audio_Regs[NR14] = value; - SQ1_trigger = (value & 0x80) > 0; - SQ1_frq &= 0xFF; - SQ1_frq |= (ushort)((value & 7) << 8); - - if (((sequencer_len & 1) > 0)) - { - if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) + Audio_Regs[NR14] = value; + SQ1_trigger = (value & 0x80) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((value & 7) << 8); + + if (((sequencer_len & 1) > 0)) + { + if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) + { + SQ1_len_cntr--; + if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = SQ1_swp_enable = false; } + } + } + + if (SQ1_trigger) + { + SQ1_enable = true; + if (SQ1_len_cntr == 0) + { + SQ1_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } + } + SQ1_intl_cntr = 0; + SQ1_vol_state = SQ1_st_vol; + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + SQ1_frq_shadow = SQ1_frq; + + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + + if ((SQ1_shift > 0) || (SQ1_swp_prd > 0)) { - SQ1_len_cntr--; - if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } - } - } - - if (SQ1_trigger) - { - SQ1_enable = true; - Audio_Regs[NR52] |= 1; - if (SQ1_len_cntr == 0) - { - SQ1_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } - } - SQ1_intl_cntr = 0; - SQ1_vol_state = SQ1_st_vol; - SQ1_vol_per = 0; - SQ1_frq_shadow = SQ1_frq; - if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } - } - - - SQ1_len_en = (value & 0x40) > 0; - - break; - case 0xFF16: // NR21 (sound length / wave pattern duty %) - Audio_Regs[NR21] = value; - SQ2_duty = (byte)((value & 0xC0) >> 6); - SQ2_length = (ushort)(64 - (value & 0x3F)); - SQ2_len_cntr = SQ2_length; - break; - case 0xFF17: // NR22 (envelope) - Audio_Regs[NR22] = value; - SQ2_st_vol = (byte)((value & 0xF0) >> 4); - SQ2_env_add = (value & 8) > 0; - SQ2_per = (byte)(value & 7); - //if (SQ2_per == 0) { SQ2_per = 8; } - if ((value & 0xF8) == 0) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } - break; - case 0xFF18: // NR23 (freq low) - Audio_Regs[NR23] = value; - SQ2_frq &= 0x700; - SQ2_frq |= value; - break; - case 0xFF19: // NR24 (freq hi) - Audio_Regs[NR24] = value; - SQ2_trigger = (value & 0x80) > 0; - SQ2_frq &= 0xFF; - SQ2_frq |= (ushort)((value & 7) << 8); - - if ((sequencer_len & 1) > 0) - { - if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) - { - SQ2_len_cntr--; - if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } - } - } - - if (SQ2_trigger) - { - SQ2_enable = true; - Audio_Regs[NR52] |= 2; - if (SQ2_len_cntr == 0) - { - SQ2_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } - } - SQ2_intl_cntr = 0; - SQ2_vol_state = SQ2_st_vol; - SQ2_vol_per = 0; - if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } - } - - SQ2_len_en = (value & 0x40) > 0; - - break; - case 0xFF1A: // NR30 (on/off) - Audio_Regs[NR30] = value; - WAVE_DAC_pow = (value & 0x80) > 0; - if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } - break; - case 0xFF1B: // NR31 (length) - Audio_Regs[NR31] = value; - WAVE_length = (ushort)(256 - value); - WAVE_len_cntr = WAVE_length; - break; - case 0xFF1C: // NR32 (level output) - Audio_Regs[NR32] = value; - WAVE_vol_code = (byte)((value & 0x60) >> 5); - break; - case 0xFF1D: // NR33 (freq low) - Audio_Regs[NR33] = value; - WAVE_frq &= 0x700; - WAVE_frq |= value; - break; - case 0xFF1E: // NR34 (freq hi) - Audio_Regs[NR34] = value; - WAVE_trigger = (value & 0x80) > 0; - WAVE_frq &= 0xFF; - WAVE_frq |= (ushort)((value & 7) << 8); - - if ((sequencer_len & 1) > 0) - { - if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) - { - WAVE_len_cntr--; - if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } - } - } - - if (WAVE_trigger) - { - WAVE_enable = true; - Audio_Regs[NR52] |= 4; - if (WAVE_len_cntr == 0) - { - WAVE_len_cntr = 256; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } - } - WAVE_intl_cntr = 0; - WAVE_wave_cntr = 0; - if (!WAVE_DAC_pow) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } - } - - WAVE_len_en = (value & 0x40) > 0; - - break; - case 0xFF20: // NR41 (length) - Audio_Regs[NR41] = value; - NOISE_length = (ushort)(64 - (value & 0x3F)); - NOISE_len_cntr = NOISE_length; - break; - case 0xFF21: // NR42 (envelope) - Audio_Regs[NR42] = value; - NOISE_st_vol = (byte)((value & 0xF0) >> 4); - NOISE_env_add = (value & 8) > 0; - NOISE_per = (byte)(value & 7); - //if (NOISE_per == 0) { NOISE_per = 8; } - if ((value & 0xF8) == 0) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } - break; - case 0xFF22: // NR43 (shift) - Audio_Regs[NR43] = value; - NOISE_clk_shft = (byte)((value & 0xF0) >> 4); - NOISE_wdth_md = (value & 8) > 0; - NOISE_div_code = (byte)(value & 7); - break; - case 0xFF23: // NR44 (trigger) - Audio_Regs[NR44] = value; - NOISE_trigger = (value & 0x80) > 0; - - if ((sequencer_len & 1) > 0) - { - if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) - { - NOISE_len_cntr--; - if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } - } - } - - if (NOISE_trigger) - { - NOISE_enable = true; - Audio_Regs[NR52] |= 8; - if (NOISE_len_cntr == 0) - { - NOISE_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } - } - NOISE_intl_cntr = 0; - NOISE_vol_state = NOISE_st_vol; - NOISE_vol_per = 0; - NOISE_LFSR = 0x7FFF; - if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } - } - - NOISE_len_en = (value & 0x40) > 0; - - break; - case 0xFF24: // NR50 (ctrl) - Audio_Regs[NR50] = value; - AUD_CTRL_vin_L_en = (value & 0x80) > 0; - AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); - AUD_CTRL_vin_R_en = (value & 8) > 0; - AUD_CTRL_vol_R = (byte)(value & 7); - break; - case 0xFF25: // NR51 (ctrl) - Audio_Regs[NR51] = value; - AUD_CTRL_noise_L_en = (value & 0x80) > 0; - AUD_CTRL_wave_L_en = (value & 0x40) > 0; - AUD_CTRL_sq2_L_en = (value & 0x20) > 0; - AUD_CTRL_sq1_L_en = (value & 0x10) > 0; - AUD_CTRL_noise_R_en = (value & 8) > 0; - AUD_CTRL_wave_R_en = (value & 4) > 0; - AUD_CTRL_sq2_R_en = (value & 2) > 0; - AUD_CTRL_sq1_R_en = (value & 1) > 0; - break; - case 0xFF26: // NR52 (ctrl) - Audio_Regs[NR52] &= 0x7F; - Audio_Regs[NR52] |= (byte)(value & 0x80); - AUD_CTRL_power = (value & 0x80) > 0; - - if (!AUD_CTRL_power) - { - power_off(); - } - break; - - // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; - } - } - // when power is off, only length counters and waveRAM are effected by writes - else - { - switch (addr) - { - case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1_length = (ushort)(64 - value & 0x3F); - break; - case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2_length = (ushort)(64 - value & 0x3F); - break; - case 0xFF1B: // NR31 (length) - WAVE_length = (ushort)(256 - value); - break; - case 0xFF20: // NR41 (length) - NOISE_length = (ushort)(64 - value & 0x3F); - break; - case 0xFF26: // NR52 (ctrl) - Audio_Regs[NR52] &= 0x7F; - Audio_Regs[NR52] |= (byte)(value & 0x80); - AUD_CTRL_power = (value & 0x80) > 0; - break; - - // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; - } - } - } - - public void tick() - { - // calculate square1's output - if (SQ1_enable) - { - SQ1_intl_cntr++; - if (SQ1_intl_cntr >= (2048 - SQ1_frq_shadow) * 4) - { - SQ1_intl_cntr = 0; - SQ1_duty_cntr++; - SQ1_duty_cntr &= 7; - - SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; - SQ1_output *= SQ1_vol_state; - } - } - - // calculate square2's output - if (SQ2_enable) - { - SQ2_intl_cntr++; - if (SQ2_intl_cntr >= (2048 - SQ2_frq) * 4) - { - SQ2_intl_cntr = 0; - SQ2_duty_cntr++; - SQ2_duty_cntr &= 7; - - SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; - SQ2_output *= SQ2_vol_state; - } - } - - // calculate wave output - if (WAVE_enable) - { - WAVE_intl_cntr++; - if (WAVE_intl_cntr >= (2048 - WAVE_frq) * 2) - { - WAVE_intl_cntr = 0; - WAVE_wave_cntr++; - WAVE_wave_cntr &= 0x1F; - - byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; - - if ((WAVE_wave_cntr & 1) == 0) - { - sample = (byte)(sample >> 4); - } - - if (WAVE_vol_code == 0) - { - sample = (byte)((sample & 0xF) >> 4); - } - else if (WAVE_vol_code == 1) - { - sample = (byte)(sample & 0xF); - } - else if (WAVE_vol_code == 2) - { - sample = (byte)((sample & 0xF) >> 1); - } - else - { - sample = (byte)((sample & 0xF) >> 2); - } - - WAVE_output = sample; - - if (!WAVE_DAC_pow) - { - WAVE_output = 0; - } - } - } - - - // calculate noise output - if (NOISE_enable) - { - NOISE_intl_cntr++; - if (NOISE_intl_cntr >= (DIVISOR[NOISE_div_code] << NOISE_clk_shft)) - { - NOISE_intl_cntr = 0; - int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); - - NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; - NOISE_LFSR |= (bit_lfsr << 14); - - if (NOISE_wdth_md) - { - NOISE_LFSR = NOISE_LFSR & 0x7FBF; - NOISE_LFSR |= (bit_lfsr << 6); - } - - NOISE_output = NOISE_LFSR & 1; - NOISE_output *= NOISE_vol_state; - } - } - - // add up components to each channel - int L_final = 0; - int R_final = 0; - - if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } - if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } - if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } - if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } - - if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } - if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } - if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } - if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } - - L_final *= (AUD_CTRL_vol_L + 1); - R_final *= (AUD_CTRL_vol_R + 1); - - // send out an actual sample every 94 cycles - master_audio_clock++; - if (master_audio_clock == 94) - { - master_audio_clock = 0; - if (AudioClocks < 1500) - { - AudioSamples[AudioClocks] = (short)(L_final * 4); - /* - Console_Write(SQ1_output); - Console_Write(" "); - Console_Write(SQ2_output); - Console_Write(" "); - Console_Write(WAVE_output); - Console_Write(" "); - Console_WriteLine(NOISE_output); - */ - AudioClocks++; - AudioSamples[AudioClocks] = (short)(R_final * 4); - AudioClocks++; - } - } - - // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) - sequencer_tick++; - - if (sequencer_tick==8192) - { - sequencer_tick = 0; - - sequencer_vol++; sequencer_vol &= 0x7; - sequencer_len++; sequencer_len &= 0x7; - sequencer_swp++; sequencer_swp &= 0x7; - - // clock the lengths - if ((sequencer_len & 1) > 0) - { - if (SQ1_len_en && SQ1_len_cntr > 0) - { - SQ1_len_cntr--; - if (SQ1_len_cntr == 0) { SQ1_enable = false; Audio_Regs[NR52] &= 0xFE; } - } - if (SQ2_len_en && SQ2_len_cntr > 0) - { - SQ2_len_cntr--; - if (SQ2_len_cntr == 0) { SQ2_enable = false; Audio_Regs[NR52] &= 0xFD; } - } - if (WAVE_len_en && WAVE_len_cntr > 0) - { - WAVE_len_cntr--; - if (WAVE_len_cntr == 0) { WAVE_enable = false; Audio_Regs[NR52] &= 0xFB; } - } - if (NOISE_len_en && NOISE_len_cntr > 0) - { - NOISE_len_cntr--; - if (NOISE_len_cntr == 0) { NOISE_enable = false; Audio_Regs[NR52] &= 0xF7; } - } - } - - // clock the sweep - if ((sequencer_swp == 3) || (sequencer_swp == 7)) - { - SQ1_intl_swp_cnt++; - if (SQ1_intl_swp_cnt >= SQ1_swp_prd) - { - SQ1_intl_swp_cnt = 0; - - if ((SQ1_swp_prd > 0) && (SQ1_shift > 0)) - { - int shadow_frq = SQ1_frq_shadow; - shadow_frq = shadow_frq >> SQ1_shift; - if (SQ1_negate) { shadow_frq = -shadow_frq; } - shadow_frq += SQ1_frq_shadow; - - // disable channel if overflow - if ((uint)shadow_frq > 2047) - { - SQ1_enable = false; - Audio_Regs[NR52] &= 0xFE; - } + SQ1_swp_enable = true; + } else { - shadow_frq &= 0x7FF; - SQ1_frq = shadow_frq; - SQ1_frq_shadow = shadow_frq; - - // note that we also write back the frequency to the actual register - Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF); - Audio_Regs[NR14] &= 0xF8; - Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7); - - // after writing, we repeat the process and do another overflow check - shadow_frq = SQ1_frq_shadow; + SQ1_swp_enable = false; + } + + if (SQ1_shift > 0) + { + int shadow_frq = SQ1_frq_shadow; shadow_frq = shadow_frq >> SQ1_shift; if (SQ1_negate) { shadow_frq = -shadow_frq; } shadow_frq += SQ1_frq_shadow; + // disable channel if overflow if ((uint)shadow_frq > 2047) { - SQ1_enable = false; - Audio_Regs[NR52] &= 0xFE; + SQ1_enable = SQ1_swp_enable = false; } } + + if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; } } - } + + SQ1_len_en = (value & 0x40) > 0; + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + Audio_Regs[NR21] = value; + SQ2_duty = (byte)((value & 0xC0) >> 6); + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + break; + case 0xFF17: // NR22 (envelope) + Audio_Regs[NR22] = value; + SQ2_st_vol = (byte)((value & 0xF0) >> 4); + SQ2_env_add = (value & 8) > 0; + SQ2_per = (byte)(value & 7); + //if (SQ2_per == 0) { SQ2_per = 8; } + if ((value & 0xF8) == 0) { SQ2_enable = false; } + break; + case 0xFF18: // NR23 (freq low) + Audio_Regs[NR23] = value; + SQ2_frq &= 0x700; + SQ2_frq |= value; + break; + case 0xFF19: // NR24 (freq hi) + Audio_Regs[NR24] = value; + SQ2_trigger = (value & 0x80) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) + { + SQ2_len_cntr--; + if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; } + } + } + + if (SQ2_trigger) + { + SQ2_enable = true; + + if (SQ2_len_cntr == 0) + { + SQ2_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } + } + SQ2_intl_cntr = 0; + SQ2_vol_state = SQ2_st_vol; + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; } + } + + SQ2_len_en = (value & 0x40) > 0; + + break; + case 0xFF1A: // NR30 (on/off) + Audio_Regs[NR30] = value; + WAVE_DAC_pow = (value & 0x80) > 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + break; + case 0xFF1B: // NR31 (length) + Audio_Regs[NR31] = value; + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + break; + case 0xFF1C: // NR32 (level output) + Audio_Regs[NR32] = value; + WAVE_vol_code = (byte)((value & 0x60) >> 5); + break; + case 0xFF1D: // NR33 (freq low) + Audio_Regs[NR33] = value; + WAVE_frq &= 0x700; + WAVE_frq |= value; + break; + case 0xFF1E: // NR34 (freq hi) + Audio_Regs[NR34] = value; + WAVE_trigger = (value & 0x80) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) + { + WAVE_len_cntr--; + if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; } + } + } + + if (WAVE_trigger) + { + WAVE_enable = true; + + if (WAVE_len_cntr == 0) + { + WAVE_len_cntr = 256; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } + } + WAVE_intl_cntr = 0; + WAVE_wave_cntr = 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + } + + WAVE_len_en = (value & 0x40) > 0; + + break; + case 0xFF20: // NR41 (length) + Audio_Regs[NR41] = value; + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + break; + case 0xFF21: // NR42 (envelope) + Audio_Regs[NR42] = value; + NOISE_st_vol = (byte)((value & 0xF0) >> 4); + NOISE_env_add = (value & 8) > 0; + NOISE_per = (byte)(value & 7); + //if (NOISE_per == 0) { NOISE_per = 8; } + if ((value & 0xF8) == 0) { NOISE_enable = false; } + break; + case 0xFF22: // NR43 (shift) + Audio_Regs[NR43] = value; + NOISE_clk_shft = (byte)((value & 0xF0) >> 4); + NOISE_wdth_md = (value & 8) > 0; + NOISE_div_code = (byte)(value & 7); + break; + case 0xFF23: // NR44 (trigger) + Audio_Regs[NR44] = value; + NOISE_trigger = (value & 0x80) > 0; + + if ((sequencer_len & 1) > 0) + { + if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) + { + NOISE_len_cntr--; + if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; } + } + } + + if (NOISE_trigger) + { + NOISE_enable = true; + + if (NOISE_len_cntr == 0) + { + NOISE_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } + } + NOISE_intl_cntr = 0; + NOISE_vol_state = NOISE_st_vol; + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + NOISE_LFSR = 0x7FFF; + if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; } + } + + NOISE_len_en = (value & 0x40) > 0; + break; + case 0xFF24: // NR50 (ctrl) + Audio_Regs[NR50] = value; + AUD_CTRL_vin_L_en = (value & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); + AUD_CTRL_vin_R_en = (value & 8) > 0; + AUD_CTRL_vol_R = (byte)(value & 7); + break; + case 0xFF25: // NR51 (ctrl) + Audio_Regs[NR51] = value; + AUD_CTRL_noise_L_en = (value & 0x80) > 0; + AUD_CTRL_wave_L_en = (value & 0x40) > 0; + AUD_CTRL_sq2_L_en = (value & 0x20) > 0; + AUD_CTRL_sq1_L_en = (value & 0x10) > 0; + AUD_CTRL_noise_R_en = (value & 8) > 0; + AUD_CTRL_wave_R_en = (value & 4) > 0; + AUD_CTRL_sq2_R_en = (value & 2) > 0; + AUD_CTRL_sq1_R_en = (value & 1) > 0; + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; + + if (!AUD_CTRL_power) { power_off(); } + break; + + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + } + } + // when power is off, only length counters and waveRAM are effected by writes + else + { + switch (addr) + { + case 0xFF11: // NR11 (sound length / wave pattern duty %) + SQ1_length = (ushort)(64 - value & 0x3F); + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + SQ2_length = (ushort)(64 - value & 0x3F); + break; + case 0xFF1B: // NR31 (length) + WAVE_length = (ushort)(256 - value); + break; + case 0xFF20: // NR41 (length) + NOISE_length = (ushort)(64 - value & 0x3F); + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; + break; + + // wave ram table + 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: + Wave_RAM[addr & 0x0F] = value; + break; + } + } + } + + public void tick() + { + // calculate square1's output + if (SQ1_enable) + { + SQ1_intl_cntr++; + if (SQ1_intl_cntr >= (2048 - SQ1_frq_shadow) * 4) + { + SQ1_intl_cntr = 0; + SQ1_duty_cntr++; + SQ1_duty_cntr &= 7; + + SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; + SQ1_output *= SQ1_vol_state; + } + } + + // calculate square2's output + if (SQ2_enable) + { + SQ2_intl_cntr++; + if (SQ2_intl_cntr >= (2048 - SQ2_frq) * 4) + { + SQ2_intl_cntr = 0; + SQ2_duty_cntr++; + SQ2_duty_cntr &= 7; + + SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; + SQ2_output *= SQ2_vol_state; + } + } + + // calculate wave output + if (WAVE_enable) + { + WAVE_intl_cntr++; + if (WAVE_intl_cntr >= (2048 - WAVE_frq) * 2) + { + WAVE_intl_cntr = 0; + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; + + byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; + + if ((WAVE_wave_cntr & 1) == 0) + { + sample = (byte)(sample >> 4); + } + + if (WAVE_vol_code == 0) + { + sample = (byte)((sample & 0xF) >> 4); + } + else if (WAVE_vol_code == 1) + { + sample = (byte)(sample & 0xF); + } + else if (WAVE_vol_code == 2) + { + sample = (byte)((sample & 0xF) >> 1); + } + else + { + sample = (byte)((sample & 0xF) >> 2); + } + + WAVE_output = sample; + + if (!WAVE_DAC_pow) { WAVE_output = 0; } + } + } + + + // calculate noise output + if (NOISE_enable) + { + NOISE_intl_cntr++; + if (NOISE_intl_cntr >= (DIVISOR[NOISE_div_code] << NOISE_clk_shft)) + { + NOISE_intl_cntr = 0; + int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); + + NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; + NOISE_LFSR |= (bit_lfsr << 14); + + if (NOISE_wdth_md) + { + NOISE_LFSR = NOISE_LFSR & 0x7FBF; + NOISE_LFSR |= (bit_lfsr << 6); + } + + NOISE_output = NOISE_LFSR & 1; + NOISE_output *= NOISE_vol_state; + } + } + + // add up components to each channel + int L_final = 0; + int R_final = 0; + + if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } + if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } + if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } + if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } + + if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } + if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } + if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } + if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } + + L_final *= (AUD_CTRL_vol_L + 1); + R_final *= (AUD_CTRL_vol_R + 1); + + // send out an actual sample every 94 cycles + master_audio_clock++; + if (master_audio_clock == 94) + { + master_audio_clock = 0; + if (AudioClocks < 1500) + { + AudioSamples[AudioClocks] = (short)(L_final * 4); + /* + Console_Write(SQ1_output); + Console_Write(" "); + Console_Write(SQ2_output); + Console_Write(" "); + Console_Write(WAVE_output); + Console_Write(" "); + Console_WriteLine(NOISE_output); + */ + AudioClocks++; + AudioSamples[AudioClocks] = (short)(R_final * 4); + AudioClocks++; + } + } + + // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) + sequencer_tick++; + + if (sequencer_tick == 8192) + { + sequencer_tick = 0; + + sequencer_vol++; sequencer_vol &= 0x7; + sequencer_len++; sequencer_len &= 0x7; + sequencer_swp++; sequencer_swp &= 0x7; + + // clock the lengths + if ((sequencer_len & 1) > 0) + { + if (SQ1_len_en && SQ1_len_cntr > 0) + { + SQ1_len_cntr--; + if (SQ1_len_cntr == 0) { SQ1_enable = SQ1_swp_enable = false; } + } + if (SQ2_len_en && SQ2_len_cntr > 0) + { + SQ2_len_cntr--; + if (SQ2_len_cntr == 0) { SQ2_enable = false; } + } + if (WAVE_len_en && WAVE_len_cntr > 0) + { + WAVE_len_cntr--; + if (WAVE_len_cntr == 0) { WAVE_enable = false; } + } + if (NOISE_len_en && NOISE_len_cntr > 0) + { + NOISE_len_cntr--; + if (NOISE_len_cntr == 0) { NOISE_enable = false; } + } + } + + // clock the sweep + if ((sequencer_swp == 3) || (sequencer_swp == 7)) + { + SQ1_intl_swp_cnt--; + if ((SQ1_intl_swp_cnt == 0) && SQ1_swp_enable) + { + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + + if ((SQ1_swp_prd > 0)) + { + int shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + // disable channel if overflow + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + else + { + if (SQ1_shift > 0) + { + shadow_frq &= 0x7FF; + SQ1_frq = shadow_frq; + SQ1_frq_shadow = shadow_frq; + + // note that we also write back the frequency to the actual register + Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF); + Audio_Regs[NR14] &= 0xF8; + Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7); + + // after writing, we repeat the process and do another overflow check + shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + } + } + } + } } // clock the volume envelope - if (sequencer_vol == 0) - { - if (SQ1_per > 0) - { - SQ1_vol_per++; - if (SQ1_vol_per >= SQ1_per) - { - SQ1_vol_per = 0; - if (SQ1_env_add && (SQ1_vol_state < 15)) { SQ1_vol_state++; } - else if (SQ1_vol_state > 0) { SQ1_vol_state--; } - } - } - if (SQ2_per > 0) - { - SQ2_vol_per++; - if (SQ2_vol_per >= SQ2_per) - { - SQ2_vol_per = 0; - if (SQ2_env_add && (SQ2_vol_state < 15)) { SQ2_vol_state++; } - else if (SQ2_vol_state > 0) { SQ2_vol_state--; } - } - } - if (NOISE_per > 0) - { - NOISE_vol_per++; - if (NOISE_vol_per >= NOISE_per) - { - NOISE_vol_per = 0; - if (NOISE_env_add && (NOISE_vol_state < 15)) { NOISE_vol_state++; } - else if (NOISE_vol_state > 0) { NOISE_vol_state--; } - } - } - } - } - } - - public void power_off() - { - for (int i = 0; i < 21; i++) - { - Audio_Regs[i] = 0; - } - SQ1_enable = false; - SQ2_enable = false; - WAVE_enable = false; - NOISE_enable = false; - - sequencer_len = 0; - sequencer_vol = 0; - sequencer_swp = 0; - sequencer_tick = 0; - master_audio_clock = 0; - } - public void Reset() - { - Wave_RAM = new byte[16]; - - Audio_Regs = new byte[21]; - - AudioClocks = 0; - master_audio_clock = 0; - - sequencer_len = 0; - sequencer_swp = 0; - sequencer_vol = 0; - sequencer_tick = 0; - } - - public void SyncState(Serializer ser) - { - ser.Sync("Audio_Regs", ref Audio_Regs, false); + if (sequencer_vol == 0) + { + if (SQ1_enable) + { + SQ1_vol_per--; + if (SQ1_vol_per == 0) + { + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + if (SQ1_env_add && (SQ1_vol_state < 15)) { SQ1_vol_state++; } + else if (SQ1_vol_state > 0) { SQ1_vol_state--; } + } + } + if (SQ2_enable) + { + SQ2_vol_per--; + if (SQ2_vol_per == 0) + { + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + if (SQ2_env_add && (SQ2_vol_state < 15)) { SQ2_vol_state++; } + else if (SQ2_vol_state > 0) { SQ2_vol_state--; } + } + } + if (NOISE_enable) + { + NOISE_vol_per--; + if (NOISE_vol_per == 0) + { + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + if (NOISE_env_add && (NOISE_vol_state < 15)) { NOISE_vol_state++; } + else if (NOISE_vol_state > 0) { NOISE_vol_state--; } + } + } + } + } + } + + public void power_off() + { + for (int i = 0; i < 21; i++) + { + Audio_Regs[i] = 0; + } + SQ1_enable = SQ1_swp_enable = false; + SQ2_enable = false; + WAVE_enable = false; + NOISE_enable = false; + + sequencer_len = 0; + sequencer_vol = 0; + sequencer_swp = 0; + sequencer_tick = 0; + master_audio_clock = 0; + } + + public void Reset() + { + Wave_RAM = new byte[16]; + + Audio_Regs = new byte[21]; + + AudioClocks = 0; + master_audio_clock = 0; + + sequencer_len = 0; + sequencer_swp = 0; + sequencer_vol = 0; + sequencer_tick = 0; + } + + public void SyncState(Serializer ser) + { + ser.Sync("Audio_Regs", ref Audio_Regs, false); ser.Sync("Wave_Ram", ref Wave_RAM, false); // save state variables - ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); - ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); - ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); - ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); - ser.Sync("SQ1_enable", ref SQ1_enable); - ser.Sync("SQ2_enable", ref SQ2_enable); - ser.Sync("WAVE_enable", ref WAVE_enable); - ser.Sync("NOISE_enable", ref NOISE_enable); - ser.Sync("SQ1_vol_state", ref SQ1_vol_state); - ser.Sync("SQ2_vol_state", ref SQ2_vol_state); - ser.Sync("NOISE_vol_state", ref NOISE_vol_state); - ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); - ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); - ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); - ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); - ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); - ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); - ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); - ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); - ser.Sync("SQ1_vol_per", ref SQ1_vol_per); - ser.Sync("SQ2_vol_per", ref SQ2_vol_per); - ser.Sync("NOISE_vol_per", ref NOISE_vol_per); - ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); - ser.Sync("NOISE_LFSR", ref NOISE_LFSR); - ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); - ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); - ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); - ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); - - - ser.Sync("sequencer_len", ref sequencer_len); - ser.Sync("sequencer_vol", ref sequencer_vol); - ser.Sync("sequencer_swp", ref sequencer_swp); - ser.Sync("sequencer_tick", ref sequencer_tick); - + ser.Sync("SQ1_swp_enable", ref SQ1_swp_enable); + ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); + ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); + ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); + ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); + ser.Sync("SQ1_enable", ref SQ1_enable); + ser.Sync("SQ2_enable", ref SQ2_enable); + ser.Sync("WAVE_enable", ref WAVE_enable); + ser.Sync("NOISE_enable", ref NOISE_enable); + ser.Sync("SQ1_vol_state", ref SQ1_vol_state); + ser.Sync("SQ2_vol_state", ref SQ2_vol_state); + ser.Sync("NOISE_vol_state", ref NOISE_vol_state); + ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); + ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); + ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); + ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); + ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); + ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); + ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); + ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); + ser.Sync("SQ1_vol_per", ref SQ1_vol_per); + ser.Sync("SQ2_vol_per", ref SQ2_vol_per); + ser.Sync("NOISE_vol_per", ref NOISE_vol_per); + ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); + ser.Sync("NOISE_LFSR", ref NOISE_LFSR); + ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); + ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); + ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); + ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); + + + ser.Sync("sequencer_len", ref sequencer_len); + ser.Sync("sequencer_vol", ref sequencer_vol); + ser.Sync("sequencer_swp", ref sequencer_swp); + ser.Sync("sequencer_tick", ref sequencer_tick); + ser.Sync("master_audio_clock", ref master_audio_clock); // get derived state - if (ser.IsReader) - { - sync_channels(); - } - } - - public void sync_channels() - { - - SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); - SQ1_negate = (Audio_Regs[NR10] & 8) > 0; - SQ1_shift = (byte)(Audio_Regs[NR10] & 7); - - SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); - SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); - - SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); - SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; - SQ1_per = (byte)(Audio_Regs[NR12] & 7); - - SQ1_frq &= 0x700; - SQ1_frq |= Audio_Regs[NR13]; - - SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; - SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; - SQ1_frq &= 0xFF; + if (ser.IsReader) + { + sync_channels(); + } + } + + public void sync_channels() + { + + SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); + SQ1_negate = (Audio_Regs[NR10] & 8) > 0; + SQ1_shift = (byte)(Audio_Regs[NR10] & 7); + + SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); + SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); + + SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); + SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; + SQ1_per = (byte)(Audio_Regs[NR12] & 7); + + SQ1_frq &= 0x700; + SQ1_frq |= Audio_Regs[NR13]; + + SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; + SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; + SQ1_frq &= 0xFF; SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); - - SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); - SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); - SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); - SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; - SQ2_per = (byte)(Audio_Regs[NR22] & 7); - - SQ2_frq &= 0x700; - SQ2_frq |= Audio_Regs[NR23]; - - SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; - SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; - SQ2_frq &= 0xFF; - SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); - - WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; - - WAVE_length = (ushort)(256 - Audio_Regs[NR31]); - - WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); - - WAVE_frq &= 0x700; - WAVE_frq |= Audio_Regs[NR33]; - - WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; - WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; - WAVE_frq &= 0xFF; - WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); - - NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); - - NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); - NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; - NOISE_per = (byte)(Audio_Regs[NR42] & 7); - - NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); - NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; - NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); - - WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; - WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; - - AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; - AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); - AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; - AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); - - AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; - AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; - AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; - AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; - AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; - AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; - AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; - AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; - - AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; + SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); + SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); + + SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); + SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; + SQ2_per = (byte)(Audio_Regs[NR22] & 7); + + SQ2_frq &= 0x700; + SQ2_frq |= Audio_Regs[NR23]; + + SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; + SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); + + WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; + + WAVE_length = (ushort)(256 - Audio_Regs[NR31]); + + WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); + + WAVE_frq &= 0x700; + WAVE_frq |= Audio_Regs[NR33]; + + WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); + + NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); + + NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); + NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; + NOISE_per = (byte)(Audio_Regs[NR42] & 7); + + NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); + NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; + NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); + + WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; + + AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); + AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; + AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); + + AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; + AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; + AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; + AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; + AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; + AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; + AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; + + AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; } - #region audio - - public bool CanProvideAsync => false; - - public int AudioClocks; - public short[] AudioSamples = new short[1500]; - - public void SetSyncMode(SyncSoundMode mode) + public byte Read_NR52() { - if (mode != SyncSoundMode.Sync) - { - throw new InvalidOperationException("Only Sync mode is supported_"); - } + return (byte)( + ((AUD_CTRL_power ? 1 : 0) << 7) | + ((SQ1_enable ? 1 : 0)) | + ((SQ2_enable ? 1 : 0) << 1) | + ((WAVE_enable ? 1 : 0) << 2) | + ((NOISE_enable ? 1 : 0) << 3)); } - public SyncSoundMode SyncMode => SyncSoundMode.Sync; + #region audio - public void GetSamplesSync(out short[] samples, out int nsamp) - { - nsamp = AudioClocks / 2; - short[] temp_samp = new short[AudioClocks]; - - for (int i = 0; i < AudioClocks; i++) - { - temp_samp[i] = AudioSamples[i]; - } - - samples = temp_samp; - - AudioClocks = 0; + public bool CanProvideAsync => false; + + public int AudioClocks; + public short[] AudioSamples = new short[1500]; + + 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) + { + nsamp = AudioClocks / 2; + short[] temp_samp = new short[AudioClocks]; + + for (int i = 0; i < AudioClocks; i++) + { + temp_samp[i] = AudioSamples[i]; + } + + samples = temp_samp; + + AudioClocks = 0; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + AudioClocks = 0; + } + + private void GetSamples(short[] samples) + { + } - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - AudioClocks = 0; - } - - private void GetSamples(short[] samples) - { - - } - - #endregion - } + #endregion + } } \ No newline at end of file From 2d7501b2031dd5e454fbcc1a37d66f8e74c69e52 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 18 Nov 2017 18:02:04 -0500 Subject: [PATCH 23/28] GBHawk: More audio work, pass more tests --- .../Consoles/Nintendo/GBHawk/Audio.cs | 99 ++++++++++++++++--- .../Consoles/Nintendo/GBHawk/PPU.cs | 6 +- 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index d5112c1c9b..04310f2455 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -72,7 +72,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int SQ1_frq, SQ2_frq, WAVE_frq; public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; // state + public bool SQ1_calc_done; public bool SQ1_swp_enable; + public bool SQ1_vol_done, SQ2_vol_done, NOISE_vol_done; public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; public byte SQ1_duty_cntr, SQ2_duty_cntr; @@ -169,6 +171,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1_swp_prd = (byte)((value & 0x70) >> 4); SQ1_negate = (value & 8) > 0; SQ1_shift = (byte)(value & 7); + + if (!SQ1_negate && SQ1_calc_done) { SQ1_enable = false; } break; case 0xFF11: // NR11 (sound length / wave pattern duty %) Audio_Regs[NR11] = value; @@ -207,6 +211,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ1_trigger) { SQ1_enable = true; + SQ1_vol_done = false; if (SQ1_len_cntr == 0) { SQ1_len_cntr = 64; @@ -218,6 +223,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1_frq_shadow = SQ1_frq; SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + SQ1_calc_done = false; if ((SQ1_shift > 0) || (SQ1_swp_prd > 0)) { @@ -240,6 +246,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { SQ1_enable = SQ1_swp_enable = false; } + + // set negate mode flag that disables channel is negate clerar + if (SQ1_negate) { SQ1_calc_done = true; } } if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; } @@ -284,6 +293,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ2_trigger) { SQ2_enable = true; + SQ2_vol_done = false; if (SQ2_len_cntr == 0) { @@ -385,6 +395,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (NOISE_trigger) { NOISE_enable = true; + NOISE_vol_done = false; if (NOISE_len_cntr == 0) { @@ -451,19 +462,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk switch (addr) { case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1_length = (ushort)(64 - value & 0x3F); + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2_length = (ushort)(64 - value & 0x3F); + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; break; case 0xFF1B: // NR31 (length) WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; break; case 0xFF20: // NR41 (length) - NOISE_length = (ushort)(64 - value & 0x3F); + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; break; case 0xFF26: // NR52 (ctrl) AUD_CTRL_power = (value & 0x80) > 0; + if (AUD_CTRL_power) + { + sequencer_vol = 0; + sequencer_len = 0; + sequencer_swp = 0; + } break; // wave ram table @@ -676,6 +697,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ1_negate) { shadow_frq = -shadow_frq; } shadow_frq += SQ1_frq_shadow; + // set negate mode flag that disables channel is negate clerar + if (SQ1_negate) { SQ1_calc_done = true; } + // disable channel if overflow if ((uint)shadow_frq > 2047) { @@ -713,34 +737,67 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // clock the volume envelope if (sequencer_vol == 0) { - if (SQ1_enable) + if (SQ1_per > 0) { SQ1_vol_per--; if (SQ1_vol_per == 0) { SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; - if (SQ1_env_add && (SQ1_vol_state < 15)) { SQ1_vol_state++; } - else if (SQ1_vol_state > 0) { SQ1_vol_state--; } + if (!SQ1_vol_done) + { + if (SQ1_env_add) + { + if (SQ1_vol_state < 15) { SQ1_vol_state++; } + else { SQ1_vol_done = true; } + } + else + { + if (SQ1_vol_state >= 1) { SQ1_vol_state--; } + else { SQ1_vol_done = true; } + } + } } } - if (SQ2_enable) + if (SQ2_per > 0) { SQ2_vol_per--; if (SQ2_vol_per == 0) { SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; - if (SQ2_env_add && (SQ2_vol_state < 15)) { SQ2_vol_state++; } - else if (SQ2_vol_state > 0) { SQ2_vol_state--; } + if (!SQ2_vol_done) + { + if (SQ2_env_add) + { + if (SQ2_vol_state < 15) { SQ2_vol_state++; } + else { SQ2_vol_done = true; } + } + else + { + if (SQ2_vol_state >= 1) { SQ2_vol_state--; } + else { SQ2_vol_done = true; } + } + } } } - if (NOISE_enable) + if (NOISE_per > 0) { NOISE_vol_per--; if (NOISE_vol_per == 0) { NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; - if (NOISE_env_add && (NOISE_vol_state < 15)) { NOISE_vol_state++; } - else if (NOISE_vol_state > 0) { NOISE_vol_state--; } + if (!NOISE_vol_done) + { + if (NOISE_env_add) + { + if (NOISE_vol_state < 15) { NOISE_vol_state++; } + else { NOISE_vol_done = true; } + } + else + { + if (NOISE_vol_state >= 1) { NOISE_vol_state--; } + else { NOISE_vol_done = true; } + } + } } } } @@ -752,16 +809,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk for (int i = 0; i < 21; i++) { Audio_Regs[i] = 0; - } + } + + // reset derived values + sync_channels(); + + // reset state variables SQ1_enable = SQ1_swp_enable = false; SQ2_enable = false; WAVE_enable = false; NOISE_enable = false; + SQ1_len_en = false; + SQ2_len_en = false; + WAVE_len_en = false; + NOISE_len_en = false; + sequencer_len = 0; sequencer_vol = 0; sequencer_swp = 0; - sequencer_tick = 0; + master_audio_clock = 0; } @@ -786,6 +853,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("Wave_Ram", ref Wave_RAM, false); // save state variables + ser.Sync("SQ1_vol_done", ref SQ1_vol_done); + ser.Sync("SQ2_vol_done", ref SQ2_vol_done); + ser.Sync("NOISE_vol_done", ref NOISE_vol_done); + ser.Sync("SQ1_calc_done", ref SQ1_calc_done); ser.Sync("SQ1_swp_enable", ref SQ1_swp_enable); ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 8bc8cee50d..edf4110745 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -102,7 +102,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF43: ret = scroll_x; break; // SCX case 0xFF44: ret = LY; break; // LY case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: /*ret = DMA_addr; */ break; // DMA (not readable?) + case 0xFF46: ret = 0xFF; break; // DMA (not readable?) /*ret = DMA_addr; */ case 0xFF47: ret = BGP; break; // BGP case 0xFF48: ret = obj_pal_0; break; // OBP0 case 0xFF49: ret = obj_pal_1; break; // OBP1 @@ -1030,8 +1030,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LYC = 0; DMA_addr = 0; BGP = 0; - obj_pal_0 = 0; - obj_pal_1 = 0; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; window_y = 0; window_x = 0; LY_inc = 1; From 087586bc4910d9d263b7014a083eeb339ec600a0 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 18 Nov 2017 19:45:11 -0500 Subject: [PATCH 24/28] GBHawk: pass new IRQ test --- .../CPUs/LR35902/Interrupts.cs | 10 +- .../CPUs/LR35902/LR35902.cs | 98 ++++++++++++++----- 2 files changed, 79 insertions(+), 29 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index ec93ae031e..24685ad908 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -27,7 +27,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } } - private void INTERRUPT_(ushort src) + private void INTERRUPT_() { cur_instr = new ushort[] {IDLE, @@ -40,19 +40,21 @@ namespace BizHawk.Emulation.Common.Components.LR35902 DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCh, - IDLE, + INT_GET, // NOTE: here is where we check for a cancelled IRQ DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCl, IDLE, - ASGN, PCl, INT_vectors[src], IDLE, + TR, PCl, W, ASGN, PCh, 0, IDLE, OP }; } - private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; + private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60, 0x00}; + + public ushort int_src; private void ResetInterrupts() { diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index f1aa8bf317..6ca017c6d8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -56,6 +56,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort JAM = 41; // all undocumented opcodes jam the machine public const ushort RD_F = 42; // special read case to pop value into F public const ushort EI_RETI = 43; // reti has no delay in interrupt enable + public const ushort INT_GET = 44; public LR35902() { @@ -134,20 +135,20 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } - // call interrupt processor with the appropriate source + // call interrupt processor // lowest bit set is highest priority - ushort priority = 0; - - if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { priority = 0; interrupt_src -= 1; } - else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { priority = 1; interrupt_src -= 2; } - else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { priority = 2; interrupt_src -= 4; } - else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { priority = 3; interrupt_src -= 8; } - else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { priority = 4; interrupt_src -= 16; } - else { /*Console.WriteLine("No source"); }*/throw new Exception("Interrupt without Source"); } + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; } + else { /*Console.WriteLine("No source"); }*/ throw new Exception("Interrupt without Source"); } + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } - INTERRUPT_(priority); + INTERRUPT_(); } else { @@ -284,22 +285,20 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - // call interrupt processor with the appropriate source - // lowest bit set is highest priority - // call interrupt processor with the appropriate source - // lowest bit set is highest priority - ushort priority = 0; - - if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { priority = 0; interrupt_src -= 1; } - else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { priority = 1; interrupt_src -= 2; } - else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { priority = 2; interrupt_src -= 4; } - else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { priority = 3; interrupt_src -= 8; } - else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { priority = 4; interrupt_src -= 16; } - else { /*Console.WriteLine("No source"); }*/throw new Exception("Interrupt without Source"); } - - if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + // call interrupt processor instr_pntr = 0; - INTERRUPT_(priority); + // lowest bit set is highest priority + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; } + else { /*Console.WriteLine("No source"); } */throw new Exception("Interrupt without Source"); } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + INTERRUPT_(); } else if (FlagI) { @@ -384,6 +383,54 @@ namespace BizHawk.Emulation.Common.Components.LR35902 break; case EI_RETI: EI_pending = 1; + break; + case INT_GET: + // check if any interrupts got cancelled along the way + // interrupt src = 5 sets the PC to zero as observed + + Console.WriteLine(int_src); + + if (int_src == 0) + { + if (interrupt_enable.Bit(0)) { interrupt_src -= 1; } + else { int_src = 5; } + } + if (int_src == 1) + { + if (interrupt_enable.Bit(1)) { interrupt_src -= 2; } + else { int_src = 5; } + } + if (int_src == 2) + { + if (interrupt_enable.Bit(2)) { interrupt_src -= 4; } + else { int_src = 5; } + } + if (int_src == 3) + { + if (interrupt_enable.Bit(3)) { interrupt_src -= 8; } + else { int_src = 5; } + } + if (int_src == 4) + { + if (interrupt_enable.Bit(4)) { interrupt_src -= 16; } + else { int_src = 5; } + } + + // if we lost the interrupt, find the next highest interrupt, if any + if (int_src == 5) + { + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; interrupt_src -= 1; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; interrupt_src -= 2; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; interrupt_src -= 4; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; interrupt_src -= 8; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; interrupt_src -= 16; } + else { int_src = 5; } + } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + Regs[W] = INT_vectors[int_src]; + break; } totalExecutedCycles++; @@ -443,6 +490,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.Sync("Halted", ref halted); ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); + ser.Sync("int_src", ref int_src); ser.Sync("instruction_pointer", ref instr_pntr); ser.Sync("current instruction", ref cur_instr, false); From 26b1a2dff0b0e58c01293d5369d5669cbe87c623 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 19 Nov 2017 09:30:18 -0500 Subject: [PATCH 25/28] GBHawk: Pass all sound tests --- .../CPUs/LR35902/Interrupts.cs | 8 +- .../CPUs/LR35902/LR35902.cs | 6 +- .../Consoles/Nintendo/GBHawk/Audio.cs | 1659 +++++++++-------- 3 files changed, 848 insertions(+), 825 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 24685ad908..0dba320f46 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -35,17 +35,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, - IDLE, DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCh, - INT_GET, // NOTE: here is where we check for a cancelled IRQ - DEC16, SPl, SPh, IDLE, + INT_GET, W,// NOTE: here is where we check for a cancelled IRQ + DEC16, SPl, SPh, WR, SPl, SPh, PCl, IDLE, IDLE, + IDLE, + IDLE, TR, PCl, W, ASGN, PCh, 0, IDLE, diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 6ca017c6d8..401f1fe397 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -387,9 +387,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case INT_GET: // check if any interrupts got cancelled along the way // interrupt src = 5 sets the PC to zero as observed - - Console.WriteLine(int_src); - if (int_src == 0) { if (interrupt_enable.Bit(0)) { interrupt_src -= 1; } @@ -429,8 +426,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } - Regs[W] = INT_vectors[int_src]; - + Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; break; } totalExecutedCycles++; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 04310f2455..5d3cbe082c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -1,117 +1,118 @@ -using System; - -using BizHawk.Common; -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; - -namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { // Audio Emulation - public class Audio : ISoundProvider - { - public GBHawk Core { get; set; } - - public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 0, 0, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 0}; - - public static int[] DIVISOR = new int[] { 8, 16, 32, 48, 64, 80, 96, 112 }; - - - 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, - 0x3F, 0x00, 0xFF, 0xBF, - 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, - 0xFF, 0x00, 0x00, 0xBF, - 0x00, 0x00, 0x70}; - - public byte[] Audio_Regs = new byte[21]; - + public class Audio : ISoundProvider + { + public GBHawk Core { get; set; } + + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 0}; + + public static int[] DIVISOR = new int[] { 8, 16, 32, 48, 64, 80, 96, 112 }; + + + 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 static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, + 0x3F, 0x00, 0xFF, 0xBF, + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, + 0xFF, 0x00, 0x00, 0xBF, + 0x00, 0x00, 0x70}; + + public byte[] Audio_Regs = new byte[21]; + public byte[] Wave_RAM = new byte[16]; // Audio Variables // derived - public bool WAVE_DAC_pow; - public bool NOISE_wdth_md; - public bool SQ1_negate; - public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; - public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; - public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; - public byte WAVE_vol_code; - public byte NOISE_clk_shft; - public byte NOISE_div_code; - public byte SQ1_shift; + public bool WAVE_DAC_pow; + public bool NOISE_wdth_md; + public bool SQ1_negate; + public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; + public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; + public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; + public byte WAVE_vol_code; + public byte NOISE_clk_shft; + public byte NOISE_div_code; + public byte SQ1_shift; public byte SQ1_duty, SQ2_duty; public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol; - public byte SQ1_per, SQ2_per, NOISE_per; - public byte SQ1_swp_prd; - public int SQ1_frq, SQ2_frq, WAVE_frq; + public byte SQ1_per, SQ2_per, NOISE_per; + public byte SQ1_swp_prd; + public int SQ1_frq, SQ2_frq, WAVE_frq; public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; // state + public bool WAVE_can_get; public bool SQ1_calc_done; public bool SQ1_swp_enable; public bool SQ1_vol_done, SQ2_vol_done, NOISE_vol_done; - public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; - public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; - public byte SQ1_duty_cntr, SQ2_duty_cntr; - public byte WAVE_wave_cntr; - public int SQ1_frq_shadow; + public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; + public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; + public byte SQ1_duty_cntr, SQ2_duty_cntr; + public byte WAVE_wave_cntr; + public int SQ1_frq_shadow; public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr; - public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; - public int SQ1_intl_swp_cnt; - public int NOISE_LFSR; + public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; + public int SQ1_intl_swp_cnt; + public int NOISE_LFSR; public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr; // computed public int SQ1_output, SQ2_output, WAVE_output, NOISE_output; // 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 int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; - - public int master_audio_clock; - - public byte ReadReg(int addr) - { - byte ret = 0; - - switch (addr) + 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 int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + + public int master_audio_clock; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) { case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) @@ -136,104 +137,110 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF26: ret = (byte)(Read_NR52() | unused_bits[NR52]); break; // NR52 (ctrl) // wave ram table - 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 = Wave_RAM[addr & 0x0F]; - break; - } - - return ret; - } - - public void WriteReg(int addr, byte value) + 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: + if (WAVE_enable) + { + if (WAVE_can_get) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } + else { ret = 0xFF; } + } + else { ret = Wave_RAM[addr & 0x0F]; } + + break; + } + + return ret; + } + + public void WriteReg(int addr, byte value) { // while power is on, everything is writable - if (AUD_CTRL_power) - { - switch (addr) - { + if (AUD_CTRL_power) + { + switch (addr) + { case 0xFF10: // NR10 (sweep) - Audio_Regs[NR10] = value; - SQ1_swp_prd = (byte)((value & 0x70) >> 4); - SQ1_negate = (value & 8) > 0; - SQ1_shift = (byte)(value & 7); - - if (!SQ1_negate && SQ1_calc_done) { SQ1_enable = false; } - break; + Audio_Regs[NR10] = value; + SQ1_swp_prd = (byte)((value & 0x70) >> 4); + SQ1_negate = (value & 8) > 0; + SQ1_shift = (byte)(value & 7); + + if (!SQ1_negate && SQ1_calc_done) { SQ1_enable = false; } + break; case 0xFF11: // NR11 (sound length / wave pattern duty %) - Audio_Regs[NR11] = value; - SQ1_duty = (byte)((value & 0xC0) >> 6); - SQ1_length = (ushort)(64 - (value & 0x3F)); - SQ1_len_cntr = SQ1_length; - break; + Audio_Regs[NR11] = value; + SQ1_duty = (byte)((value & 0xC0) >> 6); + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + break; case 0xFF12: // NR12 (envelope) - Audio_Regs[NR12] = value; - SQ1_st_vol = (byte)((value & 0xF0) >> 4); - SQ1_env_add = (value & 8) > 0; - SQ1_per = (byte)(value & 7); - if (SQ1_per == 0) { SQ1_per = 8; } - if ((value & 0xF8) == 0) { SQ1_enable = SQ1_swp_enable = false; } - break; + Audio_Regs[NR12] = value; + SQ1_st_vol = (byte)((value & 0xF0) >> 4); + SQ1_env_add = (value & 8) > 0; + SQ1_per = (byte)(value & 7); + if (SQ1_per == 0) { SQ1_per = 8; } + if ((value & 0xF8) == 0) { SQ1_enable = SQ1_swp_enable = false; } + break; case 0xFF13: // NR13 (freq low) - Audio_Regs[NR13] = value; - SQ1_frq &= 0x700; - SQ1_frq |= value; - break; + Audio_Regs[NR13] = value; + SQ1_frq &= 0x700; + SQ1_frq |= value; + break; case 0xFF14: // NR14 (freq hi) - Audio_Regs[NR14] = value; - SQ1_trigger = (value & 0x80) > 0; - SQ1_frq &= 0xFF; - SQ1_frq |= (ushort)((value & 7) << 8); - - if (((sequencer_len & 1) > 0)) - { - if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) - { - SQ1_len_cntr--; - if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = SQ1_swp_enable = false; } - } - } - - if (SQ1_trigger) - { - SQ1_enable = true; - SQ1_vol_done = false; - if (SQ1_len_cntr == 0) - { - SQ1_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } - } - SQ1_intl_cntr = 0; - SQ1_vol_state = SQ1_st_vol; - SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; - SQ1_frq_shadow = SQ1_frq; - - SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; - SQ1_calc_done = false; - + Audio_Regs[NR14] = value; + SQ1_trigger = (value & 0x80) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((value & 7) << 8); + + if (((sequencer_len & 1) > 0)) + { + if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) + { + SQ1_len_cntr--; + if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = SQ1_swp_enable = false; } + } + } + + if (SQ1_trigger) + { + SQ1_enable = true; + SQ1_vol_done = false; + if (SQ1_len_cntr == 0) + { + SQ1_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } + } + SQ1_vol_state = SQ1_st_vol; + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + SQ1_frq_shadow = SQ1_frq; + SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4; + + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + SQ1_calc_done = false; + if ((SQ1_shift > 0) || (SQ1_swp_prd > 0)) { SQ1_swp_enable = true; - } + } else { SQ1_swp_enable = false; - } - + } + if (SQ1_shift > 0) { int shadow_frq = SQ1_frq_shadow; @@ -251,461 +258,479 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ1_negate) { SQ1_calc_done = true; } } - if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; } + if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; } } - SQ1_len_en = (value & 0x40) > 0; - break; + SQ1_len_en = (value & 0x40) > 0; + break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - Audio_Regs[NR21] = value; - SQ2_duty = (byte)((value & 0xC0) >> 6); - SQ2_length = (ushort)(64 - (value & 0x3F)); - SQ2_len_cntr = SQ2_length; - break; + Audio_Regs[NR21] = value; + SQ2_duty = (byte)((value & 0xC0) >> 6); + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + break; case 0xFF17: // NR22 (envelope) - Audio_Regs[NR22] = value; - SQ2_st_vol = (byte)((value & 0xF0) >> 4); - SQ2_env_add = (value & 8) > 0; + Audio_Regs[NR22] = value; + SQ2_st_vol = (byte)((value & 0xF0) >> 4); + SQ2_env_add = (value & 8) > 0; SQ2_per = (byte)(value & 7); //if (SQ2_per == 0) { SQ2_per = 8; } - if ((value & 0xF8) == 0) { SQ2_enable = false; } - break; + if ((value & 0xF8) == 0) { SQ2_enable = false; } + break; case 0xFF18: // NR23 (freq low) - Audio_Regs[NR23] = value; - SQ2_frq &= 0x700; - SQ2_frq |= value; - break; + Audio_Regs[NR23] = value; + SQ2_frq &= 0x700; + SQ2_frq |= value; + break; case 0xFF19: // NR24 (freq hi) - Audio_Regs[NR24] = value; - SQ2_trigger = (value & 0x80) > 0; - SQ2_frq &= 0xFF; - SQ2_frq |= (ushort)((value & 7) << 8); - - if ((sequencer_len & 1) > 0) - { - if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) - { - SQ2_len_cntr--; - if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; } - } - } - - if (SQ2_trigger) - { - SQ2_enable = true; - SQ2_vol_done = false; - - if (SQ2_len_cntr == 0) - { - SQ2_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } - } - SQ2_intl_cntr = 0; - SQ2_vol_state = SQ2_st_vol; - SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; - if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; } - } - - SQ2_len_en = (value & 0x40) > 0; - - break; - case 0xFF1A: // NR30 (on/off) - Audio_Regs[NR30] = value; - WAVE_DAC_pow = (value & 0x80) > 0; - if (!WAVE_DAC_pow) { WAVE_enable = false; } - break; - case 0xFF1B: // NR31 (length) - Audio_Regs[NR31] = value; - WAVE_length = (ushort)(256 - value); - WAVE_len_cntr = WAVE_length; - break; - case 0xFF1C: // NR32 (level output) - Audio_Regs[NR32] = value; - WAVE_vol_code = (byte)((value & 0x60) >> 5); - break; - case 0xFF1D: // NR33 (freq low) - Audio_Regs[NR33] = value; - WAVE_frq &= 0x700; - WAVE_frq |= value; - break; - case 0xFF1E: // NR34 (freq hi) - Audio_Regs[NR34] = value; - WAVE_trigger = (value & 0x80) > 0; - WAVE_frq &= 0xFF; - WAVE_frq |= (ushort)((value & 7) << 8); - - if ((sequencer_len & 1) > 0) - { - if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) - { - WAVE_len_cntr--; - if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; } - } - } - - if (WAVE_trigger) - { - WAVE_enable = true; - - if (WAVE_len_cntr == 0) - { - WAVE_len_cntr = 256; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } - } - WAVE_intl_cntr = 0; - WAVE_wave_cntr = 0; - if (!WAVE_DAC_pow) { WAVE_enable = false; } - } - - WAVE_len_en = (value & 0x40) > 0; - - break; - case 0xFF20: // NR41 (length) - Audio_Regs[NR41] = value; - NOISE_length = (ushort)(64 - (value & 0x3F)); - NOISE_len_cntr = NOISE_length; - break; - case 0xFF21: // NR42 (envelope) - Audio_Regs[NR42] = value; - NOISE_st_vol = (byte)((value & 0xF0) >> 4); - NOISE_env_add = (value & 8) > 0; - NOISE_per = (byte)(value & 7); - //if (NOISE_per == 0) { NOISE_per = 8; } - if ((value & 0xF8) == 0) { NOISE_enable = false; } - break; - case 0xFF22: // NR43 (shift) - Audio_Regs[NR43] = value; - NOISE_clk_shft = (byte)((value & 0xF0) >> 4); - NOISE_wdth_md = (value & 8) > 0; - NOISE_div_code = (byte)(value & 7); - break; - case 0xFF23: // NR44 (trigger) - Audio_Regs[NR44] = value; - NOISE_trigger = (value & 0x80) > 0; - - if ((sequencer_len & 1) > 0) - { - if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) - { - NOISE_len_cntr--; - if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; } - } - } - - if (NOISE_trigger) - { - NOISE_enable = true; - NOISE_vol_done = false; - - if (NOISE_len_cntr == 0) - { - NOISE_len_cntr = 64; - if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } - } - NOISE_intl_cntr = 0; - NOISE_vol_state = NOISE_st_vol; - NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; - NOISE_LFSR = 0x7FFF; - if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; } + Audio_Regs[NR24] = value; + SQ2_trigger = (value & 0x80) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) + { + SQ2_len_cntr--; + if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; } + } } - NOISE_len_en = (value & 0x40) > 0; - break; + if (SQ2_trigger) + { + SQ2_enable = true; + SQ2_vol_done = false; + + if (SQ2_len_cntr == 0) + { + SQ2_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } + } + SQ2_intl_cntr = (2048 - SQ2_frq) * 4; + SQ2_vol_state = SQ2_st_vol; + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; } + } + + SQ2_len_en = (value & 0x40) > 0; + + break; + case 0xFF1A: // NR30 (on/off) + Audio_Regs[NR30] = value; + WAVE_DAC_pow = (value & 0x80) > 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + break; + case 0xFF1B: // NR31 (length) + Audio_Regs[NR31] = value; + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + break; + case 0xFF1C: // NR32 (level output) + Audio_Regs[NR32] = value; + WAVE_vol_code = (byte)((value & 0x60) >> 5); + break; + case 0xFF1D: // NR33 (freq low) + Audio_Regs[NR33] = value; + WAVE_frq &= 0x700; + WAVE_frq |= value; + break; + case 0xFF1E: // NR34 (freq hi) + Audio_Regs[NR34] = value; + WAVE_trigger = (value & 0x80) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) + { + WAVE_len_cntr--; + if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; } + } + } + + if (WAVE_trigger) + { + // some corruption occurs if triggering while reading + if (WAVE_enable && WAVE_intl_cntr == 2) + { + // we want to use the previous wave cntr value since it was just incremented + int t_wave_cntr = (WAVE_wave_cntr + 1) & 31; + if ((t_wave_cntr >> 1) < 4) + { + Wave_RAM[0] = Wave_RAM[t_wave_cntr >> 1]; + } + else + { + Wave_RAM[0] = Wave_RAM[(t_wave_cntr >> 3) * 4]; + Wave_RAM[1] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 1]; + Wave_RAM[2] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 2]; + Wave_RAM[3] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 3]; + } + } + + WAVE_enable = true; + + if (WAVE_len_cntr == 0) + { + WAVE_len_cntr = 256; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } + } + WAVE_intl_cntr = (2048 - WAVE_frq) * 2 + 6; // trigger delay for wave channel + WAVE_wave_cntr = 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + } + + WAVE_len_en = (value & 0x40) > 0; + + break; + case 0xFF20: // NR41 (length) + Audio_Regs[NR41] = value; + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + break; + case 0xFF21: // NR42 (envelope) + Audio_Regs[NR42] = value; + NOISE_st_vol = (byte)((value & 0xF0) >> 4); + NOISE_env_add = (value & 8) > 0; + NOISE_per = (byte)(value & 7); + //if (NOISE_per == 0) { NOISE_per = 8; } + if ((value & 0xF8) == 0) { NOISE_enable = false; } + break; + case 0xFF22: // NR43 (shift) + Audio_Regs[NR43] = value; + NOISE_clk_shft = (byte)((value & 0xF0) >> 4); + NOISE_wdth_md = (value & 8) > 0; + NOISE_div_code = (byte)(value & 7); + break; + case 0xFF23: // NR44 (trigger) + Audio_Regs[NR44] = value; + NOISE_trigger = (value & 0x80) > 0; + + if ((sequencer_len & 1) > 0) + { + if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) + { + NOISE_len_cntr--; + if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; } + } + } + + if (NOISE_trigger) + { + NOISE_enable = true; + NOISE_vol_done = false; + + if (NOISE_len_cntr == 0) + { + NOISE_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } + } + NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft); + NOISE_vol_state = NOISE_st_vol; + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + NOISE_LFSR = 0x7FFF; + if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; } + } + + NOISE_len_en = (value & 0x40) > 0; + break; case 0xFF24: // NR50 (ctrl) - Audio_Regs[NR50] = value; - AUD_CTRL_vin_L_en = (value & 0x80) > 0; - AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); - AUD_CTRL_vin_R_en = (value & 8) > 0; - AUD_CTRL_vol_R = (byte)(value & 7); - break; + Audio_Regs[NR50] = value; + AUD_CTRL_vin_L_en = (value & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); + AUD_CTRL_vin_R_en = (value & 8) > 0; + AUD_CTRL_vol_R = (byte)(value & 7); + break; case 0xFF25: // NR51 (ctrl) - Audio_Regs[NR51] = value; - AUD_CTRL_noise_L_en = (value & 0x80) > 0; - AUD_CTRL_wave_L_en = (value & 0x40) > 0; - AUD_CTRL_sq2_L_en = (value & 0x20) > 0; - AUD_CTRL_sq1_L_en = (value & 0x10) > 0; - AUD_CTRL_noise_R_en = (value & 8) > 0; - AUD_CTRL_wave_R_en = (value & 4) > 0; - AUD_CTRL_sq2_R_en = (value & 2) > 0; - AUD_CTRL_sq1_R_en = (value & 1) > 0; - break; - case 0xFF26: // NR52 (ctrl) - AUD_CTRL_power = (value & 0x80) > 0; - - if (!AUD_CTRL_power) { power_off(); } + Audio_Regs[NR51] = value; + AUD_CTRL_noise_L_en = (value & 0x80) > 0; + AUD_CTRL_wave_L_en = (value & 0x40) > 0; + AUD_CTRL_sq2_L_en = (value & 0x20) > 0; + AUD_CTRL_sq1_L_en = (value & 0x10) > 0; + AUD_CTRL_noise_R_en = (value & 8) > 0; + AUD_CTRL_wave_R_en = (value & 4) > 0; + AUD_CTRL_sq2_R_en = (value & 2) > 0; + AUD_CTRL_sq1_R_en = (value & 1) > 0; + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; + + if (!AUD_CTRL_power) { power_off(); } break; // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; - } + 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: + if (WAVE_enable) + { + if (WAVE_can_get) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } + } + else { Wave_RAM[addr & 0xF] = value; } + + break; + } } // when power is off, only length counters and waveRAM are effected by writes - else - { - switch (addr) - { + else + { + switch (addr) + { case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1_length = (ushort)(64 - (value & 0x3F)); - SQ1_len_cntr = SQ1_length; - break; + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2_length = (ushort)(64 - (value & 0x3F)); - SQ2_len_cntr = SQ2_length; - break; + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + break; case 0xFF1B: // NR31 (length) - WAVE_length = (ushort)(256 - value); - WAVE_len_cntr = WAVE_length; - break; + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + break; case 0xFF20: // NR41 (length) - NOISE_length = (ushort)(64 - (value & 0x3F)); - NOISE_len_cntr = NOISE_length; - break; - case 0xFF26: // NR52 (ctrl) - AUD_CTRL_power = (value & 0x80) > 0; + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; if (AUD_CTRL_power) { sequencer_vol = 0; sequencer_len = 0; sequencer_swp = 0; - } + } break; // wave ram table - 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: - Wave_RAM[addr & 0x0F] = value; - break; - } - } - } - - public void tick() + 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: + Wave_RAM[addr & 0x0F] = value; + break; + } + } + } + + public void tick() { // calculate square1's output - if (SQ1_enable) - { - SQ1_intl_cntr++; - if (SQ1_intl_cntr >= (2048 - SQ1_frq_shadow) * 4) - { - SQ1_intl_cntr = 0; - SQ1_duty_cntr++; - SQ1_duty_cntr &= 7; - - SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; - SQ1_output *= SQ1_vol_state; - } + if (SQ1_enable) + { + SQ1_intl_cntr--; + if (SQ1_intl_cntr == 0) + { + SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4; + SQ1_duty_cntr++; + SQ1_duty_cntr &= 7; + + SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; + SQ1_output *= SQ1_vol_state; + } } // calculate square2's output - if (SQ2_enable) - { - SQ2_intl_cntr++; - if (SQ2_intl_cntr >= (2048 - SQ2_frq) * 4) - { - SQ2_intl_cntr = 0; - SQ2_duty_cntr++; - SQ2_duty_cntr &= 7; - - SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; - SQ2_output *= SQ2_vol_state; - } + if (SQ2_enable) + { + SQ2_intl_cntr--; + if (SQ2_intl_cntr == 0) + { + SQ2_intl_cntr = (2048 - SQ2_frq) * 4; + SQ2_duty_cntr++; + SQ2_duty_cntr &= 7; + + SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; + SQ2_output *= SQ2_vol_state; + } } // calculate wave output - if (WAVE_enable) - { - WAVE_intl_cntr++; - if (WAVE_intl_cntr >= (2048 - WAVE_frq) * 2) - { - WAVE_intl_cntr = 0; - WAVE_wave_cntr++; - WAVE_wave_cntr &= 0x1F; - - byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; - - if ((WAVE_wave_cntr & 1) == 0) - { - sample = (byte)(sample >> 4); - } - - if (WAVE_vol_code == 0) - { - sample = (byte)((sample & 0xF) >> 4); - } - else if (WAVE_vol_code == 1) - { - sample = (byte)(sample & 0xF); - } - else if (WAVE_vol_code == 2) - { - sample = (byte)((sample & 0xF) >> 1); - } - else - { - sample = (byte)((sample & 0xF) >> 2); - } - - WAVE_output = sample; - - if (!WAVE_DAC_pow) { WAVE_output = 0; } - } + WAVE_can_get = false; + if (WAVE_enable) + { + WAVE_intl_cntr--; + + if (WAVE_intl_cntr == 0) + { + WAVE_intl_cntr = (2048 - WAVE_frq) * 2; + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; + + WAVE_can_get = true; + + byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; + + if ((WAVE_wave_cntr & 1) == 0) + { + sample = (byte)(sample >> 4); + } + + if (WAVE_vol_code == 0) + { + sample = (byte)((sample & 0xF) >> 4); + } + else if (WAVE_vol_code == 1) + { + sample = (byte)(sample & 0xF); + } + else if (WAVE_vol_code == 2) + { + sample = (byte)((sample & 0xF) >> 1); + } + else + { + sample = (byte)((sample & 0xF) >> 2); + } + + WAVE_output = sample; + + if (!WAVE_DAC_pow) { WAVE_output = 0; } + } } // calculate noise output - if (NOISE_enable) - { - NOISE_intl_cntr++; - if (NOISE_intl_cntr >= (DIVISOR[NOISE_div_code] << NOISE_clk_shft)) - { - NOISE_intl_cntr = 0; - int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); - - NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; - NOISE_LFSR |= (bit_lfsr << 14); - - if (NOISE_wdth_md) - { - NOISE_LFSR = NOISE_LFSR & 0x7FBF; - NOISE_LFSR |= (bit_lfsr << 6); - } - - NOISE_output = NOISE_LFSR & 1; - NOISE_output *= NOISE_vol_state; - } + if (NOISE_enable) + { + NOISE_intl_cntr--; + if (NOISE_intl_cntr == 0) + { + NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft); + int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); + + NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; + NOISE_LFSR |= (bit_lfsr << 14); + + if (NOISE_wdth_md) + { + NOISE_LFSR = NOISE_LFSR & 0x7FBF; + NOISE_LFSR |= (bit_lfsr << 6); + } + + NOISE_output = NOISE_LFSR & 1; + NOISE_output *= NOISE_vol_state; + } } // add up components to each channel - int L_final = 0; - int R_final = 0; - - if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } - if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } - if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } - if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } - - if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } - if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } - if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } - if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } - - L_final *= (AUD_CTRL_vol_L + 1); + int L_final = 0; + int R_final = 0; + + if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } + if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } + if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } + if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } + + if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } + if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } + if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } + if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } + + L_final *= (AUD_CTRL_vol_L + 1); R_final *= (AUD_CTRL_vol_R + 1); // send out an actual sample every 94 cycles - master_audio_clock++; - if (master_audio_clock == 94) - { - master_audio_clock = 0; - if (AudioClocks < 1500) - { + master_audio_clock++; + if (master_audio_clock == 94) + { + master_audio_clock = 0; + if (AudioClocks < 1500) + { AudioSamples[AudioClocks] = (short)(L_final * 4); - /* - Console_Write(SQ1_output); - Console_Write(" "); - Console_Write(SQ2_output); - Console_Write(" "); - Console_Write(WAVE_output); - Console_Write(" "); - Console_WriteLine(NOISE_output); - */ - AudioClocks++; - AudioSamples[AudioClocks] = (short)(R_final * 4); - AudioClocks++; - } + AudioClocks++; + AudioSamples[AudioClocks] = (short)(R_final * 4); + AudioClocks++; + } } // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) - sequencer_tick++; - - if (sequencer_tick == 8192) - { - sequencer_tick = 0; - - sequencer_vol++; sequencer_vol &= 0x7; - sequencer_len++; sequencer_len &= 0x7; + sequencer_tick++; + + if (sequencer_tick == 8192) + { + sequencer_tick = 0; + + sequencer_vol++; sequencer_vol &= 0x7; + sequencer_len++; sequencer_len &= 0x7; sequencer_swp++; sequencer_swp &= 0x7; // clock the lengths - if ((sequencer_len & 1) > 0) - { - if (SQ1_len_en && SQ1_len_cntr > 0) - { - SQ1_len_cntr--; - if (SQ1_len_cntr == 0) { SQ1_enable = SQ1_swp_enable = false; } - } - if (SQ2_len_en && SQ2_len_cntr > 0) - { - SQ2_len_cntr--; - if (SQ2_len_cntr == 0) { SQ2_enable = false; } - } - if (WAVE_len_en && WAVE_len_cntr > 0) - { - WAVE_len_cntr--; - if (WAVE_len_cntr == 0) { WAVE_enable = false; } - } - if (NOISE_len_en && NOISE_len_cntr > 0) - { - NOISE_len_cntr--; - if (NOISE_len_cntr == 0) { NOISE_enable = false; } - } + if ((sequencer_len & 1) > 0) + { + if (SQ1_len_en && SQ1_len_cntr > 0) + { + SQ1_len_cntr--; + if (SQ1_len_cntr == 0) { SQ1_enable = SQ1_swp_enable = false; } + } + if (SQ2_len_en && SQ2_len_cntr > 0) + { + SQ2_len_cntr--; + if (SQ2_len_cntr == 0) { SQ2_enable = false; } + } + if (WAVE_len_en && WAVE_len_cntr > 0) + { + WAVE_len_cntr--; + if (WAVE_len_cntr == 0) { WAVE_enable = false; } + } + if (NOISE_len_en && NOISE_len_cntr > 0) + { + NOISE_len_cntr--; + if (NOISE_len_cntr == 0) { NOISE_enable = false; } + } } // clock the sweep - if ((sequencer_swp == 3) || (sequencer_swp == 7)) - { - SQ1_intl_swp_cnt--; - if ((SQ1_intl_swp_cnt == 0) && SQ1_swp_enable) - { - SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; - - if ((SQ1_swp_prd > 0)) - { - int shadow_frq = SQ1_frq_shadow; - shadow_frq = shadow_frq >> SQ1_shift; - if (SQ1_negate) { shadow_frq = -shadow_frq; } + if ((sequencer_swp == 3) || (sequencer_swp == 7)) + { + SQ1_intl_swp_cnt--; + if ((SQ1_intl_swp_cnt == 0) && SQ1_swp_enable) + { + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + + if ((SQ1_swp_prd > 0)) + { + int shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } shadow_frq += SQ1_frq_shadow; // set negate mode flag that disables channel is negate clerar if (SQ1_negate) { SQ1_calc_done = true; } // disable channel if overflow - if ((uint)shadow_frq > 2047) - { - SQ1_enable = SQ1_swp_enable = false; - } - else + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + else { if (SQ1_shift > 0) { @@ -727,22 +752,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((uint)shadow_frq > 2047) { SQ1_enable = SQ1_swp_enable = false; - } - } - } - } - } + } + } + } + } + } } // clock the volume envelope - if (sequencer_vol == 0) - { - if (SQ1_per > 0) - { - SQ1_vol_per--; - if (SQ1_vol_per == 0) - { - SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + if (sequencer_vol == 0) + { + if (SQ1_per > 0) + { + SQ1_vol_per--; + if (SQ1_vol_per == 0) + { + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; if (!SQ1_vol_done) { if (SQ1_env_add) @@ -755,15 +780,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ1_vol_state >= 1) { SQ1_vol_state--; } else { SQ1_vol_done = true; } } - } - } - } - if (SQ2_per > 0) - { - SQ2_vol_per--; - if (SQ2_vol_per == 0) - { - SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + } + } + } + if (SQ2_per > 0) + { + SQ2_vol_per--; + if (SQ2_vol_per == 0) + { + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; if (!SQ2_vol_done) { if (SQ2_env_add) @@ -776,15 +801,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (SQ2_vol_state >= 1) { SQ2_vol_state--; } else { SQ2_vol_done = true; } } - } - } - } - if (NOISE_per > 0) - { - NOISE_vol_per--; - if (NOISE_vol_per == 0) - { - NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + } + } + } + if (NOISE_per > 0) + { + NOISE_vol_per--; + if (NOISE_vol_per == 0) + { + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; if (!NOISE_vol_done) { if (NOISE_env_add) @@ -797,190 +822,192 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (NOISE_vol_state >= 1) { NOISE_vol_state--; } else { NOISE_vol_done = true; } } - } - } - } - } - } - } - - public void power_off() - { - for (int i = 0; i < 21; i++) - { - Audio_Regs[i] = 0; + } + } + } + } + } + } + + public void power_off() + { + for (int i = 0; i < 21; i++) + { + Audio_Regs[i] = 0; } // reset derived values - sync_channels(); - - // reset state variables - SQ1_enable = SQ1_swp_enable = false; - SQ2_enable = false; - WAVE_enable = false; - NOISE_enable = false; - - SQ1_len_en = false; - SQ2_len_en = false; - WAVE_len_en = false; - NOISE_len_en = false; - - sequencer_len = 0; - sequencer_vol = 0; - sequencer_swp = 0; - - master_audio_clock = 0; - } - - public void Reset() - { - Wave_RAM = new byte[16]; - - Audio_Regs = new byte[21]; - - AudioClocks = 0; - master_audio_clock = 0; - - sequencer_len = 0; - sequencer_swp = 0; - sequencer_vol = 0; - sequencer_tick = 0; - } - - public void SyncState(Serializer ser) - { - ser.Sync("Audio_Regs", ref Audio_Regs, false); + sync_channels(); + + // reset state variables + SQ1_enable = SQ1_swp_enable = false; + SQ2_enable = false; + WAVE_enable = false; + NOISE_enable = false; + + SQ1_len_en = false; + SQ2_len_en = false; + WAVE_len_en = false; + NOISE_len_en = false; + + sequencer_len = 0; + sequencer_vol = 0; + sequencer_swp = 0; + + master_audio_clock = 0; + } + + public void Reset() + { + Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, + 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA }; + + Audio_Regs = new byte[21]; + + AudioClocks = 0; + master_audio_clock = 0; + + sequencer_len = 0; + sequencer_swp = 0; + sequencer_vol = 0; + sequencer_tick = 0; + } + + public void SyncState(Serializer ser) + { + ser.Sync("Audio_Regs", ref Audio_Regs, false); ser.Sync("Wave_Ram", ref Wave_RAM, false); // save state variables + ser.Sync("WAVE_can_get", ref WAVE_can_get); ser.Sync("SQ1_vol_done", ref SQ1_vol_done); ser.Sync("SQ2_vol_done", ref SQ2_vol_done); ser.Sync("NOISE_vol_done", ref NOISE_vol_done); ser.Sync("SQ1_calc_done", ref SQ1_calc_done); ser.Sync("SQ1_swp_enable", ref SQ1_swp_enable); - ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); - ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); - ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); - ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); - ser.Sync("SQ1_enable", ref SQ1_enable); - ser.Sync("SQ2_enable", ref SQ2_enable); - ser.Sync("WAVE_enable", ref WAVE_enable); - ser.Sync("NOISE_enable", ref NOISE_enable); - ser.Sync("SQ1_vol_state", ref SQ1_vol_state); - ser.Sync("SQ2_vol_state", ref SQ2_vol_state); - ser.Sync("NOISE_vol_state", ref NOISE_vol_state); - ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); - ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); - ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); - ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); - ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); - ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); - ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); - ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); - ser.Sync("SQ1_vol_per", ref SQ1_vol_per); - ser.Sync("SQ2_vol_per", ref SQ2_vol_per); - ser.Sync("NOISE_vol_per", ref NOISE_vol_per); - ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); - ser.Sync("NOISE_LFSR", ref NOISE_LFSR); - ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); - ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); - ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); - ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); - - - ser.Sync("sequencer_len", ref sequencer_len); - ser.Sync("sequencer_vol", ref sequencer_vol); - ser.Sync("sequencer_swp", ref sequencer_swp); - ser.Sync("sequencer_tick", ref sequencer_tick); - + ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); + ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); + ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); + ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); + ser.Sync("SQ1_enable", ref SQ1_enable); + ser.Sync("SQ2_enable", ref SQ2_enable); + ser.Sync("WAVE_enable", ref WAVE_enable); + ser.Sync("NOISE_enable", ref NOISE_enable); + ser.Sync("SQ1_vol_state", ref SQ1_vol_state); + ser.Sync("SQ2_vol_state", ref SQ2_vol_state); + ser.Sync("NOISE_vol_state", ref NOISE_vol_state); + ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); + ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); + ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); + ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); + ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); + ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); + ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); + ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); + ser.Sync("SQ1_vol_per", ref SQ1_vol_per); + ser.Sync("SQ2_vol_per", ref SQ2_vol_per); + ser.Sync("NOISE_vol_per", ref NOISE_vol_per); + ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); + ser.Sync("NOISE_LFSR", ref NOISE_LFSR); + ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); + ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); + ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); + ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); + + + ser.Sync("sequencer_len", ref sequencer_len); + ser.Sync("sequencer_vol", ref sequencer_vol); + ser.Sync("sequencer_swp", ref sequencer_swp); + ser.Sync("sequencer_tick", ref sequencer_tick); + ser.Sync("master_audio_clock", ref master_audio_clock); // get derived state - if (ser.IsReader) - { - sync_channels(); - } - } - - public void sync_channels() - { - - SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); - SQ1_negate = (Audio_Regs[NR10] & 8) > 0; - SQ1_shift = (byte)(Audio_Regs[NR10] & 7); - - SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); - SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); - - SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); - SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; - SQ1_per = (byte)(Audio_Regs[NR12] & 7); - - SQ1_frq &= 0x700; - SQ1_frq |= Audio_Regs[NR13]; - - SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; - SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; - SQ1_frq &= 0xFF; + if (ser.IsReader) + { + sync_channels(); + } + } + + public void sync_channels() + { + + SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); + SQ1_negate = (Audio_Regs[NR10] & 8) > 0; + SQ1_shift = (byte)(Audio_Regs[NR10] & 7); + + SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); + SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); + + SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); + SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; + SQ1_per = (byte)(Audio_Regs[NR12] & 7); + + SQ1_frq &= 0x700; + SQ1_frq |= Audio_Regs[NR13]; + + SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; + SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; + SQ1_frq &= 0xFF; SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); - SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); - SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); - - SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); - SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; - SQ2_per = (byte)(Audio_Regs[NR22] & 7); - - SQ2_frq &= 0x700; - SQ2_frq |= Audio_Regs[NR23]; - - SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; - SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; - SQ2_frq &= 0xFF; - SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); - - WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; - - WAVE_length = (ushort)(256 - Audio_Regs[NR31]); - - WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); - - WAVE_frq &= 0x700; - WAVE_frq |= Audio_Regs[NR33]; - - WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; - WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; - WAVE_frq &= 0xFF; - WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); - - NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); - - NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); - NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; - NOISE_per = (byte)(Audio_Regs[NR42] & 7); - - NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); - NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; - NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); - - WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; - WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; - - AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; - AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); - AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; - AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); - - AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; - AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; - AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; - AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; - AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; - AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; - AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; - AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; - - AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; + SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); + SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); + + SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); + SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; + SQ2_per = (byte)(Audio_Regs[NR22] & 7); + + SQ2_frq &= 0x700; + SQ2_frq |= Audio_Regs[NR23]; + + SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; + SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); + + WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; + + WAVE_length = (ushort)(256 - Audio_Regs[NR31]); + + WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); + + WAVE_frq &= 0x700; + WAVE_frq |= Audio_Regs[NR33]; + + WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); + + NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); + + NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); + NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; + NOISE_per = (byte)(Audio_Regs[NR42] & 7); + + NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); + NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; + NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); + + WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; + + AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); + AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; + AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); + + AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; + AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; + AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; + AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; + AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; + AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; + AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; + + AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; } public byte Read_NR52() @@ -993,53 +1020,53 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ((NOISE_enable ? 1 : 0) << 3)); } - #region audio + #region audio - public bool CanProvideAsync => false; - - public int AudioClocks; - public short[] AudioSamples = new short[1500]; - - 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) - { - nsamp = AudioClocks / 2; - short[] temp_samp = new short[AudioClocks]; - - for (int i = 0; i < AudioClocks; i++) - { - temp_samp[i] = AudioSamples[i]; - } - - samples = temp_samp; - - AudioClocks = 0; - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - AudioClocks = 0; - } - - private void GetSamples(short[] samples) - { - + public bool CanProvideAsync => false; + + public int AudioClocks; + public short[] AudioSamples = new short[1500]; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + { + throw new InvalidOperationException("Only Sync mode is supported_"); + } } - #endregion - } + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + nsamp = AudioClocks / 2; + short[] temp_samp = new short[AudioClocks]; + + for (int i = 0; i < AudioClocks; i++) + { + temp_samp[i] = AudioSamples[i]; + } + + samples = temp_samp; + + AudioClocks = 0; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + AudioClocks = 0; + } + + private void GetSamples(short[] samples) + { + + } + + #endregion + } } \ No newline at end of file From 8e1ba79e0a2b691e17489ccc844eb37155d421f2 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 19 Nov 2017 10:17:23 -0500 Subject: [PATCH 26/28] GBHawk: Add core picker --- BizHawk.Client.Common/RomLoader.cs | 26 +++++++++- BizHawk.Client.Common/config/Config.cs | 1 + BizHawk.Client.EmuHawk/MainForm.Designer.cs | 53 ++++++++++++++++----- BizHawk.Client.EmuHawk/MainForm.Events.cs | 16 +++++++ 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 6a9c8b68d7..99e355081e 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -936,7 +936,31 @@ namespace BizHawk.Client.Common break; case "GB": - core = CoreInventory.Instance["GB", "GBHawk"]; + if (!Global.Config.GB_AsSGB) + { + if (Global.Config.GB_UseGBHawk) + { + core = CoreInventory.Instance["GB", "GBHawk"]; + } + else + { + core = CoreInventory.Instance["GB", "Gambatte"]; + } + } + else + { + if (Global.Config.SGB_UseBsnes) + { + game.System = "SNES"; + game.AddOption("SGB"); + var snes = new LibsnesCore(game, rom.FileData, null, nextComm, GetCoreSettings(), GetCoreSyncSettings()); + nextEmulator = snes; + } + else + { + core = CoreInventory.Instance["SGB", "SameBoy"]; + } + } break; case "GBC": if (!Global.Config.GB_AsSGB) diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index bcdeb39420..4dea2406f2 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -557,6 +557,7 @@ namespace BizHawk.Client.Common public bool SNES_InSnes9x = true; public bool GBA_UsemGBA = true; public bool SGB_UseBsnes = false; + public bool GB_UseGBHawk = false; public bool CoreForcingViaGameDB = true; public string LibretroCore; } diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 9d6774638e..0e250d1e01 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -194,9 +194,12 @@ this.MgbaCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SGBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SgbSameBoyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); this.allowGameDBCoreOverridesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); @@ -1810,7 +1813,8 @@ this.CoreSNESSubMenu, this.GbaCoreSubMenu, this.SGBCoreSubmenu, - this.GBInSGBMenuItem, + this.GBCoreSubmenu, + this.GBInSGBMenuItem, this.toolStripMenuItem16, this.allowGameDBCoreOverridesToolStripMenuItem, this.toolStripSeparator8, @@ -1908,10 +1912,20 @@ this.SGBCoreSubmenu.Size = new System.Drawing.Size(239, 22); this.SGBCoreSubmenu.Text = "SGB"; this.SGBCoreSubmenu.DropDownOpened += new System.EventHandler(this.SGBCoreSubmenu_DropDownOpened); - // - // SgbBsnesMenuItem - // - this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; + // + // GBCoreSubmenu + // + this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.GBGambatteMenuItem, + this.GBGBHawkMenuItem}); + this.GBCoreSubmenu.Name = "GBCoreSubmenu"; + this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); + this.GBCoreSubmenu.Text = "GB"; + this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); + // + // SgbBsnesMenuItem + // + this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; this.SgbBsnesMenuItem.Size = new System.Drawing.Size(152, 22); this.SgbBsnesMenuItem.Text = "BSNES"; this.SgbBsnesMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); @@ -1922,10 +1936,24 @@ this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(152, 22); this.SgbSameBoyMenuItem.Text = "SameBoy"; this.SgbSameBoyMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); - // - // GBInSGBMenuItem - // - this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; + // + // GBGambatteMenuItem + // + this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; + this.GBGambatteMenuItem.Size = new System.Drawing.Size(152, 22); + this.GBGambatteMenuItem.Text = "Gambatte"; + this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBGBHawkMenuItem + // + this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; + this.GBGBHawkMenuItem.Size = new System.Drawing.Size(152, 22); + this.GBGBHawkMenuItem.Text = "GBHawk"; + this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBInSGBMenuItem + // + this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; this.GBInSGBMenuItem.Size = new System.Drawing.Size(239, 22); this.GBInSGBMenuItem.Text = "GB in SGB"; this.GBInSGBMenuItem.Click += new System.EventHandler(this.GbInSgbMenuItem_Click); @@ -4407,6 +4435,9 @@ private System.Windows.Forms.ToolStripMenuItem SGBCoreSubmenu; private System.Windows.Forms.ToolStripMenuItem SgbBsnesMenuItem; private System.Windows.Forms.ToolStripMenuItem SgbSameBoyMenuItem; + private System.Windows.Forms.ToolStripMenuItem GBCoreSubmenu; + private System.Windows.Forms.ToolStripMenuItem GBGambatteMenuItem; + private System.Windows.Forms.ToolStripMenuItem GBGBHawkMenuItem; private System.Windows.Forms.ToolStripMenuItem pCFXToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem3; private System.Windows.Forms.ToolStripMenuItem SMSControllerToolStripMenuItem; diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index fd1d42f241..026adb32be 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1244,6 +1244,12 @@ namespace BizHawk.Client.EmuHawk SgbSameBoyMenuItem.Checked = !Global.Config.SGB_UseBsnes; } + private void GBCoreSubmenu_DropDownOpened(object sender, EventArgs e) + { + GBGambatteMenuItem.Checked = !Global.Config.GB_UseGBHawk; + GBGBHawkMenuItem.Checked = Global.Config.GB_UseGBHawk; + } + private void SgbCorePick_Click(object sender, EventArgs e) { Global.Config.SGB_UseBsnes ^= true; @@ -1254,6 +1260,16 @@ namespace BizHawk.Client.EmuHawk } } + private void GBCorePick_Click(object sender, EventArgs e) + { + Global.Config.GB_UseGBHawk ^= true; + // TODO: only flag if one of these cores + if (!Emulator.IsNull()) + { + FlagNeedsReboot(); + } + } + private void GbInSgbMenuItem_Click(object sender, EventArgs e) { Global.Config.GB_AsSGB ^= true; From 69170fb110ac8009509fb37a0d55d06a1bd96262 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 19 Nov 2017 10:22:05 -0500 Subject: [PATCH 27/28] Remove old GB CPU core --- .../BizHawk.Emulation.Cores.csproj | 6 - .../CPUs/Z80-GB/Execute.cs | 2321 ----------------- .../CPUs/Z80-GB/Interrupts.cs | 52 - .../CPUs/Z80-GB/NewDisassembler.cs | 587 ----- .../CPUs/Z80-GB/Registers.cs | 166 -- BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs | 149 -- BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs | 171 -- 7 files changed, 3452 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs delete mode 100644 BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index e2ea538512..05baf2d9d0 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1223,12 +1223,6 @@ - - - - - - diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs deleted file mode 100644 index a53ef53861..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs +++ /dev/null @@ -1,2321 +0,0 @@ -using System; - -/* TODO: -+ http://www.devrs.com/gb/files/opcodes.html was used as a flags and timing reference. -+ [Opt] The DAA Table could be reduced from 128k to 8k. -+ Currently all instructions are using fixed m-cycles. Any instruction with variable cycles (ie branches) need to be checked. -+ The following instructions were rewritten or substantially modified for Z80-GB. They should be - treated with caution and checked further when the emulator is farther along. - - ADD - ADC - SUB - SBC - AND - OR - XOR - CP - SWAP - DAA - RLCA - RLA - RRCA - RRA - RLC - RL - RRC - RR - SLA - SRA - SRL -*/ - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - public int TotalExecutedCycles; - public int PendingCycles; - - bool Interruptable; - - public void ExecuteInstruction() - { - byte TB; byte TB2; sbyte TSB; ushort TUS; int TI1; int TI2; int TIR; - while (RegPC.Word == 0x031A) - break; - byte op = ReadMemory(RegPC.Word++); - int mCycleTime = mCycleTable[op]; - PendingCycles -= mCycleTime; - TotalExecutedCycles += mCycleTime; - switch (op) - { - case 0x00: // NOP - break; - case 0x01: // LD BC, nn - RegBC.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x02: // LD (BC), A - WriteMemory(RegBC.Word, RegAF.High); - break; - case 0x03: // INC BC - ++RegBC.Word; - break; - case 0x04: // INC B - RegAF.Low = (byte)(IncTable[++RegBC.High] | (RegAF.Low & 16)); - break; - case 0x05: // DEC B - RegAF.Low = (byte)(DecTable[--RegBC.High] | (RegAF.Low & 16)); - break; - case 0x06: // LD B, n - RegBC.High = ReadMemory(RegPC.Word++); - break; - case 0x07: // RLCA - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High >> 7) | (RegAF.High << 1)); - break; - case 0x08: // LD (imm), SP - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - WriteMemory(TUS++, RegSP.Low); - WriteMemory(TUS, RegSP.High); - break; - case 0x09: // ADD HL, BC - TI1 = (short)RegHL.Word; TI2 = (short)RegBC.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x0A: // LD A, (BC) - RegAF.High = ReadMemory(RegBC.Word); - break; - case 0x0B: // DEC BC - --RegBC.Word; - break; - case 0x0C: // INC C - RegAF.Low = (byte)(IncTable[++RegBC.Low] | (RegAF.Low & 16)); - break; - case 0x0D: // DEC C - RegAF.Low = (byte)(DecTable[--RegBC.Low] | (RegAF.Low & 16)); - break; - case 0x0E: // LD C, n - RegBC.Low = ReadMemory(RegPC.Word++); - break; - case 0x0F: // RRCA - RegAF.High = (byte)((RegAF.High << 7) | (RegAF.High >> 1)); - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - break; - case 0x10: // STOP - Console.WriteLine("STOP!!!!!!!!!!!!!!!!!!!!!!"); // TODO this instruction is actually STOP. not DJNZ d. - throw new Exception("CPU stopped. What now?"); - case 0x11: // LD DE, nn - RegDE.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x12: // LD (DE), A - WriteMemory(RegDE.Word, RegAF.High); - break; - case 0x13: // INC DE - ++RegDE.Word; - break; - case 0x14: // INC D - RegAF.Low = (byte)(IncTable[++RegDE.High] | (RegAF.Low & 16)); - break; - case 0x15: // DEC D - RegAF.Low = (byte)(DecTable[--RegDE.High] | (RegAF.Low & 16)); - break; - case 0x16: // LD D, n - RegDE.High = ReadMemory(RegPC.Word++); - break; - case 0x17: // RLA - TB = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - break; - case 0x18: // JR d - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x19: // ADD HL, DE - TI1 = (short)RegHL.Word; TI2 = (short)RegDE.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x1A: // LD A, (DE) - RegAF.High = ReadMemory(RegDE.Word); - break; - case 0x1B: // DEC DE - --RegDE.Word; - break; - case 0x1C: // INC E - RegAF.Low = (byte)(IncTable[++RegDE.Low] | (RegAF.Low & 16)); - break; - case 0x1D: // DEC E - RegAF.Low = (byte)(DecTable[--RegDE.Low] | (RegAF.Low & 16)); - break; - case 0x1E: // LD E, n - RegDE.Low = ReadMemory(RegPC.Word++); - break; - case 0x1F: // RRA - TB = (byte)((RegAF.High & 0x1) << 4); - RegAF.High = (byte)((RegAF.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - break; - case 0x20: // JR NZ, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (!FlagZ) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x21: // LD HL, nn - RegHL.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x22: // LDI (HL), A - WriteMemory(RegHL.Word++, RegAF.High); - break; - case 0x23: // INC HL - ++RegHL.Word; - break; - case 0x24: // INC H - RegAF.Low = (byte)(IncTable[++RegHL.High] | (RegAF.Low & 16)); - break; - case 0x25: // DEC H - RegAF.Low = (byte)(DecTable[--RegHL.High] | (RegAF.Low & 16)); - break; - case 0x26: // LD H, n - RegHL.High = ReadMemory(RegPC.Word++); - break; - case 0x27: // DAA - RegAF.Word = TableDaa[RegAF.Word]; - break; - case 0x28: // JR Z, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (FlagZ) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x29: // ADD HL, HL - TI1 = (short)RegHL.Word; TI2 = (short)RegHL.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x2A: // LDI A, (HL) - RegAF.High = ReadMemory(RegHL.Word++); - break; - case 0x2B: // DEC HL - --RegHL.Word; - break; - case 0x2C: // INC L - RegAF.Low = (byte)(IncTable[++RegHL.Low] | (RegAF.Low & 16)); - break; - case 0x2D: // DEC L - RegAF.Low = (byte)(DecTable[--RegHL.Low] | (RegAF.Low & 16)); - break; - case 0x2E: // LD L, n - RegHL.Low = ReadMemory(RegPC.Word++); - break; - case 0x2F: // CPL - RegAF.High ^= 0xFF; FlagH = true; FlagN = true; - break; - case 0x30: // JR NC, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (!FlagC) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x31: // LD SP, nn - RegSP.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x32: // LDD (HL), A - WriteMemory(RegHL.Word--, RegAF.High); - break; - case 0x33: // INC SP - ++RegSP.Word; - break; - case 0x34: // INC (HL) - TB = ReadMemory(RegHL.Word); RegAF.Low = (byte)(IncTable[++TB] | (RegAF.Low & 16)); WriteMemory(RegHL.Word, TB); - break; - case 0x35: // DEC (HL) - TB = ReadMemory(RegHL.Word); RegAF.Low = (byte)(DecTable[--TB] | (RegAF.Low & 16)); WriteMemory(RegHL.Word, TB); - break; - case 0x36: // LD (HL), n - WriteMemory(RegHL.Word, ReadMemory(RegPC.Word++)); - break; - case 0x37: // SCF - FlagH = false; FlagN = false; FlagC = true; - break; - case 0x38: // JR C, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (FlagC) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x39: // ADD HL, SP - TI1 = (short)RegHL.Word; TI2 = (short)RegSP.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x3A: // LDD A, (HL) - RegAF.High = ReadMemory(RegHL.Word--); - break; - case 0x3B: // DEC SP - --RegSP.Word; - break; - case 0x3C: // INC A - RegAF.Low = (byte)(IncTable[++RegAF.High] | (RegAF.Low & 16)); - break; - case 0x3D: // DEC A - RegAF.Low = (byte)(DecTable[--RegAF.High] | (RegAF.Low & 16)); - break; - case 0x3E: // LD A, n - RegAF.High = ReadMemory(RegPC.Word++); - break; - case 0x3F: // CCF - FlagH = FlagC; FlagN = false; FlagC ^= true; - break; - case 0x40: // LD B, B - break; - case 0x41: // LD B, C - RegBC.High = RegBC.Low; - break; - case 0x42: // LD B, D - RegBC.High = RegDE.High; - break; - case 0x43: // LD B, E - RegBC.High = RegDE.Low; - break; - case 0x44: // LD B, H - RegBC.High = RegHL.High; - break; - case 0x45: // LD B, L - RegBC.High = RegHL.Low; - break; - case 0x46: // LD B, (HL) - RegBC.High = ReadMemory(RegHL.Word); - break; - case 0x47: // LD B, A - RegBC.High = RegAF.High; - break; - case 0x48: // LD C, B - RegBC.Low = RegBC.High; - break; - case 0x49: // LD C, C - break; - case 0x4A: // LD C, D - RegBC.Low = RegDE.High; - break; - case 0x4B: // LD C, E - RegBC.Low = RegDE.Low; - break; - case 0x4C: // LD C, H - RegBC.Low = RegHL.High; - break; - case 0x4D: // LD C, L - RegBC.Low = RegHL.Low; - break; - case 0x4E: // LD C, (HL) - RegBC.Low = ReadMemory(RegHL.Word); - break; - case 0x4F: // LD C, A - RegBC.Low = RegAF.High; - break; - case 0x50: // LD D, B - RegDE.High = RegBC.High; - break; - case 0x51: // LD D, C - RegDE.High = RegBC.Low; - break; - case 0x52: // LD D, D - break; - case 0x53: // LD D, E - RegDE.High = RegDE.Low; - break; - case 0x54: // LD D, H - RegDE.High = RegHL.High; - break; - case 0x55: // LD D, L - RegDE.High = RegHL.Low; - break; - case 0x56: // LD D, (HL) - RegDE.High = ReadMemory(RegHL.Word); - break; - case 0x57: // LD D, A - RegDE.High = RegAF.High; - break; - case 0x58: // LD E, B - RegDE.Low = RegBC.High; - break; - case 0x59: // LD E, C - RegDE.Low = RegBC.Low; - break; - case 0x5A: // LD E, D - RegDE.Low = RegDE.High; - break; - case 0x5B: // LD E, E - break; - case 0x5C: // LD E, H - RegDE.Low = RegHL.High; - break; - case 0x5D: // LD E, L - RegDE.Low = RegHL.Low; - break; - case 0x5E: // LD E, (HL) - RegDE.Low = ReadMemory(RegHL.Word); - break; - case 0x5F: // LD E, A - RegDE.Low = RegAF.High; - break; - case 0x60: // LD H, B - RegHL.High = RegBC.High; - break; - case 0x61: // LD H, C - RegHL.High = RegBC.Low; - break; - case 0x62: // LD H, D - RegHL.High = RegDE.High; - break; - case 0x63: // LD H, E - RegHL.High = RegDE.Low; - break; - case 0x64: // LD H, H - break; - case 0x65: // LD H, L - RegHL.High = RegHL.Low; - break; - case 0x66: // LD H, (HL) - RegHL.High = ReadMemory(RegHL.Word); - break; - case 0x67: // LD H, A - RegHL.High = RegAF.High; - break; - case 0x68: // LD L, B - RegHL.Low = RegBC.High; - break; - case 0x69: // LD L, C - RegHL.Low = RegBC.Low; - break; - case 0x6A: // LD L, D - RegHL.Low = RegDE.High; - break; - case 0x6B: // LD L, E - RegHL.Low = RegDE.Low; - break; - case 0x6C: // LD L, H - RegHL.Low = RegHL.High; - break; - case 0x6D: // LD L, L - break; - case 0x6E: // LD L, (HL) - RegHL.Low = ReadMemory(RegHL.Word); - break; - case 0x6F: // LD L, A - RegHL.Low = RegAF.High; - break; - case 0x70: // LD (HL), B - WriteMemory(RegHL.Word, RegBC.High); - break; - case 0x71: // LD (HL), C - WriteMemory(RegHL.Word, RegBC.Low); - break; - case 0x72: // LD (HL), D - WriteMemory(RegHL.Word, RegDE.High); - break; - case 0x73: // LD (HL), E - WriteMemory(RegHL.Word, RegDE.Low); - break; - case 0x74: // LD (HL), H - WriteMemory(RegHL.Word, RegHL.High); - break; - case 0x75: // LD (HL), L - WriteMemory(RegHL.Word, RegHL.Low); - break; - case 0x76: // HALT - Halt(); - break; - case 0x77: // LD (HL), A - WriteMemory(RegHL.Word, RegAF.High); - break; - case 0x78: // LD A, B - RegAF.High = RegBC.High; - break; - case 0x79: // LD A, C - RegAF.High = RegBC.Low; - break; - case 0x7A: // LD A, D - RegAF.High = RegDE.High; - break; - case 0x7B: // LD A, E - RegAF.High = RegDE.Low; - break; - case 0x7C: // LD A, H - RegAF.High = RegHL.High; - break; - case 0x7D: // LD A, L - RegAF.High = RegHL.Low; - break; - case 0x7E: // LD A, (HL) - RegAF.High = ReadMemory(RegHL.Word); - break; - case 0x7F: // LD A, A - break; - case 0x80: // ADD B - TI1 = RegAF.High + RegBC.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x81: // ADD C - TI1 = RegAF.High + RegBC.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x82: // ADD D - TI1 = RegAF.High + RegDE.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x83: // ADD E - TI1 = RegAF.High + RegDE.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x84: // ADD H - TI1 = RegAF.High + RegHL.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x85: // ADD L - TI1 = RegAF.High + RegHL.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x86: // ADD (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High + TB; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x87: // ADD A - TI1 = RegAF.High + RegAF.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegAF.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x88: // ADC B - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegBC.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x89: // ADC C - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegBC.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8A: // ADC D - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegDE.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8B: // ADC E - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegDE.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8C: // ADC H - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegHL.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8D: // ADC L - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegHL.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8E: // ADC (HL) - TB = ReadMemory(RegHL.Word); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + TB + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8F: // ADC A - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegAF.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegAF.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x90: // SUB B - TI1 = RegAF.High - RegBC.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x91: // SUB C - TI1 = RegAF.High - RegBC.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x92: // SUB D - TI1 = RegAF.High - RegDE.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x93: // SUB E - TI1 = RegAF.High - RegDE.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x94: // SUB H - TI1 = RegAF.High - RegHL.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x95: // SUB L - TI1 = RegAF.High - RegHL.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x96: // SUB (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x97: // SUB A - TI1 = RegAF.High - RegAF.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x98: // SBC B - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegBC.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x99: // SBC C - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegBC.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9A: // SBC D - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegDE.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9B: // SBC E - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegDE.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9C: // SBC H - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegHL.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9D: // SBC L - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegHL.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9E: // SBC (HL) - TB = ReadMemory(RegHL.Word); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - TB - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9F: // SBC A - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegAF.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xA0: // AND B - RegAF.High &= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA1: // AND C - RegAF.High &= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA2: // AND D - RegAF.High &= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA3: // AND E - RegAF.High &= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA4: // AND H - RegAF.High &= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA5: // AND L - RegAF.High &= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA6: // AND (HL) - RegAF.High &= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA7: // AND A - RegAF.High &= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA8: // XOR B - RegAF.High ^= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xA9: // XOR C - RegAF.High ^= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAA: // XOR D - RegAF.High ^= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAB: // XOR E - RegAF.High ^= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAC: // XOR H - RegAF.High ^= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAD: // XOR L - RegAF.High ^= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAE: // XOR (HL) - RegAF.High ^= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAF: // XOR A - RegAF.High ^= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB0: // OR B - RegAF.High |= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB1: // OR C - RegAF.High |= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB2: // OR D - RegAF.High |= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB3: // OR E - RegAF.High |= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB4: // OR H - RegAF.High |= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB5: // OR L - RegAF.High |= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB6: // OR (HL) - RegAF.High |= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB7: // OR A - RegAF.High |= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB8: // CP B - TI1 = RegAF.High - RegBC.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xB9: // CP C - TI1 = RegAF.High - RegBC.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBA: // CP D - TI1 = RegAF.High - RegDE.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBB: // CP E - TI1 = RegAF.High - RegDE.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBC: // CP H - TI1 = RegAF.High - RegHL.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBD: // CP L - TI1 = RegAF.High - RegHL.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBE: // CP (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBF: // CP A - TI1 = RegAF.High - RegAF.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xC0: // RET NZ - if (!FlagZ) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xC1: // POP BC - RegBC.Low = ReadMemory(RegSP.Word++); RegBC.High = ReadMemory(RegSP.Word++); - break; - case 0xC2: // JP NZ, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagZ) - RegPC.Word = TUS; - break; - case 0xC3: // JP nn - RegPC.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0xC4: // CALL NZ, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagZ) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xC5: // PUSH BC - WriteMemory(--RegSP.Word, RegBC.High); WriteMemory(--RegSP.Word, RegBC.Low); - break; - case 0xC6: // ADD n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High + TB; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xC7: // RST $00 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x00; - break; - case 0xC8: // RET Z - if (FlagZ) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xC9: // RET - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xCA: // JP Z, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagZ) - RegPC.Word = TUS; - break; - case 0xCB: // (Prefix) - op = ReadMemory(RegPC.Word++); - mCycleTime = cbMCycleTable[op]; - PendingCycles -= mCycleTime; - TotalExecutedCycles += mCycleTime; - switch (op) - { - case 0x00: // RLC B - RegAF.Low = (byte)((RegBC.High & 0x80) >> 3); - RegBC.High = (byte)((RegBC.High >> 7) | (RegBC.High << 1)); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x01: // RLC C - RegAF.Low = (byte)((RegBC.Low & 0x80) >> 3); - RegBC.Low = (byte)((RegBC.Low >> 7) | (RegBC.Low << 1)); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x02: // RLC D - RegAF.Low = (byte)((RegDE.High & 0x80) >> 3); - RegDE.High = (byte)((RegDE.High >> 7) | (RegDE.High << 1)); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x03: // RLC E - RegAF.Low = (byte)((RegDE.Low & 0x80) >> 3); - RegDE.Low = (byte)((RegDE.Low >> 7) | (RegDE.Low << 1)); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x04: // RLC H - RegAF.Low = (byte)((RegHL.High & 0x80) >> 3); - RegHL.High = (byte)((RegHL.High >> 7) | (RegHL.High << 1)); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x05: // RLC L - RegAF.Low = (byte)((RegHL.Low & 0x80) >> 3); - RegHL.Low = (byte)((RegHL.Low >> 7) | (RegHL.Low << 1)); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x06: // RLC (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = (byte)((TB & 0x80) >> 3); - TB = (byte)((TB >> 7) | (TB << 1)); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x07: // RLC A - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High >> 7) | (RegAF.High << 1)); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x08: // RRC B - RegBC.High = (byte)((RegBC.High << 7) | (RegBC.High >> 1)); - RegAF.Low = (byte)((RegBC.High & 0x80) >> 3); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x09: // RRC C - RegBC.Low = (byte)((RegBC.Low << 7) | (RegBC.Low >> 1)); - RegAF.Low = (byte)((RegBC.Low & 0x80) >> 3); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0A: // RRC D - RegDE.High = (byte)((RegDE.High << 7) | (RegDE.High >> 1)); - RegAF.Low = (byte)((RegDE.High & 0x80) >> 3); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x0B: // RRC E - RegDE.Low = (byte)((RegDE.Low << 7) | (RegDE.Low >> 1)); - RegAF.Low = (byte)((RegDE.Low & 0x80) >> 3); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0C: // RRC H - RegHL.High = (byte)((RegHL.High << 7) | (RegHL.High >> 1)); - RegAF.Low = (byte)((RegHL.High & 0x80) >> 3); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x0D: // RRC L - RegHL.Low = (byte)((RegHL.Low << 7) | (RegHL.Low >> 1)); - RegAF.Low = (byte)((RegHL.Low & 0x80) >> 3); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0E: // RRC (HL) - TB = ReadMemory(RegHL.Word); - TB = (byte)((TB << 7) | (TB >> 1)); - RegAF.Low = (byte)((TB & 0x80) >> 3); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x0F: // RRC A - RegAF.High = (byte)((RegAF.High << 7) | (RegAF.High >> 1)); - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x10: // RL B - TB = (byte)((RegBC.High & 0x80) >> 3); - RegBC.High = (byte)((RegBC.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x11: // RL C - TB = (byte)((RegBC.Low & 0x80) >> 3); - RegBC.Low = (byte)((RegBC.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x12: // RL D - TB = (byte)((RegDE.High & 0x80) >> 3); - RegDE.High = (byte)((RegDE.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x13: // RL E - TB = (byte)((RegDE.Low & 0x80) >> 3); - RegDE.Low = (byte)((RegDE.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x14: // RL H - TB = (byte)((RegHL.High & 0x80) >> 3); - RegHL.High = (byte)((RegHL.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x15: // RL L - TB = (byte)((RegHL.Low & 0x80) >> 3); - RegHL.Low = (byte)((RegHL.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x16: // RL (HL) - TB2 = ReadMemory(RegHL.Word); - TB = (byte)((TB2 & 0x80) >> 3); - TB2 = (byte)((TB2 << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (TB2 == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB2); - break; - case 0x17: // RL A - TB = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x18: // RR B - TB = (byte)((RegBC.High & 0x1) << 4); - RegBC.High = (byte)((RegBC.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x19: // RR C - TB = (byte)((RegBC.Low & 0x1) << 4); - RegBC.Low = (byte)((RegBC.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1A: // RR D - TB = (byte)((RegDE.High & 0x1) << 4); - RegDE.High = (byte)((RegDE.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x1B: // RR E - TB = (byte)((RegDE.Low & 0x1) << 4); - RegDE.Low = (byte)((RegDE.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1C: // RR H - TB = (byte)((RegHL.High & 0x1) << 4); - RegHL.High = (byte)((RegHL.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x1D: // RR L - TB = (byte)((RegHL.Low & 0x1) << 4); - RegHL.Low = (byte)((RegHL.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1E: // RR (HL) - TB2 = ReadMemory(RegHL.Word); - TB = (byte)((TB2 & 0x1) << 4); - TB2 = (byte)((TB2 >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (TB2 == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB2); - break; - case 0x1F: // RR A - TB = (byte)((RegAF.High & 0x1) << 4); - RegAF.High = (byte)((RegAF.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x20: // SLA B - RegAF.Low = 0; - if ((RegBC.High & 0x80) != 0) RegAF.Low |= 0x10; - RegBC.High <<= 1; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x21: // SLA C - RegAF.Low = 0; - if ((RegBC.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegBC.Low <<= 1; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x22: // SLA D - RegAF.Low = 0; - if ((RegDE.High & 0x80) != 0) RegAF.Low |= 0x10; - RegDE.High <<= 1; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x23: // SLA E - RegAF.Low = 0; - if ((RegDE.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegDE.Low <<= 1; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x24: // SLA H - RegAF.Low = 0; - if ((RegHL.High & 0x80) != 0) RegAF.Low |= 0x10; - RegHL.High <<= 1; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x25: // SLA L - RegAF.Low = 0; - if ((RegHL.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegHL.Low <<= 1; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x26: // SLA (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 0x80) != 0) RegAF.Low |= 0x10; - TB <<= 1; - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x27: // SLA A - RegAF.Low = 0; - if ((RegAF.High & 0x80) != 0) RegAF.Low |= 0x10; - RegAF.High <<= 1; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x28: // SRA B - RegAF.Low = 0; - if ((RegBC.High & 1) != 0) RegAF.Low |= 0x10; - RegBC.High = (byte)((RegBC.High >> 1) | (RegBC.High & 0x80)); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x29: // SRA C - RegAF.Low = 0; - if ((RegBC.Low & 1) != 0) RegAF.Low |= 0x10; - RegBC.Low = (byte)((RegBC.Low >> 1) | (RegBC.Low & 0x80)); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2A: // SRA D - RegAF.Low = 0; - if ((RegDE.High & 1) != 0) RegAF.Low |= 0x10; - RegDE.High = (byte)((RegDE.High >> 1) | (RegDE.High & 0x80)); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x2B: // SRA E - RegAF.Low = 0; - if ((RegDE.Low & 1) != 0) RegAF.Low |= 0x10; - RegDE.Low = (byte)((RegDE.Low >> 1) | (RegDE.Low & 0x80)); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2C: // SRA H - RegAF.Low = 0; - if ((RegHL.High & 1) != 0) RegAF.Low |= 0x10; - RegHL.High = (byte)((RegHL.High >> 1) | (RegHL.High & 0x80)); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x2D: // SRA L - RegAF.Low = 0; - if ((RegHL.Low & 1) != 0) RegAF.Low |= 0x10; - RegHL.Low = (byte)((RegHL.Low >> 1) | (RegHL.Low & 0x80)); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2E: // SRA (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 1) != 0) RegAF.Low |= 0x10; - TB = (byte)((TB >> 1) | (TB & 0x80)); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x2F: // SRA A - RegAF.Low = 0; - if ((RegAF.High & 1) != 0) RegAF.Low |= 0x10; - RegAF.High = (byte)((RegAF.High >> 1) | (RegAF.High & 0x80)); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x30: // SWAP B - RegBC.High = SwapTable[RegBC.High]; - FlagZ = (RegBC.High == 0); - break; - case 0x31: // SWAP C - RegBC.Low = SwapTable[RegBC.Low]; - FlagZ = (RegBC.Low == 0); - break; - case 0x32: // SWAP D - RegDE.High = SwapTable[RegDE.High]; - FlagZ = (RegDE.High == 0); - break; - case 0x33: // SWAP E - RegDE.Low = SwapTable[RegDE.Low]; - FlagZ = (RegDE.Low == 0); - break; - case 0x34: // SWAP H - RegHL.High = SwapTable[RegHL.High]; - FlagZ = (RegHL.High == 0); - break; - case 0x35: // SWAP L - RegHL.Low = SwapTable[RegHL.Low]; - FlagZ = (RegHL.Low == 0); - break; - case 0x36: // SWAP (HL) - TB = SwapTable[ReadMemory(RegHL.Word)]; - WriteMemory(RegHL.Word, TB); - FlagZ = (TB == 0); - break; - case 0x37: // SWAP A - RegAF.High = SwapTable[RegAF.High]; - FlagZ = (RegAF.High == 0); - break; - case 0x38: // SRL B - RegAF.Low = 0; - if ((RegBC.High & 1) != 0) RegAF.Low |= 0x10; - RegBC.High >>= 1; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x39: // SRL C - RegAF.Low = 0; - if ((RegBC.Low & 1) != 0) RegAF.Low |= 0x10; - RegBC.Low >>= 1; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3A: // SRL D - RegAF.Low = 0; - if ((RegDE.High & 1) != 0) RegAF.Low |= 0x10; - RegDE.High >>= 1; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x3B: // SRL E - RegAF.Low = 0; - if ((RegDE.Low & 1) != 0) RegAF.Low |= 0x10; - RegDE.Low >>= 1; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3C: // SRL H - RegAF.Low = 0; - if ((RegHL.High & 1) != 0) RegAF.Low |= 0x10; - RegHL.High >>= 1; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x3D: // SRL L - RegAF.Low = 0; - if ((RegHL.Low & 1) != 0) RegAF.Low |= 0x10; - RegHL.Low >>= 1; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3E: // SRL (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 1) != 0) RegAF.Low |= 0x10; - TB >>= 1; - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x3F: // SRL A - RegAF.Low = 0; - if ((RegAF.High & 1) != 0) RegAF.Low |= 0x10; - RegAF.High >>= 1; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x40: // BIT 0, B - FlagZ = (RegBC.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x41: // BIT 0, C - FlagZ = (RegBC.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x42: // BIT 0, D - FlagZ = (RegDE.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x43: // BIT 0, E - FlagZ = (RegDE.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x44: // BIT 0, H - FlagZ = (RegHL.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x45: // BIT 0, L - FlagZ = (RegHL.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x46: // BIT 0, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x47: // BIT 0, A - FlagZ = (RegAF.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x48: // BIT 1, B - FlagZ = (RegBC.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x49: // BIT 1, C - FlagZ = (RegBC.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4A: // BIT 1, D - FlagZ = (RegDE.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4B: // BIT 1, E - FlagZ = (RegDE.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4C: // BIT 1, H - FlagZ = (RegHL.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4D: // BIT 1, L - FlagZ = (RegHL.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4E: // BIT 1, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4F: // BIT 1, A - FlagZ = (RegAF.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x50: // BIT 2, B - FlagZ = (RegBC.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x51: // BIT 2, C - FlagZ = (RegBC.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x52: // BIT 2, D - FlagZ = (RegDE.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x53: // BIT 2, E - FlagZ = (RegDE.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x54: // BIT 2, H - FlagZ = (RegHL.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x55: // BIT 2, L - FlagZ = (RegHL.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x56: // BIT 2, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x57: // BIT 2, A - FlagZ = (RegAF.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x58: // BIT 3, B - FlagZ = (RegBC.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x59: // BIT 3, C - FlagZ = (RegBC.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5A: // BIT 3, D - FlagZ = (RegDE.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5B: // BIT 3, E - FlagZ = (RegDE.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5C: // BIT 3, H - FlagZ = (RegHL.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5D: // BIT 3, L - FlagZ = (RegHL.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5E: // BIT 3, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5F: // BIT 3, A - FlagZ = (RegAF.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x60: // BIT 4, B - FlagZ = (RegBC.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x61: // BIT 4, C - FlagZ = (RegBC.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x62: // BIT 4, D - FlagZ = (RegDE.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x63: // BIT 4, E - FlagZ = (RegDE.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x64: // BIT 4, H - FlagZ = (RegHL.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x65: // BIT 4, L - FlagZ = (RegHL.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x66: // BIT 4, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x67: // BIT 4, A - FlagZ = (RegAF.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x68: // BIT 5, B - FlagZ = (RegBC.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x69: // BIT 5, C - FlagZ = (RegBC.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6A: // BIT 5, D - FlagZ = (RegDE.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6B: // BIT 5, E - FlagZ = (RegDE.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6C: // BIT 5, H - FlagZ = (RegHL.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6D: // BIT 5, L - FlagZ = (RegHL.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6E: // BIT 5, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6F: // BIT 5, A - FlagZ = (RegAF.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x70: // BIT 6, B - FlagZ = (RegBC.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x71: // BIT 6, C - FlagZ = (RegBC.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x72: // BIT 6, D - FlagZ = (RegDE.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x73: // BIT 6, E - FlagZ = (RegDE.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x74: // BIT 6, H - FlagZ = (RegHL.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x75: // BIT 6, L - FlagZ = (RegHL.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x76: // BIT 6, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x77: // BIT 6, A - FlagZ = (RegAF.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x78: // BIT 7, B - FlagZ = (RegBC.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x79: // BIT 7, C - FlagZ = (RegBC.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7A: // BIT 7, D - FlagZ = (RegDE.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7B: // BIT 7, E - FlagZ = (RegDE.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7C: // BIT 7, H - FlagZ = (RegHL.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7D: // BIT 7, L - FlagZ = (RegHL.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7E: // BIT 7, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7F: // BIT 7, A - FlagZ = (RegAF.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x80: // RES 0, B - RegBC.High &= unchecked((byte)~0x01); - break; - case 0x81: // RES 0, C - RegBC.Low &= unchecked((byte)~0x01); - break; - case 0x82: // RES 0, D - RegDE.High &= unchecked((byte)~0x01); - break; - case 0x83: // RES 0, E - RegDE.Low &= unchecked((byte)~0x01); - break; - case 0x84: // RES 0, H - RegHL.High &= unchecked((byte)~0x01); - break; - case 0x85: // RES 0, L - RegHL.Low &= unchecked((byte)~0x01); - break; - case 0x86: // RES 0, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x01))); - break; - case 0x87: // RES 0, A - RegAF.High &= unchecked((byte)~0x01); - break; - case 0x88: // RES 1, B - RegBC.High &= unchecked((byte)~0x02); - break; - case 0x89: // RES 1, C - RegBC.Low &= unchecked((byte)~0x02); - break; - case 0x8A: // RES 1, D - RegDE.High &= unchecked((byte)~0x02); - break; - case 0x8B: // RES 1, E - RegDE.Low &= unchecked((byte)~0x02); - break; - case 0x8C: // RES 1, H - RegHL.High &= unchecked((byte)~0x02); - break; - case 0x8D: // RES 1, L - RegHL.Low &= unchecked((byte)~0x02); - break; - case 0x8E: // RES 1, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x02))); - break; - case 0x8F: // RES 1, A - RegAF.High &= unchecked((byte)~0x02); - break; - case 0x90: // RES 2, B - RegBC.High &= unchecked((byte)~0x04); - break; - case 0x91: // RES 2, C - RegBC.Low &= unchecked((byte)~0x04); - break; - case 0x92: // RES 2, D - RegDE.High &= unchecked((byte)~0x04); - break; - case 0x93: // RES 2, E - RegDE.Low &= unchecked((byte)~0x04); - break; - case 0x94: // RES 2, H - RegHL.High &= unchecked((byte)~0x04); - break; - case 0x95: // RES 2, L - RegHL.Low &= unchecked((byte)~0x04); - break; - case 0x96: // RES 2, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x04))); - break; - case 0x97: // RES 2, A - RegAF.High &= unchecked((byte)~0x04); - break; - case 0x98: // RES 3, B - RegBC.High &= unchecked((byte)~0x08); - break; - case 0x99: // RES 3, C - RegBC.Low &= unchecked((byte)~0x08); - break; - case 0x9A: // RES 3, D - RegDE.High &= unchecked((byte)~0x08); - break; - case 0x9B: // RES 3, E - RegDE.Low &= unchecked((byte)~0x08); - break; - case 0x9C: // RES 3, H - RegHL.High &= unchecked((byte)~0x08); - break; - case 0x9D: // RES 3, L - RegHL.Low &= unchecked((byte)~0x08); - break; - case 0x9E: // RES 3, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x08))); - break; - case 0x9F: // RES 3, A - RegAF.High &= unchecked((byte)~0x08); - break; - case 0xA0: // RES 4, B - RegBC.High &= unchecked((byte)~0x10); - break; - case 0xA1: // RES 4, C - RegBC.Low &= unchecked((byte)~0x10); - break; - case 0xA2: // RES 4, D - RegDE.High &= unchecked((byte)~0x10); - break; - case 0xA3: // RES 4, E - RegDE.Low &= unchecked((byte)~0x10); - break; - case 0xA4: // RES 4, H - RegHL.High &= unchecked((byte)~0x10); - break; - case 0xA5: // RES 4, L - RegHL.Low &= unchecked((byte)~0x10); - break; - case 0xA6: // RES 4, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x10))); - break; - case 0xA7: // RES 4, A - RegAF.High &= unchecked((byte)~0x10); - break; - case 0xA8: // RES 5, B - RegBC.High &= unchecked((byte)~0x20); - break; - case 0xA9: // RES 5, C - RegBC.Low &= unchecked((byte)~0x20); - break; - case 0xAA: // RES 5, D - RegDE.High &= unchecked((byte)~0x20); - break; - case 0xAB: // RES 5, E - RegDE.Low &= unchecked((byte)~0x20); - break; - case 0xAC: // RES 5, H - RegHL.High &= unchecked((byte)~0x20); - break; - case 0xAD: // RES 5, L - RegHL.Low &= unchecked((byte)~0x20); - break; - case 0xAE: // RES 5, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x20))); - break; - case 0xAF: // RES 5, A - RegAF.High &= unchecked((byte)~0x20); - break; - case 0xB0: // RES 6, B - RegBC.High &= unchecked((byte)~0x40); - break; - case 0xB1: // RES 6, C - RegBC.Low &= unchecked((byte)~0x40); - break; - case 0xB2: // RES 6, D - RegDE.High &= unchecked((byte)~0x40); - break; - case 0xB3: // RES 6, E - RegDE.Low &= unchecked((byte)~0x40); - break; - case 0xB4: // RES 6, H - RegHL.High &= unchecked((byte)~0x40); - break; - case 0xB5: // RES 6, L - RegHL.Low &= unchecked((byte)~0x40); - break; - case 0xB6: // RES 6, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x40))); - break; - case 0xB7: // RES 6, A - RegAF.High &= unchecked((byte)~0x40); - break; - case 0xB8: // RES 7, B - RegBC.High &= unchecked((byte)~0x80); - break; - case 0xB9: // RES 7, C - RegBC.Low &= unchecked((byte)~0x80); - break; - case 0xBA: // RES 7, D - RegDE.High &= unchecked((byte)~0x80); - break; - case 0xBB: // RES 7, E - RegDE.Low &= unchecked((byte)~0x80); - break; - case 0xBC: // RES 7, H - RegHL.High &= unchecked((byte)~0x80); - break; - case 0xBD: // RES 7, L - RegHL.Low &= unchecked((byte)~0x80); - break; - case 0xBE: // RES 7, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x80))); - break; - case 0xBF: // RES 7, A - RegAF.High &= unchecked((byte)~0x80); - break; - case 0xC0: // SET 0, B - RegBC.High |= unchecked(0x01); - break; - case 0xC1: // SET 0, C - RegBC.Low |= unchecked(0x01); - break; - case 0xC2: // SET 0, D - RegDE.High |= unchecked(0x01); - break; - case 0xC3: // SET 0, E - RegDE.Low |= unchecked(0x01); - break; - case 0xC4: // SET 0, H - RegHL.High |= unchecked(0x01); - break; - case 0xC5: // SET 0, L - RegHL.Low |= unchecked(0x01); - break; - case 0xC6: // SET 0, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x01))); - break; - case 0xC7: // SET 0, A - RegAF.High |= unchecked(0x01); - break; - case 0xC8: // SET 1, B - RegBC.High |= unchecked(0x02); - break; - case 0xC9: // SET 1, C - RegBC.Low |= unchecked(0x02); - break; - case 0xCA: // SET 1, D - RegDE.High |= unchecked(0x02); - break; - case 0xCB: // SET 1, E - RegDE.Low |= unchecked(0x02); - break; - case 0xCC: // SET 1, H - RegHL.High |= unchecked(0x02); - break; - case 0xCD: // SET 1, L - RegHL.Low |= unchecked(0x02); - break; - case 0xCE: // SET 1, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x02))); - break; - case 0xCF: // SET 1, A - RegAF.High |= unchecked(0x02); - break; - case 0xD0: // SET 2, B - RegBC.High |= unchecked(0x04); - break; - case 0xD1: // SET 2, C - RegBC.Low |= unchecked(0x04); - break; - case 0xD2: // SET 2, D - RegDE.High |= unchecked(0x04); - break; - case 0xD3: // SET 2, E - RegDE.Low |= unchecked(0x04); - break; - case 0xD4: // SET 2, H - RegHL.High |= unchecked(0x04); - break; - case 0xD5: // SET 2, L - RegHL.Low |= unchecked(0x04); - break; - case 0xD6: // SET 2, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x04))); - break; - case 0xD7: // SET 2, A - RegAF.High |= unchecked(0x04); - break; - case 0xD8: // SET 3, B - RegBC.High |= unchecked(0x08); - break; - case 0xD9: // SET 3, C - RegBC.Low |= unchecked(0x08); - break; - case 0xDA: // SET 3, D - RegDE.High |= unchecked(0x08); - break; - case 0xDB: // SET 3, E - RegDE.Low |= unchecked(0x08); - break; - case 0xDC: // SET 3, H - RegHL.High |= unchecked(0x08); - break; - case 0xDD: // SET 3, L - RegHL.Low |= unchecked(0x08); - break; - case 0xDE: // SET 3, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x08))); - break; - case 0xDF: // SET 3, A - RegAF.High |= unchecked(0x08); - break; - case 0xE0: // SET 4, B - RegBC.High |= unchecked(0x10); - break; - case 0xE1: // SET 4, C - RegBC.Low |= unchecked(0x10); - break; - case 0xE2: // SET 4, D - RegDE.High |= unchecked(0x10); - break; - case 0xE3: // SET 4, E - RegDE.Low |= unchecked(0x10); - break; - case 0xE4: // SET 4, H - RegHL.High |= unchecked(0x10); - break; - case 0xE5: // SET 4, L - RegHL.Low |= unchecked(0x10); - break; - case 0xE6: // SET 4, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x10))); - break; - case 0xE7: // SET 4, A - RegAF.High |= unchecked(0x10); - break; - case 0xE8: // SET 5, B - RegBC.High |= unchecked(0x20); - break; - case 0xE9: // SET 5, C - RegBC.Low |= unchecked(0x20); - break; - case 0xEA: // SET 5, D - RegDE.High |= unchecked(0x20); - break; - case 0xEB: // SET 5, E - RegDE.Low |= unchecked(0x20); - break; - case 0xEC: // SET 5, H - RegHL.High |= unchecked(0x20); - break; - case 0xED: // SET 5, L - RegHL.Low |= unchecked(0x20); - break; - case 0xEE: // SET 5, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x20))); - break; - case 0xEF: // SET 5, A - RegAF.High |= unchecked(0x20); - break; - case 0xF0: // SET 6, B - RegBC.High |= unchecked(0x40); - break; - case 0xF1: // SET 6, C - RegBC.Low |= unchecked(0x40); - break; - case 0xF2: // SET 6, D - RegDE.High |= unchecked(0x40); - break; - case 0xF3: // SET 6, E - RegDE.Low |= unchecked(0x40); - break; - case 0xF4: // SET 6, H - RegHL.High |= unchecked(0x40); - break; - case 0xF5: // SET 6, L - RegHL.Low |= unchecked(0x40); - break; - case 0xF6: // SET 6, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x40))); - break; - case 0xF7: // SET 6, A - RegAF.High |= unchecked(0x40); - break; - case 0xF8: // SET 7, B - RegBC.High |= unchecked(0x80); - break; - case 0xF9: // SET 7, C - RegBC.Low |= unchecked(0x80); - break; - case 0xFA: // SET 7, D - RegDE.High |= unchecked(0x80); - break; - case 0xFB: // SET 7, E - RegDE.Low |= unchecked(0x80); - break; - case 0xFC: // SET 7, H - RegHL.High |= unchecked(0x80); - break; - case 0xFD: // SET 7, L - RegHL.Low |= unchecked(0x80); - break; - case 0xFE: // SET 7, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x80))); - break; - case 0xFF: // SET 7, A - RegAF.High |= unchecked(0x80); - break; - } - break; - case 0xCC: // CALL Z, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagZ) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xCD: // CALL nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - break; - case 0xCE: // ADC n - TB = ReadMemory(RegPC.Word++); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + TB + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xCF: // RST $08 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x08; - break; - case 0xD0: // RET NC - if (!FlagC) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xD1: // POP DE - RegDE.Low = ReadMemory(RegSP.Word++); RegDE.High = ReadMemory(RegSP.Word++); - break; - case 0xD2: // JP NC, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagC) - RegPC.Word = TUS; - break; - case 0xD3: // NOP - break; - case 0xD4: // CALL NC, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagC) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xD5: // PUSH DE - WriteMemory(--RegSP.Word, RegDE.High); WriteMemory(--RegSP.Word, RegDE.Low); - break; - case 0xD6: // SUB n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xD7: // RST $10 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x10; - break; - case 0xD8: // RET C - if (FlagC) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xD9: // RETI - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - // TODO Nothing else special needs to be done? - break; - case 0xDA: // JP C, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagC) - RegPC.Word = TUS; - break; - case 0xDB: // NOP - break; - case 0xDC: // CALL C, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagC) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xDD: // NOP - break; - case 0xDE: // SBC A, n - TB = ReadMemory(RegPC.Word++); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - TB - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xDF: // RST $18 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x18; - break; - case 0xE0: // LD ($FF00+nn), A - WriteMemory((ushort)(0xFF00 + ReadMemory(RegPC.Word++)), RegAF.High); - break; - case 0xE1: // POP HL - RegHL.Low = ReadMemory(RegSP.Word++); RegHL.High = ReadMemory(RegSP.Word++); - break; - case 0xE2: // LD ($FF00+C), A - WriteMemory((ushort)(0xFF00 + RegBC.Low), RegAF.High); - break; - case 0xE3: // NOP - break; - case 0xE4: // NOP - break; - case 0xE5: // PUSH HL - WriteMemory(--RegSP.Word, RegHL.High); WriteMemory(--RegSP.Word, RegHL.Low); - break; - case 0xE6: // AND n - RegAF.High &= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xE7: // RST $20 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x20; - break; - case 0xE8: // ADD SP, n - Console.WriteLine("E8 : ADD SP, n being executed. verify correctness"); - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegAF.Low = 0; - if (RegSP.Word + TSB > 0xFFFF) RegAF.Low |= 0x10; - if (((RegSP.Word & 0xFFF) + TSB) > 0xFFF) RegAF.Low |= 0x20; - RegSP.Word = (ushort)(RegSP.Word + TSB); - break; - case 0xE9: // JP HL - RegPC.Word = RegHL.Word; - break; - case 0xEA: // LD (imm), A - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - WriteMemory(TUS, RegAF.High); - break; - case 0xEB: // NOP - break; - case 0xEC: // NOP - break; - case 0xED: // NOP - break; - case 0xEE: // XOR n - RegAF.High ^= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xEF: // RST $28 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x28; - break; - case 0xF0: // LD A, ($FF00+nn) - RegAF.High = ReadMemory((ushort)(0xFF00 + ReadMemory(RegPC.Word++))); - break; - case 0xF1: // POP AF - RegAF.Low = ReadMemory(RegSP.Word++); RegAF.High = ReadMemory(RegSP.Word++); - break; - case 0xF2: // LD A, ($FF00+C) - RegAF.High = ReadMemory((ushort)(0xFF00 + RegBC.Low)); - break; - case 0xF3: // DI - IFF1 = IFF2 = false; - break; - case 0xF4: // NOP - break; - case 0xF5: // PUSH AF - WriteMemory(--RegSP.Word, RegAF.High); WriteMemory(--RegSP.Word, RegAF.Low); - break; - case 0xF6: // OR n - RegAF.High |= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xF7: // RST $30 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x30; - break; - case 0xF8: // LD HL, SP+nn - Console.WriteLine("F8 : LD HL, SP+n being executed. verify correctness"); - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegAF.Low = 0; - if (RegSP.Word + TSB > 0xFFFF) RegAF.Low |= 0x10; - if (((RegSP.Word & 0xFFF) + TSB) > 0xFFF) RegAF.Low |= 0x20; - RegHL.Word = (ushort)(RegSP.Word + TSB); - break; - case 0xF9: // LD SP, HL - RegSP.Word = RegHL.Word; - break; - case 0xFA: // LD A, (nnnn) - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - RegAF.High = ReadMemory(TUS); - break; - case 0xFB: // EI - IFF1 = IFF2 = true; - Interruptable = false; - break; - case 0xFC: // NOP - break; - case 0xFD: // NOP - break; - case 0xFE: // CP n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xFF: // RST $38 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x38; - break; - default: throw new Exception("unhandled opcode"); - } - LogData(); - } - - void CheckIrq() - { - if (nonMaskableInterruptPending) - { - halted = false; - - PendingCycles -= 3; - TotalExecutedCycles += 3; - nonMaskableInterruptPending = false; - - iff2 = iff1; - iff1 = false; - - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x66; - } - else if (iff1 && interrupt && Interruptable) - { - Halted = false; - - iff1 = iff2 = false; - - switch (interruptMode) - { - case 0: - PendingCycles -= 4; - TotalExecutedCycles += 4; - break; - case 1: - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x38; - PendingCycles -= 4; - TotalExecutedCycles += 4; - break; - case 2: - ushort TUS = (ushort)(RegI * 256 + 0); - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Low = ReadMemory(TUS++); RegPC.High = ReadMemory(TUS); - PendingCycles -= 5; - TotalExecutedCycles += 5; - break; - } - } - } - - public void SingleStepInto() - { - if (halted) return; - ExecuteInstruction(); - CheckIrq(); - } - - public void ExecuteCycles(int cycles) - { - PendingCycles += cycles; - - while (PendingCycles > 0) - { - Interruptable = true; - - if (halted) - { - PendingCycles -= 1; - } - else - { - ExecuteInstruction(); - } - - CheckIrq(); - } - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs deleted file mode 100644 index 59c76cdc07..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - private bool iff1; - public bool IFF1 { get { return iff1; } set { iff1 = value; } } - - private bool iff2; - public bool IFF2 { get { return iff2; } set { iff2 = value; } } - - private bool interrupt; - public bool Interrupt { get { return interrupt; } set { interrupt = value; } } - - private bool nonMaskableInterrupt; - public bool NonMaskableInterrupt - { - get { return nonMaskableInterrupt; } - set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; } - } - - private bool nonMaskableInterruptPending; - public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } } - - private int interruptMode; - public int InterruptMode - { - get { return interruptMode; } - set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } - } - - private bool halted; - public bool Halted { get { return halted; } set { halted = value; } } - - private void ResetInterrupts() - { - IFF1 = false; - IFF2 = false; - Interrupt = false; - NonMaskableInterrupt = false; - NonMaskableInterruptPending = false; - InterruptMode = 1; - Halted = false; - } - - private void Halt() - { - Halted = true; - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs deleted file mode 100644 index a5d798ec9b..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs +++ /dev/null @@ -1,587 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - // adapted from the information at http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html - public class NewDisassembler - { - static string[] table = - { - "NOP", // 00 - "LD BC,d16", // 01 - "LD (BC),A", // 02 - "INC BC", // 03 - "INC B", // 04 - "DEC B", // 05 - "LD B,d8", // 06 - "RLCA", // 07 - "LD (a16),SP", // 08 - "ADD HL,BC", // 09 - "LD A,(BC)", // 0a - "DEC BC", // 0b - "INC C", // 0c - "DEC C", // 0d - "LD C,d8", // 0e - "RRCA", // 0f - "STOP 0", // 10 - "LD DE,d16", // 11 - "LD (DE),A", // 12 - "INC DE", // 13 - "INC D", // 14 - "DEC D", // 15 - "LD D,d8", // 16 - "RLA", // 17 - "JR r8", // 18 - "ADD HL,DE", // 19 - "LD A,(DE)", // 1a - "DEC DE", // 1b - "INC E", // 1c - "DEC E", // 1d - "LD E,d8", // 1e - "RRA", // 1f - "JR NZ,r8", // 20 - "LD HL,d16", // 21 - "LD (HL+),A", // 22 - "INC HL", // 23 - "INC H", // 24 - "DEC H", // 25 - "LD H,d8", // 26 - "DAA", // 27 - "JR Z,r8", // 28 - "ADD HL,HL", // 29 - "LD A,(HL+)", // 2a - "DEC HL", // 2b - "INC L", // 2c - "DEC L", // 2d - "LD L,d8", // 2e - "CPL", // 2f - "JR NC,r8", // 30 - "LD SP,d16", // 31 - "LD (HL-),A", // 32 - "INC SP", // 33 - "INC (HL)", // 34 - "DEC (HL)", // 35 - "LD (HL),d8", // 36 - "SCF", // 37 - "JR C,r8", // 38 - "ADD HL,SP", // 39 - "LD A,(HL-)", // 3a - "DEC SP", // 3b - "INC A", // 3c - "DEC A", // 3d - "LD A,d8", // 3e - "CCF", // 3f - "LD B,B", // 40 - "LD B,C", // 41 - "LD B,D", // 42 - "LD B,E", // 43 - "LD B,H", // 44 - "LD B,L", // 45 - "LD B,(HL)", // 46 - "LD B,A", // 47 - "LD C,B", // 48 - "LD C,C", // 49 - "LD C,D", // 4a - "LD C,E", // 4b - "LD C,H", // 4c - "LD C,L", // 4d - "LD C,(HL)", // 4e - "LD C,A", // 4f - "LD D,B", // 50 - "LD D,C", // 51 - "LD D,D", // 52 - "LD D,E", // 53 - "LD D,H", // 54 - "LD D,L", // 55 - "LD D,(HL)", // 56 - "LD D,A", // 57 - "LD E,B", // 58 - "LD E,C", // 59 - "LD E,D", // 5a - "LD E,E", // 5b - "LD E,H", // 5c - "LD E,L", // 5d - "LD E,(HL)", // 5e - "LD E,A", // 5f - "LD H,B", // 60 - "LD H,C", // 61 - "LD H,D", // 62 - "LD H,E", // 63 - "LD H,H", // 64 - "LD H,L", // 65 - "LD H,(HL)", // 66 - "LD H,A", // 67 - "LD L,B", // 68 - "LD L,C", // 69 - "LD L,D", // 6a - "LD L,E", // 6b - "LD L,H", // 6c - "LD L,L", // 6d - "LD L,(HL)", // 6e - "LD L,A", // 6f - "LD (HL),B", // 70 - "LD (HL),C", // 71 - "LD (HL),D", // 72 - "LD (HL),E", // 73 - "LD (HL),H", // 74 - "LD (HL),L", // 75 - "HALT", // 76 - "LD (HL),A", // 77 - "LD A,B", // 78 - "LD A,C", // 79 - "LD A,D", // 7a - "LD A,E", // 7b - "LD A,H", // 7c - "LD A,L", // 7d - "LD A,(HL)", // 7e - "LD A,A", // 7f - "ADD A,B", // 80 - "ADD A,C", // 81 - "ADD A,D", // 82 - "ADD A,E", // 83 - "ADD A,H", // 84 - "ADD A,L", // 85 - "ADD A,(HL)", // 86 - "ADD A,A", // 87 - "ADC A,B", // 88 - "ADC A,C", // 89 - "ADC A,D", // 8a - "ADC A,E", // 8b - "ADC A,H", // 8c - "ADC A,L", // 8d - "ADC A,(HL)", // 8e - "ADC A,A", // 8f - "SUB B", // 90 - "SUB C", // 91 - "SUB D", // 92 - "SUB E", // 93 - "SUB H", // 94 - "SUB L", // 95 - "SUB (HL)", // 96 - "SUB A", // 97 - "SBC A,B", // 98 - "SBC A,C", // 99 - "SBC A,D", // 9a - "SBC A,E", // 9b - "SBC A,H", // 9c - "SBC A,L", // 9d - "SBC A,(HL)", // 9e - "SBC A,A", // 9f - "AND B", // a0 - "AND C", // a1 - "AND D", // a2 - "AND E", // a3 - "AND H", // a4 - "AND L", // a5 - "AND (HL)", // a6 - "AND A", // a7 - "XOR B", // a8 - "XOR C", // a9 - "XOR D", // aa - "XOR E", // ab - "XOR H", // ac - "XOR L", // ad - "XOR (HL)", // ae - "XOR A", // af - "OR B", // b0 - "OR C", // b1 - "OR D", // b2 - "OR E", // b3 - "OR H", // b4 - "OR L", // b5 - "OR (HL)", // b6 - "OR A", // b7 - "CP B", // b8 - "CP C", // b9 - "CP D", // ba - "CP E", // bb - "CP H", // bc - "CP L", // bd - "CP (HL)", // be - "CP A", // bf - "RET NZ", // c0 - "POP BC", // c1 - "JP NZ,a16", // c2 - "JP a16", // c3 - "CALL NZ,a16", // c4 - "PUSH BC", // c5 - "ADD A,d8", // c6 - "RST 00H", // c7 - "RET Z", // c8 - "RET", // c9 - "JP Z,a16", // ca - "PREFIX CB", // cb - "CALL Z,a16", // cc - "CALL a16", // cd - "ADC A,d8", // ce - "RST 08H", // cf - "RET NC", // d0 - "POP DE", // d1 - "JP NC,a16", // d2 - "???", // d3 - "CALL NC,a16", // d4 - "PUSH DE", // d5 - "SUB d8", // d6 - "RST 10H", // d7 - "RET C", // d8 - "RETI", // d9 - "JP C,a16", // da - "???", // db - "CALL C,a16", // dc - "???", // dd - "SBC A,d8", // de - "RST 18H", // df - "LDH (a8),A", // e0 - "POP HL", // e1 - "LD (C),A", // e2 - "???", // e3 - "???", // e4 - "PUSH HL", // e5 - "AND d8", // e6 - "RST 20H", // e7 - "ADD SP,r8", // e8 - "JP (HL)", // e9 - "LD (a16),A", // ea - "???", // eb - "???", // ec - "???", // ed - "XOR d8", // ee - "RST 28H", // ef - "LDH A,(a8)", // f0 - "POP AF", // f1 - "LD A,(C)", // f2 - "DI", // f3 - "???", // f4 - "PUSH AF", // f5 - "OR d8", // f6 - "RST 30H", // f7 - "LD HL,SP+r8", // f8 - "LD SP,HL", // f9 - "LD A,(a16)", // fa - "EI ", // fb - "???", // fc - "???", // fd - "CP d8", // fe - "RST 38H", // ff - "RLC B", // 00 - "RLC C", // 01 - "RLC D", // 02 - "RLC E", // 03 - "RLC H", // 04 - "RLC L", // 05 - "RLC (HL)", // 06 - "RLC A", // 07 - "RRC B", // 08 - "RRC C", // 09 - "RRC D", // 0a - "RRC E", // 0b - "RRC H", // 0c - "RRC L", // 0d - "RRC (HL)", // 0e - "RRC A", // 0f - "RL B", // 10 - "RL C", // 11 - "RL D", // 12 - "RL E", // 13 - "RL H", // 14 - "RL L", // 15 - "RL (HL)", // 16 - "RL A", // 17 - "RR B", // 18 - "RR C", // 19 - "RR D", // 1a - "RR E", // 1b - "RR H", // 1c - "RR L", // 1d - "RR (HL)", // 1e - "RR A", // 1f - "SLA B", // 20 - "SLA C", // 21 - "SLA D", // 22 - "SLA E", // 23 - "SLA H", // 24 - "SLA L", // 25 - "SLA (HL)", // 26 - "SLA A", // 27 - "SRA B", // 28 - "SRA C", // 29 - "SRA D", // 2a - "SRA E", // 2b - "SRA H", // 2c - "SRA L", // 2d - "SRA (HL)", // 2e - "SRA A", // 2f - "SWAP B", // 30 - "SWAP C", // 31 - "SWAP D", // 32 - "SWAP E", // 33 - "SWAP H", // 34 - "SWAP L", // 35 - "SWAP (HL)", // 36 - "SWAP A", // 37 - "SRL B", // 38 - "SRL C", // 39 - "SRL D", // 3a - "SRL E", // 3b - "SRL H", // 3c - "SRL L", // 3d - "SRL (HL)", // 3e - "SRL A", // 3f - "BIT 0,B", // 40 - "BIT 0,C", // 41 - "BIT 0,D", // 42 - "BIT 0,E", // 43 - "BIT 0,H", // 44 - "BIT 0,L", // 45 - "BIT 0,(HL)", // 46 - "BIT 0,A", // 47 - "BIT 1,B", // 48 - "BIT 1,C", // 49 - "BIT 1,D", // 4a - "BIT 1,E", // 4b - "BIT 1,H", // 4c - "BIT 1,L", // 4d - "BIT 1,(HL)", // 4e - "BIT 1,A", // 4f - "BIT 2,B", // 50 - "BIT 2,C", // 51 - "BIT 2,D", // 52 - "BIT 2,E", // 53 - "BIT 2,H", // 54 - "BIT 2,L", // 55 - "BIT 2,(HL)", // 56 - "BIT 2,A", // 57 - "BIT 3,B", // 58 - "BIT 3,C", // 59 - "BIT 3,D", // 5a - "BIT 3,E", // 5b - "BIT 3,H", // 5c - "BIT 3,L", // 5d - "BIT 3,(HL)", // 5e - "BIT 3,A", // 5f - "BIT 4,B", // 60 - "BIT 4,C", // 61 - "BIT 4,D", // 62 - "BIT 4,E", // 63 - "BIT 4,H", // 64 - "BIT 4,L", // 65 - "BIT 4,(HL)", // 66 - "BIT 4,A", // 67 - "BIT 5,B", // 68 - "BIT 5,C", // 69 - "BIT 5,D", // 6a - "BIT 5,E", // 6b - "BIT 5,H", // 6c - "BIT 5,L", // 6d - "BIT 5,(HL)", // 6e - "BIT 5,A", // 6f - "BIT 6,B", // 70 - "BIT 6,C", // 71 - "BIT 6,D", // 72 - "BIT 6,E", // 73 - "BIT 6,H", // 74 - "BIT 6,L", // 75 - "BIT 6,(HL)", // 76 - "BIT 6,A", // 77 - "BIT 7,B", // 78 - "BIT 7,C", // 79 - "BIT 7,D", // 7a - "BIT 7,E", // 7b - "BIT 7,H", // 7c - "BIT 7,L", // 7d - "BIT 7,(HL)", // 7e - "BIT 7,A", // 7f - "RES 0,B", // 80 - "RES 0,C", // 81 - "RES 0,D", // 82 - "RES 0,E", // 83 - "RES 0,H", // 84 - "RES 0,L", // 85 - "RES 0,(HL)", // 86 - "RES 0,A", // 87 - "RES 1,B", // 88 - "RES 1,C", // 89 - "RES 1,D", // 8a - "RES 1,E", // 8b - "RES 1,H", // 8c - "RES 1,L", // 8d - "RES 1,(HL)", // 8e - "RES 1,A", // 8f - "RES 2,B", // 90 - "RES 2,C", // 91 - "RES 2,D", // 92 - "RES 2,E", // 93 - "RES 2,H", // 94 - "RES 2,L", // 95 - "RES 2,(HL)", // 96 - "RES 2,A", // 97 - "RES 3,B", // 98 - "RES 3,C", // 99 - "RES 3,D", // 9a - "RES 3,E", // 9b - "RES 3,H", // 9c - "RES 3,L", // 9d - "RES 3,(HL)", // 9e - "RES 3,A", // 9f - "RES 4,B", // a0 - "RES 4,C", // a1 - "RES 4,D", // a2 - "RES 4,E", // a3 - "RES 4,H", // a4 - "RES 4,L", // a5 - "RES 4,(HL)", // a6 - "RES 4,A", // a7 - "RES 5,B", // a8 - "RES 5,C", // a9 - "RES 5,D", // aa - "RES 5,E", // ab - "RES 5,H", // ac - "RES 5,L", // ad - "RES 5,(HL)", // ae - "RES 5,A", // af - "RES 6,B", // b0 - "RES 6,C", // b1 - "RES 6,D", // b2 - "RES 6,E", // b3 - "RES 6,H", // b4 - "RES 6,L", // b5 - "RES 6,(HL)", // b6 - "RES 6,A", // b7 - "RES 7,B", // b8 - "RES 7,C", // b9 - "RES 7,D", // ba - "RES 7,E", // bb - "RES 7,H", // bc - "RES 7,L", // bd - "RES 7,(HL)", // be - "RES 7,A", // bf - "SET 0,B", // c0 - "SET 0,C", // c1 - "SET 0,D", // c2 - "SET 0,E", // c3 - "SET 0,H", // c4 - "SET 0,L", // c5 - "SET 0,(HL)", // c6 - "SET 0,A", // c7 - "SET 1,B", // c8 - "SET 1,C", // c9 - "SET 1,D", // ca - "SET 1,E", // cb - "SET 1,H", // cc - "SET 1,L", // cd - "SET 1,(HL)", // ce - "SET 1,A", // cf - "SET 2,B", // d0 - "SET 2,C", // d1 - "SET 2,D", // d2 - "SET 2,E", // d3 - "SET 2,H", // d4 - "SET 2,L", // d5 - "SET 2,(HL)", // d6 - "SET 2,A", // d7 - "SET 3,B", // d8 - "SET 3,C", // d9 - "SET 3,D", // da - "SET 3,E", // db - "SET 3,H", // dc - "SET 3,L", // dd - "SET 3,(HL)", // de - "SET 3,A", // df - "SET 4,B", // e0 - "SET 4,C", // e1 - "SET 4,D", // e2 - "SET 4,E", // e3 - "SET 4,H", // e4 - "SET 4,L", // e5 - "SET 4,(HL)", // e6 - "SET 4,A", // e7 - "SET 5,B", // e8 - "SET 5,C", // e9 - "SET 5,D", // ea - "SET 5,E", // eb - "SET 5,H", // ec - "SET 5,L", // ed - "SET 5,(HL)", // ee - "SET 5,A", // ef - "SET 6,B", // f0 - "SET 6,C", // f1 - "SET 6,D", // f2 - "SET 6,E", // f3 - "SET 6,H", // f4 - "SET 6,L", // f5 - "SET 6,(HL)", // f6 - "SET 6,A", // f7 - "SET 7,B", // f8 - "SET 7,C", // f9 - "SET 7,D", // fa - "SET 7,E", // fb - "SET 7,H", // fc - "SET 7,L", // fd - "SET 7,(HL)", // fe - "SET 7,A", // 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 (bytes[0] == 0xcb) - { - bytes.Add(reader(addr++)); - result = table[bytes[1] + 256]; - } - - if (result.Contains("d8")) - { - byte d = reader(addr++); - bytes.Add(d); - result = result.Replace("d8", string.Format("#{0:X2}h", d)); - } - else if (result.Contains("d16")) - { - byte dlo = reader(addr++); - byte dhi = reader(addr++); - bytes.Add(dlo); - bytes.Add(dhi); - result = result.Replace("d16", string.Format("#{0:X2}{1:X2}h", dhi, dlo)); - } - else if (result.Contains("a16")) - { - byte dlo = reader(addr++); - byte dhi = reader(addr++); - bytes.Add(dlo); - bytes.Add(dhi); - result = result.Replace("a16", string.Format("#{0:X2}{1:X2}h", dhi, dlo)); - } - else if (result.Contains("a8")) - { - byte d = reader(addr++); - bytes.Add(d); - result = result.Replace("a8", string.Format("#FF{0:X2}h", d)); - } - else if (result.Contains("r8")) - { - byte d = reader(addr++); - bytes.Add(d); - int offs = d; - if (offs >= 128) - offs -= 256; - result = result.Replace("r8", string.Format("{0:X4}h", (ushort)(addr + offs))); - } - 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 < 17) - ret.Append(' '); - ret.Append(result); - size = (ushort)(addr - origaddr); - return ret.ToString(); - } - } -} diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs deleted file mode 100644 index 7466eeba2a..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System.Runtime.InteropServices; -using System; - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - [StructLayout(LayoutKind.Explicit)] - [Serializable] - public struct RegisterPair - { - [FieldOffset(0)] - public ushort Word; - - [FieldOffset(0)] - public byte Low; - - [FieldOffset(1)] - public byte High; - - public RegisterPair(ushort value) - { - Word = value; - Low = (byte)(Word); - High = (byte)(Word >> 8); - } - - public static implicit operator ushort(RegisterPair rp) - { - return rp.Word; - } - - public static implicit operator RegisterPair(ushort value) - { - return new RegisterPair(value); - } - } - - public bool FlagC - { - get { return (RegAF.Low & 0x10) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x10) | (value ? 0x10 : 0x00)); } - } - - public bool FlagH - { - get { return (RegAF.Low & 0x20) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x20) | (value ? 0x20 : 0x00)); } - } - - public bool FlagN - { - get { return (RegAF.Low & 0x40) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x40) | (value ? 0x40 : 0x00)); } - } - - public bool FlagZ - { - get { return (RegAF.Low & 0x80) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x80) | (value ? 0x80 : 0x00)); } - } - - private RegisterPair RegAF; - private RegisterPair RegBC; - private RegisterPair RegDE; - private RegisterPair RegHL; - - private byte RegI; // I (interrupt vector) - - private RegisterPair RegSP; // SP (stack pointer) - private RegisterPair RegPC; // PC (program counter) - - private void ResetRegisters() - { - RegAF = 0; RegBC = 0; RegDE = 0; RegHL = 0; - RegI = 0; - RegSP.Word = 0; RegPC.Word = 0; - } - - public byte RegisterA - { - get { return RegAF.High; } - set { RegAF.High = value; } - } - - public byte RegisterF - { - get { return RegAF.Low; } - set { RegAF.Low = (byte)(value & 0xF0); } - } - - public ushort RegisterAF - { - get { return RegAF.Word; } - set { RegAF.Word = (byte)(value & 0xFFF0); } - } - - public byte RegisterB - { - get { return RegBC.High; } - set { RegBC.High = value; } - } - - public byte RegisterC - { - get { return RegBC.Low; } - set { RegBC.Low = value; } - } - - public ushort RegisterBC - { - get { return RegBC.Word; } - set { RegBC.Word = value; } - } - - public byte RegisterD - { - get { return RegDE.High; } - set { RegDE.High = value; } - } - - public byte RegisterE - { - get { return RegDE.Low; } - set { RegDE.Low = value; } - } - public ushort RegisterDE - { - get { return RegDE.Word; } - set { RegDE.Word = value; } - } - - public byte RegisterH - { - get { return RegHL.High; } - set { RegHL.High = value; } - } - - public byte RegisterL - { - get { return RegHL.Low; } - set { RegHL.Low = value; } - } - public ushort RegisterHL - { - get { return RegHL.Word; } - set { RegHL.Word = value; } - } - - public ushort RegisterPC - { - get { return RegPC.Word; } - set { RegPC.Word = value; } - } - public ushort RegisterSP - { - get { return RegSP.Word; } - set { RegSP.Word = value; } - } - public byte RegisterI - { - get { return RegI; } - set { RegI = value; } - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs deleted file mode 100644 index c2597ef028..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs +++ /dev/null @@ -1,149 +0,0 @@ -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - private void InitializeTables() - { - InitTableDaa(); - } - - private static readonly byte[] IncTable = - { - 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - private static readonly byte[] DecTable = - { - 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 - }; - - private static readonly byte[] SwapTable = - { - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, - 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, - 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, - 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, - 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, - 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, - 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, - 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, - 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, - 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, - 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, - 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, - 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, - 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, - 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, - 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF - }; - - private static readonly byte[] mCycleTable = new byte[] - { - 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, - 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, - 3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, - 3, 3, 2, 2, 1, 3, 3, 3, 3, 2, 2, 2, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 1, 6, 6, 2, 4, - 5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4, - 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, - 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4, - }; - - private static readonly byte[] cbMCycleTable = new byte[] - { - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - }; - - private ushort[] TableDaa; - private void InitTableDaa() - { - TableDaa = new ushort[65536]; - for (int af = 0; af < 65536; ++af) - { - byte a = (byte)(af >> 8); - byte tmp = a; - - if (IsN(af)) - { - if (IsH(af) || ((a & 0x0F) > 0x09)) tmp -= 0x06; - if (IsC(af) || a > 0x99) tmp -= 0x60; - } - else - { - if (IsH(af) || ((a & 0x0F) > 0x09)) tmp += 0x06; - if (IsC(af) || a > 0x99) tmp += 0x60; - } - - TableDaa[af] = (ushort)((tmp * 256) + FlagByte(IsC(af) || a > 0x99, ((a ^ tmp) & 0x10) != 0, IsN(af), tmp == 0)); - } - } - - private static byte FlagByte(bool C, bool H, bool N, bool Z) - { - return (byte)( - (C ? 0x10 : 0) + - (H ? 0x20 : 0) + - (N ? 0x40 : 0) + - (Z ? 0x80 : 0) - ); - } - - private static bool IsC(int value) { return (value & 0x10) != 0; } - private static bool IsH(int value) { return (value & 0x20) != 0; } - private static bool IsN(int value) { return (value & 0x40) != 0; } - private static bool IsZ(int value) { return (value & 0x80) != 0; } - } -} diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs deleted file mode 100644 index eb9842c806..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Globalization; -using System.IO; - -// This Z80-Gameboy emulator is a modified version of Ben Ryves 'Brazil' emulator. -// It is MIT licensed (not public domain). (See Licenses) - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public sealed partial class Z80 - { - private static bool logging = false; - private static StreamWriter log; - - static Z80() - { - if (logging) - log = new StreamWriter("log_Z80.txt"); - } - - public Z80() - { - InitializeTables(); - Reset(); - } - - public void Reset() - { - ResetRegisters(); - ResetInterrupts(); - PendingCycles = 0; - TotalExecutedCycles = 0; - } - - // Memory Access - - public Func ReadMemory; - public Action WriteMemory; - - public void UnregisterMemoryMapper() - { - ReadMemory = null; - WriteMemory = null; - } - - // State Save/Load - - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[Z80]"); - writer.WriteLine("AF {0:X4}", RegAF.Word); - writer.WriteLine("BC {0:X4}", RegBC.Word); - writer.WriteLine("DE {0:X4}", RegDE.Word); - writer.WriteLine("HL {0:X4}", RegHL.Word); - writer.WriteLine("I {0:X2}", RegI); - writer.WriteLine("SP {0:X4}", RegSP.Word); - writer.WriteLine("PC {0:X4}", RegPC.Word); - writer.WriteLine("IRQ {0}", interrupt); - writer.WriteLine("NMI {0}", nonMaskableInterrupt); - writer.WriteLine("NMIPending {0}", nonMaskableInterruptPending); - writer.WriteLine("IM {0}", InterruptMode); - writer.WriteLine("IFF1 {0}", IFF1); - writer.WriteLine("IFF2 {0}", IFF2); - writer.WriteLine("Halted {0}", Halted); - writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles); - writer.WriteLine("PendingCycles {0}", PendingCycles); - writer.WriteLine("[/Z80]"); - writer.WriteLine(); - } - - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/Z80]") break; - if (args[0] == "AF") - RegAF.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "BC") - RegBC.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "DE") - RegDE.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "HL") - RegHL.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "I") - RegI = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "SP") - RegSP.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "PC") - RegPC.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "IRQ") - interrupt = bool.Parse(args[1]); - else if (args[0] == "NMI") - nonMaskableInterrupt = bool.Parse(args[1]); - else if (args[0] == "NMIPending") - nonMaskableInterruptPending = bool.Parse(args[1]); - else if (args[0] == "IM") - InterruptMode = int.Parse(args[1]); - else if (args[0] == "IFF1") - IFF1 = bool.Parse(args[1]); - else if (args[0] == "IFF2") - IFF2 = bool.Parse(args[1]); - else if (args[0] == "Halted") - Halted = bool.Parse(args[1]); - else if (args[0] == "ExecutedCycles") - TotalExecutedCycles = int.Parse(args[1]); - else if (args[0] == "PendingCycles") - PendingCycles = int.Parse(args[1]); - - else - Console.WriteLine("Skipping unrecognized identifier " + args[0]); - } - } - - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write(RegAF.Word); - writer.Write(RegBC.Word); - writer.Write(RegDE.Word); - writer.Write(RegHL.Word); - writer.Write(RegI); - writer.Write(RegSP.Word); - writer.Write(RegPC.Word); - writer.Write(interrupt); - writer.Write(nonMaskableInterrupt); - writer.Write(nonMaskableInterruptPending); - writer.Write(InterruptMode); - writer.Write(IFF1); - writer.Write(IFF2); - writer.Write(Halted); - writer.Write(TotalExecutedCycles); - writer.Write(PendingCycles); - } - - public void LoadStateBinary(BinaryReader reader) - { - RegAF.Word = reader.ReadUInt16(); - RegBC.Word = reader.ReadUInt16(); - RegDE.Word = reader.ReadUInt16(); - RegHL.Word = reader.ReadUInt16(); - RegI = reader.ReadByte(); - RegSP.Word = reader.ReadUInt16(); - RegPC.Word = reader.ReadUInt16(); - interrupt = reader.ReadBoolean(); - nonMaskableInterrupt = reader.ReadBoolean(); - nonMaskableInterruptPending = reader.ReadBoolean(); - InterruptMode = reader.ReadInt32(); - IFF1 = reader.ReadBoolean(); - IFF2 = reader.ReadBoolean(); - Halted = reader.ReadBoolean(); - TotalExecutedCycles = reader.ReadInt32(); - PendingCycles = reader.ReadInt32(); - } - - public void LogData() - { - if (!logging) - return; - log.WriteLine("AF {0:X4}", RegAF.Word); - log.WriteLine("BC {0:X4}", RegBC.Word); - log.WriteLine("DE {0:X4}", RegDE.Word); - log.WriteLine("HL {0:X4}", RegHL.Word); - log.WriteLine("SP {0:X4}", RegSP.Word); - log.WriteLine("PC {0:X4}", RegPC.Word); - log.WriteLine("------"); - log.WriteLine(); - log.Flush(); - } - } -} \ No newline at end of file From b0095927b6e035833bb76a6fec45c3265d39b687 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 19 Nov 2017 10:36:38 -0500 Subject: [PATCH 28/28] Make Gambatte use new disassembler --- .../Consoles/Nintendo/Gameboy/GBDisassembler.cs | 4 ++-- .../Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs index 2f2fdf81c5..099c25e30e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Common.Components.Z80GB; +using BizHawk.Emulation.Common.Components.LR35902; namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { @@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public override string Disassemble(MemoryDomain m, uint addr, out int length) { ushort tmp; - string ret = NewDisassembler.Disassemble((ushort)addr, a => m.PeekByte(a), out tmp); + string ret = LR35902.Disassemble((ushort)addr, a => m.PeekByte(a), out tmp); length = tmp; return ret; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs index f2c92adc34..b7b7499e4a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs @@ -1,7 +1,7 @@ using System; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Common.Components.Z80GB; +using BizHawk.Emulation.Common.Components.LR35902; namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { @@ -19,8 +19,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy Tracer.Put(new TraceInfo { Disassembly = - NewDisassembler - .Disassemble((ushort)s[1], addr => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused) + LR35902.Disassemble((ushort)s[1], addr => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused) .PadRight(36), RegisterInfo = string.Format(