1#![allow(clippy::needless_range_loop)] 2 3use std::sync::LazyLock; 4 5const PI: f64 = std::f64::consts::PI; 6 7type Cx4Rom = [u32; 1024]; 8 9pub fn read(address: u32) -> u32 { 10 static ROM: LazyLock<Box<Cx4Rom>> = LazyLock::new(|| { 11 let mut rom: Box<Cx4Rom> = vec![0; 1024].into_boxed_slice().try_into().unwrap(); 12 13 populate_div(&mut rom); 14 populate_sqrt(&mut rom); 15 populate_sin(&mut rom); 16 populate_asin(&mut rom); 17 populate_tan(&mut rom); 18 populate_cos(&mut rom); 19 20 rom 21 }); 22 23 ROM[(address & 0x3FF) as usize] 24} 25 26fn populate_div(rom: &mut Cx4Rom) { 27 // Division by 0 produces $FFFFFF (overflow) 28 rom[0] = 0xFFFFFF; 29 30 for i in 0x001..0x100 { 31 rom[i as usize] = 0x800000 / i; 32 } 33} 34 35fn populate_sqrt(rom: &mut Cx4Rom) { 36 for i in 0x100..0x200 { 37 let input = i - 0x100; 38 let scaled_sqrt = f64::from(0x100000) * (input as f64).sqrt(); 39 rom[i] = (scaled_sqrt as u32) & 0xFFFFFF; 40 } 41} 42 43fn populate_sin(rom: &mut Cx4Rom) { 44 for i in 0x200..0x280 { 45 let input = i - 0x200; 46 let scaled_sin = f64::from(0x1000000) * (input as f64 / 256.0 * PI).sin(); 47 rom[i] = (scaled_sin as u32) & 0xFFFFFF; 48 } 49} 50 51fn populate_asin(rom: &mut Cx4Rom) { 52 for i in 0x280..0x300 { 53 let input = i - 0x280; 54 let scaled_asin = f64::from(0x800000) / (PI / 2.0) * (input as f64 / 128.0).asin(); 55 rom[i] = (scaled_asin as u32) & 0xFFFFFF; 56 } 57} 58 59fn populate_tan(rom: &mut Cx4Rom) { 60 for i in 0x300..0x380 { 61 let input = i - 0x300; 62 let scaled_tan = f64::from(0x10000) * (input as f64 / 256.0 * PI).tan(); 63 rom[i] = (scaled_tan as u32) & 0xFFFFFF; 64 } 65 66 // tan(pi / 4) is defined as 1; without this the value will be ~0.9999999 67 rom[0x340] = 0x010000; 68} 69 70fn populate_cos(rom: &mut Cx4Rom) { 71 // cos(0) produces $FFFFFF (overflow) 72 rom[0x380] = 0xFFFFFF; 73 74 for i in 0x381..0x400 { 75 let input = i - 0x380; 76 let scaled_cos = f64::from(0x1000000) * (input as f64 / 256.0 * PI).cos(); 77 rom[i] = (scaled_cos as u32) & 0xFFFFFF; 78 } 79} 80 81#[cfg(test)] 82mod tests { 83 use super::*; 84 85 #[test] 86 fn div() { 87 assert_eq!(read(0x000), 0xFFFFFF); 88 assert_eq!(read(0x0FF), 0x008080); 89 } 90 91 #[test] 92 fn sqrt() { 93 assert_eq!(read(0x100), 0x000000); 94 assert_eq!(read(0x1FF), 0xFF7FDF); 95 } 96 97 #[test] 98 fn sin() { 99 assert_eq!(read(0x200), 0x000000); 100 assert_eq!(read(0x27F), 0xFFFB10); 101 } 102 103 #[test] 104 fn asin() { 105 assert_eq!(read(0x280), 0x000000); 106 assert_eq!(read(0x2FF), 0x75CEB4); 107 } 108 109 #[test] 110 fn tan() { 111 assert_eq!(read(0x300), 0x000000); 112 assert_eq!(read(0x37F), 0x517BB5); 113 } 114 115 #[test] 116 fn cos() { 117 assert_eq!(read(0x380), 0xFFFFFF); 118 assert_eq!(read(0x3FF), 0x03243A); 119 } 120 121 #[test] 122 fn sum() { 123 let mut sum = 0; 124 for i in 0..0x400 { 125 let value = read(i); 126 sum += value & 0xFF; 127 sum += (value >> 8) & 0xFF; 128 sum += (value >> 16) & 0xFF; 129 } 130 131 assert_eq!(sum, 0x054336); 132 } 133}