Fixed some Z80 instruction timings

This commit is contained in:
transistor 2023-06-10 21:55:25 -07:00
parent cbcfb26f49
commit f9d613b3b9
7 changed files with 1676 additions and 54 deletions

View File

@ -8,6 +8,7 @@ pub struct Z80Decoder {
pub clock: ClockTime,
pub start: u16,
pub end: u16,
pub extra_instruction_bytes: u16,
pub instruction: Instruction,
}
@ -17,6 +18,7 @@ impl Default for Z80Decoder {
clock: ClockTime::START,
start: 0,
end: 0,
extra_instruction_bytes: 0,
instruction: Instruction::NOP,
}
}
@ -27,16 +29,18 @@ impl Z80Decoder {
self.clock = clock;
self.start = start;
self.end = start;
self.extra_instruction_bytes = 0;
self.instruction = self.decode_one(memory)?;
Ok(())
}
pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
let ins = self.read_instruction_byte(memory)?;
self.decode_bare(memory, ins)
self.decode_bare(memory, ins, 0)
}
pub fn decode_bare(&mut self, memory: &mut dyn Addressable, ins: u8) -> Result<Instruction, Error> {
pub fn decode_bare(&mut self, memory: &mut dyn Addressable, ins: u8, extra_instruction_bytes: u16) -> Result<Instruction, Error> {
self.extra_instruction_bytes = extra_instruction_bytes;
match get_ins_x(ins) {
0 => {
match get_ins_z(ins) {
@ -371,19 +375,22 @@ impl Z80Decoder {
}
},
4 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::INC8(half_target))
},
5 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::DEC8(half_target))
},
6 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data)))
},
_ => self.decode_bare(memory, ins),
_ => self.decode_bare(memory, ins, 4),
}
},
3 => {
@ -401,10 +408,10 @@ impl Z80Decoder {
let immediate = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::ImmediateByte(immediate)))
},
_ => self.decode_bare(memory, ins),
_ => self.decode_bare(memory, ins, 4),
}
},
_ => self.decode_bare(memory, ins),
_ => self.decode_bare(memory, ins, 4),
}
},
1 => {
@ -412,7 +419,7 @@ impl Z80Decoder {
0 | 1 => {
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins),
None => return self.decode_bare(memory, ins, 4),
};
match (ins & 0x18) >> 3 {
@ -450,7 +457,7 @@ impl Z80Decoder {
3 => {
if get_ins_q(ins) == 0 {
if get_ins_z(ins) == 6 {
return self.decode_bare(memory, ins);
return self.decode_bare(memory, ins, 4);
}
let src = get_register(get_ins_z(ins));
let offset = self.read_instruction_byte(memory)? as i8;
@ -458,7 +465,7 @@ impl Z80Decoder {
} else {
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins),
None => return self.decode_bare(memory, ins, 4),
};
Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target)))
@ -468,12 +475,13 @@ impl Z80Decoder {
}
},
2 => {
self.extra_instruction_bytes = 4;
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins),
None => return self.decode_bare(memory, ins, 4),
};
match get_ins_y(ins) {
0 => Ok(Instruction::ADDa(target)),
1 => Ok(Instruction::ADCa(target)),
@ -493,7 +501,7 @@ impl Z80Decoder {
0xE5 => Ok(Instruction::PUSH(index_reg.into())),
0xE9 => Ok(Instruction::JPIndirect(index_reg.into())),
0xF9 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(index_reg.into()))),
_ => self.decode_bare(memory, ins),
_ => self.decode_bare(memory, ins, 4),
}
},
_ => panic!("InternalError: impossible value"),

View File

@ -103,7 +103,7 @@ impl Z80 {
self.decode_next()?;
self.execute_current()?;
Ok(Z80InstructionCycles::from_instruction(&self.decoder.instruction)?
Ok(Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)?
.calculate_cycles(self.executor.took_branch))
}

View File

@ -26,7 +26,7 @@ impl Z80InstructionCycles {
}
}
pub fn from_instruction(instruction: &Instruction) -> Result<Z80InstructionCycles, Error> {
pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result<Z80InstructionCycles, Error> {
let cycles = match instruction {
Instruction::ADCa(target) |
Instruction::ADDa(target) |
@ -37,7 +37,6 @@ impl Z80InstructionCycles {
Instruction::OR(target) |
Instruction::XOR(target) => {
match target {
// TODO the undocumented DD version of this instruction is actually 8 cycles
Target::DirectReg(_) |
Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 7,
@ -70,8 +69,8 @@ impl Z80InstructionCycles {
Instruction::CALLcc(_, _) => {
return Ok(Z80InstructionCycles::Branch {
taken: 17,
not_taken: 10,
taken: 17 + extra,
not_taken: 10 + extra,
});
},
@ -95,8 +94,8 @@ impl Z80InstructionCycles {
Instruction::OTDR |
Instruction::OTIR => {
return Ok(Z80InstructionCycles::Repeating {
repeating: 21,
terminating: 16,
repeating: 21 + extra,
terminating: 16 + extra,
})
},
@ -105,7 +104,6 @@ impl Z80InstructionCycles {
Instruction::DEC8(target) |
Instruction::INC8(target) => {
// TODO the undocumented DD version of this instruction is actually 8 cycles
match target {
Target::DirectReg(_) |
Target::DirectRegHalf(_) => 4,
@ -129,8 +127,8 @@ impl Z80InstructionCycles {
Instruction::DJNZ(_) => {
return Ok(Z80InstructionCycles::Branch {
taken: 13,
not_taken: 8,
taken: 13 + extra,
not_taken: 8 + extra,
});
},
@ -171,13 +169,12 @@ impl Z80InstructionCycles {
Instruction::JRcc(_, _) => {
return Ok(Z80InstructionCycles::Branch {
taken: 12,
not_taken: 7,
taken: 12 + extra,
not_taken: 7 + extra,
});
},
Instruction::LD(dest, src) => {
// TODO the undocumented DD version of this instruction is actually 8 cycles
match (dest, src) {
// 8-Bit Operations
@ -265,8 +262,8 @@ impl Z80InstructionCycles {
Instruction::RETcc(_) => {
return Ok(Z80InstructionCycles::Branch {
taken: 11,
not_taken: 5,
taken: 11 + extra,
not_taken: 5 + extra,
});
},
@ -298,7 +295,7 @@ impl Z80InstructionCycles {
Instruction::SCF => 4,
};
Ok(Z80InstructionCycles::Single(cycles))
Ok(Z80InstructionCycles::Single(cycles + extra))
}
}

View File

@ -1,4 +1,4 @@
Last run on 2023-05-22 at commit 8c1a89a1fe788fc614c319e167372d47cb869417
Last run on 2023-06-10 at commit cbcfb26f49c23414fe00317fddc65ffcbb087b18
00.json completed, all passed!
01.json completed, all passed!
@ -614,7 +614,7 @@ dd 82.json completed, all passed!
dd 83.json completed, all passed!
dd 84.json completed, all passed!
dd 85.json completed, all passed!
dd 86.json completed, all passed!
dd 86.json completed: 0 passed, 1000 FAILED
dd 87.json completed, all passed!
dd 88.json completed, all passed!
dd 89.json completed, all passed!
@ -622,7 +622,7 @@ dd 8a.json completed, all passed!
dd 8b.json completed, all passed!
dd 8c.json completed, all passed!
dd 8d.json completed, all passed!
dd 8e.json completed, all passed!
dd 8e.json completed: 0 passed, 1000 FAILED
dd 8f.json completed, all passed!
dd 90.json completed, all passed!
dd 91.json completed, all passed!
@ -630,7 +630,7 @@ dd 92.json completed, all passed!
dd 93.json completed, all passed!
dd 94.json completed, all passed!
dd 95.json completed, all passed!
dd 96.json completed, all passed!
dd 96.json completed: 0 passed, 1000 FAILED
dd 97.json completed, all passed!
dd 98.json completed, all passed!
dd 99.json completed, all passed!
@ -638,7 +638,7 @@ dd 9a.json completed, all passed!
dd 9b.json completed, all passed!
dd 9c.json completed, all passed!
dd 9d.json completed, all passed!
dd 9e.json completed, all passed!
dd 9e.json completed: 0 passed, 1000 FAILED
dd 9f.json completed, all passed!
dd a0.json completed, all passed!
dd a1.json completed, all passed!
@ -646,7 +646,7 @@ dd a2.json completed, all passed!
dd a3.json completed, all passed!
dd a4.json completed, all passed!
dd a5.json completed, all passed!
dd a6.json completed, all passed!
dd a6.json completed: 0 passed, 1000 FAILED
dd a7.json completed, all passed!
dd a8.json completed, all passed!
dd a9.json completed, all passed!
@ -654,7 +654,7 @@ dd aa.json completed, all passed!
dd ab.json completed, all passed!
dd ac.json completed, all passed!
dd ad.json completed, all passed!
dd ae.json completed, all passed!
dd ae.json completed: 0 passed, 1000 FAILED
dd af.json completed, all passed!
dd b0.json completed, all passed!
dd b1.json completed, all passed!
@ -662,7 +662,7 @@ dd b2.json completed, all passed!
dd b3.json completed, all passed!
dd b4.json completed, all passed!
dd b5.json completed, all passed!
dd b6.json completed, all passed!
dd b6.json completed: 0 passed, 1000 FAILED
dd b7.json completed, all passed!
dd b8.json completed, all passed!
dd b9.json completed, all passed!
@ -670,7 +670,7 @@ dd ba.json completed, all passed!
dd bb.json completed, all passed!
dd bc.json completed, all passed!
dd bd.json completed, all passed!
dd be.json completed, all passed!
dd be.json completed: 0 passed, 1000 FAILED
dd bf.json completed, all passed!
dd c0.json completed, all passed!
dd c1.json completed, all passed!
@ -1038,7 +1038,7 @@ ed 5f.json completed, all passed!
ed 60.json completed, all passed!
ed 61.json completed, all passed!
ed 62.json completed, all passed!
ed 63.json completed, all passed!
ed 63.json completed: 0 passed, 1000 FAILED
ed 64.json completed, all passed!
ed 65.json completed, all passed!
ed 66.json completed, all passed!
@ -1046,7 +1046,7 @@ ed 67.json completed, all passed!
ed 68.json completed, all passed!
ed 69.json completed, all passed!
ed 6a.json completed, all passed!
ed 6b.json completed, all passed!
ed 6b.json completed: 0 passed, 1000 FAILED
ed 6c.json completed, all passed!
ed 6d.json completed, all passed!
ed 6e.json completed, all passed!
@ -1058,7 +1058,7 @@ ed 73.json completed, all passed!
ed 74.json completed, all passed!
ed 75.json completed, all passed!
ed 76.json completed, all passed!
ed 77.json completed, all passed!
ed 77.json completed: 0 passed, 1000 FAILED
ed 78.json completed, all passed!
ed 79.json completed, all passed!
ed 7a.json completed, all passed!
@ -1066,7 +1066,7 @@ ed 7b.json completed, all passed!
ed 7c.json completed, all passed!
ed 7d.json completed, all passed!
ed 7e.json completed, all passed!
ed 7f.json completed, all passed!
ed 7f.json completed: 0 passed, 1000 FAILED
ed a0.json completed, all passed!
ed a1.json completed: 0 passed, 1000 FAILED
ed a2.json completed: 13 passed, 987 FAILED
@ -1234,7 +1234,7 @@ fd 82.json completed, all passed!
fd 83.json completed, all passed!
fd 84.json completed, all passed!
fd 85.json completed, all passed!
fd 86.json completed, all passed!
fd 86.json completed: 0 passed, 1000 FAILED
fd 87.json completed, all passed!
fd 88.json completed, all passed!
fd 89.json completed, all passed!
@ -1242,7 +1242,7 @@ fd 8a.json completed, all passed!
fd 8b.json completed, all passed!
fd 8c.json completed, all passed!
fd 8d.json completed, all passed!
fd 8e.json completed, all passed!
fd 8e.json completed: 0 passed, 1000 FAILED
fd 8f.json completed, all passed!
fd 90.json completed, all passed!
fd 91.json completed, all passed!
@ -1250,7 +1250,7 @@ fd 92.json completed, all passed!
fd 93.json completed, all passed!
fd 94.json completed, all passed!
fd 95.json completed, all passed!
fd 96.json completed, all passed!
fd 96.json completed: 0 passed, 1000 FAILED
fd 97.json completed, all passed!
fd 98.json completed, all passed!
fd 99.json completed, all passed!
@ -1258,7 +1258,7 @@ fd 9a.json completed, all passed!
fd 9b.json completed, all passed!
fd 9c.json completed, all passed!
fd 9d.json completed, all passed!
fd 9e.json completed, all passed!
fd 9e.json completed: 0 passed, 1000 FAILED
fd 9f.json completed, all passed!
fd a0.json completed, all passed!
fd a1.json completed, all passed!
@ -1266,7 +1266,7 @@ fd a2.json completed, all passed!
fd a3.json completed, all passed!
fd a4.json completed, all passed!
fd a5.json completed, all passed!
fd a6.json completed, all passed!
fd a6.json completed: 0 passed, 1000 FAILED
fd a7.json completed, all passed!
fd a8.json completed, all passed!
fd a9.json completed, all passed!
@ -1274,7 +1274,7 @@ fd aa.json completed, all passed!
fd ab.json completed, all passed!
fd ac.json completed, all passed!
fd ad.json completed, all passed!
fd ae.json completed, all passed!
fd ae.json completed: 0 passed, 1000 FAILED
fd af.json completed, all passed!
fd b0.json completed, all passed!
fd b1.json completed, all passed!
@ -1282,7 +1282,7 @@ fd b2.json completed, all passed!
fd b3.json completed, all passed!
fd b4.json completed, all passed!
fd b5.json completed, all passed!
fd b6.json completed, all passed!
fd b6.json completed: 0 passed, 1000 FAILED
fd b7.json completed, all passed!
fd b8.json completed, all passed!
fd b9.json completed, all passed!
@ -1290,7 +1290,7 @@ fd ba.json completed, all passed!
fd bb.json completed, all passed!
fd bc.json completed, all passed!
fd bd.json completed, all passed!
fd be.json completed, all passed!
fd be.json completed: 0 passed, 1000 FAILED
fd bf.json completed, all passed!
fd c0.json completed, all passed!
fd c1.json completed, all passed!
@ -1611,5 +1611,5 @@ fd ff.json completed, all passed!
fe.json completed, all passed!
ff.json completed, all passed!
passed: 1594638, failed: 15362, total 99%
completed in 1m 39s
passed: 1574638, failed: 35362, total 98%
completed in 1m 19s

View File

@ -7,5 +7,5 @@ RESULTS=latest.txt
cd $LOCATION
echo "Last run on $DATE at commit $COMMIT" | tee $RESULTS
echo "" | tee -a $RESULTS
cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-undocumented | tee -a $RESULTS
cargo run -- -q --testsuite "../jsmoo/misc/tests/GeneratedTests/z80/v1/" --check-undocumented --check-timings | tee -a $RESULTS
}

View File

@ -6,16 +6,19 @@
the cost of having more events on the queue when re-scheduling. There needs to be a mechanism to avoid the event queue ballooning due to
an error
* can you somehow make devices have two step functions for running things at different times? (I'm thinking ym2612 audio gen vs timers)
* the genesis coprocessor stuff will be a good reference point for things that make multiple devices, and how to add them correctly to the system
* address repeater on ym2612 doesn't seem to work the same, when it's on the 68000 device. The Z80 device doesn't have an affect, but maybe it's not being used
* I like making address adapters like this (below)
* you could have busport take a closure or something which translates the address, and returns an error that will be passed up if it occurs
in order to implement the correct behaviour for address exceptions in 68k, transparently
* make Signal directional, by making SignalDriver and SignalInput or SignalReceiver
* fix the m68k timings
* add rust runtime checks for math to look for overflow errors
* fix the watchers in the Bus, maybe make them manual
* make Signal directional, by making SignalDriver and SignalInput or SignalReceiver
* the genesis coprocessor stuff will be a good reference point for things that make multiple devices, and how to add them correctly to the system
* make it possible to compile without audio support (minifb frontend requires it atm)
* does Z80 need a customized Z80BusPort like the 68k?
* can you make it so you don't need borrow_mut() so much?
@ -49,7 +52,6 @@
* get rustfmt, rustdoc, and clippy working in some kind of semi-automatic fashion
* you really need a full web-based debugger
* test m68k cycle timing again