home *** CD-ROM | disk | FTP | other *** search
-
- /*
- =========
- EMULATE.C
- =========
-
- Handle emulation functions --
-
- - Emulation loop
- - Stateflag handling
- - Interrupt functions
- */
-
- #define __EMULATE__
-
- //#include "winv9t9.h"
-
- #include <stdio.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <time.h>
- #include <unistd.h>
- #include <setjmp.h>
- #if __MWERKS__
- #include <time.h>
- #else
- #include <sys/time.h>
- #include <sys/types.h>
- #endif
-
- #include "v9t9_common.h"
- #include "v9t9.h"
- #include "9900.h"
- #include "grom.h"
- #include "vdp.h"
- #include "video.h"
- #include "sound.h"
- #include "cru.h"
- #include "roms.h"
- #include "keyboard.h"
- #include "emulate.h"
- #include "timer.h"
- #include "debugger.h"
- #include "memory.h"
- #include "command.h"
- #include "system.h"
- #include "9900st.h"
- #include "opcode_callbacks.h"
- #include "speech.h"
-
- #define _L LOG_CPU | LOG_INFO
-
- u32 features;
- u32 stateflag;
-
-
- /*
- * The emulator functions from the perspective of
- * a 3 MHz clock, which drives the instruction speed of
- * the processor, and secondarily, the 9901 timer, which
- * is derived from it.
- */
-
- /* Variables for controlling a "real time" emulation of the 9900
- processor. Each call to execute() sets an estimated cycle count
- for the instruction and parameters in "instcycles". We waste
- time in 1/BASE_EMULATOR_HZ second quanta to maintain the appearance of a
- 3.0 MHz clock. */
-
- int realtime = 0;
- u32 baseclockhz = 3300000;
-
- u32 instcycles; // cycles for each instruction
-
- u64 targetcycles; // target # cycles to be executed per second
- u64 totaltargetcycles; // total # target cycles expected
- u64 currentcycles = 0; // current cycles per 1/BASE_EMULATOR_HZ second
- u64 totalcurrentcycles; // total # current cycles executed
-
- u32 currenttime; /* time, [0..baseclockhz / BASE_EMULATOR_HZ) */
- u64 totalticks; /* total number of BASE_EMULATOR_HZ events passed */
-
- u32 executed; /* # instructions executed in this tick */
- u64 totalexecuted; /* total # executed */
-
-
- /* Old way of controlling time */
-
- unsigned long delaybetweeninstructions = 0;
-
-
- u64 delaycycles = 0; // delay cycles used to maintain time per 1/BASE_EMULATOR_HZ
-
- int allow_debug_interrupts = 0; // interrupt processor while debugging
-
- ///////////
-
- /*
- char *modulespath;
- char *romspath;
-
- char cpuromfilename[OS_NAMESIZE] = "994arom.bin";
- char cpugromfilename[OS_NAMESIZE] = "994agrom.bin";
-
- char modulegrom[OS_NAMESIZE] = "";
- char modulerom1[OS_NAMESIZE] = "";
- char modulerom2[OS_NAMESIZE] = "";
- */
-
- static void
- inserthacks(void)
- {
- if (!realtime && MEMORY_READ_WORD(0x496) == 0x45B) {
- logger(_L | 0, "inserting keyboard delay hack\n");
- MEMORY_WRITE_WORD(0x496, OP_KEYSLOW);
- }
- }
-
- void
- execution_pause(bool enable)
- {
- if (enable) {
- stateflag |= ST_PAUSE;
- } else {
- stateflag &= ~ST_PAUSE;
- }
- system_execution_paused(execution_paused());
- }
-
-
- #if 0
- static sigjmp_buf bogocalc;
- static void
- stop_bogo(int unused)
- {
- siglongjmp(bogocalc, 1);
- }
-
- static void
- calibrate_processor(void)
- {
- /* Calibrate processor. */
- unsigned char flag = 0;
- long long cnt;
- int tmtag = TM_UniqueTag();
- int killtimer = 0;
- int origtime;
-
- logger(_L | LOG_USER, "Calibrating processor... ");
-
- stateflag = 0;
-
- killtimer = TM_Start();
- origtime = TM_GetTicks();
- while (origtime == TM_GetTicks());
- if (sigsetjmp(bogocalc, ~0) == 0) {
- TM_SetEvent(tmtag, TM_HZ * BASE_EMULATOR_HZ, 0, TM_FUNC, stop_bogo);
- cnt = ~0;
- while (--cnt && !stateflag);
- }
- TM_ResetEvent(tmtag);
- if (killtimer)
- TM_Stop();
-
- bogocycles = (~cnt) / BASE_EMULATOR_HZ;
- logger(_L | LOG_USER | 0, "%Ld bogocycles.\n\n", bogocycles);
- }
- #endif
-
- /*************************************************/
-
- static bool calibrated_processor;
-
- static vmResult emulate9900_restart(void);
- static vmResult emulate9900_restop(void);
-
-
- static
- DECL_SYMBOL_ACTION(emulate_reset_computer)
- {
- /* Upon restart, save volatile RAMs and reload all ROMs,
- in case they were overwritten by a bug or something. ;) */
- if (task == csa_WRITE) {
- memory_volatile_save();
- memory_complete_load();
- stateflag &= ~ST_INTERRUPT;
- reset9901();
- init9900();
- hold9900pin(INTPIN_RESET);
- contextswitch(0);
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_debugger_enable)
- {
- if (task == csa_READ) {
- if (iter)
- return 0;
- command_arg_set_num(SYM_ARG_1st, debugger_enabled());
- } else {
- int val;
- command_arg_get_num(SYM_ARG_1st, &val);
- debugger_enable(val);
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_execution_pause)
- {
- if (task == csa_READ) {
- if (iter)
- return 0;
- command_arg_set_num(SYM_ARG_1st, execution_paused());
- } else {
- int val;
- command_arg_get_num(SYM_ARG_1st, &val);
- execution_pause(val);
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_single_step)
- {
- execution_pause(true);
- execution_pause(false);
- stateflag |= ST_SINGLESTEP;
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_change_clock_speed)
- {
- if (task == csa_WRITE) {
- emulate9900_restart();
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_set_pc)
- {
- int val;
- if (task == csa_READ)
- if (!iter) {
- command_arg_set_num(sym->args, pc);
- return 1;
- } else {
- return 0;
- }
-
- command_arg_get_num(sym->args, &val);
- if (verifypc(val)) {
- setandverifypc(val);
- } else {
- logger(_L | LOG_ERROR, "Cannot set PC to %04X, ignoring\n", val);
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_set_wp)
- {
- int val;
-
- if (task == csa_READ)
- if (!iter) {
- command_arg_set_num(sym->args, wp);
- return 1;
- } else {
- return 0;
- }
-
- command_arg_get_num(sym->args, &val);
- if (verifywp(val)) {
- setandverifywp(val);
- } else {
- logger(_L | LOG_ERROR, "Cannot set WP to %04X, ignoring\n", val);
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_set_st)
- {
- int val;
- if (task == csa_READ)
- if (!iter) {
- statusto9900();
- command_arg_set_num(sym->args, status);
- return 1;
- } else {
- return 0;
- }
-
- command_arg_get_num(sym->args, &val);
-
- T9900tostatus(val);
- change9900intmask(status & 0xf);
- return 1;
- }
-
- #define HEXNUM(x) (((x)>='0' && (x)<='9') ? (x)-'0' : tolower(x)-'a'+10)
- #define NUMHEX(x) ((x) < 10 ? (x)+'0' : (x)-10+'A')
-
- // Utility function to make a hex string for machine_state routines
- // If 'hex' is NULL, we xmalloc() space and return a pointer to it,
- // else the buffer at 'hex' should have size*2+1 bytes available.
- char *emulate_bin2hex(u8 *mem, char *hex, int size)
- {
- char *str;
-
- if (!hex) hex = (char *)xmalloc(size*2+1);
- str = hex;
- while (size--) {
- u8 byt = *mem++;
- *str++ = NUMHEX(byt>>4);
- *str++ = NUMHEX(byt&0xf);
- }
- *str = 0;
- return hex;
- }
-
- // Utility for converting hex string to binary.
- // 'size' is the upper limit on the expected size of the
- // string. If 'hex' is smaller, we zero-fill the memory.
- void emulate_hex2bin(char *hex, u8 *mem, int size)
- {
- while (*hex && *(hex+1) && size) {
- u8 byt = (HEXNUM(*hex) << 4) + HEXNUM(*(hex+1));
- hex += 2;
- *mem++ = byt;
- size--;
- }
- while (size--) {
- *mem++ = 0;
- }
- }
-
- /*
- * Attempt to RLE compress 'len' bytes of 'data'
- * into 'compress' but not using more than 'maxlen' bytes.
- *
- * Returns length of 'compress' or 0 if compression failed.
- */
- int rle_compress(u8 *data, int len, u8 *compress, int maxlen)
- {
- u8 curseglen;
- u8 curtok;
- int curlen = 0;
-
- while (len && curlen < maxlen) {
- curtok = *data;
-
- // use 0xc0-0xff as repeat counts 1-64
- curseglen = 1;
- while (curseglen < len &&
- data[curseglen] == curtok &&
- curseglen < 64)
- {
- curseglen++;
- }
-
- // due to using 0xc0 through 0xff as repeat counts, we must
- // allow a repeat of one for these tokens themselves.
- // However, we can only use RLE when there are two bytes
- // available in the output buffer.
- if ((curtok >= 0xc0 || curseglen > 2) && curlen <= maxlen - 2) {
- *compress++ = 0xC0 + curseglen - 1;
- *compress++ = curtok;
- curlen += 2;
- } else {
- *compress++ = curtok;
- curlen++;
- curseglen = 1;
- }
- data += curseglen;
- len -= curseglen;
- }
-
- // whoops, ran out of output space before finishing the input
- if (len || (len == 0 && curlen == maxlen))
- return 0;
- else
- return curlen;
- }
-
- /*
- * Attempt to RLE uncompress 'len' bytes of 'data'
- * into 'uncompress' using at most 'maxlen' bytes.
- *
- * Returns zero if uncompressed data cannot fit, else
- * the length of that data.
- */
- int rle_uncompress(u8 *data, int len, u8 *uncompress, int maxlen)
- {
- u8 curseglen;
- u8 curtok;
- int curlen = 0;
-
- while (len && curlen < maxlen) {
- curtok = *data++; len--;
-
- // a repeat count and available data?
- // 0xc0==1, and 0xff==64
- if (curtok >= 0xC0 && len > 0) {
- curseglen = curtok - 0xC0 + 1;
-
- curtok = *data++; len--;
-
- memset(uncompress, curtok, curseglen > (maxlen - curlen) ?
- (maxlen - curlen) : curseglen);
- uncompress += curseglen;
- curlen += curseglen;
- } else {
- *uncompress++ = curtok;
- curlen++;
- }
- }
-
- // unused compressed data?
- if (len)
- return 0;
- else
- return curlen;
- }
-
- static
- DECL_SYMBOL_ACTION(emulate_set_mem)
- {
- char *str, *typ;
- int addr;
- char hexstr[256];
- char memtyp[4];
- mem_domain dmn;
- u8 vector[64];
- u8 rle[64];
- int vectorlen, rlelen, idx;
-
- if (task == csa_READ) {
- static int saveaddr;
- u8 verify[64];
-
- /* Write memory to a hex string, CPU RAM followed by VDP RAM */
- if (!iter)
- saveaddr = 0;
-
- addr = saveaddr;
-
- // spit out 64 bytes at a time
- if (addr < 65536) {
-
- /* Note: this may not seem right, when we could have a
- * MemoryEntry for a MEMENT_STORED RAM block covering
- * this area already, but this preserves the state of
- * that RAM at the time of the session being saved. */
- mrstruct *mrarea;
- while (!HAS_RAM_ACCESS(md_cpu, addr))
- addr = (addr & ~4095) + 4096;
- dmn = md_cpu;
- mrarea = THE_AREA(md_cpu, addr);
- memtyp[0] = 'C';
- memtyp[1] = 0; // not compressed
- memtyp[2] = 0;
- } else if (addr < 65536 + 16384) {
- addr -= 65536;
- memtyp[0] = 'V';
- memtyp[1] = 0; // not compressed
- memtyp[2] = 0;
- dmn = md_video;
- } else {
- return 0;
- }
-
- command_arg_set_num(sym->args->next, addr);
-
- /* Read 64 bytes from memory */
- vectorlen = 0;
- do {
- vector[vectorlen++] = domain_read_byte(dmn, addr);
- addr++;
- } while (addr & 63);
-
- /* RLE compress the vector */
- rlelen = rle_compress(vector, vectorlen, rle, sizeof(rle));
-
- /* Returns zero if it couldn't compress any */
- if (rlelen) {
- memtyp[1] = '*'; // compressed
-
- rle_uncompress(rle, rlelen, verify, sizeof(verify));
- if (memcmp(vector, verify, vectorlen)) {
- logger(_L|LOG_FATAL, "Mismatched on RLE compress:\n"
- "In : %s\n"
- "Out: %s\n"
- "Ver: %s\n",
- emulate_bin2hex(vector, 0L, vectorlen),
- emulate_bin2hex(rle, 0L, rlelen),
- emulate_bin2hex(verify, 0L, sizeof(verify)));
- }
- } else {
- rlelen = vectorlen;
- memcpy(rle, vector, rlelen);
- }
-
- str = hexstr;
- idx = 0;
- while (idx < rlelen) {
- u8 byt;
-
- byt = rle[idx++];
-
- *str++ = NUMHEX(byt>>4);
- *str++ = NUMHEX(byt&0xf);
- }
-
- *str = 0;
- saveaddr = addr + (dmn == md_video ? 65536 : 0);
-
- command_arg_set_string(sym->args, memtyp);
- command_arg_set_string(sym->args->next->next, hexstr);
- return 1;
- }
-
- // write memory
-
- command_arg_get_string(sym->args, &typ);
- command_arg_get_num(sym->args->next, &addr);
- addr &= 0xffff;
- command_arg_get_string(sym->args->next->next, &str);
-
- if (toupper(*typ) == 'C' || *typ == '>')
- dmn = md_cpu;
- else if (toupper(*typ) == 'V')
- dmn = md_video;
- else if (toupper(*typ) == 'G')
- dmn = md_graphics;
- else if (toupper(*typ) == 'S')
- dmn = md_speech;
- else {
- logger(_L | LOG_ERROR | LOG_USER, "Can't access memory type '%s'\n", typ);
- return 0;
- }
-
- // decode string, assuming it's rle-encoded
- rlelen = 0;
-
- while (*str) {
- u8 byt = (HEXNUM(*str) << 4) + HEXNUM(*(str+1));
- rle[rlelen++] = byt;
- str+=2;
- }
-
- // second char of memory type indicates compression
- if (typ[1] && typ[1] == '*') {
- vectorlen = rle_uncompress(rle, rlelen, vector, sizeof(vector));
- } else {
- memcpy(vector, rle, rlelen);
- vectorlen = rlelen;
- }
-
- idx = 0;
- while (idx < vectorlen) {
- domain_write_byte(dmn, addr, vector[idx++]);
- addr++;
- }
- return 1;
- }
-
- static
- DECL_SYMBOL_ACTION(do_vdp_mmio_set_addr)
- {
- int num;
-
- if (task == csa_READ) {
- if (iter) return 0;
- command_arg_set_num(SYM_ARG_1st, vdp_mmio_get_addr());
- return 1;
- } else {
- command_arg_get_num(SYM_ARG_1st, &num);
- vdp_mmio_set_addr(num);
- return 1;
- }
- }
-
- static
- DECL_SYMBOL_ACTION(do_grom_mmio_set_addr)
- {
- int num;
-
- if (task == csa_READ) {
- if (iter) return 0;
- command_arg_set_num(SYM_ARG_1st, grom_mmio_get_addr());
- return 1;
- } else {
- command_arg_get_num(SYM_ARG_1st, &num);
- grom_mmio_set_addr(num);
- return 1;
- }
- }
-
- static
- DECL_SYMBOL_ACTION(do_speech_mmio_set_addr)
- {
- int num;
-
- if (task == csa_READ) {
- if (iter) return 0;
- command_arg_set_num(SYM_ARG_1st, speech_mmio_get_addr());
- return 1;
- } else {
- command_arg_get_num(SYM_ARG_1st, &num);
- speech_mmio_set_addr(num);
- return 1;
- }
- }
-
- /***************************************/
-
- static int emulate_tag, slowdown_tag;
-
- void EmulateEvent(void);
-
- static vmResult
- emulate9900_detect(void)
- {
- return vmOk;
- }
-
- static vmResult
- emulate9900_init(void)
- {
- command_symbol_table *internal =
- command_symbol_table_new("Internal Emulator Commands",
- "These options affect the mechanics of 99/4A emulation",
-
- command_symbol_new
- ("RealTimeEmulation",
- "Toggle real-time emulation mode (attempts to operate at the "
- "same speed of the original 9900)",
- c_STATIC,
- NULL /* action */ ,
- RET_FIRST_ARG,
- command_arg_new_num
- ("on|off",
- "on: execute at 9900 speed; "
- "off: execute with DelayBetweenInstructions",
- NULL,
- ARG_NUM(realtime),
- NULL /* next */ )
- ,
-
- command_symbol_new
- ("DelayBetweenInstructions",
- "Sets a constant delay between instructions (when not in real-time mode)",
- c_STATIC,
- NULL /* action */ ,
- RET_FIRST_ARG,
- command_arg_new_num
- ("cycles",
- "number of cycles to count",
- NULL,
- ARG_NUM(delaybetweeninstructions),
- NULL /* next */ )
- ,
-
- command_symbol_new
- ("ResetComputer",
- "Resets the 99/4A via RESET",
- c_DONT_SAVE,
- emulate_reset_computer,
- NULL /* ret */ ,
- NULL /* args */
- ,
-
- command_symbol_new
- ("PauseComputer",
- "Pauses emulation of the 99/4A",
- c_DONT_SAVE,
- emulate_execution_pause /* action */ ,
- RET_FIRST_ARG,
- command_arg_new_num
- ("on|off",
- NULL /* help */,
- NULL /* action */ ,
- NEW_ARG_NUM(bool),
- NULL /* next */ )
- ,
-
- command_symbol_new
- ("Debugger",
- "Enable the debugger/tracer",
- c_DYNAMIC,
- emulate_debugger_enable /* action */ ,
- RET_FIRST_ARG,
- command_arg_new_num
- ("on|off",
- NULL /* help */,
- NULL /* action */ ,
- NEW_ARG_NUM(bool),
- NULL /* next */ )
- ,
-
- command_symbol_new
- ("AllowDebuggingInterrupts",
- "Allow interrupts to occur while debugging",
- c_STATIC,
- NULL /* action */ ,
- RET_FIRST_ARG,
- command_arg_new_num
- ("on|off",
- NULL /* help */,
- NULL /* action */ ,
- ARG_NUM(allow_debug_interrupts),
- NULL /* next */ )
- ,
-
- command_symbol_new
- ("SingleStep",
- "Execute one instruction and stop",
- c_DONT_SAVE,
- emulate_single_step /* action */ ,
- RET_FIRST_ARG,
- NULL /* args */
- ,
-
- command_symbol_new
- ("BaseClockHZ",
- "Set HZ speed base clock (usually 3.0 MHz)",
- c_STATIC,
- emulate_change_clock_speed,
- RET_FIRST_ARG,
- command_arg_new_num
- ("hertz",
- "number of times per second",
- NULL /* action */,
- ARG_NUM(baseclockhz),
- NULL /* next */),
-
- NULL /* next */ )))))))),
-
- NULL /* sub */ ,
-
- NULL /* next */
- );
-
- command_symbol_table *state =
- command_symbol_table_new("Memory / Debugging Commands",
- "These options allow you to change the running state of the virtual machine",
- command_symbol_new("ProgramCounter|PC",
- "Set the program counter",
- c_DYNAMIC|c_SESSION_ONLY,
- emulate_set_pc,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "illegal addresses will be ignored",
- NULL /* action */,
- NEW_ARG_NUM(u16),
- NULL /* next */)
- ,
-
- command_symbol_new
- ("WorkspacePointer|WP",
- "Set the workspace pointer",
- c_DYNAMIC|c_SESSION_ONLY,
- emulate_set_wp,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "illegal addresses will be ignored",
- NULL /* action */,
- NEW_ARG_NUM(u16),
- NULL /* next */)
- ,
-
-
- command_symbol_new
- ("StatusRegister|ST",
- "Set the status register",
- c_DYNAMIC|c_SESSION_ONLY,
- emulate_set_st,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "illegal addresses will be ignored",
- NULL /* action */,
- NEW_ARG_NUM(u16),
- NULL /* next */)
- ,
-
- command_symbol_new
- ("VDPAddress",
- "Set the VDP address register",
- c_DYNAMIC|c_SESSION_ONLY,
- do_vdp_mmio_set_addr /* action */,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- "0->3FFF sets read address, "
- ">4000->7FFF sets write address, "
- ">8000->87FF sets VDP write register",
- NULL /* action */,
- NEW_ARG_NUM(u16),
- NULL /* next */)
- ,
-
- command_symbol_new
- ("VDPRegister",
- "Set a VDP register",
- c_DYNAMIC|c_SESSION_ONLY,
- vdp_set_register,
- NULL,
- command_arg_new_num
- ("register",
- "register number, 0-7",
- NULL /* action */,
- NEW_ARG_NUM(u8),
- command_arg_new_num
- ("value",
- "value for register",
- NULL /* action */,
- NEW_ARG_NUM(u8),
-
- NULL /* next */))
- ,
-
- command_symbol_new
- ("VDPReadAhead",
- "Set VDP read-ahead value",
- c_DYNAMIC|c_SESSION_ONLY,
- vdp_set_read_ahead,
- NULL,
- command_arg_new_num
- ("value",
- "value for register",
- NULL /* action */,
- NEW_ARG_NUM(u8),
-
- NULL /* next */)
- ,
-
- command_symbol_new
- ("VDPAddrFlag",
- "Set VDP address",
- c_DYNAMIC|c_SESSION_ONLY,
- vdp_set_addr_flag,
- NULL,
- command_arg_new_num
- ("register",
- "register number, 0-7",
- NULL /* action */,
- NEW_ARG_NUM(u8),
- command_arg_new_num
- ("value",
- "value for register",
- NULL /* action */,
- NEW_ARG_NUM(u8),
-
- NULL /* next */))
- ,
-
- command_symbol_new
- ("GROMAddress",
- "Set the GROM address register",
- c_DYNAMIC|c_SESSION_ONLY,
- do_grom_mmio_set_addr /* action */,
- RET_FIRST_ARG,
- command_arg_new_num
- ("address",
- NULL,
- NULL /* action */,
- NEW_ARG_NUM(u16),
- NULL /* next */)
- ,
-
- command_symbol_new
- ("SpeechState",
- NULL /* help */,
- c_DYNAMIC|c_SESSION_ONLY,
- speech_machine_state,
- RET_FIRST_ARG,
- command_arg_new_string("sp",
- NULL /* help */,
- NULL /* action */,
- NEW_ARG_NEW_STRBUF,
- command_arg_new_string("lpc",
- NULL /* help */,
- NULL /* action */,
- NEW_ARG_NEW_STRBUF,
- NULL /* next */)),
-
- command_symbol_new
- ("HW9901State",
- NULL /* help */,
- c_DYNAMIC|c_SESSION_ONLY,
- hw9901_machine_state,
- RET_FIRST_ARG,
- command_arg_new_string("hw9901",
- NULL /* help */,
- NULL /* action */,
- NEW_ARG_NEW_STRBUF,
- command_arg_new_string("audiogate",
- NULL /* help */,
- NULL /* action */,
- NEW_ARG_NEW_STRBUF,
- NULL /* next */)),
-
- command_symbol_new
- ("SetRAM",
- "Change contents of RAM",
- c_DYNAMIC|c_SESSION_ONLY,
- emulate_set_mem,
- NULL /* ret */,
- command_arg_new_string
- ("type",
- "memory type: C/V/G/S",
- NULL /* action */,
- NEW_ARG_STR (16),
- command_arg_new_num
- ("address",
- "illegal addresses will be ignored",
- NULL /* action */,
- NEW_ARG_NUM(u16),
- command_arg_new_string
- ("string",
- "hexadecimal string",
- NULL /* action */,
- NEW_ARG_NEW_STRBUF,
- NULL /* next */)))
- ,
-
- NULL /* next */))))))))))),
-
- NULL /* sub */,
- NULL /* next */
- );
-
- command_symbol_table_add_subtable(universe, internal);
- command_symbol_table_add_subtable(universe, state);
-
- delaybetweeninstructions = 0;
- #if 0
- bogocycles = 0;
- calibrate_processor();
- #endif
-
- emulate_tag = TM_UniqueTag();
- slowdown_tag = TM_UniqueTag(); /* one shot */
-
- totalticks = 0;
- totalexecuted = 0;
- executed = 0;
-
- memory_init();
-
- return vmOk;
- }
-
- static vmResult
- emulate9900_enable(void)
- {
- return vmOk;
- }
-
- static vmResult
- emulate9900_disable(void)
- {
- return vmOk;
- }
-
- static vmResult
- emulate9900_term(void)
- {
- return vmOk;
- }
-
- static vmResult
- emulate9900_restart(void)
- {
- targetcycles = baseclockhz / BASE_EMULATOR_HZ;
- currenttime = 0;
- totalcurrentcycles = totalticks = totalexecuted = executed = 0;
-
- TM_ResetEvent(emulate_tag);
- TM_SetEvent(emulate_tag, TM_HZ * 100 / BASE_EMULATOR_HZ, 0,
- TM_FUNC | TM_REPEAT, EmulateEvent);
-
- calibrated_processor = false;
- command_parse_text("LoadMemory\n");
-
- // be sure our register pointer is set up
- setandverifywp(wp);
-
- return vmOk;
- }
-
- static vmResult
- emulate9900_restop(void)
- {
- TM_ResetEvent(emulate_tag);
- command_parse_text("SaveMemory\n");
- return vmOk;
- }
-
- /*********************************************************/
-
- static int keep_going;
-
- int handlestateflag(void);
-
- static void
- emulate9900_stop(void)
- {
- keep_going = 0;
- }
-
- static long emulate_stopwatch;
-
- static int
- emulate9900_execute(void)
- {
- int ret;
-
- // if we're already speeding along...
- if (!stateflag && realtime && currentcycles >= targetcycles)
- return em_TooFast;
-
- emulate_stopwatch = TM_GetTicks();
-
- while (1) {
- if (debugger_check_breakpoint(pc))
- {
- debugger_enable(1);
- stateflag |= ST_SINGLESTEP;
- }
-
- if (stateflag && (ret = handlestateflag()) != em_KeepGoing) {
- break;
- }
-
- // execute() updates this for the current instruction
- instcycles = 0;
-
- execute(fetch());
- executed++;
-
- /* Check for 9901 timer, which is keyed
- on baseclockhz / 64 */
-
- if ((currentcycles & ~63) != ((currentcycles + instcycles) & ~63)) {
- // tick at the baseclockhz/64 rate; 9901.c will handle
- // correlating this to the clock interrupt
- if (currenttime < baseclockhz / 64 / BASE_EMULATOR_HZ) {
- currenttime++;
- handle9901tick();
- }
- }
-
- currentcycles += instcycles;
-
- // store new interrupt level, if any
- // (prevents an interrupt right after RTWP from one)
- change9900intmask(status & 0xf);
-
- if (!realtime) {
- ret = delaybetweeninstructions;
- while (ret--)
- ;
- } else if (currentcycles >= targetcycles) {
- ret = em_TooFast;
- break;
- }
-
- // finally, don't take too long here
- if (TM_GetTicks() > emulate_stopwatch + TM_HZ/10) {
- ret = em_Interrupted;
- break;
- }
- }
-
- return ret;
- }
-
- /*********************************************/
-
- static void
- getcommands(void)
- {
- stateflag |= ST_INTERACTIVE;
- system_getcommands();
- stateflag &= ~ST_INTERACTIVE;
- }
-
- int
- handlestateflag(void)
- {
- int ret = em_KeepGoing;
-
- logger(_L | L_2, "handlestateflag %X\n", stateflag);
-
- // set when we want to quit
- if (stateflag & ST_TERMINATE)
- return em_Quitting;
-
- // set when we want to enter commands
- while (stateflag & ST_INTERACTIVE) {
- getcommands();
- ret = em_Interrupted;
- }
-
- // set to force emulation to halt
- if (stateflag & ST_STOP) {
- stateflag &= ~ST_STOP;
- ret = em_Interrupted;
- return ret;
- }
-
- if (stateflag & ST_SINGLESTEP) {
- stateflag &= ~(ST_SINGLESTEP | ST_STOP);
- stateflag |= ST_PAUSE;
- }
- else if (stateflag & ST_PAUSE)
- return em_TooFast;
-
- // set when we want to monitor execution
- if (stateflag & ST_DEBUG) {
- debugger();
- }
-
- // any sort of interrupt that sets intpins9900
- if ((stateflag & ST_INTERRUPT)) {
- // non-maskable
- if (intpins9900 & INTPIN_LOAD) {
- intpins9900 &= ~INTPIN_LOAD;
- logger(_L | 0, "**** NMI ****");
- contextswitch(0xfffc);
- instcycles += 22;
- execute(fetch());
- } else
- // non-maskable (?)
- if (intpins9900 & INTPIN_RESET) {
- intpins9900 &= ~INTPIN_RESET;
- logger(_L | 0, "**** RESET ****\n");
- contextswitch(0);
- instcycles += 26;
- execute(fetch());
- } else
- // maskable
- if (intpins9900 & INTPIN_INTREQ) {
- u16 highmask = 1 << (intlevel9900 + 1);
-
- // 99/4A console hardcodes all interrupts as level 1,
- // so if any are available, level 1 is it.
- if (intlevel9900 && read9901int() &&
- (!(stateflag & ST_DEBUG) || (allow_debug_interrupts)))
- {
- intpins9900 &= ~INTPIN_INTREQ;
- contextswitch(0x4);
- intlevel9900--;
- instcycles += 22;
- execute(fetch());
- }
- } else
- intpins9900 = 0; // invalid
-
- if (!intpins9900)
- stateflag &= ~ST_INTERRUPT;
- }
-
- return ret;
- }
-
- /* Ensure that a restore of machine state succeeds without a reboot */
- void
- emulate_setup_for_restore(void)
- {
- // video module needs a jab
- TM_Start();
- vdpcompleteredraw();
- VIDEO(resetfromblank,());
- intpins9900 &= ~INTPIN_RESET;
- stateflag &= ~(ST_REBOOT | ST_INTERRUPT);
- }
-
- /*
- Always called BASE_EMULATOR_HZ times a second.
- */
- void
- EmulateEvent(void)
- {
- totalcurrentcycles += currentcycles;
- totaltargetcycles += targetcycles;
-
- currentcycles = 0;
- currenttime = 0;
-
- totalexecuted += executed;
- executed = 0;
- totalticks++;
-
- if (totalticks % (BASE_EMULATOR_HZ * 10) == 0) {
- report_status(STATUS_CYCLES_SECOND,
- (long)(totalcurrentcycles / (totalticks / BASE_EMULATOR_HZ)),
- (long)(totalexecuted / (totalticks / BASE_EMULATOR_HZ)));
- executed = 0;
- }
-
- }
-
- static vmCPUModule myCPUModule = {
- 2,
- emulate9900_execute,
- emulate9900_stop
- };
-
- vmModule emulate9900CPU = {
- 3,
- "9900 emulator",
- "cpu9900",
-
- vmTypeCPU,
- vmFlagsExclusive,
-
- emulate9900_detect,
- emulate9900_init,
- emulate9900_term,
- emulate9900_enable,
- emulate9900_disable,
- emulate9900_restart,
- emulate9900_restop,
- {(vmGenericModule *) & myCPUModule}
- };
-
- ///////////////
- static int round, timeout, slowing;
- static u8 lastkey = 0xff;
- static volatile int resume;
- void
- emulate_keyslow(void)
- {
- u8 curkey;
-
- logger(_L | L_2, "emulate_keyslow; slowing=%d, resume=%d\n", slowing, resume);
- if (pc == 0x496 + 2) {
- if (slowing && resume) {
- resume = slowing = 0;
- pc = register (11);
-
- return;
- }
- // is the same key pressed?
- curkey = (u8) memory_read_byte(0x8375);
- if (curkey != 0xff && curkey == lastkey) {
- logger(_L | L_2, "got a keypress [%02X]\n", curkey);
- if (!slowing) {
- logger(_L | L_2, "slowing down...\n");
- round = (round + 1) & 0x7;
- if (!round) {
- resume = 0;
- slowing = 1;
- TM_SetEvent(slowdown_tag, 1 * 100 / TM_HZ, 0, 0, &resume);
- } else {
- timeout = 1000;
- slowing = 1;
- resume = 0;
- }
- } else if (round) {
- if (!timeout--)
- resume = 1;
- }
-
- pc -= 2;
- } else {
- if (curkey != 0xff)
- lastkey = curkey;
- slowing = 0;
- pc = register (11);
- }
- }
- }
-