Skip to content

Commit c704f71

Browse files
committed
Fix tests
Cart load refactor
1 parent 6d15aff commit c704f71

8 files changed

Lines changed: 122 additions & 93 deletions

File tree

iepass-emu/src/main.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,25 +154,25 @@ impl eframe::App for EmulatorApp {
154154

155155
if run_result != RunResult::OutOfFuel {
156156
let rt = self.pico8.runtime();
157+
let memory = &mut rt.memory;
157158

158-
let screen_palette = *rt.memory.machine_state().palette(Palette::Screen);
159+
let screen_palette = *memory.machine_state().palette(Palette::Screen);
159160

160161
let map_color = |color: u8| -> Color {
161162
assert!(color < 16);
162163
palette::color_from_index(screen_palette[color as usize])
163164
};
164165

165166
self.fb_tex.set(self.fb_pool.from_iter(
166-
rt
167-
.memory
168-
.screen()
169-
.iter()
170-
.map(|byte| [map_color(*byte & 0x0F), map_color(*byte >> 4)])
171-
.flatten()
172-
.map(|color| {
173-
let (r, g, b) = color.rgb();
174-
Color32::from_rgb(r, g, b)
175-
})
167+
memory.screen()
168+
.as_slice_mut(memory)
169+
.iter()
170+
.map(|byte| [map_color(*byte & 0x0F), map_color(*byte >> 4)])
171+
.flatten()
172+
.map(|color| {
173+
let (r, g, b) = color.rgb();
174+
Color32::from_rgb(r, g, b)
175+
})
176176
), FRAMEBUFFER_OPTS);
177177

178178
self.frame = self.frame + 1;

p8rs-tests/src/runner/p8rs.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::time::{Duration, Instant};
55
use p8rs::vm;
66
use p8rs::piccolo::ExternError;
77
use p8rs_types::p8scii;
8-
use p8rs_types::p8scii::Display;
98
use crate::runner::TIMEOUT_MS;
109
use crate::summary::RunResult;
1110

p8rs/src/vm/api/drawing.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ pub fn line(rt: &mut Runtime, p1: Option<P8Num>, p2: Option<P8Num>, p3: Option<P
333333

334334
#[api_callback]
335335
pub fn spr(rt: &mut Runtime, n: Option<i16>, x: Option<i16>, y: Option<i16>, w: Option<P8Num>, h: Option<P8Num>, flip_x: Option<bool>, flip_y: Option<bool>) {
336-
let memory = &mut rt.memory;
337336
let n = n.unwrap_or(0);
338337
let sx = (n % 16) * 8;
339338
let sy = (n / 16) * 8;
@@ -345,16 +344,19 @@ pub fn spr(rt: &mut Runtime, n: Option<i16>, x: Option<i16>, y: Option<i16>, w:
345344
let flip_y = flip_y.unwrap_or(false);
346345
if n < 0 || n > 255 || w <= 0 || h <= 0 { return; }
347346

348-
let painter = memory.painter().sprite_mode(memory);
347+
let memory = &mut rt.memory;
348+
let graphics = memory.graphics();
349+
let painter = graphics.painter(memory).sprite_mode(memory);
350+
let sprites = graphics.sprites();
349351
let (x0, y0) = painter.to_abs(x, y);
350352
let x1 = x0 + w - 1;
351353
let y1 = y0 + h - 1;
352354

353355
match (flip_x, flip_y) {
354-
(false, false) => { painter.with_callback(|memory: &mut Memory, x, y| memory.sprites().get_pixel(memory, (sx + x as i16 - x0) as u8, (sy + y as i16 - y0) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
355-
(true, false) => { painter.with_callback(|memory: &mut Memory, x, y| memory.sprites().get_pixel(memory, (sx + x1 - x as i16) as u8, (sy + y as i16 - y0) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
356-
(false, true ) => { painter.with_callback(|memory: &mut Memory, x, y| memory.sprites().get_pixel(memory, (sx + x as i16 - x0) as u8, (sy + y1 - y as i16) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
357-
(true, true ) => { painter.with_callback(|memory: &mut Memory, x, y| memory.sprites().get_pixel(memory, (sx + x1 - x as i16) as u8, (sy + y1 - y as i16) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
356+
(false, false) => { painter.with_callback(|memory: &mut Memory, x, y| sprites.get_pixel(memory, (sx + x as i16 - x0) as u8, (sy + y as i16 - y0) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
357+
(true, false) => { painter.with_callback(|memory: &mut Memory, x, y| sprites.get_pixel(memory, (sx + x1 - x as i16) as u8, (sy + y as i16 - y0) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
358+
(false, true ) => { painter.with_callback(|memory: &mut Memory, x, y| sprites.get_pixel(memory, (sx + x as i16 - x0) as u8, (sy + y1 - y as i16) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
359+
(true, true ) => { painter.with_callback(|memory: &mut Memory, x, y| sprites.get_pixel(memory, (sx + x1 - x as i16) as u8, (sy + y1 - y as i16) as u8)).paint_abs(memory, x0..=x1, y0..=y1); },
358360
}
359361
}
360362

@@ -378,7 +380,10 @@ pub fn sspr(rt: &mut Runtime, sx: i16, sy: i16, sw: i16, sh: i16, mut dx: i16, m
378380
flip_y = !flip_y;
379381
}
380382

381-
let painter = rt.memory.painter().sprite_mode(&mut rt.memory);
383+
let memory = &mut rt.memory;
384+
let graphics = memory.graphics();
385+
let painter = graphics.painter(memory).sprite_mode(memory);
386+
let sprites = graphics.sprites();
382387
let (dx0, dy0) = painter.to_abs(dx, dy);
383388
let sx0 = sx;
384389
let sy0 = sy;
@@ -407,7 +412,7 @@ pub fn sspr(rt: &mut Runtime, sx: i16, sy: i16, sw: i16, sh: i16, mut dx: i16, m
407412
let sx = u8::try_from(sx).ok()?;
408413
let sy = u8::try_from(sy).ok()?;
409414

410-
memory.sprites().get_pixel(memory, sx, sy)
415+
sprites.get_pixel(memory, sx, sy)
411416
})
412417
.paint_abs(&mut rt.memory, dx0..dx0+dw, dy0..dy0+dh);
413418
}

p8rs/src/vm/cart/load_ctx.rs

Lines changed: 47 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::alloc::Allocator;
33
use thiserror::Error;
44
use p8rs_piccolo::ExternError;
55
use p8rs_types::p8scii;
6-
6+
use crate::vm::memory::sprites::Sprites;
77
use crate::vm::P8rs;
88

99
pub struct CartLoadContext<'vm, 'c, A: Allocator + 'static> {
@@ -47,29 +47,27 @@ impl<'vm, 'c, A: Allocator + 'static> CartLoadContext<'vm, 'c, A> {
4747

4848
fn load_gfx_section(&mut self, data: &[u8]) -> Result<(), CartLoadError> {
4949
let memory = self.vm.memory();
50-
let sprites = memory.sprites().as_slice_mut(memory);
51-
let mut max_offset = -1;
50+
let sprites = memory.sprites();
5251

5352
if self.gfx_loaded {
5453
debug!("load_gfx_section: GFX section was already loaded, overwriting data.");
5554
}
5655

57-
for (offset, byte) in split_nonempty_lines(data, 128) // todo: figure out pico8 behaviour, if load_ctx.long_map_loaded { 64 } else { 128 }
58-
.flat_map(|(line_idx, line)|
59-
nibble_chunks(line).take(64).map(|(h, l)| (hex_char_to_nibble(l).unwrap_or(0) << 4) | hex_char_to_nibble(h).unwrap_or(0))
60-
.enumerate().map(move |(col_idx, byte)| ((line_idx * 64 + col_idx) as i16, byte))
61-
)
62-
{
63-
sprites[offset as usize] = byte;
64-
if offset > max_offset { max_offset = offset; }
56+
// todo: figure out pico8 behaviour, if load_ctx.long_map_loaded { 64 } else { 128 }
57+
let mut written = 0;
58+
for res in hex_iter::<1>(data) {
59+
if res.col > Sprites::WIDTH as usize || res.row > Sprites::HEIGHT as usize { continue }
60+
61+
sprites.set_pixel(memory, res.col as u8, res.row as u8, res.value);
62+
written += 1;
6563
}
6664

6765
self.gfx_loaded = true;
6866

69-
if max_offset == -1 {
67+
if written <= 0 {
7068
debug!("load_gfx_section: GFX section loaded: empty section!");
7169
} else {
72-
debug!("load_gfx_section: GFX section loaded: 0x{:X} bytes written to memory", max_offset + 1);
70+
debug!("load_gfx_section: GFX section loaded: 0x{:X} bytes written to memory", written);
7371
}
7472

7573
Ok(())
@@ -78,73 +76,61 @@ impl<'vm, 'c, A: Allocator + 'static> CartLoadContext<'vm, 'c, A> {
7876
fn load_map_section(&mut self, data: &[u8]) -> Result<(), CartLoadError> {
7977
let memory = self.vm.memory();
8078
let map = memory.map();
81-
let mut max_offset = -1;
8279

8380
if self.map_loaded {
8481
debug!("load_map_section: MAP section was already loaded, overwriting data.")
8582
}
8683

87-
for (offset, byte) in split_nonempty_lines(data, 33)
88-
.flat_map(|(line_idx, line)|
89-
nibble_chunks(line).take(128).map(|(h, l)| {
90-
if let (Some(h), Some(l)) = (hex_char_to_nibble(h), hex_char_to_nibble(l)) {
91-
(h << 4) | l
92-
} else {
93-
0
94-
}
95-
}).enumerate().map(move |(col_idx, byte)| ((line_idx * 128 + col_idx) as i16, byte))
96-
)
97-
{
98-
// TODO: FIX
99-
// if offset >= 0x1000 { max_offset = 0x1000; break; }
100-
// let addr = offset as usize;
101-
// memory[addr] = byte;
102-
// if offset > max_offset { max_offset = offset; }
84+
// todo: figure out pico8 behaviour, if load_ctx.long_map_loaded { 64 } else { 128 }
85+
let mut written = 0;
86+
for res in hex_iter::<2>(data) {
87+
if res.col > map.width() as usize || res.row > map.height() as usize { continue }
88+
89+
map.set_sprite(memory, res.col as u16, res.row as u16, res.value);
90+
written += 1;
10391
}
10492

105-
// if gfx is loaded and the map section is longer than 0x1000, zero out the remaining shared memory area written by gfx (0x1000..0x2000, shared between MAP and GFX)
106-
// (Pico8 behavior)
107-
108-
// todo: figure out the behaviour
109-
// load_ctx.map_loaded = true;
110-
// if max_offset >= 0x1000 {
111-
// load_ctx.long_map_loaded = true;
112-
//
113-
// if load_ctx.gfx_loaded {
114-
// debug!("Clearing extended GFX from 0x{:X}", max_offset);
115-
// for offset in 0x1000..0x2000 {
116-
// let addr = gfx_base_addr + offset;
117-
// vm.runtime.memory[addr] = 0;
118-
// }
119-
// }
120-
// }
121-
122-
if max_offset == -1 {
93+
if written == -1 {
12394
debug!("load_map_section: MAP section loaded: empty section!");
12495
} else {
125-
debug!("load_map_section: MAP section loaded: 0x{:X} bytes written", max_offset);
96+
debug!("load_map_section: MAP section loaded: 0x{:X} bytes written to memory", written);
12697
}
12798

12899
Ok(())
129100
}
130101
}
131102

132-
fn hex_char_to_nibble(hex_char: u8) -> Option<u8> {
133-
match hex_char {
134-
b'0'..=b'9' => Some(hex_char - b'0'),
135-
b'a'..=b'f' => Some(hex_char - b'a' + 10),
136-
b'A'..=b'F' => Some(hex_char - b'A' + 10),
137-
_ => None,
138-
}
103+
struct HexIterValue {
104+
value: u8,
105+
row: usize,
106+
col: usize,
139107
}
140108

141-
fn split_nonempty_lines(data: &[u8], max_lines: usize) -> impl Iterator<Item=(usize, &[u8])> {
142-
data.split(|&b| b == b'\n' || b == b'\r')
143-
.filter(|line| !line.is_empty()).take(max_lines).enumerate()
109+
fn hex_iter<const Word: usize>(lines: &[u8]) -> impl Iterator<Item=HexIterValue> {
110+
lines.split(|&b| b == b'\n' || b == b'\r')
111+
.filter(|line| !line.is_empty())
112+
.enumerate()
113+
.flat_map(|(row, line)|
114+
line.as_chunks::<Word>().0
115+
.iter()
116+
.enumerate()
117+
.map(move |(col, chunk)| HexIterValue {
118+
row,
119+
col,
120+
value: chunk.iter()
121+
.copied()
122+
.map(hex_value)
123+
.fold(0, |acc, val| (acc << 4) + val.unwrap_or(0)),
124+
}))
144125
}
145126

146-
fn nibble_chunks(text: &[u8]) -> impl Iterator<Item=(u8, u8)> + '_ {
147-
text.chunks(2).map(|chunk| (chunk[0], chunk.get(1).copied().unwrap_or(b'0')))
127+
fn hex_value(char: u8) -> Option<u8> {
128+
match char {
129+
b'0'..=b'9' => Some(char - b'0'),
130+
b'a'..=b'f' => Some(char - b'a' + 10),
131+
b'A'..=b'F' => Some(char - b'A' + 10),
132+
_ => None,
133+
}
148134
}
149135

150136
#[derive(Error, Debug)]

p8rs/src/vm/memory/machine_state.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl MachineState<'_> {
5151

5252
/// [x_begin, y_begin, x_end, y_end]
5353
pub fn clip_rect(&mut self) -> &mut [u8; 4] {
54-
self.const_slice(0x20)
54+
self.const_slice_mut(0x20)
5555
}
5656

5757
/// Cursor must be controlled via methods in `Runtime` instead of changing memory directly!
@@ -106,7 +106,7 @@ impl MachineState<'_> {
106106
}
107107

108108
pub fn fill_pattern(&mut self) -> &mut FillPatternState {
109-
FillPatternState::from_bits_mut(self.const_slice(0x31))
109+
FillPatternState::from_bits_mut(self.const_slice_mut(0x31))
110110
}
111111

112112
pub fn color_flags(&mut self) -> &mut ColorFlags {
@@ -126,11 +126,11 @@ impl MachineState<'_> {
126126
}
127127

128128
pub fn tline_clip_size(&mut self) -> &mut [u8; 2] {
129-
self.const_slice(0x38)
129+
self.const_slice_mut(0x38)
130130
}
131131

132132
pub fn tline_clip_offset(&mut self) -> &mut [u8; 2] {
133-
self.const_slice(0x3a)
133+
self.const_slice_mut(0x3a)
134134
}
135135

136136
pub fn get_line_endpoint(&mut self) -> Option<[i16; 2]> {
@@ -154,15 +154,15 @@ impl MachineState<'_> {
154154
}
155155

156156
pub fn audio_effects_flags(&mut self) -> &mut [u8; 4] {
157-
self.const_slice(0x40)
157+
self.const_slice_mut(0x40)
158158
}
159159

160160
pub fn rnd_state(&mut self) -> &mut [u8; 8] {
161-
self.const_slice(0x44)
161+
self.const_slice_mut(0x44)
162162
}
163163

164164
pub fn btn_state(&mut self) -> &mut [u8; 8] {
165-
self.const_slice(0x4c)
165+
self.const_slice_mut(0x4c)
166166
}
167167

168168
pub fn sprite_addr_map(&mut self) -> &mut SpriteScreenMemoryMap {
@@ -182,7 +182,7 @@ impl MachineState<'_> {
182182
}
183183

184184
pub fn print_defaults(&mut self) -> &mut PrintDefaults {
185-
PrintDefaults::from_bits_mut(self.const_slice(0x58))
185+
PrintDefaults::from_bits_mut(self.const_slice_mut(0x58))
186186
}
187187

188188
pub fn btnp_rep_delay(&mut self) -> &mut BtnpRepDelay {
@@ -202,11 +202,11 @@ impl MachineState<'_> {
202202
}
203203

204204
pub fn high_color_bitfield(&mut self) -> &mut [u8; 16] {
205-
self.const_slice(0x70)
205+
self.const_slice_mut(0x70)
206206
}
207207

208208
#[inline(always)]
209-
pub(crate) fn const_slice<const S: usize>(&mut self, base: u16) -> &mut [u8; S] {
209+
pub(crate) fn const_slice_mut<const S: usize>(&mut self, base: u16) -> &mut [u8; S] {
210210
(&mut self.0[base as usize..base as usize + S]).try_into().unwrap()
211211
}
212212

p8rs/src/vm/memory/map.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Memory;
1+
use super::{Memory, MemoryAccess};
22

33
#[derive(Debug, Copy, Clone)]
44
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -39,4 +39,37 @@ impl Map {
3939

4040
Map { offset, width, height }
4141
}
42+
43+
pub fn set_sprite(&self, memory: &mut Memory, x: u16, y: u16, value: u8) {
44+
if let Some(addr) = self.sprite_addr(x, y) {
45+
memory.write(addr, value);
46+
}
47+
}
48+
49+
pub fn get_sprite(&self, memory: &Memory, x: u16, y: u16) -> Option<u8> {
50+
self.sprite_addr(x, y)
51+
.map(|addr| memory.read(addr))
52+
}
53+
54+
pub fn width(&self) -> u16 {
55+
self.width
56+
}
57+
58+
pub fn height(&self) -> u16 {
59+
self.height
60+
}
61+
62+
fn sprite_addr(&self, x: u16, y: u16) -> Option<u16> {
63+
if x >= self.width || y >= self.height { return None; }
64+
let pos = x + y * self.width;
65+
66+
let addr = match self.offset {
67+
MapOffset::Lower(offset) => 0x2000 + offset + pos,
68+
MapOffset::Extended(offset) => 0x8000 + offset + pos,
69+
MapOffset::Upper(offset) if pos < 0x1000 - offset => 0x3000 + offset + pos,
70+
MapOffset::Upper(offset) => 0x1000 + offset + pos,
71+
};
72+
73+
Some(addr)
74+
}
4275
}

p8rs/src/vm/memory/screen.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub struct Screen {
88
}
99

1010
impl Screen {
11+
pub const WIDTH: u16 = 128;
12+
pub const HEIGHT: u16 = 128;
13+
1114
pub(super) fn new(offset: u16) -> Screen {
1215
Screen { offset }
1316
}

0 commit comments

Comments
 (0)