ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/maincpu.c

This is maincpu.c in view mode; [Download] [Up]

/*
 * maincpu.c - Emulation of the main 6510 processor.
 *
 * Written by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

#include "vice.h"

#include <stdio.h>

#define _MAINCPU_C

#include "maincpu.h"
#include "types.h"
#include "machine.h"
#include "vmachine.h"
#include "ui.h"
#include "resources.h"
#include "traps.h"
#include "mon.h"
#include "mem.h"
#include "misc.h"
#include "drive.h"
#include "6510core.h"
#include "interrupt.h"

/* ------------------------------------------------------------------------- */

/* This enables a special hack that can speed up the instruction fetch quite
   a lot, but does not work when a conditional branch instruction jumps from
   ROM to RAM or vice versa.  It still works if one program lies in the I/O
   space, though.  Keeping this defined should be OK for everything, and
   makes things much faster.  */
#define INSTRUCTION_FETCH_HACK

/* ------------------------------------------------------------------------- */

/* The following #defines are useful for debugging and speed tuning.  */

/* If this is #defined, you can set the `traceflg' variable to non-zero to
   trace all the opcodes being executed.  This is mainly useful for
   debugging, and also makes things a bit slower.  */
/* #define TRACE */

/* Print a message whenever a program attempts to execute instructions fetched
   from the I/O area.  */
#undef IO_AREA_WARNING

/* Run without interpreting opcodes (just fetch them from memory).  */
#undef NO_OPCODES

/* Do not handle CPU alarms, but measure speed instead.  */
#undef EVALUATE_SPEED

/* Use a global variable for Program Counter.  This makes it slower, but also
   makes debugging easier.  This is needed by the VIC-II emulation, so avoid
   #undefining or #defining it in case it is already #defined.  */
#if !defined EXTERN_PC
#undef EXTERN_PC
#endif

/* Force `TRACE' in unstable versions.  */
#if 0 && defined UNSTABLE && !defined TRACE
#define TRACE
#endif

/* ------------------------------------------------------------------------- */

struct cpu_int_status maincpu_int_status;

/* Global clock counter.  */
CLOCK clk = 0L;

/* This is flag is set to 1 each time a Read-Modify-Write instructions that
   accesses memory is executed.  We can emulate the RMW bug of the 6510 this
   way.  VERY important notice: Always assign 1 for true, 0 for false!  Some
   functions depend on this to do some optimization.  */
int rmw_flag = 0;

/* Information about the last executed opcode.  This is used to know the
   number of write cycles in the last executed opcode and to delay interrupts
   by one more cycle if necessary, as happens with conditional branch opcodes
   when the branch is taken.  */
int last_opcode_info;

/* Number of write cycles for each 6510 opcode.  */
CLOCK _maincpu_opcode_write_cycles[] = {
	    /* 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
    /* $00 */  3, 0, 0, 2, 0, 0, 2, 2, 1, 0, 0, 0, 0, 0, 2, 2, /* $00 */
    /* $10 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $10 */
    /* $20 */  2, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $20 */
    /* $30 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $30 */
    /* $40 */  0, 0, 0, 2, 0, 0, 2, 2, 1, 0, 0, 0, 0, 0, 2, 2, /* $40 */
    /* $50 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $50 */
    /* $60 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $60 */
    /* $70 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $70 */
    /* $80 */  0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, /* $80 */
    /* $90 */  0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, /* $90 */
    /* $A0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* $A0 */
    /* $B0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* $B0 */
    /* $C0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $C0 */
    /* $D0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2, /* $D0 */
    /* $E0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, /* $E0 */
    /* $F0 */  0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 2, 2  /* $F0 */
	    /* 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
};

/* Public copy of the CPU registers.  As putting the registers into the
   function makes it faster, you have to generate a `TRAP' interrupt to have
   the values copied into this struct.  */
mos6510_regs_t maincpu_regs;

/* Implement the hack to make opcode fetches faster.  */
#ifdef INSTRUCTION_FETCH_HACK

#  define JUMP(addr)							\
       do {								\
	   reg_pc = (addr);						\
	   bank_base = mem_read_base(reg_pc);				\
       } while (0)

#else  /* !INSTRUCTION_FETCH_HACK */

#  define JUMP(addr)	(reg_pc = (addr))

#endif /* !INSTRUCTION_FETCH_HACK */

/* Trace flag.  Set this to a nonzero value from a debugger to trace the 6510
   instructions being executed.  */
#ifdef TRACE
int traceflg;
#endif

/* ------------------------------------------------------------------------- */

/* Interface to the monitor.  */
monitor_interface_t maincpu_monitor_interface = {

    /* Pointer to the registers of the CPU.  */
    &maincpu_regs,

    /* Pointer to the alarm/interrupt status.  */
    &maincpu_int_status,

    /* Pointer to the machine's clock counter.  */
    &clk,

    /* Pointer to a function that writes to memory.  */
    mem_read,

    /* Pointer to a function that reads from memory.  */
    mem_store,

    /* Pointer to a function to disable/enable watchpoint checking.  */
    mem_toggle_watchpoints
};

/* ------------------------------------------------------------------------- */

#ifdef EVALUATE_SPEED

#include <sys/time.h>

#define EVALUATE_INTERVAL	10000000L

inline static void evaluate_speed(unsigned long clk)
{
    static unsigned long old_clk;
    static unsigned long next_clk = EVALUATE_INTERVAL;
    static double old_time;

    if (clk > next_clk) {
	struct timeval tv;
	double current_time;

	gettimeofday (&tv, NULL);
	current_time = (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000.0;

	if (old_clk)
	    printf ("%ld cycles in %f seconds: %f%% speed\n",
		    clk - old_clk, current_time - old_time,
		    100.0 * (((double)(clk - old_clk)
			      / (current_time - old_time)) / 1108405.0));

	old_clk = clk;
	next_clk = old_clk + EVALUATE_INTERVAL;
	old_time = current_time;
    }
}

#endif /* EVALUATE_SPEED */

/* ------------------------------------------------------------------------- */

static void reset(void)
{
    int preserve_monitor;

    preserve_monitor = maincpu_int_status.global_pending_int & IK_MONITOR;

    printf("Main CPU: RESET\n");

    serial_reset();

    cpu_int_status_init(&maincpu_int_status,
			NUMOFALRM, NUMOFINT, &last_opcode_info);

    if (preserve_monitor)
        monitor_trap_on(&maincpu_int_status);

    /* Do machine-specific initialization.  */
    machine_reset();

    clk = 6;			/* # of clock cycles needed for RESET.  */

    initialize_memory();
}

/* ------------------------------------------------------------------------- */

#ifdef EXTERN_PC
unsigned int reg_pc;
#endif

void mainloop(ADDRESS start_address)
{
    /* Notice that using a struct for these would make it a lot slower (at
       least, on gcc 2.7.2.x).  */
    BYTE reg_a = 0;
    BYTE reg_x = 0;
    BYTE reg_y = 0;
    BYTE reg_p = 0;
    BYTE reg_sp = 0;
    BYTE flag_n = 0;
    BYTE flag_z = 0;
#ifndef EXTERN_PC
    unsigned int reg_pc;
#endif
#ifdef INSTRUCTION_FETCH_HACK
    BYTE *bank_base;
#endif

    reset();

    if (start_address)
	JUMP(start_address);
    else
	JUMP(LOAD_ADDR(0xfffc));

    printf("Main CPU: starting at $%04X.\n", reg_pc);

    while (1) {

#ifdef EVALUATE_SPEED
	evaluate_speed(clk);
#endif /* !EVALUATE_SPEED */

#ifdef IO_AREA_WARNING
	if (!bank_base)
	    printf ("Executing from I/O area at $%04X: "
		    "$%02X $%02X $%04X at clk %ld\n", reg_pc, p0, p1, p2, clk);
#endif

#ifdef NO_OPCODES

	clk += 4;

#else  /* !NO_OPCODES */

#  define CLK clk
#  define RMW_FLAG rmw_flag
#  define PAGE_ONE (ram + 0x100)
#  define LAST_OPCODE_INFO last_opcode_info
#  define TRACEFLG traceflg

#  define CPU_INT_STATUS	maincpu_int_status

#  define CHECK_PENDING_ALARM() \
     (clk >= next_alarm_clk(&maincpu_int_status))

#  define CHECK_PENDING_INTERRUPT() \
     check_pending_interrupt(&maincpu_int_status)

#  define TRAP(addr) \
     maincpu_int_status.trap_func(addr);

#  define ROM_TRAP_HANDLER() \
     traps_handler()

#  define JAM()                                                         \
     do {                                                               \
        int tmp;                                                        \
                                                                        \
        EXPORT_REGISTERS();                                             \
        tmp = ui_jam_dialog("   " CPU_STR ": JAM at $%04X   ", reg_pc); \
        switch (tmp) {                                                  \
          case UI_JAM_RESET:                                            \
            DO_INTERRUPT(IK_RESET);                                     \
            break;                                                      \
          case UI_JAM_HARD_RESET:                                       \
            mem_powerup();                                              \
            DO_INTERRUPT(IK_RESET);                                     \
            break;                                                      \
          case UI_JAM_MONITOR:                                          \
            caller_space = e_comp_space;                                \
            mon(reg_pc);                                                \
            IMPORT_REGISTERS();                                         \
            break;                                                      \
          default:                                                      \
            CLK++;                                                      \
        }                                                               \
     } while (0)

#  define CALLER		e_comp_space

#  define ROM_TRAP_ALLOWED()    mem_rom_trap_allowed(reg_pc)

#  define GLOBAL_REGS           maincpu_regs

#  include "6510core.c"

#endif /* !NO_OPCODES */

    }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.