1pub const SGC_PAR: u8 = 1;
8pub const SGC_CHR: u8 = 2;
9pub const SGC_PIC: u8 = 3;
10pub const SGC_SEC: u8 = 4;
11pub const SGC_TAB: u8 = 5;
12
13#[derive(Debug, Clone, Copy)]
14pub struct Sprm<'a> {
15 pub opcode: u16,
16 pub operand: &'a [u8],
17}
18
19impl<'a> Sprm<'a> {
20 pub fn sgc(&self) -> u8 {
21 ((self.opcode >> 10) & 0x7) as u8
22 }
23
24 pub fn byte(&self) -> u8 {
25 self.operand.first().copied().unwrap_or(0)
26 }
27
28 pub fn bool_operand(&self) -> Option<bool> {
29 match self.byte() {
30 0 => Some(false),
31 1 => Some(true),
32 _ => None, }
34 }
35
36 pub fn u16_operand(&self) -> u16 {
37 self.operand
38 .get(..2)
39 .map(|b| u16::from_le_bytes([b[0], b[1]]))
40 .unwrap_or(0)
41 }
42
43 pub fn i16_operand(&self) -> i16 {
44 self.u16_operand() as i16
45 }
46
47 pub fn u32_operand(&self) -> u32 {
48 self.operand
49 .get(..4)
50 .map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
51 .unwrap_or(0)
52 }
53}
54
55pub struct SprmIter<'a> {
57 grpprl: &'a [u8],
58 pos: usize,
59}
60
61pub fn iterate(grpprl: &[u8]) -> SprmIter<'_> {
62 SprmIter { grpprl, pos: 0 }
63}
64
65impl<'a> Iterator for SprmIter<'a> {
66 type Item = Sprm<'a>;
67
68 fn next(&mut self) -> Option<Sprm<'a>> {
69 if self.pos + 2 > self.grpprl.len() {
70 return None;
71 }
72 let opcode = u16::from_le_bytes([self.grpprl[self.pos], self.grpprl[self.pos + 1]]);
73 let spra = (opcode >> 13) & 0x7;
74 let mut operand_start = self.pos + 2;
75 let operand_len = match spra {
76 0 | 1 => 1,
77 2 | 4 | 5 => 2,
78 3 => 4,
79 7 => 3,
80 6 => {
81 if opcode == 0xD608 || opcode == 0xD606 {
84 if operand_start + 2 > self.grpprl.len() {
85 return None;
86 }
87 let cb = u16::from_le_bytes([
88 self.grpprl[operand_start],
89 self.grpprl[operand_start + 1],
90 ]) as usize;
91 operand_start += 2;
92 cb.saturating_sub(1)
93 } else {
94 if operand_start + 1 > self.grpprl.len() {
95 return None;
96 }
97 let cb = self.grpprl[operand_start] as usize;
98 operand_start += 1;
99 if opcode == 0xC615 && cb == 255 {
100 return None;
103 }
104 cb
105 }
106 }
107 _ => unreachable!(),
108 };
109 let operand_end = operand_start + operand_len;
110 if operand_end > self.grpprl.len() {
111 return None;
112 }
113 let sprm = Sprm {
114 opcode,
115 operand: &self.grpprl[operand_start..operand_end],
116 };
117 self.pos = operand_end;
118 Some(sprm)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn iterates_fixed_and_variable_operands() {
128 let grpprl: Vec<u8> = vec![
132 0x35, 0x08, 0x01, 0x43, 0x4A, 0x18, 0x00, 0x12, 0x64, 0xF0, 0x00, 0x01, 0x00,
135 ];
136 let sprms: Vec<_> = iterate(&grpprl).collect();
137 assert_eq!(sprms.len(), 3);
138 assert_eq!(sprms[0].opcode, 0x0835);
139 assert_eq!(sprms[0].byte(), 1);
140 assert_eq!(sprms[1].opcode, 0x4A43);
141 assert_eq!(sprms[1].u16_operand(), 24);
142 assert_eq!(sprms[2].opcode, 0x6412);
143 assert_eq!(sprms[2].sgc(), SGC_PAR);
144 }
145
146 #[test]
147 fn handles_tdef_table_u16_size() {
148 let mut grpprl: Vec<u8> = vec![0x08, 0xD6];
150 grpprl.extend_from_slice(&8u16.to_le_bytes());
151 grpprl.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7]);
152 grpprl.extend_from_slice(&[0x35, 0x08, 0x01]); let sprms: Vec<_> = iterate(&grpprl).collect();
154 assert_eq!(sprms.len(), 2);
155 assert_eq!(sprms[0].opcode, 0xD608);
156 assert_eq!(sprms[0].operand.len(), 7);
157 assert_eq!(sprms[1].opcode, 0x0835);
158 }
159
160 #[test]
161 fn stops_on_truncated_grpprl() {
162 let grpprl: Vec<u8> = vec![0x43, 0x4A, 0x18]; assert_eq!(iterate(&grpprl).count(), 0);
164 }
165}