-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathz80emu.cpp
154 lines (122 loc) · 4.56 KB
/
z80emu.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "z80emu.h"
#include <string.h>
using namespace llz80emu;
z80emu::z80emu(bool clk) : _clkpin(clk), _fetch_cycle(_pins, _regs), _mem_read_cycle(_pins), _mem_write_cycle(_pins), _io_read_cycle(_pins), _io_write_cycle(_pins), _bogus_cycle(_pins, _regs), _intack_cycle(_pins, _regs), _instr(*this, _regs) {
}
void z80emu::set_clkpin(bool state) {
_clkpin = state;
}
z80_pins_t z80emu::clock(z80_pinbits_t state) {
_clkpin = !_clkpin; // toggle clock pin
_pins.state = (_pins.state & _pins.dir) | (state & ~_pins.dir); // update pin state (only replacing input pin bits)
if (_clkpin) {
/* handle reset logic */
if (!(_pins.state & Z80_RESET)) {
/* reset pin pulled low */
_por = true;
_pins = Z80_PINS_INIT; // reset pins
_reset_m1t2 = (_cycle && _cycle->type == Z80_FETCH_CYCLE && _cycle->t == 0); _reset_cycles++;
_cycle = nullptr; // stop current cycle
}
else if (_por && !_cycle) {
if (_reset_cycles == 1 && _reset_m1t2) {
/* RESET was only held for 1 cycle during M1T2 - special reset */
_regs.REG_PC = 0;
}
else {
/* normal reset */
memset(&_regs, 0, sizeof(_regs)); _regs.REG_SP = _regs.REG_AF = 0xFFFF;
}
/* rising edge and still in reset - get out of reset now (and also synchronise with clock pin) */
start_fetch_cycle();
_reset_cycles = 0;
}
}
if (_cycle) {
if (_clkpin) _intpin = !(_pins.state & Z80_INT); // sample INT pin
/* operate cycle */
if (_cycle->clock(_clkpin)) {
/* cycle has finished */
if (!_instr.started()) _instr.start(); // exiting fetch/interrupt acknowledgment cycle - start decoding and executing new instruction
else _instr.next_step(); // run next step of instruction execution
if (!_instr.started()) {
/* instruction execution complete */
if (_nmiff && !_nmi_skip) {
/* NMI triggered */
_regs.iff2 = _regs.iff1; _regs.iff1 = false; // disable interrupt while keeping former IFF1 state in IFF2
_nmiff = false; _nmi_pending = true; // clear NMI flip-flop (so it can be re-activated at some other point), then stage NMI servicing
// if (!(_pins.state & Z80_HALT)) _regs.REG_PC++; // if we're halting and an interrupt occurred, we'll need to bring ourselves out of the HALT instruction
return _pins; // after this, a fetch cycle will be issued as normal, but it won't be followed by a normal instruction decode/execution
}
if (_intpin && _regs.iff1 && !_int_skip) {
/* INT triggered and can be accepted */
_regs.iff1 = false; // disable interrupt
if (!_regs.int_mode) start_intack_cycle(_regs.instr); // mode 0: read to instruction ptr (this will be handled as normal)
else { // mode 1/2
start_intack_cycle(_regs.REG_Z); // read to Z (mode 1 can ignore, mode 2 can use this to calculate vector)
_int_pending = true; // mark as handling INT so instr_decoder can work on the rest
if (!(_pins.state & Z80_HALT)) _regs.REG_PC++; // if we're halting and an interrupt occurred, we'll need to bring ourselves out of the HALT instruction
// mode 1: extra clock cycle + push PC + jump to 0x0038
// mode 2: extra clock cycle + push PC + read new PC from vector
}
return _pins;
}
_nmi_skip = _int_skip = false;
}
}
}
return _pins;
}
z80_pins_t z80emu::get_pins() {
return _pins;
}
void z80emu::trigger_nmi() {
_nmiff = true;
}
void z80emu::start_fetch_cycle(bool halt) {
_int_pending = _nmi_pending = false; // now that we're back to normal operation
_fetch_cycle.reset(halt);
_cycle = &_fetch_cycle;
}
void z80emu::start_mem_read_cycle(uint16_t addr, uint8_t& val_out) {
_mem_read_cycle.reset(addr, val_out);
_cycle = &_mem_read_cycle;
}
void z80emu::start_mem_write_cycle(uint16_t addr, uint8_t val) {
_mem_write_cycle.reset(addr, val);
_cycle = &_mem_write_cycle;
}
void z80emu::start_io_read_cycle(uint16_t addr, uint8_t& val_out) {
_io_read_cycle.reset(addr, val_out);
_cycle = &_io_read_cycle;
}
void z80emu::start_io_write_cycle(uint16_t addr, uint8_t val) {
_io_write_cycle.reset(addr, val);
_cycle = &_io_write_cycle;
}
void z80emu::start_bogus_cycle(int cycles) {
_bogus_cycle.reset(cycles);
_cycle = &_bogus_cycle;
}
void z80emu::start_intack_cycle(uint8_t& val_out) {
_intack_cycle.reset(val_out);
_cycle = &_intack_cycle;
}
z80_registers_t z80emu::get_regs() {
return _regs;
}
void z80emu::set_regs(const z80_registers_t& regs) {
_regs = regs;
}
bool z80emu::is_nmi_pending() const {
return _nmi_pending;
}
void z80emu::skip_int_handling() {
_int_skip = true;
}
void z80emu::skip_nmi_handling() {
_nmi_skip = true;
}
bool z80emu::is_int_pending() const {
return _int_pending;
}