alu.rsannotatedalu.rssource360 lines · 10.6 KB · raw
1use crate::superfx::gsu::instructions::{
2    MemoryType, clear_prefix_flags, read_register, write_register,
3};
4use crate::superfx::gsu::{GraphicsSupportUnit, MultiplierSpeed};
5use jgenesis_common::num::{GetBit, SignBit};
6
7pub(super) fn add(
8    opcode: u8,
9    memory_type: MemoryType,
10    gsu: &mut GraphicsSupportUnit,
11    rom: &[u8],
12    ram: &[u8],
13) -> u8 {
14    // ADD/ADC: Add / Add with carry
15    // ALT1 controls ADD vs. ADC
16    // ALT2 controls register operand vs. immediate operand
17    let operand =
18        if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) };
19
20    let existing_carry = if gsu.alt1 { u16::from(gsu.carry_flag) } else { 0 };
21
22    let source = read_register(gsu, gsu.sreg);
23    let (partial_sum, carry1) = source.overflowing_add(operand);
24    let (sum, carry2) = partial_sum.overflowing_add(existing_carry);
25    let carry = carry1 || carry2;
26
27    let bit_14_carry = (source & 0x7FFF) + (operand & 0x7FFF) + existing_carry >= 0x8000;
28    let overflow = bit_14_carry != carry;
29
30    gsu.zero_flag = sum == 0;
31    gsu.carry_flag = carry;
32    gsu.sign_flag = sum.sign_bit();
33    gsu.overflow_flag = overflow;
34
35    let cycles = write_register(gsu, gsu.dreg, sum, rom, ram);
36
37    clear_prefix_flags(gsu);
38    cycles + memory_type.access_cycles(gsu.clock_speed)
39}
40
41pub(super) fn sub(
42    opcode: u8,
43    memory_type: MemoryType,
44    gsu: &mut GraphicsSupportUnit,
45    rom: &[u8],
46    ram: &[u8],
47) -> u8 {
48    // SUB/SBC/CMP: Subtract / Subtract with carry / Compare
49    // ALT1 controls SUB vs. SBC/CMP
50    // For SUB, ALT2 controls register operand vs. immediate operand
51    // For SBC/CMP, ALT2 controls SBC vs. CMP
52    let operand = if !gsu.alt1 && gsu.alt2 {
53        u16::from(opcode & 0x0F)
54    } else {
55        read_register(gsu, opcode & 0x0F)
56    };
57
58    let sbc = gsu.alt1 && !gsu.alt2;
59    let existing_borrow = if sbc { u16::from(!gsu.carry_flag) } else { 0 };
60
61    let source = read_register(gsu, gsu.sreg);
62    let (partial_diff, borrow1) = source.overflowing_sub(operand);
63    let (difference, borrow2) = partial_diff.overflowing_sub(existing_borrow);
64    let borrow = borrow1 || borrow2;
65
66    let bit_14_borrow = source & 0x7FFF < (operand & 0x7FFF) + existing_borrow;
67    let overflow = bit_14_borrow != borrow;
68
69    gsu.zero_flag = difference == 0;
70    gsu.carry_flag = !borrow;
71    gsu.sign_flag = difference.sign_bit();
72    gsu.overflow_flag = overflow;
73
74    let mut cycles = 0;
75    if !(gsu.alt1 && gsu.alt2) {
76        // Only write if not CMP
77        cycles = write_register(gsu, gsu.dreg, difference, rom, ram);
78    }
79
80    clear_prefix_flags(gsu);
81    cycles + memory_type.access_cycles(gsu.clock_speed)
82}
83
84pub(super) fn fmult(
85    memory_type: MemoryType,
86    gsu: &mut GraphicsSupportUnit,
87    rom: &[u8],
88    ram: &[u8],
89) -> u8 {
90    // FMULT/LMULT: Fractional signed multiplication / Long signed multiplication
91    // ALT1 controls FMULT vs. LMULT
92    let source: i32 = (read_register(gsu, gsu.sreg) as i16).into();
93    let operand: i32 = (gsu.r[6] as i16).into();
94    let product = source * operand;
95    let high_word = (product >> 16) as u16;
96
97    if gsu.alt1 {
98        // LMULT: Write low word to R4
99        gsu.r[4] = product as u16;
100    }
101
102    let cycles = write_register(gsu, gsu.dreg, high_word, rom, ram);
103
104    gsu.zero_flag = high_word == 0;
105    gsu.carry_flag = product.bit(15);
106    gsu.sign_flag = high_word.sign_bit();
107
108    clear_prefix_flags(gsu);
109    cycles
110        + match (memory_type, gsu.multiplier_speed) {
111            (MemoryType::CodeCache, MultiplierSpeed::Standard) => 8,
112            (MemoryType::CodeCache, MultiplierSpeed::High) => 4,
113            (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::Standard) => 11,
114            (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::High) => 7,
115        }
116}
117
118pub(super) fn mult(
119    opcode: u8,
120    memory_type: MemoryType,
121    gsu: &mut GraphicsSupportUnit,
122    rom: &[u8],
123    ram: &[u8],
124) -> u8 {
125    // MULT/UMULT: Signed multiplication / Unsigned multiplication
126    // ALT1 controls MULT vs. UMULT
127    // ALT2 controls register operand vs. immediate operand
128    let operand =
129        if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) & 0xFF };
130
131    let source = read_register(gsu, gsu.sreg) & 0xFF;
132
133    let product = if gsu.alt1 {
134        // UMULT: Unsigned 8-bit x 8-bit -> 16-bit
135        source * operand
136    } else {
137        // MULT: Signed 8-bit x 8-bit -> 16-bit
138        (i16::from(source as i8) * i16::from(operand as i8)) as u16
139    };
140
141    let cycles = write_register(gsu, gsu.dreg, product, rom, ram);
142
143    gsu.zero_flag = product == 0;
144    gsu.sign_flag = product.sign_bit();
145
146    clear_prefix_flags(gsu);
147    // TODO ROM/RAM should take more cycles at 21.47 MHz?
148    cycles
149        + match (memory_type, gsu.multiplier_speed) {
150            (MemoryType::CodeCache, MultiplierSpeed::Standard) => 2,
151            (MemoryType::CodeCache, MultiplierSpeed::High) => 1,
152            (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::Standard) => 5,
153            (MemoryType::Rom | MemoryType::Ram, MultiplierSpeed::High) => 3,
154        }
155}
156
157pub(super) fn inc(
158    opcode: u8,
159    memory_type: MemoryType,
160    gsu: &mut GraphicsSupportUnit,
161    rom: &[u8],
162    ram: &[u8],
163) -> u8 {
164    // INC Rn: Increment register
165    let register = opcode & 0x0F;
166    let incremented = gsu.r[register as usize].wrapping_add(1);
167    let cycles = write_register(gsu, register, incremented, rom, ram);
168
169    gsu.zero_flag = incremented == 0;
170    gsu.sign_flag = incremented.sign_bit();
171
172    clear_prefix_flags(gsu);
173    cycles + memory_type.access_cycles(gsu.clock_speed)
174}
175
176pub(super) fn dec(
177    opcode: u8,
178    memory_type: MemoryType,
179    gsu: &mut GraphicsSupportUnit,
180    rom: &[u8],
181    ram: &[u8],
182) -> u8 {
183    // DEC Rn: Decrement register
184    let register = opcode & 0x0F;
185    let decremented = gsu.r[register as usize].wrapping_sub(1);
186    let cycles = write_register(gsu, register, decremented, rom, ram);
187
188    gsu.zero_flag = decremented == 0;
189    gsu.sign_flag = decremented.sign_bit();
190
191    clear_prefix_flags(gsu);
192    cycles + memory_type.access_cycles(gsu.clock_speed)
193}
194
195pub(super) fn and(
196    opcode: u8,
197    memory_type: MemoryType,
198    gsu: &mut GraphicsSupportUnit,
199    rom: &[u8],
200    ram: &[u8],
201) -> u8 {
202    // AND/BIC: And / And with complement
203    // ALT1 controls AND vs. BIC
204    // ALT2 controls register operand vs. immediate operand
205    let operand =
206        if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) };
207
208    let source = read_register(gsu, gsu.sreg);
209    let result = if gsu.alt1 { source & !operand } else { source & operand };
210
211    let cycles = write_register(gsu, gsu.dreg, result, rom, ram);
212
213    gsu.zero_flag = result == 0;
214    gsu.sign_flag = result.sign_bit();
215
216    clear_prefix_flags(gsu);
217    cycles + memory_type.access_cycles(gsu.clock_speed)
218}
219
220pub(super) fn or(
221    opcode: u8,
222    memory_type: MemoryType,
223    gsu: &mut GraphicsSupportUnit,
224    rom: &[u8],
225    ram: &[u8],
226) -> u8 {
227    // OR/XOR: Or / Exclusive or
228    // ALT1 controls OR vs. XOR
229    // ALT2 controls register operand vs. immediate operand
230    let operand =
231        if gsu.alt2 { u16::from(opcode & 0x0F) } else { read_register(gsu, opcode & 0x0F) };
232
233    let source = read_register(gsu, gsu.sreg);
234    let result = if gsu.alt1 { source ^ operand } else { source | operand };
235
236    let cycles = write_register(gsu, gsu.dreg, result, rom, ram);
237
238    gsu.zero_flag = result == 0;
239    gsu.sign_flag = result.sign_bit();
240
241    clear_prefix_flags(gsu);
242    cycles + memory_type.access_cycles(gsu.clock_speed)
243}
244
245pub(super) fn not(
246    memory_type: MemoryType,
247    gsu: &mut GraphicsSupportUnit,
248    rom: &[u8],
249    ram: &[u8],
250) -> u8 {
251    // NOT: Bitwise not
252    let source = read_register(gsu, gsu.sreg);
253    let inverted = !source;
254    let cycles = write_register(gsu, gsu.dreg, inverted, rom, ram);
255
256    gsu.zero_flag = inverted == 0;
257    gsu.sign_flag = inverted.sign_bit();
258
259    clear_prefix_flags(gsu);
260    cycles + memory_type.access_cycles(gsu.clock_speed)
261}
262
263pub(super) fn asr(
264    memory_type: MemoryType,
265    gsu: &mut GraphicsSupportUnit,
266    rom: &[u8],
267    ram: &[u8],
268) -> u8 {
269    // ASR/DIV2: Arithmetic shift right / Divide by 2
270    // ALT1 controls ASR vs. DIV2
271    let source = read_register(gsu, gsu.sreg);
272
273    // DIV2 is equivalent to ASR unless Sreg == -1, in which case DIV2 produces 0 and ASR produces -1
274    let shifted =
275        if gsu.alt1 && source == u16::MAX { 0 } else { (source >> 1) | (source & 0x8000) };
276
277    let cycles = write_register(gsu, gsu.dreg, shifted, rom, ram);
278
279    gsu.zero_flag = shifted == 0;
280    gsu.carry_flag = source.bit(0);
281    gsu.sign_flag = shifted.sign_bit();
282
283    clear_prefix_flags(gsu);
284    cycles + memory_type.access_cycles(gsu.clock_speed)
285}
286
287pub(super) fn lsr(
288    memory_type: MemoryType,
289    gsu: &mut GraphicsSupportUnit,
290    rom: &[u8],
291    ram: &[u8],
292) -> u8 {
293    // LSR: Logical shift right
294    let source = read_register(gsu, gsu.sreg);
295    let shifted = source >> 1;
296    let cycles = write_register(gsu, gsu.dreg, shifted, rom, ram);
297
298    gsu.zero_flag = shifted == 0;
299    gsu.carry_flag = source.bit(0);
300    gsu.sign_flag = false;
301
302    clear_prefix_flags(gsu);
303    cycles + memory_type.access_cycles(gsu.clock_speed)
304}
305
306pub(super) fn rol(
307    memory_type: MemoryType,
308    gsu: &mut GraphicsSupportUnit,
309    rom: &[u8],
310    ram: &[u8],
311) -> u8 {
312    // ROL: Rotate left
313    let source = read_register(gsu, gsu.sreg);
314    let rotated = (source << 1) | u16::from(gsu.carry_flag);
315    let cycles = write_register(gsu, gsu.dreg, rotated, rom, ram);
316
317    gsu.zero_flag = rotated == 0;
318    gsu.carry_flag = source.sign_bit();
319    gsu.sign_flag = rotated.sign_bit();
320
321    clear_prefix_flags(gsu);
322    cycles + memory_type.access_cycles(gsu.clock_speed)
323}
324
325pub(super) fn ror(
326    memory_type: MemoryType,
327    gsu: &mut GraphicsSupportUnit,
328    rom: &[u8],
329    ram: &[u8],
330) -> u8 {
331    // ROR: Rotate right
332    let source = read_register(gsu, gsu.sreg);
333    let rotated = (source >> 1) | (u16::from(gsu.carry_flag) << 15);
334    let cycles = write_register(gsu, gsu.dreg, rotated, rom, ram);
335
336    gsu.zero_flag = rotated == 0;
337    gsu.carry_flag = source.bit(0);
338    gsu.sign_flag = rotated.sign_bit();
339
340    clear_prefix_flags(gsu);
341    cycles + memory_type.access_cycles(gsu.clock_speed)
342}
343
344pub(super) fn sex(
345    memory_type: MemoryType,
346    gsu: &mut GraphicsSupportUnit,
347    rom: &[u8],
348    ram: &[u8],
349) -> u8 {
350    // SEX: Sign extend
351    let source = read_register(gsu, gsu.sreg);
352    let extended = (source as i8) as u16;
353    let cycles = write_register(gsu, gsu.dreg, extended, rom, ram);
354
355    gsu.zero_flag = extended == 0;
356    gsu.sign_flag = extended.sign_bit();
357
358    clear_prefix_flags(gsu);
359    cycles + memory_type.access_cycles(gsu.clock_speed)
360}