Skip to content

Commit 7ef3b46

Browse files
Resolve labels to relative offsets
Also fix test output which was mismatched for #0 -> $0 and update Core to use a boxed slice instead of a Vec (no resizing). It seems likely there will need to be a "resolved" and "unresolved" view of the core, one for use with load file format + emulation, and one for parse validation.
1 parent 334bbbc commit 7ef3b46

File tree

5 files changed

+114
-33
lines changed

5 files changed

+114
-33
lines changed

src/load_file.rs

Lines changed: 96 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::{
44
string::ToString,
55
};
66

7+
type LabelMap = HashMap<String, usize>;
8+
79
pub const DEFAULT_CORE_SIZE: usize = 8000;
810

911
enum_string!(pub Opcode {
@@ -148,6 +150,24 @@ impl Field {
148150
value: Value::Literal(value),
149151
}
150152
}
153+
154+
pub fn resolve(&self, from: usize, labels: &LabelMap) -> Result<Self, String> {
155+
match &self.value {
156+
Value::Literal(_) => Ok(self.clone()),
157+
Value::Label(label) => {
158+
let label_value = labels
159+
.get(label)
160+
.ok_or_else(|| format!("Label '{}' not found", &label))?;
161+
162+
let value = Value::Literal((*label_value as i32) - (from as i32));
163+
164+
Ok(Self {
165+
address_mode: self.address_mode,
166+
value,
167+
})
168+
}
169+
}
170+
}
151171
}
152172

153173
impl ToString for Field {
@@ -180,6 +200,17 @@ impl Instruction {
180200
field_b,
181201
}
182202
}
203+
204+
pub fn resolve(&self, from: usize, labels: &LabelMap) -> Result<Self, String> {
205+
let field_a = self.field_a.resolve(from, labels)?;
206+
let field_b = self.field_b.resolve(from, labels)?;
207+
Ok(Self {
208+
opcode: self.opcode,
209+
modifier: self.modifier,
210+
field_a,
211+
field_b,
212+
})
213+
}
183214
}
184215

185216
impl ToString for Instruction {
@@ -196,24 +227,30 @@ impl ToString for Instruction {
196227

197228
#[derive(PartialEq)]
198229
pub struct Core {
199-
instructions: Vec<Instruction>,
200-
labels: HashMap<String, usize>,
230+
instructions: Box<[Option<Instruction>]>,
231+
labels: LabelMap,
201232
}
202233

203234
impl Core {
204235
pub fn new(core_size: usize) -> Self {
205236
Core {
206-
instructions: vec![Instruction::default(); core_size],
237+
instructions: vec![None; core_size].into_boxed_slice(),
207238
labels: HashMap::new(),
208239
}
209240
}
210241

211-
pub fn get(&self, index: usize) -> Option<&Instruction> {
212-
self.instructions.get(index)
242+
pub fn get(&self, index: usize) -> Option<Instruction> {
243+
self.instructions.get(index)?.clone()
244+
}
245+
246+
pub fn get_resolved(&self, index: usize) -> Result<Instruction, String> {
247+
self.get(index)
248+
.unwrap_or_default()
249+
.resolve(index, &self.labels)
213250
}
214251

215252
pub fn set(&mut self, index: usize, value: Instruction) {
216-
self.instructions[index] = value;
253+
self.instructions[index] = Some(value);
217254
}
218255

219256
pub fn add_label(&mut self, index: usize, label: String) -> Result<(), String> {
@@ -238,21 +275,22 @@ impl Core {
238275
self.labels.get(label).copied()
239276
}
240277

241-
pub fn dump_all(&self) -> String {
242-
self.instructions
243-
.iter()
244-
.fold(String::new(), |result, instruction| {
245-
result + &instruction.to_string() + "\n"
246-
})
247-
}
248-
249278
pub fn dump(&self) -> String {
250-
self.instructions
279+
let resolve_result: Result<Vec<_>, String> = self
280+
.instructions
251281
.iter()
252-
.filter(|&instruction| *instruction != Instruction::default())
253-
.fold(String::new(), |result, instruction| {
282+
.filter(|&maybe_instruction| maybe_instruction.is_some())
283+
.map(|instruction| instruction.as_ref().unwrap())
284+
.enumerate()
285+
.map(|(i, instruction)| instruction.resolve(i, &self.labels))
286+
.collect();
287+
288+
match resolve_result {
289+
Err(msg) => msg,
290+
Ok(resolved) => resolved.iter().fold(String::new(), |result, instruction| {
254291
result + &instruction.to_string() + "\n"
255-
})
292+
}),
293+
}
256294
}
257295
}
258296

@@ -435,4 +473,44 @@ mod tests {
435473
assert!(core.label_address("goblin").is_none());
436474
assert!(core.label_address("never_mentioned").is_none());
437475
}
476+
477+
// TODO: add test for unresolvable label
478+
#[test]
479+
fn resolve_labels() {
480+
let mut core = Core::new(200);
481+
482+
core.add_label(123, "baz".into()).expect("Should add baz");
483+
core.add_label(0, "foo".into()).expect("Should add foo");
484+
485+
core.set(
486+
5,
487+
Instruction {
488+
field_a: Field {
489+
value: Value::Label("baz".into()),
490+
..Default::default()
491+
},
492+
field_b: Field {
493+
value: Value::Label("foo".into()),
494+
..Default::default()
495+
},
496+
..Default::default()
497+
},
498+
);
499+
500+
let resolved = core.get_resolved(5).expect("Should resolve instruction");
501+
assert_eq!(
502+
resolved,
503+
Instruction {
504+
field_a: Field {
505+
value: Value::Literal(118),
506+
..Default::default()
507+
},
508+
field_b: Field {
509+
value: Value::Literal(-5),
510+
..Default::default()
511+
},
512+
..Default::default()
513+
}
514+
)
515+
}
438516
}

src/parser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,4 +383,6 @@ mod tests {
383383

384384
assert_eq!(parsed, expected_core);
385385
}
386+
387+
// TODO: parse error for unresolvable label
386388
}

tests/data/test.red

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ preload
33
dat 1 , 2
44
mov 1, 0
55

6-
jmp 123
6+
jmp loop
7+
jmp begin
78

89
begin: add # 1, @ 2
910
sub 3, 4

tests/data/test_loadfile.red

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
1-
MOV.I #0, $1
1+
MOV.I $0, $1
22
DAT.F $1, $2
3-
MOV.I $1, #0
4-
JMP.B $123, #0
3+
MOV.I $1, $0
4+
JMP.B $16, #0
5+
JMP.B $1, #0
56
ADD.AB #1, @2
67
SUB.F $3, $4
78
MUL.F $5, $6
89
DIV.F $7, $8
910
MOD.F $9, $10
10-
JMZ.B #0, #0
11-
JMN.B #0, #0
12-
DJN.B #0, #0
13-
CMP.I #0, #0
14-
SEQ.I #0, #0
15-
SNE.I #0, #0
16-
SLT.B #0, #0
17-
SPL.B #0, #0
18-
NOP.B #0, #0
11+
JMZ.B $0, #0
12+
JMN.B $0, #0
13+
DJN.B $0, #0
14+
CMP.B $0, #0
15+
SEQ.B $0, #0
16+
SNE.B $0, #0
17+
SLT.B $0, #0
18+
SPL.B $0, #0
19+
NOP.B $0, #0
1920
MOV.A $1, $2
2021
MOV.B $1, $2
2122
MOV.AB $1, $2
2223
MOV.BA $1, $2
2324
MOV.F $1, $2
2425
MOV.X $1, $2
2526
MOV.I $1, $2
26-
JMP.B $-22, #0
27+
JMP.B $-7, #0

tests/dump.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ fn run_test(input: &str, expected_output: &str) {
1313
}
1414

1515
#[test]
16-
#[ignore = "Fails because labels are not yet converted to offsets"]
1716
fn dump_all_opcodes() {
1817
run_test(
1918
include_str!("data/test.red"),

0 commit comments

Comments
 (0)