feat(bios): implement SoftReset restart service
Execute SWI 0x00 through original ARM code. Read the restart flag before clearing 512 bytes of BIOS work RAM, reset System/SVC/IRQ stacks and specified registers, then branch to ROM or EWRAM in ARM System mode. Keep device state and clocks active, preserve unrelated CPU banks, and document the deterministic IRQ-masked restart status. Add eleven tests for every flag byte, ARM/Thumb and User/System callers, exact memory bounds, bank state, restart execution, DMA/timers, and unsupported exception callers. Validated on Darwin arm64: 705 default and 694 core-only tests in debug and release; formatting, Clippy, rustdoc, ten native window smoke tests, and CPU/timer demos pass.
This commit is contained in:
parent
f60db7e43b
commit
2dfab137a6
5 changed files with 468 additions and 12 deletions
11
src/bios.rs
11
src/bios.rs
|
|
@ -1,5 +1,5 @@
|
|||
//! Original, optional ARM BIOS replacement. No Nintendo firmware is included.
|
||||
//! Supports interrupt waits, integer/fixed-point arithmetic, affine matrices,
|
||||
//! Supports SoftReset, interrupt waits, integer/fixed-point arithmetic, affine matrices,
|
||||
//! memory copy/fill, bit unpacking, LZ77/run-length/Huffman decoding, and differential filters.
|
||||
//! This is a functional subset, not a complete boot ROM or a timing-compatible BIOS.
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ mod decompression;
|
|||
mod differential;
|
||||
mod huffman;
|
||||
mod lz77;
|
||||
mod reset;
|
||||
mod run_length;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -46,8 +47,10 @@ pub fn boot(rom: Vec<u8>) -> Result<Machine, MemoryError> {
|
|||
/// Build a deterministic 16 KiB image from original ARM instructions and a
|
||||
/// mathematically generated sine table. No Nintendo firmware bytes are included.
|
||||
/// SWIs use bits 16–23 of the ARM immediate, or the Thumb immediate byte.
|
||||
/// Services support User/System callers and preserve caller status. Registers
|
||||
/// other than each arithmetic service's documented outputs are preserved.
|
||||
/// Services support User/System callers. Returning services preserve caller status
|
||||
/// and registers other than each arithmetic service's documented outputs.
|
||||
/// SoftReset instead clears BIOS work RAM, resets specified CPU banks, and enters
|
||||
/// ROM or RAM in ARM System mode with IRQ masked. It does not reset devices.
|
||||
/// IRQ callbacks must preserve r4–r11, acknowledge IF, update IRQ_FLAGS for wait
|
||||
/// services, and return with BX lr. Nested IRQs and SWIs from callbacks are unsupported.
|
||||
/// Service routines use the Supervisor stack; exact firmware stack layout and
|
||||
|
|
@ -118,6 +121,7 @@ pub fn image() -> Vec<u8> {
|
|||
a.emit(0x01a0_c82c); // MOVEQ r12,r12,LSR #16
|
||||
a.emit(0xe20c_c0ff); // AND r12,r12,#0xff
|
||||
for (number, target) in [
|
||||
(0, "soft_reset"),
|
||||
(2, "halt"),
|
||||
(4, "intr_wait"),
|
||||
(5, "vblank_wait"),
|
||||
|
|
@ -233,6 +237,7 @@ pub fn image() -> Vec<u8> {
|
|||
// The preceding routine ends with an unconditional branch. Place a literal
|
||||
// pool here so reset/dispatcher loads stay within ARM's 4 KiB reach.
|
||||
a.flush_literals();
|
||||
reset::emit(&mut a);
|
||||
arithmetic::emit(&mut a);
|
||||
affine::emit(&mut a);
|
||||
angles::emit(&mut a);
|
||||
|
|
|
|||
38
src/bios/reset.rs
Normal file
38
src/bios/reset.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//! Original ARM SoftReset. This is a restart service, not a hardware reset.
|
||||
|
||||
use super::{ArmImage, IRQ_STACK, ROM_START, SVC_STACK, SYSTEM_STACK};
|
||||
|
||||
pub(super) fn emit(a: &mut ArmImage) {
|
||||
a.label("soft_reset");
|
||||
// Read the byte before clearing its containing RAM and the common SWI frame.
|
||||
// No stack accesses follow: this service never returns to its caller.
|
||||
a.literal(0, 0x0300_7ffa);
|
||||
a.emit(0xe5d0_2000); // LDRB r2,[r0] (zero: ROM, nonzero: EWRAM)
|
||||
a.literal(0, 0x0300_7e00);
|
||||
a.emit(0xe3a0_1080); // MOV r1,#128 (512 bytes)
|
||||
a.emit(0xe3a0_3000); // MOV r3,#0
|
||||
a.label("soft_reset_clear");
|
||||
a.emit(0xe480_3004); // STR r3,[r0],#4
|
||||
a.emit(0xe251_1001); // SUBS r1,r1,#1
|
||||
a.branch(1, "soft_reset_clear");
|
||||
|
||||
a.emit(0xe169_f003); // MSR SPSR_fc,r3 (SVC saved status = 0)
|
||||
a.emit(0xe1a0_e003); // MOV lr,r3 (SVC link = 0)
|
||||
a.literal(13, SVC_STACK);
|
||||
a.emit(0xe321_f0d2); // MSR CPSR_c,#IRQ|I|F
|
||||
a.emit(0xe169_f003); // MSR SPSR_fc,r3 (IRQ saved status = 0)
|
||||
a.emit(0xe1a0_e003); // MOV lr,r3 (IRQ link = 0)
|
||||
a.literal(13, IRQ_STACK);
|
||||
a.emit(0xe321_f0df); // MSR CPSR_c,#System|I|F
|
||||
a.literal(13, SYSTEM_STACK);
|
||||
a.emit(0xe352_0000); // CMP r2,#0
|
||||
a.literal(14, ROM_START);
|
||||
a.emit(0x13a0_e402); // MOVNE lr,#0x02000000
|
||||
for register in 0..=12 {
|
||||
a.emit(0xe3a0_0000 | register << 12); // MOV rN,#0
|
||||
}
|
||||
// Deterministic subset policy: clear NZCV and F, keep I set. Do not change
|
||||
// IME, IE, IF, devices, or unrelated CPU banks. IRQ stays masked throughout.
|
||||
a.emit(0xe329_f09f); // MSR CPSR_fc,#System|I
|
||||
a.emit(0xe12f_ff1e); // BX lr (ARM target, no return through erased SWI frame)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue