diff options
Diffstat (limited to 'trunk/src/emu/cpu/tms34010/tms34010.c')
-rw-r--r-- | trunk/src/emu/cpu/tms34010/tms34010.c | 1830 |
1 files changed, 1830 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/tms34010/tms34010.c b/trunk/src/emu/cpu/tms34010/tms34010.c new file mode 100644 index 00000000000..b534f69a73f --- /dev/null +++ b/trunk/src/emu/cpu/tms34010/tms34010.c @@ -0,0 +1,1830 @@ +/*************************************************************************** + + TMS34010: Portable Texas Instruments TMS34010 emulator + + Copyright Alex Pasadyn/Zsolt Vasvari + Parts based on code by Aaron Giles + +***************************************************************************/ + +#include "emu.h" +#include "debugger.h" +#include "tms34010.h" + + +/*************************************************************************** + DEBUG STATE & STRUCTURES +***************************************************************************/ + +#define VERBOSE 0 +#define LOG_CONTROL_REGS 0 +#define LOG_GRAPHICS_OPS 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + + +/*************************************************************************** + CORE STATE +***************************************************************************/ + +/* TMS34010 State */ +typedef struct _XY XY; +struct _XY +{ +#ifdef LSB_FIRST + INT16 x; + INT16 y; +#else + INT16 y; + INT16 x; +#endif +}; + +typedef struct _tms34010_state tms34010_state; +struct _tms34010_state +{ + UINT32 pc; + UINT32 ppc; + UINT32 st; + void (*pixel_write)(tms34010_state *tms, offs_t offset, UINT32 data); + UINT32 (*pixel_read)(tms34010_state *tms, offs_t offset); + UINT32 (*raster_op)(tms34010_state *tms, UINT32 newpix, UINT32 oldpix); + UINT32 convsp; + UINT32 convdp; + UINT32 convmp; + UINT16 * shiftreg; + INT32 gfxcycles; + UINT8 pixelshift; + UINT8 is_34020; + UINT8 reset_deferred; + UINT8 hblank_stable; + UINT8 external_host_access; + UINT8 executing; + device_irq_acknowledge_callback irq_callback; + legacy_cpu_device *device; + address_space *program; + direct_read_data *direct; + const tms34010_config *config; + screen_device *screen; + emu_timer * scantimer; + int icount; + + /* A registers 0-15 map to regs[0]-regs[15] */ + /* B registers 0-15 map to regs[30]-regs[15] */ + union + { + INT32 reg; + XY xy; + } regs[31]; + + UINT16 IOregs[64]; +}; + +INLINE tms34010_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == TMS34010 || + device->type() == TMS34020); + return (tms34010_state *)downcast<legacy_cpu_device *>(device)->token(); +} + +#include "34010ops.h" + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* default configuration */ +static const tms34010_config default_config = +{ + 0 +}; + +static void check_interrupt(tms34010_state *tms); +static TIMER_CALLBACK( scanline_callback ); +static void tms34010_state_postload(tms34010_state *tms); + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* status register definitions */ +#define STBIT_N (1 << 31) +#define STBIT_C (1 << 30) +#define STBIT_Z (1 << 29) +#define STBIT_V (1 << 28) +#define STBIT_P (1 << 25) +#define STBIT_IE (1 << 21) +#define STBIT_FE1 (1 << 11) +#define STBITS_F1 (0x1f << 6) +#define STBIT_FE0 (1 << 5) +#define STBITS_F0 (0x1f << 0) + +/* register definitions and shortcuts */ +#define N_FLAG(T) ((T)->st & STBIT_N) +#define Z_FLAG(T) ((T)->st & STBIT_Z) +#define C_FLAG(T) ((T)->st & STBIT_C) +#define V_FLAG(T) ((T)->st & STBIT_V) +#define P_FLAG(T) ((T)->st & STBIT_P) +#define IE_FLAG(T) ((T)->st & STBIT_IE) +#define FE0_FLAG(T) ((T)->st & STBIT_FE0) +#define FE1_FLAG(T) ((T)->st & STBIT_FE1) + +/* register file access */ +#define AREG(T,i) ((T)->regs[i].reg) +#define AREG_XY(T,i) ((T)->regs[i].xy) +#define AREG_X(T,i) ((T)->regs[i].xy.x) +#define AREG_Y(T,i) ((T)->regs[i].xy.y) +#define BREG(T,i) ((T)->regs[30 - (i)].reg) +#define BREG_XY(T,i) ((T)->regs[30 - (i)].xy) +#define BREG_X(T,i) ((T)->regs[30 - (i)].xy.x) +#define BREG_Y(T,i) ((T)->regs[30 - (i)].xy.y) +#define SP(T) AREG(T,15) +#define FW(T,i) (((T)->st >> (i ? 6 : 0)) & 0x1f) +#define FWEX(T,i) (((T)->st >> (i ? 6 : 0)) & 0x3f) + +/* opcode decode helpers */ +#define SRCREG(O) (((O) >> 5) & 0x0f) +#define DSTREG(O) ((O) & 0x0f) +#define SKIP_WORD(T) ((T)->pc += (2 << 3)) +#define SKIP_LONG(T) ((T)->pc += (4 << 3)) +#define PARAM_K(O) (((O) >> 5) & 0x1f) +#define PARAM_N(O) ((O) & 0x1f) +#define PARAM_REL8(O) ((INT8)(O)) + +/* memory I/O */ +#define WFIELD0(T,a,b) (*tms34010_wfield_functions[FW(T,0)])(T,a,b) +#define WFIELD1(T,a,b) (*tms34010_wfield_functions[FW(T,1)])(T,a,b) +#define RFIELD0(T,a) (*tms34010_rfield_functions[FWEX(T,0)])(T,a) +#define RFIELD1(T,a) (*tms34010_rfield_functions[FWEX(T,1)])(T,a) +#define WPIXEL(T,a,b) (*(T)->pixel_write)(T,a,b) +#define RPIXEL(T,a) (*(T)->pixel_read)(T,a) + +/* Implied Operands */ +#define SADDR(T) BREG(T,0) +#define SADDR_X(T) BREG_X(T,0) +#define SADDR_Y(T) BREG_Y(T,0) +#define SADDR_XY(T) BREG_XY(T,0) +#define SPTCH(T) BREG(T,1) +#define DADDR(T) BREG(T,2) +#define DADDR_X(T) BREG_X(T,2) +#define DADDR_Y(T) BREG_Y(T,2) +#define DADDR_XY(T) BREG_XY(T,2) +#define DPTCH(T) BREG(T,3) +#define OFFSET(T) BREG(T,4) +#define WSTART_X(T) BREG_X(T,5) +#define WSTART_Y(T) BREG_Y(T,5) +#define WEND_X(T) BREG_X(T,6) +#define WEND_Y(T) BREG_Y(T,6) +#define DYDX_X(T) BREG_X(T,7) +#define DYDX_Y(T) BREG_Y(T,7) +#define COLOR0(T) BREG(T,8) +#define COLOR1(T) BREG(T,9) +#define COUNT(T) BREG(T,10) +#define INC1_X(T) BREG_X(T,11) +#define INC1_Y(T) BREG_Y(T,11) +#define INC2_X(T) BREG_X(T,12) +#define INC2_Y(T) BREG_Y(T,12) +#define PATTRN(T) BREG(T,13) +#define TEMP(T) BREG(T,14) + +/* I/O registers */ +#define WINDOW_CHECKING(T) ((IOREG(T, REG_CONTROL) >> 6) & 0x03) + + + +/*************************************************************************** + INLINE SHORTCUTS +***************************************************************************/ + +/* Break up Status Register into indiviual flags */ +INLINE void SET_ST(tms34010_state *tms, UINT32 st) +{ + tms->st = st; + /* interrupts might have been enabled, check it */ + check_interrupt(tms); +} + +/* Intialize Status to 0x0010 */ +INLINE void RESET_ST(tms34010_state *tms) +{ + SET_ST(tms, 0x00000010); +} + +/* shortcuts for reading opcodes */ +INLINE UINT32 ROPCODE(tms34010_state *tms) +{ + UINT32 pc = TOBYTE(tms->pc); + tms->pc += 2 << 3; + return tms->direct->read_decrypted_word(pc); +} + +INLINE INT16 PARAM_WORD(tms34010_state *tms) +{ + UINT32 pc = TOBYTE(tms->pc); + tms->pc += 2 << 3; + return tms->direct->read_raw_word(pc); +} + +INLINE INT32 PARAM_LONG(tms34010_state *tms) +{ + UINT32 pc = TOBYTE(tms->pc); + tms->pc += 4 << 3; + return (UINT16)tms->direct->read_raw_word(pc) | (tms->direct->read_raw_word(pc + 2) << 16); +} + +INLINE INT16 PARAM_WORD_NO_INC(tms34010_state *tms) +{ + return tms->direct->read_raw_word(TOBYTE(tms->pc)); +} + +INLINE INT32 PARAM_LONG_NO_INC(tms34010_state *tms) +{ + UINT32 pc = TOBYTE(tms->pc); + return (UINT16)tms->direct->read_raw_word(pc) | (tms->direct->read_raw_word(pc + 2) << 16); +} + +/* read memory byte */ +INLINE UINT32 RBYTE(tms34010_state *tms, offs_t offset) +{ + UINT32 ret; + RFIELDMAC_8(tms); + return ret; +} + +/* write memory byte */ +INLINE void WBYTE(tms34010_state *tms, offs_t offset, UINT32 data) +{ + WFIELDMAC_8(tms); +} + +/* read memory long */ +INLINE UINT32 RLONG(tms34010_state *tms, offs_t offset) +{ + RFIELDMAC_32(tms); +} + +/* write memory long */ +INLINE void WLONG(tms34010_state *tms, offs_t offset, UINT32 data) +{ + WFIELDMAC_32(tms); +} + +/* pushes/pops a value from the stack */ +INLINE void PUSH(tms34010_state *tms, UINT32 data) +{ + SP(tms) -= 0x20; + WLONG(tms, SP(tms), data); +} + +INLINE INT32 POP(tms34010_state *tms) +{ + INT32 ret = RLONG(tms, SP(tms)); + SP(tms) += 0x20; + return ret; +} + + + +/*************************************************************************** + PIXEL READS +***************************************************************************/ + +#define RP(T,m1,m2) \ + /* TODO: Plane masking */ \ + return (TMS34010_RDMEM_WORD(T, TOBYTE(offset & 0xfffffff0)) >> (offset & m1)) & m2; + +static UINT32 read_pixel_1(tms34010_state *tms, offs_t offset) { RP(tms,0x0f,0x01) } +static UINT32 read_pixel_2(tms34010_state *tms, offs_t offset) { RP(tms,0x0e,0x03) } +static UINT32 read_pixel_4(tms34010_state *tms, offs_t offset) { RP(tms,0x0c,0x0f) } +static UINT32 read_pixel_8(tms34010_state *tms, offs_t offset) { RP(tms,0x08,0xff) } +static UINT32 read_pixel_16(tms34010_state *tms, offs_t offset) +{ + /* TODO: Plane masking */ + return TMS34010_RDMEM_WORD(tms, TOBYTE(offset & 0xfffffff0)); +} +static UINT32 read_pixel_32(tms34010_state *tms, offs_t offset) +{ + /* TODO: Plane masking */ + return TMS34010_RDMEM_DWORD(tms, TOBYTE(offset & 0xffffffe0)); +} + +/* Shift register read */ +static UINT32 read_pixel_shiftreg(tms34010_state *tms, offs_t offset) +{ + if (tms->config->to_shiftreg) + tms->config->to_shiftreg(tms->program, offset, &tms->shiftreg[0]); + else + fatalerror("To ShiftReg function not set. PC = %08X\n", tms->pc); + return tms->shiftreg[0]; +} + + + +/*************************************************************************** + PIXEL WRITES +***************************************************************************/ + +/* No Raster Op + No Transparency */ +#define WP(T,m1,m2) \ + UINT32 a = TOBYTE(offset & 0xfffffff0); \ + UINT32 pix = TMS34010_RDMEM_WORD(T,a); \ + UINT32 shiftcount = offset & m1; \ + \ + /* TODO: plane masking */ \ + data &= m2; \ + pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount); \ + TMS34010_WRMEM_WORD(T, a, pix); \ + +/* No Raster Op + Transparency */ +#define WP_T(T,m1,m2) \ + /* TODO: plane masking */ \ + data &= m2; \ + if (data) \ + { \ + UINT32 a = TOBYTE(offset & 0xfffffff0); \ + UINT32 pix = TMS34010_RDMEM_WORD(T,a); \ + UINT32 shiftcount = offset & m1; \ + \ + /* TODO: plane masking */ \ + pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount); \ + TMS34010_WRMEM_WORD(T, a, pix); \ + } \ + +/* Raster Op + No Transparency */ +#define WP_R(T,m1,m2) \ + UINT32 a = TOBYTE(offset & 0xfffffff0); \ + UINT32 pix = TMS34010_RDMEM_WORD(T,a); \ + UINT32 shiftcount = offset & m1; \ + \ + /* TODO: plane masking */ \ + data = (*(T)->raster_op)(tms, data & m2, (pix >> shiftcount) & m2) & m2; \ + pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount); \ + TMS34010_WRMEM_WORD(T, a, pix); \ + +/* Raster Op + Transparency */ +#define WP_R_T(T,m1,m2) \ + UINT32 a = TOBYTE(offset & 0xfffffff0); \ + UINT32 pix = TMS34010_RDMEM_WORD(T,a); \ + UINT32 shiftcount = offset & m1; \ + \ + /* TODO: plane masking */ \ + data = (*(T)->raster_op)(tms, data & m2, (pix >> shiftcount) & m2) & m2; \ + if (data) \ + { \ + pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount); \ + TMS34010_WRMEM_WORD(T, a, pix); \ + } \ + + +/* No Raster Op + No Transparency */ +static void write_pixel_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0f, 0x01); } +static void write_pixel_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0e, 0x03); } +static void write_pixel_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0c, 0x0f); } +static void write_pixel_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x08, 0xff); } +static void write_pixel_16(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xfffffff0), data); +} +static void write_pixel_32(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xffffffe0), data); +} + +/* No Raster Op + Transparency */ +static void write_pixel_t_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0f, 0x01); } +static void write_pixel_t_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0e, 0x03); } +static void write_pixel_t_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0c, 0x0f); } +static void write_pixel_t_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x08, 0xff); } +static void write_pixel_t_16(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + if (data) + TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xfffffff0), data); +} +static void write_pixel_t_32(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + if (data) + TMS34010_WRMEM_DWORD(tms, TOBYTE(offset & 0xffffffe0), data); +} + +/* Raster Op + No Transparency */ +static void write_pixel_r_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0f, 0x01); } +static void write_pixel_r_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0e, 0x03); } +static void write_pixel_r_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0c, 0x0f); } +static void write_pixel_r_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x08, 0xff); } +static void write_pixel_r_16(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + UINT32 a = TOBYTE(offset & 0xfffffff0); + TMS34010_WRMEM_WORD(tms, a, (*tms->raster_op)(tms, data, TMS34010_RDMEM_WORD(tms, a))); +} +static void write_pixel_r_32(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + UINT32 a = TOBYTE(offset & 0xffffffe0); + TMS34010_WRMEM_DWORD(tms, a, (*tms->raster_op)(tms, data, TMS34010_RDMEM_DWORD(tms, a))); +} + +/* Raster Op + Transparency */ +static void write_pixel_r_t_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0f,0x01); } +static void write_pixel_r_t_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0e,0x03); } +static void write_pixel_r_t_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0c,0x0f); } +static void write_pixel_r_t_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x08,0xff); } +static void write_pixel_r_t_16(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + UINT32 a = TOBYTE(offset & 0xfffffff0); + data = (*tms->raster_op)(tms, data, TMS34010_RDMEM_WORD(tms, a)); + + if (data) + TMS34010_WRMEM_WORD(tms, a, data); +} +static void write_pixel_r_t_32(tms34010_state *tms, offs_t offset, UINT32 data) +{ + /* TODO: plane masking */ + UINT32 a = TOBYTE(offset & 0xffffffe0); + data = (*tms->raster_op)(tms, data, TMS34010_RDMEM_DWORD(tms, a)); + + if (data) + TMS34010_WRMEM_DWORD(tms, a, data); +} + +/* Shift register write */ +static void write_pixel_shiftreg(tms34010_state *tms, offs_t offset, UINT32 data) +{ + if (tms->config->from_shiftreg) + tms->config->from_shiftreg(tms->program, offset, &tms->shiftreg[0]); + else + fatalerror("From ShiftReg function not set. PC = %08X\n", tms->pc); +} + + + +/*************************************************************************** + RASTER OPS +***************************************************************************/ + +/* Raster operations */ +static UINT32 raster_op_1(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix & oldpix; } +static UINT32 raster_op_2(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix & ~oldpix; } +static UINT32 raster_op_3(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return 0; } +static UINT32 raster_op_4(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix | ~oldpix; } +static UINT32 raster_op_5(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~(newpix ^ oldpix); } +static UINT32 raster_op_6(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~oldpix; } +static UINT32 raster_op_7(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~(newpix | oldpix); } +static UINT32 raster_op_8(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix | oldpix; } +static UINT32 raster_op_9(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return oldpix; } +static UINT32 raster_op_10(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix ^ oldpix; } +static UINT32 raster_op_11(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix & oldpix; } +static UINT32 raster_op_12(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return 0xffff; } +static UINT32 raster_op_13(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix | oldpix; } +static UINT32 raster_op_14(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~(newpix & oldpix); } +static UINT32 raster_op_15(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix; } +static UINT32 raster_op_16(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix + oldpix; } +static UINT32 raster_op_17(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) +{ + UINT32 max = (UINT32)0xffffffff >> (32 - IOREG(tms, REG_PSIZE)); + UINT32 res = newpix + oldpix; + return (res > max) ? max : res; +} +static UINT32 raster_op_18(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return oldpix - newpix; } +static UINT32 raster_op_19(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? oldpix - newpix : 0; } +static UINT32 raster_op_20(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? oldpix : newpix; } +static UINT32 raster_op_21(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? newpix : oldpix; } + + + +/*************************************************************************** + OPCODE TABLE & IMPLEMENTATIONS +***************************************************************************/ + +#include "34010fld.c" + +/* includes the static function prototypes and the master opcode table */ +#include "34010tbl.c" + +/* includes the actual opcode implementations */ +#include "34010ops.c" +#include "34010gfx.c" + + + +/*************************************************************************** + Internal interrupt check +****************************************************************************/ + +/* Generate pending interrupts. */ +static void check_interrupt(tms34010_state *tms) +{ + int vector = 0; + int irqline = -1; + int irq; + + /* if we're not actively executing, skip it */ + if (!tms->executing) + return; + + /* check for NMI first */ + if (IOREG(tms, REG_HSTCTLH) & 0x0100) + { + LOG(("TMS34010 '%s' takes NMI\n", tms->device->tag())); + + /* ack the NMI */ + IOREG(tms, REG_HSTCTLH) &= ~0x0100; + + /* handle NMI mode bit */ + if (!(IOREG(tms, REG_HSTCTLH) & 0x0200)) + { + PUSH(tms, tms->pc); + PUSH(tms, tms->st); + } + + /* leap to the vector */ + RESET_ST(tms); + tms->pc = RLONG(tms, 0xfffffee0); + COUNT_CYCLES(tms,16); + return; + } + + /* early out if everything else is disabled */ + irq = IOREG(tms, REG_INTPEND) & IOREG(tms, REG_INTENB); + if (!IE_FLAG(tms) || !irq) + return; + + /* host interrupt */ + if (irq & TMS34010_HI) + { + LOG(("TMS34010 '%s' takes HI\n", tms->device->tag())); + vector = 0xfffffec0; + } + + /* display interrupt */ + else if (irq & TMS34010_DI) + { + LOG(("TMS34010 '%s' takes DI\n", tms->device->tag())); + vector = 0xfffffea0; + } + + /* window violation interrupt */ + else if (irq & TMS34010_WV) + { + LOG(("TMS34010 '%s' takes WV\n", tms->device->tag())); + vector = 0xfffffe80; + } + + /* external 1 interrupt */ + else if (irq & TMS34010_INT1) + { + LOG(("TMS34010 '%s' takes INT1\n", tms->device->tag())); + vector = 0xffffffc0; + irqline = 0; + } + + /* external 2 interrupt */ + else if (irq & TMS34010_INT2) + { + LOG(("TMS34010 '%s' takes INT2\n", tms->device->tag())); + vector = 0xffffffa0; + irqline = 1; + } + + /* if we took something, generate it */ + if (vector) + { + PUSH(tms, tms->pc); + PUSH(tms, tms->st); + RESET_ST(tms); + tms->pc = RLONG(tms, vector); + COUNT_CYCLES(tms,16); + + /* call the callback for externals */ + if (irqline >= 0) + (void)(*tms->irq_callback)(tms->device, irqline); + } +} + + + +/*************************************************************************** + Reset the CPU emulation +***************************************************************************/ + +static CPU_INIT( tms34010 ) +{ + const tms34010_config *configdata = device->static_config() ? (const tms34010_config *)device->static_config() : &default_config; + tms34010_state *tms = get_safe_token(device); + + tms->external_host_access = FALSE; + + tms->config = configdata; + tms->irq_callback = irqcallback; + tms->device = device; + tms->program = device->space(AS_PROGRAM); + tms->direct = &tms->program->direct(); + tms->screen = downcast<screen_device *>(device->machine().device(configdata->screen_tag)); + + /* set up the state table */ + { + device_state_interface *state; + device->interface(state); + state->state_add(TMS34010_PC, "PC", tms->pc); + state->state_add(STATE_GENPC, "GENPC", tms->pc).noshow(); + state->state_add(STATE_GENPCBASE, "GENPCBASE", tms->ppc).noshow(); + state->state_add(TMS34010_SP, "SP", tms->regs[15].reg); + state->state_add(STATE_GENSP, "GENSP", tms->regs[15].reg).noshow(); + state->state_add(TMS34010_ST, "ST", tms->st); + state->state_add(STATE_GENFLAGS, "GENFLAGS", tms->st).noshow().formatstr("%18s"); + + astring tempstr; + for (int regnum = 0; regnum < 15; regnum++) + state->state_add(TMS34010_A0 + regnum, tempstr.format("A%d", regnum), tms->regs[regnum].reg); + for (int regnum = 0; regnum < 15; regnum++) + state->state_add(TMS34010_B0 + regnum, tempstr.format("B%d", regnum), tms->regs[30 - regnum].reg); + } + + /* allocate a scanline timer and set it to go off at the start */ + tms->scantimer = device->machine().scheduler().timer_alloc(FUNC(scanline_callback), tms); + tms->scantimer->adjust(attotime::zero); + + /* allocate the shiftreg */ + tms->shiftreg = auto_alloc_array(device->machine(), UINT16, SHIFTREG_SIZE/2); + + device->save_item(NAME(tms->pc)); + device->save_item(NAME(tms->st)); + device->save_item(NAME(tms->reset_deferred)); + device->save_pointer(NAME(tms->shiftreg), SHIFTREG_SIZE / 2); + device->save_item(NAME(tms->IOregs)); + device->save_item(NAME(tms->convsp)); + device->save_item(NAME(tms->convdp)); + device->save_item(NAME(tms->convmp)); + device->save_item(NAME(tms->pixelshift)); + device->save_item(NAME(tms->gfxcycles)); + device->save_pointer(NAME(&tms->regs[0].reg), ARRAY_LENGTH(tms->regs)); + device->machine().save().register_postload(save_prepost_delegate(FUNC(tms34010_state_postload), tms)); +} + +static CPU_RESET( tms34010 ) +{ + /* zap the state and copy in the config pointer */ + tms34010_state *tms = get_safe_token(device); + const tms34010_config *config = tms->config; + screen_device *screen = tms->screen; + UINT16 *shiftreg = tms->shiftreg; + device_irq_acknowledge_callback save_irqcallback = tms->irq_callback; + emu_timer *save_scantimer = tms->scantimer; + + memset(tms, 0, sizeof(*tms)); + + tms->config = config; + tms->screen = screen; + tms->shiftreg = shiftreg; + tms->irq_callback = save_irqcallback; + tms->scantimer = save_scantimer; + tms->device = device; + tms->program = device->space(AS_PROGRAM); + tms->direct = &tms->program->direct(); + + /* fetch the initial PC and reset the state */ + tms->pc = RLONG(tms, 0xffffffe0) & 0xfffffff0; + RESET_ST(tms); + + /* HALT the CPU if requested, and remember to re-read the starting PC */ + /* the first time we are run */ + tms->reset_deferred = tms->config->halt_on_reset; + if (tms->config->halt_on_reset) + tms34010_io_register_w(device->space(AS_PROGRAM), REG_HSTCTLH, 0x8000, 0xffff); +} + + +static CPU_RESET( tms34020 ) +{ + tms34010_state *tms = get_safe_token(device); + CPU_RESET_CALL(tms34010); + tms->is_34020 = 1; +} + + + +/*************************************************************************** + Shut down the CPU emulation +***************************************************************************/ + +static CPU_EXIT( tms34010 ) +{ + tms34010_state *tms = get_safe_token(device); + tms->shiftreg = NULL; +} + + + +/*************************************************************************** + Set IRQ line state +***************************************************************************/ + +static void set_irq_line(tms34010_state *tms, int irqline, int linestate) +{ + LOG(("TMS34010 '%s' set irq line %d state %d\n", tms->device->tag(), irqline, linestate)); + + /* set the pending interrupt */ + switch (irqline) + { + case 0: + if (linestate != CLEAR_LINE) + IOREG(tms, REG_INTPEND) |= TMS34010_INT1; + else + IOREG(tms, REG_INTPEND) &= ~TMS34010_INT1; + break; + + case 1: + if (linestate != CLEAR_LINE) + IOREG(tms, REG_INTPEND) |= TMS34010_INT2; + else + IOREG(tms, REG_INTPEND) &= ~TMS34010_INT2; + break; + } +} + + + +/*************************************************************************** + Generate internal interrupt +***************************************************************************/ + +static TIMER_CALLBACK( internal_interrupt_callback ) +{ + tms34010_state *tms = (tms34010_state *)ptr; + int type = param; + + /* call through to the CPU to generate the int */ + IOREG(tms, REG_INTPEND) |= type; + LOG(("TMS34010 '%s' set internal interrupt $%04x\n", tms->device->tag(), type)); + + /* generate triggers so that spin loops can key off them */ + device_triggerint(tms->device); +} + + + +/*************************************************************************** + Execute +***************************************************************************/ + +static CPU_EXECUTE( tms34010 ) +{ + tms34010_state *tms = get_safe_token(device); + + /* Get out if CPU is halted. Absolutely no interrupts must be taken!!! */ + if (IOREG(tms, REG_HSTCTLH) & 0x8000) + { + tms->icount = 0; + return; + } + + /* if the CPU's reset was deferred, do it now */ + if (tms->reset_deferred) + { + tms->reset_deferred = 0; + tms->pc = RLONG(tms, 0xffffffe0); + } + + /* check interrupts first */ + tms->executing = TRUE; + check_interrupt(tms); + if ((tms->device->machine().debug_flags & DEBUG_FLAG_ENABLED) == 0) + { + do + { + UINT16 op; + tms->ppc = tms->pc; + op = ROPCODE(tms); + (*opcode_table[op >> 4])(tms, op); + } while (tms->icount > 0); + } + else + { + do + { + UINT16 op; + debugger_instruction_hook(tms->device, tms->pc); + tms->ppc = tms->pc; + op = ROPCODE(tms); + (*opcode_table[op >> 4])(tms, op); + } while (tms->icount > 0); + } + tms->executing = FALSE; +} + + + +/*************************************************************************** + PIXEL OPS +***************************************************************************/ + +static void (*const pixel_write_ops[4][6])(tms34010_state *tms, offs_t offset, UINT32 data) = +{ + { write_pixel_1, write_pixel_2, write_pixel_4, write_pixel_8, write_pixel_16, write_pixel_32 }, + { write_pixel_r_1, write_pixel_r_2, write_pixel_r_4, write_pixel_r_8, write_pixel_r_16, write_pixel_r_32 }, + { write_pixel_t_1, write_pixel_t_2, write_pixel_t_4, write_pixel_t_8, write_pixel_t_16, write_pixel_t_32 }, + { write_pixel_r_t_1, write_pixel_r_t_2, write_pixel_r_t_4, write_pixel_r_t_8, write_pixel_r_t_16, write_pixel_r_t_32 } +}; + +static UINT32 (*const pixel_read_ops[6])(tms34010_state *tms, offs_t offset) = +{ + read_pixel_1, read_pixel_2, read_pixel_4, read_pixel_8, read_pixel_16, read_pixel_32 +}; + + +static void set_pixel_function(tms34010_state *tms) +{ + UINT32 i1,i2; + + if (IOREG(tms, REG_DPYCTL) & 0x0800) + { + /* Shift Register Transfer */ + tms->pixel_write = write_pixel_shiftreg; + tms->pixel_read = read_pixel_shiftreg; + return; + } + + switch (IOREG(tms, REG_PSIZE)) + { + default: + case 0x01: i2 = 0; break; + case 0x02: i2 = 1; break; + case 0x04: i2 = 2; break; + case 0x08: i2 = 3; break; + case 0x10: i2 = 4; break; + case 0x20: i2 = 5; break; + } + + if (IOREG(tms, REG_CONTROL) & 0x20) + i1 = tms->raster_op ? 3 : 2; + else + i1 = tms->raster_op ? 1 : 0; + + tms->pixel_write = pixel_write_ops[i1][i2]; + tms->pixel_read = pixel_read_ops [i2]; +} + + + +/*************************************************************************** + RASTER OPS +***************************************************************************/ + +static UINT32 (*const raster_ops[32]) (tms34010_state *tms, UINT32 newpix, UINT32 oldpix) = +{ + 0, raster_op_1 , raster_op_2 , raster_op_3, + raster_op_4 , raster_op_5 , raster_op_6 , raster_op_7, + raster_op_8 , raster_op_9 , raster_op_10, raster_op_11, + raster_op_12, raster_op_13, raster_op_14, raster_op_15, + raster_op_16, raster_op_17, raster_op_18, raster_op_19, + raster_op_20, raster_op_21, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + + +static void set_raster_op(tms34010_state *tms) +{ + tms->raster_op = raster_ops[(IOREG(tms, REG_CONTROL) >> 10) & 0x1f]; +} + + + +/*************************************************************************** + VIDEO TIMING HELPERS +***************************************************************************/ + +static TIMER_CALLBACK( scanline_callback ) +{ + tms34010_state *tms = (tms34010_state *)ptr; + int vsblnk, veblnk, vtotal; + int vcount = param; + int enabled; + int master; + + /* fetch the core timing parameters */ + const rectangle ¤t_visarea = tms->screen->visible_area(); + enabled = SMART_IOREG(tms, DPYCTL) & 0x8000; + master = (tms->is_34020 || (SMART_IOREG(tms, DPYCTL) & 0x2000)); + vsblnk = SMART_IOREG(tms, VSBLNK); + veblnk = SMART_IOREG(tms, VEBLNK); + vtotal = SMART_IOREG(tms, VTOTAL); + if (!master) + { + vtotal = MIN(tms->screen->height() - 1, vtotal); + vcount = tms->screen->vpos(); + } + + /* update the VCOUNT */ + SMART_IOREG(tms, VCOUNT) = vcount; + + /* if we match the display interrupt scanline, signal an interrupt */ + if (enabled && vcount == SMART_IOREG(tms, DPYINT)) + { + /* generate the display interrupt signal */ + internal_interrupt_callback(machine, tms, TMS34010_DI); + } + + /* at the start of VBLANK, load the starting display address */ + if (vcount == vsblnk) + { + /* 34010 loads DPYADR with DPYSTRT, and inverts if the origin is 0 */ + if (!tms->is_34020) + { + IOREG(tms, REG_DPYADR) = IOREG(tms, REG_DPYSTRT); + LOG(("Start of VBLANK, DPYADR = %04X\n", IOREG(tms, REG_DPYADR))); + } + + /* 34020 loads DPYNXx with DPYSTx */ + else + { + IOREG(tms, REG020_DPYNXL) = IOREG(tms, REG020_DPYSTL) & 0xffe0; + IOREG(tms, REG020_DPYNXH) = IOREG(tms, REG020_DPYSTH); + } + } + + /* at the end of the screen, update the display parameters */ + if (vcount == vtotal) + { + /* only do this if we have an incoming pixel clock */ + /* also, only do it if the HEBLNK/HSBLNK values are stable */ + if (master && (tms->config->scanline_callback_ind16 != NULL || tms->config->scanline_callback_rgb32 != NULL)) + { + int htotal = SMART_IOREG(tms, HTOTAL); + if (htotal > 0 && vtotal > 0) + { + attoseconds_t refresh = HZ_TO_ATTOSECONDS(tms->config->pixclock) * (htotal + 1) * (vtotal + 1); + int width = (htotal + 1) * tms->config->pixperclock; + int height = vtotal + 1; + rectangle visarea; + + /* extract the visible area */ + visarea.min_x = SMART_IOREG(tms, HEBLNK) * tms->config->pixperclock; + visarea.max_x = SMART_IOREG(tms, HSBLNK) * tms->config->pixperclock - 1; + visarea.min_y = veblnk; + visarea.max_y = vsblnk - 1; + + /* if everything looks good, set the info */ + if (visarea.min_x < visarea.max_x && visarea.max_x <= width && visarea.min_y < visarea.max_y && visarea.max_y <= height) + { + /* because many games play with the HEBLNK/HSBLNK for effects, we don't change + if they are the only thing that has changed, unless they are stable for a couple + of frames */ + int current_width = tms->screen->width(); + int current_height = tms->screen->height(); + + if (width != current_width || height != current_height || visarea.min_y != current_visarea.min_y || visarea.max_y != current_visarea.max_y || + (tms->hblank_stable > 2 && (visarea.min_x != current_visarea.min_x || visarea.max_x != current_visarea.max_x))) + { + tms->screen->configure(width, height, visarea, refresh); + } + tms->hblank_stable++; + } + + LOG(("Configuring screen: HTOTAL=%3d BLANK=%3d-%3d VTOTAL=%3d BLANK=%3d-%3d refresh=%f\n", + htotal, SMART_IOREG(tms, HEBLNK), SMART_IOREG(tms, HSBLNK), vtotal, veblnk, vsblnk, ATTOSECONDS_TO_HZ(refresh))); + + /* interlaced timing not supported */ + if ((SMART_IOREG(tms, DPYCTL) & 0x4000) == 0) + fatalerror("Interlaced video configured on the TMS34010 (unsupported)"); + } + } + } + + /* force a partial update within the visible area */ + if (vcount >= current_visarea.min_y && vcount <= current_visarea.max_y && (tms->config->scanline_callback_ind16 != NULL || tms->config->scanline_callback_rgb32 != NULL)) + tms->screen->update_partial(vcount); + + /* if we are in the visible area, increment DPYADR by DUDATE */ + if (vcount >= veblnk && vcount < vsblnk) + { + /* 34010 increments by the DUDATE field in DPYCTL */ + if (!tms->is_34020) + { + UINT16 dpyadr = IOREG(tms, REG_DPYADR); + if ((dpyadr & 3) == 0) + dpyadr = ((dpyadr & 0xfffc) - (IOREG(tms, REG_DPYCTL) & 0x03fc)) | (IOREG(tms, REG_DPYSTRT) & 0x0003); + else + dpyadr = (dpyadr & 0xfffc) | ((dpyadr - 1) & 3); + IOREG(tms, REG_DPYADR) = dpyadr; + } + + /* 34020 updates based on the DINC register, including zoom */ + else + { + UINT32 dpynx = IOREG(tms, REG020_DPYNXL) | (IOREG(tms, REG020_DPYNXH) << 16); + UINT32 dinc = IOREG(tms, REG020_DINCL) | (IOREG(tms, REG020_DINCH) << 16); + dpynx = (dpynx & 0xffffffe0) | ((dpynx + dinc) & 0x1f); + if ((dpynx & 0x1f) == 0) + dpynx += dinc & 0xffffffe0; + IOREG(tms, REG020_DPYNXL) = dpynx; + IOREG(tms, REG020_DPYNXH) = dpynx >> 16; + } + } + + /* adjust for the next callback */ + vcount++; + if (vcount > vtotal) + vcount = 0; + + /* note that we add !master (0 or 1) as a attoseconds value; this makes no practical difference */ + /* but helps ensure that masters are updated first before slaves */ + tms->scantimer->adjust(tms->screen->time_until_pos(vcount) + attotime(0, !master), vcount); +} + + +void tms34010_get_display_params(device_t *cpu, tms34010_display_params *params) +{ + tms34010_state *tms = get_safe_token(cpu); + + params->enabled = ((SMART_IOREG(tms, DPYCTL) & 0x8000) != 0); + params->vcount = SMART_IOREG(tms, VCOUNT); + params->veblnk = SMART_IOREG(tms, VEBLNK); + params->vsblnk = SMART_IOREG(tms, VSBLNK); + params->heblnk = SMART_IOREG(tms, HEBLNK) * tms->config->pixperclock; + params->hsblnk = SMART_IOREG(tms, HSBLNK) * tms->config->pixperclock; + + /* 34010 gets its address from DPYADR and DPYTAP */ + if (!tms->is_34020) + { + UINT16 dpyadr = IOREG(tms, REG_DPYADR); + if (!(IOREG(tms, REG_DPYCTL) & 0x0400)) + dpyadr ^= 0xfffc; + params->rowaddr = dpyadr >> 4; + params->coladdr = ((dpyadr & 0x007c) << 4) | (IOREG(tms, REG_DPYTAP) & 0x3fff); + params->yoffset = (IOREG(tms, REG_DPYSTRT) - IOREG(tms, REG_DPYADR)) & 3; + } + + /* 34020 gets its address from DPYNX */ + else + { + params->rowaddr = IOREG(tms, REG020_DPYNXH); + params->coladdr = IOREG(tms, REG020_DPYNXL) & 0xffe0; + params->yoffset = 0; + if ((IOREG(tms, REG020_DINCL) & 0x1f) != 0) + params->yoffset = (IOREG(tms, REG020_DPYNXL) & 0x1f) / (IOREG(tms, REG020_DINCL) & 0x1f); + } +} + + +SCREEN_UPDATE_IND16( tms340x0_ind16 ) +{ + pen_t blackpen = get_black_pen(screen.machine()); + tms34010_display_params params; + tms34010_state *tms = NULL; + device_t *cpu; + int x; + + /* find the owning CPU */ + device_iterator iter(screen.machine().root_device()); + for (cpu = iter.first(); cpu != NULL; cpu = iter.next()) + { + device_type type = cpu->type(); + if (type == TMS34010 || type == TMS34020) + { + tms = get_safe_token(cpu); + if (tms->config != NULL && tms->config->scanline_callback_ind16 != NULL && tms->screen == &screen) + break; + tms = NULL; + } + } + if (tms == NULL) + fatalerror("Unable to locate matching CPU for screen '%s'\n", screen.tag()); + + /* get the display parameters for the screen */ + tms34010_get_display_params(tms->device, ¶ms); + + /* if the display is enabled, call the scanline callback */ + if (params.enabled) + { + /* call through to the callback */ + LOG((" Update: scan=%3d ROW=%04X COL=%04X\n", cliprect.min_y, params.rowaddr, params.coladdr)); + (*tms->config->scanline_callback_ind16)(screen, bitmap, cliprect.min_y, ¶ms); + } + + /* otherwise, just blank the current scanline */ + else + params.heblnk = params.hsblnk = cliprect.max_x + 1; + + /* blank out the blank regions */ + UINT16 *dest = &bitmap.pix16(cliprect.min_y); + for (x = cliprect.min_x; x < params.heblnk; x++) + dest[x] = blackpen; + for (x = params.hsblnk; x <= cliprect.max_x; x++) + dest[x] = blackpen; + return 0; + +} + +SCREEN_UPDATE_RGB32( tms340x0_rgb32 ) +{ + pen_t blackpen = get_black_pen(screen.machine()); + tms34010_display_params params; + tms34010_state *tms = NULL; + device_t *cpu; + int x; + + /* find the owning CPU */ + device_iterator iter(screen.machine().root_device()); + for (cpu = iter.first(); cpu != NULL; cpu = iter.next()) + { + device_type type = cpu->type(); + if (type == TMS34010 || type == TMS34020) + { + tms = get_safe_token(cpu); + if (tms->config != NULL && tms->config->scanline_callback_rgb32 != NULL && tms->screen == &screen) + break; + tms = NULL; + } + } + if (tms == NULL) + fatalerror("Unable to locate matching CPU for screen '%s'\n", screen.tag()); + + /* get the display parameters for the screen */ + tms34010_get_display_params(tms->device, ¶ms); + + /* if the display is enabled, call the scanline callback */ + if (params.enabled) + { + /* call through to the callback */ + LOG((" Update: scan=%3d ROW=%04X COL=%04X\n", cliprect.min_y, params.rowaddr, params.coladdr)); + (*tms->config->scanline_callback_rgb32)(screen, bitmap, cliprect.min_y, ¶ms); + } + + /* otherwise, just blank the current scanline */ + else + params.heblnk = params.hsblnk = cliprect.max_x + 1; + + /* blank out the blank regions */ + UINT32 *dest = &bitmap.pix32(cliprect.min_y); + for (x = cliprect.min_x; x < params.heblnk; x++) + dest[x] = blackpen; + for (x = params.hsblnk; x <= cliprect.max_x; x++) + dest[x] = blackpen; + return 0; +} + + +/*************************************************************************** + I/O REGISTER WRITES +***************************************************************************/ + +static const char *const ioreg_name[] = +{ + "HESYNC", "HEBLNK", "HSBLNK", "HTOTAL", + "VESYNC", "VEBLNK", "VSBLNK", "VTOTAL", + "DPYCTL", "DPYSTART", "DPYINT", "CONTROL", + "HSTDATA", "HSTADRL", "HSTADRH", "HSTCTLL", + + "HSTCTLH", "INTENB", "INTPEND", "CONVSP", + "CONVDP", "PSIZE", "PMASK", "RESERVED", + "RESERVED", "RESERVED", "RESERVED", "DPYTAP", + "HCOUNT", "VCOUNT", "DPYADR", "REFCNT" +}; + +WRITE16_HANDLER( tms34010_io_register_w ) +{ + tms34010_state *tms = get_safe_token(&space->device()); + int oldreg, newreg; + + /* Set register */ + oldreg = IOREG(tms, offset); + IOREG(tms, offset) = data; + + switch (offset) + { + case REG_CONTROL: + set_raster_op(tms); + set_pixel_function(tms); + break; + + case REG_PSIZE: + set_pixel_function(tms); + + switch (data) + { + default: + case 0x01: tms->pixelshift = 0; break; + case 0x02: tms->pixelshift = 1; break; + case 0x04: tms->pixelshift = 2; break; + case 0x08: tms->pixelshift = 3; break; + case 0x10: tms->pixelshift = 4; break; + } + break; + + case REG_PMASK: + if (data) logerror("Plane masking not supported. PC=%08X\n", cpu_get_pc(&space->device())); + break; + + case REG_DPYCTL: + set_pixel_function(tms); + break; + + case REG_HSTCTLH: + /* if the CPU is halting itself, stop execution right away */ + if ((data & 0x8000) && !tms->external_host_access) + tms->icount = 0; + device_set_input_line(tms->device, INPUT_LINE_HALT, (data & 0x8000) ? ASSERT_LINE : CLEAR_LINE); + + /* NMI issued? */ + if (data & 0x0100) + tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), 0, tms); + break; + + case REG_HSTCTLL: + /* the TMS34010 can change MSGOUT, can set INTOUT, and can clear INTIN */ + if (!tms->external_host_access) + { + newreg = (oldreg & 0xff8f) | (data & 0x0070); + newreg |= data & 0x0080; + newreg &= data | ~0x0008; + } + + /* the host can change MSGIN, can set INTIN, and can clear INTOUT */ + else + { + newreg = (oldreg & 0xfff8) | (data & 0x0007); + newreg &= data | ~0x0080; + newreg |= data & 0x0008; + } + IOREG(tms, offset) = newreg; + + /* the TMS34010 can set output interrupt? */ + if (!(oldreg & 0x0080) && (newreg & 0x0080)) + { + if (tms->config->output_int) + (*tms->config->output_int)(&space->device(), 1); + } + else if ((oldreg & 0x0080) && !(newreg & 0x0080)) + { + if (tms->config->output_int) + (*tms->config->output_int)(&space->device(), 0); + } + + /* input interrupt? (should really be state-based, but the functions don't exist!) */ + if (!(oldreg & 0x0008) && (newreg & 0x0008)) + tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), TMS34010_HI, tms); + else if ((oldreg & 0x0008) && !(newreg & 0x0008)) + IOREG(tms, REG_INTPEND) &= ~TMS34010_HI; + break; + + case REG_CONVSP: + tms->convsp = 1 << (~data & 0x1f); + break; + + case REG_CONVDP: + tms->convdp = 1 << (~data & 0x1f); + break; + + case REG_INTENB: + check_interrupt(tms); + break; + + case REG_INTPEND: + /* X1P, X2P and HIP are read-only */ + /* WVP and DIP can only have 0's written to them */ + IOREG(tms, REG_INTPEND) = oldreg; + if (!(data & TMS34010_WV)) + IOREG(tms, REG_INTPEND) &= ~TMS34010_WV; + if (!(data & TMS34010_DI)) + IOREG(tms, REG_INTPEND) &= ~TMS34010_DI; + break; + + case REG_HEBLNK: + case REG_HSBLNK: + if (oldreg != data) + tms->hblank_stable = 0; + break; + } + +// if (LOG_CONTROL_REGS) +// logerror("%s: %s = %04X (%d)\n", tms->device->machine().describe_context(), ioreg_name[offset], IOREG(tms, offset), tms->screen.vpos()); +} + + +static const char *const ioreg020_name[] = +{ + "VESYNC", "HESYNC", "VEBLNK", "HEBLNK", + "VSBLNK", "HSBLNK", "VTOTAL", "HTOTAL", + "DPYCTL", "DPYSTRT", "DPYINT", "CONTROL", + "HSTDATA", "HSTADRL", "HSTADRH", "HSTCTLL", + + "HSTCTLH", "INTENB", "INTPEND", "CONVSP", + "CONVDP", "PSIZE", "PMASKL", "PMASKH", + "CONVMP", "CONTROL2", "CONFIG", "DPYTAP", + "VCOUNT", "HCOUNT", "DPYADR", "REFADR", + + "DPYSTL", "DPYSTH", "DPYNXL", "DPYNXH", + "DINCL", "DINCH", "RES0", "HESERR", + "RES1", "RES2", "RES3", "RES4", + "SCOUNT", "BSFLTST", "DPYMSK", "RES5", + + "SETVCNT", "SETHCNT", "BSFLTDL", "BSFLTDH", + "RES6", "RES7", "RES8", "RES9", + "IHOST1L", "IHOST1H", "IHOST2L", "IHOST2H", + "IHOST3L", "IHOST3H", "IHOST4L", "IHOST4H" +}; + +WRITE16_HANDLER( tms34020_io_register_w ) +{ + tms34010_state *tms = get_safe_token(&space->device()); + int oldreg, newreg; + + /* Set register */ + oldreg = IOREG(tms, offset); + IOREG(tms, offset) = data; + +// if (LOG_CONTROL_REGS) +// logerror("%s: %s = %04X (%d)\n", device->machine().describe_context(), ioreg020_name[offset], IOREG(tms, offset), tms->screen.vpos()); + + switch (offset) + { + case REG020_CONTROL: + case REG020_CONTROL2: + IOREG(tms, REG020_CONTROL) = data; + IOREG(tms, REG020_CONTROL2) = data; + set_raster_op(tms); + set_pixel_function(tms); + break; + + case REG020_PSIZE: + set_pixel_function(tms); + + switch (data) + { + default: + case 0x01: tms->pixelshift = 0; break; + case 0x02: tms->pixelshift = 1; break; + case 0x04: tms->pixelshift = 2; break; + case 0x08: tms->pixelshift = 3; break; + case 0x10: tms->pixelshift = 4; break; + case 0x20: tms->pixelshift = 5; break; + } + break; + + case REG020_PMASKL: + case REG020_PMASKH: + if (data) logerror("Plane masking not supported. PC=%08X\n", cpu_get_pc(&space->device())); + break; + + case REG020_DPYCTL: + set_pixel_function(tms); + break; + + case REG020_HSTCTLH: + /* if the CPU is halting itself, stop execution right away */ + if ((data & 0x8000) && !tms->external_host_access) + tms->icount = 0; + device_set_input_line(tms->device, INPUT_LINE_HALT, (data & 0x8000) ? ASSERT_LINE : CLEAR_LINE); + + /* NMI issued? */ + if (data & 0x0100) + tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), 0, tms); + break; + + case REG020_HSTCTLL: + /* the TMS34010 can change MSGOUT, can set INTOUT, and can clear INTIN */ + if (!tms->external_host_access) + { + newreg = (oldreg & 0xff8f) | (data & 0x0070); + newreg |= data & 0x0080; + newreg &= data | ~0x0008; + } + + /* the host can change MSGIN, can set INTIN, and can clear INTOUT */ + else + { + newreg = (oldreg & 0xfff8) | (data & 0x0007); + newreg &= data | ~0x0080; + newreg |= data & 0x0008; + } + IOREG(tms, offset) = newreg; + + /* the TMS34010 can set output interrupt? */ + if (!(oldreg & 0x0080) && (newreg & 0x0080)) + { + if (tms->config->output_int) + (*tms->config->output_int)(&space->device(), 1); + } + else if ((oldreg & 0x0080) && !(newreg & 0x0080)) + { + if (tms->config->output_int) + (*tms->config->output_int)(&space->device(), 0); + } + + /* input interrupt? (should really be state-based, but the functions don't exist!) */ + if (!(oldreg & 0x0008) && (newreg & 0x0008)) + tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), TMS34010_HI, tms); + else if ((oldreg & 0x0008) && !(newreg & 0x0008)) + IOREG(tms, REG020_INTPEND) &= ~TMS34010_HI; + break; + + case REG020_INTENB: + check_interrupt(tms); + break; + + case REG020_INTPEND: + /* X1P, X2P and HIP are read-only */ + /* WVP and DIP can only have 0's written to them */ + IOREG(tms, REG020_INTPEND) = oldreg; + if (!(data & TMS34010_WV)) + IOREG(tms, REG020_INTPEND) &= ~TMS34010_WV; + if (!(data & TMS34010_DI)) + IOREG(tms, REG020_INTPEND) &= ~TMS34010_DI; + break; + + case REG020_CONVSP: + if (data & 0x001f) + { + if (data & 0x1f00) + tms->convsp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f)); + else + tms->convsp = 1 << (~data & 0x1f); + } + else + tms->convsp = data; + break; + + case REG020_CONVDP: + if (data & 0x001f) + { + if (data & 0x1f00) + tms->convdp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f)); + else + tms->convdp = 1 << (~data & 0x1f); + } + else + tms->convdp = data; + break; + + case REG020_CONVMP: + if (data & 0x001f) + { + if (data & 0x1f00) + tms->convmp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f)); + else + tms->convmp = 1 << (~data & 0x1f); + } + else + tms->convmp = data; + break; + + case REG020_DPYSTRT: + case REG020_DPYADR: + case REG020_DPYTAP: + break; + + case REG020_HEBLNK: + case REG020_HSBLNK: + if (oldreg != data) + tms->hblank_stable = 0; + break; + } +} + + + +/*************************************************************************** + I/O REGISTER READS +***************************************************************************/ + +READ16_HANDLER( tms34010_io_register_r ) +{ + tms34010_state *tms = get_safe_token(&space->device()); + int result, total; + +// if (LOG_CONTROL_REGS) +// logerror("%s: read %s\n", device->machine().describe_context(), ioreg_name[offset]); + + switch (offset) + { + case REG_HCOUNT: + /* scale the horizontal position from screen width to HTOTAL */ + result = tms->screen->hpos(); + total = IOREG(tms, REG_HTOTAL) + 1; + result = result * total / tms->screen->width(); + + /* offset by the HBLANK end */ + result += IOREG(tms, REG_HEBLNK); + + /* wrap around */ + if (result > total) + result -= total; + return result; + + case REG_REFCNT: + return (tms->device->total_cycles() / 16) & 0xfffc; + + case REG_INTPEND: + result = IOREG(tms, offset); + + /* Cool Pool loops in mainline code on the appearance of the DI, even though they */ + /* have an IRQ handler. For this reason, we return it signalled a bit early in order */ + /* to make it past these loops. */ + if (SMART_IOREG(tms, VCOUNT) + 1 == SMART_IOREG(tms, DPYINT) && + tms->scantimer->remaining() < attotime::from_hz(40000000/8/3)) + result |= TMS34010_DI; + return result; + } + + return IOREG(tms, offset); +} + + +READ16_HANDLER( tms34020_io_register_r ) +{ + tms34010_state *tms = get_safe_token(&space->device()); + int result, total; + +// if (LOG_CONTROL_REGS) +// logerror("%s: read %s\n", device->machine().describe_context(), ioreg_name[offset]); + + switch (offset) + { + case REG020_HCOUNT: + /* scale the horizontal position from screen width to HTOTAL */ + result = tms->screen->hpos(); + total = IOREG(tms, REG020_HTOTAL) + 1; + result = result * total / tms->screen->width(); + + /* offset by the HBLANK end */ + result += IOREG(tms, REG020_HEBLNK); + + /* wrap around */ + if (result > total) + result -= total; + return result; + + case REG020_REFADR: + { + int refreshrate = (IOREG(tms, REG020_CONFIG) >> 8) & 7; + if (refreshrate < 6) + return (tms->device->total_cycles() / refreshrate) & 0xffff; + break; + } + } + + return IOREG(tms, offset); +} + + + +/*************************************************************************** + SAVE STATE +***************************************************************************/ + +static void tms34010_state_postload(tms34010_state *tms) +{ + set_raster_op(tms); + set_pixel_function(tms); +} + + +/*************************************************************************** + HOST INTERFACE WRITES +***************************************************************************/ + +void tms34010_host_w(device_t *cpu, int reg, int data) +{ + address_space *space; + tms34010_state *tms = get_safe_token(cpu); + unsigned int addr; + + switch (reg) + { + /* upper 16 bits of the address */ + case TMS34010_HOST_ADDRESS_H: + IOREG(tms, REG_HSTADRH) = data; + break; + + /* lower 16 bits of the address */ + case TMS34010_HOST_ADDRESS_L: + IOREG(tms, REG_HSTADRL) = data; + break; + + /* actual data */ + case TMS34010_HOST_DATA: + + /* write to the address */ + addr = (IOREG(tms, REG_HSTADRH) << 16) | IOREG(tms, REG_HSTADRL); + TMS34010_WRMEM_WORD(tms, TOBYTE(addr & 0xfffffff0), data); + + /* optional postincrement */ + if (IOREG(tms, REG_HSTCTLH) & 0x0800) + { + addr += 0x10; + IOREG(tms, REG_HSTADRH) = addr >> 16; + IOREG(tms, REG_HSTADRL) = (UINT16)addr; + } + break; + + /* control register */ + case TMS34010_HOST_CONTROL: + tms->external_host_access = TRUE; + space = tms->device->space(AS_PROGRAM); + tms34010_io_register_w(space, REG_HSTCTLH, data & 0xff00, 0xffff); + tms34010_io_register_w(space, REG_HSTCTLL, data & 0x00ff, 0xffff); + tms->external_host_access = FALSE; + break; + + /* error case */ + default: + logerror("tms34010_host_control_w called on invalid register %d\n", reg); + break; + } +} + + + +/*************************************************************************** + HOST INTERFACE READS +***************************************************************************/ + +int tms34010_host_r(device_t *cpu, int reg) +{ + tms34010_state *tms = get_safe_token(cpu); + unsigned int addr; + int result = 0; + + /* swap to the target cpu */ + + switch (reg) + { + /* upper 16 bits of the address */ + case TMS34010_HOST_ADDRESS_H: + result = IOREG(tms, REG_HSTADRH); + break; + + /* lower 16 bits of the address */ + case TMS34010_HOST_ADDRESS_L: + result = IOREG(tms, REG_HSTADRL); + break; + + /* actual data */ + case TMS34010_HOST_DATA: + + /* read from the address */ + addr = (IOREG(tms, REG_HSTADRH) << 16) | IOREG(tms, REG_HSTADRL); + result = TMS34010_RDMEM_WORD(tms, TOBYTE(addr & 0xfffffff0)); + + /* optional postincrement (it says preincrement, but data is preloaded, so it + is effectively a postincrement */ + if (IOREG(tms, REG_HSTCTLH) & 0x1000) + { + addr += 0x10; + IOREG(tms, REG_HSTADRH) = addr >> 16; + IOREG(tms, REG_HSTADRL) = (UINT16)addr; + } + break; + + /* control register */ + case TMS34010_HOST_CONTROL: + result = (IOREG(tms, REG_HSTCTLH) & 0xff00) | (IOREG(tms, REG_HSTCTLL) & 0x00ff); + break; + + /* error case */ + default: + logerror("tms34010_host_control_r called on invalid register %d\n", reg); + break; + } + + return result; +} + + + +/************************************************************************** + * Generic set_info + **************************************************************************/ + +static CPU_SET_INFO( tms34010 ) +{ + tms34010_state *tms = get_safe_token(device); + + switch (state) + { + /* --- the following bits of info are set as 64-bit signed integers --- */ + case CPUINFO_INT_INPUT_STATE + 0: set_irq_line(tms, 0, info->i); break; + case CPUINFO_INT_INPUT_STATE + 1: set_irq_line(tms, 1, info->i); break; + } +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +CPU_EXPORT_STRING( tms34010 ) +{ + tms34010_state *tms = get_safe_token(device); + + switch (entry.index()) + { + case STATE_GENFLAGS: + string.printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", + tms->st & 0x80000000 ? 'N':'.', + tms->st & 0x40000000 ? 'C':'.', + tms->st & 0x20000000 ? 'Z':'.', + tms->st & 0x10000000 ? 'V':'.', + tms->st & 0x02000000 ? 'P':'.', + tms->st & 0x00200000 ? 'I':'.', + tms->st & 0x00000800 ? 'E':'.', + tms->st & 0x00000400 ? 'F':'.', + tms->st & 0x00000200 ? 'F':'.', + tms->st & 0x00000100 ? 'F':'.', + tms->st & 0x00000080 ? 'F':'.', + tms->st & 0x00000040 ? 'F':'.', + tms->st & 0x00000020 ? 'E':'.', + tms->st & 0x00000010 ? 'F':'.', + tms->st & 0x00000008 ? 'F':'.', + tms->st & 0x00000004 ? 'F':'.', + tms->st & 0x00000002 ? 'F':'.', + tms->st & 0x00000001 ? 'F':'.'); + break; + } +} + +CPU_GET_INFO( tms34010 ) +{ + tms34010_state *tms = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL; + + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(tms34010_state); break; + case CPUINFO_INT_INPUT_LINES: info->i = 2; break; + case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break; + case DEVINFO_INT_ENDIANNESS: info->i = ENDIANNESS_LITTLE; break; + case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break; + case CPUINFO_INT_CLOCK_DIVIDER: info->i = 8; break; + case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 2; break; + case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 10; break; + case CPUINFO_INT_MIN_CYCLES: info->i = 1; break; + case CPUINFO_INT_MAX_CYCLES: info->i = 10000; break; + + case DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 16; break; + case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: info->i = 32; break; + case DEVINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: info->i = 3; break; + + case CPUINFO_INT_INPUT_STATE + 0: info->i = (IOREG(tms, REG_INTPEND) & TMS34010_INT1) ? ASSERT_LINE : CLEAR_LINE; break; + case CPUINFO_INT_INPUT_STATE + 1: info->i = (IOREG(tms, REG_INTPEND) & TMS34010_INT2) ? ASSERT_LINE : CLEAR_LINE; break; + + /* --- the following bits of info are returned as pointers to functions --- */ + case CPUINFO_FCT_SET_INFO: info->setinfo = CPU_SET_INFO_NAME(tms34010); break; + case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(tms34010); break; + case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(tms34010); break; + case CPUINFO_FCT_EXIT: info->exit = CPU_EXIT_NAME(tms34010); break; + case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(tms34010); break; + case CPUINFO_FCT_BURN: info->burn = NULL; break; + case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(tms34010); break; + case CPUINFO_FCT_EXPORT_STRING: info->export_string = CPU_EXPORT_STRING_NAME(tms34010); break; + + /* --- the following bits of info are returned as pointers --- */ + case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &tms->icount; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "TMS34010"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Texas Instruments 340x0"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Alex Pasadyn/Zsolt Vasvari\nParts based on code by Aaron Giles"); break; + } +} + + +/************************************************************************** + * CPU-specific set_info + **************************************************************************/ + +CPU_GET_INFO( tms34020 ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case CPUINFO_INT_CLOCK_DIVIDER: info->i = 4; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(tms34020); break; + case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(tms34020); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "TMS34020"); break; + + default: CPU_GET_INFO_CALL(tms34010); break; + } +} + +DEFINE_LEGACY_CPU_DEVICE(TMS34010, tms34010); +DEFINE_LEGACY_CPU_DEVICE(TMS34020, tms34020); |