alu.rsannotatedalu.rssource563 lines · 16.4 KB · raw
1#[allow(clippy::wildcard_imports)]
2use super::*;
3use crate::StatusRegister;
4use jgenesis_common::num::U16Ext;
5
6fn adc(operand_l: u8, operand_r: u8, psw: &mut StatusRegister) -> u8 {
7    let existing_carry: u8 = psw.carry.into();
8
9    let (partial_sum, carry1) = operand_l.overflowing_add(operand_r);
10    let (sum, carry2) = partial_sum.overflowing_add(existing_carry);
11    let carry = carry1 || carry2;
12
13    let bit_6_carry = (operand_l & 0x7F) + (operand_r & 0x7F) + existing_carry >= 0x80;
14    let overflow = bit_6_carry != carry;
15
16    let half_carry = (operand_l & 0x0F) + (operand_r & 0x0F) + existing_carry >= 0x10;
17
18    psw.zero = sum == 0;
19    psw.negative = sum.sign_bit();
20    psw.carry = carry;
21    psw.half_carry = half_carry;
22    psw.overflow = overflow;
23
24    sum
25}
26
27impl_read_op!(
28    [
29        immediate -> adc_imm,
30        x_indirect -> adc_xind,
31        direct_page -> adc_dp,
32        direct_page_x -> adc_dpx,
33        absolute -> adc_abs,
34        absolute_x -> adc_absx,
35        absolute_y -> adc_absy,
36        indirect_x -> adc_indx,
37        indirect_y -> adc_indy,
38    ]
39    (|registers, value| {
40        registers.a = adc(registers.a, value, &mut registers.psw);
41    })
42);
43
44impl_memory_to_memory_op!(
45    [
46        x_y -> adc_x_y_ind,
47        dp_dp -> adc_dp_dp,
48        dp_imm -> adc_dp_imm,
49    ]
50    (|registers, operand_l, operand_r| {
51        adc(operand_l, operand_r, &mut registers.psw)
52    })
53);
54
55fn sbc(operand_l: u8, operand_r: u8, psw: &mut StatusRegister) -> u8 {
56    let existing_borrow: u8 = (!psw.carry).into();
57
58    let (partial_diff, borrow1) = operand_l.overflowing_sub(operand_r);
59    let (difference, borrow2) = partial_diff.overflowing_sub(existing_borrow);
60    let borrow = borrow1 || borrow2;
61
62    let bit_6_borrow = operand_l & 0x7F < (operand_r & 0x7F) + existing_borrow;
63    let overflow = bit_6_borrow != borrow;
64
65    let half_borrow = operand_l & 0x0F < (operand_r & 0x0F) + existing_borrow;
66
67    psw.zero = difference == 0;
68    psw.negative = difference.sign_bit();
69    psw.carry = !borrow;
70    psw.half_carry = !half_borrow;
71    psw.overflow = overflow;
72
73    difference
74}
75
76impl_read_op!(
77    [
78        immediate -> sbc_imm,
79        x_indirect -> sbc_xind,
80        direct_page -> sbc_dp,
81        direct_page_x -> sbc_dpx,
82        absolute -> sbc_abs,
83        absolute_x -> sbc_absx,
84        absolute_y -> sbc_absy,
85        indirect_x -> sbc_indx,
86        indirect_y -> sbc_indy,
87    ]
88    (|registers, value| {
89        registers.a = sbc(registers.a, value, &mut registers.psw);
90    })
91);
92
93impl_memory_to_memory_op!(
94    [
95        x_y -> sbc_x_y_ind,
96        dp_dp -> sbc_dp_dp,
97        dp_imm -> sbc_dp_imm,
98    ]
99    (|registers, operand_l, operand_r| {
100        sbc(operand_l, operand_r, &mut registers.psw)
101    })
102);
103
104fn cmp(operand_l: u8, operand_r: u8, psw: &mut StatusRegister) {
105    let (difference, borrow) = operand_l.overflowing_sub(operand_r);
106    psw.zero = difference == 0;
107    psw.negative = difference.sign_bit();
108    psw.carry = !borrow;
109}
110
111impl_read_op!(
112    [
113        immediate -> cmp_a_imm,
114        x_indirect -> cmp_a_xind,
115        direct_page -> cmp_a_dp,
116        direct_page_x -> cmp_a_dpx,
117        absolute -> cmp_a_abs,
118        absolute_x -> cmp_a_absx,
119        absolute_y -> cmp_a_absy,
120        indirect_x -> cmp_a_indx,
121        indirect_y -> cmp_a_indy,
122    ]
123    (|registers, value| {
124        cmp(registers.a, value, &mut registers.psw);
125    })
126);
127
128impl_memory_to_memory_op!(
129    [
130        x_y_no_write -> cmp_x_y_ind,
131        dp_dp_no_write -> cmp_dp_dp,
132        dp_imm_no_write -> cmp_dp_imm,
133    ]
134    (|registers, operand_l, operand_r| {
135        cmp(operand_l, operand_r, &mut registers.psw);
136        operand_l
137    })
138);
139
140impl_read_op!(
141    [
142        immediate -> cmp_x_imm,
143        direct_page -> cmp_x_dp,
144        absolute -> cmp_x_abs,
145    ]
146    (|registers, value| {
147        cmp(registers.x, value, &mut registers.psw);
148    })
149);
150
151impl_read_op!(
152    [
153        immediate -> cmp_y_imm,
154        direct_page -> cmp_y_dp,
155        absolute -> cmp_y_abs,
156    ]
157    (|registers, value| {
158        cmp(registers.y, value, &mut registers.psw);
159    })
160);
161
162impl_read_op!(
163    [
164        immediate -> and_imm,
165        x_indirect -> and_xind,
166        direct_page -> and_dp,
167        direct_page_x -> and_dpx,
168        absolute -> and_abs,
169        absolute_x -> and_absx,
170        absolute_y -> and_absy,
171        indirect_x -> and_indx,
172        indirect_y -> and_indy,
173    ]
174    (|registers, value| {
175        registers.a &= value;
176        registers.psw.zero = registers.a == 0;
177        registers.psw.negative = registers.a.sign_bit();
178    })
179);
180
181impl_memory_to_memory_op!(
182    [
183        x_y -> and_x_y_ind,
184        dp_dp -> and_dp_dp,
185        dp_imm -> and_dp_imm,
186    ]
187    (|registers, operand_l, operand_r| {
188        let result = operand_l & operand_r;
189        registers.psw.zero = result == 0;
190        registers.psw.negative = result.sign_bit();
191        result
192    })
193);
194
195impl_read_op!(
196    [
197        immediate -> or_imm,
198        x_indirect -> or_xind,
199        direct_page -> or_dp,
200        direct_page_x -> or_dpx,
201        absolute -> or_abs,
202        absolute_x -> or_absx,
203        absolute_y -> or_absy,
204        indirect_x -> or_indx,
205        indirect_y -> or_indy,
206    ]
207    (|registers, value| {
208        registers.a |= value;
209        registers.psw.zero = registers.a == 0;
210        registers.psw.negative = registers.a.sign_bit();
211    })
212);
213
214impl_memory_to_memory_op!(
215    [
216        x_y -> or_x_y_ind,
217        dp_dp -> or_dp_dp,
218        dp_imm -> or_dp_imm,
219    ]
220    (|registers, operand_l, operand_r| {
221        let result = operand_l | operand_r;
222        registers.psw.zero = result == 0;
223        registers.psw.negative = result.sign_bit();
224        result
225    })
226);
227
228impl_read_op!(
229    [
230        immediate -> eor_imm,
231        x_indirect -> eor_xind,
232        direct_page -> eor_dp,
233        direct_page_x -> eor_dpx,
234        absolute -> eor_abs,
235        absolute_x -> eor_absx,
236        absolute_y -> eor_absy,
237        indirect_x -> eor_indx,
238        indirect_y -> eor_indy,
239    ]
240    (|registers, value| {
241        registers.a ^= value;
242        registers.psw.zero = registers.a == 0;
243        registers.psw.negative = registers.a.sign_bit();
244    })
245);
246
247impl_memory_to_memory_op!(
248    [
249        x_y -> eor_x_y_ind,
250        dp_dp -> eor_dp_dp,
251        dp_imm -> eor_dp_imm,
252    ]
253    (|registers, operand_l, operand_r| {
254        let result = operand_l ^ operand_r;
255        registers.psw.zero = result == 0;
256        registers.psw.negative = result.sign_bit();
257        result
258    })
259);
260
261impl_modify_op!(
262    [
263        accumulator -> inc_a,
264        direct_page -> inc_dp,
265        direct_page_x -> inc_dpx,
266        absolute -> inc_abs,
267    ]
268    (|registers, value| {
269        let result = value.wrapping_add(1);
270        registers.psw.zero = result == 0;
271        registers.psw.negative = result.sign_bit();
272        result
273    })
274);
275
276impl_modify_op!(
277    [
278        accumulator -> dec_a,
279        direct_page -> dec_dp,
280        direct_page_x -> dec_dpx,
281        absolute -> dec_abs,
282    ]
283    (|registers, value| {
284        let result = value.wrapping_sub(1);
285        registers.psw.zero = result == 0;
286        registers.psw.negative = result.sign_bit();
287        result
288    })
289);
290
291macro_rules! impl_register_increment_op {
292    ($name:ident, $register:ident, $inc_method:ident) => {
293        impl_registers_op!($name, |registers| {
294            let value = registers.$register.$inc_method(1);
295            registers.$register = value;
296
297            registers.psw.zero = value == 0;
298            registers.psw.negative = value.sign_bit();
299        });
300    };
301}
302
303impl_register_increment_op!(inc_x, x, wrapping_add);
304impl_register_increment_op!(inc_y, y, wrapping_add);
305impl_register_increment_op!(dec_x, x, wrapping_sub);
306impl_register_increment_op!(dec_y, y, wrapping_sub);
307
308pub(crate) fn addw<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
309    match cpu.state.cycle {
310        1 => {
311            cpu.state.t0 = fetch_operand(cpu, bus);
312        }
313        2 => {
314            let address = u16::from_le_bytes([cpu.state.t0, cpu.direct_page_msb()]);
315            cpu.state.t1 = bus.read(address);
316        }
317        3 => {
318            bus.idle();
319        }
320        4 => {
321            cpu.final_cycle();
322
323            let address_lsb = cpu.state.t0.wrapping_add(1);
324            let address = u16::from_le_bytes([address_lsb, cpu.direct_page_msb()]);
325            let operand_msb = bus.read(address);
326            let operand = u16::from_le_bytes([cpu.state.t1, operand_msb]);
327
328            let ya = cpu.registers.ya();
329            let (sum, carry) = ya.overflowing_add(operand);
330
331            let bit_14_carry = (ya & 0x7FFF) + (operand & 0x7FFF) >= 0x8000;
332            let overflow = bit_14_carry != carry;
333
334            let half_carry = (ya & 0x0FFF) + (operand & 0x0FFF) >= 0x1000;
335
336            cpu.registers.set_ya(sum);
337            cpu.registers.psw.zero = sum == 0;
338            cpu.registers.psw.negative = sum.sign_bit();
339            cpu.registers.psw.carry = carry;
340            cpu.registers.psw.half_carry = half_carry;
341            cpu.registers.psw.overflow = overflow;
342        }
343        _ => invalid_cycle!(cpu),
344    }
345}
346
347pub(crate) fn subw<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
348    match cpu.state.cycle {
349        1 => {
350            cpu.state.t0 = fetch_operand(cpu, bus);
351        }
352        2 => {
353            let address = u16::from_le_bytes([cpu.state.t0, cpu.direct_page_msb()]);
354            cpu.state.t1 = bus.read(address);
355        }
356        3 => {
357            bus.idle();
358        }
359        4 => {
360            cpu.final_cycle();
361
362            let address_lsb = cpu.state.t0.wrapping_add(1);
363            let address = u16::from_le_bytes([address_lsb, cpu.direct_page_msb()]);
364            let operand_msb = bus.read(address);
365            let operand = u16::from_le_bytes([cpu.state.t1, operand_msb]);
366
367            let ya = cpu.registers.ya();
368            let (difference, borrow) = ya.overflowing_sub(operand);
369
370            let bit_14_borrow = ya & 0x7FFF < operand & 0x7FFF;
371            let overflow = bit_14_borrow != borrow;
372
373            let half_borrow = ya & 0x0FFF < operand & 0x0FFF;
374
375            cpu.registers.set_ya(difference);
376            cpu.registers.psw.zero = difference == 0;
377            cpu.registers.psw.negative = difference.sign_bit();
378            cpu.registers.psw.carry = !borrow;
379            cpu.registers.psw.half_carry = !half_borrow;
380            cpu.registers.psw.overflow = overflow;
381        }
382        _ => invalid_cycle!(cpu),
383    }
384}
385
386pub(crate) fn cmpw<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
387    match cpu.state.cycle {
388        1 => {
389            cpu.state.t0 = fetch_operand(cpu, bus);
390        }
391        2 => {
392            let address = u16::from_le_bytes([cpu.state.t0, cpu.direct_page_msb()]);
393            cpu.state.t1 = bus.read(address);
394        }
395        3 => {
396            cpu.final_cycle();
397
398            let address_lsb = cpu.state.t0.wrapping_add(1);
399            let address = u16::from_le_bytes([address_lsb, cpu.direct_page_msb()]);
400            let operand_msb = bus.read(address);
401            let operand = u16::from_le_bytes([cpu.state.t1, operand_msb]);
402
403            let ya = cpu.registers.ya();
404            let (difference, borrow) = ya.overflowing_sub(operand);
405
406            cpu.registers.psw.zero = difference == 0;
407            cpu.registers.psw.negative = difference.sign_bit();
408            cpu.registers.psw.carry = !borrow;
409        }
410        _ => invalid_cycle!(cpu),
411    }
412}
413
414macro_rules! impl_16bit_increment {
415    ($name:ident, $inc_method:ident) => {
416        pub(crate) fn $name<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
417            match cpu.state.cycle {
418                1 => {
419                    cpu.state.t0 = fetch_operand(cpu, bus);
420                }
421                2 => {
422                    let address = u16::from_le_bytes([cpu.state.t0, cpu.direct_page_msb()]);
423                    cpu.state.t1 = bus.read(address);
424                }
425                3 => {
426                    let address = u16::from_le_bytes([cpu.state.t0, cpu.direct_page_msb()]);
427                    bus.write(address, cpu.state.t1.$inc_method(1));
428                }
429                4 => {
430                    let address_lsb = cpu.state.t0.wrapping_add(1);
431                    let address = u16::from_le_bytes([address_lsb, cpu.direct_page_msb()]);
432                    cpu.state.t2 = bus.read(address);
433                }
434                5 => {
435                    cpu.final_cycle();
436
437                    let value = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]).$inc_method(1);
438
439                    let address_lsb = cpu.state.t0.wrapping_add(1);
440                    let address = u16::from_le_bytes([address_lsb, cpu.direct_page_msb()]);
441                    bus.write(address, value.msb());
442
443                    cpu.registers.psw.zero = value == 0;
444                    cpu.registers.psw.negative = value.sign_bit();
445                }
446                _ => invalid_cycle!(cpu),
447            }
448        }
449    };
450}
451
452impl_16bit_increment!(incw, wrapping_add);
453impl_16bit_increment!(decw, wrapping_sub);
454
455pub(crate) fn mul<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
456    // MUL takes 9 cycles, but every cycle is a bus idle cycle except for the opcode fetch
457    match cpu.state.cycle {
458        1..=7 => {
459            bus.idle();
460        }
461        8 => {
462            cpu.final_cycle();
463            bus.idle();
464
465            let result = u16::from(cpu.registers.y) * u16::from(cpu.registers.a);
466            cpu.registers.set_ya(result);
467
468            // Z flag is set based on the MSB (Y), not the full 16-bit result
469            cpu.registers.psw.zero = cpu.registers.y == 0;
470            cpu.registers.psw.negative = cpu.registers.y.sign_bit();
471        }
472        _ => invalid_cycle!(cpu),
473    }
474}
475
476pub(crate) fn div<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
477    // DIV takes 12 cycles, but every cycle is a bus idle cycle except for the opcode fetch
478    match cpu.state.cycle {
479        1..=10 => {
480            bus.idle();
481        }
482        11 => {
483            cpu.final_cycle();
484            bus.idle();
485
486            // Algorithm from https://board.zsnes.com/phpBB3/viewtopic.php?t=3753
487
488            cpu.registers.psw.overflow = cpu.registers.y >= cpu.registers.x;
489            cpu.registers.psw.half_carry = cpu.registers.y & 0x0F >= cpu.registers.x & 0x0F;
490
491            let x: u16 = cpu.registers.x.into();
492            let y: u16 = cpu.registers.y.into();
493            let ya = cpu.registers.ya();
494            if y < 2 * x {
495                // Normal division
496                cpu.registers.a = (ya / x) as u8;
497                cpu.registers.y = (ya % x) as u8;
498            } else {
499                // Overflow (including divide by zero)
500                cpu.registers.a = (255 - (ya - x * 0x200) / (256 - x)) as u8;
501                cpu.registers.y = (x + (ya - x * 0x200) % (256 - x)) as u8;
502            }
503
504            cpu.registers.psw.zero = cpu.registers.a == 0;
505            cpu.registers.psw.negative = cpu.registers.a.sign_bit();
506        }
507        _ => invalid_cycle!(cpu),
508    }
509}
510
511pub(crate) fn daa<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
512    match cpu.state.cycle {
513        1 => {
514            bus.idle();
515        }
516        2 => {
517            cpu.final_cycle();
518            bus.idle();
519
520            // Algorithm from https://board.zsnes.com/phpBB3/viewtopic.php?t=3753
521
522            if cpu.registers.psw.carry || cpu.registers.a > 0x99 {
523                cpu.registers.psw.carry = true;
524                cpu.registers.a = cpu.registers.a.wrapping_add(0x60);
525            }
526
527            if cpu.registers.psw.half_carry || (cpu.registers.a & 0x0F) > 0x09 {
528                cpu.registers.a = cpu.registers.a.wrapping_add(0x06);
529            }
530
531            cpu.registers.psw.zero = cpu.registers.a == 0;
532            cpu.registers.psw.negative = cpu.registers.a.sign_bit();
533        }
534        _ => invalid_cycle!(cpu),
535    }
536}
537
538pub(crate) fn das<B: BusInterface>(cpu: &mut Spc700, bus: &mut B) {
539    match cpu.state.cycle {
540        1 => {
541            bus.idle();
542        }
543        2 => {
544            cpu.final_cycle();
545            bus.idle();
546
547            // Algorithm from https://board.zsnes.com/phpBB3/viewtopic.php?t=3753
548
549            if !cpu.registers.psw.carry || cpu.registers.a > 0x99 {
550                cpu.registers.psw.carry = false;
551                cpu.registers.a = cpu.registers.a.wrapping_sub(0x60);
552            }
553
554            if !cpu.registers.psw.half_carry || (cpu.registers.a & 0x0F) > 0x09 {
555                cpu.registers.a = cpu.registers.a.wrapping_sub(0x06);
556            }
557
558            cpu.registers.psw.zero = cpu.registers.a == 0;
559            cpu.registers.psw.negative = cpu.registers.a.sign_bit();
560        }
561        _ => invalid_cycle!(cpu),
562    }
563}