mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-12 00:30:31 +00:00
Merge pull request #163 from TomHarte/WaitSampling
Adjusts the timing of the Z80's wait line sampling to be on a half clock and better regularises 'action' partial bus cycles
This commit is contained in:
commit
b9f4f7a530
@ -99,8 +99,12 @@ HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &c
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Interrupt:
|
case CPU::Z80::PartialMachineCycle::Interrupt:
|
||||||
|
// resetting event is M1 and IOREQ both simultaneously having leading edges;
|
||||||
|
// that happens 2 cycles before the end of INTACK. So the timer was reset and
|
||||||
|
// now has advanced twice.
|
||||||
|
horizontal_counter_ = HalfCycles(2);
|
||||||
|
|
||||||
*cycle.value = 0xff;
|
*cycle.value = 0xff;
|
||||||
horizontal_counter_ = 0;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Refresh:
|
case CPU::Z80::PartialMachineCycle::Refresh:
|
||||||
@ -109,7 +113,7 @@ HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &c
|
|||||||
// final two cycles of an opcode fetch. Therefore communicate a transient signalling
|
// final two cycles of an opcode fetch. Therefore communicate a transient signalling
|
||||||
// of the IRQ line if necessary.
|
// of the IRQ line if necessary.
|
||||||
if(!(address & 0x40)) {
|
if(!(address & 0x40)) {
|
||||||
set_interrupt_line(true, -2);
|
set_interrupt_line(true, Cycles(-2));
|
||||||
set_interrupt_line(false);
|
set_interrupt_line(false);
|
||||||
}
|
}
|
||||||
if(has_latched_video_byte_) {
|
if(has_latched_video_byte_) {
|
||||||
@ -126,8 +130,7 @@ HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &c
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::ReadOpcodeStart:
|
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||||
case CPU::Z80::PartialMachineCycle::ReadOpcodeWait:
|
|
||||||
// Check for use of the fast tape hack.
|
// Check for use of the fast tape hack.
|
||||||
if(use_fast_tape_hack_ && address == tape_trap_address_ && tape_player_.has_tape()) {
|
if(use_fast_tape_hack_ && address == tape_trap_address_ && tape_player_.has_tape()) {
|
||||||
uint64_t prior_offset = tape_player_.get_tape()->get_offset();
|
uint64_t prior_offset = tape_player_.get_tape()->get_offset();
|
||||||
|
@ -34,6 +34,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0xbd, 0x00, 0x00, // [4] LDA $0000, x (no wrap)
|
0xbd, 0x00, 0x00, // [4] LDA $0000, x (no wrap)
|
||||||
0xbd, 0x02, 0x00, // [5] LDA $0002, x (wrap)
|
0xbd, 0x02, 0x00, // [5] LDA $0002, x (wrap)
|
||||||
0xb9, 0x00, 0x00, // [4] LDA $0000, y (no wrap)
|
0xb9, 0x00, 0x00, // [4] LDA $0000, y (no wrap)
|
||||||
|
|
||||||
0xb9, 0x10, 0x00, // [5] LDA $0010, y (wrap)
|
0xb9, 0x10, 0x00, // [5] LDA $0010, y (wrap)
|
||||||
0xa1, 0x44, // [6] LDA ($44, x)
|
0xa1, 0x44, // [6] LDA ($44, x)
|
||||||
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
||||||
@ -222,7 +223,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
|
|
||||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||||
if self.endTime == 0 {
|
if self.endTime == 0 {
|
||||||
self.endTime = machine.timestamp - 1
|
self.endTime = (machine.timestamp / 2) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (uint32_t)timestamp {
|
- (uint32_t)timestamp {
|
||||||
return _processor->get_timestamp();
|
return _processor->get_timestamp().as_int();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setIrqLine:(BOOL)irqLine {
|
- (void)setIrqLine:(BOOL)irqLine {
|
||||||
|
@ -61,7 +61,7 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
|
|||||||
@property(nonatomic, readonly, nonnull) NSArray<CSTestMachineZ80BusOperationCapture *> *busOperationCaptures;
|
@property(nonatomic, readonly, nonnull) NSArray<CSTestMachineZ80BusOperationCapture *> *busOperationCaptures;
|
||||||
|
|
||||||
@property(nonatomic, readonly) BOOL isHalted;
|
@property(nonatomic, readonly) BOOL isHalted;
|
||||||
@property(nonatomic, readonly) int completedCycles;
|
@property(nonatomic, readonly) int completedHalfCycles;
|
||||||
|
|
||||||
@property(nonatomic) BOOL nmiLine;
|
@property(nonatomic) BOOL nmiLine;
|
||||||
@property(nonatomic) BOOL irqLine;
|
@property(nonatomic) BOOL irqLine;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation
|
- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation
|
||||||
address:(uint16_t)address
|
address:(uint16_t)address
|
||||||
value:(uint8_t)value
|
value:(uint8_t)value
|
||||||
timeStamp:(int)time_stamp;
|
timeStamp:(HalfCycles)time_stamp;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark - C++ delegate handlers
|
#pragma mark - C++ delegate handlers
|
||||||
@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat
|
|||||||
public:
|
public:
|
||||||
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
|
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
|
||||||
|
|
||||||
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) {
|
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
|
||||||
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
|
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
return _processor->get_halt_line() ? YES : NO;
|
return _processor->get_halt_line() ? YES : NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)completedCycles {
|
- (int)completedHalfCycles {
|
||||||
return _processor->get_timestamp();
|
return _processor->get_timestamp().as_int();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setNmiLine:(BOOL)nmiLine {
|
- (void)setNmiLine:(BOOL)nmiLine {
|
||||||
@ -184,7 +184,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
_processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr);
|
_processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp {
|
- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(HalfCycles)timeStamp {
|
||||||
if(self.captureBusActivity) {
|
if(self.captureBusActivity) {
|
||||||
CSTestMachineZ80BusOperationCapture *capture = [[CSTestMachineZ80BusOperationCapture alloc] init];
|
CSTestMachineZ80BusOperationCapture *capture = [[CSTestMachineZ80BusOperationCapture alloc] init];
|
||||||
switch(operation) {
|
switch(operation) {
|
||||||
@ -216,7 +216,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
}
|
}
|
||||||
capture.address = address;
|
capture.address = address;
|
||||||
capture.value = value;
|
capture.value = value;
|
||||||
capture.timeStamp = timeStamp;
|
capture.timeStamp = timeStamp.as_int();
|
||||||
|
|
||||||
[_busOperationCaptures addObject:capture];
|
[_busOperationCaptures addObject:capture];
|
||||||
}
|
}
|
||||||
|
@ -195,8 +195,8 @@ class FUSETests: XCTestCase {
|
|||||||
machine.runForNumber(ofCycles: Int32(targetState.tStates))
|
machine.runForNumber(ofCycles: Int32(targetState.tStates))
|
||||||
|
|
||||||
// Verify that exactly the right number of cycles was hit; this is a primitive cycle length tester.
|
// Verify that exactly the right number of cycles was hit; this is a primitive cycle length tester.
|
||||||
let cyclesRun = machine.completedCycles
|
let halfCyclesRun = machine.completedHalfCycles
|
||||||
XCTAssert(cyclesRun == Int32(targetState.tStates), "Instruction length off; was \(machine.completedCycles) but should be \(targetState.tStates): \(name)")
|
XCTAssert(halfCyclesRun == Int32(targetState.tStates) * 2, "Instruction length off; was \(machine.completedHalfCycles) but should be \(targetState.tStates * 2): \(name)")
|
||||||
|
|
||||||
let finalState = RegisterState(machine: machine)
|
let finalState = RegisterState(machine: machine)
|
||||||
|
|
||||||
|
@ -61,7 +61,9 @@ class Z80MachineCycleTests: XCTestCase {
|
|||||||
// array access
|
// array access
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
if length != busCycles[index].length || cycle.operation != busCycles[index].operation {
|
XCTAssert(length & Int32(1) == 0, "While performing \(machine.busOperationCaptures) Z80 ended on a half cycle")
|
||||||
|
|
||||||
|
if length != busCycles[index].length*2 || cycle.operation != busCycles[index].operation {
|
||||||
matches = false
|
matches = false
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
|
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
|
||||||
timestamp_++;
|
timestamp_ += Cycles(1);
|
||||||
|
|
||||||
if(operation == BusOperation::ReadOpcode) {
|
if(operation == BusOperation::ReadOpcode) {
|
||||||
check_address_for_trap(address);
|
check_address_for_trap(address);
|
||||||
|
@ -25,7 +25,7 @@ void AllRAMProcessor::get_data_at_address(uint16_t startAddress, size_t length,
|
|||||||
memcpy(data, &memory_[startAddress], endAddress - startAddress);
|
memcpy(data, &memory_[startAddress], endAddress - startAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AllRAMProcessor::get_timestamp() {
|
HalfCycles AllRAMProcessor::get_timestamp() {
|
||||||
return timestamp_;
|
return timestamp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
|
|
||||||
class AllRAMProcessor {
|
class AllRAMProcessor {
|
||||||
public:
|
public:
|
||||||
AllRAMProcessor(size_t memory_size);
|
AllRAMProcessor(size_t memory_size);
|
||||||
virtual uint32_t get_timestamp();
|
HalfCycles get_timestamp();
|
||||||
void set_data_at_address(uint16_t startAddress, size_t length, const uint8_t *data);
|
void set_data_at_address(uint16_t startAddress, size_t length, const uint8_t *data);
|
||||||
void get_data_at_address(uint16_t startAddress, size_t length, uint8_t *data);
|
void get_data_at_address(uint16_t startAddress, size_t length, uint8_t *data);
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ class AllRAMProcessor {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<uint8_t> memory_;
|
std::vector<uint8_t> memory_;
|
||||||
uint32_t timestamp_;
|
HalfCycles timestamp_;
|
||||||
|
|
||||||
inline void check_address_for_trap(uint16_t address) {
|
inline void check_address_for_trap(uint16_t address) {
|
||||||
if(traps_[address]) {
|
if(traps_[address]) {
|
||||||
|
@ -67,8 +67,7 @@ enum Flag: uint8_t {
|
|||||||
*/
|
*/
|
||||||
struct PartialMachineCycle {
|
struct PartialMachineCycle {
|
||||||
enum Operation {
|
enum Operation {
|
||||||
ReadOpcodeStart = 0,
|
ReadOpcode = 0,
|
||||||
ReadOpcodeWait,
|
|
||||||
Read,
|
Read,
|
||||||
Write,
|
Write,
|
||||||
Input,
|
Input,
|
||||||
@ -79,16 +78,19 @@ struct PartialMachineCycle {
|
|||||||
Internal,
|
Internal,
|
||||||
BusAcknowledge,
|
BusAcknowledge,
|
||||||
|
|
||||||
|
ReadOpcodeWait,
|
||||||
ReadWait,
|
ReadWait,
|
||||||
WriteWait,
|
WriteWait,
|
||||||
InputWait,
|
InputWait,
|
||||||
OutputWait,
|
OutputWait,
|
||||||
InterruptWait,
|
InterruptWait,
|
||||||
|
|
||||||
|
ReadOpcodeStart,
|
||||||
ReadStart,
|
ReadStart,
|
||||||
WriteStart,
|
WriteStart,
|
||||||
InputStart,
|
InputStart,
|
||||||
OutputStart,
|
OutputStart,
|
||||||
|
InterruptStart,
|
||||||
} operation;
|
} operation;
|
||||||
HalfCycles length;
|
HalfCycles length;
|
||||||
uint16_t *address;
|
uint16_t *address;
|
||||||
@ -102,33 +104,37 @@ struct PartialMachineCycle {
|
|||||||
return operation <= Operation::BusAcknowledge;
|
return operation <= Operation::BusAcknowledge;
|
||||||
}
|
}
|
||||||
inline bool is_wait() const {
|
inline bool is_wait() const {
|
||||||
return operation >= Operation::ReadWait && operation <= Operation::InterruptWait;
|
return operation >= Operation::ReadOpcodeWait && operation <= Operation::InterruptWait;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Elemental bus operations
|
// Elemental bus operations
|
||||||
#define ReadOpcodeStart() {PartialMachineCycle::ReadOpcodeStart, HalfCycles(4), &pc_.full, &operation_, false}
|
#define ReadOpcodeStart() {PartialMachineCycle::ReadOpcodeStart, HalfCycles(3), &pc_.full, &operation_, false}
|
||||||
#define ReadOpcodeWait(f) {PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f}
|
#define ReadOpcodeWait(f) {PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f}
|
||||||
|
#define ReadOpcodeEnd() {PartialMachineCycle::ReadOpcode, HalfCycles(1), &pc_.full, &operation_, false}
|
||||||
|
|
||||||
#define Refresh(len) {PartialMachineCycle::Refresh, HalfCycles(len), &refresh_addr_.full, nullptr, false}
|
#define Refresh(len) {PartialMachineCycle::Refresh, HalfCycles(len), &refresh_addr_.full, nullptr, false}
|
||||||
|
|
||||||
#define ReadStart(addr, val) {PartialMachineCycle::ReadStart, HalfCycles(4), &addr.full, &val, false}
|
#define ReadStart(addr, val) {PartialMachineCycle::ReadStart, HalfCycles(3), &addr.full, &val, false}
|
||||||
#define ReadWait(l, addr, val, f) {PartialMachineCycle::ReadWait, HalfCycles(l), &addr.full, &val, f}
|
#define ReadWait(l, addr, val, f) {PartialMachineCycle::ReadWait, HalfCycles(l), &addr.full, &val, f}
|
||||||
#define ReadEnd(addr, val) {PartialMachineCycle::Read, HalfCycles(2), &addr.full, &val, false}
|
#define ReadEnd(addr, val) {PartialMachineCycle::Read, HalfCycles(3), &addr.full, &val, false}
|
||||||
|
|
||||||
#define WriteStart(addr, val) {PartialMachineCycle::WriteStart,HalfCycles(4), &addr.full, &val, false}
|
#define WriteStart(addr, val) {PartialMachineCycle::WriteStart,HalfCycles(3), &addr.full, &val, false}
|
||||||
#define WriteWait(l, addr, val, f) {PartialMachineCycle::WriteWait, HalfCycles(l), &addr.full, &val, f}
|
#define WriteWait(l, addr, val, f) {PartialMachineCycle::WriteWait, HalfCycles(l), &addr.full, &val, f}
|
||||||
#define WriteEnd(addr, val) {PartialMachineCycle::Write, HalfCycles(2), &addr.full, &val, false}
|
#define WriteEnd(addr, val) {PartialMachineCycle::Write, HalfCycles(3), &addr.full, &val, false}
|
||||||
|
|
||||||
#define InputStart(addr, val) {PartialMachineCycle::InputStart, HalfCycles(4), &addr.full, &val, false}
|
#define InputStart(addr, val) {PartialMachineCycle::InputStart, HalfCycles(3), &addr.full, &val, false}
|
||||||
#define InputWait(addr, val, f) {PartialMachineCycle::InputWait, HalfCycles(2), &addr.full, &val, f}
|
#define InputWait(addr, val, f) {PartialMachineCycle::InputWait, HalfCycles(2), &addr.full, &val, f}
|
||||||
#define InputEnd(addr, val) {PartialMachineCycle::Input, HalfCycles(2), &addr.full, &val, false}
|
#define InputEnd(addr, val) {PartialMachineCycle::Input, HalfCycles(3), &addr.full, &val, false}
|
||||||
|
|
||||||
#define OutputStart(addr, val) {PartialMachineCycle::OutputStart, HalfCycles(4), &addr.full, &val, false}
|
#define OutputStart(addr, val) {PartialMachineCycle::OutputStart, HalfCycles(3), &addr.full, &val, false}
|
||||||
#define OutputWait(addr, val, f) {PartialMachineCycle::OutputWait, HalfCycles(2), &addr.full, &val, f}
|
#define OutputWait(addr, val, f) {PartialMachineCycle::OutputWait, HalfCycles(2), &addr.full, &val, f}
|
||||||
#define OutputEnd(addr, val) {PartialMachineCycle::Output, HalfCycles(2), &addr.full, &val, false}
|
#define OutputEnd(addr, val) {PartialMachineCycle::Output, HalfCycles(3), &addr.full, &val, false}
|
||||||
|
|
||||||
#define IntAck(length, val) {PartialMachineCycle::Interrupt, HalfCycles(length), nullptr, &val, false}
|
#define IntAckStart(length, val) {PartialMachineCycle::InterruptStart, HalfCycles(length), nullptr, &val, false}
|
||||||
#define IntWait(val) {PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true}
|
#define IntWait(val) {PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true}
|
||||||
|
#define IntAckEnd(val) {PartialMachineCycle::Interrupt, HalfCycles(3), nullptr, &val, false}
|
||||||
|
|
||||||
|
|
||||||
// A wrapper to express a bus operation as a micro-op
|
// A wrapper to express a bus operation as a micro-op
|
||||||
#define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
|
#define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
|
||||||
@ -741,12 +747,14 @@ template <class T> class Processor {
|
|||||||
const MicroOp normal_fetch_decode_execute[] = {
|
const MicroOp normal_fetch_decode_execute[] = {
|
||||||
BusOp(ReadOpcodeStart()),
|
BusOp(ReadOpcodeStart()),
|
||||||
BusOp(ReadOpcodeWait(true)),
|
BusOp(ReadOpcodeWait(true)),
|
||||||
|
BusOp(ReadOpcodeEnd()),
|
||||||
{ MicroOp::DecodeOperation }
|
{ MicroOp::DecodeOperation }
|
||||||
};
|
};
|
||||||
const MicroOp short_fetch_decode_execute[] = {
|
const MicroOp short_fetch_decode_execute[] = {
|
||||||
BusOp(ReadOpcodeStart()),
|
BusOp(ReadOpcodeStart()),
|
||||||
BusOp(ReadOpcodeWait(false)),
|
BusOp(ReadOpcodeWait(false)),
|
||||||
BusOp(ReadOpcodeWait(true)),
|
BusOp(ReadOpcodeWait(true)),
|
||||||
|
BusOp(ReadOpcodeEnd()),
|
||||||
{ MicroOp::DecodeOperation }
|
{ MicroOp::DecodeOperation }
|
||||||
};
|
};
|
||||||
copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute);
|
copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute);
|
||||||
@ -813,6 +821,7 @@ template <class T> class Processor {
|
|||||||
{ MicroOp::BeginNMI },
|
{ MicroOp::BeginNMI },
|
||||||
BusOp(ReadOpcodeStart()),
|
BusOp(ReadOpcodeStart()),
|
||||||
BusOp(ReadOpcodeWait(true)),
|
BusOp(ReadOpcodeWait(true)),
|
||||||
|
BusOp(ReadOpcodeEnd()),
|
||||||
BusOp(Refresh(6)),
|
BusOp(Refresh(6)),
|
||||||
Push(pc_),
|
Push(pc_),
|
||||||
{ MicroOp::JumpTo66, nullptr, nullptr},
|
{ MicroOp::JumpTo66, nullptr, nullptr},
|
||||||
@ -820,14 +829,16 @@ template <class T> class Processor {
|
|||||||
};
|
};
|
||||||
MicroOp irq_mode0_program[] = {
|
MicroOp irq_mode0_program[] = {
|
||||||
{ MicroOp::BeginIRQMode0 },
|
{ MicroOp::BeginIRQMode0 },
|
||||||
BusOp(IntAck(8, operation_)),
|
BusOp(IntAckStart(5, operation_)),
|
||||||
BusOp(IntWait(operation_)),
|
BusOp(IntWait(operation_)),
|
||||||
|
BusOp(IntAckEnd(operation_)),
|
||||||
{ MicroOp::DecodeOperationNoRChange }
|
{ MicroOp::DecodeOperationNoRChange }
|
||||||
};
|
};
|
||||||
MicroOp irq_mode1_program[] = {
|
MicroOp irq_mode1_program[] = {
|
||||||
{ MicroOp::BeginIRQ },
|
{ MicroOp::BeginIRQ },
|
||||||
BusOp(IntAck(10, operation_)),
|
BusOp(IntAckStart(7, operation_)),
|
||||||
BusOp(IntWait(operation_)),
|
BusOp(IntWait(operation_)),
|
||||||
|
BusOp(IntAckEnd(operation_)),
|
||||||
BusOp(Refresh(4)),
|
BusOp(Refresh(4)),
|
||||||
Push(pc_),
|
Push(pc_),
|
||||||
{ MicroOp::Move16, &temp16_.full, &pc_.full },
|
{ MicroOp::Move16, &temp16_.full, &pc_.full },
|
||||||
@ -835,8 +846,9 @@ template <class T> class Processor {
|
|||||||
};
|
};
|
||||||
MicroOp irq_mode2_program[] = {
|
MicroOp irq_mode2_program[] = {
|
||||||
{ MicroOp::BeginIRQ },
|
{ MicroOp::BeginIRQ },
|
||||||
BusOp(IntAck(10, temp16_.bytes.low)),
|
BusOp(IntAckStart(7, temp16_.bytes.low)),
|
||||||
BusOp(IntWait(temp16_.bytes.low)),
|
BusOp(IntWait(temp16_.bytes.low)),
|
||||||
|
BusOp(IntAckEnd(temp16_.bytes.low)),
|
||||||
BusOp(Refresh(4)),
|
BusOp(Refresh(4)),
|
||||||
Push(pc_),
|
Push(pc_),
|
||||||
{ MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high },
|
{ MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high },
|
||||||
@ -1882,7 +1894,7 @@ template <class T> class Processor {
|
|||||||
how many cycles before now the line changed state. The value may not be longer than the
|
how many cycles before now the line changed state. The value may not be longer than the
|
||||||
current machine cycle. If called at any other time, this must be zero.
|
current machine cycle. If called at any other time, this must be zero.
|
||||||
*/
|
*/
|
||||||
void set_interrupt_line(bool value, int offset = 0) {
|
void set_interrupt_line(bool value, HalfCycles offset = 0) {
|
||||||
if(irq_line_ == value) return;
|
if(irq_line_ == value) return;
|
||||||
|
|
||||||
// IRQ requests are level triggered and masked.
|
// IRQ requests are level triggered and masked.
|
||||||
@ -1896,7 +1908,7 @@ template <class T> class Processor {
|
|||||||
// If this change happened at least one cycle ago then: (i) we're promised that this is a machine
|
// If this change happened at least one cycle ago then: (i) we're promised that this is a machine
|
||||||
// cycle per the contract on supplying an offset; and (ii) that means it happened before the lines
|
// cycle per the contract on supplying an offset; and (ii) that means it happened before the lines
|
||||||
// were sampled. So adjust the most recent sample.
|
// were sampled. So adjust the most recent sample.
|
||||||
if(offset < 0) {
|
if(offset <= HalfCycles(-2)) {
|
||||||
last_request_status_ = (last_request_status_ & ~Interrupt::IRQ) | (request_status_ & Interrupt::IRQ);
|
last_request_status_ = (last_request_status_ & ~Interrupt::IRQ) | (request_status_ & Interrupt::IRQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,14 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
|||||||
ConcreteAllRAMProcessor() : AllRAMProcessor() {}
|
ConcreteAllRAMProcessor() : AllRAMProcessor() {}
|
||||||
|
|
||||||
inline Cycles perform_machine_cycle(const PartialMachineCycle &cycle) {
|
inline Cycles perform_machine_cycle(const PartialMachineCycle &cycle) {
|
||||||
timestamp_ += cycle.length.as_int();
|
timestamp_ += cycle.length;
|
||||||
if(!cycle.is_terminal()) {
|
if(!cycle.is_terminal()) {
|
||||||
return Cycles(0);
|
return Cycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||||
switch(cycle.operation) {
|
switch(cycle.operation) {
|
||||||
case PartialMachineCycle::ReadOpcodeStart:
|
case PartialMachineCycle::ReadOpcode:
|
||||||
check_address_for_trap(address);
|
check_address_for_trap(address);
|
||||||
case PartialMachineCycle::Read:
|
case PartialMachineCycle::Read:
|
||||||
*cycle.value = memory_[address];
|
*cycle.value = memory_[address];
|
||||||
@ -57,7 +57,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(delegate_ != nullptr) {
|
if(delegate_ != nullptr) {
|
||||||
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_ >> 1);
|
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Cycles(0);
|
return Cycles(0);
|
||||||
@ -94,10 +94,6 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
|
|||||||
void set_wait_line(bool value) {
|
void set_wait_line(bool value) {
|
||||||
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_wait_line(value);
|
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_wait_line(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_timestamp() {
|
|
||||||
return timestamp_ >> 1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class AllRAMProcessor:
|
|||||||
static AllRAMProcessor *Processor();
|
static AllRAMProcessor *Processor();
|
||||||
|
|
||||||
struct MemoryAccessDelegate {
|
struct MemoryAccessDelegate {
|
||||||
virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) = 0;
|
virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) = 0;
|
||||||
};
|
};
|
||||||
inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) {
|
inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) {
|
||||||
delegate_ = delegate;
|
delegate_ = delegate;
|
||||||
@ -38,8 +38,6 @@ class AllRAMProcessor:
|
|||||||
virtual void set_non_maskable_interrupt_line(bool value) = 0;
|
virtual void set_non_maskable_interrupt_line(bool value) = 0;
|
||||||
virtual void set_wait_line(bool value) = 0;
|
virtual void set_wait_line(bool value) = 0;
|
||||||
|
|
||||||
virtual uint32_t get_timestamp() = 0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MemoryAccessDelegate *delegate_;
|
MemoryAccessDelegate *delegate_;
|
||||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536), delegate_(nullptr) {}
|
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536), delegate_(nullptr) {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user