yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
instructions.cc
Go to the documentation of this file.
2#include "util/log.h"
3
4namespace yaze {
5namespace emu {
6
7// ===========================================================================
8// CYCLE-ACCURATE ATOMIC INSTRUCTION IMPLEMENTATIONS
9// ===========================================================================
10// All instructions are now fully atomic (no bstep mechanism).
11// Cycle counts match Anomie's SPC700 reference and nesdev.org timing table.
12// Memory-targeting MOV instructions include dummy read cycles as documented.
13// ===========================================================================
14
15// ---------------------------------------------------------------------------
16// MOV Instructions (Load from memory to register)
17// ---------------------------------------------------------------------------
18
19void Spc700::MOV(uint16_t adr) {
20 // MOV A, (adr) - Read from memory to A
21 A = read(adr);
22 PSW.Z = (A == 0);
23 PSW.N = (A & 0x80);
24}
25
26void Spc700::MOVX(uint16_t adr) {
27 // MOV X, (adr) - Read from memory to X
28 X = read(adr);
29 PSW.Z = (X == 0);
30 PSW.N = (X & 0x80);
31}
32
33void Spc700::MOVY(uint16_t adr) {
34 // MOV Y, (adr) - Read from memory to Y
35 Y = read(adr);
36 PSW.Z = (Y == 0);
37 PSW.N = (Y & 0x80);
38}
39
40// ---------------------------------------------------------------------------
41// MOV Instructions (Store from register to memory)
42// ---------------------------------------------------------------------------
43// Note: Per Anomie's doc, these include a dummy read cycle before writing
44
45void Spc700::MOVS(uint16_t address) {
46 // MOV (address), A - Write A to memory (with dummy read)
47 read(address); // Dummy read (documented behavior)
48 write(address, A);
49}
50
51void Spc700::MOVSX(uint16_t address) {
52 // MOV (address), X - Write X to memory (with dummy read)
53 read(address); // Dummy read (documented behavior)
54 write(address, X);
55}
56
57void Spc700::MOVSY(uint16_t address) {
58 // MOV (address), Y - Write Y to memory (with dummy read)
59 read(address); // Dummy read (documented behavior)
60 write(address, Y);
61}
62
63void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
64 // MOV (address), #imm - Write immediate to memory (with dummy read)
65 read(address); // Dummy read (documented behavior)
66 write(address, operand);
67}
68
69// ---------------------------------------------------------------------------
70// Arithmetic Instructions (ADC, SBC)
71// ---------------------------------------------------------------------------
72
73void Spc700::ADC(uint16_t adr) {
74 // ADC A, (adr) - Add with carry
75 uint8_t value = read(adr);
76 uint16_t result = A + value + PSW.C;
77 PSW.V = ((A ^ result) & (value ^ result) & 0x80) != 0;
78 PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
79 PSW.C = (result > 0xFF);
80 A = result & 0xFF;
81 PSW.Z = (A == 0);
82 PSW.N = (A & 0x80) != 0;
83}
84
85void Spc700::ADCM(uint16_t& dest, uint8_t operand) {
86 // ADC (dest), operand - Add with carry to memory
87 uint8_t applyOn = read(dest);
88 int result = applyOn + operand + PSW.C;
89 PSW.V = ((applyOn & 0x80) == (operand & 0x80)) &&
90 ((operand & 0x80) != (result & 0x80));
91 PSW.H = ((applyOn & 0xf) + (operand & 0xf) + PSW.C) > 0xf;
92 PSW.C = result > 0xff;
93 write(dest, result & 0xFF);
94 PSW.Z = ((result & 0xFF) == 0);
95 PSW.N = (result & 0x80) != 0;
96}
97
98void Spc700::SBC(uint16_t adr) {
99 // SBC A, (adr) - Subtract with carry (borrow)
100 uint8_t value = read(adr) ^ 0xff;
101 int result = A + value + PSW.C;
102 PSW.V = ((A & 0x80) == (value & 0x80)) &&
103 ((value & 0x80) != (result & 0x80));
104 PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
105 PSW.C = result > 0xff;
106 A = result & 0xFF;
107 PSW.Z = (A == 0);
108 PSW.N = (A & 0x80) != 0;
109}
110
111void Spc700::SBCM(uint16_t& dest, uint8_t operand) {
112 // SBC (dest), operand - Subtract with carry from memory
113 operand ^= 0xff;
114 uint8_t applyOn = read(dest);
115 int result = applyOn + operand + PSW.C;
116 PSW.V = ((applyOn & 0x80) == (operand & 0x80)) &&
117 ((operand & 0x80) != (result & 0x80));
118 PSW.H = ((applyOn & 0xF) + (operand & 0xF) + PSW.C) > 0xF;
119 PSW.C = result > 0xFF;
120 write(dest, result & 0xFF);
121 PSW.Z = ((result & 0xFF) == 0);
122 PSW.N = (result & 0x80) != 0;
123}
124
125// ---------------------------------------------------------------------------
126// Comparison Instructions (CMP, CMPX, CMPY, CMPM)
127// ---------------------------------------------------------------------------
128
129void Spc700::CMP(uint16_t adr) {
130 // CMP A, (adr) - Compare A with memory
131 uint8_t value = read(adr) ^ 0xff;
132 int result = A + value + 1;
133 PSW.C = result > 0xff;
134 PSW.Z = ((result & 0xFF) == 0);
135 PSW.N = (result & 0x80) != 0;
136}
137
138void Spc700::CMPX(uint16_t adr) {
139 // CMP X, (adr) - Compare X with memory
140 uint8_t value = read(adr) ^ 0xff;
141 int result = X + value + 1;
142 PSW.C = result > 0xff;
143 PSW.Z = ((result & 0xFF) == 0);
144 PSW.N = (result & 0x80) != 0;
145}
146
147void Spc700::CMPY(uint16_t adr) {
148 // CMP Y, (adr) - Compare Y with memory
149 uint8_t value = read(adr) ^ 0xff;
150 int result = Y + value + 1;
151 PSW.C = result > 0xff;
152 PSW.Z = ((result & 0xFF) == 0);
153 PSW.N = (result & 0x80) != 0;
154}
155
156void Spc700::CMPM(uint16_t dst, uint8_t value) {
157 // CMP (dst), value - Compare memory with value
158 value ^= 0xff;
159 int result = read(dst) + value + 1;
160 PSW.C = result > 0xff;
161 callbacks_.idle(false); // Extra cycle for memory comparison
162 PSW.Z = ((result & 0xFF) == 0);
163 PSW.N = (result & 0x80) != 0;
164}
165
166// ---------------------------------------------------------------------------
167// Logical Instructions (AND, OR, EOR)
168// ---------------------------------------------------------------------------
169
170void Spc700::AND(uint16_t adr) {
171 // AND A, (adr) - Logical AND with memory
172 A &= read(adr);
173 PSW.Z = (A == 0);
174 PSW.N = (A & 0x80) != 0;
175}
176
177void Spc700::ANDM(uint16_t dest, uint8_t operand) {
178 // AND (dest), operand - Logical AND memory with value
179 uint8_t result = read(dest) & operand;
180 write(dest, result);
181 PSW.Z = (result == 0);
182 PSW.N = (result & 0x80) != 0;
183}
184
185void Spc700::OR(uint16_t adr) {
186 // OR A, (adr) - Logical OR with memory
187 A |= read(adr);
188 PSW.Z = (A == 0);
189 PSW.N = (A & 0x80) != 0;
190}
191
192void Spc700::ORM(uint16_t dst, uint8_t value) {
193 // OR (dst), value - Logical OR memory with value
194 uint8_t result = read(dst) | value;
195 write(dst, result);
196 PSW.Z = (result == 0);
197 PSW.N = (result & 0x80) != 0;
198}
199
200void Spc700::EOR(uint16_t adr) {
201 // EOR A, (adr) - Logical XOR with memory
202 A ^= read(adr);
203 PSW.Z = (A == 0);
204 PSW.N = (A & 0x80) != 0;
205}
206
207void Spc700::EORM(uint16_t dest, uint8_t operand) {
208 // EOR (dest), operand - Logical XOR memory with value
209 uint8_t result = read(dest) ^ operand;
210 write(dest, result);
211 PSW.Z = (result == 0);
212 PSW.N = (result & 0x80) != 0;
213}
214
215// ---------------------------------------------------------------------------
216// Shift and Rotate Instructions (ASL, LSR, ROL, ROR)
217// ---------------------------------------------------------------------------
218
219void Spc700::ASL(uint16_t adr) {
220 // ASL (adr) - Arithmetic shift left
221 uint8_t val = read(adr);
222 write(adr, val); // Dummy write (RMW instruction)
223 PSW.C = (val & 0x80) != 0;
224 val <<= 1;
225 write(adr, val); // Actual write
226 PSW.Z = (val == 0);
227 PSW.N = (val & 0x80) != 0;
228}
229
230void Spc700::LSR(uint16_t adr) {
231 // LSR (adr) - Logical shift right
232 uint8_t val = read(adr);
233 write(adr, val); // Dummy write (RMW instruction)
234 PSW.C = (val & 0x01) != 0;
235 val >>= 1;
236 write(adr, val); // Actual write
237 PSW.Z = (val == 0);
238 PSW.N = (val & 0x80) != 0;
239}
240
241void Spc700::ROL(uint16_t adr) {
242 // ROL (adr) - Rotate left through carry
243 uint8_t val = read(adr);
244 write(adr, val); // Dummy write (RMW instruction)
245 bool newC = (val & 0x80) != 0;
246 val = (val << 1) | PSW.C;
247 PSW.C = newC;
248 write(adr, val); // Actual write
249 PSW.Z = (val == 0);
250 PSW.N = (val & 0x80) != 0;
251}
252
253void Spc700::ROR(uint16_t adr) {
254 // ROR (adr) - Rotate right through carry
255 uint8_t val = read(adr);
256 write(adr, val); // Dummy write (RMW instruction)
257 bool newC = (val & 1) != 0;
258 val = (val >> 1) | (PSW.C << 7);
259 PSW.C = newC;
260 write(adr, val); // Actual write
261 PSW.Z = (val == 0);
262 PSW.N = (val & 0x80) != 0;
263}
264
265// ---------------------------------------------------------------------------
266// Increment/Decrement Instructions (INC, DEC)
267// ---------------------------------------------------------------------------
268
269void Spc700::INC(uint16_t adr) {
270 // INC (adr) - Increment memory
271 uint8_t val = read(adr);
272 write(adr, val); // Dummy write (RMW instruction)
273 val++;
274 write(adr, val); // Actual write
275 PSW.Z = (val == 0);
276 PSW.N = (val & 0x80) != 0;
277}
278
279void Spc700::DEC(uint16_t adr) {
280 // DEC (adr) - Decrement memory
281 uint8_t val = read(adr);
282 write(adr, val); // Dummy write (RMW instruction)
283 val--;
284 write(adr, val); // Actual write
285 PSW.Z = (val == 0);
286 PSW.N = (val & 0x80) != 0;
287}
288
289void Spc700::XCN(uint8_t operand, bool isImmediate) {
290 // XCN - Exchange nibbles (note: this is only used for A register)
291 uint8_t value = isImmediate ? imm() : operand;
292 value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
293 PSW.Z = (value == 0);
294 PSW.N = (value & 0x80) != 0;
295}
296
297// ---------------------------------------------------------------------------
298// 16-bit Instructions (MOVW, INCW, DECW, ADDW, SUBW, CMPW)
299// ---------------------------------------------------------------------------
300
301void Spc700::MOVW(uint16_t& dest, uint16_t operand) {
302 // MOVW - Move 16-bit word
303 dest = operand;
304 PSW.Z = (operand == 0);
305 PSW.N = (operand & 0x8000) != 0;
306}
307
308void Spc700::INCW(uint16_t& operand) {
309 // INCW - Increment 16-bit word
310 operand++;
311 PSW.Z = (operand == 0);
312 PSW.N = (operand & 0x8000) != 0;
313}
314
315void Spc700::DECW(uint16_t& operand) {
316 // DECW - Decrement 16-bit word
317 operand--;
318 PSW.Z = (operand == 0);
319 PSW.N = (operand & 0x8000) != 0;
320}
321
322void Spc700::ADDW(uint16_t& dest, uint16_t operand) {
323 // ADDW - Add 16-bit word
324 uint32_t result = dest + operand;
325 PSW.C = (result > 0xFFFF);
326 PSW.Z = ((result & 0xFFFF) == 0);
327 PSW.N = (result & 0x8000) != 0;
328 PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000) != 0;
329 PSW.H = ((dest & 0xfff) + (operand & 0xfff)) > 0xfff;
330 dest = result & 0xFFFF;
331}
332
333void Spc700::SUBW(uint16_t& dest, uint16_t operand) {
334 // SUBW - Subtract 16-bit word
335 uint32_t result = dest - operand;
336 PSW.C = (result <= 0xFFFF);
337 PSW.Z = ((result & 0xFFFF) == 0);
338 PSW.N = (result & 0x8000) != 0;
339 PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000) != 0;
340 PSW.H = ((dest & 0xfff) - (operand & 0xfff)) >= 0;
341 dest = result & 0xFFFF;
342}
343
344void Spc700::CMPW(uint16_t operand) {
345 // CMPW - Compare 16-bit word with YA
346 uint32_t result = YA - operand;
347 PSW.C = (result <= 0xFFFF);
348 PSW.Z = ((result & 0xFFFF) == 0);
349 PSW.N = (result & 0x8000) != 0;
350}
351
352// ---------------------------------------------------------------------------
353// Multiply/Divide Instructions
354// ---------------------------------------------------------------------------
355
356void Spc700::MUL(uint8_t operand) {
357 // MUL - Multiply A * Y -> YA
358 uint16_t result = A * operand;
359 YA = result;
360 PSW.Z = (result == 0);
361 PSW.N = (result & 0x8000) != 0;
362}
363
364void Spc700::DIV(uint8_t operand) {
365 // DIV - Divide YA / X -> A (quotient), Y (remainder)
366 // Note: Hardware behavior is complex; simplified here
367 if (operand == 0) {
368 // Divide by zero - undefined behavior
369 // Real hardware has specific behavior, but we'll just return
370 return;
371 }
372 uint8_t quotient = A / operand;
373 uint8_t remainder = A % operand;
374 A = quotient;
375 Y = remainder;
376 PSW.Z = (quotient == 0);
377 PSW.N = (quotient & 0x80) != 0;
378}
379
380// ---------------------------------------------------------------------------
381// Branch Instructions
382// ---------------------------------------------------------------------------
383// Note: Branch timing is handled in DoBranch() in spc700.cc
384// These helpers are only used by old code paths
385
386void Spc700::BRA(int8_t offset) { PC += offset; }
387void Spc700::BEQ(int8_t offset) { if (PSW.Z) PC += offset; }
388void Spc700::BNE(int8_t offset) { if (!PSW.Z) PC += offset; }
389void Spc700::BCS(int8_t offset) { if (PSW.C) PC += offset; }
390void Spc700::BCC(int8_t offset) { if (!PSW.C) PC += offset; }
391void Spc700::BVS(int8_t offset) { if (PSW.V) PC += offset; }
392void Spc700::BVC(int8_t offset) { if (!PSW.V) PC += offset; }
393void Spc700::BMI(int8_t offset) { if (PSW.N) PC += offset; }
394void Spc700::BPL(int8_t offset) { if (!PSW.N) PC += offset; }
395
396void Spc700::BBS(uint8_t bit, uint8_t operand) {
397 if (operand & (1 << bit)) PC += rel();
398}
399
400void Spc700::BBC(uint8_t bit, uint8_t operand) {
401 if (!(operand & (1 << bit))) PC += rel();
402}
403
404// ---------------------------------------------------------------------------
405// Jump and Call Instructions
406// ---------------------------------------------------------------------------
407
408void Spc700::JMP(uint16_t address) {
409 PC = address;
410}
411
412void Spc700::CALL(uint16_t address) {
413 uint16_t return_address = PC + 2;
414 push_byte((return_address >> 8) & 0xFF);
415 push_byte(return_address & 0xFF);
416 PC = address;
417}
418
419void Spc700::PCALL(uint8_t offset) {
420 uint16_t return_address = PC + 2;
421 push_byte((return_address >> 8) & 0xFF);
422 push_byte(return_address & 0xFF);
423 PC += offset;
424}
425
426void Spc700::TCALL(uint8_t offset) {
427 uint16_t return_address = PC + 2;
428 push_byte((return_address >> 8) & 0xFF);
429 push_byte(return_address & 0xFF);
430 PC = 0xFFDE + offset;
431}
432
434 push_word(PC);
436 PSW.I = false;
437 PSW.B = true;
438 PC = read_word(0xFFDE);
439}
440
442 PC = pull_word();
443}
444
447 PC = pull_word();
448}
449
450// ---------------------------------------------------------------------------
451// Stack Instructions
452// ---------------------------------------------------------------------------
453
454void Spc700::PUSH(uint8_t operand) {
455 push_byte(operand);
456}
457
458void Spc700::POP(uint8_t& operand) {
459 operand = pull_byte();
460}
461
462// ---------------------------------------------------------------------------
463// Bit Manipulation Instructions
464// ---------------------------------------------------------------------------
465
466void Spc700::SET1(uint8_t bit, uint8_t& operand) {
467 operand |= (1 << bit);
468}
469
470void Spc700::CLR1(uint8_t bit, uint8_t& operand) {
471 operand &= ~(1 << bit);
472}
473
474void Spc700::TSET1(uint8_t bit, uint8_t& operand) {
475 PSW.C = (operand & (1 << bit)) != 0;
476 operand |= (1 << bit);
477}
478
479void Spc700::TCLR1(uint8_t bit, uint8_t& operand) {
480 PSW.C = (operand & (1 << bit)) != 0;
481 operand &= ~(1 << bit);
482}
483
484void Spc700::AND1(uint8_t bit, uint8_t& operand) {
485 operand &= (1 << bit);
486 PSW.Z = (operand == 0);
487 PSW.N = (operand & 0x80) != 0;
488}
489
490void Spc700::OR1(uint8_t bit, uint8_t& operand) {
491 operand |= (1 << bit);
492 PSW.Z = (operand == 0);
493 PSW.N = (operand & 0x80) != 0;
494}
495
496void Spc700::EOR1(uint8_t bit, uint8_t& operand) {
497 operand ^= (1 << bit);
498 PSW.Z = (operand == 0);
499 PSW.N = (operand & 0x80) != 0;
500}
501
502void Spc700::NOT1(uint8_t bit, uint8_t& operand) {
503 operand ^= (1 << bit);
504 PSW.Z = (operand == 0);
505 PSW.N = (operand & 0x80) != 0;
506}
507
508void Spc700::MOV1(uint8_t bit, uint8_t& operand) {
509 PSW.C = (operand & (1 << bit)) != 0;
510 operand |= (1 << bit);
511}
512
513// ---------------------------------------------------------------------------
514// Flag Instructions
515// ---------------------------------------------------------------------------
516
517void Spc700::CLRC() { PSW.C = false; }
518void Spc700::SETC() { PSW.C = true; }
519void Spc700::NOTC() { PSW.C = !PSW.C; }
520void Spc700::CLRV() { PSW.V = false; PSW.H = false; }
521void Spc700::CLRP() { PSW.P = false; }
522void Spc700::SETP() { PSW.P = true; }
523void Spc700::EI() { PSW.I = true; }
524void Spc700::DI() { PSW.I = false; }
525
526// ---------------------------------------------------------------------------
527// Special Instructions
528// ---------------------------------------------------------------------------
529
530void Spc700::NOP() {
531 // No operation - PC already advanced by ReadOpcode()
532}
533
535 // Sleep mode - handled in ExecuteInstructions
536}
537
539 // Stop mode - handled in ExecuteInstructions
540}
541
542} // namespace emu
543} // namespace yaze
void CMPM(uint16_t dst, uint8_t value)
void BEQ(int8_t offset)
void SUBW(uint16_t &dest, uint16_t operand)
void PCALL(uint8_t offset)
void JMP(uint16_t address)
void MOVS(uint16_t adr)
uint16_t pull_word()
Definition spc700.h:205
uint8_t read(uint16_t address)
Definition spc700.h:154
void MOVSX(uint16_t adr)
void NOT1(uint8_t bit, uint8_t &operand)
uint16_t adr
Definition spc700.h:80
void MOVX(uint16_t adr)
void AND1(uint8_t bit, uint8_t &operand)
void SBC(uint16_t adr)
void CMPY(uint16_t adr)
void ANDM(uint16_t dest, uint8_t operand)
void MOVW(uint16_t &dest, uint16_t operand)
void EORM(uint16_t dest, uint8_t operand)
void DEC(uint16_t operand)
void BCS(int8_t offset)
void DIV(uint8_t operand)
void MOV(uint16_t adr)
void EOR1(uint8_t bit, uint8_t &operand)
void BVS(int8_t offset)
void SBCM(uint16_t &dest, uint8_t operand)
void BVC(int8_t offset)
void LSR(uint16_t adr)
void SET1(uint8_t bit, uint8_t &operand)
void push_byte(uint8_t value)
Definition spc700.h:190
void CMPX(uint16_t adr)
void TCLR1(uint8_t bit, uint8_t &operand)
uint8_t FlagsToByte(Flags flags)
Definition spc700.h:121
void TCALL(uint8_t offset)
void CMP(uint16_t adr)
void ROL(uint16_t operand)
void MOV_ADDR(uint16_t address, uint8_t operand)
void write(uint16_t address, uint8_t value)
Definition spc700.h:186
void XCN(uint8_t operand, bool isImmediate=false)
void PUSH(uint8_t operand)
void INC(uint16_t adr)
void ADDW(uint16_t &dest, uint16_t operand)
void INCW(uint16_t &operand)
void BBC(uint8_t bit, uint8_t operand)
void BMI(int8_t offset)
void CALL(uint16_t address)
void BNE(int8_t offset)
void DECW(uint16_t &operand)
void push_word(uint16_t value)
Definition spc700.h:195
void ADC(uint16_t adr)
void EOR(uint16_t adr)
void ROR(uint16_t adr)
void BBS(uint8_t bit, uint8_t operand)
uint16_t read_word(uint16_t address)
Definition spc700.h:156
void POP(uint8_t &operand)
void ASL(uint16_t operand)
void BCC(int8_t offset)
void CLR1(uint8_t bit, uint8_t &operand)
uint16_t YA
Definition spc700.h:105
void OR(uint16_t adr)
void ADCM(uint16_t &dest, uint8_t operand)
void BRA(int8_t offset)
void BPL(int8_t offset)
void OR1(uint8_t bit, uint8_t &operand)
void CMPW(uint16_t operand)
uint16_t PC
Definition spc700.h:106
void AND(uint16_t adr)
uint8_t pull_byte()
Definition spc700.h:200
void MOV1(uint8_t bit, uint8_t &operand)
void TSET1(uint8_t bit, uint8_t &operand)
ApuCallbacks callbacks_
Definition spc700.h:71
void MOVSY(uint16_t adr)
void MOVY(uint16_t adr)
void ORM(uint16_t dest, uint8_t operand)
Flags ByteToFlags(uint8_t byte)
Definition spc700.h:126
void MUL(uint8_t operand)
Main namespace for the application.
std::function< void(bool)> idle
Definition spc700.h:55