1use crate::ir::{Item, OpKind};
2use crate::meta::{Meta, Nls};
3use anyhow::{anyhow, bail, Result};
4use encoding_rs::{GB18030, SHIFT_JIS, UTF_8};
5use std::collections::HashMap;
6
7fn enc_text(meta: &Meta, s: &str) -> Result<Vec<u8>> {
8 let (cow, _, had_errors) = match meta.nls {
9 Nls::Utf8 => UTF_8.encode(s),
10 Nls::ShiftJis => SHIFT_JIS.encode(s),
11 Nls::Gb18030 => GB18030.encode(s),
12 };
13 if had_errors {
14 bail!("text encoding error for string: {s}");
15 }
16 Ok(cow.into_owned())
17}
18
19fn enc_cstr(meta: &Meta, s: &str) -> Result<Vec<u8>> {
20 let mut b = enc_text(meta, s)?;
21 b.push(0);
22 if b.len() > 255 {
23 bail!(
24 "string too long for u8 length (including NUL): len={} (max=255)",
25 b.len()
26 );
27 }
28 Ok(b)
29}
30
31fn opcode(k: &OpKind) -> u8 {
32 match k {
33 OpKind::Nop => 0x00,
34 OpKind::InitStack { .. } => 0x01,
35 OpKind::CallFn { .. } => 0x02,
36 OpKind::Syscall { .. } => 0x03,
37 OpKind::Ret => 0x04,
38 OpKind::Retv => 0x05,
39 OpKind::JmpAbs { .. } | OpKind::JmpLabel { .. } => 0x06,
40 OpKind::JzAbs { .. } | OpKind::JzLabel { .. } => 0x07,
41 OpKind::PushNil => 0x08,
42 OpKind::PushTrue => 0x09,
43 OpKind::PushI32(..) => 0x0A,
44 OpKind::PushI16(..) => 0x0B,
45 OpKind::PushI8(..) => 0x0C,
46 OpKind::PushF32(..) => 0x0D,
47 OpKind::PushString(..) => 0x0E,
48 OpKind::PushGlobal(..) => 0x0F,
49 OpKind::PushStack(..) => 0x10,
50 OpKind::PushGlobalTable(..) => 0x11,
51 OpKind::PushLocalTable(..) => 0x12,
52 OpKind::PushTop => 0x13,
53 OpKind::PushReturn => 0x14,
54 OpKind::PopGlobal(..) => 0x15,
55 OpKind::PopStack(..) => 0x16,
56 OpKind::PopGlobalTable(..) => 0x17,
57 OpKind::PopLocalTable(..) => 0x18,
58 OpKind::Neg => 0x19,
59 OpKind::Add => 0x1A,
60 OpKind::Sub => 0x1B,
61 OpKind::Mul => 0x1C,
62 OpKind::Div => 0x1D,
63 OpKind::Mod => 0x1E,
64 OpKind::BitTest => 0x1F,
65 OpKind::And => 0x20,
66 OpKind::Or => 0x21,
67 OpKind::SetE => 0x22,
68 OpKind::SetNe => 0x23,
69 OpKind::SetG => 0x24,
70 OpKind::SetLe => 0x25,
71 OpKind::SetL => 0x26,
72 OpKind::SetGe => 0x27,
73 }
74}
75
76fn op_size(meta: &Meta, k: &OpKind) -> Result<usize> {
77 let sz = match k {
78 OpKind::Nop
79 | OpKind::Ret
80 | OpKind::Retv
81 | OpKind::PushNil
82 | OpKind::PushTrue
83 | OpKind::PushTop
84 | OpKind::PushReturn
85 | OpKind::Neg
86 | OpKind::Add
87 | OpKind::Sub
88 | OpKind::Mul
89 | OpKind::Div
90 | OpKind::Mod
91 | OpKind::BitTest
92 | OpKind::And
93 | OpKind::Or
94 | OpKind::SetE
95 | OpKind::SetNe
96 | OpKind::SetG
97 | OpKind::SetLe
98 | OpKind::SetL
99 | OpKind::SetGe => 1,
100
101 OpKind::InitStack { .. } => 3,
102 OpKind::CallFn { .. } => 5,
103 OpKind::Syscall { .. } => 3,
104 OpKind::JmpAbs { .. }
105 | OpKind::JzAbs { .. }
106 | OpKind::JmpLabel { .. }
107 | OpKind::JzLabel { .. } => 5,
108
109 OpKind::PushI8(..) => 2,
110 OpKind::PushI16(..) => 3,
111 OpKind::PushI32(..) => 5,
112 OpKind::PushF32(..) => 5,
113 OpKind::PushString(s) => {
114 let b = enc_cstr(meta, s)?;
115 1 + 1 + b.len()
116 }
117
118 OpKind::PushGlobal(..)
119 | OpKind::PushGlobalTable(..)
120 | OpKind::PopGlobal(..)
121 | OpKind::PopGlobalTable(..) => 3,
122
123 OpKind::PushStack(..)
124 | OpKind::PopStack(..)
125 | OpKind::PushLocalTable(..)
126 | OpKind::PopLocalTable(..) => 2,
127 };
128 Ok(sz)
129}
130
131pub fn assemble(meta: &Meta, items: &[Item]) -> Result<(Vec<u8>, HashMap<String, u32>)> {
132 let base_addr: u32 = 4;
133
134 let mut labels: HashMap<String, u32> = HashMap::new();
136 let mut addr: u32 = base_addr;
137 for it in items {
138 match it {
139 Item::Label(l) => {
140 labels.insert(l.name.clone(), addr);
141 }
142 Item::Op(op) => {
143 let sz = op_size(meta, op)? as u32;
144 addr = addr
145 .checked_add(sz)
146 .ok_or_else(|| anyhow!("address overflow"))?;
147 }
148 }
149 }
150
151 let mut out: Vec<u8> = Vec::new();
153 for it in items {
154 let op = match it {
155 Item::Label(_) => continue,
156 Item::Op(op) => op,
157 };
158
159 out.push(opcode(op));
160
161 match op {
162 OpKind::Nop
163 | OpKind::Ret
164 | OpKind::Retv
165 | OpKind::PushNil
166 | OpKind::PushTrue
167 | OpKind::PushTop
168 | OpKind::PushReturn
169 | OpKind::Neg
170 | OpKind::Add
171 | OpKind::Sub
172 | OpKind::Mul
173 | OpKind::Div
174 | OpKind::Mod
175 | OpKind::BitTest
176 | OpKind::And
177 | OpKind::Or
178 | OpKind::SetE
179 | OpKind::SetNe
180 | OpKind::SetG
181 | OpKind::SetLe
182 | OpKind::SetL
183 | OpKind::SetGe => {}
184
185 OpKind::InitStack { args, locals } => {
186 out.push(*args as u8);
187 out.push(*locals as u8);
188 }
189
190 OpKind::CallFn { name } => {
191 let lbl = format!("fn:{name}");
192 let tgt = labels
193 .get(&lbl)
194 .copied()
195 .ok_or_else(|| anyhow!("unknown function label: {lbl}"))?;
196 out.extend_from_slice(&tgt.to_le_bytes());
197 }
198
199 OpKind::Syscall { id } => {
200 out.extend_from_slice(&id.to_le_bytes());
201 }
202
203 OpKind::JmpAbs { target } => {
204 out.extend_from_slice(&target.to_le_bytes());
205 }
206 OpKind::JzAbs { target } => {
207 out.extend_from_slice(&target.to_le_bytes());
208 }
209
210 OpKind::JmpLabel { label } => {
211 let tgt = labels
212 .get(label)
213 .copied()
214 .ok_or_else(|| anyhow!("unknown label: {label}"))?;
215 out.extend_from_slice(&tgt.to_le_bytes());
216 }
217 OpKind::JzLabel { label } => {
218 let tgt = labels
219 .get(label)
220 .copied()
221 .ok_or_else(|| anyhow!("unknown label: {label}"))?;
222 out.extend_from_slice(&tgt.to_le_bytes());
223 }
224
225 OpKind::PushI8(v) => out.push(*v as u8),
226 OpKind::PushI16(v) => out.extend_from_slice(&v.to_le_bytes()),
227 OpKind::PushI32(v) => out.extend_from_slice(&v.to_le_bytes()),
228 OpKind::PushF32(v) => out.extend_from_slice(&v.to_le_bytes()),
229 OpKind::PushString(s) => {
230 let b = enc_cstr(meta, s)?;
231 out.push(b.len() as u8);
232 out.extend_from_slice(&b);
233 }
234
235 OpKind::PushGlobal(idx)
236 | OpKind::PushGlobalTable(idx)
237 | OpKind::PopGlobal(idx)
238 | OpKind::PopGlobalTable(idx) => {
239 out.extend_from_slice(&idx.to_le_bytes());
240 }
241
242 OpKind::PushStack(idx) | OpKind::PopStack(idx) | OpKind::PushLocalTable(idx) | OpKind::PopLocalTable(idx) => {
243 out.push(*idx as u8);
244 }
245 }
246 }
247
248 Ok((out, labels))
249}
250
251pub fn build_sysdesc(meta: &Meta, entry_point: u32) -> Result<Vec<u8>> {
252 let mut buf: Vec<u8> = Vec::new();
253
254 buf.extend_from_slice(&entry_point.to_le_bytes());
255 buf.extend_from_slice(&meta.non_volatile_global_count.to_le_bytes());
256 buf.extend_from_slice(&meta.volatile_global_count.to_le_bytes());
257 buf.push(meta.game_mode);
258 buf.push(meta.game_mode_reserved);
259
260 let title_b = enc_cstr(meta, &meta.game_title)?;
261 buf.push(title_b.len() as u8);
262 buf.extend_from_slice(&title_b);
263
264 let sc_count = meta.syscall_count();
265 buf.extend_from_slice(&sc_count.to_le_bytes());
266
267 for sc in &meta.syscalls {
268 let name_b = enc_cstr(meta, &sc.name)?;
269 buf.push(sc.args);
270 buf.push(name_b.len() as u8);
271 buf.extend_from_slice(&name_b);
272 }
273
274 buf.extend_from_slice(&meta.custom_syscall_count.to_le_bytes());
275
276 Ok(buf)
277}