mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
7fc2412c9c
* move state to new thread * more * more
500 lines
16 KiB
Rust
500 lines
16 KiB
Rust
use crate::device;
|
|
use crate::ui;
|
|
|
|
const SP_MEM_ADDR_REG: usize = 0;
|
|
const SP_DRAM_ADDR_REG: usize = 1;
|
|
const SP_RD_LEN_REG: usize = 2;
|
|
const SP_WR_LEN_REG: usize = 3;
|
|
pub const SP_STATUS_REG: usize = 4;
|
|
const SP_DMA_FULL_REG: usize = 5;
|
|
const SP_DMA_BUSY_REG: usize = 6;
|
|
const SP_SEMAPHORE_REG: usize = 7;
|
|
pub const SP_REGS_COUNT: usize = 8;
|
|
|
|
pub const SP_PC_REG: usize = 0;
|
|
//const SP_IBIST_REG: usize = 1;
|
|
pub const SP_REGS2_COUNT: usize = 2;
|
|
|
|
/* SP_STATUS - read */
|
|
pub const SP_STATUS_HALT: u32 = 1 << 0;
|
|
const SP_STATUS_BROKE: u32 = 1 << 1;
|
|
const SP_STATUS_DMA_BUSY: u32 = 1 << 2;
|
|
const SP_STATUS_DMA_FULL: u32 = 1 << 3;
|
|
//const SP_STATUS_IO_FULL: u32 = 1 << 4;
|
|
const SP_STATUS_SSTEP: u32 = 1 << 5;
|
|
const SP_STATUS_INTR_BREAK: u32 = 1 << 6;
|
|
const SP_STATUS_SIG0: u32 = 1 << 7;
|
|
const SP_STATUS_SIG1: u32 = 1 << 8;
|
|
const SP_STATUS_SIG2: u32 = 1 << 9;
|
|
const SP_STATUS_SIG3: u32 = 1 << 10;
|
|
const SP_STATUS_SIG4: u32 = 1 << 11;
|
|
const SP_STATUS_SIG5: u32 = 1 << 12;
|
|
const SP_STATUS_SIG6: u32 = 1 << 13;
|
|
const SP_STATUS_SIG7: u32 = 1 << 14;
|
|
|
|
/* SP_STATUS - write */
|
|
const SP_CLR_HALT: u32 = 1 << 0;
|
|
pub const SP_SET_HALT: u32 = 1 << 1;
|
|
const SP_CLR_BROKE: u32 = 1 << 2;
|
|
const SP_CLR_INTR: u32 = 1 << 3;
|
|
const SP_SET_INTR: u32 = 1 << 4;
|
|
const SP_CLR_SSTEP: u32 = 1 << 5;
|
|
const SP_SET_SSTEP: u32 = 1 << 6;
|
|
const SP_CLR_INTR_BREAK: u32 = 1 << 7;
|
|
const SP_SET_INTR_BREAK: u32 = 1 << 8;
|
|
const SP_CLR_SIG0: u32 = 1 << 9;
|
|
const SP_SET_SIG0: u32 = 1 << 10;
|
|
const SP_CLR_SIG1: u32 = 1 << 11;
|
|
const SP_SET_SIG1: u32 = 1 << 12;
|
|
const SP_CLR_SIG2: u32 = 1 << 13;
|
|
const SP_SET_SIG2: u32 = 1 << 14;
|
|
const SP_CLR_SIG3: u32 = 1 << 15;
|
|
const SP_SET_SIG3: u32 = 1 << 16;
|
|
const SP_CLR_SIG4: u32 = 1 << 17;
|
|
const SP_SET_SIG4: u32 = 1 << 18;
|
|
const SP_CLR_SIG5: u32 = 1 << 19;
|
|
const SP_SET_SIG5: u32 = 1 << 20;
|
|
const SP_CLR_SIG6: u32 = 1 << 21;
|
|
const SP_SET_SIG6: u32 = 1 << 22;
|
|
const SP_CLR_SIG7: u32 = 1 << 23;
|
|
const SP_SET_SIG7: u32 = 1 << 24;
|
|
|
|
const RSP_MEM_MASK: usize = 0x1FFF;
|
|
|
|
#[derive(PartialEq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub enum DmaDir {
|
|
None,
|
|
Write,
|
|
Read,
|
|
}
|
|
|
|
#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct RspDma {
|
|
pub dir: DmaDir,
|
|
pub length: u32,
|
|
pub memaddr: u32,
|
|
pub dramaddr: u32,
|
|
}
|
|
|
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct Rsp {
|
|
pub cpu: device::rsp_cpu::Cpu,
|
|
pub regs: [u32; SP_REGS_COUNT],
|
|
pub regs2: [u32; SP_REGS2_COUNT],
|
|
#[serde(with = "serde_big_array::BigArray")]
|
|
pub mem: [u8; 0x2000],
|
|
pub fifo: [RspDma; 2],
|
|
pub last_status_value: u32,
|
|
pub run_after_dma: bool,
|
|
}
|
|
|
|
pub fn read_mem_fast(
|
|
device: &device::Device,
|
|
address: u64,
|
|
_access_size: device::memory::AccessSize,
|
|
) -> u32 {
|
|
let masked_address = address as usize & RSP_MEM_MASK;
|
|
u32::from_be_bytes(
|
|
device.rsp.mem[masked_address..masked_address + 4]
|
|
.try_into()
|
|
.unwrap(),
|
|
)
|
|
}
|
|
|
|
pub fn read_mem(
|
|
device: &mut device::Device,
|
|
address: u64,
|
|
access_size: device::memory::AccessSize,
|
|
) -> u32 {
|
|
device::cop0::add_cycles(device, access_size as u64 / 4);
|
|
|
|
let masked_address = address as usize & RSP_MEM_MASK;
|
|
u32::from_be_bytes(
|
|
device.rsp.mem[masked_address..masked_address + 4]
|
|
.try_into()
|
|
.unwrap(),
|
|
)
|
|
}
|
|
|
|
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, _mask: u32) {
|
|
let masked_address = address as usize & RSP_MEM_MASK;
|
|
let mut data = u32::from_be_bytes(
|
|
device.rsp.mem[masked_address..masked_address + 4]
|
|
.try_into()
|
|
.unwrap(),
|
|
);
|
|
device::memory::masked_write_32(&mut data, value, 0xFFFFFFFF);
|
|
device.rsp.mem[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
|
|
|
|
if masked_address & 0x1000 != 0 {
|
|
// imem being updated
|
|
device.rsp.cpu.instructions[(masked_address & 0xFFF) / 4].func =
|
|
device::rsp_cpu::decode_opcode(device, data);
|
|
device.rsp.cpu.instructions[(masked_address & 0xFFF) / 4].opcode = data;
|
|
}
|
|
|
|
// SH/SB are broken: They overwrite the whole 32 bit, filling everything that isn't written with zeroes
|
|
}
|
|
|
|
fn do_dma(device: &mut device::Device, dma: RspDma) {
|
|
let l = dma.length;
|
|
|
|
let length = ((l & 0xfff) | 7) + 1;
|
|
let count = ((l >> 12) & 0xff) + 1;
|
|
let skip = (l >> 20) & 0xff8;
|
|
|
|
let mut mem_addr = dma.memaddr & 0xff8;
|
|
let mut dram_addr = dma.dramaddr & 0xfffff8;
|
|
let offset = dma.memaddr & 0x1000;
|
|
|
|
ui::video::check_framebuffers(dram_addr, count * (length + skip) - skip);
|
|
if dma.dir == DmaDir::Read {
|
|
let mut j = 0;
|
|
while j < count {
|
|
let mut i = 0;
|
|
while i < length {
|
|
let data = u32::from_be_bytes(
|
|
device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize
|
|
..(offset + (mem_addr & 0xFFF)) as usize + 4]
|
|
.try_into()
|
|
.unwrap(),
|
|
);
|
|
device
|
|
.rdram
|
|
.mem
|
|
.get_mut(dram_addr as usize..dram_addr as usize + 4)
|
|
.unwrap_or(&mut [0; 4])
|
|
.copy_from_slice(&data.to_ne_bytes());
|
|
mem_addr += 4;
|
|
dram_addr += 4;
|
|
i += 4;
|
|
}
|
|
dram_addr += skip;
|
|
j += 1;
|
|
}
|
|
} else {
|
|
let mut j = 0;
|
|
while j < count {
|
|
let mut i = 0;
|
|
while i < length {
|
|
let data = u32::from_ne_bytes(
|
|
device
|
|
.rdram
|
|
.mem
|
|
.get(dram_addr as usize..dram_addr as usize + 4)
|
|
.unwrap_or(&[0; 4])
|
|
.try_into()
|
|
.unwrap_or_default(),
|
|
);
|
|
|
|
if offset != 0 {
|
|
// imem being updated
|
|
device.rsp.cpu.instructions[((mem_addr & 0xFFF) / 4) as usize].func =
|
|
device::rsp_cpu::decode_opcode(device, data);
|
|
device.rsp.cpu.instructions[((mem_addr & 0xFFF) / 4) as usize].opcode = data;
|
|
}
|
|
device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize
|
|
..(offset + (mem_addr & 0xFFF)) as usize + 4]
|
|
.copy_from_slice(&data.to_be_bytes());
|
|
mem_addr += 4;
|
|
dram_addr += 4;
|
|
i += 4;
|
|
}
|
|
dram_addr += skip;
|
|
j += 1;
|
|
}
|
|
}
|
|
device.rsp.regs[SP_MEM_ADDR_REG] = (mem_addr & 0xfff) + (dma.memaddr & 0x1000);
|
|
device.rsp.regs[SP_DRAM_ADDR_REG] = dram_addr;
|
|
device.rsp.regs[SP_RD_LEN_REG] = 0xff8;
|
|
device.rsp.regs[SP_WR_LEN_REG] = 0xff8;
|
|
|
|
device::events::create_event(
|
|
device,
|
|
device::events::EVENT_TYPE_SPDMA,
|
|
device::rdram::rdram_calculate_cycles((count * length) as u64) + 9,
|
|
);
|
|
}
|
|
|
|
fn fifo_push(device: &mut device::Device, dir: DmaDir) {
|
|
if device.rsp.regs[SP_DMA_FULL_REG] != 0 {
|
|
panic!("RSP DMA already full")
|
|
}
|
|
|
|
device.rsp.cpu.sync_point = true;
|
|
|
|
if device.rsp.regs[SP_DMA_BUSY_REG] != 0 {
|
|
device.rsp.fifo[1].dir = dir;
|
|
if dir == DmaDir::Read {
|
|
device.rsp.fifo[1].length = device.rsp.regs[SP_WR_LEN_REG]
|
|
} else {
|
|
device.rsp.fifo[1].length = device.rsp.regs[SP_RD_LEN_REG]
|
|
}
|
|
device.rsp.fifo[1].memaddr = device.rsp.regs[SP_MEM_ADDR_REG];
|
|
device.rsp.fifo[1].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG];
|
|
device.rsp.regs[SP_DMA_FULL_REG] = 1;
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_DMA_FULL
|
|
} else {
|
|
device.rsp.fifo[0].dir = dir;
|
|
if dir == DmaDir::Read {
|
|
device.rsp.fifo[0].length = device.rsp.regs[SP_WR_LEN_REG]
|
|
} else {
|
|
device.rsp.fifo[0].length = device.rsp.regs[SP_RD_LEN_REG]
|
|
}
|
|
device.rsp.fifo[0].memaddr = device.rsp.regs[SP_MEM_ADDR_REG];
|
|
device.rsp.fifo[0].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG];
|
|
device.rsp.regs[SP_DMA_BUSY_REG] = 1;
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_DMA_BUSY;
|
|
|
|
do_dma(device, device.rsp.fifo[0])
|
|
}
|
|
}
|
|
|
|
pub fn fifo_pop(device: &mut device::Device) {
|
|
if device.rsp.regs[SP_DMA_FULL_REG] != 0 {
|
|
device.rsp.fifo[0].dir = device.rsp.fifo[1].dir;
|
|
device.rsp.fifo[0].length = device.rsp.fifo[1].length;
|
|
device.rsp.fifo[0].memaddr = device.rsp.fifo[1].memaddr;
|
|
device.rsp.fifo[0].dramaddr = device.rsp.fifo[1].dramaddr;
|
|
device.rsp.regs[SP_DMA_FULL_REG] = 0;
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_DMA_FULL;
|
|
|
|
do_dma(device, device.rsp.fifo[0])
|
|
} else {
|
|
device.rsp.regs[SP_DMA_BUSY_REG] = 0;
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_DMA_BUSY;
|
|
if device.rsp.run_after_dma {
|
|
device.rsp.run_after_dma = false;
|
|
do_task(device);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn read_regs(
|
|
device: &mut device::Device,
|
|
address: u64,
|
|
_access_size: device::memory::AccessSize,
|
|
) -> u32 {
|
|
if !device.rsp.cpu.running {
|
|
device::cop0::add_cycles(device, 20);
|
|
}
|
|
let reg = (address & 0xFFFF) >> 2;
|
|
match reg as usize {
|
|
SP_STATUS_REG => {
|
|
let value = device.rsp.regs[reg as usize]
|
|
& (SP_STATUS_SIG0
|
|
| SP_STATUS_SIG1
|
|
| SP_STATUS_SIG2
|
|
| SP_STATUS_SIG3
|
|
| SP_STATUS_SIG4
|
|
| SP_STATUS_SIG5
|
|
| SP_STATUS_SIG6
|
|
| SP_STATUS_SIG7);
|
|
if value == device.rsp.last_status_value && value != 0 {
|
|
device.rsp.cpu.sync_point = true;
|
|
}
|
|
|
|
device.rsp.last_status_value = value;
|
|
device.rsp.regs[reg as usize]
|
|
}
|
|
SP_SEMAPHORE_REG => {
|
|
let value = device.rsp.regs[reg as usize];
|
|
if value == 1 {
|
|
device.rsp.cpu.sync_point = true;
|
|
}
|
|
device.rsp.regs[reg as usize] = 1;
|
|
value
|
|
}
|
|
_ => device.rsp.regs[reg as usize],
|
|
}
|
|
}
|
|
|
|
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
|
let reg = (address & 0xFFFF) >> 2;
|
|
match reg as usize {
|
|
SP_STATUS_REG => update_sp_status(device, value),
|
|
SP_RD_LEN_REG => {
|
|
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask);
|
|
fifo_push(device, DmaDir::Write)
|
|
}
|
|
SP_WR_LEN_REG => {
|
|
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask);
|
|
fifo_push(device, DmaDir::Read)
|
|
}
|
|
SP_SEMAPHORE_REG => {
|
|
device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], 0, mask)
|
|
}
|
|
_ => device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask),
|
|
}
|
|
}
|
|
|
|
pub fn read_regs2(
|
|
device: &mut device::Device,
|
|
address: u64,
|
|
_access_size: device::memory::AccessSize,
|
|
) -> u32 {
|
|
device::cop0::add_cycles(device, 20);
|
|
device.rsp.regs2[((address & 0xFFFF) >> 2) as usize]
|
|
}
|
|
|
|
pub fn write_regs2(device: &mut device::Device, address: u64, value: u32, mask: u32) {
|
|
let reg = (address & 0xFFFF) >> 2;
|
|
match reg as usize {
|
|
SP_PC_REG => {
|
|
device::memory::masked_write_32(
|
|
&mut device.rsp.regs2[reg as usize],
|
|
value & 0xFFC,
|
|
mask,
|
|
);
|
|
}
|
|
_ => device::memory::masked_write_32(&mut device.rsp.regs2[reg as usize], value, mask),
|
|
}
|
|
}
|
|
|
|
fn update_sp_status(device: &mut device::Device, w: u32) {
|
|
let was_halted = device.rsp.regs[SP_STATUS_REG] & SP_STATUS_HALT != 0;
|
|
|
|
/* clear / set halt */
|
|
if w & SP_CLR_HALT != 0 && w & SP_SET_HALT == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_HALT
|
|
}
|
|
if w & SP_SET_HALT != 0 && w & SP_CLR_HALT == 0 {
|
|
device::events::remove_event(device, device::events::EVENT_TYPE_SP);
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_HALT
|
|
}
|
|
|
|
/* clear broke */
|
|
if w & SP_CLR_BROKE != 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_BROKE
|
|
}
|
|
|
|
/* clear SP interrupt */
|
|
if (w & SP_CLR_INTR) != 0 && (w & SP_SET_INTR) == 0 {
|
|
device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_SP)
|
|
}
|
|
/* set SP interrupt */
|
|
if (w & SP_SET_INTR) != 0 && (w & SP_CLR_INTR) == 0 {
|
|
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP);
|
|
}
|
|
|
|
/* clear / set single step */
|
|
if w & SP_CLR_SSTEP != 0 && w & SP_SET_SSTEP == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SSTEP
|
|
}
|
|
if w & SP_SET_SSTEP != 0 && w & SP_CLR_SSTEP == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SSTEP
|
|
}
|
|
|
|
/* clear / set interrupt on break */
|
|
if w & SP_CLR_INTR_BREAK != 0 && w & SP_SET_INTR_BREAK == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_INTR_BREAK
|
|
}
|
|
if w & SP_SET_INTR_BREAK != 0 && w & SP_CLR_INTR_BREAK == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_INTR_BREAK
|
|
}
|
|
|
|
/* clear / set signal 0 */
|
|
if w & SP_CLR_SIG0 != 0 && w & SP_SET_SIG0 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG0
|
|
}
|
|
if w & SP_SET_SIG0 != 0 && w & SP_CLR_SIG0 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG0
|
|
}
|
|
|
|
/* clear / set signal 1 */
|
|
if w & SP_CLR_SIG1 != 0 && w & SP_SET_SIG1 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG1
|
|
}
|
|
if w & SP_SET_SIG1 != 0 && w & SP_CLR_SIG1 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG1
|
|
}
|
|
|
|
/* clear / set signal 2 */
|
|
if w & SP_CLR_SIG2 != 0 && w & SP_SET_SIG2 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG2
|
|
}
|
|
if w & SP_SET_SIG2 != 0 && w & SP_CLR_SIG2 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG2
|
|
}
|
|
|
|
/* clear / set signal 3 */
|
|
if w & SP_CLR_SIG3 != 0 && w & SP_SET_SIG3 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG3
|
|
}
|
|
if w & SP_SET_SIG3 != 0 && w & SP_CLR_SIG3 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG3
|
|
}
|
|
|
|
/* clear / set signal 4 */
|
|
if w & SP_CLR_SIG4 != 0 && w & SP_SET_SIG4 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG4
|
|
}
|
|
if w & SP_SET_SIG4 != 0 && w & SP_CLR_SIG4 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG4
|
|
}
|
|
|
|
/* clear / set signal 5 */
|
|
if w & SP_CLR_SIG5 != 0 && w & SP_SET_SIG5 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG5
|
|
}
|
|
if w & SP_SET_SIG5 != 0 && w & SP_CLR_SIG5 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG5
|
|
}
|
|
|
|
/* clear / set signal 6 */
|
|
if w & SP_CLR_SIG6 != 0 && w & SP_SET_SIG6 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG6
|
|
}
|
|
if w & SP_SET_SIG6 != 0 && w & SP_CLR_SIG6 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG6
|
|
}
|
|
|
|
/* clear / set signal 7 */
|
|
if w & SP_CLR_SIG7 != 0 && w & SP_SET_SIG7 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] &= !SP_STATUS_SIG7
|
|
}
|
|
if w & SP_SET_SIG7 != 0 && w & SP_CLR_SIG7 == 0 {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_SIG7
|
|
}
|
|
|
|
if device.rsp.regs[SP_STATUS_REG] & SP_STATUS_HALT == 0 && was_halted {
|
|
device.rsp.cpu.broken = false;
|
|
device.rsp.cpu.halted = false;
|
|
do_task(device);
|
|
}
|
|
}
|
|
|
|
fn do_task(device: &mut device::Device) {
|
|
device.rsp.cpu.sync_point = false;
|
|
device.rsp.last_status_value = 0;
|
|
device.rdp.last_status_value = 0;
|
|
if device.rsp.regs[SP_DMA_BUSY_REG] == 1 {
|
|
device.rsp.run_after_dma = true
|
|
} else {
|
|
let timer = device::rsp_cpu::run(device);
|
|
|
|
device::events::create_event(device, device::events::EVENT_TYPE_SP, timer)
|
|
}
|
|
}
|
|
|
|
pub fn rsp_event(device: &mut device::Device) {
|
|
if device.rsp.cpu.broken {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_HALT | SP_STATUS_BROKE;
|
|
|
|
if device.rsp.regs[SP_STATUS_REG] & SP_STATUS_INTR_BREAK != 0 {
|
|
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP)
|
|
}
|
|
return;
|
|
}
|
|
if device.rsp.cpu.halted {
|
|
device.rsp.regs[SP_STATUS_REG] |= SP_STATUS_HALT;
|
|
return;
|
|
}
|
|
do_task(device)
|
|
}
|
|
|
|
pub fn init(device: &mut device::Device) {
|
|
device.rsp.regs[SP_STATUS_REG] = 1;
|
|
device::rsp_cpu::init(device);
|
|
}
|