mirror of
https://github.com/birth1mark/orbis8.git
synced 2026-10-28 23:00:29 +01:00
1008 lines
34 KiB
C++
1008 lines
34 KiB
C++
#include <string>
|
|
#include <vector>
|
|
|
|
#include "doctest.h"
|
|
|
|
#include "../source/vm.h"
|
|
#include "../source/hostVmShared.h"
|
|
#include "../source/host.h"
|
|
#include "stubhost.h"
|
|
|
|
#include "../source/fontdata.h"
|
|
|
|
|
|
TEST_CASE("Vm memory functions") {
|
|
StubHost* stubHost = new StubHost();
|
|
PicoRam* memory = new PicoRam();
|
|
memory->Reset();
|
|
Graphics* graphics = new Graphics(get_font_data(), memory);
|
|
Input* input = new Input(memory);
|
|
Audio* audio = new Audio(memory);
|
|
|
|
Vm* vm = new Vm(stubHost, memory, graphics, input, audio);
|
|
|
|
SUBCASE("memory data stats with 0"){
|
|
CHECK(memory->data[0] == 0);
|
|
}
|
|
SUBCASE("resetting memory zeroes out everything except general use, color bitmask, screen data mapping, and map width"){
|
|
for(int i = 0; i < 0x10000; ++i) {
|
|
memory->data[i] = i & 255;
|
|
}
|
|
|
|
memory->Reset();
|
|
|
|
bool correctValues = true;
|
|
for(int i = 0; i < 0x8000; ++i) {
|
|
if (i == 0x5f5e) {
|
|
correctValues &= memory->data[i] == 255;
|
|
}
|
|
else if (i == 0x5f55) {
|
|
correctValues &= memory->data[i] == 0x60;
|
|
}
|
|
else if (i == 0x5f56) {
|
|
correctValues &= memory->data[i] == 0x20;
|
|
}
|
|
else if (i == 0x5f57) {
|
|
correctValues &= memory->data[i] == 128;
|
|
}
|
|
else if (i < 0x4300 || i > 0x5eff) {
|
|
correctValues &= memory->data[i] == 0;
|
|
}
|
|
}
|
|
|
|
CHECK(correctValues);
|
|
}
|
|
SUBCASE("size of PicoRam struct corrct"){
|
|
CHECK(sizeof(PicoRam) == 0x10000);
|
|
}
|
|
SUBCASE("size of data correct"){
|
|
CHECK(sizeof(memory->data) == 0x10000);
|
|
}
|
|
SUBCASE("simple peek and poke"){
|
|
vm->vm_poke(2, 232);
|
|
|
|
CHECK(vm->vm_peek(2) == 232);
|
|
}
|
|
SUBCASE("simple peek2 and poke2"){
|
|
vm->vm_poke2(41, -1031);
|
|
|
|
CHECK(vm->vm_peek2(41) == -1031);
|
|
}
|
|
SUBCASE("simple peek4 and poke4"){
|
|
vm->vm_poke4(0x7123, 3584.0059);
|
|
|
|
CHECK(vm->vm_peek4(0x7123).bits() == ((z8::fix32)3584.0059).bits());
|
|
}
|
|
SUBCASE("poking spritesheet"){
|
|
//147: 1001 0011
|
|
//left pixel: 0011 = 3
|
|
//right pixel: 1001 = 9
|
|
vm->vm_poke(0, 147);
|
|
|
|
CHECK_EQ(graphics->sget(0, 0), 3);
|
|
CHECK_EQ(graphics->sget(1, 0), 9);
|
|
}
|
|
SUBCASE("poking map"){
|
|
vm->vm_poke(0x2000, 201);
|
|
vm->vm_poke(0x2003, 11);
|
|
|
|
CHECK_EQ(graphics->mget(0, 0), 201);
|
|
CHECK_EQ(graphics->mget(3, 0), 11);
|
|
}
|
|
SUBCASE("poking sprite flags"){
|
|
//39: 0010 0111
|
|
vm->vm_poke(0x3000, 39);
|
|
|
|
CHECK_EQ(graphics->fget(0), 39);
|
|
CHECK_EQ(graphics->fget(0, 0), true);
|
|
CHECK_EQ(graphics->fget(0, 3), false);
|
|
}
|
|
SUBCASE("poking music"){
|
|
//78: 0100 1110
|
|
vm->vm_poke(0x3100, 78);
|
|
|
|
CHECK_EQ(memory->songs[0].getSfx0(), 78);
|
|
CHECK_EQ(memory->songs[0].getStart(), 0);
|
|
}
|
|
SUBCASE("poking sfx"){
|
|
//97: 0110 0001
|
|
//key is first 6 bits: 10 0001 : 33
|
|
//waveform is next 3: (0) 01 : 1
|
|
vm->vm_poke(0x3200, 97);
|
|
|
|
CHECK_EQ(memory->sfx[0].notes[0].getKey(), 33);
|
|
CHECK_EQ(memory->sfx[0].notes[0].getWaveform(), 1);
|
|
}
|
|
SUBCASE("poking general use ram"){
|
|
vm->vm_poke(0x4300, 215);
|
|
|
|
CHECK_EQ(memory->generalUseRam[0], 215);
|
|
}
|
|
SUBCASE("setting cart data"){
|
|
vm->vm_cartdata("dummy");
|
|
vm->vm_dset(13, 56);
|
|
|
|
CHECK_EQ(vm->vm_dget(13), (fix32)56);
|
|
}
|
|
SUBCASE("poking cart data"){
|
|
//need to poke after cartdata is called to set the current key, otherwise it will get wiped
|
|
vm->vm_cartdata("dummy");
|
|
vm->vm_poke(0x5e02, 56);
|
|
|
|
CHECK_EQ(memory->cartData[2], 56);
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)56);
|
|
}
|
|
SUBCASE("poking and peeking cart data"){
|
|
vm->vm_cartdata("dummy");
|
|
vm->vm_poke4(0x5e80, (fix32)133);
|
|
|
|
CHECK_EQ(vm->vm_peek4(0x5e80), (fix32)133);
|
|
CHECK_EQ(vm->vm_dget(32), (fix32)133);
|
|
}
|
|
SUBCASE("poking draw palette data"){
|
|
//21 : 0001 0101 (transparet: true) (color mapped 5)
|
|
vm->vm_poke(0x5f02, 21);
|
|
|
|
CHECK_EQ(memory->drawState.drawPaletteMap[2], 21);
|
|
CHECK_EQ(graphics->getDrawPalMappedColor(2), 5);
|
|
CHECK_EQ(graphics->isColorTransparent(2), true);
|
|
}
|
|
SUBCASE("poking clip data"){
|
|
//7 : 0000 0111
|
|
vm->vm_poke(0x5f21, 89);
|
|
|
|
CHECK_EQ(memory->drawState.clip_yb, 89);
|
|
}
|
|
SUBCASE("poking unknown ram at 0x5f24"){
|
|
//7 : 0000 0111
|
|
vm->vm_poke(0x5f24, 254);
|
|
|
|
CHECK_EQ(memory->drawState.unknown05f24, 254);
|
|
}
|
|
SUBCASE("poking color"){
|
|
vm->vm_poke(0x5f25, 12);
|
|
|
|
CHECK_EQ(memory->drawState.color, 12);
|
|
}
|
|
SUBCASE("poking text cursor"){
|
|
vm->vm_poke(0x5f26, 74);
|
|
|
|
CHECK_EQ(memory->drawState.text_x, 74);
|
|
}
|
|
SUBCASE("poking camera position"){
|
|
vm->vm_poke2(0x5f2a, -72);
|
|
|
|
CHECK_EQ(memory->drawState.camera_y, -72);
|
|
}
|
|
SUBCASE("poking draw mode"){
|
|
vm->vm_poke(0x5f2c, 2);
|
|
|
|
CHECK_EQ(memory->drawState.drawMode, 2);
|
|
}
|
|
SUBCASE("poking dev kit mode"){
|
|
vm->vm_poke(0x5f2d, 2);
|
|
|
|
CHECK_EQ(memory->drawState.devkitMode, 2);
|
|
}
|
|
SUBCASE("poking persist palette"){
|
|
vm->vm_poke(0x5f2e, 1);
|
|
|
|
CHECK_EQ(memory->drawState.persistPalette, 1);
|
|
}
|
|
SUBCASE("poking sound pause state"){
|
|
vm->vm_poke(0x5f2f, 4);
|
|
|
|
CHECK_EQ(memory->drawState.soundPauseState, 4);
|
|
}
|
|
SUBCASE("poking suppress pause"){
|
|
vm->vm_poke(0x5f30, 1);
|
|
|
|
CHECK_EQ(memory->drawState.suppressPause, 1);
|
|
}
|
|
SUBCASE("poking fill pattern"){
|
|
vm->vm_poke(0x5f31, 24);
|
|
|
|
CHECK_EQ(memory->drawState.fillPattern[0], 24);
|
|
}
|
|
SUBCASE("poking fill pattern transparency bit"){
|
|
vm->vm_poke(0x5f33, 1);
|
|
|
|
CHECK_EQ(memory->drawState.fillPatternTransparencyBit, 1);
|
|
}
|
|
SUBCASE("poking color setting flag"){
|
|
/*
|
|
-- bit 0x1000.0000 means the non-colour bits should be observed
|
|
-- bit 0x0100.0000 transparency bit
|
|
-- bits 0x00FF.0000 are the usual colour bits
|
|
-- bits 0x0000.FFFF are interpreted as the fill pattern
|
|
*/
|
|
vm->vm_poke(0x5f34, 36);
|
|
|
|
CHECK_EQ(memory->drawState.colorSettingFlag, 36);
|
|
}
|
|
SUBCASE("poking line invalid"){
|
|
vm->vm_poke(0x5f35, 1);
|
|
|
|
CHECK_EQ(memory->drawState.lineInvalid, 1);
|
|
}
|
|
SUBCASE("poking tline width"){
|
|
vm->vm_poke(0x5f38, 65);
|
|
|
|
CHECK_EQ(memory->drawState.tlineMapWidth, 65);
|
|
}
|
|
SUBCASE("poking tline height"){
|
|
vm->vm_poke(0x5f39, 66);
|
|
|
|
CHECK_EQ(memory->drawState.tlineMapHeight, 66);
|
|
}
|
|
SUBCASE("poking tline x"){
|
|
vm->vm_poke(0x5f3a, 67);
|
|
|
|
CHECK_EQ(memory->drawState.tlineMapXOffset, 67);
|
|
}
|
|
SUBCASE("poking tline y"){
|
|
vm->vm_poke(0x5f3b, 68);
|
|
|
|
CHECK_EQ(memory->drawState.tlineMapYOffset, 68);
|
|
}
|
|
SUBCASE("poking line x"){
|
|
vm->vm_poke2(0x5f3c, -3);
|
|
|
|
CHECK_EQ(memory->drawState.line_x, -3);
|
|
}
|
|
SUBCASE("poking line y"){
|
|
vm->vm_poke2(0x5f3e, 262);
|
|
|
|
CHECK_EQ(memory->drawState.line_y, 262);
|
|
}
|
|
SUBCASE("poking audio hardware state half rate") {
|
|
vm->vm_poke(0x5f40, 3);
|
|
|
|
CHECK_EQ(memory->hwState.half_rate, 3);
|
|
}
|
|
SUBCASE("poking audio hardware state distort") {
|
|
vm->vm_poke(0x5f42, 5);
|
|
|
|
CHECK_EQ(memory->hwState.distort, 5);
|
|
}
|
|
SUBCASE("poking rng state") {
|
|
//rng gets seeded, we need to zero it out to test this easier
|
|
memory->hwState.rngState[0] = 0;
|
|
vm->vm_poke(0x5f44, 20);
|
|
|
|
CHECK_EQ(memory->hwState.rngState[0], 20);
|
|
}
|
|
SUBCASE("poking button state doesn't affect input") {
|
|
//0000 1010
|
|
vm->vm_poke(0x5f4c, 10);
|
|
|
|
CHECK_EQ(memory->hwState.buttonStates[0], 10);
|
|
CHECK_EQ(input->btn(), 0);
|
|
}
|
|
SUBCASE("poking print attributes") {
|
|
vm->vm_poke(0x5f58, 53);
|
|
|
|
CHECK_EQ(memory->hwState.printAttributes, 53);
|
|
}
|
|
SUBCASE("poking btnp repeat delay") {
|
|
vm->vm_poke(0x5f5c, 10);
|
|
|
|
CHECK_EQ(memory->hwState.btnpRepeatDelay, 10);
|
|
}
|
|
SUBCASE("poking btnp repeat interval") {
|
|
vm->vm_poke(0x5f5d, 25);
|
|
|
|
CHECK_EQ(memory->hwState.btnpRepeatInterval, 25);
|
|
}
|
|
SUBCASE("poking color bitmask") {
|
|
vm->vm_poke(0x5f5e, 42);
|
|
|
|
CHECK_EQ(memory->hwState.colorBitmask, 42);
|
|
}
|
|
SUBCASE("poking alternate palette flag") {
|
|
vm->vm_poke(0x5f5f, 10);
|
|
|
|
CHECK_EQ(memory->hwState.alternatePaletteFlag, 10);
|
|
}
|
|
SUBCASE("poking alternate palette map") {
|
|
vm->vm_poke(0x5f60, 11);
|
|
|
|
CHECK_EQ(memory->hwState.alternatePaletteMap[0], 11);
|
|
}
|
|
SUBCASE("poking alternate palette map") {
|
|
vm->vm_poke(0x5f70, 7);
|
|
|
|
CHECK_EQ(memory->hwState.alternatePaletteScreenLineBitfield[0], 7);
|
|
}
|
|
SUBCASE("poking gpio pins") {
|
|
vm->vm_poke(0x5f80, 192);
|
|
|
|
CHECK_EQ(memory->hwState.gpioPins[0], 192);
|
|
}
|
|
SUBCASE("poking screen data (first two pixels)") {
|
|
//195: 1100 0011 (left pixel 3, right pixel 12)
|
|
vm->vm_poke(0x6000, 195);
|
|
|
|
CHECK_EQ(memory->screenBuffer[0], 195);
|
|
CHECK_EQ(graphics->pget(0, 0), 3);
|
|
CHECK_EQ(graphics->pget(1, 0), 12);
|
|
}
|
|
SUBCASE("poking screen data (last two pixels)") {
|
|
//210: 1101 0010
|
|
vm->vm_poke(0x7fff, 210);
|
|
|
|
CHECK_EQ(memory->screenBuffer[8191], 210);
|
|
CHECK_EQ(graphics->pget(126, 127), 2);
|
|
CHECK_EQ(graphics->pget(127, 127), 13);
|
|
}
|
|
SUBCASE("poking user data (first byte)") {
|
|
vm->vm_poke(0x8000, 129);
|
|
|
|
CHECK_EQ(memory->data[0x8000], 129);
|
|
CHECK_EQ(memory->userData[0], 129);
|
|
}
|
|
SUBCASE("poking user data (last byte)") {
|
|
vm->vm_poke(0xffff, 211);
|
|
|
|
CHECK_EQ(memory->data[0xffff], 211);
|
|
CHECK_EQ(memory->userData[32767], 211);
|
|
}
|
|
SUBCASE("Loading cart copies cart rom to memory") {
|
|
vm->LoadCart("cartparsetest.p8.png");
|
|
|
|
SUBCASE("Gfx data is populated") {
|
|
CHECK(memory->spriteSheetData[0] == 255);
|
|
CHECK(memory->spriteSheetData[1] == 1);
|
|
CHECK(memory->spriteSheetData[2] == 2);
|
|
CHECK(memory->spriteSheetData[3] == 3);
|
|
}
|
|
SUBCASE("Sprite flags is populated") {
|
|
CHECK(memory->spriteFlags[0] == 0);
|
|
CHECK(memory->spriteFlags[1] == 1);
|
|
}
|
|
SUBCASE("Map data is populated") {
|
|
CHECK(memory->mapData[0] == 1);
|
|
CHECK(memory->mapData[1] == 0);
|
|
}
|
|
SUBCASE("Sfx data is populated") {
|
|
CHECK(memory->sfx[0].data[0] == 13);
|
|
CHECK(memory->sfx[0].filters == 0);
|
|
CHECK(memory->sfx[0].loopRangeEnd == 0);
|
|
CHECK(memory->sfx[0].loopRangeEnd == 0);
|
|
CHECK(memory->sfx[0].speed == 29);
|
|
|
|
CHECK(memory->sfx[0].notes[0].getWaveform() == 0);
|
|
CHECK(memory->sfx[0].notes[0].getEffect() == 0);
|
|
CHECK(memory->sfx[0].notes[0].getKey() == 13);
|
|
CHECK(memory->sfx[0].notes[0].getVolume() == 3);
|
|
CHECK(memory->sfx[0].notes[0].data[0] == 13);
|
|
CHECK(memory->sfx[0].notes[0].data[1] == 6);
|
|
}
|
|
SUBCASE("Music data is populated") {
|
|
CHECK(memory->songs[0].getSfx0() == 1);
|
|
CHECK(memory->songs[0].getStart() == 0);
|
|
CHECK(memory->songs[0].getSfx1() == 66);
|
|
CHECK(memory->songs[0].getLoop() == 0);
|
|
CHECK(memory->songs[0].getSfx2() == 67);
|
|
CHECK(memory->songs[0].getStop() == 0);
|
|
CHECK(memory->songs[0].getSfx3() == 68);
|
|
CHECK(memory->songs[0].getMode() == 0);
|
|
}
|
|
}
|
|
SUBCASE("reload writes cart rom over changes") {
|
|
vm->LoadCart("cartparsetest.p8.png");
|
|
|
|
graphics->sset(0, 0, 4);
|
|
graphics->fset(0, 3);
|
|
graphics->mset(0, 0, 10);
|
|
|
|
vm->vm_reload(0, 0, 0x4300, "");
|
|
|
|
CHECK_EQ(graphics->sget(0, 0), 15);
|
|
CHECK_EQ(graphics->fget(0), 0);
|
|
CHECK_EQ(graphics->mget(0, 0), 1);
|
|
}
|
|
SUBCASE("memset sets memory") {
|
|
vm->vm_memset(0x3000, 13, 4);
|
|
|
|
CHECK_EQ(graphics->fget(0), 13);
|
|
CHECK_EQ(graphics->fget(3), 13);
|
|
}
|
|
SUBCASE("memcpy copies memory") {
|
|
vm->LoadCart("cartparsetest.p8.png");
|
|
vm->vm_memcpy(0x3000, 0, 4);
|
|
|
|
CHECK_EQ(memory->spriteSheetData[1], 1);
|
|
}
|
|
SUBCASE("dget before setting cart data is allowed for backward compat") {
|
|
vm->vm_dset(34, 1925);
|
|
|
|
CHECK_EQ(vm->vm_dget(34), (fix32)1925);
|
|
}
|
|
SUBCASE("dget after setting cart data key stores value") {
|
|
vm->vm_cartdata("uniquekey");
|
|
vm->vm_dset(34, 1923);
|
|
|
|
CHECK_EQ(vm->vm_dget(34), (fix32)1923);
|
|
}
|
|
SUBCASE("pico driller style input"){
|
|
vm->LoadCart("drillerinputtest.p8");
|
|
//this loads the cart + glue code into a coroutine, but does not execute anything yet
|
|
//should probably get renamed. Same as typing LOAD() in pico 8
|
|
vm->vm_run();
|
|
vm->Step(); //this will call the cart code, init, and update once
|
|
//30 fps game loop requires 1 step to update and update buttons, and another to draw
|
|
|
|
SUBCASE("initial state"){
|
|
bool btnbits = vm->ExecuteLua(
|
|
"function btnbitsinitialtest()\n"
|
|
" return btnbits == 0\n"
|
|
"end\n",
|
|
"btnbitsinitialtest");
|
|
|
|
CHECK(btnbits);
|
|
}
|
|
|
|
SUBCASE("no buttons gives 0"){
|
|
vm->Step();
|
|
bool btnbits = vm->ExecuteLua(
|
|
"function btnbitstest0()\n"
|
|
" return btnbits == 0\n"
|
|
"end\n",
|
|
"btnbitstest0");
|
|
|
|
CHECK(btnbits);
|
|
}
|
|
SUBCASE("left pushed returns 1"){
|
|
stubHost->stubInput(1, 1);
|
|
//requires two loops to ensure the button state is updated on 30 fps game
|
|
vm->Step();
|
|
vm->Step();
|
|
bool btnbits = vm->ExecuteLua(
|
|
"function btnbitstest1()\n"
|
|
" return btnbits == 1\n"
|
|
"end\n",
|
|
"btnbitstest1");
|
|
|
|
CHECK(btnbits);
|
|
}
|
|
SUBCASE("right pushed returns 2"){
|
|
stubHost->stubInput(2, 2);
|
|
//requires two loops to ensure the button state is updated on 30 fps game
|
|
vm->Step();
|
|
vm->Step();
|
|
bool btnbits = vm->ExecuteLua(
|
|
"function btnbitstest2()\n"
|
|
" return btnbits == 2\n"
|
|
"end\n",
|
|
"btnbitstest2");
|
|
|
|
CHECK(btnbits);
|
|
}
|
|
SUBCASE("right pushed detected by band op"){
|
|
stubHost->stubInput(2, 2);
|
|
//requires two loops to ensure the button state is updated on 30 fps game
|
|
vm->Step();
|
|
vm->Step();
|
|
bool btnbits = vm->ExecuteLua(
|
|
"function rightbandtest()\n"
|
|
" return rightband == 2\n"
|
|
"end\n",
|
|
"rightbandtest");
|
|
|
|
CHECK(btnbits);
|
|
}
|
|
vm->CloseCart();
|
|
}
|
|
|
|
SUBCASE("emoji button variables are available in sandbox"){
|
|
vm->LoadCart("emojibuttons.p8");
|
|
vm->vm_run();
|
|
vm->Step();
|
|
|
|
SUBCASE("emoji button variables resolve to correct numeric values"){
|
|
// Test that ⬅️ = 0, ➡️ = 1, ⬆️ = 2, ⬇️ = 3, 🅾️ = 4, ❎ = 5
|
|
bool left_correct = vm->ExecuteLua(
|
|
"function test_left_val()\n"
|
|
" return left_val == 0\n"
|
|
"end\n",
|
|
"test_left_val");
|
|
CHECK(left_correct);
|
|
|
|
bool right_correct = vm->ExecuteLua(
|
|
"function test_right_val()\n"
|
|
" return right_val == 1\n"
|
|
"end\n",
|
|
"test_right_val");
|
|
CHECK(right_correct);
|
|
|
|
bool up_correct = vm->ExecuteLua(
|
|
"function test_up_val()\n"
|
|
" return up_val == 2\n"
|
|
"end\n",
|
|
"test_up_val");
|
|
CHECK(up_correct);
|
|
|
|
bool down_correct = vm->ExecuteLua(
|
|
"function test_down_val()\n"
|
|
" return down_val == 3\n"
|
|
"end\n",
|
|
"test_down_val");
|
|
CHECK(down_correct);
|
|
|
|
bool o_correct = vm->ExecuteLua(
|
|
"function test_o_val()\n"
|
|
" return o_val == 4\n"
|
|
"end\n",
|
|
"test_o_val");
|
|
CHECK(o_correct);
|
|
|
|
bool x_correct = vm->ExecuteLua(
|
|
"function test_x_val()\n"
|
|
" return x_val == 5\n"
|
|
"end\n",
|
|
"test_x_val");
|
|
CHECK(x_correct);
|
|
}
|
|
|
|
SUBCASE("btn with emoji argument checks correct button"){
|
|
// Press O button (bit 4 = 16) and verify btn(🅾️) returns true but btn(❎) returns false
|
|
stubHost->stubInput(16, 16); // O button pressed
|
|
vm->Step();
|
|
vm->Step();
|
|
|
|
bool o_pressed = vm->ExecuteLua(
|
|
"function test_btn_o()\n"
|
|
" return btn_o == true\n"
|
|
"end\n",
|
|
"test_btn_o");
|
|
CHECK(o_pressed);
|
|
|
|
bool x_not_pressed = vm->ExecuteLua(
|
|
"function test_btn_x_not()\n"
|
|
" return btn_x == false\n"
|
|
"end\n",
|
|
"test_btn_x_not");
|
|
CHECK(x_not_pressed);
|
|
|
|
bool left_not_pressed = vm->ExecuteLua(
|
|
"function test_btn_left_not()\n"
|
|
" return btn_left == false\n"
|
|
"end\n",
|
|
"test_btn_left_not");
|
|
CHECK(left_not_pressed);
|
|
}
|
|
|
|
SUBCASE("left button does not trigger O or X button checks"){
|
|
// Press left button (bit 0 = 1) and verify btn(🅾️) and btn(❎) return false
|
|
stubHost->stubInput(1, 1); // Left button pressed
|
|
vm->Step();
|
|
vm->Step();
|
|
|
|
bool left_pressed = vm->ExecuteLua(
|
|
"function test_left_pressed()\n"
|
|
" return btn_left == true\n"
|
|
"end\n",
|
|
"test_left_pressed");
|
|
CHECK(left_pressed);
|
|
|
|
bool o_not_pressed = vm->ExecuteLua(
|
|
"function test_o_not_pressed()\n"
|
|
" return btn_o == false\n"
|
|
"end\n",
|
|
"test_o_not_pressed");
|
|
CHECK(o_not_pressed);
|
|
|
|
bool x_not_pressed = vm->ExecuteLua(
|
|
"function test_x_not_pressed()\n"
|
|
" return btn_x == false\n"
|
|
"end\n",
|
|
"test_x_not_pressed");
|
|
CHECK(x_not_pressed);
|
|
}
|
|
|
|
vm->CloseCart();
|
|
}
|
|
|
|
SUBCASE("togglepausemenu resets and restores draw state") {
|
|
graphics->pal(10, 12, 0);
|
|
graphics->fillp(25);
|
|
graphics->clip(11, 12, 21, 22);
|
|
graphics->camera(1, 2);
|
|
graphics->color(3);
|
|
|
|
SUBCASE("togglepausemenu resets draw state"){
|
|
vm->togglePauseMenu();
|
|
|
|
CHECK_EQ(10, graphics->getDrawPalMappedColor(10));
|
|
|
|
CHECK_EQ(0, memory->drawState.fillPattern[0]);
|
|
|
|
CHECK_EQ(0, memory->drawState.clip_xb);
|
|
CHECK_EQ(0, memory->drawState.clip_yb);
|
|
CHECK_EQ(128, memory->drawState.clip_xe);
|
|
CHECK_EQ(128, memory->drawState.clip_ye);
|
|
|
|
CHECK_EQ(0, memory->drawState.camera_x);
|
|
CHECK_EQ(0, memory->drawState.camera_y);
|
|
|
|
CHECK_EQ(6, memory->drawState.color);
|
|
}
|
|
SUBCASE("togglepausemenu second time restores draw state"){
|
|
vm->togglePauseMenu();
|
|
vm->togglePauseMenu();
|
|
|
|
CHECK_EQ(12, graphics->getDrawPalMappedColor(10));
|
|
|
|
CHECK_EQ(25, memory->drawState.fillPattern[0]);
|
|
|
|
CHECK_EQ(11, memory->drawState.clip_xb);
|
|
CHECK_EQ(12, memory->drawState.clip_yb);
|
|
CHECK_EQ(32, memory->drawState.clip_xe);
|
|
CHECK_EQ(34, memory->drawState.clip_ye);
|
|
|
|
CHECK_EQ(1, memory->drawState.camera_x);
|
|
CHECK_EQ(2, memory->drawState.camera_y);
|
|
|
|
CHECK_EQ(3, memory->drawState.color);
|
|
}
|
|
}
|
|
|
|
SUBCASE("getSerializedCartData for all zeroes"){
|
|
vm->vm_cartdata("serializeTest");
|
|
for(int i = 0; i < 256; i++) {
|
|
memory->cartData[i] = 0;
|
|
}
|
|
|
|
auto result = vm->getSerializedCartData();
|
|
|
|
std::string expected =
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
CHECK_EQ(expected, result);
|
|
}
|
|
SUBCASE("getSerializedCartData with 0-255"){
|
|
vm->vm_cartdata("serializeTest");
|
|
for(int i = 0; i < 256; i++) {
|
|
memory->cartData[i] = (uint8_t)i;
|
|
}
|
|
|
|
auto result = vm->getSerializedCartData();
|
|
|
|
std::string expected =
|
|
"03020100070605040b0a09080f0e0d0c13121110171615141b1a19181f1e1d1c\n"
|
|
"23222120272625242b2a29282f2e2d2c33323130373635343b3a39383f3e3d3c\n"
|
|
"43424140474645444b4a49484f4e4d4c53525150575655545b5a59585f5e5d5c\n"
|
|
"63626160676665646b6a69686f6e6d6c73727170777675747b7a79787f7e7d7c\n"
|
|
"83828180878685848b8a89888f8e8d8c93929190979695949b9a99989f9e9d9c\n"
|
|
"a3a2a1a0a7a6a5a4abaaa9a8afaeadacb3b2b1b0b7b6b5b4bbbab9b8bfbebdbc\n"
|
|
"c3c2c1c0c7c6c5c4cbcac9c8cfcecdccd3d2d1d0d7d6d5d4dbdad9d8dfdedddc\n"
|
|
"e3e2e1e0e7e6e5e4ebeae9e8efeeedecf3f2f1f0f7f6f5f4fbfaf9f8fffefdfc\n";
|
|
|
|
CHECK_EQ(expected, result);
|
|
}
|
|
SUBCASE("deserializeCartDataToMemory for all zeroes"){
|
|
vm->vm_cartdata("serializeTest");
|
|
for(int i = 0; i < 256; i++) {
|
|
memory->cartData[i] = 1;
|
|
}
|
|
|
|
std::string allZeroes =
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
vm->deserializeCartDataToMemory(allZeroes);
|
|
|
|
bool matches = true;
|
|
for(int i = 0; i < 256; i++) {
|
|
matches &= memory->cartData[i] == 0;
|
|
}
|
|
|
|
CHECK(matches);
|
|
}
|
|
SUBCASE("deserializeCartDataToMemory with 0-255"){
|
|
vm->vm_cartdata("serializeTest");
|
|
for(int i = 0; i < 256; i++) {
|
|
memory->cartData[i] = 1;
|
|
}
|
|
|
|
std::string values =
|
|
"03020100070605040b0a09080f0e0d0c13121110171615141b1a19181f1e1d1c\n"
|
|
"23222120272625242b2a29282f2e2d2c33323130373635343b3a39383f3e3d3c\n"
|
|
"43424140474645444b4a49484f4e4d4c53525150575655545b5a59585f5e5d5c\n"
|
|
"63626160676665646b6a69686f6e6d6c73727170777675747b7a79787f7e7d7c\n"
|
|
"83828180878685848b8a89888f8e8d8c93929190979695949b9a99989f9e9d9c\n"
|
|
"a3a2a1a0a7a6a5a4abaaa9a8afaeadacb3b2b1b0b7b6b5b4bbbab9b8bfbebdbc\n"
|
|
"c3c2c1c0c7c6c5c4cbcac9c8cfcecdccd3d2d1d0d7d6d5d4dbdad9d8dfdedddc\n"
|
|
"e3e2e1e0e7e6e5e4ebeae9e8efeeedecf3f2f1f0f7f6f5f4fbfaf9f8fffefdfc\n";
|
|
|
|
vm->deserializeCartDataToMemory(values);
|
|
|
|
bool matches = true;
|
|
for(int i = 0; i < 256; i++) {
|
|
matches &= memory->cartData[i] == i;
|
|
}
|
|
|
|
CHECK(matches);
|
|
}
|
|
SUBCASE("dset then getSerializedCartData returns correct values"){
|
|
vm->vm_cartdata("serializeTest");
|
|
vm->vm_dset(32, 128);
|
|
vm->vm_dset(0, 1);
|
|
vm->vm_dset(1, 2);
|
|
|
|
auto actual = vm->getSerializedCartData();
|
|
|
|
std::string expected =
|
|
"0001000000020000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0080000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
CHECK_EQ(expected, actual);
|
|
}
|
|
SUBCASE("dset then getSerializedCartData with negatives returns correct values"){
|
|
vm->vm_cartdata("serializeTest");
|
|
vm->vm_dset(0, -1);
|
|
vm->vm_dset(1, 1);
|
|
vm->vm_dset(2, 32767);
|
|
vm->vm_dset(3, -32768);
|
|
|
|
auto actual = vm->getSerializedCartData();
|
|
|
|
std::string expected =
|
|
"ffff0000000100007fff00008000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
CHECK_EQ(expected, actual);
|
|
}
|
|
SUBCASE("multiple cartdata keys can be used (up to 4)"){
|
|
vm->vm_cartdata("key1");
|
|
vm->vm_dset(0, 100);
|
|
vm->vm_cartdata("key2");
|
|
vm->vm_cartdata("key3");
|
|
vm->vm_cartdata("key4");
|
|
vm->vm_dset(0, 400);
|
|
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)400);
|
|
|
|
vm->vm_cartdata("key2");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)0);
|
|
|
|
vm->vm_dset(0, 200);
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)200);
|
|
|
|
vm->vm_cartdata("key1");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)100);
|
|
}
|
|
SUBCASE("error when trying to use more than 4 cartdata keys"){
|
|
vm->vm_cartdata("key1");
|
|
vm->vm_cartdata("key2");
|
|
vm->vm_cartdata("key3");
|
|
vm->vm_cartdata("key4");
|
|
|
|
// 5th key should fail
|
|
CHECK_FALSE(vm->vm_cartdata("key5"));
|
|
CHECK_EQ(vm->GetBiosError(), "too many cart data keys (max 4)");
|
|
}
|
|
SUBCASE("switching between different cartdata keys works correctly"){
|
|
vm->vm_cartdata("save1");
|
|
vm->vm_dset(0, 111);
|
|
vm->vm_dset(1, 222);
|
|
|
|
vm->vm_cartdata("save2");
|
|
vm->vm_dset(0, 333);
|
|
vm->vm_dset(1, 444);
|
|
|
|
vm->vm_cartdata("save1");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)111);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)222);
|
|
|
|
vm->vm_cartdata("save2");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)333);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)444);
|
|
}
|
|
SUBCASE("reusing same cartdata key multiple times works"){
|
|
// Use the same key multiple times - should work fine
|
|
vm->vm_cartdata("sharedkey");
|
|
vm->vm_dset(0, 555);
|
|
|
|
vm->vm_cartdata("sharedkey");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)555);
|
|
|
|
vm->vm_dset(1, 666);
|
|
|
|
vm->vm_cartdata("sharedkey");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)555);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)666);
|
|
}
|
|
SUBCASE("backward compatibility - single key usage still works"){
|
|
vm->vm_cartdata("singlekey");
|
|
vm->vm_dset(0, 777);
|
|
vm->vm_dset(1, 888);
|
|
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)777);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)888);
|
|
|
|
vm->vm_cartdata("singlekey");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)777);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)888);
|
|
}
|
|
SUBCASE("current active key is tracked correctly for serialization"){
|
|
vm->vm_cartdata("active1");
|
|
vm->vm_dset(0, 999);
|
|
vm->vm_dset(1, 1111);
|
|
|
|
vm->vm_cartdata("active2");
|
|
vm->vm_dset(0, 2222);
|
|
vm->vm_dset(1, 3333);
|
|
|
|
vm->vm_cartdata("active1");
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)999);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)1111);
|
|
|
|
vm->vm_dset(0, 4444);
|
|
vm->vm_dset(1, 5555);
|
|
|
|
auto serialized = vm->getSerializedCartData();
|
|
|
|
// 4444 = 0x115C, 5555 = 0x15B3 (in little-endian format)
|
|
std::string expected =
|
|
"115c000015b30000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
CHECK_EQ(expected, serialized);
|
|
}
|
|
SUBCASE("deserializeCartDataToMemory with negative ints"){
|
|
vm->vm_cartdata("serializeTest");
|
|
for(int i = 0; i < 256; i++) {
|
|
memory->cartData[i] = 0;
|
|
}
|
|
|
|
std::string values =
|
|
"ffff0000000100007fff00008000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n"
|
|
"0000000000000000000000000000000000000000000000000000000000000000\n";
|
|
|
|
vm->deserializeCartDataToMemory(values);
|
|
|
|
CHECK_EQ(vm->vm_dget(0), (fix32)-1);
|
|
CHECK_EQ(vm->vm_dget(1), (fix32)1);
|
|
CHECK_EQ(vm->vm_dget(2), (fix32)32767);
|
|
CHECK_EQ(vm->vm_dget(3), (fix32)-32768);
|
|
}
|
|
SUBCASE("SFX Note getters match expected values"){
|
|
memory->sfx[0].notes[0].data[0] = 205;
|
|
memory->sfx[0].notes[0].data[1] = 233;
|
|
|
|
CHECK(memory->sfx[0].notes[0].getWaveform() == 7);
|
|
CHECK(memory->sfx[0].notes[0].getEffect() == 6);
|
|
CHECK(memory->sfx[0].notes[0].getKey() == 13);
|
|
CHECK(memory->sfx[0].notes[0].getVolume() == 4);
|
|
CHECK(memory->sfx[0].notes[0].getCustom() == 1);
|
|
}
|
|
SUBCASE("SFX Note settiers set correct values"){
|
|
memory->sfx[0].notes[0].setWaveform(7);
|
|
memory->sfx[0].notes[0].setEffect(6);
|
|
memory->sfx[0].notes[0].setKey(13);
|
|
memory->sfx[0].notes[0].setVolume(4);
|
|
memory->sfx[0].notes[0].setCustom(1);
|
|
|
|
CHECK(memory->sfx[0].notes[0].data[0] == 205);
|
|
CHECK(memory->sfx[0].notes[0].data[1] == 233);
|
|
}
|
|
SUBCASE("cartlist gets sorted"){
|
|
std::vector<std::string> vec = { "Qcart.p8", "Acart.p8", "Zcart.p8.png", "Jcart.p8.png" };
|
|
vm->SetCartList(vec);
|
|
auto sorted = vm->GetCartList();
|
|
|
|
CHECK_EQ("Acart.p8", sorted[0]);
|
|
CHECK_EQ("Jcart.p8.png", sorted[1]);
|
|
CHECK_EQ("Qcart.p8", sorted[2]);
|
|
CHECK_EQ("Zcart.p8.png", sorted[3]);
|
|
}
|
|
SUBCASE("reset sets hw and draw state back to defaults"){
|
|
graphics->pal(4, 5, 0);
|
|
graphics->color(14);
|
|
|
|
memory->hwState.widthOfTheMap = 17;
|
|
memory->hwState.alternatePaletteFlag = 2;
|
|
|
|
vm->vm_reset();
|
|
|
|
CHECK_EQ(128, memory->hwState.widthOfTheMap);
|
|
CHECK_EQ(6, memory->drawState.color);
|
|
CHECK_EQ(4, graphics->getDrawPalMappedColor(4));
|
|
CHECK_EQ(0, memory->hwState.alternatePaletteFlag);
|
|
}
|
|
SUBCASE("pause menu - initial state is not paused") {
|
|
CHECK(vm->IsPaused() == false);
|
|
}
|
|
SUBCASE("pause menu - togglePauseMenu toggles state") {
|
|
CHECK(vm->IsPaused() == false);
|
|
vm->togglePauseMenu();
|
|
CHECK(vm->IsPaused() == true);
|
|
vm->togglePauseMenu();
|
|
CHECK(vm->IsPaused() == false);
|
|
}
|
|
SUBCASE("picoluaapi handles negative colors correctly"){
|
|
SUBCASE("pset negative number gets set correctly"){
|
|
vm->LoadCart("cartparsetest.p8");
|
|
|
|
vm->vm_run();
|
|
vm->Step();
|
|
|
|
bool result = vm->ExecuteLua(
|
|
"function psetnegativetest()\n"
|
|
" pset(0, 0, -1)\n"
|
|
" return pget(0, 0) == 15\n"
|
|
"end\n",
|
|
"psetnegativetest");
|
|
|
|
CHECK(result);
|
|
|
|
}
|
|
SUBCASE("pal negative number gets set correctly"){
|
|
vm->LoadCart("cartpalnegativetest.p8");
|
|
|
|
vm->vm_run();
|
|
vm->Step();
|
|
|
|
|
|
bool result = vm->ExecuteLua(
|
|
"function palnegativetest()\n"
|
|
" pal(0, -1, 0)\n"
|
|
" pset(0, 0, 0)\n"
|
|
" return pget(0, 0) == 15\n"
|
|
"end\n",
|
|
"palnegativetest");
|
|
|
|
CHECK(result);
|
|
|
|
}
|
|
}
|
|
|
|
delete stubHost;
|
|
delete graphics;
|
|
delete input;
|
|
delete audio;
|
|
|
|
delete vm;
|
|
|
|
delete memory;
|
|
}
|