implements PLN-0023
This commit is contained in:
parent
dd90ff812c
commit
cc700c6cf8
@ -51,14 +51,30 @@ impl HardwareBridge for Hardware {
|
|||||||
self.frame_composer.begin_frame();
|
self.frame_composer.begin_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_sprite(&mut self, sprite: Sprite) {
|
fn bind_scene(&mut self, scene_bank_id: usize) -> bool {
|
||||||
let _ = self.frame_composer.emit_sprite(sprite);
|
self.frame_composer.bind_scene(scene_bank_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind_scene(&mut self) {
|
||||||
|
self.frame_composer.unbind_scene();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_camera(&mut self, x: i32, y: i32) {
|
||||||
|
self.frame_composer.set_camera(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_sprite(&mut self, sprite: Sprite) -> bool {
|
||||||
|
self.frame_composer.emit_sprite(sprite)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_frame(&mut self) {
|
fn render_frame(&mut self) {
|
||||||
self.frame_composer.render_frame(&mut self.gfx);
|
self.frame_composer.render_frame(&mut self.gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_glyph_bank(&self, bank_id: usize) -> bool {
|
||||||
|
self.gfx.glyph_banks.glyph_bank_slot(bank_id).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
fn gfx(&self) -> &dyn GfxBridge {
|
fn gfx(&self) -> &dyn GfxBridge {
|
||||||
&self.gfx
|
&self.gfx
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,12 @@ use crate::touch_bridge::TouchBridge;
|
|||||||
|
|
||||||
pub trait HardwareBridge {
|
pub trait HardwareBridge {
|
||||||
fn begin_frame(&mut self);
|
fn begin_frame(&mut self);
|
||||||
fn emit_sprite(&mut self, sprite: Sprite);
|
fn bind_scene(&mut self, scene_bank_id: usize) -> bool;
|
||||||
|
fn unbind_scene(&mut self);
|
||||||
|
fn set_camera(&mut self, x: i32, y: i32);
|
||||||
|
fn emit_sprite(&mut self, sprite: Sprite) -> bool;
|
||||||
fn render_frame(&mut self);
|
fn render_frame(&mut self);
|
||||||
|
fn has_glyph_bank(&self, bank_id: usize) -> bool;
|
||||||
|
|
||||||
fn gfx(&self) -> &dyn GfxBridge;
|
fn gfx(&self) -> &dyn GfxBridge;
|
||||||
fn gfx_mut(&mut self) -> &mut dyn GfxBridge;
|
fn gfx_mut(&mut self) -> &mut dyn GfxBridge;
|
||||||
|
|||||||
@ -4,11 +4,14 @@ use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
|||||||
use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef};
|
use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef};
|
||||||
use prometeu_hal::cartridge::AppMode;
|
use prometeu_hal::cartridge::AppMode;
|
||||||
use prometeu_hal::color::Color;
|
use prometeu_hal::color::Color;
|
||||||
|
use prometeu_hal::glyph::Glyph;
|
||||||
use prometeu_hal::log::{LogLevel, LogSource};
|
use prometeu_hal::log::{LogLevel, LogSource};
|
||||||
|
use prometeu_hal::sprite::Sprite;
|
||||||
use prometeu_hal::syscalls::Syscall;
|
use prometeu_hal::syscalls::Syscall;
|
||||||
use prometeu_hal::vm_fault::VmFault;
|
use prometeu_hal::vm_fault::VmFault;
|
||||||
use prometeu_hal::{
|
use prometeu_hal::{
|
||||||
AudioOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, expect_int,
|
AudioOpStatus, ComposerOpStatus, HostContext, HostReturn, NativeInterface, SyscallId,
|
||||||
|
expect_bool, expect_int,
|
||||||
};
|
};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
@ -53,6 +56,23 @@ impl VirtualMachineRuntime {
|
|||||||
pub(crate) fn get_color(&self, value: i64) -> Color {
|
pub(crate) fn get_color(&self, value: i64) -> Color {
|
||||||
Color::from_raw(value as u16)
|
Color::from_raw(value as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn int_arg_to_usize_status(value: i64) -> Result<usize, ComposerOpStatus> {
|
||||||
|
usize::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_arg_to_i32_trap(value: i64, name: &str) -> Result<i32, VmFault> {
|
||||||
|
i32::try_from(value)
|
||||||
|
.map_err(|_| VmFault::Trap(TRAP_OOB, format!("{name} value out of bounds")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_arg_to_u8_status(value: i64) -> Result<u8, ComposerOpStatus> {
|
||||||
|
u8::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_arg_to_u16_status(value: i64) -> Result<u16, ComposerOpStatus> {
|
||||||
|
u16::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeInterface for VirtualMachineRuntime {
|
impl NativeInterface for VirtualMachineRuntime {
|
||||||
@ -148,13 +168,100 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Syscall::ComposerBindScene
|
Syscall::ComposerBindScene => {
|
||||||
| Syscall::ComposerUnbindScene
|
let scene_bank_id = match Self::int_arg_to_usize_status(expect_int(args, 0)?) {
|
||||||
| Syscall::ComposerSetCamera
|
Ok(id) => id,
|
||||||
| Syscall::ComposerEmitSprite => Err(VmFault::Trap(
|
Err(status) => {
|
||||||
TRAP_INVALID_SYSCALL,
|
ret.push_int(status as i64);
|
||||||
"Composer syscall support is not implemented yet".into(),
|
return Ok(());
|
||||||
)),
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = if hw.bind_scene(scene_bank_id) {
|
||||||
|
ComposerOpStatus::Ok
|
||||||
|
} else {
|
||||||
|
ComposerOpStatus::SceneUnavailable
|
||||||
|
};
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Syscall::ComposerUnbindScene => {
|
||||||
|
hw.unbind_scene();
|
||||||
|
ret.push_int(ComposerOpStatus::Ok as i64);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Syscall::ComposerSetCamera => {
|
||||||
|
let x = Self::int_arg_to_i32_trap(expect_int(args, 0)?, "camera x")?;
|
||||||
|
let y = Self::int_arg_to_i32_trap(expect_int(args, 1)?, "camera y")?;
|
||||||
|
hw.set_camera(x, y);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Syscall::ComposerEmitSprite => {
|
||||||
|
let glyph_id = match Self::int_arg_to_u16_status(expect_int(args, 0)?) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(status) => {
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let palette_id = match Self::int_arg_to_u8_status(expect_int(args, 1)?) {
|
||||||
|
Ok(value) if value < 64 => value,
|
||||||
|
_ => {
|
||||||
|
ret.push_int(ComposerOpStatus::ArgRangeInvalid as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let x = Self::int_arg_to_i32_trap(expect_int(args, 2)?, "sprite x")?;
|
||||||
|
let y = Self::int_arg_to_i32_trap(expect_int(args, 3)?, "sprite y")?;
|
||||||
|
let layer = match Self::int_arg_to_u8_status(expect_int(args, 4)?) {
|
||||||
|
Ok(value) if value < 4 => value,
|
||||||
|
Ok(_) => {
|
||||||
|
ret.push_int(ComposerOpStatus::LayerInvalid as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(status) => {
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let bank_id = match Self::int_arg_to_u8_status(expect_int(args, 5)?) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(status) => {
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let flip_x = expect_bool(args, 6)?;
|
||||||
|
let flip_y = expect_bool(args, 7)?;
|
||||||
|
let priority = match Self::int_arg_to_u8_status(expect_int(args, 8)?) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(status) => {
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !hw.has_glyph_bank(bank_id as usize) {
|
||||||
|
ret.push_int(ComposerOpStatus::BankInvalid as i64);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let emitted = hw.emit_sprite(Sprite {
|
||||||
|
glyph: Glyph { glyph_id, palette_id },
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
layer,
|
||||||
|
bank_id,
|
||||||
|
active: false,
|
||||||
|
flip_x,
|
||||||
|
flip_y,
|
||||||
|
priority,
|
||||||
|
});
|
||||||
|
let status =
|
||||||
|
if emitted { ComposerOpStatus::Ok } else { ComposerOpStatus::SpriteOverflow };
|
||||||
|
ret.push_int(status as i64);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Syscall::AudioPlaySample => {
|
Syscall::AudioPlaySample => {
|
||||||
let sample_id_raw = expect_int(args, 0)?;
|
let sample_id_raw = expect_int(args, 0)?;
|
||||||
let voice_id_raw = expect_int(args, 1)?;
|
let voice_id_raw = expect_int(args, 1)?;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use prometeu_bytecode::model::{BytecodeModule, ConstantPoolEntry, FunctionMeta,
|
|||||||
use prometeu_drivers::hardware::Hardware;
|
use prometeu_drivers::hardware::Hardware;
|
||||||
use prometeu_drivers::{GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolInstaller};
|
use prometeu_drivers::{GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolInstaller};
|
||||||
use prometeu_hal::AudioOpStatus;
|
use prometeu_hal::AudioOpStatus;
|
||||||
use prometeu_hal::GfxOpStatus;
|
use prometeu_hal::ComposerOpStatus;
|
||||||
use prometeu_hal::InputSignals;
|
use prometeu_hal::InputSignals;
|
||||||
use prometeu_hal::asset::{
|
use prometeu_hal::asset::{
|
||||||
AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus,
|
AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus,
|
||||||
@ -298,6 +298,62 @@ fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
|||||||
assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw());
|
assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_renders_scene_through_public_composer_syscalls() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble(
|
||||||
|
"PUSH_I32 0\nHOSTCALL 0\nPOP_N 1\n\
|
||||||
|
PUSH_I32 0\nPUSH_I32 0\nHOSTCALL 1\n\
|
||||||
|
PUSH_I32 0\nPUSH_I32 2\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 2\n\
|
||||||
|
FRAME_SYNC\nHALT",
|
||||||
|
)
|
||||||
|
.expect("assemble");
|
||||||
|
let program = serialized_single_function_module(
|
||||||
|
code,
|
||||||
|
vec![
|
||||||
|
SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "bind_scene".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 1,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "set_camera".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 2,
|
||||||
|
ret_slots: 0,
|
||||||
|
},
|
||||||
|
SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "emit_sprite".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 9,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||||
|
|
||||||
|
let banks = Arc::new(MemoryBanks::new());
|
||||||
|
banks.install_glyph_bank(0, Arc::new(runtime_test_glyph_bank(TileSize::Size8, 2, Color::BLUE)));
|
||||||
|
banks.install_scene_bank(0, Arc::new(runtime_test_scene(0, 2, TileSize::Size8)));
|
||||||
|
let mut hardware = Hardware::new_with_memory_banks(Arc::clone(&banks));
|
||||||
|
hardware.gfx.scene_fade_level = 31;
|
||||||
|
hardware.gfx.hud_fade_level = 31;
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
|
|
||||||
|
assert!(report.is_none(), "public composer path must not crash");
|
||||||
|
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::Ok as i64)]);
|
||||||
|
hardware.gfx.present();
|
||||||
|
assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn initialize_vm_success_clears_previous_crash_report() {
|
fn initialize_vm_success_clears_previous_crash_report() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
@ -429,22 +485,19 @@ fn initialize_vm_failure_clears_previous_identity_and_handles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_gfx_set_sprite_operational_error_returns_status_not_crash() {
|
fn tick_composer_bind_scene_operational_error_returns_status_not_crash() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let mut hardware = Hardware::new();
|
let mut hardware = Hardware::new();
|
||||||
let signals = InputSignals::default();
|
let signals = InputSignals::default();
|
||||||
let code = assemble(
|
let code = assemble("PUSH_I32 99\nHOSTCALL 0\nHALT").expect("assemble");
|
||||||
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
|
||||||
)
|
|
||||||
.expect("assemble");
|
|
||||||
let program = serialized_single_function_module(
|
let program = serialized_single_function_module(
|
||||||
code,
|
code,
|
||||||
vec![SyscallDecl {
|
vec![SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "bind_scene".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 1,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
@ -454,26 +507,29 @@ fn tick_gfx_set_sprite_operational_error_returns_status_not_crash() {
|
|||||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
assert!(report.is_none(), "operational error must not crash");
|
assert!(report.is_none(), "operational error must not crash");
|
||||||
assert!(vm.is_halted());
|
assert!(vm.is_halted());
|
||||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::BankInvalid as i64)]);
|
assert_eq!(
|
||||||
|
vm.operand_stack_top(1),
|
||||||
|
vec![Value::Int64(ComposerOpStatus::SceneUnavailable as i64)]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_gfx_set_sprite_invalid_index_returns_status_not_crash() {
|
fn tick_composer_emit_sprite_operational_error_returns_status_not_crash() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let mut hardware = Hardware::new();
|
let mut hardware = Hardware::new();
|
||||||
let signals = InputSignals::default();
|
let signals = InputSignals::default();
|
||||||
let code = assemble(
|
let code = assemble(
|
||||||
"PUSH_I32 0\nPUSH_I32 512\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
||||||
)
|
)
|
||||||
.expect("assemble");
|
.expect("assemble");
|
||||||
let program = serialized_single_function_module(
|
let program = serialized_single_function_module(
|
||||||
code,
|
code,
|
||||||
vec![SyscallDecl {
|
vec![SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "emit_sprite".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
@ -481,28 +537,57 @@ fn tick_gfx_set_sprite_invalid_index_returns_status_not_crash() {
|
|||||||
|
|
||||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
assert!(report.is_none(), "invalid sprite index must not crash");
|
assert!(report.is_none(), "operational error must not crash");
|
||||||
assert!(vm.is_halted());
|
assert!(vm.is_halted());
|
||||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::InvalidSpriteIndex as i64)]);
|
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::BankInvalid as i64)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() {
|
fn tick_composer_emit_sprite_invalid_layer_returns_status_not_crash() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let mut hardware = Hardware::new();
|
let mut hardware = Hardware::new();
|
||||||
let signals = InputSignals::default();
|
let signals = InputSignals::default();
|
||||||
let code = assemble(
|
let code = assemble(
|
||||||
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 64\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 4\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
||||||
)
|
)
|
||||||
.expect("assemble");
|
.expect("assemble");
|
||||||
let program = serialized_single_function_module(
|
let program = serialized_single_function_module(
|
||||||
code,
|
code,
|
||||||
vec![SyscallDecl {
|
vec![SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "emit_sprite".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 9,
|
||||||
|
ret_slots: 1,
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
|
assert!(report.is_none(), "invalid layer must not crash");
|
||||||
|
assert!(vm.is_halted());
|
||||||
|
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(ComposerOpStatus::LayerInvalid as i64)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_composer_emit_sprite_invalid_range_returns_status_not_crash() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hardware = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let code = assemble(
|
||||||
|
"PUSH_I32 0\nPUSH_I32 64\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
||||||
|
)
|
||||||
|
.expect("assemble");
|
||||||
|
let program = serialized_single_function_module(
|
||||||
|
code,
|
||||||
|
vec![SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "emit_sprite".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
@ -517,9 +602,12 @@ fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() {
|
|||||||
|
|
||||||
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
assert!(report.is_none(), "invalid gfx parameter range must not crash");
|
assert!(report.is_none(), "invalid composer parameter range must not crash");
|
||||||
assert!(vm.is_halted());
|
assert!(vm.is_halted());
|
||||||
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::ArgRangeInvalid as i64)]);
|
assert_eq!(
|
||||||
|
vm.operand_stack_top(1),
|
||||||
|
vec![Value::Int64(ComposerOpStatus::ArgRangeInvalid as i64)]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -881,13 +969,13 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() {
|
fn tick_status_first_surface_smoke_across_composer_audio_and_asset() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let mut hardware = Hardware::new();
|
let mut hardware = Hardware::new();
|
||||||
let signals = InputSignals::default();
|
let signals = InputSignals::default();
|
||||||
let code = assemble(
|
let code = assemble(
|
||||||
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\n\
|
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\n\
|
||||||
PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 1\n\
|
PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 1\n\
|
||||||
PUSH_I32 999\nPUSH_I32 0\nHOSTCALL 2\n\
|
PUSH_I32 999\nPUSH_I32 0\nHOSTCALL 2\n\
|
||||||
HALT"
|
HALT"
|
||||||
@ -897,10 +985,10 @@ fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() {
|
|||||||
code,
|
code,
|
||||||
vec![
|
vec![
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "emit_sprite".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
},
|
},
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
@ -931,28 +1019,28 @@ fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() {
|
|||||||
Value::Int64(0),
|
Value::Int64(0),
|
||||||
Value::Int64(AssetLoadError::AssetNotFound as i64),
|
Value::Int64(AssetLoadError::AssetNotFound as i64),
|
||||||
Value::Int64(AudioOpStatus::BankInvalid as i64),
|
Value::Int64(AudioOpStatus::BankInvalid as i64),
|
||||||
Value::Int64(GfxOpStatus::BankInvalid as i64),
|
Value::Int64(ComposerOpStatus::BankInvalid as i64),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tick_gfx_set_sprite_type_mismatch_surfaces_trap_not_panic() {
|
fn tick_composer_emit_sprite_type_mismatch_surfaces_trap_not_panic() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
let mut vm = VirtualMachine::default();
|
let mut vm = VirtualMachine::default();
|
||||||
let mut hardware = Hardware::new();
|
let mut hardware = Hardware::new();
|
||||||
let signals = InputSignals::default();
|
let signals = InputSignals::default();
|
||||||
let code = assemble(
|
let code = assemble(
|
||||||
"PUSH_BOOL 1\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
"PUSH_BOOL 1\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT",
|
||||||
)
|
)
|
||||||
.expect("assemble");
|
.expect("assemble");
|
||||||
let program = serialized_single_function_module(
|
let program = serialized_single_function_module(
|
||||||
code,
|
code,
|
||||||
vec![SyscallDecl {
|
vec![SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "emit_sprite".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user