load.rsannotatedload.rssource440 lines · 13.2 KB · raw
1use crate::superfx::gsu::instructions::{
2    MemoryType, clear_prefix_flags, fetch_opcode, read_register, write_register,
3};
4use crate::superfx::gsu::{ClockSpeed, GraphicsSupportUnit};
5use jgenesis_common::num::{SignBit, U16Ext};
6
7pub(super) fn ldb(
8    opcode: u8,
9    memory_type: MemoryType,
10    gsu: &mut GraphicsSupportUnit,
11    rom: &[u8],
12    ram: &[u8],
13) -> u8 {
14    // LDB (Rm): Load byte from RAM
15    let m = opcode & 0x0F;
16    let ram_addr = (gsu.r[m as usize] as usize) & (ram.len() - 1);
17    let value = ram[ram_addr];
18
19    let cycles = write_register(gsu, gsu.dreg, value.into(), rom, ram);
20
21    gsu.state.ram_address_buffer = ram_addr as u16;
22
23    log::trace!("Loaded {value:02X} from RAM[{ram_addr:X}]");
24
25    clear_prefix_flags(gsu);
26    cycles
27        + match (gsu.clock_speed, memory_type) {
28            (ClockSpeed::Slow, MemoryType::CodeCache) => 5,
29            (ClockSpeed::Fast, MemoryType::CodeCache) => 7,
30            (ClockSpeed::Slow, MemoryType::Rom) => 8,
31            (ClockSpeed::Fast, MemoryType::Rom) => 9,
32            (ClockSpeed::Slow, MemoryType::Ram) => 10,
33            (ClockSpeed::Fast, MemoryType::Ram) => 11,
34        }
35}
36
37pub(super) fn ldw(
38    opcode: u8,
39    memory_type: MemoryType,
40    gsu: &mut GraphicsSupportUnit,
41    rom: &[u8],
42    ram: &[u8],
43) -> u8 {
44    // LDW (Rm): Load word from RAM
45    let m = opcode & 0x0F;
46    let ram_addr = (gsu.r[m as usize] as usize) & (ram.len() - 1);
47    let value_lsb = ram[ram_addr];
48    let value_msb = ram[ram_addr ^ 1];
49    let value = u16::from_le_bytes([value_lsb, value_msb]);
50
51    let cycles = write_register(gsu, gsu.dreg, value, rom, ram);
52
53    log::trace!("Loaded {value:04X} from RAM[{ram_addr:X}]");
54
55    gsu.state.ram_address_buffer = ram_addr as u16;
56
57    clear_prefix_flags(gsu);
58    cycles
59        + match (gsu.clock_speed, memory_type) {
60            (ClockSpeed::Slow, MemoryType::CodeCache) => 7,
61            (ClockSpeed::Fast, MemoryType::CodeCache) => 11,
62            (ClockSpeed::Slow, MemoryType::Rom) => 10,
63            (ClockSpeed::Fast, MemoryType::Rom) => 13,
64            (ClockSpeed::Slow, MemoryType::Ram) => 12,
65            (ClockSpeed::Fast, MemoryType::Ram) => 15,
66        }
67}
68
69pub(super) fn stb(
70    opcode: u8,
71    memory_type: MemoryType,
72    gsu: &mut GraphicsSupportUnit,
73    ram: &mut [u8],
74) -> u8 {
75    // STB (Rm): Store byte in RAM
76    let byte = read_register(gsu, gsu.sreg) as u8;
77
78    let register = opcode & 0x0F;
79    let ram_addr = (gsu.r[register as usize] as usize) & (ram.len() - 1);
80    ram[ram_addr] = byte;
81
82    log::trace!("Stored {byte:02X} at RAM[{ram_addr:X}]");
83
84    let cycles = gsu.state.ram_buffer_wait_cycles;
85    gsu.state.ram_buffer_wait_cycles = gsu.clock_speed.memory_access_cycles();
86    gsu.state.ram_buffer_written = true;
87
88    clear_prefix_flags(gsu);
89    cycles
90        + match memory_type {
91            MemoryType::CodeCache => 1,
92            MemoryType::Rom => 3,
93            MemoryType::Ram => 6,
94        }
95}
96
97pub(super) fn stw(
98    opcode: u8,
99    memory_type: MemoryType,
100    gsu: &mut GraphicsSupportUnit,
101    ram: &mut [u8],
102) -> u8 {
103    // STW (Rm): Store word in RAM
104    let source = read_register(gsu, gsu.sreg);
105    let [source_lsb, source_msb] = source.to_le_bytes();
106
107    let register = opcode & 0x0F;
108    let ram_addr = (gsu.r[register as usize] as usize) & (ram.len() - 1);
109    ram[ram_addr] = source_lsb;
110    ram[ram_addr ^ 1] = source_msb;
111
112    log::trace!("Stored {source:04X} at RAM[{ram_addr:X}]");
113
114    let cycles = gsu.state.ram_buffer_wait_cycles;
115    gsu.state.ram_buffer_wait_cycles = 2 * gsu.clock_speed.memory_access_cycles();
116    gsu.state.ram_buffer_written = true;
117
118    clear_prefix_flags(gsu);
119    match memory_type {
120        MemoryType::CodeCache => 1 + cycles.saturating_sub(1),
121        MemoryType::Rom | MemoryType::Ram => gsu.clock_speed.memory_access_cycles() + cycles,
122    }
123}
124
125pub(super) fn ibt(
126    opcode: u8,
127    memory_type: MemoryType,
128    gsu: &mut GraphicsSupportUnit,
129    rom: &[u8],
130    ram: &[u8],
131) -> u8 {
132    // IBT Rn, #pp: Load immediate byte (sign extended to 16 bits)
133    let pp = gsu.state.opcode_buffer;
134    fetch_opcode(gsu, rom, ram);
135
136    let register = opcode & 0xF;
137    let value = (pp as i8) as u16;
138    let write_cycles = write_register(gsu, register, value, rom, ram);
139
140    clear_prefix_flags(gsu);
141    write_cycles + 2 * memory_type.access_cycles(gsu.clock_speed)
142}
143
144pub(super) fn iwt(
145    opcode: u8,
146    memory_type: MemoryType,
147    gsu: &mut GraphicsSupportUnit,
148    rom: &[u8],
149    ram: &[u8],
150) -> u8 {
151    // IWT Rn, #xx: Load immediate word
152    let lsb = gsu.state.opcode_buffer;
153    fetch_opcode(gsu, rom, ram);
154    let msb = gsu.state.opcode_buffer;
155    fetch_opcode(gsu, rom, ram);
156
157    let value = u16::from_le_bytes([lsb, msb]);
158    let register = opcode & 0x0F;
159    let cycles = write_register(gsu, register, value, rom, ram);
160
161    clear_prefix_flags(gsu);
162    cycles + 3 * memory_type.access_cycles(gsu.clock_speed)
163}
164
165pub(super) fn lm(
166    opcode: u8,
167    memory_type: MemoryType,
168    gsu: &mut GraphicsSupportUnit,
169    rom: &[u8],
170    ram: &[u8],
171) -> u8 {
172    // LM Rn, (xx): Load from RAM
173    let ram_addr_lsb = gsu.state.opcode_buffer;
174    fetch_opcode(gsu, rom, ram);
175    let ram_addr_msb = gsu.state.opcode_buffer;
176    fetch_opcode(gsu, rom, ram);
177
178    let ram_addr = (u16::from_le_bytes([ram_addr_lsb, ram_addr_msb]) as usize) & (ram.len() - 1);
179    let value_lsb = ram[ram_addr];
180    let value_msb = ram[ram_addr ^ 1];
181    let value = u16::from_le_bytes([value_lsb, value_msb]);
182
183    log::trace!("Loaded {value:04X} from RAM[{ram_addr:X}");
184
185    let register = opcode & 0x0F;
186    let cycles = write_register(gsu, register, value, rom, ram);
187
188    gsu.state.ram_address_buffer = ram_addr as u16;
189
190    clear_prefix_flags(gsu);
191    cycles
192        + match (gsu.clock_speed, memory_type) {
193            (ClockSpeed::Slow, MemoryType::CodeCache) => 9,
194            (ClockSpeed::Fast, MemoryType::CodeCache) => 13,
195            (ClockSpeed::Slow, MemoryType::Rom) => 17,
196            (ClockSpeed::Fast, MemoryType::Rom) => 24,
197            (ClockSpeed::Slow, MemoryType::Ram) => 18,
198            (ClockSpeed::Fast, MemoryType::Ram) => 25,
199        }
200}
201
202pub(super) fn lms(
203    opcode: u8,
204    memory_type: MemoryType,
205    gsu: &mut GraphicsSupportUnit,
206    rom: &[u8],
207    ram: &[u8],
208) -> u8 {
209    // LMS Rn, (yy): Load from RAM short
210    let kk = gsu.state.opcode_buffer;
211    fetch_opcode(gsu, rom, ram);
212
213    let yy = u16::from(kk) << 1;
214    let ram_addr = (yy as usize) & (ram.len() - 1);
215    let lsb = ram[ram_addr];
216    let msb = ram[ram_addr ^ 1];
217    let value = u16::from_le_bytes([lsb, msb]);
218
219    let register = opcode & 0x0F;
220    let cycles = write_register(gsu, register, value, rom, ram);
221
222    log::trace!("Loaded {value:04X} from RAM[{ram_addr:X}]");
223
224    gsu.state.ram_address_buffer = ram_addr as u16;
225
226    clear_prefix_flags(gsu);
227    cycles
228        + match (gsu.clock_speed, memory_type) {
229            (ClockSpeed::Slow, MemoryType::CodeCache) => 8,
230            (ClockSpeed::Fast, MemoryType::CodeCache) => 12,
231            (ClockSpeed::Slow, MemoryType::Rom | MemoryType::Ram) => 15,
232            (ClockSpeed::Fast, MemoryType::Rom | MemoryType::Ram) => 20,
233        }
234}
235
236pub(super) fn sm(
237    opcode: u8,
238    memory_type: MemoryType,
239    gsu: &mut GraphicsSupportUnit,
240    rom: &[u8],
241    ram: &mut [u8],
242) -> u8 {
243    // SM (xx), Rn: Store in RAM
244    let ram_addr_lsb = gsu.state.opcode_buffer;
245    fetch_opcode(gsu, rom, ram);
246    let ram_addr_msb = gsu.state.opcode_buffer;
247    fetch_opcode(gsu, rom, ram);
248
249    let ram_addr = (u16::from_le_bytes([ram_addr_lsb, ram_addr_msb]) as usize) & (ram.len() - 1);
250
251    let register = opcode & 0x0F;
252    let [value_lsb, value_msb] = read_register(gsu, register).to_le_bytes();
253    ram[ram_addr] = value_lsb;
254    ram[ram_addr ^ 1] = value_msb;
255
256    log::trace!("Stored {value_msb:02X}{value_lsb:02X} at RAM[{ram_addr:X}]");
257
258    let cycles = gsu.state.ram_buffer_wait_cycles;
259    gsu.state.ram_buffer_wait_cycles = 2 * gsu.clock_speed.memory_access_cycles();
260    gsu.state.ram_buffer_written = true;
261
262    clear_prefix_flags(gsu);
263    cycles
264        + match memory_type {
265            MemoryType::CodeCache => 2,
266            MemoryType::Rom => 9,
267            MemoryType::Ram => 15,
268        }
269}
270
271pub(super) fn sms(
272    opcode: u8,
273    memory_type: MemoryType,
274    gsu: &mut GraphicsSupportUnit,
275    rom: &[u8],
276    ram: &mut [u8],
277) -> u8 {
278    // SMS (yy), Rn: Store in RAM short
279    let kk = gsu.state.opcode_buffer;
280    fetch_opcode(gsu, rom, ram);
281
282    let yy = u16::from(kk) << 1;
283    let ram_addr = (yy as usize) & (ram.len() - 1);
284
285    let register = opcode & 0x0F;
286    let [lsb, msb] = read_register(gsu, register).to_le_bytes();
287    ram[ram_addr] = lsb;
288    ram[ram_addr ^ 1] = msb;
289
290    log::trace!("Stored {msb:02X}{lsb:02X} at RAM[{ram_addr:X}]");
291
292    let cycles = gsu.state.ram_buffer_wait_cycles;
293    gsu.state.ram_buffer_wait_cycles = 2 * gsu.clock_speed.memory_access_cycles();
294    gsu.state.ram_buffer_written = true;
295
296    clear_prefix_flags(gsu);
297    cycles
298        + match memory_type {
299            MemoryType::CodeCache => 1,
300            MemoryType::Rom => 6,
301            MemoryType::Ram => 10,
302        }
303}
304
305pub(super) fn sbk(memory_type: MemoryType, gsu: &mut GraphicsSupportUnit, ram: &mut [u8]) -> u8 {
306    // SBK: Store word in RAM at last address used
307    let [source_lsb, source_msb] = read_register(gsu, gsu.sreg).to_le_bytes();
308    let ram_addr = gsu.state.ram_address_buffer;
309    ram[ram_addr as usize] = source_lsb;
310    ram[(ram_addr ^ 1) as usize] = source_msb;
311
312    log::trace!("Stored {source_msb:02X}{source_lsb:02X} at RAM[{ram_addr:X}]");
313
314    let cycles = gsu.state.ram_buffer_wait_cycles;
315    gsu.state.ram_buffer_wait_cycles = 2 * gsu.clock_speed.memory_access_cycles();
316    gsu.state.ram_buffer_written = true;
317
318    clear_prefix_flags(gsu);
319    match memory_type {
320        MemoryType::CodeCache => 1 + cycles.saturating_sub(1),
321        MemoryType::Rom | MemoryType::Ram => gsu.clock_speed.memory_access_cycles() + cycles,
322    }
323}
324
325pub(super) fn romb(memory_type: MemoryType, gsu: &mut GraphicsSupportUnit) -> u8 {
326    // ROMB: Set ROM bank register
327    gsu.rombr = read_register(gsu, gsu.sreg) as u8;
328
329    log::trace!("  Set ROMBR to {:02X}", gsu.rombr);
330
331    let cycles = gsu.state.rom_buffer_wait_cycles;
332    gsu.state.rom_buffer_wait_cycles = 0;
333
334    clear_prefix_flags(gsu);
335    cycles + memory_type.access_cycles(gsu.clock_speed)
336}
337
338pub(super) fn getb(
339    memory_type: MemoryType,
340    gsu: &mut GraphicsSupportUnit,
341    rom: &[u8],
342    ram: &[u8],
343) -> u8 {
344    // GETB/GETBH/GETBL/GETBS: Get byte from ROM
345    // !ALT1 && !ALT2: GETB (Write to LSB, zero extend)
346    // ALT1 && !ALT2: GETBH (Write to MSB, leave LSB unchanged)
347    // !ALT1 && ALT2: GETBL (Write to LSB, leave MSB unchanged)
348    // ALT1 && ALT2: GETBS (Write to LSB, sign extend)
349    let byte = gsu.state.rom_buffer;
350    let source = read_register(gsu, gsu.sreg);
351
352    let value = match (gsu.alt1, gsu.alt2) {
353        (false, false) => u16::from(byte),
354        (true, false) => u16::from_le_bytes([source.lsb(), byte]),
355        (false, true) => u16::from_le_bytes([byte, source.msb()]),
356        (true, true) => byte as i8 as u16,
357    };
358
359    let mut cycles = write_register(gsu, gsu.dreg, value, rom, ram);
360
361    cycles += gsu.state.rom_buffer_wait_cycles;
362    gsu.state.rom_buffer_wait_cycles = 0;
363
364    clear_prefix_flags(gsu);
365    cycles + memory_type.access_cycles(gsu.clock_speed)
366}
367
368pub(super) fn hib(
369    memory_type: MemoryType,
370    gsu: &mut GraphicsSupportUnit,
371    rom: &[u8],
372    ram: &[u8],
373) -> u8 {
374    // HIB: High byte of register
375    let source = read_register(gsu, gsu.sreg);
376    let source_msb = source.msb();
377    let cycles = write_register(gsu, gsu.dreg, source_msb.into(), rom, ram);
378
379    gsu.zero_flag = source_msb == 0;
380    gsu.sign_flag = source_msb.sign_bit();
381
382    clear_prefix_flags(gsu);
383    cycles + memory_type.access_cycles(gsu.clock_speed)
384}
385
386pub(super) fn lob(
387    memory_type: MemoryType,
388    gsu: &mut GraphicsSupportUnit,
389    rom: &[u8],
390    ram: &[u8],
391) -> u8 {
392    // LOB: Low byte of register
393    let source = read_register(gsu, gsu.sreg);
394    let source_lsb = source as u8;
395    let cycles = write_register(gsu, gsu.dreg, source_lsb.into(), rom, ram);
396
397    gsu.zero_flag = source_lsb == 0;
398    gsu.sign_flag = source_lsb.sign_bit();
399
400    clear_prefix_flags(gsu);
401    cycles + memory_type.access_cycles(gsu.clock_speed)
402}
403
404pub(super) fn swap(
405    memory_type: MemoryType,
406    gsu: &mut GraphicsSupportUnit,
407    rom: &[u8],
408    ram: &[u8],
409) -> u8 {
410    // SWAP: Swap bytes
411    let source = read_register(gsu, gsu.sreg);
412    let swapped = source.swap_bytes();
413    let cycles = write_register(gsu, gsu.dreg, swapped, rom, ram);
414
415    gsu.zero_flag = swapped == 0;
416    gsu.sign_flag = swapped.sign_bit();
417
418    clear_prefix_flags(gsu);
419    cycles + memory_type.access_cycles(gsu.clock_speed)
420}
421
422pub(super) fn merge(
423    memory_type: MemoryType,
424    gsu: &mut GraphicsSupportUnit,
425    rom: &[u8],
426    ram: &[u8],
427) -> u8 {
428    // MERGE: Merge high bytes
429    // Dreg.H = R7.H, Dreg.L = R8.H
430    let merged = (gsu.r[7] & 0xFF00) | (gsu.r[8] >> 8);
431    let cycles = write_register(gsu, gsu.dreg, merged, rom, ram);
432
433    gsu.zero_flag = merged & 0xF0F0 != 0;
434    gsu.carry_flag = merged & 0xE0E0 != 0;
435    gsu.sign_flag = merged & 0x8080 != 0;
436    gsu.overflow_flag = merged & 0xC0C0 != 0;
437
438    clear_prefix_flags(gsu);
439    cycles + memory_type.access_cycles(gsu.clock_speed)
440}