@neovand/zilion 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/shader.d.ts +15 -0
- package/dist/shader.d.ts.map +1 -0
- package/dist/shader.js +120 -0
- package/dist/shader.js.map +1 -0
- package/dist/z80-core.wgsl.d.ts +2 -0
- package/dist/z80-core.wgsl.d.ts.map +1 -0
- package/dist/z80-core.wgsl.js +790 -0
- package/dist/z80-core.wgsl.js.map +1 -0
- package/dist/zilion.d.ts +67 -0
- package/dist/zilion.d.ts.map +1 -0
- package/dist/zilion.js +184 -0
- package/dist/zilion.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,790 @@
|
|
|
1
|
+
// Zilion — WebGPU Z80 core (WGSL), generated from a differentially-tested
|
|
2
|
+
// implementation. Do not edit by hand; see the project README.
|
|
3
|
+
//
|
|
4
|
+
// `__MEM_SIZE__` and `__MEM_MASK__` are replaced at shader build time with the
|
|
5
|
+
// per-instance memory size (a power of two) and its address mask.
|
|
6
|
+
export const Z80_CORE_WGSL = /* wgsl */ `
|
|
7
|
+
// === Z80 CPU State (per invocation) ===
|
|
8
|
+
var<private> cpu_a: u32;
|
|
9
|
+
var<private> cpu_f: u32;
|
|
10
|
+
var<private> cpu_b: u32;
|
|
11
|
+
var<private> cpu_c: u32;
|
|
12
|
+
var<private> cpu_d: u32;
|
|
13
|
+
var<private> cpu_e: u32;
|
|
14
|
+
var<private> cpu_h: u32;
|
|
15
|
+
var<private> cpu_l: u32;
|
|
16
|
+
var<private> cpu_sp: u32;
|
|
17
|
+
var<private> cpu_pc: u32;
|
|
18
|
+
var<private> cpu_a2: u32;
|
|
19
|
+
var<private> cpu_f2: u32;
|
|
20
|
+
var<private> cpu_b2: u32;
|
|
21
|
+
var<private> cpu_c2: u32;
|
|
22
|
+
var<private> cpu_d2: u32;
|
|
23
|
+
var<private> cpu_e2: u32;
|
|
24
|
+
var<private> cpu_h2: u32;
|
|
25
|
+
var<private> cpu_l2: u32;
|
|
26
|
+
var<private> cpu_halted: u32;
|
|
27
|
+
var<private> cpu_iff1: u32;
|
|
28
|
+
var<private> cpu_iff2: u32;
|
|
29
|
+
var<private> cpu_ix: u32;
|
|
30
|
+
var<private> cpu_iy: u32;
|
|
31
|
+
// Index-prefix state for the instruction currently executing:
|
|
32
|
+
// idx_mode: 0 = HL, 1 = IX, 2 = IY
|
|
33
|
+
// idx_disp: sign-extended displacement for (IX+d)/(IY+d)
|
|
34
|
+
// idx_uses_mem: 1 when this instruction dereferences (IX+d)/(IY+d), which
|
|
35
|
+
// means H/L operands are NOT substituted by IXH/IXL (real Z80 rule).
|
|
36
|
+
var<private> idx_mode: u32;
|
|
37
|
+
var<private> idx_disp: u32;
|
|
38
|
+
var<private> idx_uses_mem: u32;
|
|
39
|
+
|
|
40
|
+
// HOST CONTRACT: the host shader must declare the following BEFORE this core:
|
|
41
|
+
// fn mem_read(addr: u32) -> u32 // read one byte from memory
|
|
42
|
+
// fn mem_write(addr: u32, val: u32) // write one byte to memory
|
|
43
|
+
// fn on_fetch_opcode(op: u32) -> bool // return true to skip (NOP) an
|
|
44
|
+
// // opcode after prefix resolution
|
|
45
|
+
// This lets the host choose the memory model (mask, modulo, storage buffer, …)
|
|
46
|
+
// and hook opcode execution (e.g. instruction suppression). See buildComputeShader.
|
|
47
|
+
|
|
48
|
+
// Z80 flag bits
|
|
49
|
+
const CF: u32 = 0x01u;
|
|
50
|
+
const NF: u32 = 0x02u;
|
|
51
|
+
const PF: u32 = 0x04u;
|
|
52
|
+
const F3: u32 = 0x08u;
|
|
53
|
+
const HF: u32 = 0x10u;
|
|
54
|
+
const F5: u32 = 0x20u;
|
|
55
|
+
const ZF: u32 = 0x40u;
|
|
56
|
+
const SFl: u32 = 0x80u;
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
fn z80_fetch() -> u32 {
|
|
60
|
+
let val = mem_read(cpu_pc);
|
|
61
|
+
cpu_pc = (cpu_pc + 1u) & 0xffffu;
|
|
62
|
+
return val;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn z80_fetch_word() -> u32 {
|
|
66
|
+
let lo = z80_fetch();
|
|
67
|
+
let hi = z80_fetch();
|
|
68
|
+
return (hi << 8u) | lo;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn z80_push16(val: u32) {
|
|
72
|
+
cpu_sp = (cpu_sp - 1u) & 0xffffu;
|
|
73
|
+
mem_write(cpu_sp, (val >> 8u) & 0xffu);
|
|
74
|
+
cpu_sp = (cpu_sp - 1u) & 0xffffu;
|
|
75
|
+
mem_write(cpu_sp, val & 0xffu);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn z80_pop16() -> u32 {
|
|
79
|
+
let lo = mem_read(cpu_sp);
|
|
80
|
+
cpu_sp = (cpu_sp + 1u) & 0xffffu;
|
|
81
|
+
let hi = mem_read(cpu_sp);
|
|
82
|
+
cpu_sp = (cpu_sp + 1u) & 0xffffu;
|
|
83
|
+
return (hi << 8u) | lo;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fn signed_byte(b: u32) -> i32 {
|
|
87
|
+
let sb = i32(b);
|
|
88
|
+
if (sb > 127) { return sb - 256; }
|
|
89
|
+
return sb;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// === Register Access ===
|
|
93
|
+
fn get_bc() -> u32 { return (cpu_b << 8u) | cpu_c; }
|
|
94
|
+
fn get_de() -> u32 { return (cpu_d << 8u) | cpu_e; }
|
|
95
|
+
fn get_hl() -> u32 { return (cpu_h << 8u) | cpu_l; }
|
|
96
|
+
fn get_af() -> u32 { return (cpu_a << 8u) | cpu_f; }
|
|
97
|
+
|
|
98
|
+
fn set_bc(v: u32) { cpu_b = (v >> 8u) & 0xffu; cpu_c = v & 0xffu; }
|
|
99
|
+
fn set_de(v: u32) { cpu_d = (v >> 8u) & 0xffu; cpu_e = v & 0xffu; }
|
|
100
|
+
fn set_hl(v: u32) { cpu_h = (v >> 8u) & 0xffu; cpu_l = v & 0xffu; }
|
|
101
|
+
fn set_af(v: u32) { cpu_a = (v >> 8u) & 0xffu; cpu_f = v & 0xffu; }
|
|
102
|
+
|
|
103
|
+
fn get_reg(idx: u32) -> u32 {
|
|
104
|
+
switch(idx) {
|
|
105
|
+
case 0u: { return cpu_b; }
|
|
106
|
+
case 1u: { return cpu_c; }
|
|
107
|
+
case 2u: { return cpu_d; }
|
|
108
|
+
case 3u: { return cpu_e; }
|
|
109
|
+
case 4u: {
|
|
110
|
+
if (idx_mode != 0u && idx_uses_mem == 0u) { return (idx_reg16() >> 8u) & 0xffu; } // IXH/IYH
|
|
111
|
+
return cpu_h;
|
|
112
|
+
}
|
|
113
|
+
case 5u: {
|
|
114
|
+
if (idx_mode != 0u && idx_uses_mem == 0u) { return idx_reg16() & 0xffu; } // IXL/IYL
|
|
115
|
+
return cpu_l;
|
|
116
|
+
}
|
|
117
|
+
case 6u: { return mem_read(idx_addr()); }
|
|
118
|
+
case 7u: { return cpu_a; }
|
|
119
|
+
default: { return 0u; }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fn set_reg(idx: u32, val: u32) {
|
|
124
|
+
let v = val & 0xffu;
|
|
125
|
+
switch(idx) {
|
|
126
|
+
case 0u: { cpu_b = v; }
|
|
127
|
+
case 1u: { cpu_c = v; }
|
|
128
|
+
case 2u: { cpu_d = v; }
|
|
129
|
+
case 3u: { cpu_e = v; }
|
|
130
|
+
case 4u: {
|
|
131
|
+
if (idx_mode != 0u && idx_uses_mem == 0u) { set_idx_reg16((idx_reg16() & 0x00ffu) | (v << 8u)); }
|
|
132
|
+
else { cpu_h = v; }
|
|
133
|
+
}
|
|
134
|
+
case 5u: {
|
|
135
|
+
if (idx_mode != 0u && idx_uses_mem == 0u) { set_idx_reg16((idx_reg16() & 0xff00u) | v); }
|
|
136
|
+
else { cpu_l = v; }
|
|
137
|
+
}
|
|
138
|
+
case 6u: { mem_write(idx_addr(), v); }
|
|
139
|
+
case 7u: { cpu_a = v; }
|
|
140
|
+
default: {}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn get_reg16(idx: u32) -> u32 {
|
|
145
|
+
switch(idx) {
|
|
146
|
+
case 0u: { return get_bc(); }
|
|
147
|
+
case 1u: { return get_de(); }
|
|
148
|
+
case 2u: { return idx_reg16(); } // HL / IX / IY
|
|
149
|
+
case 3u: { return cpu_sp; }
|
|
150
|
+
default: { return 0u; }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
fn set_reg16(idx: u32, val: u32) {
|
|
155
|
+
let v = val & 0xffffu;
|
|
156
|
+
switch(idx) {
|
|
157
|
+
case 0u: { set_bc(v); }
|
|
158
|
+
case 1u: { set_de(v); }
|
|
159
|
+
case 2u: { set_idx_reg16(v); } // HL / IX / IY
|
|
160
|
+
case 3u: { cpu_sp = v; }
|
|
161
|
+
default: {}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn get_reg16_af(idx: u32) -> u32 {
|
|
166
|
+
if (idx == 3u) { return get_af(); }
|
|
167
|
+
return get_reg16(idx);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn set_reg16_af(idx: u32, val: u32) {
|
|
171
|
+
if (idx == 3u) { set_af(val); } else { set_reg16(idx, val); }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// === Index register (IX/IY) helpers ===
|
|
175
|
+
// The 16-bit register the current prefix maps HL to (HL itself when no prefix).
|
|
176
|
+
fn idx_reg16() -> u32 {
|
|
177
|
+
if (idx_mode == 1u) { return cpu_ix; }
|
|
178
|
+
if (idx_mode == 2u) { return cpu_iy; }
|
|
179
|
+
return get_hl();
|
|
180
|
+
}
|
|
181
|
+
fn set_idx_reg16(v: u32) {
|
|
182
|
+
if (idx_mode == 1u) { cpu_ix = v & 0xffffu; }
|
|
183
|
+
else if (idx_mode == 2u) { cpu_iy = v & 0xffffu; }
|
|
184
|
+
else { set_hl(v); }
|
|
185
|
+
}
|
|
186
|
+
// Address used for (HL) / (IX+d) / (IY+d).
|
|
187
|
+
fn idx_addr() -> u32 {
|
|
188
|
+
if (idx_mode != 0u) { return (idx_reg16() + idx_disp) & 0xffffu; }
|
|
189
|
+
return get_hl();
|
|
190
|
+
}
|
|
191
|
+
// Sign-extend a displacement byte to 16 bits (two's complement).
|
|
192
|
+
fn signext(b: u32) -> u32 {
|
|
193
|
+
if (b >= 0x80u) { return b | 0xff00u; }
|
|
194
|
+
return b;
|
|
195
|
+
}
|
|
196
|
+
// Does this main opcode dereference (HL)? (Determines displacement fetch and
|
|
197
|
+
// whether H/L operands are IXH/IXL or real H/L under a DD/FD prefix.)
|
|
198
|
+
fn op_uses_hl_mem(op: u32) -> bool {
|
|
199
|
+
let x = (op >> 6u) & 3u;
|
|
200
|
+
let y = (op >> 3u) & 7u;
|
|
201
|
+
let z = op & 7u;
|
|
202
|
+
if (x == 1u) { return (y == 6u || z == 6u) && !(y == 6u && z == 6u); } // LD r,(HL)/(HL),r (not HALT)
|
|
203
|
+
if (x == 2u) { return z == 6u; } // ALU A,(HL)
|
|
204
|
+
if (x == 0u) {
|
|
205
|
+
if (z == 4u || z == 5u || z == 6u) { return y == 6u; } // INC/DEC (HL), LD (HL),n
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
// Write a REAL 8-bit register (no IX/IY substitution) — used by the
|
|
211
|
+
// undocumented DDCB/FDCB register-copy side effect.
|
|
212
|
+
fn set_reg_raw(idx: u32, val: u32) {
|
|
213
|
+
let v = val & 0xffu;
|
|
214
|
+
switch(idx) {
|
|
215
|
+
case 0u: { cpu_b = v; }
|
|
216
|
+
case 1u: { cpu_c = v; }
|
|
217
|
+
case 2u: { cpu_d = v; }
|
|
218
|
+
case 3u: { cpu_e = v; }
|
|
219
|
+
case 4u: { cpu_h = v; }
|
|
220
|
+
case 5u: { cpu_l = v; }
|
|
221
|
+
case 7u: { cpu_a = v; }
|
|
222
|
+
default: {}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// === Flag Helpers ===
|
|
227
|
+
fn sz_flags(val: u32) -> u32 {
|
|
228
|
+
var f = val & SFl;
|
|
229
|
+
if (val == 0u) { f |= ZF; }
|
|
230
|
+
f |= val & (F3 | F5);
|
|
231
|
+
return f;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
fn parity(val: u32) -> bool {
|
|
235
|
+
var p = val;
|
|
236
|
+
p ^= p >> 4u;
|
|
237
|
+
p ^= p >> 2u;
|
|
238
|
+
p ^= p >> 1u;
|
|
239
|
+
return (p & 1u) == 0u;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn check_cc(cc: u32) -> bool {
|
|
243
|
+
switch(cc) {
|
|
244
|
+
case 0u: { return (cpu_f & ZF) == 0u; }
|
|
245
|
+
case 1u: { return (cpu_f & ZF) != 0u; }
|
|
246
|
+
case 2u: { return (cpu_f & CF) == 0u; }
|
|
247
|
+
case 3u: { return (cpu_f & CF) != 0u; }
|
|
248
|
+
case 4u: { return (cpu_f & PF) == 0u; }
|
|
249
|
+
case 5u: { return (cpu_f & PF) != 0u; }
|
|
250
|
+
case 6u: { return (cpu_f & SFl) == 0u; }
|
|
251
|
+
case 7u: { return (cpu_f & SFl) != 0u; }
|
|
252
|
+
default: { return false; }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// === ALU ===
|
|
257
|
+
fn z80_alu(op: u32, val: u32) {
|
|
258
|
+
let a = cpu_a;
|
|
259
|
+
let c = cpu_f & CF;
|
|
260
|
+
switch(op) {
|
|
261
|
+
case 0u: { // ADD
|
|
262
|
+
let r = a + val;
|
|
263
|
+
cpu_f = sz_flags(r & 0xffu) | select(0u, CF, r > 0xffu) |
|
|
264
|
+
((a ^ val ^ r) & HF) |
|
|
265
|
+
select(0u, PF, ((~(a ^ val)) & (a ^ r) & 0x80u) != 0u);
|
|
266
|
+
cpu_a = r & 0xffu;
|
|
267
|
+
}
|
|
268
|
+
case 1u: { // ADC
|
|
269
|
+
let r = a + val + c;
|
|
270
|
+
cpu_f = sz_flags(r & 0xffu) | select(0u, CF, r > 0xffu) |
|
|
271
|
+
((a ^ val ^ r) & HF) |
|
|
272
|
+
select(0u, PF, ((~(a ^ val)) & (a ^ r) & 0x80u) != 0u);
|
|
273
|
+
cpu_a = r & 0xffu;
|
|
274
|
+
}
|
|
275
|
+
case 2u: { // SUB
|
|
276
|
+
let r = i32(a) - i32(val);
|
|
277
|
+
let ru = u32(r) & 0xffu;
|
|
278
|
+
cpu_f = sz_flags(ru) | NF | select(0u, CF, r < 0) |
|
|
279
|
+
((a ^ val ^ u32(r)) & HF) |
|
|
280
|
+
select(0u, PF, (((a ^ val) & (a ^ u32(r))) & 0x80u) != 0u);
|
|
281
|
+
cpu_a = ru;
|
|
282
|
+
}
|
|
283
|
+
case 3u: { // SBC
|
|
284
|
+
let r = i32(a) - i32(val) - i32(c);
|
|
285
|
+
let ru = u32(r) & 0xffu;
|
|
286
|
+
cpu_f = sz_flags(ru) | NF | select(0u, CF, r < 0) |
|
|
287
|
+
((a ^ val ^ u32(r)) & HF) |
|
|
288
|
+
select(0u, PF, (((a ^ val) & (a ^ u32(r))) & 0x80u) != 0u);
|
|
289
|
+
cpu_a = ru;
|
|
290
|
+
}
|
|
291
|
+
case 4u: { // AND
|
|
292
|
+
cpu_a = a & val;
|
|
293
|
+
cpu_f = sz_flags(cpu_a) | HF | select(0u, PF, parity(cpu_a));
|
|
294
|
+
}
|
|
295
|
+
case 5u: { // XOR
|
|
296
|
+
cpu_a = a ^ val;
|
|
297
|
+
cpu_f = sz_flags(cpu_a) | select(0u, PF, parity(cpu_a));
|
|
298
|
+
}
|
|
299
|
+
case 6u: { // OR
|
|
300
|
+
cpu_a = a | val;
|
|
301
|
+
cpu_f = sz_flags(cpu_a) | select(0u, PF, parity(cpu_a));
|
|
302
|
+
}
|
|
303
|
+
case 7u: { // CP
|
|
304
|
+
let r = i32(a) - i32(val);
|
|
305
|
+
let ru = u32(r) & 0xffu;
|
|
306
|
+
cpu_f = (ru & SFl) | select(0u, ZF, ru == 0u) | (val & (F3 | F5)) | NF |
|
|
307
|
+
select(0u, CF, r < 0) | ((a ^ val ^ u32(r)) & HF) |
|
|
308
|
+
select(0u, PF, (((a ^ val) & (a ^ u32(r))) & 0x80u) != 0u);
|
|
309
|
+
}
|
|
310
|
+
default: {}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
fn z80_inc8(val: u32) -> u32 {
|
|
315
|
+
let r = (val + 1u) & 0xffu;
|
|
316
|
+
cpu_f = (cpu_f & CF) | sz_flags(r) |
|
|
317
|
+
select(0u, PF, val == 0x7fu) |
|
|
318
|
+
select(0u, HF, (r & 0x0fu) == 0u);
|
|
319
|
+
return r;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
fn z80_dec8(val: u32) -> u32 {
|
|
323
|
+
let r = (val - 1u) & 0xffu;
|
|
324
|
+
cpu_f = (cpu_f & CF) | sz_flags(r) | NF |
|
|
325
|
+
select(0u, PF, val == 0x80u) |
|
|
326
|
+
select(0u, HF, (val & 0x0fu) == 0u);
|
|
327
|
+
return r;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fn z80_add_hl(val: u32) {
|
|
331
|
+
let hl = idx_reg16(); // ADD HL,rp / ADD IX,rp / ADD IY,rp
|
|
332
|
+
let r = hl + val;
|
|
333
|
+
cpu_f = (cpu_f & (SFl | ZF | PF)) |
|
|
334
|
+
select(0u, CF, r > 0xffffu) |
|
|
335
|
+
select(0u, HF, ((hl ^ val ^ r) & 0x1000u) != 0u) |
|
|
336
|
+
((r >> 8u) & (F3 | F5));
|
|
337
|
+
set_idx_reg16(r & 0xffffu);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// === Rotate/Shift for accumulator ===
|
|
341
|
+
fn z80_rot_accum(y: u32) {
|
|
342
|
+
let a = cpu_a;
|
|
343
|
+
let c = cpu_f & CF;
|
|
344
|
+
let keep = cpu_f & (SFl | ZF | PF);
|
|
345
|
+
switch(y) {
|
|
346
|
+
case 0u: { // RLCA
|
|
347
|
+
cpu_a = ((a << 1u) | (a >> 7u)) & 0xffu;
|
|
348
|
+
cpu_f = keep | (a >> 7u) | (cpu_a & (F3 | F5));
|
|
349
|
+
}
|
|
350
|
+
case 1u: { // RRCA
|
|
351
|
+
cpu_a = ((a >> 1u) | (a << 7u)) & 0xffu;
|
|
352
|
+
cpu_f = keep | (a & 1u) | (cpu_a & (F3 | F5));
|
|
353
|
+
}
|
|
354
|
+
case 2u: { // RLA
|
|
355
|
+
cpu_a = ((a << 1u) | c) & 0xffu;
|
|
356
|
+
cpu_f = keep | (a >> 7u) | (cpu_a & (F3 | F5));
|
|
357
|
+
}
|
|
358
|
+
case 3u: { // RRA
|
|
359
|
+
cpu_a = ((a >> 1u) | (c << 7u)) & 0xffu;
|
|
360
|
+
cpu_f = keep | (a & 1u) | (cpu_a & (F3 | F5));
|
|
361
|
+
}
|
|
362
|
+
case 4u: { // DAA
|
|
363
|
+
var correction = 0u;
|
|
364
|
+
var carry = c;
|
|
365
|
+
if ((cpu_f & HF) != 0u || (a & 0x0fu) > 9u) { correction |= 0x06u; }
|
|
366
|
+
if (c != 0u || a > 0x99u) { correction |= 0x60u; carry = 1u; }
|
|
367
|
+
if ((cpu_f & NF) != 0u) { cpu_a = (a - correction) & 0xffu; }
|
|
368
|
+
else { cpu_a = (a + correction) & 0xffu; }
|
|
369
|
+
cpu_f = (cpu_f & NF) | sz_flags(cpu_a) | carry |
|
|
370
|
+
((a ^ cpu_a) & HF) | select(0u, PF, parity(cpu_a));
|
|
371
|
+
}
|
|
372
|
+
case 5u: { // CPL
|
|
373
|
+
cpu_a = (~a) & 0xffu;
|
|
374
|
+
cpu_f = (cpu_f & (SFl | ZF | PF | CF)) | HF | NF | (cpu_a & (F3 | F5));
|
|
375
|
+
}
|
|
376
|
+
case 6u: { // SCF
|
|
377
|
+
cpu_f = (cpu_f & (SFl | ZF | PF)) | CF | (cpu_a & (F3 | F5));
|
|
378
|
+
}
|
|
379
|
+
case 7u: { // CCF
|
|
380
|
+
cpu_f = (cpu_f & (SFl | ZF | PF)) |
|
|
381
|
+
select(0u, HF, c != 0u) |
|
|
382
|
+
select(CF, 0u, c != 0u) |
|
|
383
|
+
(cpu_a & (F3 | F5));
|
|
384
|
+
}
|
|
385
|
+
default: {}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// === CB Prefix (bit ops, rotates, shifts) ===
|
|
390
|
+
fn z80_cb_rot(op: u32, val: u32) -> u32 {
|
|
391
|
+
let c = cpu_f & CF;
|
|
392
|
+
var r = 0u;
|
|
393
|
+
switch(op) {
|
|
394
|
+
case 0u: { r = ((val << 1u) | (val >> 7u)) & 0xffu; cpu_f = sz_flags(r) | (val >> 7u) | select(0u, PF, parity(r)); }
|
|
395
|
+
case 1u: { r = ((val >> 1u) | (val << 7u)) & 0xffu; cpu_f = sz_flags(r) | (val & 1u) | select(0u, PF, parity(r)); }
|
|
396
|
+
case 2u: { r = ((val << 1u) | c) & 0xffu; cpu_f = sz_flags(r) | (val >> 7u) | select(0u, PF, parity(r)); }
|
|
397
|
+
case 3u: { r = ((val >> 1u) | (c << 7u)) & 0xffu; cpu_f = sz_flags(r) | (val & 1u) | select(0u, PF, parity(r)); }
|
|
398
|
+
case 4u: { r = (val << 1u) & 0xffu; cpu_f = sz_flags(r) | (val >> 7u) | select(0u, PF, parity(r)); }
|
|
399
|
+
case 5u: { r = ((val >> 1u) | (val & 0x80u)) & 0xffu; cpu_f = sz_flags(r) | (val & 1u) | select(0u, PF, parity(r)); }
|
|
400
|
+
case 6u: { r = ((val << 1u) | 1u) & 0xffu; cpu_f = sz_flags(r) | (val >> 7u) | select(0u, PF, parity(r)); }
|
|
401
|
+
case 7u: { r = (val >> 1u) & 0xffu; cpu_f = sz_flags(r) | (val & 1u) | select(0u, PF, parity(r)); }
|
|
402
|
+
default: { r = val; }
|
|
403
|
+
}
|
|
404
|
+
return r;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
fn z80_exec_cb() {
|
|
408
|
+
let op = z80_fetch();
|
|
409
|
+
let x = (op >> 6u) & 3u;
|
|
410
|
+
let y = (op >> 3u) & 7u;
|
|
411
|
+
let z = op & 7u;
|
|
412
|
+
let val = get_reg(z);
|
|
413
|
+
switch(x) {
|
|
414
|
+
case 0u: { set_reg(z, z80_cb_rot(y, val)); }
|
|
415
|
+
case 1u: { // BIT
|
|
416
|
+
cpu_f = (cpu_f & CF) | HF |
|
|
417
|
+
select(0u, ZF | PF, (val & (1u << y)) == 0u) |
|
|
418
|
+
select(0u, SFl, y == 7u && (val & 0x80u) != 0u) |
|
|
419
|
+
(val & (F3 | F5));
|
|
420
|
+
}
|
|
421
|
+
case 2u: { set_reg(z, val & ~(1u << y)); }
|
|
422
|
+
case 3u: { set_reg(z, val | (1u << y)); }
|
|
423
|
+
default: {}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// DDCB / FDCB: operates on (IX+d)/(IY+d). The displacement (idx_disp) has
|
|
428
|
+
// already been fetched. For rot/shift/RES/SET the result is written to memory
|
|
429
|
+
// AND (undocumented) copied to the real register in the low 3 bits unless it is
|
|
430
|
+
// 6. For BIT, the undocumented F3/F5 come from the high byte of the address.
|
|
431
|
+
fn z80_exec_idxcb(cbop: u32) {
|
|
432
|
+
let addr = (idx_reg16() + idx_disp) & 0xffffu;
|
|
433
|
+
let cx = (cbop >> 6u) & 3u;
|
|
434
|
+
let cy = (cbop >> 3u) & 7u;
|
|
435
|
+
let cz = cbop & 7u;
|
|
436
|
+
let val = mem_read(addr);
|
|
437
|
+
switch(cx) {
|
|
438
|
+
case 0u: {
|
|
439
|
+
let r = z80_cb_rot(cy, val);
|
|
440
|
+
mem_write(addr, r);
|
|
441
|
+
if (cz != 6u) { set_reg_raw(cz, r); }
|
|
442
|
+
}
|
|
443
|
+
case 1u: { // BIT n,(IX+d)
|
|
444
|
+
cpu_f = (cpu_f & CF) | HF |
|
|
445
|
+
select(0u, ZF | PF, (val & (1u << cy)) == 0u) |
|
|
446
|
+
select(0u, SFl, cy == 7u && (val & 0x80u) != 0u) |
|
|
447
|
+
((addr >> 8u) & (F3 | F5));
|
|
448
|
+
}
|
|
449
|
+
case 2u: {
|
|
450
|
+
let r = val & ~(1u << cy);
|
|
451
|
+
mem_write(addr, r);
|
|
452
|
+
if (cz != 6u) { set_reg_raw(cz, r); }
|
|
453
|
+
}
|
|
454
|
+
case 3u: {
|
|
455
|
+
let r = val | (1u << cy);
|
|
456
|
+
mem_write(addr, r);
|
|
457
|
+
if (cz != 6u) { set_reg_raw(cz, r); }
|
|
458
|
+
}
|
|
459
|
+
default: {}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// === Block Transfer (ED prefix) ===
|
|
464
|
+
fn z80_ldi() {
|
|
465
|
+
let val = mem_read(get_hl());
|
|
466
|
+
mem_write(get_de(), val);
|
|
467
|
+
set_hl((get_hl() + 1u) & 0xffffu);
|
|
468
|
+
set_de((get_de() + 1u) & 0xffffu);
|
|
469
|
+
set_bc((get_bc() - 1u) & 0xffffu);
|
|
470
|
+
let n = (val + cpu_a) & 0xffu;
|
|
471
|
+
cpu_f = (cpu_f & (SFl | ZF | CF)) |
|
|
472
|
+
select(0u, PF, get_bc() != 0u) |
|
|
473
|
+
(n & F3) | select(0u, F5, (n & 0x02u) != 0u);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
fn z80_ldd() {
|
|
477
|
+
let val = mem_read(get_hl());
|
|
478
|
+
mem_write(get_de(), val);
|
|
479
|
+
set_hl((get_hl() - 1u) & 0xffffu);
|
|
480
|
+
set_de((get_de() - 1u) & 0xffffu);
|
|
481
|
+
set_bc((get_bc() - 1u) & 0xffffu);
|
|
482
|
+
let n = (val + cpu_a) & 0xffu;
|
|
483
|
+
cpu_f = (cpu_f & (SFl | ZF | CF)) |
|
|
484
|
+
select(0u, PF, get_bc() != 0u) |
|
|
485
|
+
(n & F3) | select(0u, F5, (n & 0x02u) != 0u);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
fn z80_cpi() {
|
|
489
|
+
let val = mem_read(get_hl());
|
|
490
|
+
let r = (cpu_a - val) & 0xffu;
|
|
491
|
+
let hf = (cpu_a ^ val ^ r) & HF;
|
|
492
|
+
// Undocumented F3/F5 come from n = A-(HL)-HF (bit 3 -> F3, bit 1 -> F5).
|
|
493
|
+
let n = (r - select(0u, 1u, hf != 0u)) & 0xffu;
|
|
494
|
+
set_hl((get_hl() + 1u) & 0xffffu);
|
|
495
|
+
set_bc((get_bc() - 1u) & 0xffffu);
|
|
496
|
+
cpu_f = (cpu_f & CF) | (r & SFl) | select(0u, ZF, r == 0u) | NF |
|
|
497
|
+
hf | select(0u, PF, get_bc() != 0u) |
|
|
498
|
+
(n & F3) | ((n & 0x02u) << 4u);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
fn z80_cpd() {
|
|
502
|
+
let val = mem_read(get_hl());
|
|
503
|
+
let r = (cpu_a - val) & 0xffu;
|
|
504
|
+
let hf = (cpu_a ^ val ^ r) & HF;
|
|
505
|
+
let n = (r - select(0u, 1u, hf != 0u)) & 0xffu;
|
|
506
|
+
set_hl((get_hl() - 1u) & 0xffffu);
|
|
507
|
+
set_bc((get_bc() - 1u) & 0xffffu);
|
|
508
|
+
cpu_f = (cpu_f & CF) | (r & SFl) | select(0u, ZF, r == 0u) | NF |
|
|
509
|
+
hf | select(0u, PF, get_bc() != 0u) |
|
|
510
|
+
(n & F3) | ((n & 0x02u) << 4u);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// === ED Prefix ===
|
|
514
|
+
fn z80_exec_ed() {
|
|
515
|
+
let op = z80_fetch();
|
|
516
|
+
switch(op) {
|
|
517
|
+
case 0xa0u: { z80_ldi(); }
|
|
518
|
+
case 0xa8u: { z80_ldd(); }
|
|
519
|
+
case 0xb0u: { z80_ldi(); if (get_bc() != 0u) { cpu_pc = (cpu_pc - 2u) & 0xffffu; } } // LDIR
|
|
520
|
+
case 0xb8u: { z80_ldd(); if (get_bc() != 0u) { cpu_pc = (cpu_pc - 2u) & 0xffffu; } } // LDDR
|
|
521
|
+
case 0xa1u: { z80_cpi(); }
|
|
522
|
+
case 0xa9u: { z80_cpd(); }
|
|
523
|
+
case 0xb1u: { z80_cpi(); if (get_bc() != 0u && (cpu_f & ZF) == 0u) { cpu_pc = (cpu_pc - 2u) & 0xffffu; } }
|
|
524
|
+
case 0xb9u: { z80_cpd(); if (get_bc() != 0u && (cpu_f & ZF) == 0u) { cpu_pc = (cpu_pc - 2u) & 0xffffu; } }
|
|
525
|
+
// NEG
|
|
526
|
+
case 0x44u, 0x4cu, 0x54u, 0x5cu, 0x64u, 0x6cu, 0x74u, 0x7cu: {
|
|
527
|
+
let a = cpu_a; cpu_a = 0u; z80_alu(2u, a);
|
|
528
|
+
}
|
|
529
|
+
// RETN/RETI
|
|
530
|
+
case 0x45u, 0x4du, 0x55u, 0x5du, 0x65u, 0x6du, 0x75u, 0x7du: {
|
|
531
|
+
cpu_iff1 = cpu_iff2; cpu_pc = z80_pop16();
|
|
532
|
+
}
|
|
533
|
+
// LD I,A / LD R,A / LD A,I / LD A,R
|
|
534
|
+
case 0x47u: {} // LD I,A - no I register in our sim
|
|
535
|
+
case 0x4fu: {} // LD R,A
|
|
536
|
+
// LD A,I / LD A,R: I and R are not modelled (treated as 0), so A becomes 0.
|
|
537
|
+
// Flags: S/Z from the loaded value, PF = IFF2, N/H reset, C preserved.
|
|
538
|
+
case 0x57u: { cpu_a = 0u; cpu_f = (cpu_f & CF) | sz_flags(0u) | select(0u, PF, cpu_iff2 != 0u); }
|
|
539
|
+
case 0x5fu: { cpu_a = 0u; cpu_f = (cpu_f & CF) | sz_flags(0u) | select(0u, PF, cpu_iff2 != 0u); }
|
|
540
|
+
// LD (nn), rr
|
|
541
|
+
case 0x43u, 0x53u, 0x63u, 0x73u: {
|
|
542
|
+
let nn = z80_fetch_word();
|
|
543
|
+
let rp = (op >> 4u) & 3u;
|
|
544
|
+
let val = get_reg16(rp);
|
|
545
|
+
mem_write(nn, val & 0xffu);
|
|
546
|
+
mem_write((nn + 1u) & 0xffffu, (val >> 8u) & 0xffu);
|
|
547
|
+
}
|
|
548
|
+
// LD rr, (nn)
|
|
549
|
+
case 0x4bu, 0x5bu, 0x6bu, 0x7bu: {
|
|
550
|
+
let nn = z80_fetch_word();
|
|
551
|
+
let rp = (op >> 4u) & 3u;
|
|
552
|
+
let lo = mem_read(nn);
|
|
553
|
+
let hi = mem_read((nn + 1u) & 0xffffu);
|
|
554
|
+
set_reg16(rp, (hi << 8u) | lo);
|
|
555
|
+
}
|
|
556
|
+
// ADC HL, rr
|
|
557
|
+
case 0x4au, 0x5au, 0x6au, 0x7au: {
|
|
558
|
+
let rp = (op >> 4u) & 3u;
|
|
559
|
+
let hl = get_hl();
|
|
560
|
+
let val = get_reg16(rp);
|
|
561
|
+
let c = cpu_f & CF;
|
|
562
|
+
let r = hl + val + c;
|
|
563
|
+
cpu_f = ((r >> 8u) & SFl) | select(0u, ZF, (r & 0xffffu) == 0u) |
|
|
564
|
+
select(0u, HF, ((hl ^ val ^ r) & 0x1000u) != 0u) |
|
|
565
|
+
select(0u, PF, ((~(hl ^ val)) & (hl ^ r) & 0x8000u) != 0u) |
|
|
566
|
+
select(0u, CF, r > 0xffffu) | ((r >> 8u) & (F3 | F5));
|
|
567
|
+
set_hl(r & 0xffffu);
|
|
568
|
+
}
|
|
569
|
+
// SBC HL, rr
|
|
570
|
+
case 0x42u, 0x52u, 0x62u, 0x72u: {
|
|
571
|
+
let rp = (op >> 4u) & 3u;
|
|
572
|
+
let hl = get_hl();
|
|
573
|
+
let val = get_reg16(rp);
|
|
574
|
+
let c = cpu_f & CF;
|
|
575
|
+
let r = i32(hl) - i32(val) - i32(c);
|
|
576
|
+
let ru = u32(r) & 0xffffu;
|
|
577
|
+
cpu_f = ((ru >> 8u) & SFl) | select(0u, ZF, ru == 0u) | NF |
|
|
578
|
+
select(0u, HF, ((hl ^ val ^ u32(r)) & 0x1000u) != 0u) |
|
|
579
|
+
select(0u, PF, (((hl ^ val) & (hl ^ u32(r))) & 0x8000u) != 0u) |
|
|
580
|
+
select(0u, CF, r < 0) | ((ru >> 8u) & (F3 | F5));
|
|
581
|
+
set_hl(ru);
|
|
582
|
+
}
|
|
583
|
+
// RRD
|
|
584
|
+
case 0x67u: {
|
|
585
|
+
let m = mem_read(get_hl());
|
|
586
|
+
mem_write(get_hl(), ((cpu_a << 4u) | (m >> 4u)) & 0xffu);
|
|
587
|
+
cpu_a = (cpu_a & 0xf0u) | (m & 0x0fu);
|
|
588
|
+
cpu_f = (cpu_f & CF) | sz_flags(cpu_a) | select(0u, PF, parity(cpu_a));
|
|
589
|
+
}
|
|
590
|
+
// RLD
|
|
591
|
+
case 0x6fu: {
|
|
592
|
+
let m = mem_read(get_hl());
|
|
593
|
+
mem_write(get_hl(), ((m << 4u) | (cpu_a & 0x0fu)) & 0xffu);
|
|
594
|
+
cpu_a = (cpu_a & 0xf0u) | (m >> 4u);
|
|
595
|
+
cpu_f = (cpu_f & CF) | sz_flags(cpu_a) | select(0u, PF, parity(cpu_a));
|
|
596
|
+
}
|
|
597
|
+
// IN r,(C) - simplified, just set to 0
|
|
598
|
+
case 0x40u, 0x48u, 0x50u, 0x58u, 0x60u, 0x68u, 0x70u, 0x78u: {
|
|
599
|
+
set_reg((op >> 3u) & 7u, 0u);
|
|
600
|
+
}
|
|
601
|
+
default: {} // unknown ED ops = NOP
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// === Main Opcode Execution ===
|
|
606
|
+
fn z80_exec_x0(y: u32, z: u32, p: u32, q: u32) {
|
|
607
|
+
switch(z) {
|
|
608
|
+
case 0u: {
|
|
609
|
+
switch(y) {
|
|
610
|
+
case 0u: {} // NOP
|
|
611
|
+
case 1u: { // EX AF,AF'
|
|
612
|
+
var t = cpu_a; cpu_a = cpu_a2; cpu_a2 = t;
|
|
613
|
+
t = cpu_f; cpu_f = cpu_f2; cpu_f2 = t;
|
|
614
|
+
}
|
|
615
|
+
case 2u: { // DJNZ
|
|
616
|
+
let d = signed_byte(z80_fetch());
|
|
617
|
+
cpu_b = (cpu_b - 1u) & 0xffu;
|
|
618
|
+
if (cpu_b != 0u) { cpu_pc = u32(i32(cpu_pc) + d) & 0xffffu; }
|
|
619
|
+
}
|
|
620
|
+
case 3u: { // JR
|
|
621
|
+
let d = signed_byte(z80_fetch());
|
|
622
|
+
cpu_pc = u32(i32(cpu_pc) + d) & 0xffffu;
|
|
623
|
+
}
|
|
624
|
+
default: { // JR cc (y-4)
|
|
625
|
+
let d = signed_byte(z80_fetch());
|
|
626
|
+
if (check_cc(y - 4u)) { cpu_pc = u32(i32(cpu_pc) + d) & 0xffffu; }
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
case 1u: {
|
|
631
|
+
if (q == 0u) { set_reg16(p, z80_fetch_word()); }
|
|
632
|
+
else { z80_add_hl(get_reg16(p)); }
|
|
633
|
+
}
|
|
634
|
+
case 2u: {
|
|
635
|
+
if (q == 0u) {
|
|
636
|
+
switch(p) {
|
|
637
|
+
case 0u: { mem_write(get_bc(), cpu_a); }
|
|
638
|
+
case 1u: { mem_write(get_de(), cpu_a); }
|
|
639
|
+
case 2u: { let nn = z80_fetch_word(); let hl = idx_reg16(); mem_write(nn, hl & 0xffu); mem_write((nn+1u) & 0xffffu, (hl >> 8u) & 0xffu); }
|
|
640
|
+
case 3u: { mem_write(z80_fetch_word(), cpu_a); }
|
|
641
|
+
default: {}
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
switch(p) {
|
|
645
|
+
case 0u: { cpu_a = mem_read(get_bc()); }
|
|
646
|
+
case 1u: { cpu_a = mem_read(get_de()); }
|
|
647
|
+
case 2u: { let nn = z80_fetch_word(); let lo = mem_read(nn); let hi = mem_read((nn+1u) & 0xffffu); set_idx_reg16((hi << 8u) | lo); }
|
|
648
|
+
case 3u: { cpu_a = mem_read(z80_fetch_word()); }
|
|
649
|
+
default: {}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
case 3u: {
|
|
654
|
+
if (q == 0u) { set_reg16(p, (get_reg16(p) + 1u) & 0xffffu); }
|
|
655
|
+
else { set_reg16(p, (get_reg16(p) - 1u) & 0xffffu); }
|
|
656
|
+
}
|
|
657
|
+
case 4u: { set_reg(y, z80_inc8(get_reg(y))); }
|
|
658
|
+
case 5u: { set_reg(y, z80_dec8(get_reg(y))); }
|
|
659
|
+
case 6u: { set_reg(y, z80_fetch()); }
|
|
660
|
+
case 7u: { z80_rot_accum(y); }
|
|
661
|
+
default: {}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
fn z80_exec_x3(y: u32, z: u32, p: u32, q: u32) {
|
|
666
|
+
switch(z) {
|
|
667
|
+
case 0u: { if (check_cc(y)) { cpu_pc = z80_pop16(); } }
|
|
668
|
+
case 1u: {
|
|
669
|
+
if (q == 0u) { set_reg16_af(p, z80_pop16()); }
|
|
670
|
+
else {
|
|
671
|
+
switch(p) {
|
|
672
|
+
case 0u: { cpu_pc = z80_pop16(); } // RET
|
|
673
|
+
case 1u: { // EXX
|
|
674
|
+
var t = cpu_b; cpu_b = cpu_b2; cpu_b2 = t;
|
|
675
|
+
t = cpu_c; cpu_c = cpu_c2; cpu_c2 = t;
|
|
676
|
+
t = cpu_d; cpu_d = cpu_d2; cpu_d2 = t;
|
|
677
|
+
t = cpu_e; cpu_e = cpu_e2; cpu_e2 = t;
|
|
678
|
+
t = cpu_h; cpu_h = cpu_h2; cpu_h2 = t;
|
|
679
|
+
t = cpu_l; cpu_l = cpu_l2; cpu_l2 = t;
|
|
680
|
+
}
|
|
681
|
+
case 2u: { cpu_pc = idx_reg16(); } // JP (HL)/(IX)/(IY)
|
|
682
|
+
case 3u: { cpu_sp = idx_reg16(); } // LD SP,HL/IX/IY
|
|
683
|
+
default: {}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
case 2u: { let nn = z80_fetch_word(); if (check_cc(y)) { cpu_pc = nn; } }
|
|
688
|
+
case 3u: {
|
|
689
|
+
switch(y) {
|
|
690
|
+
case 0u: { cpu_pc = z80_fetch_word(); } // JP nn
|
|
691
|
+
case 1u: { z80_exec_cb(); }
|
|
692
|
+
case 2u: { z80_fetch(); } // OUT (n),A - no I/O device, discard (matches zff outPort no-op)
|
|
693
|
+
case 3u: { z80_fetch(); cpu_a = 0u; } // IN A,(n) - no I/O device, reads 0 (matches zff inPort→0)
|
|
694
|
+
case 4u: { // EX (SP),HL / EX (SP),IX / EX (SP),IY
|
|
695
|
+
let lo = mem_read(cpu_sp);
|
|
696
|
+
let hi = mem_read((cpu_sp + 1u) & 0xffffu);
|
|
697
|
+
let hl = idx_reg16();
|
|
698
|
+
mem_write(cpu_sp, hl & 0xffu);
|
|
699
|
+
mem_write((cpu_sp + 1u) & 0xffffu, (hl >> 8u) & 0xffu);
|
|
700
|
+
set_idx_reg16((hi << 8u) | lo);
|
|
701
|
+
}
|
|
702
|
+
case 5u: { // EX DE,HL
|
|
703
|
+
let td = cpu_d; let te = cpu_e;
|
|
704
|
+
cpu_d = cpu_h; cpu_e = cpu_l;
|
|
705
|
+
cpu_h = td; cpu_l = te;
|
|
706
|
+
}
|
|
707
|
+
case 6u: { cpu_iff1 = 0u; cpu_iff2 = 0u; }
|
|
708
|
+
case 7u: { cpu_iff1 = 1u; cpu_iff2 = 1u; }
|
|
709
|
+
default: {}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
case 4u: { let nn = z80_fetch_word(); if (check_cc(y)) { z80_push16(cpu_pc); cpu_pc = nn; } }
|
|
713
|
+
case 5u: {
|
|
714
|
+
if (q == 0u) { z80_push16(get_reg16_af(p)); }
|
|
715
|
+
else {
|
|
716
|
+
switch(p) {
|
|
717
|
+
case 0u: { let nn = z80_fetch_word(); z80_push16(cpu_pc); cpu_pc = nn; } // CALL nn
|
|
718
|
+
case 1u, 3u: {} // DD/FD prefix handled in z80_step
|
|
719
|
+
case 2u: { z80_exec_ed(); }
|
|
720
|
+
default: {}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
case 6u: { z80_alu(y, z80_fetch()); }
|
|
725
|
+
case 7u: { z80_push16(cpu_pc); cpu_pc = y * 8u; } // RST
|
|
726
|
+
default: {}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
fn z80_execute(op: u32) {
|
|
731
|
+
let x = (op >> 6u) & 3u;
|
|
732
|
+
let y = (op >> 3u) & 7u;
|
|
733
|
+
let z = op & 7u;
|
|
734
|
+
let p = (y >> 1u) & 3u;
|
|
735
|
+
let q = y & 1u;
|
|
736
|
+
switch(x) {
|
|
737
|
+
case 0u: { z80_exec_x0(y, z, p, q); }
|
|
738
|
+
case 1u: {
|
|
739
|
+
if (y == 6u && z == 6u) { cpu_halted = 1u; }
|
|
740
|
+
else { set_reg(y, get_reg(z)); }
|
|
741
|
+
}
|
|
742
|
+
case 2u: { z80_alu(y, get_reg(z)); }
|
|
743
|
+
case 3u: { z80_exec_x3(y, z, p, q); }
|
|
744
|
+
default: {}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
fn z80_step() {
|
|
749
|
+
if (cpu_halted != 0u) { return; }
|
|
750
|
+
// Reset per-instruction index-prefix state.
|
|
751
|
+
idx_mode = 0u;
|
|
752
|
+
idx_uses_mem = 0u;
|
|
753
|
+
idx_disp = 0u;
|
|
754
|
+
|
|
755
|
+
var op = z80_fetch();
|
|
756
|
+
// A DD/FD prefix selects IX/IY for the following opcode.
|
|
757
|
+
if (op == 0xddu || op == 0xfdu) {
|
|
758
|
+
idx_mode = select(2u, 1u, op == 0xddu);
|
|
759
|
+
let next = z80_fetch();
|
|
760
|
+
// A prefix immediately followed by another prefix or ED is a wasted M1:
|
|
761
|
+
// this step consumes just the prefix; back up so the next step restarts
|
|
762
|
+
// at the following byte (matches real Z80 timing and avoids an
|
|
763
|
+
// unbounded fetch loop on all-prefix programs).
|
|
764
|
+
if (next == 0xddu || next == 0xfdu || next == 0xedu) {
|
|
765
|
+
cpu_pc = (cpu_pc - 1u) & 0xffffu;
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
op = next;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Host hook: skip execution (treat as NOP) if requested.
|
|
772
|
+
if (on_fetch_opcode(op)) { return; }
|
|
773
|
+
|
|
774
|
+
if (idx_mode != 0u) {
|
|
775
|
+
if (op == 0xcbu) {
|
|
776
|
+
// DDCB/FDCB: displacement precedes the CB opcode.
|
|
777
|
+
idx_disp = signext(z80_fetch());
|
|
778
|
+
let cbop = z80_fetch();
|
|
779
|
+
z80_exec_idxcb(cbop);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (op_uses_hl_mem(op)) {
|
|
783
|
+
idx_uses_mem = 1u;
|
|
784
|
+
idx_disp = signext(z80_fetch());
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
z80_execute(op);
|
|
788
|
+
}
|
|
789
|
+
`;
|
|
790
|
+
//# sourceMappingURL=z80-core.wgsl.js.map
|