// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** tms32031.c TMS32031/2 emulator ***************************************************************************/ #include "emu.h" #include "tms32031.h" #include "dis32031.h" #include "debugger.h" //************************************************************************** // CONSTANTS //************************************************************************** // indexes into the register file enum { TMR_R0 = 0, TMR_R1, TMR_R2, TMR_R3, TMR_R4, TMR_R5, TMR_R6, TMR_R7, TMR_AR0, TMR_AR1, TMR_AR2, TMR_AR3, TMR_AR4, TMR_AR5, TMR_AR6, TMR_AR7, TMR_DP, TMR_IR0, TMR_IR1, TMR_BK, TMR_SP, TMR_ST, TMR_IE, TMR_IF, TMR_IOF, TMR_RS, TMR_RE, TMR_RC, TMR_R8, // 3204x only TMR_R9, // 3204x only TMR_R10, // 3204x only TMR_R11, // 3204x only TMR_TEMP1, // used by the interpreter TMR_TEMP2, // used by the interpreter TMR_TEMP3 // used by the interpreter }; // flags const int CFLAG = 0x0001; const int VFLAG = 0x0002; const int ZFLAG = 0x0004; const int NFLAG = 0x0008; const int UFFLAG = 0x0010; const int LVFLAG = 0x0020; const int LUFFLAG = 0x0040; const int OVMFLAG = 0x0080; const int RMFLAG = 0x0100; //const int CFFLAG = 0x0400; //const int CEFLAG = 0x0800; //const int CCFLAG = 0x1000; const int GIEFLAG = 0x2000; //************************************************************************** // MACROS //************************************************************************** #define IREG(rnum) (m_r[rnum].i32[0]) //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(TMS32030, tms32030_device, "tms32030", "Texas Instruments TMS32030") DEFINE_DEVICE_TYPE(TMS32031, tms32031_device, "tms32031", "Texas Instruments TMS32031") DEFINE_DEVICE_TYPE(TMS32032, tms32032_device, "tms32032", "Texas Instruments TMS32032") // memory map common to all 'C30 devices // TODO: expand to cover all the standard internal peripherals void tms3203x_device::common_3203x(address_map &map) { map(0x808064, 0x808064).rw(FUNC(tms3203x_device::primary_bus_control_r), FUNC(tms3203x_device::primary_bus_control_w)); } // internal memory maps void tms32030_device::internal_32030(address_map &map) { common_3203x(map); map(0x809800, 0x809fff).ram(); } void tms32031_device::internal_32031(address_map &map) { common_3203x(map); map(0x809800, 0x809fff).ram(); } void tms32032_device::internal_32032(address_map &map) { common_3203x(map); map(0x87fe00, 0x87ffff).ram(); } // ROM definitions for the internal boot loader programs // (Using assembled versions until the code ROMs are extracted from both DSPs) ROM_START( tms32030 ) ROM_REGION(0x4000, "tms32030", 0) ROM_LOAD( "c30boot.bin", 0x0000, 0x4000, BAD_DUMP CRC(bddc2763) SHA1(96b2170ecee5bec5abaa1741bb2d3b6096ecc262)) ROM_END ROM_START( tms32031 ) ROM_REGION(0x4000, "tms32031", 0) ROM_LOAD( "c31boot.bin", 0x0000, 0x4000, BAD_DUMP CRC(bddc2763) SHA1(96b2170ecee5bec5abaa1741bb2d3b6096ecc262) ) // Assembled from c31boot.asm (02-07-92) ROM_END ROM_START( tms32032 ) ROM_REGION(0x4000, "tms32032", 0) ROM_LOAD( "c32boot.bin", 0x0000, 0x4000, BAD_DUMP CRC(ecf84729) SHA1(4d32ead450f921f563514b061ea561a222283616) ) // Assembled from c32boot.asm (03-04-96) ROM_END //************************************************************************** // TMSREG REGISTER //************************************************************************** //------------------------------------------------- // as_float - interpret the contents of a tmsreg // as a DSP-encoded floating-point value, and // extract a 32-bit IEEE float from it //------------------------------------------------- float tms3203x_device::tmsreg::as_float() const { int_double id; // map 0 to 0 if (mantissa() == 0 && exponent() == -128) return 0; // handle positive numbers else if (mantissa() >= 0) { int exp = (exponent() + 127) << 23; id.i[0] = exp + (mantissa() >> 8); } // handle negative numbers else { int exp = (exponent() + 127) << 23; int32_t man = -mantissa(); id.i[0] = 0x80000000 + exp + ((man >> 8) & 0x00ffffff); } // return the converted float return id.f[0]; } //------------------------------------------------- // as_double - interpret the contents of a tmsreg // as a DSP-encoded floating-point value, and // extract a 64-bit IEEE double from it //------------------------------------------------- double tms3203x_device::tmsreg::as_double() const { int_double id; // map 0 to 0 if (mantissa() == 0 && exponent() == -128) return 0; // handle positive numbers else if (mantissa() >= 0) { int exp = (exponent() + 1023) << 20; id.i[BYTE_XOR_BE(0)] = exp + (mantissa() >> 11); id.i[BYTE_XOR_BE(1)] = (mantissa() << 21) & 0xffe00000; } // handle negative numbers else { int exp = (exponent() + 1023) << 20; int32_t man = -mantissa(); id.i[BYTE_XOR_BE(0)] = 0x80000000 + exp + ((man >> 11) & 0x001fffff); id.i[BYTE_XOR_BE(1)] = (man << 21) & 0xffe00000; } // return the converted double return id.d; } //------------------------------------------------- // from_double - import a 64-bit IEEE double into // the DSP's internal floating point format //------------------------------------------------- void tms3203x_device::tmsreg::from_double(double val) { // extract mantissa and exponent from the IEEE input int_double id; id.d = val; int32_t mantissa = ((id.i[BYTE_XOR_BE(0)] & 0x000fffff) << 11) | ((id.i[BYTE_XOR_BE(1)] & 0xffe00000) >> 21); int32_t exponent = ((id.i[BYTE_XOR_BE(0)] & 0x7ff00000) >> 20) - 1023; // if we're too small, map to 0 if (exponent < -128) { set_mantissa(0); set_exponent(-128); } // if we're too large, map to the maximum value else if (exponent > 127) { if ((int32_t)id.i[BYTE_XOR_BE(0)] >= 0) set_mantissa(0x7fffffff); else set_mantissa(0x80000001); set_exponent(127); } // if we're positive, map directly else if ((int32_t)id.i[BYTE_XOR_BE(0)] >= 0) { set_mantissa(mantissa); set_exponent(exponent); } // if we're negative with a non-zero mantissa, remove the leading sign bit else if (mantissa != 0) { set_mantissa(0x80000000 | -mantissa); set_exponent(exponent); } // if we're negative with a zero mantissa, normalize else { set_mantissa(0x80000000); set_exponent(exponent - 1); } } //************************************************************************** // DEVICE INTERFACE //************************************************************************** //------------------------------------------------- // tms3203x_device - constructor //------------------------------------------------- tms3203x_device::tms3203x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t chiptype, address_map_constructor internal_map) : cpu_device(mconfig, type, tag, owner, clock), m_program_config("program", ENDIANNESS_LITTLE, 32, 24, -2, internal_map), m_chip_type(chiptype), m_pc(0), m_bkmask(0), m_primary_bus_control(0), m_irq_state(0), m_delayed(false), m_irq_pending(false), m_is_idling(false), m_icount(0), m_program(nullptr), m_cache(nullptr), m_mcbl_mode(false), m_xf0_cb(*this), m_xf1_cb(*this), m_iack_cb(*this), m_holda_cb(*this) { // initialize remaining state memset(&m_r, 0, sizeof(m_r)); // set our instruction counter set_icountptr(m_icount); #if (TMS_3203X_LOG_OPCODE_USAGE) memset(m_hits, 0, sizeof(m_hits)); #endif } tms32030_device::tms32030_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms3203x_device(mconfig, TMS32030, tag, owner, clock, CHIP_TYPE_TMS32030, address_map_constructor(FUNC(tms32030_device::internal_32030), this)) { } tms32031_device::tms32031_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms3203x_device(mconfig, TMS32031, tag, owner, clock, CHIP_TYPE_TMS32031, address_map_constructor(FUNC(tms32031_device::internal_32031), this)) { } tms32032_device::tms32032_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : tms3203x_device(mconfig, TMS32032, tag, owner, clock, CHIP_TYPE_TMS32032, address_map_constructor(FUNC(tms32032_device::internal_32032), this)) { } //------------------------------------------------- // ~tms3203x_device - destructor //------------------------------------------------- tms3203x_device::~tms3203x_device() { #if (TMS_3203X_LOG_OPCODE_USAGE) for (int i = 0; i < ARRAY_LENGTH(m_hits); i++) if (m_hits[i] != 0) printf("%10d - %03X.%X\n", m_hits[i], i / 4, i % 4); #endif } //------------------------------------------------- // rom_region - return a pointer to the device's // internal ROM region //------------------------------------------------- const tiny_rom_entry *tms3203x_device::device_rom_region() const { switch (m_chip_type) { default: case CHIP_TYPE_TMS32030: return ROM_NAME( tms32030 ); case CHIP_TYPE_TMS32031: return ROM_NAME( tms32031 ); case CHIP_TYPE_TMS32032: return ROM_NAME( tms32032 ); } } //------------------------------------------------- // ROPCODE - fetch an opcode //------------------------------------------------- inline uint32_t tms3203x_device::ROPCODE(offs_t pc) { return m_cache->read_dword(pc); } //------------------------------------------------- // RMEM - read memory //------------------------------------------------- inline uint32_t tms3203x_device::RMEM(offs_t addr) { return m_program->read_dword(addr); } //------------------------------------------------- // WMEM - write memory //------------------------------------------------- inline void tms3203x_device::WMEM(offs_t addr, uint32_t data) { m_program->write_dword(addr, data); } //------------------------------------------------- // device_start - start up the device //------------------------------------------------- void tms3203x_device::device_start() { // find address spaces m_program = &space(AS_PROGRAM); m_cache = m_program->cache<2, -2, ENDIANNESS_LITTLE>(); // resolve devcb handlers m_xf0_cb.resolve_safe(); m_xf1_cb.resolve_safe(); m_iack_cb.resolve_safe(); m_holda_cb.resolve_safe(); // set up the internal boot loader ROM if (m_mcbl_mode) m_program->install_rom(0x000000, 0x000fff, memregion(shortname())->base()); // save state save_item(NAME(m_pc)); for (int regnum = 0; regnum < 36; regnum++) save_item(NAME(m_r[regnum].i32), regnum); save_item(NAME(m_bkmask)); save_item(NAME(m_primary_bus_control)); save_item(NAME(m_irq_state)); save_item(NAME(m_delayed)); save_item(NAME(m_irq_pending)); save_item(NAME(m_is_idling)); save_item(NAME(m_mcbl_mode)); save_item(NAME(m_hold_state)); // register our state for the debugger state_add(TMS3203X_PC, "PC", m_pc); state_add(STATE_GENPC, "GENPC", m_pc).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", m_r[TMR_ST].i32[0]).mask(0xff).noshow().formatstr("%8s"); state_add(TMS3203X_R0, "R0", m_r[TMR_R0].i32[0]); state_add(TMS3203X_R1, "R1", m_r[TMR_R1].i32[0]); state_add(TMS3203X_R2, "R2", m_r[TMR_R2].i32[0]); state_add(TMS3203X_R3, "R3", m_r[TMR_R3].i32[0]); state_add(TMS3203X_R4, "R4", m_r[TMR_R4].i32[0]); state_add(TMS3203X_R5, "R5", m_r[TMR_R5].i32[0]); state_add(TMS3203X_R6, "R6", m_r[TMR_R6].i32[0]); state_add(TMS3203X_R7, "R7", m_r[TMR_R7].i32[0]); state_add(TMS3203X_R0F, "R0F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R1F, "R1F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R2F, "R2F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R3F, "R3F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R4F, "R4F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R5F, "R5F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R6F, "R6F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_R7F, "R7F", m_iotemp).callimport().callexport().formatstr("%12s"); state_add(TMS3203X_AR0, "AR0", m_r[TMR_AR0].i32[0]); state_add(TMS3203X_AR1, "AR1", m_r[TMR_AR1].i32[0]); state_add(TMS3203X_AR2, "AR2", m_r[TMR_AR2].i32[0]); state_add(TMS3203X_AR3, "AR3", m_r[TMR_AR3].i32[0]); state_add(TMS3203X_AR4, "AR4", m_r[TMR_AR4].i32[0]); state_add(TMS3203X_AR5, "AR5", m_r[TMR_AR5].i32[0]); state_add(TMS3203X_AR6, "AR6", m_r[TMR_AR6].i32[0]); state_add(TMS3203X_AR7, "AR7", m_r[TMR_AR7].i32[0]); state_add(TMS3203X_DP, "DP", m_r[TMR_DP].i32[0]).mask(0xff); state_add(TMS3203X_IR0, "IR0", m_r[TMR_IR0].i32[0]); state_add(TMS3203X_IR1, "IR1", m_r[TMR_IR1].i32[0]); state_add(TMS3203X_BK, "BK", m_r[TMR_BK].i32[0]); state_add(TMS3203X_SP, "SP", m_r[TMR_SP].i32[0]); state_add(TMS3203X_ST, "ST", m_r[TMR_ST].i32[0]); state_add(TMS3203X_IE, "IE", m_r[TMR_IE].i32[0]); state_add(TMS3203X_IF, "IF", m_r[TMR_IF].i32[0]); state_add(TMS3203X_IOF, "IOF", m_r[TMR_IOF].i32[0]); state_add(TMS3203X_RS, "RS", m_r[TMR_RS].i32[0]); state_add(TMS3203X_RE, "RE", m_r[TMR_RE].i32[0]); state_add(TMS3203X_RC, "RC", m_r[TMR_RC].i32[0]); } //------------------------------------------------- // device_reset - reset the device //------------------------------------------------- void tms3203x_device::device_reset() { m_pc = RMEM(0); // reset some registers IREG(TMR_IE) = 0; IREG(TMR_IF) = 0; IREG(TMR_ST) = 0; IREG(TMR_IOF) = 0; // update IF with the external interrupt state (required for boot loader operation) IREG(TMR_IF) |= m_irq_state & 0x0f; // reset peripheral registers m_primary_bus_control = 0x000010f8; // reset internal stuff m_delayed = m_irq_pending = m_is_idling = false; } //------------------------------------------------- // memory_space_config - return the configuration // of the specified address space, or nullptr if // the space doesn't exist //------------------------------------------------- device_memory_interface::space_config_vector tms3203x_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config) }; } //------------------------------------------------- // state_import - import state into the device, // after it has been set //------------------------------------------------- void tms3203x_device::state_import(const device_state_entry &entry) { switch (entry.index()) { case TMS3203X_R0F: case TMS3203X_R1F: case TMS3203X_R2F: case TMS3203X_R3F: case TMS3203X_R4F: case TMS3203X_R5F: case TMS3203X_R6F: case TMS3203X_R7F: m_r[TMR_R0 + (entry.index() - TMS3203X_R0F)].from_double(*(float *)&m_iotemp); break; default: fatalerror("CPU_IMPORT_STATE(tms3203x) called for unexpected value\n"); } } //------------------------------------------------- // state_export - export state into the device, // before returning it to the caller //------------------------------------------------- void tms3203x_device::state_export(const device_state_entry &entry) { switch (entry.index()) { case TMS3203X_R0F: case TMS3203X_R1F: case TMS3203X_R2F: case TMS3203X_R3F: case TMS3203X_R4F: case TMS3203X_R5F: case TMS3203X_R6F: case TMS3203X_R7F: *(float *)&m_iotemp = m_r[TMR_R0 + (entry.index() - TMS3203X_R0F)].as_float(); break; default: fatalerror("CPU_IMPORT_STATE(tms3203x) called for unexpected value\n"); } } //------------------------------------------------- // state_string_export - export state as a string // for the debugger //------------------------------------------------- void tms3203x_device::state_string_export(const device_state_entry &entry, std::string &str) const { switch (entry.index()) { case TMS3203X_R0F: case TMS3203X_R1F: case TMS3203X_R2F: case TMS3203X_R3F: case TMS3203X_R4F: case TMS3203X_R5F: case TMS3203X_R6F: case TMS3203X_R7F: str = string_format("%12g", m_r[TMR_R0 + (entry.index() - TMS3203X_R0F)].as_double()); break; case STATE_GENFLAGS: uint32_t temp = m_r[TMR_ST].i32[0]; str = string_format("%c%c%c%c%c%c%c%c", (temp & 0x80) ? 'O':'.', (temp & 0x40) ? 'U':'.', (temp & 0x20) ? 'V':'.', (temp & 0x10) ? 'u':'.', (temp & 0x08) ? 'n':'.', (temp & 0x04) ? 'z':'.', (temp & 0x02) ? 'v':'.', (temp & 0x01) ? 'c':'.'); break; } } //------------------------------------------------- // disassemble - call the disassembly // helper function //------------------------------------------------- std::unique_ptr tms3203x_device::create_disassembler() { return std::make_unique(); } //************************************************************************** // PUBLIC INTERFACES //************************************************************************** //------------------------------------------------- // fp_to_float - convert a 32-bit value from DSP // floating-point format a 32-bit IEEE float //------------------------------------------------- float tms3203x_device::fp_to_float(uint32_t floatdata) { tmsreg gen(floatdata << 8, (int32_t)floatdata >> 24); return gen.as_float(); } //------------------------------------------------- // fp_to_double - convert a 32-bit value from DSP // floating-point format a 64-bit IEEE double //------------------------------------------------- double tms3203x_device::fp_to_double(uint32_t floatdata) { tmsreg gen(floatdata << 8, (int32_t)floatdata >> 24); return gen.as_double(); } //------------------------------------------------- // float_to_fp - convert a 32-bit IEEE float to // a 32-bit DSP floating-point value //------------------------------------------------- uint32_t tms3203x_device::float_to_fp(float fval) { tmsreg gen(fval); return (gen.exponent() << 24) | ((uint32_t)gen.mantissa() >> 8); } //------------------------------------------------- // double_to_fp - convert a 64-bit IEEE double to // a 32-bit DSP floating-point value //------------------------------------------------- uint32_t tms3203x_device::double_to_fp(double dval) { tmsreg gen(dval); return (gen.exponent() << 24) | ((uint32_t)gen.mantissa() >> 8); } //************************************************************************** // EXECUTION //************************************************************************** //------------------------------------------------- // check_irqs - check for pending IRQs and take // them if enabled //------------------------------------------------- void tms3203x_device::check_irqs() { // determine if we have any live interrupts uint16_t validints = IREG(TMR_IF) & IREG(TMR_IE) & 0x0fff; if (validints == 0 || (IREG(TMR_ST) & GIEFLAG) == 0) return; // find the lowest signalled value int whichtrap = 0; for (int i = 0; i < 12; i++) if (validints & (1 << i)) { whichtrap = i + 1; break; } // no longer idling if we get here m_is_idling = false; if (!m_delayed) { uint16_t intmask = 1 << (whichtrap - 1); // bit in IF is cleared when interrupt is taken IREG(TMR_IF) &= ~intmask; trap(whichtrap); // after auto-clearing the interrupt bit, we need to re-trigger // level-sensitive interrupts if (m_chip_type == CHIP_TYPE_TMS32031 || (IREG(TMR_ST) & 0x4000) == 0) IREG(TMR_IF) |= m_irq_state & 0x0f; } else m_irq_pending = true; } //------------------------------------------------- // execute_min_cycles - return minimum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t tms3203x_device::execute_min_cycles() const { return 1; } //------------------------------------------------- // execute_max_cycles - return maximum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t tms3203x_device::execute_max_cycles() const { return 4; } //------------------------------------------------- // execute_input_lines - return the number of // input/interrupt lines //------------------------------------------------- uint32_t tms3203x_device::execute_input_lines() const { return 14; } //------------------------------------------------- // execute_set_input - set input and IRQ lines //------------------------------------------------- void tms3203x_device::execute_set_input(int inputnum, int state) { if (inputnum == TMS3203X_MCBL) { // switch between microcomputer/boot loader and microprocessor modes bool old_mode = m_mcbl_mode; m_mcbl_mode = (state == ASSERT_LINE); if (m_mcbl_mode != old_mode) { if (m_mcbl_mode) m_program->install_rom(0x000000, 0x000fff, memregion(shortname())->base()); else m_program->unmap_read(0x000000, 0x000fff); } return; } if (inputnum == TMS3203X_HOLD) { m_hold_state = (state == ASSERT_LINE); // FIXME: "there is a minimum of one cycle delay from the time when // the processor recognises /HOLD = 0 until /HOLDA = 0" if (m_hold_state) { // assert hold acknowledge if external hold enabled if (!(m_primary_bus_control & NOHOLD)) { m_primary_bus_control |= HOLDST; m_holda_cb(ASSERT_LINE); } } else { // clear hold acknowledge if port is held externally if ((m_primary_bus_control & HOLDST) && !(m_primary_bus_control & HIZ)) { m_primary_bus_control &= ~HOLDST; m_holda_cb(CLEAR_LINE); } } return; } // update the external state uint16_t intmask = 1 << inputnum; if (state == ASSERT_LINE) { m_irq_state |= intmask; IREG(TMR_IF) |= intmask; } else m_irq_state &= ~intmask; // external interrupts are level-sensitive on the '31 and can be // configured as such on the '32; in that case, if the external // signal is high, we need to update the value in IF accordingly if (m_chip_type != CHIP_TYPE_TMS32032 || (IREG(TMR_ST) & 0x4000) == 0) IREG(TMR_IF) |= m_irq_state & 0x0f; } //------------------------------------------------- // execute_run - execute until our icount expires //------------------------------------------------- void tms3203x_device::execute_run() { // check IRQs up front check_irqs(); // if we're idling, just eat the cycles if (m_is_idling) { m_icount = 0; return; } // non-debug case if ((machine().debug_flags & DEBUG_FLAG_ENABLED) == 0) { while (m_icount > 0) { if ((IREG(TMR_ST) & RMFLAG) && m_pc == IREG(TMR_RE) + 1) { if ((int32_t)--IREG(TMR_RC) >= 0) m_pc = IREG(TMR_RS); else { IREG(TMR_ST) &= ~RMFLAG; if (m_delayed) { m_delayed = false; if (m_irq_pending) { m_irq_pending = false; check_irqs(); } } } continue; } execute_one(); } } // debugging case else { while (m_icount > 0) { // watch for out-of-range stack pointers if (IREG(TMR_SP) & 0xff000000) machine().debug_break(); if ((IREG(TMR_ST) & RMFLAG) && m_pc == IREG(TMR_RE) + 1) { if ((int32_t)--IREG(TMR_RC) >= 0) m_pc = IREG(TMR_RS); else { IREG(TMR_ST) &= ~RMFLAG; if (m_delayed) { m_delayed = false; if (m_irq_pending) { m_irq_pending = false; check_irqs(); } } } continue; } debugger_instruction_hook(m_pc); execute_one(); } } } // internal peripherals WRITE32_MEMBER(tms3203x_device::primary_bus_control_w) { // change in internal hold state if ((m_primary_bus_control ^ data) & HIZ) { if (m_primary_bus_control & HOLDST) m_primary_bus_control &= ~HOLDST; else m_primary_bus_control |= HOLDST; m_holda_cb(data & HIZ ? ASSERT_LINE : CLEAR_LINE); } // enable of external hold with hold pending if ((m_primary_bus_control & NOHOLD) && !(data & NOHOLD) && m_hold_state) { m_primary_bus_control |= HOLDST; m_holda_cb(ASSERT_LINE); } // update register m_primary_bus_control = (m_primary_bus_control & ~(mem_mask | WMASK)) | (data & mem_mask & WMASK); } //************************************************************************** // CORE OPCODES //************************************************************************** #include "32031ops.hxx"