mirror of
https://github.com/holub/mame
synced 2025-05-25 23:35:26 +03:00
Added cycle-precise implementation of tms9980a; changed tms9900.c to allow for subclassing tms9900 and tms9980a from a common parent. [Michael Zapf]
This commit is contained in:
parent
86dfb47394
commit
ca025ce099
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -563,6 +563,7 @@ src/emu/cpu/tms9900/tms9900.h svneol=native#text/plain
|
|||||||
src/emu/cpu/tms9900/tms9900l.c svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9900l.c svneol=native#text/plain
|
||||||
src/emu/cpu/tms9900/tms9900l.h svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9900l.h svneol=native#text/plain
|
||||||
src/emu/cpu/tms9900/tms9980a.c svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9980a.c svneol=native#text/plain
|
||||||
|
src/emu/cpu/tms9900/tms9980a.h svneol=native#text/plain
|
||||||
src/emu/cpu/tms9900/tms9980al.c svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9980al.c svneol=native#text/plain
|
||||||
src/emu/cpu/tms9900/tms9995.c svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9995.c svneol=native#text/plain
|
||||||
src/emu/cpu/tms9900/tms9995.h svneol=native#text/plain
|
src/emu/cpu/tms9900/tms9995.h svneol=native#text/plain
|
||||||
|
@ -1608,6 +1608,7 @@ ifneq ($(filter TMS9900,$(CPUS)),)
|
|||||||
OBJDIRS += $(CPUOBJ)/tms9900
|
OBJDIRS += $(CPUOBJ)/tms9900
|
||||||
CPUOBJS += $(CPUOBJ)/tms9900/tms9900.o
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9900.o
|
||||||
CPUOBJS += $(CPUOBJ)/tms9900/tms9900l.o
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9900l.o
|
||||||
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9980a.o
|
||||||
CPUOBJS += $(CPUOBJ)/tms9900/tms9980al.o
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9980al.o
|
||||||
CPUOBJS += $(CPUOBJ)/tms9900/tms9995.o
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9995.o
|
||||||
CPUOBJS += $(CPUOBJ)/tms9900/tms9995l.o
|
CPUOBJS += $(CPUOBJ)/tms9900/tms9995l.o
|
||||||
@ -1623,13 +1624,18 @@ $(CPUOBJ)/tms9900/tms9900l.o: $(CPUSRC)/tms9900/tms9900l.c \
|
|||||||
$(CPUSRC)/tms9900/99xxcore.h \
|
$(CPUSRC)/tms9900/99xxcore.h \
|
||||||
$(CPUSRC)/tms9900/99xxstat.h
|
$(CPUSRC)/tms9900/99xxstat.h
|
||||||
|
|
||||||
|
$(CPUOBJ)/tms9900/tms9980a.o: $(CPUSRC)/tms9900/tms9980a.c \
|
||||||
|
$(CPUSRC)/tms9900/tms9980a.h \
|
||||||
|
$(CPUSRC)/tms9900/tms9900.c \
|
||||||
|
$(CPUSRC)/tms9900/tms9900.h
|
||||||
|
|
||||||
$(CPUOBJ)/tms9900/tms9980al.o: $(CPUSRC)/tms9900/tms9980al.c \
|
$(CPUOBJ)/tms9900/tms9980al.o: $(CPUSRC)/tms9900/tms9980al.c \
|
||||||
$(CPUSRC)/tms9900/tms9900l.h \
|
$(CPUSRC)/tms9900/tms9900l.h \
|
||||||
$(CPUSRC)/tms9900/99xxcore.h \
|
$(CPUSRC)/tms9900/99xxcore.h \
|
||||||
$(CPUSRC)/tms9900/99xxstat.h
|
$(CPUSRC)/tms9900/99xxstat.h
|
||||||
|
|
||||||
$(CPUOBJ)/tms9900/tms9995.o: $(CPUSRC)/tms9900/tms9995.c \
|
$(CPUOBJ)/tms9900/tms9995.o: $(CPUSRC)/tms9900/tms9995.c \
|
||||||
$(CPUSRC)/tms9900/tms9900.h
|
$(CPUSRC)/tms9900/tms9995.h
|
||||||
|
|
||||||
$(CPUOBJ)/tms9900/tms9995l.o: $(CPUSRC)/tms9900/tms9995l.c \
|
$(CPUOBJ)/tms9900/tms9995l.o: $(CPUSRC)/tms9900/tms9995l.c \
|
||||||
$(CPUSRC)/tms9900/tms9900l.h \
|
$(CPUSRC)/tms9900/tms9900l.h \
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -47,6 +47,12 @@
|
|||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
LOAD_INT = -1,
|
||||||
|
RESET_INT = -2
|
||||||
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
TI990_10_ID = 1,
|
TI990_10_ID = 1,
|
||||||
@ -61,7 +67,7 @@ enum
|
|||||||
TMS99110A_ID = 12
|
TMS99110A_ID = 12
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MCFG_TMS9900_ADD(_tag, _device, _clock, _prgmap, _iomap, _config) \
|
#define MCFG_TMS99xx_ADD(_tag, _device, _clock, _prgmap, _iomap, _config) \
|
||||||
MCFG_DEVICE_ADD(_tag, _device, _clock) \
|
MCFG_DEVICE_ADD(_tag, _device, _clock) \
|
||||||
MCFG_DEVICE_PROGRAM_MAP(_prgmap) \
|
MCFG_DEVICE_PROGRAM_MAP(_prgmap) \
|
||||||
MCFG_DEVICE_IO_MAP(_iomap) \
|
MCFG_DEVICE_IO_MAP(_iomap) \
|
||||||
@ -76,7 +82,18 @@ enum
|
|||||||
LREX_OP = 7
|
LREX_OP = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _tms9900_config
|
static const char opname[][5] =
|
||||||
|
{ "ILL ", "A ", "AB ", "ABS ", "AI ", "ANDI", "B ", "BL ", "BLWP", "C ",
|
||||||
|
"CI ", "CB ", "CKOF", "CKON", "CLR ", "COC ", "CZC ", "DEC ", "DECT", "DIV ",
|
||||||
|
"IDLE", "INC ", "INCT", "INV ", "JEQ ", "JGT ", "JH ", "JHE ", "JL ", "JLE ",
|
||||||
|
"JLT ", "JMP ", "JNC ", "JNE ", "JNO ", "JOC ", "JOP ", "LDCR", "LI ", "LIMI",
|
||||||
|
"LREX", "LWPI", "MOV ", "MOVB", "MPY ", "NEG ", "ORI ", "RSET", "RTWP", "S ",
|
||||||
|
"SB ", "SBO ", "SBZ ", "SETO", "SLA ", "SOC ", "SOCB", "SRA ", "SRC ", "SRL ",
|
||||||
|
"STCR", "STST", "STWP", "SWPB", "SZC ", "SZCB", "TB ", "X ", "XOP ", "XOR ",
|
||||||
|
"*int"
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _tms99xx_config
|
||||||
{
|
{
|
||||||
devcb_write8 external_callback;
|
devcb_write8 external_callback;
|
||||||
devcb_read8 irq_level;
|
devcb_read8 irq_level;
|
||||||
@ -84,15 +101,19 @@ typedef struct _tms9900_config
|
|||||||
devcb_write_line clock_out;
|
devcb_write_line clock_out;
|
||||||
devcb_write_line wait_line;
|
devcb_write_line wait_line;
|
||||||
devcb_write_line holda_line;
|
devcb_write_line holda_line;
|
||||||
} tms9900_config;
|
} tms99xx_config;
|
||||||
|
|
||||||
#define TMS9900_CONFIG(name) \
|
#define TMS99xx_CONFIG(name) \
|
||||||
const tms9900_config(name) =
|
const tms99xx_config(name) =
|
||||||
|
|
||||||
class tms9900_device : public cpu_device
|
class tms99xx_device : public cpu_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
tms9900_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
tms99xx_device(const machine_config &mconfig, device_type type, const char *name,
|
||||||
|
const char *tag, int databus_width, int prg_addr_bits, int cru_addr_bits,
|
||||||
|
device_t *owner, UINT32 clock);
|
||||||
|
|
||||||
|
~tms99xx_device();
|
||||||
|
|
||||||
// READY input line. When asserted (high), the memory is ready for data exchange.
|
// READY input line. When asserted (high), the memory is ready for data exchange.
|
||||||
void set_ready(int state);
|
void set_ready(int state);
|
||||||
@ -122,7 +143,23 @@ protected:
|
|||||||
|
|
||||||
const address_space_config* memory_space_config(address_spacenum spacenum) const;
|
const address_space_config* memory_space_config(address_spacenum spacenum) const;
|
||||||
|
|
||||||
private:
|
// Let these methods be overloaded by the TMS9980.
|
||||||
|
virtual void mem_read(void);
|
||||||
|
virtual void mem_write(void);
|
||||||
|
virtual void acquire_instruction(void);
|
||||||
|
void decode(UINT16 inst);
|
||||||
|
|
||||||
|
const address_space_config m_program_config;
|
||||||
|
const address_space_config m_io_config;
|
||||||
|
address_space* m_prgspace;
|
||||||
|
address_space* m_cru;
|
||||||
|
|
||||||
|
virtual UINT16 read_workspace_register_debug(int reg);
|
||||||
|
virtual void write_workspace_register_debug(int reg, UINT16 data);
|
||||||
|
|
||||||
|
// Cycle counter
|
||||||
|
int m_icount;
|
||||||
|
|
||||||
// TMS9900 hardware registers
|
// TMS9900 hardware registers
|
||||||
UINT16 WP; // Workspace pointer
|
UINT16 WP; // Workspace pointer
|
||||||
UINT16 PC; // Program counter
|
UINT16 PC; // Program counter
|
||||||
@ -131,38 +168,72 @@ private:
|
|||||||
// Internal register
|
// Internal register
|
||||||
UINT16 IR; // Instruction register
|
UINT16 IR; // Instruction register
|
||||||
|
|
||||||
|
// Stored address
|
||||||
|
UINT16 m_address;
|
||||||
|
|
||||||
|
// Stores the recently read word or the word to be written
|
||||||
|
UINT16 m_current_value;
|
||||||
|
|
||||||
// Decoded command
|
// Decoded command
|
||||||
UINT16 m_command;
|
UINT16 m_command;
|
||||||
|
|
||||||
|
// Issue clock pulses. Note that each machine cycle has two clock cycles.
|
||||||
|
void pulse_clock(int count);
|
||||||
|
|
||||||
|
// For multi-pass operations. For instance, memory word accesses are
|
||||||
|
// executed as two consecutive byte accesses. CRU accesses are repeated
|
||||||
|
// single-bit accesses. (Needed for TMS9980)
|
||||||
|
int m_pass;
|
||||||
|
|
||||||
|
// Data bus width. Needed for TMS9980.
|
||||||
|
int m_databus_width;
|
||||||
|
|
||||||
|
// Needed for TMS9980
|
||||||
|
bool m_lowbyte;
|
||||||
|
|
||||||
|
// Check the READY line?
|
||||||
|
bool m_check_ready;
|
||||||
|
|
||||||
|
// Max address
|
||||||
|
const UINT16 m_prgaddr_mask;
|
||||||
|
const UINT16 m_cruaddr_mask;
|
||||||
|
|
||||||
|
bool m_load_state;
|
||||||
|
bool m_irq_state;
|
||||||
|
bool m_reset;
|
||||||
|
|
||||||
|
// Determine the interrupt level using the IC0-IC2/3 lines
|
||||||
|
virtual int get_intlevel(int state);
|
||||||
|
|
||||||
|
// Interrupt level as acquired from input lines (TMS9900: IC0-IC3, TMS9980: IC0-IC2)
|
||||||
|
// We assume all values right-justified, i.e. TMS9980 also counts up by one
|
||||||
|
int m_irq_level;
|
||||||
|
|
||||||
|
// Used to display the number of consumed cycles in the log.
|
||||||
|
int m_first_cycle;
|
||||||
|
|
||||||
|
// Signal to the outside world that we are now getting an instruction
|
||||||
|
devcb_resolved_write_line m_iaq_line;
|
||||||
|
|
||||||
|
// Get the value of the interrupt level lines
|
||||||
|
devcb_resolved_read8 m_get_intlevel;
|
||||||
|
|
||||||
|
private:
|
||||||
// Indicates if this is a byte-oriented command
|
// Indicates if this is a byte-oriented command
|
||||||
inline bool byte_operation();
|
inline bool byte_operation();
|
||||||
|
|
||||||
const address_space_config m_program_config;
|
|
||||||
const address_space_config m_io_config;
|
|
||||||
address_space* m_prgspace;
|
|
||||||
address_space* m_cru;
|
|
||||||
|
|
||||||
// Processor states
|
// Processor states
|
||||||
bool m_idle_state;
|
bool m_idle_state;
|
||||||
bool m_load_state;
|
|
||||||
bool m_irq_state;
|
|
||||||
bool m_ready_state;
|
bool m_ready_state;
|
||||||
bool m_wait_state;
|
bool m_wait_state;
|
||||||
bool m_hold_state;
|
bool m_hold_state;
|
||||||
|
|
||||||
bool m_reset;
|
|
||||||
|
|
||||||
int m_irq_level; // Interrupt level as acquired from input lines IC0-IC3
|
|
||||||
int m_icount; // Cycle counter
|
|
||||||
|
|
||||||
// State / debug management
|
// State / debug management
|
||||||
UINT16 m_state_any;
|
UINT16 m_state_any;
|
||||||
static const char* s_statename[];
|
static const char* s_statename[];
|
||||||
void state_import(const device_state_entry &entry);
|
void state_import(const device_state_entry &entry);
|
||||||
void state_export(const device_state_entry &entry);
|
void state_export(const device_state_entry &entry);
|
||||||
void state_string_export(const device_state_entry &entry, astring &string);
|
void state_string_export(const device_state_entry &entry, astring &string);
|
||||||
UINT16 read_workspace_register_debug(int reg);
|
|
||||||
void write_workspace_register_debug(int reg, UINT16 data);
|
|
||||||
|
|
||||||
// Interrupt handling
|
// Interrupt handling
|
||||||
void service_interrupt();
|
void service_interrupt();
|
||||||
@ -176,7 +247,7 @@ private:
|
|||||||
typedef const UINT8* microprogram;
|
typedef const UINT8* microprogram;
|
||||||
|
|
||||||
// Method pointer
|
// Method pointer
|
||||||
typedef void (tms9900_device::*ophandler)(void);
|
typedef void (tms99xx_device::*ophandler)(void);
|
||||||
|
|
||||||
// Opcode list entry
|
// Opcode list entry
|
||||||
typedef struct _tms_instruction
|
typedef struct _tms_instruction
|
||||||
@ -201,18 +272,16 @@ private:
|
|||||||
lookup_entry* m_lotables[32];
|
lookup_entry* m_lotables[32];
|
||||||
|
|
||||||
// List of pointers for micro-operations
|
// List of pointers for micro-operations
|
||||||
static const tms9900_device::ophandler s_microoperation[];
|
static const tms99xx_device::ophandler s_microoperation[];
|
||||||
|
|
||||||
// Opcode table
|
// Opcode table
|
||||||
static const tms9900_device::tms_instruction s_command[];
|
static const tms99xx_device::tms_instruction s_command[];
|
||||||
|
|
||||||
// Micro-operation declarations
|
// Micro-operation declarations
|
||||||
void acquire_instruction(void);
|
|
||||||
void mem_read(void);
|
|
||||||
void mem_write(void);
|
|
||||||
void register_read(void);
|
void register_read(void);
|
||||||
void register_write(void);
|
void register_write(void);
|
||||||
void cru_operation(void);
|
void cru_input_operation(void);
|
||||||
|
void cru_output_operation(void);
|
||||||
void data_derivation_subprogram(void);
|
void data_derivation_subprogram(void);
|
||||||
void return_from_subprogram(void);
|
void return_from_subprogram(void);
|
||||||
void command_completed(void);
|
void command_completed(void);
|
||||||
@ -258,9 +327,6 @@ private:
|
|||||||
void alu_int(void);
|
void alu_int(void);
|
||||||
|
|
||||||
void abort_operation(void);
|
void abort_operation(void);
|
||||||
UINT16 pulse_and_read_memory(UINT16 address);
|
|
||||||
void pulse_and_write_memory(UINT16 address, UINT16 data);
|
|
||||||
void decode(UINT16 inst);
|
|
||||||
|
|
||||||
// Micro-operation
|
// Micro-operation
|
||||||
UINT8 m_op;
|
UINT8 m_op;
|
||||||
@ -278,27 +344,15 @@ private:
|
|||||||
// State of the micro-operation. Needed for repeated ALU calls.
|
// State of the micro-operation. Needed for repeated ALU calls.
|
||||||
int m_state;
|
int m_state;
|
||||||
|
|
||||||
// Check the READY line?
|
|
||||||
bool m_check_ready;
|
|
||||||
|
|
||||||
// Has HOLD been acknowledged yet?
|
// Has HOLD been acknowledged yet?
|
||||||
bool m_hold_acknowledged;
|
bool m_hold_acknowledged;
|
||||||
|
|
||||||
// Issue clock pulses. Note that each machine cycle has two clock cycles.
|
|
||||||
inline void pulse_clock(int count);
|
|
||||||
|
|
||||||
// Signal the wait state via the external line
|
// Signal the wait state via the external line
|
||||||
inline void set_wait_state(bool state);
|
inline void set_wait_state(bool state);
|
||||||
|
|
||||||
// Used to acknowledge HOLD and enter the HOLD state
|
// Used to acknowledge HOLD and enter the HOLD state
|
||||||
inline void acknowledge_hold();
|
inline void acknowledge_hold();
|
||||||
|
|
||||||
// Stored address
|
|
||||||
UINT16 m_address;
|
|
||||||
|
|
||||||
// Stores the recently read word or the word to be written
|
|
||||||
UINT16 m_current_value;
|
|
||||||
|
|
||||||
// Was the source operand a byte from an even address?
|
// Was the source operand a byte from an even address?
|
||||||
bool m_source_even;
|
bool m_source_even;
|
||||||
|
|
||||||
@ -308,6 +362,10 @@ private:
|
|||||||
// Intermediate storage for the source operand
|
// Intermediate storage for the source operand
|
||||||
UINT16 m_source_address;
|
UINT16 m_source_address;
|
||||||
UINT16 m_source_value;
|
UINT16 m_source_value;
|
||||||
|
UINT16 m_address_saved;
|
||||||
|
|
||||||
|
// Another copy of the address
|
||||||
|
UINT16 m_address_copy;
|
||||||
|
|
||||||
// Stores the recently read register contents
|
// Stores the recently read register contents
|
||||||
UINT16 m_register_contents;
|
UINT16 m_register_contents;
|
||||||
@ -315,15 +373,15 @@ private:
|
|||||||
// Stores the register number for the next register access
|
// Stores the register number for the next register access
|
||||||
int m_regnumber;
|
int m_regnumber;
|
||||||
|
|
||||||
// CRU support: Indicates whether the CRU shall be configured to output mode
|
|
||||||
bool m_cru_output;
|
|
||||||
|
|
||||||
// CRU support: Stores the CRU address
|
// CRU support: Stores the CRU address
|
||||||
UINT16 m_cru_address;
|
UINT16 m_cru_address;
|
||||||
|
|
||||||
// CRU support: Stores the number of bits to be transferred
|
// CRU support: Stores the number of bits to be transferred
|
||||||
int m_count;
|
int m_count;
|
||||||
|
|
||||||
|
// Copy of the value
|
||||||
|
UINT16 m_value_copy;
|
||||||
|
|
||||||
// Another internal register, storing intermediate values
|
// Another internal register, storing intermediate values
|
||||||
// Using 32 bits to support MPY
|
// Using 32 bits to support MPY
|
||||||
UINT32 m_value;
|
UINT32 m_value;
|
||||||
@ -336,9 +394,6 @@ private:
|
|||||||
inline void compare_and_set_lae(UINT16 value1, UINT16 value2);
|
inline void compare_and_set_lae(UINT16 value1, UINT16 value2);
|
||||||
void set_status_parity(UINT8 value);
|
void set_status_parity(UINT8 value);
|
||||||
|
|
||||||
// Used to display the number of consumed cycles in the log.
|
|
||||||
int m_first_cycle;
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
// Trigger external operation. This is achieved by putting a special value in
|
// Trigger external operation. This is achieved by putting a special value in
|
||||||
@ -363,12 +418,6 @@ private:
|
|||||||
// chip emulations we use a dedicated callback.
|
// chip emulations we use a dedicated callback.
|
||||||
devcb_resolved_write8 m_external_operation;
|
devcb_resolved_write8 m_external_operation;
|
||||||
|
|
||||||
// Get the value of the interrupt level lines
|
|
||||||
devcb_resolved_read8 m_get_ic0123;
|
|
||||||
|
|
||||||
// Signal to the outside world that we are now getting an instruction
|
|
||||||
devcb_resolved_write_line m_iaq_line;
|
|
||||||
|
|
||||||
// Clock output. This is not a pin of the TMS9900 because the TMS9900
|
// Clock output. This is not a pin of the TMS9900 because the TMS9900
|
||||||
// needs an external clock, and usually one of those external lines is
|
// needs an external clock, and usually one of those external lines is
|
||||||
// used for this purpose.
|
// used for this purpose.
|
||||||
@ -381,6 +430,15 @@ private:
|
|||||||
devcb_resolved_write_line m_holda_line;
|
devcb_resolved_write_line m_holda_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
class tms9900_device : public tms99xx_device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tms9900_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
unsigned Dasm9900(char *buffer, unsigned pc, int model_id, const UINT8 *oprom, const UINT8 *opram);
|
unsigned Dasm9900(char *buffer, unsigned pc, int model_id, const UINT8 *oprom, const UINT8 *opram);
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
|
@ -1,4 +1,245 @@
|
|||||||
/*
|
/*
|
||||||
This file will contain the re-implementation of the tms9980a. The
|
Cycle-precise implementation of the TMS9980A.
|
||||||
previous implementation can be found as tms9980al.
|
Subclassed from tms99xx_device in tms9900.c.
|
||||||
|
|
||||||
|
+----------------+
|
||||||
|
/HOLD | 1 \/ 40| /MEMEN
|
||||||
|
HOLDA | 2 39| READY
|
||||||
|
IAQ | 3 38| /WE
|
||||||
|
LSB +- A13,CRUOUT | 4 37| CRUCLK
|
||||||
|
| A12 | 5 36| Vdd
|
||||||
|
| A11 | 6 35| Vss
|
||||||
|
| A10 | 7 34| CKIN
|
||||||
|
Address A9 | 8 33| D7 --+
|
||||||
|
bus A8 | 9 32| D6 |
|
||||||
|
| A7 |10 31| D5 Data
|
||||||
|
16KiB A6 |11 30| D4 bus
|
||||||
|
| A5 |12 29| D3 |
|
||||||
|
| A4 |13 28| D2 2 * 8 bit
|
||||||
|
| A3 |14 27| D1 |
|
||||||
|
| A2 |15 26| D0 --+
|
||||||
|
| A1 |16 25| INT0 --+
|
||||||
|
MSB +-- A0 |17 24| INT1 | Interrupt levels
|
||||||
|
DBIN |18 23| INT2 --+
|
||||||
|
CRUIN |19 22| /PHI3
|
||||||
|
Vcc |20 21| Vbb
|
||||||
|
+----------------+
|
||||||
|
|
||||||
|
The TMS9980A is similar to the TMS9900, with the following differences:
|
||||||
|
|
||||||
|
- Address bus is only 14 bit wide (16 KiB)
|
||||||
|
- Data bus is 16 bit wide and multiplexed on 8 lines (2 bytes per access)
|
||||||
|
- CRU space is limited to 2048 bits (due to fewer address lines)
|
||||||
|
- Only three interrupt level lines, for a maximum of 8 levels.
|
||||||
|
- No INTREQ, RESET, and LOAD lines. All interrupts are signaled via INT0 -
|
||||||
|
INT2. Reset=00x, Load=010, Level1=011, Level2=100, Level3=101, Level4=110,
|
||||||
|
all interrupts cleared=111.
|
||||||
|
- Memory accesses are always 2 bytes (even address byte, odd address byte)
|
||||||
|
even for byte operations. Thus the 9980A, like the TMS9900, needs to
|
||||||
|
pre-fetch the word at the destination before overwriting it.
|
||||||
|
- On the cycle level both TMS9900 and TMS9980A are equal, except for the
|
||||||
|
additional cycles needed for memory read and write access. Accordingly,
|
||||||
|
the emulation shares the core and the microprograms and redefines the
|
||||||
|
memory access and the interrupt handling only.
|
||||||
|
- The 9980A has the same external instructions as the TMS9900, but it
|
||||||
|
indicates the command via A0, A1, and A13 (instead of A0-A2).
|
||||||
|
|
||||||
|
For pin definitions see tms9900.c
|
||||||
|
|
||||||
|
Michael Zapf, 2012
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "tms9980a.h"
|
||||||
|
|
||||||
|
#define LOG logerror
|
||||||
|
#define VERBOSE 1
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
Constructor
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
tms9980a_device::tms9980a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||||
|
: tms99xx_device(mconfig, TMS9980A, "TMS9980A", tag, 8, 14, 11, owner, clock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT16 tms9980a_device::read_workspace_register_debug(int reg)
|
||||||
|
{
|
||||||
|
int temp = m_icount;
|
||||||
|
int addr = (WP+(reg<<1)) & 0xfffe & m_prgaddr_mask;
|
||||||
|
UINT16 value = (m_prgspace->read_byte(addr) << 8) | (m_prgspace->read_byte(addr+1) & 0xff);
|
||||||
|
m_icount = temp;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tms9980a_device::write_workspace_register_debug(int reg, UINT16 data)
|
||||||
|
{
|
||||||
|
int temp = m_icount;
|
||||||
|
int addr = (WP+(reg<<1)) & 0xfffe & m_prgaddr_mask;
|
||||||
|
m_prgspace->write_byte(addr, data>>8);
|
||||||
|
m_prgspace->write_byte(addr+1, data & 0xff);
|
||||||
|
m_icount = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Interrupt input. Keep in mind that the TMS9980A does not have any INTREQ
|
||||||
|
line but signals interrupts via IC0-IC2 only. Thus we cannot take down any
|
||||||
|
single interrupt; only all interrupts can be cleared at once using level 7.
|
||||||
|
The state parameter is actually not needed.
|
||||||
|
*/
|
||||||
|
void tms9980a_device::execute_set_input(int irqline, int state)
|
||||||
|
{
|
||||||
|
m_irq_level = get_intlevel(state);
|
||||||
|
|
||||||
|
if (m_irq_level != 7)
|
||||||
|
{
|
||||||
|
if (m_irq_level == LOAD_INT)
|
||||||
|
{
|
||||||
|
// Clearing m_reset is a hack to prevent an initial RESET.
|
||||||
|
// Should fix that in tms99xx
|
||||||
|
m_reset = false;
|
||||||
|
m_load_state = true;
|
||||||
|
}
|
||||||
|
else m_irq_state = true;
|
||||||
|
if (VERBOSE>6) LOG("tms9980a: interrupt level=%d, ST=%04x\n", m_irq_level, ST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tms9980a_device::get_intlevel(int state)
|
||||||
|
{
|
||||||
|
int level = m_get_intlevel(0) & 0x0007;
|
||||||
|
|
||||||
|
// Just to stay consistent.
|
||||||
|
if (state==CLEAR_LINE) level = 7;
|
||||||
|
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
level = RESET_INT;
|
||||||
|
m_reset = true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
level = LOAD_INT;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
level = level - 2;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
// Clear all interrupts
|
||||||
|
m_load_state = false;
|
||||||
|
m_irq_state = false;
|
||||||
|
if (VERBOSE>6) LOG("tms9980a: clear interrupts\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Memory read:
|
||||||
|
Clock cycles: 4 + 2W, W = number of wait states
|
||||||
|
*/
|
||||||
|
void tms9980a_device::mem_read()
|
||||||
|
{
|
||||||
|
UINT8 value;
|
||||||
|
if (m_lowbyte)
|
||||||
|
{
|
||||||
|
value = m_prgspace->read_byte((m_address & m_prgaddr_mask) | 1);
|
||||||
|
m_current_value = m_current_value | (value & 0x00ff);
|
||||||
|
if (VERBOSE>7) LOG("tms9980a: memory read low byte %04x -> complete word %04x\n", (m_address & m_prgaddr_mask) | 1, m_current_value);
|
||||||
|
m_lowbyte = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = m_prgspace->read_byte(m_address & 0x3ffe);
|
||||||
|
if (VERBOSE>7) LOG("tms9980a: memory read high byte %04x -> %02x\n", m_address & m_prgaddr_mask, value);
|
||||||
|
m_current_value = (value << 8) & 0xff00;
|
||||||
|
m_lowbyte = true;
|
||||||
|
m_pass = 2; // make the CPU visit this method once more
|
||||||
|
}
|
||||||
|
pulse_clock(2);
|
||||||
|
m_check_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tms9980a_device::mem_write()
|
||||||
|
{
|
||||||
|
if (m_lowbyte)
|
||||||
|
{
|
||||||
|
m_prgspace->write_byte((m_address & 0x3ffe) | 1, m_current_value & 0xff);
|
||||||
|
if (VERBOSE>7) LOG("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff);
|
||||||
|
m_lowbyte = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_prgspace->write_byte(m_address & 0x3ffe, (m_current_value >> 8)&0xff);
|
||||||
|
if (VERBOSE>7) LOG("tms9980a: memory write high byte %04x <- %02x\n", m_address & m_prgaddr_mask, (m_current_value >> 8)&0xff);
|
||||||
|
m_lowbyte = true;
|
||||||
|
m_pass = 2; // make the CPU visit this method once more
|
||||||
|
}
|
||||||
|
pulse_clock(2);
|
||||||
|
m_check_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tms9980a_device::acquire_instruction()
|
||||||
|
{
|
||||||
|
if (!m_lowbyte)
|
||||||
|
{
|
||||||
|
m_iaq_line(ASSERT_LINE);
|
||||||
|
m_address = PC;
|
||||||
|
m_first_cycle = m_icount;
|
||||||
|
mem_read();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mem_read();
|
||||||
|
decode(m_current_value);
|
||||||
|
if (VERBOSE>3) LOG("tms9980a: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC);
|
||||||
|
debugger_instruction_hook(this, PC);
|
||||||
|
PC = (PC + 2) & 0xfffe & m_prgaddr_mask;
|
||||||
|
}
|
||||||
|
// IAQ will be cleared in the main loop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************/
|
||||||
|
UINT32 tms9980a_device::execute_min_cycles() const
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Compute this value, just a wild guess for the average
|
||||||
|
UINT32 tms9980a_device::execute_max_cycles() const
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 tms9980a_device::execute_input_lines() const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clocks to cycles, cycles to clocks = id
|
||||||
|
// execute_default_irq_vector = 0
|
||||||
|
// execute_burn = nop
|
||||||
|
|
||||||
|
// device_disasm_interface overrides
|
||||||
|
UINT32 tms9980a_device::disasm_min_opcode_bytes() const
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 tms9980a_device::disasm_max_opcode_bytes() const
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
offs_t tms9980a_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
|
||||||
|
{
|
||||||
|
return Dasm9900(buffer, pc, TMS9980_ID, oprom, opram);
|
||||||
|
}
|
||||||
|
|
||||||
|
const device_type TMS9980A = &device_creator<tms9980a_device>;
|
||||||
|
54
src/emu/cpu/tms9900/tms9980a.h
Normal file
54
src/emu/cpu/tms9900/tms9980a.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
TMS9980A.
|
||||||
|
|
||||||
|
See tms9980a.c and tms9900.c for documentation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TMS9980A_H__
|
||||||
|
#define __TMS9980A_H__
|
||||||
|
|
||||||
|
#include "emu.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
#include "tms9900.h"
|
||||||
|
|
||||||
|
#define MCFG_TMS9980A_ADD(_tag, _device, _clock, _prgmap, _iomap, _config) \
|
||||||
|
MCFG_DEVICE_ADD(_tag, _device, _clock ) \
|
||||||
|
MCFG_DEVICE_PROGRAM_MAP(_prgmap) \
|
||||||
|
MCFG_DEVICE_IO_MAP(_iomap) \
|
||||||
|
MCFG_DEVICE_CONFIG(_config)
|
||||||
|
|
||||||
|
#define TMS9980A_CONFIG(name) \
|
||||||
|
const tms9900_config(name) =
|
||||||
|
|
||||||
|
|
||||||
|
class tms9980a_device : public tms99xx_device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tms9980a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mem_read(void);
|
||||||
|
void mem_write(void);
|
||||||
|
void acquire_instruction(void);
|
||||||
|
|
||||||
|
UINT16 read_workspace_register_debug(int reg);
|
||||||
|
void write_workspace_register_debug(int reg, UINT16 data);
|
||||||
|
|
||||||
|
UINT32 execute_min_cycles() const;
|
||||||
|
UINT32 execute_max_cycles() const;
|
||||||
|
UINT32 execute_input_lines() const;
|
||||||
|
void execute_set_input(int irqline, int state);
|
||||||
|
|
||||||
|
UINT32 disasm_min_opcode_bytes() const;
|
||||||
|
UINT32 disasm_max_opcode_bytes() const;
|
||||||
|
offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options);
|
||||||
|
address_space_config m_program_config80;
|
||||||
|
address_space_config m_io_config80;
|
||||||
|
|
||||||
|
int get_intlevel(int state);
|
||||||
|
};
|
||||||
|
|
||||||
|
// device type definition
|
||||||
|
extern const device_type TMS9980A;
|
||||||
|
|
||||||
|
#endif /* __TMS9995_H__ */
|
Loading…
Reference in New Issue
Block a user