diff options
author | 2020-07-02 20:04:21 +0200 | |
---|---|---|
committer | 2020-07-02 14:04:21 -0400 | |
commit | 729b27d7c1b78bc70a443bc6281585501b34ffcd (patch) | |
tree | 3579c9f48d90d77c8083c7de89df3c04bf556d05 | |
parent | 50baacce8b98bc561213e07e61c505394176b6c4 (diff) |
hp_ipc: added beeper (#6893)
-rw-r--r-- | scripts/src/machine.lua | 13 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 1 | ||||
-rw-r--r-- | src/devices/machine/cop452.cpp | 350 | ||||
-rw-r--r-- | src/devices/machine/cop452.h | 96 | ||||
-rw-r--r-- | src/mame/drivers/hp_ipc.cpp | 34 |
5 files changed, 490 insertions, 4 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 88d8a38f7a4..686018bff27 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -133,6 +133,19 @@ end --------------------------------------------------- -- +--@src/devices/machine/cop452.h,MACHINES["COP452"] = true +--------------------------------------------------- + +if (MACHINES["COP452"]~=null) then + files { + MAME_DIR .. "src/devices/machine/cop452.cpp", + MAME_DIR .. "src/devices/machine/cop452.h", + } +end + + +--------------------------------------------------- +-- --@src/devices/machine/cr511b.h,MACHINES["CR511B"] = true --------------------------------------------------- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 72d51b9cf96..ab012a1b084 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -474,6 +474,7 @@ MACHINES["CDP1879"] = true MACHINES["CHESSMACHINE"] = true MACHINES["CMOS40105"] = true MACHINES["COM8116"] = true +MACHINES["COP452"] = true MACHINES["CR589"] = true MACHINES["CS4031"] = true MACHINES["CS8221"] = true diff --git a/src/devices/machine/cop452.cpp b/src/devices/machine/cop452.cpp new file mode 100644 index 00000000000..cd3e1cb9ecb --- /dev/null +++ b/src/devices/machine/cop452.cpp @@ -0,0 +1,350 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/*************************************************************************** + + cop452.h + + Frequency generator & counter + +***************************************************************************/ + +#include "emu.h" +#include "cop452.h" + +// Debugging +//#define VERBOSE LOG_GENERAL +#define VERBOSE 0 +#include "logmacro.h" + +// Device type definition +DEFINE_DEVICE_TYPE(COP452, cop452_device, "COP452", "National Semiconductor COP452 frequency generator") + +cop452_device::cop452_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) +: device_t(mconfig , COP452 , tag , owner , clock) + , m_out_handlers(*this) +{ +} + +WRITE_LINE_MEMBER(cop452_device::cs_w) +{ + m_cs = state; + if (m_cs) { + // CS removed + m_spi_state = 0; + m_sr = 0; + } +} + +WRITE_LINE_MEMBER(cop452_device::sk_w) +{ + if (!m_cs && !m_sk && state) { + // Rising edge on SK + LOG("bit %d %u\n" , m_di , m_spi_state); + if (m_spi_state == 0 && m_di) { + // Got start bit + m_spi_state = 1; + } else if (m_spi_state >= 1 && m_spi_state < 6) { + // Shifting instruction in + m_sr = (m_sr << 1) | m_di; + m_spi_state++; + if (m_spi_state == 6) { + LOG("Inst = %x\n" , m_sr); + m_spi_state = 22; + unsigned idx = !BIT(m_sr , 0); + char reg = idx ? 'B' : 'A'; + switch (m_sr) { + case 0b00000: + case 0b00001: + // LDRA/B + LOG("LDR%c\n" , reg); + m_spi_state = 6; + m_reg[ idx ] = 0; + break; + + case 0b00010: + case 0b00011: + // RDRA/B + // TODO: not implemented ATM + LOG("RDR%c\n" , reg); + break; + + case 0b00100: + case 0b00101: + // TRCA/B + LOG("TRC%c\n" , reg); + m_cnt[ idx ] = m_reg[ idx ]; + set_timer(idx); + break; + + case 0b00110: + case 0b00111: + // TCRA/B + // TODO: + LOG("TCR%c\n" , reg); + break; + + case 0b01000: + // CK1 + LOG("CK1\n"); + m_clk_div_4 = false; + break; + + case 0b01001: + // CK4 + LOG("CK4\n"); + m_clk_div_4 = true; + break; + + default: + if (m_sr & 0b10000) { + // LDM + m_mode = m_sr & 0b01111; + LOG("LDM %x\n" , m_mode); + set_timer(0); + set_timer(1); + if (m_mode == MODE_NUMBER_PULSES) { + // Always start with OA = 1 + set_output(0 , true); + } else if (m_mode == MODE_WHITE_NOISE || + m_mode == MODE_GATED_WHITE) { + // Preset bit 15 & 16 of register A when entering + // white noise modes + m_reg[ 0 ] |= 0x8000; + m_regA_b16 = true; + } + } else { + // Unknown instruction + LOG("Unknown instruction\n"); + } + break; + } + } + } else if (m_spi_state >= 6 && m_spi_state < 22) { + // Loading A/B register + unsigned idx = !BIT(m_sr , 0); + char reg = idx ? 'B' : 'A'; + m_reg[ idx ] = (m_reg[ idx ] << 1) | m_di; + m_spi_state++; + if (m_spi_state == 22) { + LOG("REG%c = %04x\n" , reg , m_reg[ idx ]); + } + } + } + m_sk = state; +} + +WRITE_LINE_MEMBER(cop452_device::di_w) +{ + m_di = state; +} + +READ_LINE_MEMBER(cop452_device::do_r) +{ + // TODO: + return 0; +} + +void cop452_device::device_start() +{ + m_out_handlers.resolve_all_safe(); + + m_timers[ 0 ] = timer_alloc(0); + m_timers[ 1 ] = timer_alloc(1); + + save_item(NAME(m_mode)); + save_item(NAME(m_clk_div_4)); + save_item(NAME(m_cs)); + save_item(NAME(m_sk)); + save_item(NAME(m_di)); + save_item(NAME(m_out)); + save_item(NAME(m_regA_b16)); + save_item(NAME(m_reg)); + save_item(NAME(m_cnt)); + save_item(NAME(m_spi_state)); + save_item(NAME(m_sr)); +} + +void cop452_device::device_reset() +{ + // Set reset mode + m_mode = MODE_RESET; + m_clk_div_4 = true; + m_out[ 0 ] = m_out[ 1 ] = true; + set_output(0 , false); + set_output(1 , false); + m_spi_state = 0; + m_sr = 0; + m_timers[ 0 ]->reset(); + m_timers[ 1 ]->reset(); +} + +void cop452_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + attotime target = attotime::never; + + switch (m_mode) { + case MODE_DUAL_FREQ: + toggle_n_reload(id); + target = counts_to_attotime(m_cnt[ id ]); + break; + + case MODE_TRIG_PULSE: + // TODO: NOT IMPLEMENTED + break; + + case MODE_NUMBER_PULSES: + if (id == 0) { + toggle_n_reload(0); + target = counts_to_attotime(m_cnt[ 0 ]); + if (!m_out[ 0 ]) { + // It seems that cnt B decrements each time OA goes low + if (m_cnt[ 1 ] != 0) { + m_cnt[ 1 ]--; + } else { + // End of pulse train + toggle_n_reload(1); + m_mode = MODE_RESET; + target = attotime::never; + } + } + } + break; + + case MODE_DUTY_CYCLE: + // TODO: NOT IMPLEMENTED + break; + + case MODE_FREQ_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_DUAL_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_WAVE_MEAS: + // TODO: NOT IMPLEMENTED + break; + + case MODE_TRIG_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_WHITE_NOISE: + case MODE_GATED_WHITE: + { + if (id == 0) { + // Reg A & its 17th bit (m_regA_b16) form a 17-bit LFSR + // LFSR uses X^17+X^14+1 polynomial to generate a pseudo-random + // maximal-length sequence + bool feedback = m_regA_b16 ^ BIT(m_reg[ 0 ] , 13); + m_regA_b16 = BIT(m_reg[ 0 ] , 15); + m_reg[ 0 ] <<= 1; + m_reg[ 0 ] |= feedback; + } else { + toggle_n_reload(1); + } + bool new_out_0 = m_regA_b16; + if (m_mode == MODE_GATED_WHITE) { + new_out_0 &= m_out[ 1 ]; + } + set_output(0 , new_out_0); + } + break; + + default: + break; + } + + timer.adjust(target); +} + +attotime cop452_device::counts_to_attotime(unsigned counts) const +{ + if (m_clk_div_4) { + return clocks_to_attotime((counts + 1) * 4); + } else { + return clocks_to_attotime(counts + 1); + } +} + +void cop452_device::set_timer(unsigned idx) +{ + attotime target = attotime::never; + + switch (m_mode) { + case MODE_DUAL_FREQ: + // Cnt A & B count independently + target = counts_to_attotime(m_cnt[ idx ]); + break; + + case MODE_TRIG_PULSE: + // TODO: NOT IMPLEMENTED + break; + + case MODE_NUMBER_PULSES: + // Cnt A generates OA frequency + // Cnt B counts the periods to output + if (idx == 0) { + target = counts_to_attotime(m_cnt[ 0 ]); + } + break; + + case MODE_DUTY_CYCLE: + // TODO: NOT IMPLEMENTED + break; + + case MODE_FREQ_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_DUAL_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_WAVE_MEAS: + // TODO: NOT IMPLEMENTED + break; + + case MODE_TRIG_COUNT: + // TODO: NOT IMPLEMENTED + break; + + case MODE_WHITE_NOISE: + case MODE_GATED_WHITE: + // Cnt A is not used. Timer 0 expires once per internal clock period. + // Cnt B generates squarewave signal on OB + if (idx == 0) { + target = counts_to_attotime(0); + } else { + target = counts_to_attotime(m_cnt[ 1 ]); + } + break; + + default: + break; + } + + m_timers[ idx ]->adjust(target); +} + +void cop452_device::set_output(unsigned idx , bool state) +{ + if (m_out[ idx ] != state) { + m_out[ idx ] = state; + LOG("OUT %u=%d @%s\n" , idx , state , machine().time().as_string()); + m_out_handlers[ idx ](state); + } +} + +void cop452_device::toggle_output(unsigned idx) +{ + set_output(idx , !m_out[ idx ]); +} + +void cop452_device::toggle_n_reload(unsigned idx) +{ + // Toggle output OA/OB and reload its associated counter + toggle_output(idx); + m_cnt[ idx ] = m_reg[ idx ]; +} diff --git a/src/devices/machine/cop452.h b/src/devices/machine/cop452.h new file mode 100644 index 00000000000..f4981be72f3 --- /dev/null +++ b/src/devices/machine/cop452.h @@ -0,0 +1,96 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/*************************************************************************** + + cop452.h + + Frequency generator & counter + +**************************************************************************** + ____ ____ + ZO 1 |* \_/ | 14 ZI + OA 2 | | 13 DO + INB 3 | | 12 DI + ENB 4 | COP452 | 11 SK + OB 5 | | 10 CS/ + Vcc 6 | | 9 GND + CKO 7 |___________| 8 CKI + +***************************************************************************/ + +#ifndef MAME_MACHINE_COP452_H +#define MAME_MACHINE_COP452_H + +#pragma once + +class cop452_device : public device_t +{ +public: + cop452_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + // SPI I/O + DECLARE_WRITE_LINE_MEMBER(cs_w); + DECLARE_WRITE_LINE_MEMBER(sk_w); + DECLARE_WRITE_LINE_MEMBER(di_w); + DECLARE_READ_LINE_MEMBER(do_r); + + // Signal outputs + auto oa_w() { return m_out_handlers[ 0 ].bind(); } + auto ob_w() { return m_out_handlers[ 1 ].bind(); } + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + +private: + // Index 0 is for "A" side, 1 is for "B" side + devcb_write_line::array<2> m_out_handlers; + + // Timers + emu_timer *m_timers[ 2 ]; + + // State + uint8_t m_mode; + bool m_clk_div_4; + bool m_cs; + bool m_sk; + bool m_di; + bool m_out[ 2 ]; + bool m_regA_b16; + uint16_t m_reg[ 2 ]; + uint16_t m_cnt[ 2 ]; + // 0 : idle + // 1..5 : shifting instruction in + // 6..21: shifting register in + // >= 22: done + unsigned m_spi_state; + uint8_t m_sr; + + // Operating modes + enum : uint8_t { + MODE_DUAL_FREQ, // 0000 Dual frequency + MODE_TRIG_PULSE, // 0001 Triggered pulse + MODE_NUMBER_PULSES, // 0010 Number of pulses + MODE_DUTY_CYCLE, // 0011 Duty cycle + MODE_FREQ_COUNT, // 0100 Frequency and count + MODE_DUAL_COUNT, // 0101 Dual count + MODE_WAVE_MEAS, // 0110 Waveform measurement + MODE_TRIG_COUNT, // 0111 Triggered pulse and count + MODE_WHITE_NOISE, // 1000 White noise & frequency + MODE_GATED_WHITE, // 1001 Gated white noise + MODE_RESET = 15 // 1111 Reset + }; + + attotime counts_to_attotime(unsigned counts) const; + void set_timer(unsigned idx); + void set_output(unsigned idx , bool state); + void toggle_output(unsigned idx); + void toggle_n_reload(unsigned idx); +}; + +// device type definition +DECLARE_DEVICE_TYPE(COP452, cop452_device) + +#endif // MAME_MACHINE_COP452_H diff --git a/src/mame/drivers/hp_ipc.cpp b/src/mame/drivers/hp_ipc.cpp index a62a5b06394..0dbbccbc3a2 100644 --- a/src/mame/drivers/hp_ipc.cpp +++ b/src/mame/drivers/hp_ipc.cpp @@ -11,12 +11,11 @@ Driver to-do list - keyboard: NMI generation - RTC chip: proper month, day (possibly a different chip, 82167) - HP-IL printer -- sound (needs dump of COP452) QA + diagnstc.td0: display test - diagnstc.td0: complete keyboard test [second connector not implemented] -- diagnstc.td0: speaker test ++ diagnstc.td0: speaker test - diagnstc.td0: printer test + diagnstc.td0: auto: floppy disc test + diagnstc.td0: auto: ram test @@ -370,6 +369,10 @@ Software to look for #include "video/hp1ll3.h" #include "machine/tms9914.h" #include "bus/ieee488/ieee488.h" +#include "machine/cop452.h" +#include "speaker.h" +#include "sound/dac.h" +#include "sound/volt_reg.h" #include "emupal.h" #include "screen.h" @@ -386,6 +389,8 @@ public: , m_ram(*this, RAM_TAG) , m_gpu(*this , "gpu") , m_screen(*this, "screen") + , m_spkr(*this , "spkr") + , m_dac(*this , "dac") { } void hp_ipc_base(machine_config &config); @@ -404,6 +409,7 @@ private: void ram_w(offs_t offset, uint16_t data, uint16_t mem_mask); uint16_t trap_r(offs_t offset, uint16_t mem_mask); void trap_w(offs_t offset, uint16_t data, uint16_t mem_mask); + void spkr_w(offs_t offset, uint16_t data); uint8_t floppy_id_r(); void floppy_id_w(uint8_t data); @@ -431,6 +437,8 @@ private: required_device<ram_device> m_ram; required_device<hp1ll3_device> m_gpu; required_device<screen_device> m_screen; + required_device<cop452_device> m_spkr; + required_device<dac_1bit_device> m_dac; uint32_t m_mmu[4], m_lowest_ram_addr; uint16_t *m_internal_ram; @@ -498,7 +506,8 @@ void hp_ipc_state::hp_ipc_mem_inner_base(address_map &map) map(0x0630000, 0x063FFFF).mask(0xf).rw("hpib" , FUNC(tms9914_device::read) , FUNC(tms9914_device::write)).umask16(0x00ff); map(0x0640000, 0x064002F).rw("rtc", FUNC(mm58167_device::read), FUNC(mm58167_device::write)).umask16(0x00ff); map(0x0660000, 0x06600FF).rw("mlc", FUNC(hp_hil_mlc_device::read), FUNC(hp_hil_mlc_device::write)).umask16(0x00ff); // 'caravan', scrn/caravan.h - map(0x0670000, 0x067FFFF).noprw(); // Speaker (NatSemi COP 452) + map(0x0670000, 0x067FFFF).w(FUNC(hp_ipc_state::spkr_w)); // Speaker (NatSemi COP 452) + map(0x0670000, 0x067FFFF).nopr(); // Speaker (NatSemi COP 452) map(0x0680000, 0x068FFFF).noprw(); // 'SIMON (98628) fast HP-IB card' -- sys/simon.h map(0x0700000, 0x07FFFFF).unmaprw(); // External I/O map(0x0800000, 0x0FFFFFF).rw(FUNC(hp_ipc_state::ram_r), FUNC(hp_ipc_state::ram_w)); @@ -569,6 +578,12 @@ void hp_ipc_state::trap_w(offs_t offset, uint16_t data, uint16_t mem_mask) if (!machine().side_effects_disabled()) set_bus_error((offset << 1) & 0xFFFFFF, false, mem_mask); } +void hp_ipc_state::spkr_w(offs_t offset, uint16_t data) +{ + m_spkr->cs_w(!BIT(data , 0)); + m_spkr->sk_w(BIT(data , 1)); + m_spkr->di_w(BIT(data , 2)); +} uint16_t hp_ipc_state::ram_r(offs_t offset, uint16_t mem_mask) { @@ -710,6 +725,9 @@ void hp_ipc_state::machine_start() void hp_ipc_state::machine_reset() { m_floppy = nullptr; + m_spkr->cs_w(1); + m_spkr->sk_w(0); + m_spkr->di_w(0); } @@ -783,6 +801,14 @@ void hp_ipc_state::hp_ipc_base(machine_config &config) ieee.ren_callback().set("hpib" , FUNC(tms9914_device::ren_w)); IEEE488_SLOT(config , "ieee_rem" , 0 , remote488_devices , nullptr); + // Beeper + COP452(config , m_spkr , 2_MHz_XTAL); + SPEAKER(config, "mono").front_center(); + DAC_1BIT(config, m_dac , 0).add_route(ALL_OUTPUTS, "mono", 0.5, AUTO_ALLOC_INPUT, 0); + voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); + vref.add_route(0, "dac", 1.0, DAC_VREF_POS_INPUT); + m_spkr->oa_w().set(m_dac , FUNC(dac_1bit_device::write)); + RAM(config, RAM_TAG).set_default_size("512K").set_extra_options("768K,1M,1576K,2M,3M,4M,5M,6M,7M,7680K"); } @@ -855,5 +881,5 @@ ROM_END #define rom_hp9808a rom_hp_ipc // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS -COMP( 1985, hp_ipc, 0, 0, hp_ipc, hp_ipc, hp_ipc_state, empty_init, "Hewlett-Packard", "Integral Personal Computer 9807A", MACHINE_NO_SOUND) +COMP( 1985, hp_ipc, 0, 0, hp_ipc, hp_ipc, hp_ipc_state, empty_init, "Hewlett-Packard", "Integral Personal Computer 9807A", 0) COMP( 1985, hp9808a, 0, 0, hp9808a, hp_ipc, hp_ipc_state, empty_init, "Hewlett-Packard", "Integral Personal Computer 9808A", MACHINE_NOT_WORKING) |