Add RLUnCompWram and RLUnCompVram with block bounds, word-independent buffering, and device-progress tests.
64 lines
2.7 KiB
Rust
64 lines
2.7 KiB
Rust
//! Original run-length decoder emitted as ARM instructions.
|
|
//! Runs must fit the declared output size. Halfword output requires an even
|
|
//! destination and size; malformed-input diagnostics do not emulate BIOS quirks.
|
|
|
|
use super::ArmImage;
|
|
|
|
pub(super) fn emit(a: &mut ArmImage) {
|
|
a.label("rl_wram");
|
|
a.emit(0xe3a0_c000); // MOV r12,#0 (byte stores)
|
|
a.branch(14, "rl_start");
|
|
a.label("rl_vram");
|
|
a.emit(0xe3a0_c001); // MOV r12,#1 (buffered halfword stores)
|
|
a.label("rl_start");
|
|
a.emit(0xe92d_0ff0); // STMDB sp!,{r4-r11} (60 bytes total SVC stack use)
|
|
a.emit(0xe310_0003); // TST r0,#3 (word-aligned header)
|
|
a.branch(1, "invalid_argument");
|
|
a.emit(0xe350_0402); // CMP r0,#0x02000000 (reject protected source region)
|
|
a.branch(3, "invalid_argument");
|
|
a.emit(0xe490_2004); // LDR r2,[r0],#4
|
|
a.emit(0xe202_b0ff); // AND r11,r2,#0xff
|
|
a.emit(0xe35b_0030); // CMP r11,#0x30 (run-length type, reserved bits zero)
|
|
a.branch(1, "invalid_argument");
|
|
a.emit(0xe1a0_2422); // MOV r2,r2,LSR #8 (24-bit output byte count)
|
|
a.emit(0xe35c_0000); // CMP r12,#0
|
|
a.branch(0, "rl_validate_end");
|
|
a.emit(0xe181_b002); // ORR r11,r1,r2
|
|
a.emit(0xe31b_0001); // TST r11,#1 (even destination and length)
|
|
a.branch(1, "invalid_argument");
|
|
a.label("rl_validate_end");
|
|
a.emit(0xe091_b002); // ADDS r11,r1,r2
|
|
a.branch(2, "invalid_argument"); // Reject destination address wrap.
|
|
a.emit(0xe352_0000); // CMP r2,#0
|
|
a.branch(0, "rl_done");
|
|
a.emit(0xe3a0_9000); // MOV r9,#0 (pending halfword)
|
|
a.emit(0xe3a0_a000); // MOV r10,#0 (bit position 0 or 8)
|
|
|
|
a.label("rl_block");
|
|
a.emit(0xe4d0_3001); // LDRB r3,[r0],#1 (block control)
|
|
a.emit(0xe203_707f); // AND r7,r3,#0x7f
|
|
a.emit(0xe313_0080); // TST r3,#0x80 (repeat flag)
|
|
a.emit(0x0287_7001); // ADDEQ r7,r7,#1 (1..128 literal bytes)
|
|
a.emit(0x1287_7003); // ADDNE r7,r7,#3 (3..130 repeated bytes)
|
|
a.emit(0xe157_0002); // CMP r7,r2
|
|
a.branch(8, "invalid_argument"); // Reject a block exceeding remaining output.
|
|
a.emit(0xe313_0080); // TST r3,#0x80
|
|
a.branch(0, "rl_literal");
|
|
a.emit(0xe4d0_5001); // LDRB r5,[r0],#1 (read repeated value only once)
|
|
a.label("rl_repeat");
|
|
a.call("decompress_store_byte");
|
|
a.emit(0xe257_7001); // SUBS r7,r7,#1
|
|
a.branch(1, "rl_repeat");
|
|
a.branch(14, "rl_next");
|
|
a.label("rl_literal");
|
|
a.emit(0xe4d0_5001); // LDRB r5,[r0],#1
|
|
a.call("decompress_store_byte");
|
|
a.emit(0xe257_7001); // SUBS r7,r7,#1
|
|
a.branch(1, "rl_literal");
|
|
a.label("rl_next");
|
|
a.emit(0xe352_0000); // CMP r2,#0
|
|
a.branch(1, "rl_block");
|
|
a.label("rl_done");
|
|
a.emit(0xe8bd_0ff0); // LDMIA sp!,{r4-r11}
|
|
a.branch(14, "return");
|
|
}
|