Merge branch 'master' into 'master'

Misc Cleanup

See merge request InfiniteNesLives/INL-retro-progdump!6
This commit is contained in:
Paul Molloy 2018-11-25 06:36:37 +00:00
commit b32945027e
4 changed files with 333 additions and 201 deletions

View File

@ -0,0 +1,75 @@
-- Main application flow for interacting with cartridges via USB device.
-- Refactored version that doesn't require commenting/uncommenting to change functionality.
-- Helper function that checks if a string is empty or nil.
local function isempty(s)
return s == nil or s == ''
end
-- Wrapper for managing NES/Famicom operations.
function nes_exec(mapper, dump_filename, flash_filename, verify_filename)
local dict = require "scripts.app.dict"
local nes = require "scripts.app.nes"
-- Always test!
local do_test = true
-- If a dump filename was provided, dump data from cartridge to a file.
local do_read = not isempty(dump_filename)
-- If a flash filename was provided, write its contents to the cartridge.
local do_erase = not isempty(flash_filename)
-- If writing, always erase.
local do_program = do_erase
-- If a verify_filename was provided, dump data from cartridge after flash to a file.
local do_verify = not isempty(verify_filename)
-- TODO: Add other mappers.
local mappers = {
mmc1 = require "scripts.nes.mmc1",
mmc3 = require "scripts.nes.mmc3",
nrom = require "scripts.nes.nrom"
}
dict.io("IO_RESET")
dict.io("NES_INIT")
nes.detect_mapper_mirroring(true)
m = mappers[mapper]
if m == nil then
print("UNSUPPORTED MAPPER")
else
-- Attempt requested operations with hardware!
-- TODO: Do plumbing for interacting with RAM.
m.process(do_test, do_read, do_erase, do_program, do_verify, dump_filename, flash_filename, verify_filename)
end
end
-- Point of entry from C language half of program.
function main()
-- Globals passed in from C:
-- console_name: string, name of console.
-- mapper_name: string, name of mapper.
-- dump_filename: string, filename used for writing dumped data.
-- flash_filename: string, filename containing data to write cartridge.
-- verify_filename: string, filename used for writing back data written to cartridge for verification.
-- TODO: Add SNES support, as it appears to be currently usable?
local consoles = {
NES = nes_exec,
}
f = consoles[console_name]
if f == nil then
print("UNSUPPORTED CONSOLE")
else
f(mapper_name, dump_filename, flash_filename, verify_filename)
end
end
-- Don't do this. Next iteration will call a function, not the whole script.
main()

View File

@ -51,28 +51,28 @@ local function mirror_test( debug )
--MM = 0: 1 screen A
dict.nes("NES_MMC1_WR", 0x8000, 0x00)
if (nes.detect_mapper_mirroring() ~= "1SCNA") then
print("MMC1 mirror test fail")
print("MMC1 mirror test fail (1 screen A)")
return false
end
--MM = 1: 1 screen B
dict.nes("NES_MMC1_WR", 0x8000, 0x01)
if (nes.detect_mapper_mirroring() ~= "1SCNB") then
print("MMC1 mirror test fail")
print("MMC1 mirror test fail (1 screen B)")
return false
end
--MM = 2: Vertical
dict.nes("NES_MMC1_WR", 0x8000, 0x02)
if (nes.detect_mapper_mirroring() ~= "VERT") then
print("MMC1 mirror test fail")
print("MMC1 mirror test fail (Vertical)")
return false
end
--MM = 3: Horizontal
dict.nes("NES_MMC1_WR", 0x8000, 0x03)
if (nes.detect_mapper_mirroring() ~= "HORZ") then
print("MMC1 mirror test fail")
print("MMC1 mirror test fail (Horizontal)")
return false
end

View File

@ -29,6 +29,144 @@
#include "lua/lauxlib.h"
#include "lua/lualib.h"
// Struct used to control functionality.
typedef struct {
char *console_name;
char *mapper_name;
int display_help;
char *dump_filename;
char *program_filename;
char *lua_filename;
} INLOptions;
// Parse options and flags, create struct to drive program.
INLOptions* parseOptions(int argc, char *argv[]) {
// lower case flags suggested for average user
const char *FLAG_FORMAT = "hc:d:m:p:s:";
int index = 0;
int rv = 0;
// opterr = 0;
/* FLAGS NOT YET SUPPORTED
int e_flag = 0; //FORCE ERASE
int f_flag = 0; //FORCE ALL CHECKS PASSING
int i_flag = 0; //input file is interlaced
int n_flag = 0; //LED OFF
int o_flag = 0; //LED ON
int t_flag = 0; //test all SRAMs found
int x_flag = 0; //disable all auto detecting
int y_flag = 0; //diable all auto doubling
char *b_value = NULL; //SUBMAPPER #
char *p_value = NULL; //PROGRAM FILE
char *v_value = NULL; //MAPPER VARIANT
//upper case flags suggested for ADVANCED users
int T_flag = 0; //TEST
char *C_value = NULL; //program chr file
char *L_value = NULL; //LIBUSB debugging value
char *K_value = NULL; //connect to kazzo firmware version #
char *O_value = NULL; //start read/writing at offset base value
char *P_value = NULL; //program prg file
char *S_value = NULL; //program SNES binary file
char *W_value = NULL; //program WRAM/SRAM file
*/
INLOptions *opts = calloc(1, sizeof(INLOptions));
//getopt returns args till done then returns -1
//string of possible args : denotes 1 required additional arg
//:: denotes optional additional arg follows
while((rv = getopt(argc, argv, FLAG_FORMAT)) != -1) {
switch(rv) {
// case 'o': o_flag = 1; break;
// case 'n': n_flag = 1; break;
// case 'e': e_flag = 1; break;
// case 'f': f_flag = 1; break;
case 'h': opts->display_help = 1; break;
// case 'i': i_flag = 1; break;
// case 't': t_flag = 1; break;
// case 'x': x_flag = 1; break;
// case 'y': y_flag = 1; break;
// case 'T': T_flag = 1; break;
// case 'b': b_value = optarg; break;
case 'c': opts->console_name = optarg; break;
case 'd': opts->dump_filename = optarg; break;
case 'm': opts->mapper_name = optarg; break;
case 'p': opts->program_filename = optarg; break;
case 's': opts->lua_filename = optarg; break;
// case 'v': v_value = optarg; break;
// case 'C': C_value = optarg; break;
// case 'L': L_value = optarg; break;
// case 'K': K_value = optarg; break;
// case 'O': O_value = optarg; break;
// case 'P': P_value = optarg; break;
// case 'S': S_value = optarg; break;
// case 'W': W_value = optarg; break;
case '?':
if(
// ( optopt == 'b' ) ||
( optopt == 'c' )
|| ( optopt == 'd' )
|| ( optopt == 'm' )
|| ( optopt == 'p' )
|| ( optopt == 's' )
// || ( optopt == 'v' )
// || ( optopt == 'C' )
// || ( optopt == 'L' )
// || ( optopt == 'K' )
// || ( optopt == 'O' )
// || ( optopt == 'P' )
// || ( optopt == 'S' )
// || ( optopt == 'W' )
){
log_err("Option -%c requires an argument.", optopt);
//goto error;
return NULL;
} else if ( isprint(optopt)) {
log_err("Unknown option -%c .", optopt);
return NULL;
// goto error;
} else {
log_err("Unknown option character '\\x%x'", optopt);
// goto error;
return NULL;
}
log_err("Improper arguements passed");
// goto error;
return NULL;
break;
default:
//sentinel("getopt failed to catch all arg cases");
printf("getopt failed to catch all arg cases");
return 0;
}
}
// debug("flags= o:%d n:%d e:%d f:%d h:%d i:%d t:%d x:%d y:%d T:%d",
// o_flag, n_flag, e_flag, f_flag, h_flag, i_flag, t_flag, x_flag, y_flag, T_flag );
// debug("args= b:%s c:%s d:%s m:%s p:%s", b_value, c_value, d_value, m_value, p_value);
// debug("args= s:%s v:%s C:%s L:%s K:%s", s_value, v_value, C_value, L_value, K_value);
// debug("args= O:%s P:%s S:%s W:%s", O_value, P_value, S_value, W_value);
for( index = optind; index < argc; index++) {
log_err("Non-option arguement: %s \n", argv[index]);
}
//TODO display better help message
if (opts->display_help) {
printf("You've asked for help but the help message still needs created...\n");
return NULL;
}
return opts;
}
void error_lua (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
@ -55,148 +193,80 @@ void load (lua_State *L, const char *fname, int *w, int *h) {
*h = getglobint(L, "height");
}
int main(int argc, char *argv[])
{
//lower case flags suggested for average user
int e_flag = 0; //FORCE ERASE
int f_flag = 0; //FORCE ALL CHECKS PASSING
int h_flag = 0; //HELP MESSAGE
int i_flag = 0; //input file is interlaced
int n_flag = 0; //LED OFF
int o_flag = 0; //LED ON
int t_flag = 0; //test all SRAMs found
int x_flag = 0; //disable all auto detecting
int y_flag = 0; //diable all auto doubling
char *b_value = NULL; //SUBMAPPER #
char *c_value = NULL; //CONSOLE NAME
char *d_value = NULL; //DUMP TO FILE
char *m_value = NULL; //MAPPER #
char *p_value = NULL; //PROGRAM FILE
char *s_value = NULL; //SCRIPT FILE
char *v_value = NULL; //MAPPER VARIANT
//upper case flags suggested for ADVANCED users
int T_flag = 0; //TEST
char *C_value = NULL; //program chr file
char *L_value = NULL; //LIBUSB debugging value
char *K_value = NULL; //connect to kazzo firmware version #
char *O_value = NULL; //start read/writing at offset base value
char *P_value = NULL; //program prg file
char *S_value = NULL; //program SNES binary file
char *W_value = NULL; //program WRAM/SRAM file
// int operation = 0; //used to denote the overall operation being requested flash/dump/verify etc
int index = 0;
int rv = 0;
opterr = 0;
//usb variables
USBtransfer *transfer = NULL;
//lua variables
lua_State *L = NULL;
//getopt returns args till done then returns -1
//string of possible args : denotes 1 required additional arg
//:: denotes optional additional arg follows
while( (rv = getopt( argc, argv, "onefhitxyTb:c:d:m:p:s:v:C:L:K:O:P:S:W:")) != -1) {
switch(rv) {
case 'o': o_flag = 1; break;
case 'n': n_flag = 1; break;
case 'e': e_flag = 1; break;
case 'f': f_flag = 1; break;
case 'h': h_flag = 1; break;
case 'i': i_flag = 1; break;
case 't': t_flag = 1; break;
case 'x': x_flag = 1; break;
case 'y': y_flag = 1; break;
case 'T': T_flag = 1; break;
case 'b': b_value = optarg; break;
case 'c': c_value = optarg; break;
case 'd': d_value = optarg; break;
case 'm': m_value = optarg; break;
case 'p': p_value = optarg; break;
case 's': s_value = optarg; break;
case 'v': v_value = optarg; break;
case 'C': C_value = optarg; break;
case 'L': L_value = optarg; break;
case 'K': K_value = optarg; break;
case 'O': O_value = optarg; break;
case 'P': P_value = optarg; break;
case 'S': S_value = optarg; break;
case 'W': W_value = optarg; break;
case '?':
if(( optopt == 'b' )
|| ( optopt == 'c' )
|| ( optopt == 'd' )
|| ( optopt == 'm' )
|| ( optopt == 'p' )
|| ( optopt == 's' )
|| ( optopt == 'v' )
|| ( optopt == 'C' )
|| ( optopt == 'L' )
|| ( optopt == 'K' )
|| ( optopt == 'O' )
|| ( optopt == 'P' )
|| ( optopt == 'S' )
|| ( optopt == 'W' )){
log_err("Option -%c requires an argument.", optopt);
goto error;
} else if ( isprint(optopt)) {
log_err("Unknown option -%c .", optopt);
goto error;
} else {
log_err("Unknown option character '\\x%x'", optopt);
goto error;
}
log_err("Improper arguements passed");
goto error;
break;
default:
sentinel("getopt failed to catch all arg cases");
}
}
debug("flags= o:%d n:%d e:%d f:%d h:%d i:%d t:%d x:%d y:%d T:%d",
o_flag, n_flag, e_flag, f_flag, h_flag, i_flag, t_flag, x_flag, y_flag, T_flag );
debug("args= b:%s c:%s d:%s m:%s p:%s", b_value, c_value, d_value, m_value, p_value);
debug("args= s:%s v:%s C:%s L:%s K:%s", s_value, v_value, C_value, L_value, K_value);
debug("args= O:%s P:%s S:%s W:%s", O_value, P_value, S_value, W_value);
for( index = optind; index < argc; index++) {
log_err("Non-option arguement: %s \n", argv[index]);
}
//TODO display better help message
if (h_flag) {
printf("You've asked for help but the help message still needs created...\n");
return 0;
}
/*
if ( O_value || v_value || b_value || y_flag || t_flag || f_flag ) {
printf("option not currently supported sorry...\n");
}
*/
//Start up Lua
L = luaL_newstate(); //opens Lua
// Setup Lua environment.
lua_State *lua_init() {
lua_State *L = luaL_newstate(); //opens Lua
luaL_openlibs(L); //opens the standard libraries
//register C functions that can be called from Lua
// lua_pushcfunction(L, lua_dictionary_call);
// Register C functions that can be called from Lua.
// lua_pushcfunction(L, lua_dictionary_call);
lua_pushcfunction(L, lua_usb_vend_xfr);
lua_setglobal(L, "usb_vend_xfr");
return L;
}
// Setup INL USB Device.
USBtransfer *usb_init_inldevice(libusb_context *context, int libusb_log) {
// Create USBtransfer struct to hold all transfer info
USBtransfer *transfer = calloc(1, sizeof(USBtransfer));
// Create USB device handle pointer to interact with retro-prog.
transfer->handle = open_usb_device( context, libusb_log );
return transfer;
}
// Close and cleanup INL USB Device.
void usb_close_inldevice(libusb_context* context, USBtransfer* transfer) {
if (context && transfer) {
close_usb(context, transfer->handle);
}
if (transfer) {
free(transfer);
}
}
// Safely cleanup for exiting program and release resources.
void cleanup(libusb_context *context, USBtransfer *transfer, lua_State *L) {
usb_close_inldevice(context, transfer);
if (L) {
lua_close(L);
}
/*
if(rom->fileptr != NULL) {
fclose(rom->fileptr);
}
*/
}
int main(int argc, char *argv[])
{
// int operation = 0; //used to denote the overall operation being requested flash/dump/verify etc
// opterr = 0;
// USB variables
USBtransfer *transfer = NULL;
// Context set to NULL since only acting as single user of libusb.
libusb_context *context = NULL;
// Default to no libusb logging.
int libusb_log = LIBUSB_LOG_LEVEL_NONE;
// Lua variables.
lua_State *L = NULL;
const char *LUA_SCRIPT_USB = "scripts/app/usb_device.lua";
// Parse command-line options and flags.
INLOptions *opts = parseOptions(argc, argv);
if(!opts) {
// If unparseable, exit.
return 1;
}
//Start up Lua
L = lua_init();
/*
//flags about input files only used for writes
@ -214,17 +284,6 @@ int main(int argc, char *argv[])
//TODO all commandline args must be collected and passed into lua
//context set to NULL since only acting as single user of libusb
libusb_context *context = NULL;
//create USBtransfer struct to hold all transfer info
transfer = malloc( sizeof(USBtransfer));
check_mem(transfer);
//create usb device handle pointer to interact with retro-prog
transfer->handle = NULL;
//create file object/struct
// rom_image *rom = malloc( sizeof(rom_image));
//
@ -232,36 +291,57 @@ int main(int argc, char *argv[])
// init_rom_elements(rom);
//lua script arg to set different LIBUSB debugging options
check( !(luaL_loadfile(L, "scripts/app/usb_device.lua") || lua_pcall(L, 0, 0, 0)),
"cannot run config. file: %s", lua_tostring(L, -1));
// Setup and check connection to USB Device.
// TODO get usb device settings from usb_device.lua
int libusb_log = LIBUSB_LOG_LEVEL_NONE; // 0: default no msgs ever printed
libusb_log = getglobint(L, "libusb_log");
// Lua script arg to set different libusb debugging options.
check(!(luaL_loadfile(L, LUA_SCRIPT_USB) || lua_pcall(L, 0, 0, 0)),
"Cannot run config. file: %s", lua_tostring(L, -1));
//any value > 0 also prints debug statements in open_usb_device function
check( ((libusb_log >= LIBUSB_LOG_LEVEL_NONE) && (libusb_log <=LIBUSB_LOG_LEVEL_DEBUG)),
"Invalid LIBUSB_LOG_LEVEL: %d, must be from 0 to 4", libusb_log );
// Any value > 0 for libusb_log also prints debug statements in open_usb_device function.
libusb_log = getglobint(L, "libusb_log");
check(((libusb_log >= LIBUSB_LOG_LEVEL_NONE) && (libusb_log <= LIBUSB_LOG_LEVEL_DEBUG)),
"Invalid LIBUSB_LOG_LEVEL: %d, must be from 0 to 4", libusb_log );
//TODO get usb device settings from usb_device.lua
transfer = usb_init_inldevice(context, libusb_log);
check_mem(transfer);
check(transfer->handle != NULL, "Unable to open INL retro-prog usb device handle.");
//open INL retro prog with firmware version 2.0 or greater
//if (K_value != NULL) {
// //TODO take K_value option to connect to different version kazzo
//}
transfer->handle = open_usb_device( context, libusb_log );
check( transfer->handle != NULL, "Unable to open INL retro-prog usb device handle.");
//provide dictionary.c with pointer to transfer so it can update it's local pointer
//init_dictionary( transfer );
//usb device is open, pass args and control over to lua
if (s_value) {
check( !(luaL_loadfile(L, s_value) || lua_pcall(L, 0, 0, 0)),
"cannot run config. file: %s", lua_tostring(L, -1));
}
// Pass args to Lua
lua_pushstring(L, opts->console_name);
lua_setglobal(L, "console_name");
lua_pushstring(L, opts->mapper_name);
lua_setglobal(L, "mapper_name");
lua_pushstring(L, opts->dump_filename);
lua_setglobal(L, "dump_filename");
lua_pushstring(L, opts->program_filename);
lua_setglobal(L, "flash_filename");
// USB device is open, pass args and control over to Lua.
// If lua_filename isn't set from args, use default script.
const char *DEFAULT_SCRIPT = "scripts/inlretro.lua";
char *script = DEFAULT_SCRIPT;
if (opts->lua_filename) {
script = opts->lua_filename;
}
check(!(luaL_loadfile(L, script) || lua_pcall(L, 0, 0, 0)),
"cannot run config. file: %s", lua_tostring(L, -1));
//program flow doesn't come back to this point until script call ends/returns
/*
@ -407,36 +487,13 @@ int main(int argc, char *argv[])
}
*/
//close:
lua_close(L);
close_usb( context, transfer->handle);
free(transfer);
// if(rom->fileptr != NULL){
// //close file
// fclose(rom->fileptr);
// }
cleanup(context, transfer, L);
return 0;
error: //checks goto error when failed
printf("main program went to error\n");
if ( transfer != NULL )
free(transfer);
if ( L != NULL )
lua_close(L);
//
// close_usb( context, transfer->handle);
// if(rom->fileptr != NULL){
// fclose(rom->fileptr);
// }
// 'check' macros goto this label if they fail.
error:
printf("Fatal error encountered, exiting.\n");
cleanup(context, transfer, L);
return 1;
}

View File

@ -22,7 +22,7 @@ static libusb_device_handle *lua_usb_handle = NULL;
//LIBUSB_ENDPOINT_IN In: device-to-host.
//LIBUSB_ENDPOINT_OUT Out: host-to-device.
libusb_device_handle * open_usb_device( libusb_context *context, int log_level )
libusb_device_handle *open_usb_device( libusb_context *context, int log_level )
{
int rv = 0;
@ -90,12 +90,12 @@ libusb_device_handle * open_usb_device( libusb_context *context, int log_level )
const char *rprog_prod = "INL Retro-Prog";
uint16_t min_fw_ver = 0x200;
if (log_level>0) printf("Searching %d total devices\n", dev_count-1);
if (log_level>0) printf("Searching %ld total devices\n", dev_count-1);
for( i=0; i<dev_count; i++) {
device = device_list[i];
if (log_level>0) printf("getting dev desc #%d ", i);
if (log_level>0) printf("getting dev desc #%zd ", i);
rv = libusb_get_device_descriptor( device, &desc);
check( rv == LIBUSB_SUCCESS, "Unable to get device #%d descriptor: %s", i, libusb_strerror(rv));
check( rv == LIBUSB_SUCCESS, "Unable to get device #%zd descriptor: %s", i, libusb_strerror(rv));
if (log_level>0) printf("checking %x vendor ", desc.idVendor);
if (log_level>0) printf("checking %x product\n", desc.idProduct);