diff --git a/host/scripts/inlretro2.lua b/host/scripts/inlretro2.lua new file mode 100644 index 0000000..3402d9f --- /dev/null +++ b/host/scripts/inlretro2.lua @@ -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() \ No newline at end of file diff --git a/host/source/inlprog.c b/host/source/inlprog.c index 03456dd..a87088c 100644 --- a/host/source/inlprog.c +++ b/host/source/inlprog.c @@ -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; } diff --git a/host/source/usb_operations.c b/host/source/usb_operations.c index 7f0d673..2ffa57e 100644 --- a/host/source/usb_operations.c +++ b/host/source/usb_operations.c @@ -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; i0) 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);