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:
0xrsydn 2026-10-05 15:35:09 +07:00
commit 2dfab137a6
5 changed files with 468 additions and 12 deletions

View file

@ -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
View 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)
}