mirror of
https://github.com/st3fan/ewm.git
synced 2025-01-14 06:30:51 +00:00
Introduce Lua 5.2 support
This commit is contained in:
parent
c68fc356c6
commit
ee1e8dc5a6
@ -24,7 +24,7 @@ os: osx
|
||||
language: c
|
||||
before_install:
|
||||
- brew update
|
||||
- brew install sdl2
|
||||
- brew install sdl2 lua
|
||||
before_script:
|
||||
- cd src
|
||||
script:
|
||||
|
22
src/Makefile
22
src/Makefile
@ -22,32 +22,32 @@
|
||||
|
||||
CC?=cc
|
||||
CFLAGS=-std=gnu11 -O3 -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
LDFLAGS=-g -L/usr/local/lib
|
||||
LDFLAGS=-L/usr/local/lib
|
||||
|
||||
EWM_EXECUTABLE=ewm
|
||||
EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c two.c scr.c dsk.c chr.c alc.c one.c tty.c utl.c boo.c
|
||||
EWM_SOURCES=cpu.c ins.c pia.c mem.c ewm.c fmt.c two.c scr.c dsk.c chr.c alc.c one.c tty.c utl.c boo.c lua.c
|
||||
EWM_OBJECTS=$(EWM_SOURCES:.c=.o)
|
||||
EWM_LIBS=-lSDL2
|
||||
EWM_LIBS=-lSDL2 -llua
|
||||
|
||||
CPU_TEST_EXECUTABLE=cpu_test
|
||||
CPU_TEST_SOURCES=cpu.c ins.c mem.c fmt.c utl.c cpu_test.c
|
||||
CPU_TEST_SOURCES=cpu.c ins.c mem.c fmt.c utl.c cpu_test.c lua.c
|
||||
CPU_TEST_OBJECTS=$(CPU_TEST_SOURCES:.c=.o)
|
||||
CPU_TEST_LIBS=
|
||||
CPU_TEST_LIBS=-llua
|
||||
|
||||
SCR_TEST_EXECUTABLE=scr_test
|
||||
SCR_TEST_SOURCES=cpu.c ins.c mem.c fmt.c two.c scr.c dsk.c chr.c alc.c utl.c scr_test.c
|
||||
SCR_TEST_SOURCES=cpu.c ins.c mem.c fmt.c two.c scr.c dsk.c chr.c alc.c utl.c scr_test.c lua.c
|
||||
SCR_TEST_OBJECTS=$(SCR_TEST_SOURCES:.c=.o)
|
||||
SCR_TEST_LIBS=-lSDL2
|
||||
SCR_TEST_LIBS=-lSDL2 -llua
|
||||
|
||||
CPU_BENCH=cpu_bench
|
||||
CPU_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c utl.c cpu_bench.c
|
||||
CPU_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c utl.c cpu_bench.c lua.c
|
||||
CPU_BENCH_OBJECTS=$(CPU_BENCH_SOURCES:.c=.o)
|
||||
CPU_BENCH_LIBS=
|
||||
CPU_BENCH_LIBS=-llua
|
||||
|
||||
MEM_BENCH=mem_bench
|
||||
MEM_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c utl.c mem_bench.c
|
||||
MEM_BENCH_SOURCES=cpu.c ins.c mem.c fmt.c utl.c mem_bench.c lua.c
|
||||
MEM_BENCH_OBJECTS=$(MEM_BENCH_SOURCES:.c=.o)
|
||||
MEM_BENCH_LIBS=
|
||||
MEM_BENCH_LIBS=-llua
|
||||
|
||||
all: $(EWM_SOURCES) $(EWM_EXECUTABLE) $(CPU_TEST_SOURCES) $(CPU_TEST_EXECUTABLE) $(SCR_TEST_EXECUTABLE) $(CPU_BENCH) $(MEM_BENCH)
|
||||
|
||||
|
263
src/cpu.c
263
src/cpu.c
@ -21,6 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
@ -36,6 +37,7 @@
|
||||
#include "ins.h"
|
||||
#include "mem.h"
|
||||
#include "fmt.h"
|
||||
#include "lua.h"
|
||||
|
||||
// Stack management.
|
||||
|
||||
@ -131,6 +133,26 @@ static int cpu_execute_instruction(struct cpu_t *cpu) {
|
||||
cpu->state.pc += i->bytes;
|
||||
}
|
||||
|
||||
if (i->lua_before_handler != LUA_NOREF) {
|
||||
lua_rawgeti(cpu->lua->state, LUA_REGISTRYINDEX, i->lua_before_handler);
|
||||
ewm_lua_push_cpu(cpu->lua, cpu);
|
||||
lua_pushinteger(cpu->lua->state, i->opcode);
|
||||
switch (i->bytes) {
|
||||
case 1:
|
||||
lua_pushinteger(cpu->lua->state, 0);
|
||||
break;
|
||||
case 2:
|
||||
lua_pushinteger(cpu->lua->state, mem_get_byte(cpu, pc+1));
|
||||
break;
|
||||
case 3:
|
||||
lua_pushinteger(cpu->lua->state, mem_get_word(cpu, pc+1));
|
||||
break;
|
||||
}
|
||||
if (lua_pcall(cpu->lua->state, 3, 0, 0) != LUA_OK) {
|
||||
printf("cpu: script error: %s\n", lua_tostring(cpu->lua->state, -1));
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute instruction */
|
||||
switch (i->bytes) {
|
||||
case 1:
|
||||
@ -144,6 +166,26 @@ static int cpu_execute_instruction(struct cpu_t *cpu) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i->lua_after_handler != LUA_NOREF) {
|
||||
lua_rawgeti(cpu->lua->state, LUA_REGISTRYINDEX, i->lua_after_handler);
|
||||
ewm_lua_push_cpu(cpu->lua, cpu);
|
||||
lua_pushinteger(cpu->lua->state, i->opcode);
|
||||
switch (i->bytes) {
|
||||
case 1:
|
||||
lua_pushinteger(cpu->lua->state, 0);
|
||||
break;
|
||||
case 2:
|
||||
lua_pushinteger(cpu->lua->state, mem_get_byte(cpu, pc+1));
|
||||
break;
|
||||
case 3:
|
||||
lua_pushinteger(cpu->lua->state, mem_get_word(cpu, pc+1));
|
||||
break;
|
||||
}
|
||||
if (lua_pcall(cpu->lua->state, 3, 0, 0) != LUA_OK) {
|
||||
printf("cpu: script error: %s\n", lua_tostring(cpu->lua->state, -1));
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu->trace) {
|
||||
cpu_format_state(cpu, trace_state);
|
||||
cpu_format_stack(cpu, trace_stack);
|
||||
@ -190,7 +232,8 @@ static int cpu_init(struct cpu_t *cpu, int model) {
|
||||
|
||||
memset(cpu, 0x00, sizeof(struct cpu_t));
|
||||
cpu->model = model;
|
||||
cpu->instructions = (cpu->model == EWM_CPU_MODEL_6502) ? instructions : instructions_65C02;
|
||||
cpu->instructions = malloc(sizeof instructions);
|
||||
memcpy(cpu->instructions, (cpu->model == EWM_CPU_MODEL_6502) ? instructions : instructions_65C02, sizeof instructions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -198,6 +241,7 @@ static int cpu_init(struct cpu_t *cpu, int model) {
|
||||
struct cpu_t *cpu_create(int model) {
|
||||
struct cpu_t *cpu = malloc(sizeof(struct cpu_t));
|
||||
if (cpu_init(cpu, model) != 0) {
|
||||
cpu_destroy(cpu);
|
||||
free(cpu);
|
||||
cpu = NULL;
|
||||
}
|
||||
@ -205,6 +249,9 @@ struct cpu_t *cpu_create(int model) {
|
||||
}
|
||||
|
||||
void cpu_destroy(struct cpu_t *cpu) {
|
||||
if (cpu->instructions != NULL) {
|
||||
free(cpu->instructions);
|
||||
}
|
||||
if (cpu->trace != NULL) {
|
||||
(void) fclose(cpu->trace);
|
||||
cpu->trace = NULL;
|
||||
@ -442,3 +489,217 @@ int cpu_nmi(struct cpu_t *cpu) {
|
||||
int cpu_step(struct cpu_t *cpu) {
|
||||
return cpu_execute_instruction(cpu);
|
||||
}
|
||||
|
||||
// Lua support
|
||||
|
||||
// cpu state functions
|
||||
|
||||
static int cpu_lua_index(lua_State *state) {
|
||||
void *cpu_data = luaL_checkudata(state, 1, "cpu_meta_table");
|
||||
struct cpu_t *cpu = *((struct cpu_t**) cpu_data);
|
||||
|
||||
if (!lua_isstring(state, 2)) {
|
||||
printf("TODO lua_cpu_index: arg 2 is not a string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *name = lua_tostring(state, 2);
|
||||
|
||||
if (strcmp(name, "a") == 0) {
|
||||
lua_pushnumber(state, cpu->state.a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "x") == 0) {
|
||||
lua_pushnumber(state, cpu->state.x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "y") == 0) {
|
||||
lua_pushnumber(state, cpu->state.y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "s") == 0) {
|
||||
lua_pushnumber(state, _cpu_get_status(cpu));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "pc") == 0) {
|
||||
lua_pushnumber(state, cpu->state.pc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "sp") == 0) {
|
||||
lua_pushnumber(state, cpu->state.sp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(name, "model") == 0) {
|
||||
switch (cpu->model) {
|
||||
case EWM_CPU_MODEL_6502:
|
||||
lua_pushstring(state, "6502");
|
||||
break;
|
||||
case EWM_CPU_MODEL_65C02:
|
||||
lua_pushstring(state, "65C02");
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
luaL_getmetatable(state, "cpu_methods_meta_table");
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cpu_lua_newindex(lua_State *state) {
|
||||
void *cpu_data = luaL_checkudata(state, 1, "cpu_meta_table");
|
||||
struct cpu_t *cpu = *((struct cpu_t**) cpu_data);
|
||||
|
||||
if(!lua_isstring(state, 2)) {
|
||||
printf("TODO lua_cpu_new_index: arg 2 is not a string\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!lua_isnumber(state, 3)) {
|
||||
printf("TODO lua_cpu_new_index: arg 3 is not a string\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *name = lua_tostring(state, 2);
|
||||
int value = lua_tointeger(state, 3);
|
||||
|
||||
if (strcmp(name, "a") == 0) {
|
||||
cpu->state.a = (uint8_t) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "x") == 0) {
|
||||
cpu->state.x = (uint8_t) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "y") == 0) {
|
||||
cpu->state.y = (uint8_t) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "s") == 0) {
|
||||
_cpu_set_status(cpu, (uint8_t) value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "pc") == 0) {
|
||||
cpu->state.pc = (uint16_t) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "sp") == 0) {
|
||||
cpu->state.pc = (uint16_t) value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpu_lua_reset(lua_State *state) {
|
||||
void *cpu_data = luaL_checkudata(state, 1, "cpu_meta_table");
|
||||
struct cpu_t *cpu = *((struct cpu_t**) cpu_data);
|
||||
cpu_reset(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cpu module functions
|
||||
|
||||
// onBeforeExecution(op, fn)
|
||||
static int cpu_lua_onBeforeExecuteInstruction(lua_State *state) {
|
||||
if (lua_gettop(state) != 3) {
|
||||
printf("Not enough arguments\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *cpu_data = luaL_checkudata(state, 1, "cpu_meta_table");
|
||||
struct cpu_t *cpu = *((struct cpu_t**) cpu_data);
|
||||
|
||||
if (lua_type(state, 2) != LUA_TNUMBER) {
|
||||
printf("First arg fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lua_type(state, 3) != LUA_TFUNCTION) {
|
||||
printf("Second arg fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t opcode = lua_tointeger(state, 2);
|
||||
|
||||
lua_pushvalue(state, 3);
|
||||
cpu->instructions[opcode].lua_before_handler = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// onAfterExecuteFuncton(op, fn)
|
||||
static int cpu_lua_onAfterExecuteInstruction(lua_State *state) {
|
||||
if (lua_gettop(state) != 3) {
|
||||
printf("Not enough arguments\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *cpu_data = luaL_checkudata(state, 1, "cpu_meta_table");
|
||||
struct cpu_t *cpu = *((struct cpu_t**) cpu_data);
|
||||
|
||||
if(lua_type(state, 2) != LUA_TNUMBER) {
|
||||
printf("First arg fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(lua_type(state, 3) != LUA_TFUNCTION) {
|
||||
printf("Second arg fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t opcode = lua_tointeger(state, 2);
|
||||
|
||||
lua_pushvalue(state, 3);
|
||||
cpu->instructions[opcode].lua_after_handler = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ewm_cpu_init_lua(struct cpu_t *cpu, struct ewm_lua_t *lua) {
|
||||
// TODO Most of this needs to move to cpu_luaopen so that we don't
|
||||
// actually enable lua support until this module is required in a
|
||||
// script. Same for other components.
|
||||
|
||||
cpu->lua = lua;
|
||||
|
||||
luaL_Reg functions[] = {
|
||||
{"__index", cpu_lua_index},
|
||||
{"__newindex", cpu_lua_newindex},
|
||||
{NULL, NULL}
|
||||
};
|
||||
ewm_lua_register_component(lua, "cpu", functions);
|
||||
|
||||
luaL_Reg cpu_methods[] = {
|
||||
{"onBeforeExecuteInstruction", cpu_lua_onBeforeExecuteInstruction},
|
||||
{"onAfterExecuteInstruction", cpu_lua_onAfterExecuteInstruction},
|
||||
{"reset", cpu_lua_reset},
|
||||
{NULL, NULL}
|
||||
};
|
||||
ewm_lua_register_component(lua, "cpu_methods", cpu_methods);
|
||||
|
||||
// Register a global cpu instance
|
||||
|
||||
void *cpu_data = lua_newuserdata(lua->state, sizeof(struct cpu_t*));
|
||||
*((struct cpu_t**) cpu_data) = cpu;
|
||||
|
||||
luaL_getmetatable(lua->state, "cpu_meta_table");
|
||||
lua_setmetatable(lua->state, -2);
|
||||
lua_setglobal(lua->state, "cpu");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define EWM_VECTOR_IRQ 0xfffe
|
||||
|
||||
struct cpu_instruction_t;
|
||||
struct ewm_lua_t;
|
||||
|
||||
struct cpu_state_t {
|
||||
uint8_t a, x, y, s, sp;
|
||||
@ -57,6 +58,8 @@ struct cpu_t {
|
||||
|
||||
uint8_t *page0;
|
||||
uint8_t *page1;
|
||||
|
||||
struct ewm_lua_t *lua;
|
||||
};
|
||||
|
||||
typedef void (*cpu_instruction_handler_t)(struct cpu_t *cpu);
|
||||
@ -122,4 +125,6 @@ uint8_t cpu_memory_get_byte(struct cpu_t *cpu, uint16_t addr);
|
||||
void cpu_memory_set_word(struct cpu_t *cpu, uint16_t addr, uint16_t v);
|
||||
void cpu_memory_set_byte(struct cpu_t *cpu, uint16_t addr, uint8_t v);
|
||||
|
||||
int ewm_cpu_init_lua(struct cpu_t *cpu, struct ewm_lua_t *lua);
|
||||
|
||||
#endif
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
#include "utl.h"
|
||||
#include "lua.h"
|
||||
|
||||
int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path) {
|
||||
struct cpu_t *cpu = cpu_create(model);
|
||||
@ -37,6 +38,16 @@ int test(int model, uint16_t start_addr, uint16_t success_addr, char *rom_path)
|
||||
cpu_reset(cpu);
|
||||
cpu->state.pc = start_addr;
|
||||
|
||||
#if 1
|
||||
cpu->lua = ewm_lua_create();
|
||||
ewm_cpu_init_lua(cpu, cpu->lua);
|
||||
|
||||
if (ewm_lua_load_script(cpu->lua, "cpu_test.lua") != 0) {
|
||||
printf("Lua script failed to load\n"); // TODO Move errors reporting into C code
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t last_pc = cpu->state.pc;
|
||||
|
||||
struct timespec start;
|
||||
|
31
src/cpu_test.lua
Normal file
31
src/cpu_test.lua
Normal file
@ -0,0 +1,31 @@
|
||||
-- EWM Meets Lua
|
||||
|
||||
local function myerrhandler(err)
|
||||
print(err)
|
||||
print(debug.traceback())
|
||||
return false
|
||||
end
|
||||
|
||||
-- TODO How do we do this in C?
|
||||
xpcall(function()
|
||||
-- Add some random intercepts to measure the performance impact
|
||||
|
||||
local cpu = require 'cpu'
|
||||
|
||||
cpu.onBeforeExecuteInstruction(0x60, function(state, opcode, operand)
|
||||
--print(string.format('Before RTS from 0x%.4x', state.pc))
|
||||
end)
|
||||
|
||||
cpu.onBeforeExecuteInstruction(0x20, function(state, opcode, operand)
|
||||
--print(string.format('Before JSR from 0x%.4x', state.pc))
|
||||
end)
|
||||
|
||||
cpu.onAfterExecuteInstruction(0x60, function(state, opcode, operand)
|
||||
--print(string.format('After RTS from 0x%.4x', state.pc))
|
||||
end)
|
||||
|
||||
cpu.onAfterExecuteInstruction(0x20, function(state, opcode, operand)
|
||||
--print(string.format('After JSR from 0x%.4x', state.pc))
|
||||
end)
|
||||
|
||||
end , myerrhandler)
|
23
src/dsk.c
23
src/dsk.c
@ -30,6 +30,7 @@
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "utl.h"
|
||||
#include "lua.h"
|
||||
#include "dsk.h"
|
||||
|
||||
//
|
||||
@ -553,3 +554,25 @@ int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t drive, bool readonly, c
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Lua Support
|
||||
|
||||
static int ewm_dsk_lua_hello(lua_State *lua) {
|
||||
printf("This is dsk.hello()\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ewm_dsk_luaopen(lua_State *state) {
|
||||
luaL_Reg functions[] = {
|
||||
{"hello", ewm_dsk_lua_hello},
|
||||
{NULL, NULL}
|
||||
};
|
||||
luaL_newlib(state, functions);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ewm_dsk_init_lua(struct ewm_dsk_t *dsk, struct ewm_lua_t *lua) {
|
||||
dsk->lua = lua;
|
||||
luaL_requiref(dsk->lua->state, "dsk", ewm_dsk_luaopen, 0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
struct cpu_t;
|
||||
struct mem_t;
|
||||
struct ewm_lua_t;
|
||||
|
||||
#define EWM_DSK_DRIVE1 (0)
|
||||
#define EWM_DSK_DRIVE2 (1)
|
||||
@ -61,6 +62,7 @@ struct ewm_dsk_t {
|
||||
struct ewm_dsk_drive_t drives[2];
|
||||
uint8_t drive; // 0 based
|
||||
int skip;
|
||||
struct ewm_lua_t *lua;
|
||||
};
|
||||
|
||||
#define EWM_DSK_TYPE_UNKNOWN (-1)
|
||||
@ -72,4 +74,6 @@ struct ewm_dsk_t *ewm_dsk_create(struct cpu_t *cpu);
|
||||
int ewm_dsk_set_disk_data(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, void *data, size_t length, int type);
|
||||
int ewm_dsk_set_disk_file(struct ewm_dsk_t *dsk, uint8_t index, bool readonly, char *path);
|
||||
|
||||
int ewm_dsk_init_lua(struct ewm_dsk_t *dsk, struct ewm_lua_t *lua);
|
||||
|
||||
#endif
|
||||
|
37
src/frogger.lua
Normal file
37
src/frogger.lua
Normal file
@ -0,0 +1,37 @@
|
||||
-- EWM Meets Lua
|
||||
|
||||
local function myerrhandler(err)
|
||||
print(err)
|
||||
print(debug.traceback())
|
||||
return false
|
||||
end
|
||||
|
||||
xpcall(function()
|
||||
--
|
||||
-- Frogger.lua - Change the default Frogger key bindings.
|
||||
--
|
||||
-- Intercept LDA $C000. The key code will be in the
|
||||
-- accumulator. Map the values to our own preferences.
|
||||
--
|
||||
-- GETKEY: LDA $C000 ; AD 00 C0
|
||||
-- CMP #$80 ; C9 80
|
||||
-- BCC GETKEY ; 90 F9
|
||||
-- STA $C010 ; 8D 10 C0
|
||||
--
|
||||
|
||||
local cpu = require 'cpu'
|
||||
cpu.onAfterExecuteInstruction(0xAD, function(state, opcode, operand)
|
||||
if operand == 0xc000 then
|
||||
if state.a == 0xc9 then -- I
|
||||
state.a = 0xc1 -- A
|
||||
elseif state.a == 0xcb then -- K
|
||||
state.a = 0xda -- Z
|
||||
elseif state.a == 0xca then -- J
|
||||
state.a = 0x88 -- <-
|
||||
elseif state.a == 0xcc then -- L
|
||||
state.a = 0x95 -- ->
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
end , myerrhandler )
|
@ -32,6 +32,9 @@ struct cpu_instruction_t {
|
||||
uint8_t cycles;
|
||||
int8_t stack; // How much stack does this instruction need. Negative means pull, positive push
|
||||
void *handler;
|
||||
|
||||
int lua_before_handler;
|
||||
int lua_after_handler;
|
||||
};
|
||||
|
||||
extern struct cpu_instruction_t instructions[256];
|
||||
|
17
src/inverse.lua
Normal file
17
src/inverse.lua
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
--
|
||||
-- Intercept JSR COUT calls and turn A-Z into inverse. The key code is
|
||||
-- in the accumulator so we can simply see if it is in the range we
|
||||
-- are interested in and then shift it to the inverse character range.
|
||||
--
|
||||
|
||||
JSR = 0x20
|
||||
COUT = 0xfded
|
||||
|
||||
cpu.onBeforeExecuteInstruction(JSR, function(state, opcode, operand)
|
||||
if operand == COUT then
|
||||
if state.a >= 0xc1 and state.a <= 0xda then
|
||||
state.a = state.a - 0xC0
|
||||
end
|
||||
end
|
||||
end)
|
94
src/lua.c
Normal file
94
src/lua.c
Normal file
@ -0,0 +1,94 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "two.h"
|
||||
#include "cpu.h"
|
||||
#include "dsk.h"
|
||||
|
||||
static int ewm_lua_init(struct ewm_lua_t *lua) {
|
||||
memset(lua, 0x00, sizeof(struct ewm_lua_t));
|
||||
lua->state = luaL_newstate();
|
||||
luaL_openlibs(lua->state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ewm_lua_t *ewm_lua_create() {
|
||||
struct ewm_lua_t *lua = malloc(sizeof(struct ewm_lua_t));
|
||||
if (ewm_lua_init(lua) != 0) {
|
||||
free(lua);
|
||||
lua = NULL;
|
||||
}
|
||||
return lua;
|
||||
}
|
||||
|
||||
int ewm_lua_load_script(struct ewm_lua_t *lua, char *script_path) {
|
||||
if (luaL_dofile(lua->state, script_path) != LUA_OK) {
|
||||
printf("ewm: script error: %s\n", lua_tostring(lua->state, -1));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ewm_lua_push_cpu(struct ewm_lua_t *lua, struct cpu_t *cpu) {
|
||||
void *cpu_data = lua_newuserdata(lua->state, sizeof(struct cpu_t*));
|
||||
*((struct cpu_t**) cpu_data) = cpu;
|
||||
luaL_getmetatable(lua->state, "cpu_meta_table");
|
||||
lua_setmetatable(lua->state, -2);
|
||||
}
|
||||
|
||||
void ewm_lua_push_two(struct ewm_lua_t *lua, struct ewm_two_t *two) {
|
||||
void *two_data = lua_newuserdata(lua->state, sizeof(struct ewm_two_t*));
|
||||
*((struct ewm_two_t**) two_data) = two;
|
||||
luaL_getmetatable(lua->state, "two_meta_table");
|
||||
lua_setmetatable(lua->state, -2);
|
||||
}
|
||||
|
||||
void ewm_lua_push_dsk(struct ewm_lua_t *lua, struct ewm_dsk_t *dsk) {
|
||||
void *dsk_data = lua_newuserdata(lua->state, sizeof(struct ewm_dsk_t*));
|
||||
*((struct ewm_dsk_t**) dsk_data) = dsk;
|
||||
luaL_getmetatable(lua->state, "dsk_meta_table");
|
||||
lua_setmetatable(lua->state, -2);
|
||||
}
|
||||
|
||||
void ewm_lua_register_component(struct ewm_lua_t *lua, char *name, luaL_Reg *functions) {
|
||||
char table_name[64];
|
||||
strncpy(table_name, name, sizeof(table_name)-1);
|
||||
strncat(table_name, "_meta_table", sizeof(table_name)-1);
|
||||
|
||||
char global_name[64];
|
||||
strncpy(global_name, name, sizeof(global_name)-1);
|
||||
for (size_t i = 0; i < strlen(global_name); i++) {
|
||||
global_name[i] = toupper(global_name[i]);
|
||||
}
|
||||
|
||||
// Register the cpu meta table
|
||||
luaL_newmetatable(lua->state, table_name);
|
||||
luaL_setfuncs(lua->state, functions, 0);
|
||||
}
|
47
src/lua.h
Normal file
47
src/lua.h
Normal file
@ -0,0 +1,47 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015 Stefan Arentz - http://github.com/st3fan/ewm
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef LUA_H
|
||||
#define LUA_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
struct cpu_t;
|
||||
struct ewm_two_t;
|
||||
struct ewm_dsk_t;
|
||||
|
||||
struct ewm_lua_t {
|
||||
lua_State *state;
|
||||
};
|
||||
|
||||
struct ewm_lua_t *ewm_lua_create();
|
||||
int ewm_lua_load_script(struct ewm_lua_t *lua, char *script_path);
|
||||
|
||||
void ewm_lua_push_cpu(struct ewm_lua_t *lua, struct cpu_t *cpu);
|
||||
void ewm_lua_push_two(struct ewm_lua_t *lua, struct ewm_two_t *two);
|
||||
void ewm_lua_push_dsk(struct ewm_lua_t *lua, struct ewm_dsk_t *dsk);
|
||||
|
||||
void ewm_lua_register_component(struct ewm_lua_t *lua, char *name, luaL_Reg *functions);
|
||||
|
||||
#endif // LUA_H
|
32
src/test.lua
Normal file
32
src/test.lua
Normal file
@ -0,0 +1,32 @@
|
||||
-- EWM Meets Lua
|
||||
|
||||
cpu:onAfterExecuteInstruction(0xAD, function(cpu, opcode, operand)
|
||||
if operand == 0xc000 and cpu.a == 0xd2 then
|
||||
print(string.format("This is a %s", cpu.model))
|
||||
cpu:reset()
|
||||
end
|
||||
end)
|
||||
|
||||
-- cpu.onBeforeExecuteInstruction(0x60, function(state, opcode, operand)
|
||||
-- print(string.format('Before RTS from 0x%.4x', state.pc))
|
||||
-- end)
|
||||
|
||||
-- cpu.onBeforeExecuteInstruction(0x20, function(state, opcode, operand)
|
||||
-- print(string.format('Before JSR from 0x%.4x', state.pc))
|
||||
-- end)
|
||||
|
||||
-- cpu.onBeforeExecuteInstruction(0xAD, function(state, opcode, operand)
|
||||
-- print(string.format('Before LDA from 0x%.4x', state.pc))
|
||||
-- end)
|
||||
|
||||
-- cpu.onAfterExecuteInstruction(0x60, function(state, opcode, operand)
|
||||
-- print(string.format('After RTS from 0x%.4x', state.pc))
|
||||
-- end)
|
||||
|
||||
-- cpu.onAfterExecuteInstruction(0x20, function(state, opcode, operand)
|
||||
-- print(string.format('After JSR from 0x%.4x', state.pc))
|
||||
-- end)
|
||||
|
||||
-- cpu.onAfterExecuteInstruction(0xAD, function(state, opcode, operand)
|
||||
-- print(string.format('After LDA from 0x%.4x', state.pc))
|
||||
-- end)
|
52
src/two.c
52
src/two.c
@ -34,9 +34,9 @@
|
||||
#include "alc.h"
|
||||
#include "chr.h"
|
||||
#include "scr.h"
|
||||
#include "lua.h"
|
||||
#include "two.h"
|
||||
|
||||
|
||||
#define EWM_A2P_SS_KBD 0xc000
|
||||
#define EWM_A2P_SS_KBDSTRB 0xc010
|
||||
#define EWM_A2P_SS_SPKR 0xc030
|
||||
@ -347,15 +347,32 @@ struct ewm_two_t *ewm_two_create(int type, SDL_Renderer *renderer, SDL_Joystick
|
||||
}
|
||||
|
||||
void ewm_two_destroy(struct ewm_two_t *two) {
|
||||
// TODO
|
||||
// TODO Or maybe not.
|
||||
}
|
||||
|
||||
// Lua support
|
||||
|
||||
int ewm_two_luaopen(lua_State *state) {
|
||||
luaL_Reg ewm_two_functions[] = {
|
||||
// TODO What module level functions do we need here?
|
||||
{NULL, NULL}
|
||||
};
|
||||
luaL_newlib(state, ewm_two_functions);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ewm_two_init_lua(struct ewm_two_t *two, struct ewm_lua_t *lua) {
|
||||
two->lua = lua;
|
||||
luaL_requiref(two->lua->state, "two", ewm_two_luaopen, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// External API
|
||||
|
||||
int ewm_two_load_disk(struct ewm_two_t *two, int drive, char *path) {
|
||||
return ewm_dsk_set_disk_file(two->dsk, drive, false, path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool ewm_two_poll_event(struct ewm_two_t *two, SDL_Window *window) { // TODO Should window be part of ewm_two_t?
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event) != 0) {
|
||||
@ -537,6 +554,7 @@ static void ewm_two_update_status_bar(struct ewm_two_t *two, double mhz) {
|
||||
#define EWM_TWO_OPT_TRACE (6)
|
||||
#define EWM_TWO_OPT_STRICT (7)
|
||||
#define EWM_TWO_OPT_DEBUG (8)
|
||||
#define EWM_TWO_OPT_SCRIPT (9)
|
||||
|
||||
static struct option one_options[] = {
|
||||
{ "help", no_argument, NULL, EWM_TWO_OPT_HELP },
|
||||
@ -548,6 +566,7 @@ static struct option one_options[] = {
|
||||
{ "trace", optional_argument, NULL, EWM_TWO_OPT_TRACE },
|
||||
{ "strict", no_argument, NULL, EWM_TWO_OPT_STRICT },
|
||||
{ "debug", no_argument, NULL, EWM_TWO_OPT_DEBUG },
|
||||
{ "script", required_argument, NULL, EWM_TWO_OPT_SCRIPT },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@ -561,6 +580,7 @@ static void usage() {
|
||||
fprintf(stderr, " --trace <file> trace cpu to file\n");
|
||||
fprintf(stderr, " --strict run emulator in strict mode\n");
|
||||
fprintf(stderr, " --debug print debug info\n");
|
||||
fprintf(stderr, " --script <script> load Lua script into the emulator\n");
|
||||
}
|
||||
|
||||
int ewm_two_main(int argc, char **argv) {
|
||||
@ -574,6 +594,7 @@ int ewm_two_main(int argc, char **argv) {
|
||||
char *trace_path = NULL;
|
||||
bool strict = false;
|
||||
bool debug = false;
|
||||
char *script_path = NULL;
|
||||
|
||||
int ch;
|
||||
while ((ch = getopt_long_only(argc, argv, "", one_options, NULL)) != -1) {
|
||||
@ -612,6 +633,9 @@ int ewm_two_main(int argc, char **argv) {
|
||||
case EWM_TWO_OPT_DEBUG:
|
||||
debug = true;
|
||||
break;
|
||||
case EWM_TWO_OPT_SCRIPT:
|
||||
script_path = optarg;
|
||||
break;
|
||||
default: {
|
||||
usage();
|
||||
exit(1);
|
||||
@ -719,6 +743,26 @@ int ewm_two_main(int argc, char **argv) {
|
||||
cpu_strict(two->cpu, strict);
|
||||
cpu_trace(two->cpu, trace_path);
|
||||
|
||||
// Setup a Lua environment if scripts were specified
|
||||
|
||||
if (script_path != NULL) {
|
||||
struct ewm_lua_t *lua = ewm_lua_create();
|
||||
if (lua == NULL) {
|
||||
printf("Failed to setup Lua environment\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ewm_two_init_lua(two, lua);
|
||||
ewm_cpu_init_lua(two->cpu, lua);
|
||||
ewm_dsk_init_lua(two->dsk, lua);
|
||||
|
||||
if (ewm_lua_load_script(two->lua, script_path) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset things to a known state
|
||||
|
||||
cpu_reset(two->cpu);
|
||||
|
||||
//
|
||||
|
@ -56,11 +56,13 @@
|
||||
struct mem_t;
|
||||
struct ewm_dsk_t;
|
||||
struct scr;
|
||||
struct ewm_lua_t;
|
||||
|
||||
struct ewm_two_t {
|
||||
int type;
|
||||
struct cpu_t *cpu;
|
||||
struct scr_t *scr;
|
||||
struct ewm_lua_t *lua;
|
||||
struct ewm_dsk_t *dsk;
|
||||
struct ewm_alc_t *alc;
|
||||
|
||||
@ -104,6 +106,8 @@ struct ewm_two_t {
|
||||
struct ewm_two_t *ewm_two_create(int type, SDL_Renderer *renderer, SDL_Joystick *joystick);
|
||||
void ewm_two_destroy(struct ewm_two_t *two);
|
||||
|
||||
int ewm_two_init_lua(struct ewm_two_t *two, struct ewm_lua_t *lua);
|
||||
|
||||
int ewm_two_load_disk(struct ewm_two_t *two, int drive, char *path);
|
||||
|
||||
int ewm_two_main(int argc, char **argv);
|
||||
|
Loading…
x
Reference in New Issue
Block a user