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)) && ((value & 0x80) != (result & 0x80));
103 PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
104 PSW.C = result > 0xff;
105 A = result & 0xFF;
106 PSW.Z = (A == 0);
107 PSW.N = (A & 0x80) != 0;
108}
109
110void Spc700::SBCM(uint16_t& dest, uint8_t operand) {
111 // SBC (dest), operand - Subtract with carry from memory
112 operand ^= 0xff;
113 uint8_t applyOn = read(dest);
114 int result = applyOn + operand + PSW.C;
115 PSW.V = ((applyOn & 0x80) == (operand & 0x80)) &&
116 ((operand & 0x80) != (result & 0x80));
117 PSW.H = ((applyOn & 0xF) + (operand & 0xF) + PSW.C) > 0xF;
118 PSW.C = result > 0xFF;
119 write(dest, result & 0xFF);
120 PSW.Z = ((result & 0xFF) == 0);
121 PSW.N = (result & 0x80) != 0;
122}
123
124// ---------------------------------------------------------------------------
125// Comparison Instructions (CMP, CMPX, CMPY, CMPM)
126// ---------------------------------------------------------------------------
127
128void Spc700::CMP(uint16_t adr) {
129 // CMP A, (adr) - Compare A with memory
130 uint8_t value = read(adr) ^ 0xff;
131 int result = A + value + 1;
132 PSW.C = result > 0xff;
133 PSW.Z = ((result & 0xFF) == 0);
134 PSW.N = (result & 0x80) != 0;
135}
136
137void Spc700::CMPX(uint16_t adr) {
138 // CMP X, (adr) - Compare X with memory
139 uint8_t value = read(adr) ^ 0xff;
140 int result = X + value + 1;
141 PSW.C = result > 0xff;
142 PSW.Z = ((result & 0xFF) == 0);
143 PSW.N = (result & 0x80) != 0;
144}
145
146void Spc700::CMPY(uint16_t adr) {
147 // CMP Y, (adr) - Compare Y with memory
148 uint8_t value = read(adr) ^ 0xff;
149 int result = Y + value + 1;
150 PSW.C = result > 0xff;
151 PSW.Z = ((result & 0xFF) == 0);
152 PSW.N = (result & 0x80) != 0;
153}
154
155void Spc700::CMPM(uint16_t dst, uint8_t value) {
156 // CMP (dst), value - Compare memory with value
157 value ^= 0xff;
158 int result = read(dst) + value + 1;
159 PSW.C = result > 0xff;
160 callbacks_.idle(false); // Extra cycle for memory comparison
161 PSW.Z = ((result & 0xFF) == 0);
162 PSW.N = (result & 0x80) != 0;
163}
164
165// ---------------------------------------------------------------------------
166// Logical Instructions (AND, OR, EOR)
167// ---------------------------------------------------------------------------
168
169void Spc700::AND(uint16_t adr) {
170 // AND A, (adr) - Logical AND with memory
171 A &= read(adr);
172 PSW.Z = (A == 0);
173 PSW.N = (A & 0x80) != 0;
174}
175
176void Spc700::ANDM(uint16_t dest, uint8_t operand) {
177 // AND (dest), operand - Logical AND memory with value
178 uint8_t result = read(dest) & operand;
179 write(dest, result);
180 PSW.Z = (result == 0);
181 PSW.N = (result & 0x80) != 0;
182}
183
184void Spc700::OR(uint16_t adr) {
185 // OR A, (adr) - Logical OR with memory
186 A |= read(adr);
187 PSW.Z = (A == 0);
188 PSW.N = (A & 0x80) != 0;
189}
190
191void Spc700::ORM(uint16_t dst, uint8_t value) {
192 // OR (dst), value - Logical OR memory with value
193 uint8_t result = read(dst) | value;
194 write(dst, result);
195 PSW.Z = (result == 0);
196 PSW.N = (result & 0x80) != 0;
197}
198
199void Spc700::EOR(uint16_t adr) {
200 // EOR A, (adr) - Logical XOR with memory
201 A ^= read(adr);
202 PSW.Z = (A == 0);
203 PSW.N = (A & 0x80) != 0;
204}
205
206void Spc700::EORM(uint16_t dest, uint8_t operand) {
207 // EOR (dest), operand - Logical XOR memory with value
208 uint8_t result = read(dest) ^ operand;
209 write(dest, result);
210 PSW.Z = (result == 0);
211 PSW.N = (result & 0x80) != 0;
212}
213
214// ---------------------------------------------------------------------------
215// Shift and Rotate Instructions (ASL, LSR, ROL, ROR)
216// ---------------------------------------------------------------------------
217
218void Spc700::ASL(uint16_t adr) {
219 // ASL (adr) - Arithmetic shift left
220 uint8_t val = read(adr);
221 write(adr, val); // Dummy write (RMW instruction)
222 PSW.C = (val & 0x80) != 0;
223 val <<= 1;
224 write(adr, val); // Actual write
225 PSW.Z = (val == 0);
226 PSW.N = (val & 0x80) != 0;
227}
228
229void Spc700::LSR(uint16_t adr) {
230 // LSR (adr) - Logical shift right
231 uint8_t val = read(adr);
232 write(adr, val); // Dummy write (RMW instruction)
233 PSW.C = (val & 0x01) != 0;
234 val >>= 1;
235 write(adr, val); // Actual write
236 PSW.Z = (val == 0);
237 PSW.N = (val & 0x80) != 0;
238}
239
240void Spc700::ROL(uint16_t adr) {
241 // ROL (adr) - Rotate left through carry
242 uint8_t val = read(adr);
243 write(adr, val); // Dummy write (RMW instruction)
244 bool newC = (val & 0x80) != 0;
245 val = (val << 1) | PSW.C;
246 PSW.C = newC;
247 write(adr, val); // Actual write
248 PSW.Z = (val == 0);
249 PSW.N = (val & 0x80) != 0;
250}
251
252void Spc700::ROR(uint16_t adr) {
253 // ROR (adr) - Rotate right through carry
254 uint8_t val = read(adr);
255 write(adr, val); // Dummy write (RMW instruction)
256 bool newC = (val & 1) != 0;
257 val = (val >> 1) | (PSW.C << 7);
258 PSW.C = newC;
259 write(adr, val); // Actual write
260 PSW.Z = (val == 0);
261 PSW.N = (val & 0x80) != 0;
262}
263
264// ---------------------------------------------------------------------------
265// Increment/Decrement Instructions (INC, DEC)
266// ---------------------------------------------------------------------------
267
268void Spc700::INC(uint16_t adr) {
269 // INC (adr) - Increment memory
270 uint8_t val = read(adr);
271 write(adr, val); // Dummy write (RMW instruction)
272 val++;
273 write(adr, val); // Actual write
274 PSW.Z = (val == 0);
275 PSW.N = (val & 0x80) != 0;
276}
277
278void Spc700::DEC(uint16_t adr) {
279 // DEC (adr) - Decrement memory
280 uint8_t val = read(adr);
281 write(adr, val); // Dummy write (RMW instruction)
282 val--;
283 write(adr, val); // Actual write
284 PSW.Z = (val == 0);
285 PSW.N = (val & 0x80) != 0;
286}
287
288void Spc700::XCN(uint8_t operand, bool isImmediate) {
289 // XCN - Exchange nibbles (note: this is only used for A register)
290 uint8_t value = isImmediate ? imm() : operand;
291 value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
292 PSW.Z = (value == 0);
293 PSW.N = (value & 0x80) != 0;
294}
295
296// ---------------------------------------------------------------------------
297// 16-bit Instructions (MOVW, INCW, DECW, ADDW, SUBW, CMPW)
298// ---------------------------------------------------------------------------
299
300void Spc700::MOVW(uint16_t& dest, uint16_t operand) {
301 // MOVW - Move 16-bit word
302 dest = operand;
303 PSW.Z = (operand == 0);
304 PSW.N = (operand & 0x8000) != 0;
305}
306
307void Spc700::INCW(uint16_t& operand) {
308 // INCW - Increment 16-bit word
309 operand++;
310 PSW.Z = (operand == 0);
311 PSW.N = (operand & 0x8000) != 0;
312}
313
314void Spc700::DECW(uint16_t& operand) {
315 // DECW - Decrement 16-bit word
316 operand--;
317 PSW.Z = (operand == 0);
318 PSW.N = (operand & 0x8000) != 0;
319}
320
321void Spc700::ADDW(uint16_t& dest, uint16_t operand) {
322 // ADDW - Add 16-bit word
323 uint32_t result = dest + operand;
324 PSW.C = (result > 0xFFFF);
325 PSW.Z = ((result & 0xFFFF) == 0);
326 PSW.N = (result & 0x8000) != 0;
327 PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000) != 0;
328 PSW.H = ((dest & 0xfff) + (operand & 0xfff)) > 0xfff;
329 dest = result & 0xFFFF;
330}
331
332void Spc700::SUBW(uint16_t& dest, uint16_t operand) {
333 // SUBW - Subtract 16-bit word
334 uint32_t result = dest - operand;
335 PSW.C = (result <= 0xFFFF);
336 PSW.Z = ((result & 0xFFFF) == 0);
337 PSW.N = (result & 0x8000) != 0;
338 PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000) != 0;
339 PSW.H = ((dest & 0xfff) - (operand & 0xfff)) >= 0;
340 dest = result & 0xFFFF;
341}
342
343void Spc700::CMPW(uint16_t operand) {
344 // CMPW - Compare 16-bit word with YA
345 uint32_t result = YA - operand;
346 PSW.C = (result <= 0xFFFF);
347 PSW.Z = ((result & 0xFFFF) == 0);
348 PSW.N = (result & 0x8000) != 0;
349}
350
351// ---------------------------------------------------------------------------
352// Multiply/Divide Instructions
353// ---------------------------------------------------------------------------
354
355void Spc700::MUL(uint8_t operand) {
356 // MUL - Multiply A * Y -> YA
357 uint16_t result = A * operand;
358 YA = result;
359 PSW.Z = (result == 0);
360 PSW.N = (result & 0x8000) != 0;
361}
362
363void Spc700::DIV(uint8_t operand) {
364 // DIV - Divide YA / X -> A (quotient), Y (remainder)
365 // Note: Hardware behavior is complex; simplified here
366 if (operand == 0) {
367 // Divide by zero - undefined behavior
368 // Real hardware has specific behavior, but we'll just return
369 return;
370 }
371 uint8_t quotient = A / operand;
372 uint8_t remainder = A % operand;
373 A = quotient;
374 Y = remainder;
375 PSW.Z = (quotient == 0);
376 PSW.N = (quotient & 0x80) != 0;
377}
378
379// ---------------------------------------------------------------------------
380// Branch Instructions
381// ---------------------------------------------------------------------------
382// Note: Branch timing is handled in DoBranch() in spc700.cc
383// These helpers are only used by old code paths
384
385void Spc700::BRA(int8_t offset) {
386 PC += offset;
387}
388void Spc700::BEQ(int8_t offset) {
389 if (PSW.Z)
390 PC += offset;
391}
392void Spc700::BNE(int8_t offset) {
393 if (!PSW.Z)
394 PC += offset;
395}
396void Spc700::BCS(int8_t offset) {
397 if (PSW.C)
398 PC += offset;
399}
400void Spc700::BCC(int8_t offset) {
401 if (!PSW.C)
402 PC += offset;
403}
404void Spc700::BVS(int8_t offset) {
405 if (PSW.V)
406 PC += offset;
407}
408void Spc700::BVC(int8_t offset) {
409 if (!PSW.V)
410 PC += offset;
411}
412void Spc700::BMI(int8_t offset) {
413 if (PSW.N)
414 PC += offset;
415}
416void Spc700::BPL(int8_t offset) {
417 if (!PSW.N)
418 PC += offset;
419}
420
421void Spc700::BBS(uint8_t bit, uint8_t operand) {
422 if (operand & (1 << bit))
423 PC += rel();
424}
425
426void Spc700::BBC(uint8_t bit, uint8_t operand) {
427 if (!(operand & (1 << bit)))
428 PC += rel();
429}
430
431// ---------------------------------------------------------------------------
432// Jump and Call Instructions
433// ---------------------------------------------------------------------------
434
435void Spc700::JMP(uint16_t address) {
436 PC = address;
437}
438
439void Spc700::CALL(uint16_t address) {
440 uint16_t return_address = PC + 2;
441 push_byte((return_address >> 8) & 0xFF);
442 push_byte(return_address & 0xFF);
443 PC = address;
444}
445
446void Spc700::PCALL(uint8_t offset) {
447 uint16_t return_address = PC + 2;
448 push_byte((return_address >> 8) & 0xFF);
449 push_byte(return_address & 0xFF);
450 PC += offset;
451}
452
453void Spc700::TCALL(uint8_t offset) {
454 uint16_t return_address = PC + 2;
455 push_byte((return_address >> 8) & 0xFF);
456 push_byte(return_address & 0xFF);
457 PC = 0xFFDE + offset;
458}
459
461 push_word(PC);
463 PSW.I = false;
464 PSW.B = true;
465 PC = read_word(0xFFDE);
466}
467
469 PC = pull_word();
470}
471
474 PC = pull_word();
475}
476
477// ---------------------------------------------------------------------------
478// Stack Instructions
479// ---------------------------------------------------------------------------
480
481void Spc700::PUSH(uint8_t operand) {
482 push_byte(operand);
483}
484
485void Spc700::POP(uint8_t& operand) {
486 operand = pull_byte();
487}
488
489// ---------------------------------------------------------------------------
490// Bit Manipulation Instructions
491// ---------------------------------------------------------------------------
492
493void Spc700::SET1(uint8_t bit, uint8_t& operand) {
494 operand |= (1 << bit);
495}
496
497void Spc700::CLR1(uint8_t bit, uint8_t& operand) {
498 operand &= ~(1 << bit);
499}
500
501void Spc700::TSET1(uint8_t bit, uint8_t& operand) {
502 PSW.C = (operand & (1 << bit)) != 0;
503 operand |= (1 << bit);
504}
505
506void Spc700::TCLR1(uint8_t bit, uint8_t& operand) {
507 PSW.C = (operand & (1 << bit)) != 0;
508 operand &= ~(1 << bit);
509}
510
511void Spc700::AND1(uint8_t bit, uint8_t& operand) {
512 operand &= (1 << bit);
513 PSW.Z = (operand == 0);
514 PSW.N = (operand & 0x80) != 0;
515}
516
517void Spc700::OR1(uint8_t bit, uint8_t& operand) {
518 operand |= (1 << bit);
519 PSW.Z = (operand == 0);
520 PSW.N = (operand & 0x80) != 0;
521}
522
523void Spc700::EOR1(uint8_t bit, uint8_t& operand) {
524 operand ^= (1 << bit);
525 PSW.Z = (operand == 0);
526 PSW.N = (operand & 0x80) != 0;
527}
528
529void Spc700::NOT1(uint8_t bit, uint8_t& operand) {
530 operand ^= (1 << bit);
531 PSW.Z = (operand == 0);
532 PSW.N = (operand & 0x80) != 0;
533}
534
535void Spc700::MOV1(uint8_t bit, uint8_t& operand) {
536 PSW.C = (operand & (1 << bit)) != 0;
537 operand |= (1 << bit);
538}
539
540// ---------------------------------------------------------------------------
541// Flag Instructions
542// ---------------------------------------------------------------------------
543
545 PSW.C = false;
546}
548 PSW.C = true;
549}
551 PSW.C = !PSW.C;
552}
554 PSW.V = false;
555 PSW.H = false;
556}
558 PSW.P = false;
559}
561 PSW.P = true;
562}
564 PSW.I = true;
565}
567 PSW.I = false;
568}
569
570// ---------------------------------------------------------------------------
571// Special Instructions
572// ---------------------------------------------------------------------------
573
575 // No operation - PC already advanced by ReadOpcode()
576}
577
579 // Sleep mode - handled in ExecuteInstructions
580}
581
583 // Stop mode - handled in ExecuteInstructions
584}
585
586} // namespace emu
587} // 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:208
uint8_t read(uint16_t address)
Definition spc700.h:157
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:193
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:189
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:198
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:159
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:203
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)
std::function< void(bool)> idle
Definition spc700.h:55