x49gp/main.c
claudiol c61f11cff2 On behalf of: 3298 - Applied 23 patches by 3298:
Misc changes, mostly fixes:
- fix ./newconfig systems other than OSX (broke in c8b823f)
- fix palette usage in 2-bit color mode (was broken ever since grayscale was implemented in 18e1003 and its improperly attributed copy f7913eb)
- fix continuing from breakpoints in the debugger (never worked, was exposed when the debugger was enabled in 9c1f2ed)
- restore the printf statements commented out in 9c1f2ed and hide them with #ifdefs instead
- close the server socket after accepting a debugger connection to allow another simultaneous debug session to be started using the same TCP port
- use the symbolic constant DEFAULT_GDBSTUB_PORT (already defined in gdb_stub.h as 1234) when starting the gdb server in main.c in place of the raw number 1234
- change Makefile to read the name of the firmware file from the file update.scp instead of hardcoding it; this allows users to switch to another firmware
by simply pasting it along with its accompanying update.scp into the x49gp directory

- Enhance port G (keyboard) handling to remember the value of output bits across periods with these bits configured as input
This fixes interaction with HPGCC3 keyboard routines, and it also fixes keys with eint==7 (assuming the stock firmware is in use)
needing a double-tap to work unless pressed very shortly after another keypress (the latter broke in b5f93ed)
- Get rid of the deprecated function warning by switching from gdk_pixbuf_new_from_inline to gdk_pixbuf_new_from_data (based on code by chwdt)
- Delete remaining now-redundant CVS files
- Don't release all buttons anymore if there are still physical keys and/or the left mouse button are holding some down
On the other hand, forcibly release all buttons when losing focus to avoid getting stuck in a state with buttons down
when they are not held down by anything; this would happen due to missed events while not in focus
- Add a context menu to the screen, containing only "Reset" and "Quit" items for now
- Ensure that the files backing flash, sram, and s3c2410-sram exist and have the correct size when opening them
Note that if the flash file does not exist, this will not fill it with the code that's supposed to be in there, obviously causing the calculator to crash. That's an improvement for later.
- Allow the config system to fill not only numbers, but also strings (including filenames) with default values
basename is excluded, but it's planned to be dropped entirely.
- Add an "install" target to the Makefile
- Implement a more generic command-line parser for substantially improved flexibility
- Also adds a proper help option, though the manual referenced in the corresponding output (a manpage, hopefully) does not exist yet.
- Drop the "basename" config property in favor of interpreting relative paths in the config as relative to the config file's location
- Retire the "image" config property in favor of simply loading the image from next to the binary or from the install directory
- Split the UI name property into name (affecting only the window title) and type (affecting the UI image and in the future also the default bootcode) properties
- Change the default calculator type to the 50g everywhere, which probably matches today's user expectations better than the 49g+.
- Create a flash file from the calculator model's appropriate boot file if it does not exist, relying on the bootcode to detect the absence of a firmware
  The bootcode will complain about the missing firmware and enter update mode, so the user needs to supply their favorite firmware version and point the bootcode's updater to it.
  The easiest way is probably pointing the emulated SD card at a directory containing the firmware and its accompanying update.scp file, and then starting the SD-based update.
- Add SD mount / unmount options to the right-click / menu-key popup menu
- Remove most of the old script-based config-generating system since the binary now has these capabilities as well
- Add an applications menu item for installing
- Keep some debug output on stderr and a huge vvfat.log file from showing up when not debugging x49gp itself
- Allow (re-)connecting a debugger to a running session
  This is done through the right-click / menu-key popup menu.
  To avoid confusion due to the accidental clicks leading to an unresponsive interface (caused by waiting for the debugger to connect),
  this option is hidden unless option -d or its new companion -D (same as -d, but does not start the debug interface right away) is present.
- Improved support for hardware keyboards
- Update README.md, add manpage, rename other README files to TODO to reflect their contents
2018-05-07 17:32:14 -04:00

701 lines
14 KiB
C

/* $Id: main.c,v 1.30 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <x49gp.h>
#include <x49gp_ui.h>
#include <memory.h>
#include <s3c2410.h>
#include <s3c2410_power.h>
#include <s3c2410_timer.h>
#include <x49gp_timer.h>
#include "gdbstub.h"
static x49gp_t *x49gp;
#ifdef QEMU_OLD // LD TEMPO HACK
extern
#endif
CPUState *__GLOBAL_env;
int semihosting_enabled = 1;
/* LD TEMPO HACK */
#ifndef QEMU_OLD
uint8_t *phys_ram_base;
int phys_ram_size;
ram_addr_t ram_size = 0x80000; // LD ???
/* vl.c */
int singlestep;
#if !(defined(__APPLE__) || defined(_POSIX_C_SOURCE) && !defined(__sun__))
static void *oom_check(void *ptr)
{
if (ptr == NULL) {
abort();
}
return ptr;
}
#endif
void *qemu_memalign(size_t alignment, size_t size)
{
#if defined(__APPLE__) || defined(_POSIX_C_SOURCE) && !defined(__sun__)
int ret;
void *ptr;
ret = posix_memalign(&ptr, alignment, size);
if (ret != 0)
abort();
return ptr;
#elif defined(CONFIG_BSD)
return oom_check(valloc(size));
#else
return oom_check(memalign(alignment, size));
#endif
}
void qemu_init_vcpu(void *_env)
{
CPUState *env = _env;
env->nr_cores = 1;
env->nr_threads = 1;
}
int qemu_cpu_self(void *env)
{
return 1;
}
void qemu_cpu_kick(void *env)
{
}
void armv7m_nvic_set_pending(void *opaque, int irq)
{
abort();
}
int armv7m_nvic_acknowledge_irq(void *opaque)
{
abort();
}
void armv7m_nvic_complete_irq(void *opaque, int irq)
{
abort();
}
void gdb_register_coprocessor(CPUState * env,
void * get_reg, void * set_reg,
int num_regs, const char *xml, int g_pos)
{
fprintf(stderr, "TODO: %s\n", __FUNCTION__);
}
#endif /* !QEMU_OLD */
void *
qemu_malloc(size_t size)
{
return malloc(size);
}
void *
qemu_mallocz(size_t size)
{
void *ptr;
ptr = qemu_malloc(size);
if (NULL == ptr)
return NULL;
memset(ptr, 0, size);
return ptr;
}
void
qemu_free(void *ptr)
{
free(ptr);
}
void *
qemu_vmalloc(size_t size)
{
#if defined(__linux__)
void *mem;
if (0 == posix_memalign(&mem, sysconf(_SC_PAGE_SIZE), size))
return mem;
return NULL;
#else
return valloc(size);
#endif
}
#ifdef QEMU_OLD
int
term_vprintf(const char *fmt, va_list ap)
{
return vprintf(fmt, ap);
}
int
term_printf(const char *fmt, ...)
{
va_list ap;
int n;
va_start(ap, fmt);
n = vprintf(fmt, ap);
va_end(ap);
return n;
}
#endif
#define SWI_Breakpoint 0x180000
#ifdef QEMU_OLD
int
do_arm_semihosting(CPUState *env, uint32_t number)
#else
uint32_t
do_arm_semihosting(CPUState *env)
#endif
{
#ifndef QEMU_OLD
uint32_t number;
if (env->thumb) {
number = lduw_code(env->regs[15] - 2) & 0xff;
} else {
number = ldl_code(env->regs[15] - 4) & 0xffffff;
}
#endif
switch (number) {
case SWI_Breakpoint:
break;
case 0:
#ifdef DEBUG_X49GP_SYSCALL
printf("%s: SWI LR %08x: syscall %u: args %08x %08x %08x %08x %08x %08x %08x\n",
__FUNCTION__, env->regs[14], env->regs[0],
env->regs[1], env->regs[2], env->regs[3],
env->regs[4], env->regs[5], env->regs[6],
env->regs[7]);
#endif
#if 1
switch (env->regs[0]) {
case 305: /* Beep */
printf("%s: BEEP: frequency %u, time %u, override %u\n",
__FUNCTION__, env->regs[1], env->regs[2], env->regs[3]);
gdk_beep();
env->regs[0] = 0;
return 1;
case 28: /* CheckBeepEnd */
env->regs[0] = 0;
return 1;
case 29: /* StopBeep */
env->regs[0] = 0;
return 1;
default:
break;
}
#endif
break;
default:
break;
}
return 0;
}
void
x49gp_set_idle(x49gp_t *x49gp, x49gp_arm_idle_t idle)
{
#ifdef DEBUG_X49GP_ARM_IDLE
if (idle != x49gp->arm_idle) {
printf("%s: arm_idle %u, idle %u\n", __FUNCTION__, x49gp->arm_idle, idle);
}
#endif
x49gp->arm_idle = idle;
if (x49gp->arm_idle == X49GP_ARM_RUN) {
x49gp->env->halted = 0;
} else {
x49gp->env->halted = 1;
#ifdef QEMU_OLD
cpu_interrupt(x49gp->env, CPU_INTERRUPT_EXIT);
#else
cpu_exit(x49gp->env);
#endif
}
}
static void
arm_sighnd(int sig)
{
switch (sig) {
case SIGUSR1:
// stop_simulator = 1;
// x49gp->arm->CallDebug ^= 1;
break;
default:
fprintf(stderr, "%s: sig %u\n", __FUNCTION__, sig);
break;
}
}
void
x49gp_gtk_timer(void *data)
{
while (gtk_events_pending()) {
// printf("%s: gtk_main_iteration_do()\n", __FUNCTION__);
gtk_main_iteration_do(FALSE);
}
x49gp_mod_timer(x49gp->gtk_timer,
x49gp_get_clock() + X49GP_GTK_REFRESH_INTERVAL);
}
void
x49gp_lcd_timer(void *data)
{
x49gp_t *x49gp = data;
int64_t now, expires;
// printf("%s: lcd_update\n", __FUNCTION__);
x49gp_lcd_update(x49gp);
gdk_flush();
now = x49gp_get_clock();
expires = now + X49GP_LCD_REFRESH_INTERVAL;
// printf("%s: now: %lld, next update: %lld\n", __FUNCTION__, now, expires);
x49gp_mod_timer(x49gp->lcd_timer, expires);
}
struct options {
char *config;
int debug_port;
int start_debugger;
int more_options;
};
struct option_def;
typedef int (*option_action)(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
struct option_def {
option_action action;
char *longname;
char shortname;
};
static int action_help(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
static int action_debuglater(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
static int action_debug(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
static int action_unknown_with_param(struct options *opt,
struct option_def *match, char *this_opt,
char *param, char *progname);
static int action_longopt(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
static int action_endopt(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname);
struct option_def option_defs[] = {
{ action_help, "help", 'h' },
{ action_debuglater, "enable-debug", 'D' },
{ action_debug, "debug", 'd' },
{ action_longopt, NULL, '-' },
{ action_unknown_with_param, NULL, '=' },
{ action_endopt, "", '\0' }
};
static void
warn_unneeded_param(struct option_def *match, char *this_opt)
{
if (this_opt[1] == '-') {
fprintf(stderr, "The option \"--%s\" does not support"
" parameters\n", match->longname);
} else
fprintf(stderr, "The option '-%c' does not support parameters\n",
match->shortname);
}
static int
action_help(struct options *opt, struct option_def *match, char *this_opt,
char *param, char *progname)
{
if (param != NULL)
warn_unneeded_param(match, this_opt);
fprintf(stderr, "Emulator for HP 49G+ / 50G calculators\n"
"Usage: %s [<options>] [<config-file>]\n"
"Valid options:\n"
" -D, --enable-debug[=<port] enable the debugger interface\n"
" (default port: %u)\n"
" -d, --debug[=<port>] like -D, but also start the"
" debugger immediately\n"
" -h, --help print this message and exit\n"
"The config file is formatted as INI file and contains the"
" settings for which\n"
"persistence makes sense, like calculator model, CPU"
" registers, etc.\n"
"If the config file is omitted, ~/.%s/config is used.\n"
"Please consult the manual for more details on config file"
" settings.\n", progname, DEFAULT_GDBSTUB_PORT, progname);
exit(0);
}
static int
action_debuglater(struct options *opt, struct option_def *match, char *this_opt,
char *param, char *progname)
{
char *end;
int port;
if (param == NULL) {
if (opt->debug_port == 0)
opt->debug_port = DEFAULT_GDBSTUB_PORT;
return FALSE;
}
port = strtoul(param, &end, 0);
if ((end == param) || (*end != '\0')) {
fprintf(stderr, "Invalid port \"%s\", using default\n", param);
if (opt->debug_port == 0)
opt->debug_port = DEFAULT_GDBSTUB_PORT;
return TRUE;
}
if (opt->debug_port != 0 && opt->debug_port != DEFAULT_GDBSTUB_PORT)
fprintf(stderr, "Additional debug port \"%s\" specified,"
" overriding\n", param);
opt->debug_port = port;
return TRUE;
}
static int
action_debug(struct options *opt, struct option_def *match, char *this_opt,
char *param, char *progname)
{
opt->start_debugger = TRUE;
return action_debuglater(opt, match, this_opt, param, progname);
}
static int
action_longopt(struct options *opt, struct option_def *match, char *this_opt,
char *param, char *progname)
{
int i;
char *test_str, *option_str;
if (this_opt[1] != '-' || param != NULL) {
fprintf(stderr, "Unrecognized option '-', ignoring\n");
return FALSE;
}
for (i = 0; i < sizeof(option_defs) / sizeof(option_defs[0]); i++) {
if (option_defs[i].longname == NULL)
continue;
test_str = option_defs[i].longname;
option_str = this_opt + 2;
while (*test_str != '\0' && *test_str == *option_str) {
test_str++;
option_str++;
}
if (*test_str != '\0') continue;
switch (*option_str) {
case '\0':
(option_defs[i].action)(opt, option_defs + i, this_opt,
NULL, progname);
return TRUE;
case '=':
(option_defs[i].action)(opt, option_defs + i, this_opt,
option_str+2, progname);
return TRUE;
}
}
fprintf(stderr, "Unrecognized option \"%s\", ignoring\n", this_opt + 2);
return TRUE;
}
static int
action_unknown_with_param(struct options *opt, struct option_def *match,
char *this_opt, char *param, char *progname)
{
return TRUE;
}
static int
action_endopt(struct options *opt, struct option_def *match, char *this_opt,
char *param, char *progname)
{
opt->more_options = FALSE;
return TRUE;
}
static void
parse_shortopt(struct options *opt, char *this_opt, char *progname)
{
char *option = this_opt + 1;
char *param;
int i;
if (*option == '\0') {
fprintf(stderr,
"Empty option present, ignoring\n");
return;
}
do {
for (i = 0; i < sizeof(option_defs) / sizeof(option_defs[0]);
i++) {
if (*option == option_defs[i].shortname) {
if (*(option + 1) == '=') {
param = option + 2;
} else {
param = NULL;
}
if ((option_defs[i].action)(opt, option_defs + i,
this_opt, param,
progname))
return;
break;
}
}
if (i == sizeof(option_defs) / sizeof(option_defs[0]))
fprintf(stderr,
"Unrecognized option '%c', ignoring\n",
*option);
option++;
} while (*option != '\0');
}
static void
parse_options(struct options *opt, int argc, char **argv, char *progname)
{
opt->more_options = TRUE;
while (argc > 1) {
switch (argv[1][0]) {
case '\0':
break;
break;
case '-':
if (opt->more_options) {
parse_shortopt(opt, argv[1], progname);
break;
}
/* FALL THROUGH */
default:
if (opt->config != NULL) {
fprintf(stderr,
"Additional config file \"%s\""
" specified, overriding\n",
argv[1]);
}
opt->config = argv[1];
}
argc--;
argv++;
}
}
void
ui_sighnd(int sig)
{
switch (sig) {
case SIGINT:
case SIGQUIT:
case SIGTERM:
x49gp->arm_exit = 1;
#ifdef QEMU_OLD
cpu_interrupt(x49gp->env, CPU_INTERRUPT_EXIT);
#else
cpu_exit(x49gp->env);
#endif
break;
}
}
int
main(int argc, char **argv)
{
char *progname, *progpath;
int error;
struct options opt;
const char *home;
progname = g_path_get_basename(argv[0]);
progpath = g_path_get_dirname(argv[0]);
gtk_init(&argc, &argv);
opt.config = NULL;
opt.debug_port = 0;
opt.start_debugger = FALSE;
parse_options(&opt, argc, argv, progname);
x49gp = malloc(sizeof(x49gp_t));
if (NULL == x49gp) {
fprintf(stderr, "%s: %s:%u: Out of memory\n",
progname, __FUNCTION__, __LINE__);
exit(1);
}
memset(x49gp, 0, sizeof(x49gp_t));
#ifdef DEBUG_X49GP_MAIN
fprintf(stderr, "_SC_PAGE_SIZE: %08lx\n", sysconf(_SC_PAGE_SIZE));
printf("%s:%u: x49gp: %p\n", __FUNCTION__, __LINE__, x49gp);
#endif
INIT_LIST_HEAD(&x49gp->modules);
x49gp->progname = progname;
x49gp->progpath = progpath;
x49gp->clk_tck = sysconf(_SC_CLK_TCK);
x49gp->emulator_fclk = 75000000;
x49gp->PCLK_ratio = 4;
x49gp->PCLK = 75000000 / 4;
#ifdef QEMU_OLD
x49gp->env = cpu_init();
#else
//cpu_set_log(0xffffffff);
cpu_exec_init_all(0);
x49gp->env = cpu_init("arm926");
#endif
__GLOBAL_env = x49gp->env;
// cpu_set_log(cpu_str_to_log_mask("all"));
x49gp_timer_init(x49gp);
x49gp->gtk_timer = x49gp_new_timer(X49GP_TIMER_REALTIME,
x49gp_gtk_timer, x49gp);
x49gp->lcd_timer = x49gp_new_timer(X49GP_TIMER_VIRTUAL,
x49gp_lcd_timer, x49gp);
x49gp_ui_init(x49gp);
x49gp_s3c2410_arm_init(x49gp);
x49gp_flash_init(x49gp);
x49gp_sram_init(x49gp);
x49gp_s3c2410_init(x49gp);
if (x49gp_modules_init(x49gp)) {
exit(1);
}
if (opt.config == NULL) {
char config_dir[strlen(progname) + 2];
home = g_get_home_dir();
sprintf(config_dir, ".%s", progname);
opt.config = g_build_filename(home, config_dir,
"config", NULL);
}
x49gp->basename = g_path_get_dirname(opt.config);
x49gp->debug_port = opt.debug_port;
error = x49gp_modules_load(x49gp, opt.config);
if (error) {
if (error != -EAGAIN) {
exit(1);
}
x49gp_modules_reset(x49gp, X49GP_RESET_POWER_ON);
}
// x49gp_modules_reset(x49gp, X49GP_RESET_POWER_ON);
signal(SIGINT, ui_sighnd);
signal(SIGTERM, ui_sighnd);
signal(SIGQUIT, ui_sighnd);
signal(SIGUSR1, arm_sighnd);
x49gp_set_idle(x49gp, 0);
// stl_phys(0x08000a1c, 0x55555555);
x49gp_mod_timer(x49gp->gtk_timer, x49gp_get_clock());
x49gp_mod_timer(x49gp->lcd_timer, x49gp_get_clock());
if(opt.debug_port != 0 && opt.start_debugger) {
gdbserver_start(opt.debug_port);
gdb_handlesig(x49gp->env, 0);
}
x49gp_main_loop(x49gp);
x49gp_modules_save(x49gp, opt.config);
x49gp_modules_exit(x49gp);
#if 0
printf("ClkTicks: %lu\n", ARMul_Time(x49gp->arm));
printf("D TLB: hit0 %lu, hit1 %lu, search %lu (%lu), walk %lu\n",
x49gp->mmu->dTLB.hit0, x49gp->mmu->dTLB.hit1,
x49gp->mmu->dTLB.search, x49gp->mmu->dTLB.nsearch,
x49gp->mmu->dTLB.walk);
printf("I TLB: hit0 %lu, hit1 %lu, search %lu (%lu), walk %lu\n",
x49gp->mmu->iTLB.hit0, x49gp->mmu->iTLB.hit1,
x49gp->mmu->iTLB.search, x49gp->mmu->iTLB.nsearch,
x49gp->mmu->iTLB.walk);
#endif
return 0;
}