diff --git a/README.md b/README.md index f4137ba..8a9e807 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,13 @@ General Options By default, the minifb frontend will scale the window by 2. This can be changed with the `--scale [1,2,4]` option. +The `-t` or `--threaded` options will run the simulated hardware in a separate +thread from the frontend, which will run as fast as possible, faster than +real-time. By default, the simulated hardware is run inline with the frontend's +update cycle, which is limited to 60Hz. The simulation will be run for 16.6ms of +simulated time for each frame the frontend draws. But the simulated time is not +accurate and Sega Genesis games will run slower than they should. + The `-d` or `--debugger` option will make the emulator start the debugger before running. There is a simple built-in debugger for stepping through the rom instructions being emulated. The state of the CPU registers will diff --git a/src/cpus/z80/execute.rs b/src/cpus/z80/execute.rs index a03c81d..6bc42de 100644 --- a/src/cpus/z80/execute.rs +++ b/src/cpus/z80/execute.rs @@ -53,7 +53,6 @@ impl Transmutable for Z80 { } - impl Z80 { pub fn step_internal(&mut self, system: &System) -> Result<(), Error> { match self.state.status { @@ -62,7 +61,6 @@ impl Z80 { Status::Running => { match self.cycle_one(system) { Ok(()) => Ok(()), - //Err(Error { err: ErrorType::Processor, native, .. }) => { Err(Error { err: ErrorType::Processor, .. }) => { //self.exception(system, native as u8, false)?; Ok(()) @@ -80,30 +78,15 @@ impl Z80 { } pub fn cycle_one(&mut self, system: &System) -> Result<(), Error> { - //self.timer.cycle.start(); self.decode_next()?; self.execute_current()?; - //self.timer.cycle.end(); - - //if (self.timer.cycle.events % 500) == 0 { - // println!("{}", self.timer); - //} - //self.check_pending_interrupts(system)?; self.check_breakpoints(system); Ok(()) } pub fn decode_next(&mut self) -> Result<(), Error> { - //self.timer.decode.start(); self.decoder.decode_at(&mut self.port, self.state.pc)?; - //self.timer.decode.end(); - - //if self.debugger.use_tracing { - //self.decoder.dump_decoded(&mut self.port); - //self.dump_state(); - //} - self.state.pc = self.decoder.end; Ok(()) } @@ -329,24 +312,27 @@ impl Z80 { Direction::ToAcc => { self.state.reg[Register::A as usize] = *addr; }, } } - //Instruction::LDD => { - //}, - //Instruction::LDDR => { - //}, - //Instruction::LDI => { - //}, - Instruction::LDIR => { + Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => { + let diff = if self.decoder.instruction == Instruction::LDI || self.decoder.instruction == Instruction::LDIR { + 1 + } else { + -1 + }; + let src_value = self.get_load_target_value(LoadTarget::IndirectRegByte(RegisterPair::HL))?; self.set_load_target_value(LoadTarget::IndirectRegByte(RegisterPair::DE), src_value)?; - self.add_to_regpair(RegisterPair::DE, 1); - self.add_to_regpair(RegisterPair::HL, 1); + self.add_to_regpair(RegisterPair::DE, diff); + self.add_to_regpair(RegisterPair::HL, diff); let count = self.add_to_regpair(RegisterPair::BC, -1); - if count != 0 { - self.state.pc -= 2; - } let mask = (Flags::AddSubtract as u8) | (Flags::HalfCarry as u8) | (Flags::Parity as u8); let parity = if count != 0 { Flags::Parity as u8 } else { 0 }; self.set_flags(mask, parity); + + if self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR { + if count != 0 { + self.state.pc -= 2; + } + } }, Instruction::NEG => { let acc = self.get_register_value(Register::A); diff --git a/src/machines/genesis.rs b/src/machines/genesis.rs index ba12083..0a3c755 100644 --- a/src/machines/genesis.rs +++ b/src/machines/genesis.rs @@ -62,18 +62,19 @@ pub fn build_genesis(host: &mut H, options: SegaGenesisOptions) -> Resu // Build the Coprocessor's Bus let bank_register = Signal::new(0); - let coproc_bus = Rc::new(RefCell::new(Bus::new())); let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000])); let coproc_ym_sound = wrap_transmutable(YM2612::new()); let coproc_sn_sound = wrap_transmutable(SN76489::new()); let coproc_register = wrap_transmutable(CoprocessorBankRegister::new(bank_register.clone())); let coproc_area = wrap_transmutable(CoprocessorBankArea::new(bank_register, system.bus.clone())); + + let coproc_bus = Rc::new(RefCell::new(Bus::new())); coproc_bus.borrow_mut().insert(0x0000, coproc_ram.clone()); coproc_bus.borrow_mut().insert(0x4000, coproc_ym_sound.clone()); coproc_bus.borrow_mut().insert(0x6000, coproc_register.clone()); coproc_bus.borrow_mut().insert(0x7f11, coproc_sn_sound.clone()); coproc_bus.borrow_mut().insert(0x8000, coproc_area); - let mut coproc = Z80::new(Z80Type::Z80, 3_579_545, BusPort::new(0, 16, 8, coproc_bus.clone())); + let coproc = Z80::new(Z80Type::Z80, 3_579_545, BusPort::new(0, 16, 8, coproc_bus.clone())); let reset = coproc.reset.clone(); let bus_request = coproc.bus_request.clone(); diff --git a/todo.txt b/todo.txt index 4376358..c809540 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,5 @@ +* add command line arguments to speed up or slow down either the frame rate limiter or the simulated time per frame * currently you need to implement the 1.5ms reset in the genesis controllers * should SharedData be HostData, or something else? I don't think the name is very informative * can you make the connections between things (like memory adapters), be expressed in a way that's more similar to the electrical design? @@ -41,7 +42,7 @@ Genesis/Mega Drive: * make tests for each instruction * check all instructions in the docs - * unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, ILLEGAL, MOVEfromCCR,, RTR, RTD, SBCD, SUBX + * unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, ILLEGAL, RTR, RTD, SBCD, SUBX * >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, RTM, PACK, TRAPcc, UNPK * add support for MMU