/* * ============================================================================ * Title: MAIN * Author: J. Zbiciak * $Id: jzintv.c,v 1.41 2001/11/02 02:00:02 im14u2c Exp $ * ============================================================================ * Main Simulator Driver File * ============================================================================ * This doesn't do much yet. :-) * ============================================================================ */ static const char rcs_id[]="$Id: jzintv.c,v 1.41 2001/11/02 02:00:02 im14u2c Exp $"; #include "config.h" #ifndef macintosh #include /* Ack! Needed for hacks to main() */ #endif #include #include "macros.h" #include "plat/plat.h" #include "periph/periph.h" #include "cp1600/cp1600.h" #include "mem/mem.h" #include "icart/icart.h" #include "bincfg/bincfg.h" #include "bincfg/legacy.h" #include "pads/pads.h" #include "pads/pads_cgc.h" #include "pads/pads_intv2pc.h" #include "gfx/gfx.h" #include "snd/snd.h" #include "stic/stic.h" #include "ay8910/ay8910.h" #include "speed/speed.h" #include "debug/debug.h" #include "event/event.h" #include "file/file.h" #include "ivoice/ivoice.h" #include "cfg/cfg.h" #ifdef macintosh # include "console.h" #endif double elapsed(int); /*volatile int please_die = 0;*/ /*volatile int reset = 0;*/ static cfg_t *intv; static uint_32 checksum(uint_16 *mem, int len) UNUSED; /*static void usage(void);*/ void dump_state(void); /* * ============================================================================ * RELEASE -- Program name / release * ============================================================================ */ static char * release(void) { char *s2; const char *Name = "$Name: $", *s1; static char name_buf[64]; static int named = 0; if (named) return name_buf; s1 = Name; while (*s1 && *s1 != ':') s1++; if (*s1 == ':') s1++; while (*s1 && isspace(*s1) && *s1 != '$') s1++; if (!*s1 || *s1 == '$') { strncpy(name_buf, "[unreleased]", sizeof(name_buf)-1); name_buf[sizeof(name_buf)-1] = 0; } else { s2 = name_buf; while (s2 < (name_buf + sizeof(name_buf) - 1) && *s1 && *s1!='$') *s2++ = *s1++; *s2++ = 0; } named = 1; return name_buf; } #if 0 /* * ============================================================================ * CART_NAME -- Look for a game name in a cartridge image. * ============================================================================ */ static char * cart_name(uint_16 *game, uint_16 addr, uint_16 size) { static char name_buf[64]; char * s1; uint_16 *s2; uint_16 ofs; int len = 0; s2 = game + 0x000A; ofs = (s2[1] << 8) + (s2[0]) - addr + 1; if (ofs > size) { strcpy(name_buf, "[unknown]"); return name_buf; } s2 = game + ofs; s1 = name_buf; while ((*s2 & 0xFF) && len++ < 60) *s1++ = *s2++; return name_buf; } #endif /* * ============================================================================ * GRACEFUL_DEATH -- Die gracefully. * ============================================================================ */ static void graceful_death(int x) { if (intv->do_exit < 2) { if (intv->do_exit) fprintf(stderr, "\nOUCH!\n"); fprintf(stderr, "\nRequesting exit: Received signal %d.\n" "(You may need to press enter if you're at a prompt.)\n", x); } else { fprintf(stderr, "\nReceived 3 signals: Aborting on signal %d.\n", x); exit(1); } intv->do_exit++; } /* * ============================================================================ * ELAPSED -- Returns amount of time that's elapsed since the program * started, in CP1610 clock cycles (895kHz) * ============================================================================ */ double elapsed(int restart) { static double start; static int init = 0; double now; if (!init || restart) { start = get_time(); init = 1; } now = get_time(); return (now - start) * 894886.25; } /* * ============================================================================ * In the beginning, there was a main.... * ============================================================================ */ //#ifdef WIN32 //main(int argc, char *argv[]) //#else int main(int argc, char *argv[]) //#endif { int iter = 0, arg; double cycles = 0, rate, irate, then, now, icyc; uint_32 s_cnt = 0; int paused = 0; /* -------------------------------------------------------------------- */ /* On Windows, let's try to get back our stdio. */ /* -------------------------------------------------------------------- */ #ifdef WIN32 { FILE *newfp; newfp = fopen("CON:", "w"); if (newfp) *stdout = *newfp; newfp = fopen("CON:", "w"); if (newfp) *stderr = *newfp; newfp = fopen("CON:", "r"); if (newfp) *stdin = *newfp; } #endif /* -------------------------------------------------------------------- */ /* Platform-specific initialization. */ /* -------------------------------------------------------------------- */ if (plat_init()) { fprintf(stderr, "Error initializing.\n"); exit(1); } fflush(stdout); /* -------------------------------------------------------------------- */ /* Parse arguments. The commandline is of the following form: */ /* */ /* jzintv [-e exec.bin] [-g grom.bin] [-d] [game.bin] */ /* */ /* -e specifies a different exec.bin file to load. */ /* -g specifies a different grom.bin file to load. */ /* -d enables debugging mode. */ /* -s toggle the sprite-hack */ /* -p toggle the pad-hack */ /* -r toggle rate control (speed_t) */ /* -a# specifies audio sampling rate (0 == off) */ /* -f file specifies a file to dump raw audio samples to. */ /* -w# changes the sound-averaging window (AY8910 only) */ /* -i### set hand-controller interface port #. */ /* -x toggle full-screen mode */ /* -r# Set display resolution: 0 = 320x200, 1 = 640x480 */ /* */ /* TODO: Do this right w/ getopt() or the like. */ /* -------------------------------------------------------------------- */ #ifdef macintosh argc = ccommand( &argv ); #endif /* -------------------------------------------------------------------- */ /* Temporary hack so ^C, kill work. */ /* -------------------------------------------------------------------- */ signal( 2, graceful_death); signal(15, graceful_death); /* -------------------------------------------------------------------- */ /* Go get an Intellivision! */ /* -------------------------------------------------------------------- */ intv = cfg_init(argc, argv); fflush(stdout); /* -------------------------------------------------------------------- */ /* On Windows, open \\.\UserPort\ if the program has requested */ /* INTV2PC access. This causes the UserPort driver to yield port */ /* access, if UserPort is active. */ /* -------------------------------------------------------------------- */ #ifdef WIN32 if (intv->i2pc0_port || intv->i2pc1_port) { FILE *UserPortFP; UserPortFP = fopen("\\\\.\\UserPort\\", "r"); if (!UserPortFP) { #if 0 perror("fopen()"); fprintf ( stderr, "Warning: Unable to open \\\\.\\UserPort\\.\n" " Under WinNT/2K/XP, jzIntv may be unable to use INTV2PC.\n" " Win95/98/ME users can ignore this warning.\n" ); #endif } } #endif /* -------------------------------------------------------------------- */ /* Set the window title. If we recognize a standard Intellivision */ /* ROM header at 0x5000, then also include the cartridge name. */ /* -------------------------------------------------------------------- */ #if 0 snprintf(title, 128, "jzintv %.32s : %.64s", release(), cart_name(game_img, 0x5000, 0x1000)); gfx_set_title(&gfx,title); #endif /* -------------------------------------------------------------------- */ /* Run the simulator. */ /* -------------------------------------------------------------------- */ if (intv->debugging) debug_tk((periph_p)&(intv->debug),1); printf("Starting jzIntv...\n"); fflush(stdout); sleep(1); restart: iter = 1; now = elapsed(1); while (now == elapsed(0)) /* Burn some time. */ ; icyc = 0; s_cnt = 0; cycles = 0; if (intv->rate_ctl) speed_resync(&(intv->speed)); if (!intv->debugging) intv->debug.step_count = ~0U; paused = 0; while (intv->do_exit == 0) { uint_64 max_step; if (intv->do_reset) { max_step = 1000; /* arbitrary */ } else { if (s_cnt) { s_cnt = 0; periph_reset(intv->intv); } /* This is incredibly hackish, and is an outgrowth of my * decoupled tick architecture. */ max_step = intv->stic.next_phase - intv->stic.stic_cr.now; #if 1 if (intv->cp1600.periph.now > intv->stic.stic_cr.now) { uint_64 diff; diff = intv->cp1600.periph.now - intv->stic.stic_cr.now; if (diff < max_step) max_step -= diff; } else if (intv->stic.stic_cr.now > intv->cp1600.periph.now) { uint_64 diff; diff = intv->stic.stic_cr.now - intv->cp1600.periph.now; if (diff < max_step) max_step -= diff; } #endif if (max_step < 5) max_step = 5; } //printf("cpu.now = %-8d stic.now = %-8d diff = %-8d step = %-8d\n", (int)intv->cp1600.periph.now, (int)intv->stic.stic_cr.now, (int)intv->cp1600.periph.now-(int)intv->stic.stic_cr.now, (int)max_step); // if (intv->do_pause) { paused = !paused; intv->do_pause = 0; if (!paused) speed_resync(&(intv->speed)); } if (intv->event.change_kbd) { if (intv->event.change_kbd == 5) { intv->event.cur_kbd = (intv->event.cur_kbd + 1) & 3; } else if (intv->event.change_kbd == 6) { intv->event.cur_kbd = (intv->event.cur_kbd - 1) & 3; } else { intv->event.cur_kbd = (intv->event.change_kbd - 1) & 3; } fprintf(stderr, "Change keyboard to %d\n", intv->event.cur_kbd); intv->event.change_kbd = 0; memset(intv->pad0.l, 0, sizeof(intv->pad0.l)); memset(intv->pad0.r, 0, sizeof(intv->pad0.r)); memset(intv->pad0.k, 0, sizeof(intv->pad0.k)); memset(intv->pad1.l, 0, sizeof(intv->pad1.l)); memset(intv->pad1.r, 0, sizeof(intv->pad1.r)); memset(intv->pad1.k, 0, sizeof(intv->pad1.k)); } if (paused) { intv->event.periph.tick((periph_p)&(intv->event), 0); plat_delay(1000/60); } else cycles += periph_tick((periph_p)(intv->intv), max_step); if (!intv->debugging && intv->debug.step_count == 0) intv->debug.step_count = ~0U; if (!intv->debugging && !intv->do_reset && (iter++&1023) == 0) { then = now; now = elapsed(0); rate = (cycles / now); if (now - then > 0.01) { irate = (cycles - icyc) / (now - then); icyc = cycles; printf("Rate: [%6.2f%% %6.2f%%] Drop Gfx:[%6.2f%% %6d] " "Snd:[%6.2f%% %6d %6.3f]" //#ifdef WIN32 //"\n", //#else "\r", //#endif rate * 100., irate * 100., 100. * intv->gfx.tot_dropped_frames / intv->gfx.tot_frames, (int)intv->gfx.tot_dropped_frames, 100. * intv->snd.mixbuf.tot_drop / intv->snd.tot_frame, (int)intv->snd.mixbuf.tot_drop, (double)intv->snd.tot_dirty / intv->snd.tot_frame); #if 0 printf("speed: min=%-8d max=%-8d thresh=%-8.1f frame=%-8d\n", intv->speed.periph.min_tick, intv->speed.periph.max_tick, intv->speed.threshold * 1e6, intv->gfx.tot_frames); #endif fflush(stdout); } if ((iter&2047)==0) { elapsed(1); cycles = icyc = 0; } } if (intv->do_reset) { intv->cp1600.r[7] = 0x1000; gfx_vid_enable(&(intv->gfx), 0); s_cnt++; } else { if (s_cnt > 1500) break; s_cnt = 0; } } s_cnt = 0x2A3A4A5A; arg = 0; gfx_set_bord (&(intv->gfx), 0); gfx_vid_enable(&(intv->gfx), 1); while (intv->do_exit == 0) { int i, j; uint_8 p; if (intv->do_reset) arg = 1; if (!intv->do_reset && arg) { p = intv->stic.raw[0x2C] & 15; for (i = 0; i < 160 * 200; i++) intv->stic.disp[i] = p; goto restart; } for (i = 0; i < 160 * 200; i++) { for (j = 0; j < 16; j++) s_cnt = (s_cnt << 1) | (1 & ((s_cnt >> 28) ^ (s_cnt >> 30))); p = (s_cnt & 0xF) + 16; if (p == 16) p = 0; intv->stic.disp[i] = p; } intv->gfx.dirty = 1; intv->gfx.periph.tick ((periph_p)&(intv->gfx), 0); intv->event.periph.tick((periph_p)&(intv->event), 0); plat_delay(1000/60); } if (intv->do_exit) printf("\nExited on user request.\n"); return 0; } #include "cp1600/op_decode.h" #include "cp1600/op_exec.h" /* * ============================================================================ * DUMP_STATE -- Called by op_exec:fn_HLT. Dumps state of universe. * ============================================================================ */ void dump_state(void) { FILE *f; int addr, data, i, j; f = fopen("dump.mem","wb"); if (!f) { perror("fopen(\"dump.mem\", \"w\")"); printf("couldn't open dump.mem, not dumping memory.\n"); } else { intv->debug.show_rd = 0; intv->debug.show_wr = 0; for (addr = 0; addr <= 0xFFFF; addr++) { data = periph_peek((periph_p)intv->intv, (periph_p)intv->intv, addr, 0); fputc((data >> 8) & 0xFF, f); fputc((data ) & 0xFF, f); } fclose(f); } f = fopen("dump.cpu", "wb"); if (!f) { perror("fopen(\"dump.cpu\", \"w\")"); printf("couldn't open dump.cpu, not dumping cpu info.\n"); return; } fprintf(f, "CP-1600 State Dump\n"); fprintf(f, "Tot Cycles: %lld\n", intv->cp1600.tot_cycle); fprintf(f, "Tot Instrs: %lld\n", intv->cp1600.tot_instr); fprintf(f, "Tot Cache: %d\n", intv->cp1600.tot_cache); fprintf(f, "Tot NonCache: %d\n", intv->cp1600.tot_noncache); fprintf(f, "Registers: %.4x %.4x %.4x %.4x %.4x %.4x %.4x %.4x\n", intv->cp1600.r[0], intv->cp1600.r[1], intv->cp1600.r[2], intv->cp1600.r[3], intv->cp1600.r[4], intv->cp1600.r[5], intv->cp1600.r[6], intv->cp1600.r[7]); fprintf(f, "Flags: S:%d C:%d O:%d Z:%d I:%d D:%d intr:%d irq:%d\n", intv->cp1600.S, intv->cp1600.C, intv->cp1600.O, intv->cp1600.Z, intv->cp1600.I, intv->cp1600.D, intv->cp1600.intr, intv->cp1600.req_bus.intrq); fprintf(f, "Cacheability Map:\n"); for (i = 0; i < 1 << (CP1600_MEMSIZE-CP1600_DECODE_PAGE - 5); i++) { addr = (i << (CP1600_DECODE_PAGE + 5)); fprintf(f, " %.4x-%.4x:", addr, addr+(32<cp1600.cacheable[i] >> j)); } fprintf(f, "\n"); } fprintf(f,"Decoded Instruction Map:\n"); for (i = 0; i < 1 << (CP1600_MEMSIZE - 6); i++) { addr = i << 6; fprintf(f, " %.4x-%.4x:", addr, addr + 63); for (j = 0; j < 64; j++) { fprintf(f, "%c", (void*)intv->cp1600.execute[addr+j]==(void*)fn_decode_1st?'-' : (void*)intv->cp1600.execute[addr+j]==(void*)fn_decode ?'N' : (void*)intv->cp1600.execute[addr+j]==(void*)fn_invalid ?'!' : 'C'); } fprintf(f, "\n"); } fclose(f); } /* * ============================================================================ * READ_ROM -- Reads a ROM image file, adjusting it for endian if * necessary * ============================================================================ */ int read_rom(char * filename, int size, uint_16 * image) { FILE *f; int tot = 0, block, addr; #ifdef macintosh # pragma unused( addr ) #endif memset(image, 0xFF, size * 2); f = fopen(filename, "rb"); if (!f) { fprintf(stderr, "Could not open '%s' for reading.\n", filename); perror("fopen()"); exit(1); } while (tot < size) { block = size - tot > 128 ? 128 : size - tot; block = fread(image + tot, 2, block, f); tot += block; if (block < 0) { fprintf(stderr, "Error while reading '%s'\n", filename); perror("fread()"); exit(1); } if (block == 0) break; } fclose(f); #ifdef _LITTLE_ENDIAN for (addr = 0; addr < size; addr++) { image[addr] = (0x00FF & (image[addr] >> 8)) | (0xFF00 & (image[addr] << 8)); } #endif printf("Read %d words from '%s'.\n", tot, filename); return tot; } /* * ============================================================================ * CHECKSUM -- Returns a simple checksum on a region of memory. * I was using this to try to find some bugs, assuming * memory corruption was happening. * ============================================================================ */ static uint_32 checksum(uint_16 *mem, int len) { uint_32 cksum = 0x2A3A4A5A; while (len-->0) { cksum ^= *mem; cksum = (cksum >> 1) | (cksum << 31); cksum += *mem++; } return cksum; } #if 0 /* * ============================================================================ * USAGE -- Prints a help message. Whee. * ============================================================================ */ static void usage(void) { printf ( "jzIntv %s\n\n" "usage: jzintv [-d] [-v] [-e exec_img] [-g grom_img] [game_img]\n" " jzintv [-?|-h]\n" "\n" " -d Enables debugger\n" " -s Toggles sprite-boxing\n" " -p Toggles pad-input sequencer\n" " -e Specifies alternate EXEC image\n" " -g Specifies alternate GROM image\n" " -2 Enables second PSG\n" " -a### Specifies audio sample rate. -a0 disables sound\n" " -w### Specifies window size for AY8910 sliding window avg\n" " -f raw Dumps audio to raw sample file 'raw'\n" " -? -h This help info.\n" #ifdef DIRECT_INTV2PC " -i### Enable Direct Hand-Controller Interface support on\n" " port number ###. -h alone defaults to 0x378.\n" #endif " -x Attempt full-screen mode.\n" " -z# Set display resolution (0 = 320x200, 1 = 640x480\n" "\n" " By default, jzintv looks for 'exec.bin', 'grom.bin' and\n" " 'game.bin' in the current directory. These are overridden\n" " by the command-line options given above.\n\n", release() ); return; } #endif /* ======================================================================== */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ======================================================================== */ /* Copyright (c) 1998-1999, Joseph Zbiciak */ /* ======================================================================== */