Skip to content

Commit a0dcf2b

Browse files
committed
rect, rectfill and fillp
1 parent f1d07ef commit a0dcf2b

8 files changed

Lines changed: 119 additions & 32 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

p8rs-tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ test = false
1717
p8rs = { workspace = true, features = ["log-04", "std"] }
1818
p8rs-types.workspace = true
1919
anyhow.workspace = true
20+
env_logger.workspace = true
2021
colored = "3.0.0"
2122
serde = { version = "1.0.228", features = ["derive"] }
2223
serde_json = "1.0.145"

p8rs-tests/src/tester.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ fn setup() {
2121
err => err.expect("Unable to remove tmp dir"),
2222
}
2323
fs::create_dir_all(TMP_DIR).expect("Unable to create tmp dir");
24+
25+
env_logger::init();
2426
});
2527
}
2628

p8rs/src/vm/api/drawing.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use core::alloc::Allocator;
22
use p8rs_macros::api_callback;
33
use p8rs_piccolo::Context;
44
use p8rs_types::p8num::P8Num;
5+
56
use crate::vm::Runtime;
67

78
pub fn install_pico8_drawing<A: Allocator + 'static>(ctx: Context) {
89
ctx.set_global("rectfill", rectfill::callback::<A>(ctx));
10+
ctx.set_global("rect", rect::callback::<A>(ctx));
911
}
1012

1113
#[api_callback]
@@ -15,6 +17,27 @@ pub fn rectfill<A: Allocator + 'static>(rt: &mut Runtime<A>, x0: Option<i16>, y0
1517
let y0 = y0.unwrap_or(0);
1618
let x1 = x1.unwrap_or(0);
1719
let y1 = y1.unwrap_or(0);
20+
let (x0, x1) = (x0.min(x1), x0.max(x1));
21+
let (y0, y1) = (y0.min(y1), y0.max(y1));
1822

1923
rt.memory.painter().paint_range(x0..=x1, y0..=y1);
2024
}
25+
26+
#[api_callback]
27+
pub fn rect<A: Allocator + 'static>(rt: &mut Runtime<A>, x0: Option<i16>, y0: Option<i16>, x1: Option<i16>, y1: Option<i16>, col: Option<P8Num>) {
28+
if let Some(col) = col { rt.memory.machine_state().set_pen_color(col); }
29+
let x0 = x0.unwrap_or(0);
30+
let y0 = y0.unwrap_or(0);
31+
let x1 = x1.unwrap_or(0);
32+
let y1 = y1.unwrap_or(0);
33+
let (x0, x1) = (x0.min(x1), x0.max(x1));
34+
let (y0, y1) = (y0.min(y1), y0.max(y1));
35+
36+
let mut painter = rt.memory.painter();
37+
painter.paint_range(x0..=x1, y0..=y0);
38+
painter.paint_range(x0..=x1, y1..=y1);
39+
painter.paint_range(x0..=x0, y0..=y1);
40+
painter.paint_range(x1..=x1, y0..=y1);
41+
}
42+
43+

p8rs/src/vm/api/gfx.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use p8rs_macros::{api_callback, p8};
33
use p8rs_piccolo::{Context, Value};
44
use p8rs_types::p8num::P8Num;
55

6-
use crate::vm::memory::machine_state::Palette;
6+
use crate::vm::memory::machine_state::{FillPatternFlags, FillPatternState, Palette};
77
use crate::vm::Runtime;
88

99
pub fn install_pico8_gfx<A: Allocator + 'static>(ctx: Context) {
@@ -13,6 +13,7 @@ pub fn install_pico8_gfx<A: Allocator + 'static>(ctx: Context) {
1313
ctx.set_global("pal", pal::callback::<A>(ctx));
1414
ctx.set_global("cls", cls::callback::<A>(ctx));
1515
ctx.set_global("cursor", cursor::callback::<A>(ctx));
16+
ctx.set_global("fillp", fillp::callback::<A>(ctx));
1617
}
1718

1819
#[api_callback]
@@ -114,4 +115,15 @@ pub fn pal<'gc, A: Allocator + 'static>(rt: &mut Runtime<A>, c0: Option<Value<'g
114115
let k = k.to_integer().cast_unsigned() as usize % 16;
115116
pal[k] = v.to_integer() as u8;
116117
}
118+
}
119+
120+
#[api_callback]
121+
pub fn fillp<'gc, A: Allocator + 'static>(rt: &mut Runtime<A>, pat: Option<P8Num>) {
122+
let pat = pat.unwrap().saturating_add(P8Num::ZERO);
123+
let flags = (pat.to_raw() >> 8) as u8;
124+
let flags = flags.reverse_bits() & 0b0000_0111;
125+
let flags = FillPatternFlags::from_bits_retain(flags);
126+
let pattern = pat.to_integer().cast_unsigned();
127+
128+
*rt.memory.machine_state().fill_pattern() = FillPatternState::new(pattern, flags);
117129
}

p8rs/src/vm/memory/machine_state.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,16 +369,20 @@ impl FillPatternState {
369369
u16::from_le_bytes([self.0[0], self.0[1]])
370370
}
371371

372-
pub fn expand(&self) -> [[bool; 4]; 4] {
372+
pub fn expand(&self) -> Option<[[bool; 4]; 4]> {
373373
let lo = self.0[0];
374374
let hi = self.0[1];
375375

376-
[
377-
[hi & 1 << 7 != 0, hi & 1 << 6 != 0, hi & 1 << 5 != 0, hi & 1 << 4 != 0],
378-
[hi & 1 << 3 != 0, hi & 1 << 2 != 0, hi & 1 << 1 != 0, hi & 1 << 0 != 0],
379-
[lo & 1 << 7 != 0, lo & 1 << 6 != 0, lo & 1 << 5 != 0, lo & 1 << 4 != 0],
380-
[lo & 1 << 3 != 0, lo & 1 << 2 != 0, lo & 1 << 1 != 0, lo & 1 << 0 != 0],
381-
]
376+
if lo == 0 && hi == 0 {
377+
None
378+
} else {
379+
Some([
380+
[hi & 1 << 7 != 0, hi & 1 << 6 != 0, hi & 1 << 5 != 0, hi & 1 << 4 != 0],
381+
[hi & 1 << 3 != 0, hi & 1 << 2 != 0, hi & 1 << 1 != 0, hi & 1 << 0 != 0],
382+
[lo & 1 << 7 != 0, lo & 1 << 6 != 0, lo & 1 << 5 != 0, lo & 1 << 4 != 0],
383+
[lo & 1 << 3 != 0, lo & 1 << 2 != 0, lo & 1 << 1 != 0, lo & 1 << 0 != 0],
384+
])
385+
}
382386
}
383387
}
384388

@@ -392,9 +396,9 @@ bitflags! {
392396
/// Enables transparency
393397
const TRANSPARENT = 1 << 0;
394398
/// When drawing sprites, fill pattern will determine nibble of secondary palette to use
395-
const SPRITES_REMAP = 1 << 1;
399+
const REMAP_SPRITES = 1 << 1;
396400
/// All drawing functions that accept fill pattern will use it to determine nibble of secondary palette to use
397-
const ALL_REMAP = 1 << 2;
401+
const REMAP_ALL = 1 << 2;
398402

399403
const _ = !0;
400404
}

p8rs/src/vm/memory/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use core::alloc::Allocator;
22
use core::ops::{Deref, DerefMut};
33
use alloc::boxed::Box;
44
use alloc::alloc::Global;
5+
use core::fmt::{Debug, Formatter};
56
use p8rs_types::p8num::P8Num;
67
use p8rs_macros::TransparentRef;
78

@@ -22,9 +23,9 @@ use music::Music;
2223
use sound_effects::SoundEffects;
2324
use machine_state::MachineState;
2425
use screen::Screen;
25-
use crate::vm::memory::painter::Painter;
26+
use painter::Painter;
2627

27-
#[derive(Debug, Clone, TransparentRef)]
28+
#[derive(Clone, TransparentRef)]
2829
#[repr(transparent)]
2930
pub struct Memory([u8; 0x10000]);
3031

@@ -145,6 +146,19 @@ impl DerefMut for Memory {
145146
}
146147
}
147148

149+
impl Debug for Memory {
150+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
151+
f.write_str("Memory[64KB]")
152+
}
153+
}
154+
155+
#[cfg(feature = "defmt")]
156+
impl defmt::Format for Memory {
157+
fn format(&self, fmt: defmt::Formatter) {
158+
defmt::write!(fmt, "Memory[64KB]");
159+
}
160+
}
161+
148162
pub trait Serializable {
149163
fn read<const S: usize>(mem: &[u8; S], addr: u16) -> Self;
150164
fn write<const S: usize>(self, mem: &mut [u8; S], addr: u16);

p8rs/src/vm/memory/painter.rs

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
use core::ops::{Range, RangeInclusive};
2-
use crate::vm::memory::machine_state::FillPatternFlags;
1+
use core::ops::{Not, Range, RangeInclusive};
2+
3+
use crate::vm::memory::machine_state::{FillPatternFlags, Palette};
34
use crate::vm::memory::Memory;
45

56
#[derive(Debug)]
7+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68
pub struct Painter<'a> {
79
memory: &'a mut Memory,
810
fg: u8,
9-
_bg: u8,
11+
bg: Option<u8>,
1012
clip_x: Range<i16>,
1113
clip_y: Range<i16>,
1214
camera: Vector<i16>,
13-
_fill: [[bool; 4]; 4],
14-
_fill_flags: FillPatternFlags,
15+
fill: Option<[[bool; 4]; 4]>,
1516
}
1617

1718
impl Painter<'_> {
@@ -21,27 +22,46 @@ impl Painter<'_> {
2122
let camera = memory.machine_state().get_camera_position();
2223
let fill = memory.machine_state().fill_pattern().expand();
2324
let fill_flags = *memory.machine_state().fill_pattern().flags();
24-
let fg = pen_color & 0xF;
25-
let bg = pen_color >> 4;
25+
26+
let (fg, bg) = if fill_flags.contains(FillPatternFlags::REMAP_ALL) {
27+
let pal = *memory.machine_state().palette(Palette::Secondary);
28+
let color = pal[(pen_color & 0xF) as usize];
29+
30+
(color & 0x0F, color >> 4)
31+
} else {
32+
let pal = *memory.machine_state().palette(Palette::Draw);
33+
let fg = pal[(pen_color & 0xF) as usize];
34+
let bg = pal[(pen_color >> 4) as usize];
35+
36+
(fg, bg)
37+
};
38+
39+
let bg = fill_flags.contains(FillPatternFlags::TRANSPARENT)
40+
.not()
41+
.then_some(bg);
2642

2743
Painter {
2844
memory,
2945
fg,
30-
_bg: bg,
46+
bg,
3147
clip_x: clip[0] as i16 .. clip[2].min(128) as i16,
3248
clip_y: clip[1] as i16 .. clip[3].min(128) as i16,
3349
camera: Vector::from(camera),
34-
_fill: fill,
35-
_fill_flags: fill_flags,
50+
fill,
3651
}
3752
}
3853

3954
pub fn paint(&mut self, x: i16, y: i16) {
40-
let x = x.saturating_add(self.camera.x);
41-
let y = y.saturating_add(self.camera.y);
55+
let x = x.saturating_sub(self.camera.x);
56+
let y = y.saturating_sub(self.camera.y);
57+
58+
self.paint_abs_impl(x, y);
59+
}
60+
61+
pub fn paint_abs(mut self, x: i16, y: i16) {
4262
if !self.clip_x.contains(&x) || !self.clip_y.contains(&y) { return }
4363

44-
self.paint_abs(x, y);
64+
self.paint_abs_impl(x, y);
4565
}
4666

4767
pub fn paint_range(&mut self, x: impl IntoClip, y: impl IntoClip) {
@@ -51,13 +71,23 @@ impl Painter<'_> {
5171

5272
for y in y {
5373
for x in x.clone() {
54-
self.paint_abs(x, y);
74+
self.paint_abs_impl(x, y);
5575
}
5676
}
5777
}
5878

59-
fn paint_abs(&mut self, x: i16, y: i16) {
60-
self.memory.screen().set_pixel(x, y, self.fg);
79+
fn paint_abs_impl(&mut self, x: i16, y: i16) {
80+
let col = if let Some(fill) = self.fill && fill[y as usize % 4][x as usize % 4] {
81+
if let Some(bg) = self.bg {
82+
bg
83+
} else {
84+
return;
85+
}
86+
} else {
87+
self.fg
88+
};
89+
90+
self.memory.screen().set_pixel(x, y, col);
6191
}
6292
}
6393

@@ -86,17 +116,17 @@ macro_rules! impl_clip {
86116
($typ:ty $(, $rest:ty )*) => {
87117
impl IntoClip for Range<$typ> {
88118
fn into_clip(self, camera: i16) -> Range<i16> {
89-
let start = (self.start as i16).saturating_add(camera);
90-
let end = (self.end as i16).saturating_add(camera);
119+
let start = (self.start as i16).saturating_sub(camera);
120+
let end = (self.end as i16).saturating_sub(camera);
91121

92122
start..end
93123
}
94124
}
95125

96126
impl IntoClip for RangeInclusive<$typ> {
97127
fn into_clip(self, camera: i16) -> Range<i16> {
98-
let start = (*self.start() as i16).saturating_add(camera);
99-
let end = (*self.end() as i16).saturating_add(camera).saturating_add(1);
128+
let start = (*self.start() as i16).saturating_sub(camera);
129+
let end = (*self.end() as i16).saturating_sub(camera).saturating_add(1);
100130

101131
start..end
102132
}

0 commit comments

Comments
 (0)