mii_emu/src/mii.h

371 lines
9.0 KiB
C

/*
* mii.h
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "mii_65c02.h"
#include "mii_dd.h"
#include "mii_bank.h"
#include "mii_slot.h"
#include "mii_video.h"
#include "mii_speaker.h"
#include "mii_mouse.h"
#include "mii_analog.h"
#include "mii_vcd.h"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
enum {
MII_BANK_MAIN = 0, // main 48K address space
MII_BANK_BSR, // 0xd000 - 0xffff bank switched RAM 16KB
MII_BANK_BSR_P2, // 0xd000 - 0xe000 bank switched RAM aux 4KB
// this one is the fixed one, used by video
MII_BANK_AUX_BASE, // aux 48K address space (80 cols card)
// these one can 'move' in the block of ramworks ram
MII_BANK_AUX, // aux 48K address space (80 cols card)
MII_BANK_AUX_BSR, // 0xd000 - 0xffff bank switched RAM aux 16KB
MII_BANK_AUX_BSR_P2, // 0xd000 - 0xe000 bank switched RAM aux 4KB (aux bank)
MII_BANK_ROM, // 0xc000 - 0xffff 16K ROM
MII_BANK_CARD_ROM, // 0xc100 - 0xcfff Card ROM access
MII_BANK_SW, // 0xc000 - 0xc0ff Softswitches
MII_BANK_COUNT,
};
/*
* A 'trap' is a sequence of 2 special NOPs that are used to trigger
* a callback. The callback is called with the mii_t * and the trap ID
*/
typedef void (*mii_trap_handler_cb)(
mii_t * mii,
uint8_t trap);
typedef struct mii_trap_t {
uint16_t map;
struct {
mii_trap_handler_cb cb;
} trap[16];
} mii_trap_t;
// state of the emulator
enum {
MII_INIT = 0,
MII_RUNNING, // default
MII_STOPPED,
MII_STEP,
MII_TERMINATE,
};
enum {
MII_BP_PC = (1 << 0), // breakpoint on PC
MII_BP_W = (1 << 1), // breakpoint on write
MII_BP_R = (1 << 2), // breakpoint on read
MII_BP_HIT = (1 << 3), // breakpoint was hit
MII_BP_SILENT = (1 << 4), // don't dump state (used for the 'next' command)
MII_BP_STICKY = (1 << 7), // breakpoint is sticky (rearms itself)
};
#define MII_PC_LOG_SIZE 16
/*
* this keeps track of the last few PC values, for the debugger
*/
typedef struct mii_trace_t {
uint16_t log[MII_PC_LOG_SIZE];
uint8_t idx;
// when in MII_STEP, do not fall back to MII_STOPPED until these
// run out
uint32_t step_inst;
} mii_trace_t;
typedef uint64_t (*mii_timer_p)(
mii_t * mii,
void * param );
#define MII_SPEED_NTSC 1.0227271429 // 14.31818 MHz / 14
#define MII_SPEED_PAL 1.0178571429 // 14.25 MHz / 14
#define MII_SPEED_TITAN 3.58
/*
* principal emulator state, for a faceless emulation
*/
typedef struct mii_t {
mii_cpu_t cpu;
mii_cpu_state_t cpu_state;
/* this is the video frame/VBL rate vs 60hz, default to MII_SPEED_NTSC */
float speed;
unsigned int state;
/*
* These are 'cycle timers' -- they count down from a set value,
* and stop at 0 (or possibly -1 or -2, depending on the instructions)
* and call the callback (if present).
* The callback returns the number of cycles to wait until the next
* call.
*/
struct {
uint64_t map;
#if MII_65C02_DIRECT_ACCESS
uint8_t last_cycle;
#endif
struct {
mii_timer_p cb;
void * param;
int64_t when;
const char * name; // debug
} timers[64];
} timer;
/*
* bank index for each memory page number, this is recalculated
* everytime a MMU soft switch is triggered
*/
struct {
union {
struct {
uint8_t write : 4, read : 4;
};
uint8_t both;
};
} mem[256];
int mem_dirty; // recalculate mem[] on next access
struct {
unsigned __int128 avail;
uint8_t * bank[128];
} ramworks;
uint32_t sw_state; // B_SW* bitfield
mii_trace_t trace;
int trace_cpu;
mii_trap_t trap;
mii_signal_pool_t sig_pool; // vcd support
/*
* Used for debugging only
*/
struct {
uint16_t bp_map;
struct {
uint32_t kind : 8,
addr : 16,
size : 8,
silent : 1;
} bp[16];
} debug;
mii_bank_t bank[MII_BANK_COUNT];
// the page c000 can have individual callbacks to override/supplement
// existing default behaviour. This is currently used for the titan
// accelerator 'card'
mii_bank_access_t * soft_switches_override;
mii_slot_t slot[7];
mii_video_t video;
mii_speaker_t speaker;
mii_mouse_t mouse;
mii_dd_system_t dd;
mii_analog_t analog;
uint8_t random[256];
uint8_t random_index;
} mii_t;
enum {
MII_INIT_NSC = (1 << 0), // Install no slot clock
MII_INIT_TITAN = (1 << 1), // Install Titan 'card'
MII_INIT_SILENT = (1 << 2), // No audio, ever
MII_INIT_MOCKINGBOARD = (1 << 3), // Install mockingboard
// number of 256KB banks added to the ramworks
MII_INIT_RAMWORKS_BIT = 4, // bit 4 in flags. Can be up to 12
MII_INIT_DEFAULT = MII_INIT_NSC,
};
/*
* Call this first, to initialize the emulator state
* This doesn't initializes any driver.
*/
void
mii_init(
mii_t *mii );
/*
* Call this to prepare the emulator, instantiate and install drivers
* etc. Presumably after you have used mii_argv_parse or loaded a config
* file to set up the drivers.
* flags is a combination of MII_INIT_*
*/
void
mii_prepare(
mii_t *mii,
uint32_t flags );
/*
* Stop the emulator, dispose of everything, free memory etc.
*/
void
mii_dispose(
mii_t *mii );
/*
* Parses arguments until in finds one that isn't for mii, and returns
* the index of that argument in *index.
* Return value is 0 if there's an argument that wasn't handled or 1
* if all the arguments were parsed (and *index == argc)
* mii parameter is the state AFTER mii_init() has been called.
*
* ioFlags is a combination of MII_INIT_*, it will be updated with the
* flags found in the arguments. Pass this to mii_prepare()
*/
int
mii_argv_parse(
mii_t *mii,
int argc,
const char *argv[],
int *index,
uint32_t *ioFlags );
/*
* Locate driver_name, and attempt to register it with slot_id slot.
* Returns 0 on success, -1 on failure
*/
int
mii_slot_drv_register(
mii_t *mii,
uint8_t slot_id,
const char *driver_name);
/* returns the driver registered for slot slot_id (or NULL) */
mii_slot_drv_t *
mii_slot_drv_get(
mii_t *mii,
uint8_t slot_id);
/*
* Reset the emulator, cold reset if cold is true, otherwise warm reset
*/
void
mii_reset(
mii_t *mii,
bool cold);
/* Execute one instruction, respecting breakpoints, runs the video code,
* Check for traps and other associated debug stuff.
*/
void
mii_run(
mii_t *mii);
// this one is thread safe, there's a FIFO behind it.
void
mii_keypress(
mii_t *mii,
uint8_t key);
/* read a byte as the processor would (ex softwitches!), this will
* respect the status of slot ROMs, language card, 80 columns etc */
uint8_t
mii_read_one(
mii_t *mii,
uint16_t addr);
/* write a byte as the processor would (ex softwitches!), this will
* respect the status of slot ROMs, language card, 80 columns etc */
void
mii_write_one(
mii_t *mii,
uint16_t addr,
uint8_t d);
/* read a word as the processor would (ex softwitches!), this will
* respect the status of slot ROMs, language card, 80 columns etc */
uint16_t
mii_read_word(
mii_t *mii,
uint16_t addr);
/* write a word as the processor would (ex softwitches!), this will
* respect the status of slot ROMs, language card, 80 columns etc */
void
mii_write_word(
mii_t *mii,
uint16_t addr,
uint16_t w);
/* lower level call to access memory -- this one can trigger softswitches
* if specified. Otherwise behaves as the previous ones, one byte at a time
*/
void
mii_mem_access(
mii_t *mii,
uint16_t addr,
uint8_t * byte,
bool write,
bool do_sw);
/* register a callback to call when a specific soft switches is hit,
* this allows overriding/supplementing/tracing access to sw.
*/
void
mii_set_sw_override(
mii_t *mii,
uint16_t sw_addr,
mii_bank_access_cb cb,
void *param);
/* register a cycle timer. cb will be called when (at least) when
* cycles have been spent -- the callback returns how many it should
* spend until the next call */
uint8_t
mii_timer_register(
mii_t *mii,
mii_timer_p cb, // this is optional, can be NULL
void *param,
int64_t when,
const char *name);
/* return the cycles left for timer_id (can be negative !)*/
int64_t
mii_timer_get(
mii_t *mii,
uint8_t timer_id);
int
mii_timer_set(
mii_t *mii,
uint8_t timer_id,
int64_t when);
void
mii_dump_trace_state(
mii_t *mii);
void
mii_dump_run_trace(
mii_t *mii);
extern mii_slot_drv_t * mii_slot_drv_list;
#define MI_DRIVER_REGISTER(_mii_driver)\
__attribute__((constructor,unused)) \
static void _mii_register_##_mii_driver() { \
_mii_driver.next = mii_slot_drv_list; \
mii_slot_drv_list = &_mii_driver; \
}
#define MII_TRAP 0xdbfb
/*
* Request a trap ID for the given callback. Calling code is responsible
* for setting up the trap using the 2 magic NOPs in sequence.
* See mii_smartport.c for an example.
*/
uint8_t
mii_register_trap(
mii_t *mii,
mii_trap_handler_cb cb);
/*
* this is used if libmish is active, to register the 'mii' commands
*/
#define MII_MISH_KIND MISH_FCC('m','i','i',' ')
#define MII_MISH(_name,_cmd) \
MISH_CMD_REGISTER_KIND(_name, _cmd, 0, MII_MISH_KIND)
void
mii_cpu_step(
mii_t *mii,
uint32_t count );
void
mii_cpu_next(
mii_t *mii);