public void process_delays() { // triggering an interrupt with a write to the control register takes 4 cycles to trigger interrupt controller_delay_cd--; if (controller_delay_cd == 0) { if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } REG_FF0F |= 0x10; delays_to_process = false; } }
public void do_controller_check() { // check if new input changed the input register and triggered IRQ byte contr_prev = input_register; input_register &= 0xF0; if ((input_register & 0x30) == 0x20) { input_register |= (byte)(controller_state & 0xF); } else if ((input_register & 0x30) == 0x10) { input_register |= (byte)((controller_state & 0xF0) >> 4); } else if ((input_register & 0x30) == 0x00) { // if both polls are set, then a bit is zero if either or both pins are zero byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); input_register |= temp; } else { input_register |= 0xF; } // check for interrupts if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) { if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } REG_FF0F |= 0x10; } }
public void Write_Registers(int addr, byte value) { switch (addr) { // select input case 0xFF00: input_register &= 0xCF; input_register |= (byte)(value & 0x30); // top 2 bits always 1 // check for high to low transitions that trigger IRQs byte contr_prev = input_register; input_register &= 0xF0; if ((input_register & 0x30) == 0x20) { input_register |= (byte)(controller_state & 0xF); } else if ((input_register & 0x30) == 0x10) { input_register |= (byte)((controller_state & 0xF0) >> 4); } else if ((input_register & 0x30) == 0x00) { // if both polls are set, then a bit is zero if either or both pins are zero byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); input_register |= temp; } else { input_register |= 0xF; } // check for interrupts // if an interrupt is triggered, it is delayed by 4 cycles if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) { controller_delay_cd = 4; delays_to_process = true; } break; // Serial data port case 0xFF01: serialport.WriteReg(addr, value); break; // Serial port control case 0xFF02: serialport.WriteReg(addr, value); 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; // GBC compatibility register (I think) case 0xFF4C: if ((value != 0xC0) && (value != 0x80) && (GB_bios_register == 0)) // && (value != 0xFF) && (value != 0x04)) { GBC_compat = false; // cpu operation is a function of hardware only //cpu.is_GBC = GBC_compat; } Console.Write("GBC Compatibility? "); Console.WriteLine(value); break; // Speed Control for GBC case 0xFF4D: if (GBC_compat) { speed_switch = (value & 1) > 0; } break; // VBK case 0xFF4F: if (is_GBC /* && !ppu.HDMA_active*/) { VRAM_Bank = (byte)(value & 1); } break; // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs case 0xFF50: // Console.WriteLine(value); if (GB_bios_register == 0) { GB_bios_register = value; } break; // PPU Regs for GBC case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: if (GBC_compat) { ppu.WriteReg(addr, value); } break; case 0xFF56: if (is_GBC) { IR_reg = (byte)((value & 0xC1) | (IR_reg & 0x3E)); // send IR signal out if ((IR_reg & 0x1) == 0x1) { IR_signal = (byte)(0 | IR_mask); } else { IR_signal = 2; } // receive own signal if IR on and receive on if ((IR_reg & 0xC1) == 0xC1) { IR_self = (byte)(0 | IR_mask); } else { IR_self = 2; } IR_write = 8; } break; case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: if (is_GBC) { ppu.WriteReg(addr, value); } break; // RAM Bank in GBC mode case 0xFF70: if (GBC_compat) { RAM_Bank = value & 7; if (RAM_Bank == 0) { RAM_Bank = 1; } } break; case 0xFF6C: if (GBC_compat) { undoc_6C |= (byte)(value & 1); } break; case 0xFF72: if (is_GBC) { undoc_72 = value; } break; case 0xFF73: if (is_GBC) { undoc_73 = value; } break; case 0xFF74: if (GBC_compat) { undoc_74 = value; } break; case 0xFF75: if (is_GBC) { undoc_75 |= (byte)(value & 0x70); } break; case 0xFF76: // read only break; case 0xFF77: // read only break; // interrupt control register case 0xFFFF: REG_FFFF = 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; default: Console.WriteLine(addr + " " + value); break; } }
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; // check if new input changed the input register and triggered IRQ byte contr_prev = input_register; input_register &= 0xF0; if ((input_register & 0x30) == 0x20) { input_register |= (byte)(controller_state & 0xF); } else if ((input_register & 0x30) == 0x10) { input_register |= (byte)((controller_state & 0xF0) >> 4); } else if ((input_register & 0x30) == 0x00) { // if both polls are set, then a bit is zero if either or both pins are zero byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); input_register |= temp; } else { input_register |= 0xF; } // check for interrupts if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) { if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } REG_FF0F |= 0x10; } while (!vblank_rise && (ticker < 100000)) { audio.tick(); timer.tick_1(); ppu.tick(); serialport.serial_transfer_tick(); if (Use_RTC) { mapper.RTC_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 Write_Registers(int addr, byte value) { switch (addr) { // select input case 0xFF00: input_register &= 0xCF; input_register |= (byte)(value & 0x30); // top 2 bits always 1 // check for high to low transitions that trigger IRQs byte contr_prev = input_register; input_register &= 0xF0; if ((input_register & 0x30) == 0x20) { input_register |= (byte)(controller_state & 0xF); } else if ((input_register & 0x30) == 0x10) { input_register |= (byte)((controller_state & 0xF0) >> 4); } else if ((input_register & 0x30) == 0x00) { // if both polls are set, then a bit is zero if either or both pins are zero byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); input_register |= temp; } else { input_register |= 0xF; } // check for interrupts if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) { if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } REG_FF0F |= 0x10; } break; // Serial data port case 0xFF01: serialport.WriteReg(addr, value); break; // Serial port control case 0xFF02: serialport.WriteReg(addr, value); 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; // GBC compatibility register (I think) case 0xFF4C: if ((value != 0xC0) && (value != 0x80)) { Console.Write("GBC Compatibility? "); Console.WriteLine(value); GBC_compat = false; } break; // Speed Control for GBC case 0xFF4D: if (is_GBC) { speed_switch = (value & 1) > 0; } break; // VBK case 0xFF4F: if (is_GBC && !ppu.HDMA_active) { VRAM_Bank = (byte)(value & 1); } 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; // PPU Regs for GBC case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: if (is_GBC) { ppu.WriteReg(addr, value); } break; // RAM Bank in GBC mode case 0xFF70: //Console.WriteLine(value); if (is_GBC) { RAM_Bank = value & 7; if (RAM_Bank == 0) { RAM_Bank = 1; } } 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; default: Console.Write(addr); Console.Write(" "); Console.WriteLine(value); break; } }
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; // check if new input changed the input register and triggered IRQ byte contr_prev = input_register; input_register &= 0xF0; if ((input_register & 0x30) == 0x20) { input_register |= (byte)(controller_state & 0xF); } else if ((input_register & 0x30) == 0x10) { input_register |= (byte)((controller_state & 0xF0) >> 4); } else if ((input_register & 0x30) == 0x00) { // if both polls are set, then a bit is zero if either or both pins are zero byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); input_register |= temp; } else { input_register |= 0xF; } // check for interrupts if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) || ((contr_prev & 4) > 0) && ((input_register & 4) == 0) || ((contr_prev & 2) > 0) && ((input_register & 2) == 0) || ((contr_prev & 1) > 0) && ((input_register & 1) == 0)) { if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } REG_FF0F |= 0x10; } while (!vblank_rise) { // These things do not change speed in GBC double spped mode audio.tick(); ppu.tick(); if (Use_MT) { mapper.Mapper_Tick(); } if (!HDMA_transfer) { // These things all tick twice as fast in GBC double speed mode ppu.DMA_tick(); timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); if (double_speed) { ppu.DMA_tick(); timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); } } else { timer.tick_1(); timer.tick_2(); cpu.TotalExecutedCycles++; if (double_speed) { timer.tick_1(); timer.tick_2(); cpu.TotalExecutedCycles++; } } if (in_vblank && !in_vblank_old) { vblank_rise = true; } ticker++; // if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } in_vblank_old = in_vblank; REG_FF0F_OLD = REG_FF0F; } vblank_rise = false; }