From f2ba762e0a39c294ff60912fc30ec1c50925d15c Mon Sep 17 00:00:00 2001 From: arbee Date: Mon, 10 Feb 2025 22:46:29 -0500 Subject: [PATCH] apple/macpwrbk030.cpp: Moved macpd210 out to new driver for MSC-based portables. [R. Belmont] apple/gsc.cpp: Split Apple Gray Scale Controller (remarked C&T 65210) to its own device. [R. Belmont] apple/csc.cpp: Initial Apple Color Screen Controller (remarked C&T 65220) support. [R. Belmont] apple/msc.cpp: Initial Apple Main System Controller and MSC II support. [R. Belmont] m6805/m68hc05pge.cpp: Initial support for the Apple/Motorola "PG&E" microcontroller. [R. Belmont] apple/macpwrbkmsc.cpp: New driver for MSC and MSC II-based Mac PowerBooks and PowerBook Duos. [R. Belmont] Systems promoted to working --------------------------- Apple Macintosh PowerBook Duo 210 [R. Belmont] New working systems ------------------- Apple Macintosh PowerBook Duo 270c [R. Belmont] Apple Macintosh PowerBook Duo 280 [R. Belmont] New working clones ------------------ Apple Macintosh PowerBook Duo 230 [R. Belmont] Apple Macintosh PowerBook Duo 250 [R. Belmont] Apple Macintosh PowerBook Duo 280c [R. Belmont] --- scripts/src/cpu.lua | 2 + src/devices/cpu/m6805/m68hc05pge.cpp | 926 ++++++++++++++++++++++++++ src/devices/cpu/m6805/m68hc05pge.h | 150 +++++ src/devices/machine/6522via.h | 5 +- src/mame/apple/csc.cpp | 340 ++++++++++ src/mame/apple/csc.h | 55 ++ src/mame/apple/gsc.cpp | 176 +++++ src/mame/apple/gsc.h | 51 ++ src/mame/apple/macpwrbk030.cpp | 188 +----- src/mame/apple/macpwrbkmsc.cpp | 937 +++++++++++++++++++++++++++ src/mame/apple/msc.cpp | 508 +++++++++++++++ src/mame/apple/msc.h | 128 ++++ src/mame/apple/pseudovia.cpp | 11 + src/mame/apple/pseudovia.h | 6 +- src/mame/mame.lst | 7 + 15 files changed, 3320 insertions(+), 170 deletions(-) create mode 100644 src/devices/cpu/m6805/m68hc05pge.cpp create mode 100644 src/devices/cpu/m6805/m68hc05pge.h create mode 100644 src/mame/apple/csc.cpp create mode 100644 src/mame/apple/csc.h create mode 100644 src/mame/apple/gsc.cpp create mode 100644 src/mame/apple/gsc.h create mode 100644 src/mame/apple/macpwrbkmsc.cpp create mode 100644 src/mame/apple/msc.cpp create mode 100644 src/mame/apple/msc.h diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index f65aaf725be..99bb9efb18b 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -2012,6 +2012,8 @@ if CPUS["M6805"] then MAME_DIR .. "src/devices/cpu/m6805/m68hc05.h", MAME_DIR .. "src/devices/cpu/m6805/m68hc05e1.cpp", MAME_DIR .. "src/devices/cpu/m6805/m68hc05e1.h", + MAME_DIR .. "src/devices/cpu/m6805/m68hc05pge.cpp", + MAME_DIR .. "src/devices/cpu/m6805/m68hc05pge.h", } end diff --git a/src/devices/cpu/m6805/m68hc05pge.cpp b/src/devices/cpu/m6805/m68hc05pge.cpp new file mode 100644 index 00000000000..880540ffd03 --- /dev/null +++ b/src/devices/cpu/m6805/m68hc05pge.cpp @@ -0,0 +1,926 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Motorola M68HC05PGE semi-custom "PG&E" 68HC05 Power Management Unit for Apple + Emulation by R. Belmont + + Named for the electric utility in Cupertino, Pacific Gas & Electric. + Early versions of this chip had screened artwork of a power station with + lightning bolts, see the PCB photo at: + https://www.dobreprogramy.pl/@macminik/powerbook-duo-best-of-both-worlds,blog,39291 + + This is a 160-pin part with a lot of feature blocks. Most of the components appear + in other 68HC05 variants so I have included references to HC05 parts with similar + or identical versions of that functionality. + + - 512 bytes of internal ROM, which can be banked out + - Eleven 8-bit GPIO ports A, B, C, D, E, F, G, H, J, K, and L + - Four PWM (pulse width modulation) analog outputs (68HC05F32) + - One PLM (pulse length modulation) analog output (68HC05B4) + - Four ADC inputs (68HC05B4) + - An SPI interface (68HC05F32) + - A hardware 10x8 keyboard matrix scanner + - A 2-axis hardware quadrature decoder for a mouse or trackball + - Fixed-interval one second and 5.86 millisecond timers + - A custom RTC that counts a uint32 number of seconds in the classic Mac/IIgs format + - A custom Apple Desktop Bus modem +*/ + +#include "emu.h" +#include "m68hc05pge.h" +#include "m6805defs.h" +#include "6805dasm.h" + +#define LOG_ADB (1U << 1) +#define LOG_IRQ (1U << 2) +#define LOG_PWM (1U << 3) +#define LOG_PLM (1U << 4) +#define LOG_SPI (1U << 5) +#define LOG_SPI_VERBOSE (1U << 6) +#define LOG_ADC (1U << 7) + +#define VERBOSE (0) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(M68HC05PGE, m68hc05pge_device, "m68hc05pge", "Motorola M68HC05PGE") + +static constexpr int M68HC05PGE_INT_IRQ = M6805_IRQ_LINE; // external IRQ line +static constexpr int M68HC05PGE_INT_ADB = M68HC05PGE_INT_IRQ + 1; // ADB interrupt +static constexpr int M68HC05PGE_INT_RTI = M68HC05PGE_INT_IRQ + 2; // real-time (5.86 ms) interrupt +static constexpr int M68HC05PGE_INT_CPI = M68HC05PGE_INT_IRQ + 3; // one second interrupt +static constexpr int M68HC05PGE_INT_SPI = M68HC05PGE_INT_IRQ + 4; // SPI interrupt +static constexpr int M68HC05PGE_INT_KEY = M68HC05PGE_INT_IRQ + 5; // keyboard scanner interrupt + +static constexpr u8 CSCR_SRAM_CS = 5; // chip select for $8000-$FFFF SRAM +static constexpr u8 CSCR_RESET = 0; + +static constexpr u8 OPTION_INTROM = 7; // internal ROM at fe00, else external address bus +static constexpr u8 OPTION_EXTBUS = 6; // tri-state external address bus, else ext. bus enabled +static constexpr u8 OPTION_IRQSENSE = 1; // IRQ is edge-triggered, else level triggered +static constexpr u8 OPTION_RESET = (1<adjust(attotime::from_hz(1), 0, attotime::from_hz(1)); + // and the 5.86ms timer (5.86ms = 5860 uSec) + m_cpi_timer->adjust(attotime::from_usec(5860), 0, attotime::from_usec(5860)); +} + +device_memory_interface::space_config_vector m68hc05pge_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_PROGRAM, &m_program_config) + }; +} + +void m68hc05pge_device::interrupt_vector() +{ + for (int irq = M68HC05PGE_INT_IRQ; irq <= M68HC05PGE_INT_KEY; irq++) + { + if (BIT(m_pending_interrupts, irq)) + { + LOGMASKED(LOG_IRQ, "Taking IRQ %d vector %04x\n", irq, 0xfffa - (irq << 1)); + m_pending_interrupts &= ~(1 << irq); + rm16(0xfffa - (irq << 1), m_pc); + return; + } + } +} + +u64 m68hc05pge_device::execute_clocks_to_cycles(u64 clocks) const noexcept +{ + return (clocks + 1) / 2; +} + +u64 m68hc05pge_device::execute_cycles_to_clocks(u64 cycles) const noexcept +{ + return cycles * 2; +} + +std::unique_ptr m68hc05pge_device::create_disassembler() +{ + return std::make_unique(); +} + +void m68hc05pge_device::send_port(u8 offset, u8 data) +{ + m_write_p[offset](data); +} + +u8 m68hc05pge_device::ports_r(offs_t offset) +{ + u8 incoming = m_read_p[offset](); + + // apply data direction registers + incoming &= (m_ddrs[offset] ^ 0xff); + // OR in ddr-masked version of port writes + incoming |= (m_ports[offset] & m_ddrs[offset]); + + return incoming; +} + +void m68hc05pge_device::ports_w(offs_t offset, u8 data) +{ + send_port(offset, (data & m_ddrs[offset]) | (m_pullups[offset] & ~m_ddrs[offset])); + m_ports[offset] = data; +} + +u8 m68hc05pge_device::ddrs_r(offs_t offset) +{ + return m_ddrs[offset]; +} + +void m68hc05pge_device::ddrs_w(offs_t offset, u8 data) +{ + send_port(offset, (m_ports[offset] & data) | (m_pullups[offset] & ~data)); + m_ddrs[offset] = data; +} + +u8 m68hc05pge_device::pll_r() +{ + return m_pll_ctrl; +} + +void m68hc05pge_device::pll_w(u8 data) +{ + if (m_pll_ctrl != data) + { + static const int clocks[4] = {524288, 1048576, 2097152, 4194304}; + LOG("PLL ctrl: clock %d TCS:%d BCS:%d AUTO:%d BWC:%d PLLON:%d (PC=%x)\n", clocks[data & 3], + (data & 0x80) ? 1 : 0, + (data & 0x40) ? 1 : 0, + (data & 0x20) ? 1 : 0, + (data & 0x10) ? 1 : 0, + (data & 0x08) ? 1 : 0, pc()); + } + + m_pll_ctrl = data; +} + +void m68hc05pge_device::execute_set_input(int inputnum, int state) +{ + if (state == CLEAR_LINE) + { + m_pending_interrupts &= ~(1 << inputnum); + } + else + { + m_pending_interrupts |= (1 << inputnum); + } +} + +// fires every 1 second +TIMER_CALLBACK_MEMBER(m68hc05pge_device::seconds_tick) +{ + m_rtc++; + m_cpicsr |= (1 << CPICSR_ONESEC_IRQ_FLAG); + if (BIT(m_cpicsr, CPICSR_ONESEC_IRQ_ENABLE)) + { + set_input_line(M68HC05PGE_INT_CPI, ASSERT_LINE); + } +} + +// fires every 5.86ms +TIMER_CALLBACK_MEMBER(m68hc05pge_device::cpi_tick) +{ + m_cpicsr |= (1 << CPICSR_586_IRQ_FLAG); + if (BIT(m_cpicsr, CPICSR_586_IRQ_ENABLE)) + { + set_input_line(M68HC05PGE_INT_RTI, ASSERT_LINE); + } +} + +const tiny_rom_entry *m68hc05pge_device::device_rom_region() const +{ + return ROM_NAME(m68hc05pge); +} + +void m68hc05pge_device::m68hc05pge_map(address_map &map) +{ + map(0x0000, 0x0003).rw(FUNC(m68hc05pge_device::ports_r), FUNC(m68hc05pge_device::ports_w)); + map(0x0004, 0x0006).rw(FUNC(m68hc05pge_device::ddrs_r), FUNC(m68hc05pge_device::ddrs_w)); + map(0x0007, 0x0007).rw(FUNC(m68hc05pge_device::pll_r), FUNC(m68hc05pge_device::pll_w)); + map(0x000a, 0x000c).rw(FUNC(m68hc05pge_device::spi_r), FUNC(m68hc05pge_device::spi_w)); + map(0x000d, 0x000d).rw(FUNC(m68hc05pge_device::cpicsr_r), FUNC(m68hc05pge_device::cpicsr_w)); + map(0x000e, 0x000e).rw(FUNC(m68hc05pge_device::cscr_r), FUNC(m68hc05pge_device::cscr_w)); + map(0x000f, 0x000f).rw(FUNC(m68hc05pge_device::kcsr_r), FUNC(m68hc05pge_device::kcsr_w)); + map(0x0014, 0x0016).rw(FUNC(m68hc05pge_device::trackball_r), FUNC(m68hc05pge_device::trackball_w)); + map(0x0018, 0x001a).rw(FUNC(m68hc05pge_device::adb_r), FUNC(m68hc05pge_device::adb_w)); + map(0x001c, 0x001c).rw(FUNC(m68hc05pge_device::option_r), FUNC(m68hc05pge_device::option_w)); + map(0x001d, 0x001e).rw(FUNC(m68hc05pge_device::adc_r), FUNC(m68hc05pge_device::adc_w)); + map(0x0020, 0x002c).rw(FUNC(m68hc05pge_device::ports_high_r), FUNC(m68hc05pge_device::ports_high_w)); + map(0x002d, 0x0032).rw(FUNC(m68hc05pge_device::pwm_r), FUNC(m68hc05pge_device::pwm_w)); + map(0x0034, 0x0036).rw(FUNC(m68hc05pge_device::plm_r), FUNC(m68hc05pge_device::plm_w)); + map(0x0038, 0x003b).rw(FUNC(m68hc05pge_device::rtc_r), FUNC(m68hc05pge_device::rtc_w)); + map(0x0040, 0x03ff).ram().share(m_internal_ram); // internal RAM + map(0x0ff0, 0x0ff0).nopw(); // watchdog reset (period not known) + + map(0x8000, 0xffff).view(m_introm); + m_introm[0](0x8000, 0xffff).rw(FUNC(m68hc05pge_device::sram_r), FUNC(m68hc05pge_device::sram_w)); + m_introm[1](0x8000, 0xffff).rw(FUNC(m68hc05pge_device::sram_r), FUNC(m68hc05pge_device::sram_w)); + m_introm[1](0xfe00, 0xffff).rom().region("pge", 0); +} + +u8 m68hc05pge_device::sram_r(offs_t offset) +{ + if (BIT(m_cscr, CSCR_SRAM_CS) && !BIT(m_option, OPTION_EXTBUS)) + { + return m_sram[offset]; + } + + return 0xff; +} + +void m68hc05pge_device::sram_w(offs_t offset, u8 data) +{ + if (BIT(m_cscr, CSCR_SRAM_CS) && !BIT(m_option, OPTION_EXTBUS)) + { + m_sram[offset] = data; + } +} + +u8 m68hc05pge_device::spi_r(offs_t offset) +{ + switch (offset) + { + case 0: + return m_spcr; + + case 1: + return m_spsr; + + case 2: + if (!machine().side_effects_disabled()) + { + LOGMASKED(LOG_SPI, "SPI got %02x\n", m_spi_in); + if (BIT(m_spsr, SPSR_IRQ_FLAG)) + { + set_input_line(M68HC05PGE_INT_SPI, CLEAR_LINE); + } + } + return m_spi_in; + } + + return 0; +} + +void m68hc05pge_device::spi_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 0: + m_spcr = data; + m_spi_clock = BIT(m_spcr, SPCR_POLARITY); + write_spi_clock(m_spi_clock); + break; + + case 2: + if (!BIT(m_spcr, SPCR_MASTER)) + { + logerror("68HC05PGE: SPI slave mode not implemented\n"); + } + + if (BIT(m_spsr, SPSR_IRQ_FLAG)) + { + set_input_line(M68HC05PGE_INT_SPI, CLEAR_LINE); + } + m_spsr &= ~(1 << SPSR_IRQ_FLAG); + + m_spi_clock = BIT(m_spcr, SPCR_POLARITY); + write_spi_clock(m_spi_clock); + + m_spi_out = data; + m_spi_in = 0; + LOGMASKED(LOG_SPI, "SPI: sending %02x, clock rate %d\n", data, clock() / s_spi_divisors[m_spcr & 3]); + m_spi_bit = 16; + m_spi_timer->adjust(attotime::from_hz(clock() / s_spi_divisors[m_spcr & 3])); + break; + } +} + +TIMER_CALLBACK_MEMBER(m68hc05pge_device::spi_tick) +{ + LOGMASKED(LOG_SPI_VERBOSE, "spi_tick: bit %d\n", m_spi_bit); + + // first clock edge of a bit + if (!(m_spi_bit & 1)) + { + // phase = 0, set up the output data before sending the first clock edge of the bit + if (!BIT(m_spcr, SPCR_PHASE)) + { + write_spi_mosi(BIT(m_spi_out, 7)); + m_spi_out <<= 1; + } + + write_spi_clock(m_spi_clock ^ 1); + + // phase = 0, input bit became valid on that first edge + if (!BIT(m_spcr, SPCR_PHASE)) + { + m_spi_in <<= 1; + m_spi_in |= m_spi_miso; + LOGMASKED(LOG_SPI_VERBOSE, "PGE: MISO %d, shift %02x (PH0 POL%d)\n", m_spi_miso, m_spi_in, BIT(m_spcr, SPCR_POLARITY)); + } + } + else // second clock edge of the bit + { + // phase = 1, the output bit must be valid before this clock edge + if (BIT(m_spcr, SPCR_PHASE)) + { + write_spi_mosi(BIT(m_spi_out, 7)); + m_spi_out <<= 1; + } + + write_spi_clock(m_spi_clock); + + // phase = 1, input bit became valid on this second edge + if (BIT(m_spcr, SPCR_PHASE)) + { + m_spi_in <<= 1; + m_spi_in |= m_spi_miso; + LOGMASKED(LOG_SPI_VERBOSE, "PGE: MISO %d, shift %02x (PH1 POL%d)\n", m_spi_miso, m_spi_in, BIT(m_spcr, SPCR_POLARITY)); + } + } + + m_spi_bit--; + if (m_spi_bit > 0) + { + m_spi_timer->adjust(attotime::from_hz(clock() / s_spi_divisors[m_spcr & 3])); + } + else + { + m_spsr |= (1 << SPSR_IRQ_FLAG); + if (BIT(m_spcr, SPCR_IRQ_ENABLE)) + { + set_input_line(M68HC05PGE_INT_SPI, ASSERT_LINE); + } + } +} + +u8 m68hc05pge_device::cpicsr_r() +{ + return m_cpicsr; +} + +void m68hc05pge_device::cpicsr_w(u8 data) +{ + m_cpicsr = data; + if (!BIT(data, CPICSR_ONESEC_IRQ_FLAG)) + { + set_input_line(M68HC05PGE_INT_CPI, CLEAR_LINE); + } + if (!BIT(data, CPICSR_586_IRQ_FLAG)) + { + set_input_line(M68HC05PGE_INT_RTI, CLEAR_LINE); + } +} + +u8 m68hc05pge_device::cscr_r() +{ + return m_cscr; +} + +void m68hc05pge_device::cscr_w(u8 data) +{ + m_cscr = data; +} + +u8 m68hc05pge_device::kcsr_r() +{ + return 0; +} + +void m68hc05pge_device::kcsr_w(u8 data) +{ +// printf("%02x to KCSR\n", data); +} + +u8 m68hc05pge_device::trackball_r(offs_t offset) +{ + switch (offset) + { + case 0: // TBCS + return (m_read_tbB() << 7) | m_tbcs; // button not pressed + break; + + case 1: // signed X delta + return m_read_tbX(); + + case 2: // signed Y delta + return m_read_tbY(); + } + + return 0; +} + +void m68hc05pge_device::trackball_w(offs_t offset, u8 data) +{ + if (offset == 0) + { + m_tbcs = data & 0x7f; + } +} + +u8 m68hc05pge_device::adb_r(offs_t offset) +{ + switch (offset) + { + case 0: + return m_adbcr; + + case 1: + return m_adbsr; + + case 2: + return m_adbdr; + } + + return 0; +} + +void m68hc05pge_device::adb_w(offs_t offset, u8 data) +{ + //printf("%02x to ADB @ %d\n", data, offset); + switch (offset) + { + case 0: + //printf("%02x to ADBCR, previous %02x\n", data, m_adbcr); + // if we're clearing transmit complete, set transmitter empty + if (BIT(m_adbcr, ADBXR_TC) && !BIT(data, ADBXR_TC)) + { + //printf("ADB enabling transmitter empty\n"); + m_adbsr |= (1 << ADBXR_TDRE); + } + + // if we're clearing transmitter empty, kick the timer for transmitter complete + if (BIT(m_adbcr, ADBXR_TDRE) && !BIT(data, ADBXR_TDRE)) + { + //printf("ADB setting completion timer\n"); + m_adb_timer->adjust(attotime::from_usec(50), 1); + } + + m_adbcr = data; + if (m_adbsr & m_adbcr & ADBXR_IRQS) + { + set_input_line(M68HC05PGE_INT_ADB, ASSERT_LINE); + } + else + { + set_input_line(M68HC05PGE_INT_ADB, CLEAR_LINE); + } + break; + + case 1: + m_adbsr = data; + if (m_adbsr & m_adbcr & ADBXR_IRQS) + { + set_input_line(M68HC05PGE_INT_ADB, ASSERT_LINE); + } + else + { + set_input_line(M68HC05PGE_INT_ADB, CLEAR_LINE); + } + break; + + case 2: + m_adbdr = data; + LOGMASKED(LOG_ADB, "ADB sending %02x\n", data); + m_adbsr &= ~((1 << ADBXR_TDRE) | (1 << ADBXR_TC)); + m_adb_timer->adjust(attotime::from_usec(1200), 0); + break; + } +} + +TIMER_CALLBACK_MEMBER(m68hc05pge_device::adb_tick) +{ + switch (param) + { + case 0: // byte transmitted, trigger transmitter empty + m_adbsr |= (1 << ADBXR_TDRE); + break; + + case 1: + m_adbsr |= (1 << ADBXR_TC); + break; + } + + if (m_adbsr & m_adbcr & ADBXR_IRQS) + { + set_input_line(M68HC05PGE_INT_ADB, ASSERT_LINE); + } +} + +u8 m68hc05pge_device::option_r() +{ + return m_option; +} + +void m68hc05pge_device::option_w(u8 data) +{ + LOGMASKED(LOG_GENERAL, "%02x to OPTION\n", data); + m_option = data; + + m_introm.select(BIT(data, OPTION_INTROM)); +} + +u8 m68hc05pge_device::adc_r(offs_t offset) +{ + if (!offset) + { + LOGMASKED(LOG_ADC, "ADC read ch %d\n", m_adcsr & ADCSR_CHANNEL_MASK); + return m_ad_in[m_adcsr & ADCSR_CHANNEL_MASK](); + } + + return m_adcsr; +} + +void m68hc05pge_device::adc_w(offs_t offset, u8 data) +{ + LOGMASKED(LOG_ADC, "%02x to ADC @ %d\n", data, offset); + if (offset) + { + m_adcsr = data; + + if (BIT(m_adcsr, ADCSR_START_CONV)) + { + m_adcsr |= (1 << ADCSR_CONV_COMPLETE); + } + } +} + +u8 m68hc05pge_device::ports_high_r(offs_t offset) +{ + switch (offset) + { + case 0: // PORTE + case 2: // PORTF + case 4: // PORTG + case 6: // PORTH + case 8: // PORTJ + return ports_r((offset >> 1) + PGE_PORTE); + + case 0xa: // PORTL + return ports_r(PGE_PORTL); + + case 0xc: // PORTK + return ports_r(PGE_PORTK); + + case 1: // DDRE + case 3: // DDRF + case 5: // DDRG + case 7: // DDRH + case 9: // DDRJ + return m_ddrs[(offset >> 1) + PGE_PORTE]; + + case 0xb: // DDRL + return m_ddrs[PGE_PORTL]; + } + + return 0; +} + +void m68hc05pge_device::ports_high_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 0: // PORTE + case 2: // PORTF + case 4: // PORTG + case 6: // PORTH + case 8: // PORTJ + ports_w((offset >> 1) + PGE_PORTE, data); + break; + + case 0xa: // PORTL + ports_w(PGE_PORTL, data); + break; + + case 0xc: // PORTK + ports_w(PGE_PORTK, data); + break; + + case 1: // DDRE + case 3: // DDRF + case 5: // DDRG + case 7: // DDRH + case 9: // DDRJ + ddrs_w((offset >> 1) + PGE_PORTE, data); + break; + + case 0xb: // DDRL + ddrs_w(PGE_PORTL, data); + break; + } +} + +u8 m68hc05pge_device::pwm_r(offs_t offset) +{ + switch (offset) + { + case 0: + return m_pwmacr; + + case 1: + return m_pwma0; + + case 2: + return m_pwma1; + + case 3: + return m_pwmbcr; + + case 4: + return m_pwmb0; + + case 5: + return m_pwmb1; + } + + return 0; +} + +void m68hc05pge_device::pwm_w(offs_t offset, u8 data) +{ + LOGMASKED(LOG_PWM, "%02x to PWM @ %d\n", data, offset); + switch (offset) + { + case 0: // PWMACR + m_pwmacr = data; + break; + + case 1: // PWMA0 + LOGMASKED(LOG_PWM, "%02x to PWMA0\n", data); + m_pwma0 = data; + m_pwm_out[PGE_PWMA0](data); + break; + + case 2: // PWMA1 + LOGMASKED(LOG_PWM, "%02x to PWMA1\n", data); + m_pwma1 = data; + m_pwm_out[PGE_PWMA1](data); + break; + + case 3: // PWMBCR + m_pwmbcr = data; + break; + + case 4: // PWMB0 + LOGMASKED(LOG_PWM, "%02x to PWMB0\n", data); + m_pwmb0 = data; + m_pwm_out[PGE_PWMB0](data); + break; + + case 5: // PWMB1 + LOGMASKED(LOG_PWM, "%02x to PWMB1\n", data); + m_pwmb1 = data; + m_pwm_out[PGE_PWMB1](data); + break; + } +} + +u8 m68hc05pge_device::plm_r(offs_t offset) +{ + switch (offset) + { + case 0: + return m_plmcr; + + case 1: + return m_plmt1; + + case 2: + return m_plmt2; + } + + return 0; +} + +void m68hc05pge_device::plm_w(offs_t offset, u8 data) +{ + LOGMASKED(LOG_PLM, "%02x to PLM @ %d\n", data, offset); + switch (offset) + { + case 0: // PLMCR + m_plmcr = data; + break; + + case 1: // PLM timer 1 + m_plmt1 = data; + break; + + case 2: // PLM timer 2 + m_plmt2 = data; + break; + } +} + +u8 m68hc05pge_device::rtc_r(offs_t offset) +{ + switch (offset) + { + case 0: + return m_rtc >> 24; + + case 1: + return (m_rtc >> 16) & 0xff; + + case 2: + return (m_rtc >> 8) & 0xff; + + case 3: + return m_rtc & 0xff; + } + + return 0; +} + +void m68hc05pge_device::rtc_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 0: + m_rtc &= ~0xff000000; + m_rtc |= data << 24; + break; + + case 1: + m_rtc &= ~0x00ff0000; + m_rtc |= data << 16; + break; + + case 2: + m_rtc &= ~0x0000ff00; + m_rtc |= data << 8; + break; + + case 3: + m_rtc &= ~0x000000ff; + m_rtc |= data; + break; + } +} + +void m68hc05pge_device::nvram_default() +{ +} + +bool m68hc05pge_device::nvram_read(util::read_stream &file) +{ + auto const [err, actual] = read(file, m_internal_ram, 0x3c0); + auto const [err2, actual2] = read(file, m_sram, 0x8000); + + m_internal_ram[0x91 - 0x40] = 0; // clear power flag so the boot ROM does a cold boot + + return !err && !err2; +} + +bool m68hc05pge_device::nvram_write(util::write_stream &file) +{ + auto const [err, actual] = write(file, m_internal_ram, 0x3c0); + auto const [err2, actual2] = write(file, m_sram, 0x8000); + return !err && !err2; +} diff --git a/src/devices/cpu/m6805/m68hc05pge.h b/src/devices/cpu/m6805/m68hc05pge.h new file mode 100644 index 00000000000..bd29b19057b --- /dev/null +++ b/src/devices/cpu/m6805/m68hc05pge.h @@ -0,0 +1,150 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +#ifndef MAME_CPU_M6805_M68HC05PGE_H +#define MAME_CPU_M6805_M68HC05PGE_H + +#pragma once + +#include "emu.h" +#include "m6805.h" + +class m68hc05pge_device : public m6805_base_device, public device_nvram_interface +{ +public: + const address_space_config m_program_config; + + static constexpr int PGE_PORTA = 0; + static constexpr int PGE_PORTB = 1; + static constexpr int PGE_PORTC = 2; + static constexpr int PGE_PORTD = 3; + static constexpr int PGE_PORTE = 4; + static constexpr int PGE_PORTF = 5; + static constexpr int PGE_PORTG = 6; + static constexpr int PGE_PORTH = 7; + static constexpr int PGE_PORTJ = 8; + static constexpr int PGE_PORTK = 9; + static constexpr int PGE_PORTL = 10; + + static constexpr int PGE_PWMA0 = 0; + static constexpr int PGE_PWMA1 = 1; + static constexpr int PGE_PWMB0 = 2; + static constexpr int PGE_PWMB1 = 3; + + m68hc05pge_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + auto read_tbX() { return m_read_tbX.bind(); } // trackball X + auto read_tbY() { return m_read_tbY.bind(); } // trackball Y + auto read_tbB() { return m_read_tbB.bind(); } // trackball button + template auto read_p() { return m_read_p[Bit].bind(); } + template auto write_p() { return m_write_p[Bit].bind(); } + template void set_pullups(u8 mask) { m_pullups[Bit] = mask; } + + template auto ad_in() { return m_ad_in[ad_port].bind(); } + + template auto pwm_out() { return m_pwm_out[pwm].bind(); } + + auto spi_mosi_callback() { return write_spi_mosi.bind(); } + auto spi_clock_callback() { return write_spi_clock.bind(); } + void spi_miso_w(int state) { m_spi_miso = state; } + +protected: + // construction/destruction + m68hc05pge_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int addrbits, address_map_constructor internal_map); + + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual space_config_vector memory_space_config() const override; + virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD; + + virtual void nvram_default() override; + virtual bool nvram_read(util::read_stream &file) override; + virtual bool nvram_write(util::write_stream &file) override; + + virtual void interrupt_vector() override; + + virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const noexcept override; + virtual uint64_t execute_cycles_to_clocks(uint64_t cycles) const noexcept override; + virtual void execute_set_input(int inputnum, int state) override; + virtual std::unique_ptr create_disassembler() override; + + void m68hc05pge_map(address_map &map) ATTR_COLD; + + u8 ports_r(offs_t offset); + void ports_w(offs_t offset, u8 data); + u8 ddrs_r(offs_t offset); + void ddrs_w(offs_t offset, u8 data); + u8 read_port(u8 offset); + void send_port(u8 offset, u8 data); + u8 pll_r(); + void pll_w(u8 data); + u8 spi_r(offs_t offset); + void spi_w(offs_t offset, u8 data); + u8 cpicsr_r(); + void cpicsr_w(u8 data); + u8 cscr_r(); + void cscr_w(u8 data); + u8 kcsr_r(); + void kcsr_w(u8 data); + u8 trackball_r(offs_t offset); + void trackball_w(offs_t offset, u8 data); + u8 adb_r(offs_t offset); + void adb_w(offs_t offset, u8 data); + u8 option_r(); + void option_w(u8 data); + u8 adc_r(offs_t offset); + void adc_w(offs_t offset, u8 data); + u8 ports_high_r(offs_t offset); + void ports_high_w(offs_t offset, u8 data); + u8 pwm_r(offs_t offset); + void pwm_w(offs_t offset, u8 data); + u8 plm_r(offs_t offset); + void plm_w(offs_t offset, u8 data); + u8 rtc_r(offs_t offset); + void rtc_w(offs_t offset, u8 data); + u8 sram_r(offs_t offset); + void sram_w(offs_t offset, u8 data); + + TIMER_CALLBACK_MEMBER(seconds_tick); + TIMER_CALLBACK_MEMBER(cpi_tick); + TIMER_CALLBACK_MEMBER(spi_tick); + TIMER_CALLBACK_MEMBER(adb_tick); + + required_shared_ptr m_internal_ram; + + memory_view m_introm; + + devcb_read8 m_read_tbX, m_read_tbY; + devcb_read_line m_read_tbB; + devcb_read8::array<11> m_read_p; + devcb_write8::array<11> m_write_p; + + devcb_read8::array<16> m_ad_in; + + devcb_write8::array<4> m_pwm_out; + + devcb_write_line write_spi_mosi, write_spi_clock; + + u8 m_ports[11], m_ddrs[11], m_pullups[11]; + u8 m_pll_ctrl; + u8 m_timer_ctrl; + u8 m_onesec; + u8 m_option, m_cscr; + u8 m_spi_in, m_spi_out; + int m_spi_bit, m_spi_clock, m_spi_miso; + u8 m_spcr, m_spsr; + u8 m_cpicsr; + u8 m_adcsr; + u8 m_adbcr, m_adbsr, m_adbdr; + u8 m_tbcs; + u8 m_pwmacr, m_pwma0, m_pwma1; + u8 m_pwmbcr, m_pwmb0, m_pwmb1; + u8 m_plmcr, m_plmt1, m_plmt2; + emu_timer *m_seconds_timer, *m_cpi_timer, *m_spi_timer, *m_adb_timer; + u32 m_rtc; + u8 m_sram[0x8000]; +}; + +DECLARE_DEVICE_TYPE(M68HC05PGE, m68hc05pge_device) + +#endif // MAME_CPU_M6805_M58HC05PGE_H diff --git a/src/devices/machine/6522via.h b/src/devices/machine/6522via.h index 7895ee45d39..12210ffc7a1 100644 --- a/src/devices/machine/6522via.h +++ b/src/devices/machine/6522via.h @@ -130,12 +130,13 @@ protected: TIMER_CALLBACK_MEMBER(t2_tick); TIMER_CALLBACK_MEMBER(ca2_tick); + void set_int(int data); + void clear_int(int data); + private: uint16_t get_counter1_value(); void counter2_decrement(); - void set_int(int data); - void clear_int(int data); void shift_out(); void shift_in(); void set_pa_line(int line, int state); diff --git a/src/mame/apple/csc.cpp b/src/mame/apple/csc.cpp new file mode 100644 index 00000000000..c58c3db194e --- /dev/null +++ b/src/mame/apple/csc.cpp @@ -0,0 +1,340 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Apple Color Screen Controller (CSC) video, aka Chips & Technologies 65220 + Emulation by R. Belmont + + References: + https://github.com/elliotnunn/supermario/blob/master/base/SuperMarioProj.1994-02-09/Internal/Asm/HardwarePrivateEqu.a#L1220 + https://github.com/elliotnunn/supermario/tree/master/base/SuperMarioProj.1994-02-09/DeclData/DeclVideo/CSC + + For Escher (PowerBook Duo 270C), panel 0 is the only option. + For Yeager (PowerBook Duo280/280C), panel 0 is color 640x480, panel 4 is TFT grayscale, and panel 6 is STN grayscale + For Blackbird (Powerbook 5xx), panel 0 is Sharp color TFT, 1 is color STN, 2 is NEC color TFT, 3 is Hosiden color TFT, + 4 is Toshiba color TFT, 5 is Sharp grayscale STN, and 6 is Hosiden grayscale TFT +*/ + +#include "emu.h" +#include "csc.h" + +DEFINE_DEVICE_TYPE(CSC, csc_device, "applecsc", "Apple Color Screen Controller video") + +static constexpr u8 CSC_PANEL_ID = 0x02; +static constexpr u8 CSC_PANEL_SETUP = 0x08; +static constexpr u8 CSC_DISPLAY_FORMAT = 0x12; +static constexpr u8 CSC_DISPLAY_STATUS = 0x14; +static constexpr u8 CSC_ADDR_REG_W = 0x40; +static constexpr u8 CSC_DATA_REG = 0x42; +static constexpr u8 CSC_ADDR_REG_R = 0x46; + +static constexpr u8 CSC_DISPSTAT_IRQ_ENABLE = 0; +static constexpr u8 CSC_DISPSTAT_IRQ = 1; + +static constexpr u8 CSC_PANELSETUP_480 = 1; +static constexpr u8 CSC_PANELSETUP_COLOR = 2; +static constexpr u8 CSC_PANELSETUP_ENABLED = 3; + +// mask to determine a color 480-tall panel in 400-tall mode to fit 15bpp in 512K +static constexpr u8 CSC_PANELSETUP_COLOR400_MASK = (1 << CSC_PANELSETUP_480) | (1 << CSC_PANELSETUP_COLOR); + +void csc_device::map(address_map & map) +{ + map(0x00f20000, 0x00f2007f).rw(FUNC(csc_device::csc_r), FUNC(csc_device::csc_w)); + map(0x10000000, 0x10ffffff).rw(FUNC(csc_device::vram_r), FUNC(csc_device::vram_w)); +} + +csc_device::csc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, CSC, tag, owner, clock), + m_screen(*this, "screen"), + m_palette(*this, "palette"), + m_irq(*this), + m_csc_panel_id(0), + m_pal_idx(0), + m_pmu_blank_display(true) +{ + std::fill(std::begin(m_csc_regs), std::end(m_csc_regs), 0); +} + +void csc_device::device_start() +{ + m_vram = std::make_unique(0x80000); + + save_item(NAME(m_csc_regs)); + save_item(NAME(m_pal_idx)); + save_item(NAME(m_pmu_blank_display)); + save_pointer(NAME(m_vram), 0x80000); +} + +void csc_device::device_reset() +{ + if (m_csc_panel_id >= 4) + { + m_screen->set_raw(21604953, 800, 0, 640, 449, 0, 400); + } +} + +void csc_device::device_add_mconfig(machine_config &config) +{ + PALETTE(config, m_palette).set_entries(256); + + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_raw(25175000, 800, 0, 640, 525, 0, 480); + m_screen->set_screen_update(FUNC(csc_device::screen_update_csc)); + m_screen->screen_vblank().set(FUNC(csc_device::csc_irq_w)); +} + +void csc_device::set_panel_id(int panel_id) +{ + m_csc_panel_id = panel_id; +} + +u8 csc_device::csc_r(offs_t offset) +{ + if (offset == CSC_PANEL_ID) + { + return m_csc_panel_id; + } + else if (offset == CSC_DATA_REG) + { + u8 component = 0; + switch (m_pal_idx) + { + case 0: + component = m_palette->pen_color(m_csc_regs[CSC_ADDR_REG_R]).r(); + break; + case 1: + component = m_palette->pen_color(m_csc_regs[CSC_ADDR_REG_R]).g(); + break; + case 2: + component = m_palette->pen_color(m_csc_regs[CSC_ADDR_REG_R]).b(); + break; + } + m_pal_idx++; + if (m_pal_idx == 3) + { + m_pal_idx = 0; + m_csc_regs[CSC_ADDR_REG_R]++; + } + + return component; + } + + return m_csc_regs[offset]; +} + +void csc_device::csc_w(offs_t offset, u8 data) +{ + switch (offset) + { + case CSC_DISPLAY_STATUS: + if (BIT(data, CSC_DISPSTAT_IRQ)) + { + m_irq(CLEAR_LINE); + data = m_csc_regs[offset] & ~(1 << CSC_DISPSTAT_IRQ); + } + break; + + case CSC_DISPLAY_FORMAT: + // if this is a monochrome panel, setup the palette correctly for the mode + if (!BIT(m_csc_regs[CSC_PANEL_SETUP], CSC_PANELSETUP_COLOR)) + { + switch (data) + { + case 0: + m_palette->set_pen_color(0, rgb_t(255, 255, 255)); + m_palette->set_pen_color(0x80, rgb_t(0, 0, 0)); + break; + + case 1: + m_palette->set_pen_color(0, rgb_t(255, 255, 255)); + m_palette->set_pen_color(0x40, rgb_t(192, 192, 192)); + m_palette->set_pen_color(0x80, rgb_t(64, 64, 64)); + m_palette->set_pen_color(0xc0, rgb_t(0, 0, 0)); + break; + + case 2: + for (int i = 15; i >= 0; i--) + { + m_palette->set_pen_red_level(i<<4, (15 - i) << 4); + m_palette->set_pen_green_level(i<<4, (15 - i) << 4); + m_palette->set_pen_blue_level(i<<4, (15 - i) << 4); + } + break; + + case 3: + for (int i = 255; i >= 0; i--) + { + m_palette->set_pen_red_level(i, 255 - i); + m_palette->set_pen_green_level(i, 255 - i); + m_palette->set_pen_blue_level(i, 255 - i); + } + break; + } + } + break; + + case CSC_ADDR_REG_W: // write CLUT index + m_pal_idx = 0; + break; + + case CSC_DATA_REG: + switch (m_pal_idx) + { + case 0: + m_palette->set_pen_red_level(m_csc_regs[CSC_ADDR_REG_W], data); + break; + case 1: + m_palette->set_pen_green_level(m_csc_regs[CSC_ADDR_REG_W], data); + break; + case 2: + m_palette->set_pen_blue_level(m_csc_regs[CSC_ADDR_REG_W], data); + break; + } + m_pal_idx++; + if (m_pal_idx == 3) + { + m_pal_idx = 0; + m_csc_regs[CSC_ADDR_REG_W]++; + } + break; + + case CSC_ADDR_REG_R: // read CLUT index + m_pal_idx = 0; + break; + } + + m_csc_regs[offset] = data; +} + +void csc_device::csc_irq_w(int state) +{ + if (state == ASSERT_LINE) + { + m_csc_regs[CSC_DISPLAY_STATUS] |= (1 << CSC_DISPSTAT_IRQ); + if (BIT(m_csc_regs[CSC_DISPLAY_STATUS], 0)) + { + m_irq(ASSERT_LINE); + } + } +} + +u32 csc_device::screen_update_csc(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + // is the display enabled? + if (!BIT(m_csc_regs[CSC_PANEL_SETUP], CSC_PANELSETUP_ENABLED)) + { + bitmap.fill(0xf, cliprect); + return 0; + } + + const auto vram8 = util::big_endian_cast(&m_vram[0]); + const auto vram16 = util::big_endian_cast(&m_vram[0]); + const pen_t *pens = m_palette->pens(); + const int vres = BIT(m_csc_regs[CSC_PANEL_SETUP], CSC_PANELSETUP_480) ? 480 : 400; + const int dispoffs = ((m_csc_regs[CSC_PANEL_SETUP] & CSC_PANELSETUP_COLOR400_MASK) == (1 << CSC_PANELSETUP_COLOR)) ? 40 : 0; + + if (dispoffs) + { + bitmap.fill(0xf, cliprect); + } + + switch (m_csc_regs[CSC_DISPLAY_FORMAT]) + { + case 0: + { + for (int y = 0; y < vres; y++) + { + u32 *scanline = &bitmap.pix(y + dispoffs); + for (int x = 0; x < 640; x += 8) + { + u8 const pixels = vram8[(y * 80) + (x / 8)]; + + *scanline++ = pens[pixels & 0x80]; + *scanline++ = pens[(pixels << 1) & 0x80]; + *scanline++ = pens[(pixels << 2) & 0x80]; + *scanline++ = pens[(pixels << 3) & 0x80]; + *scanline++ = pens[(pixels << 4) & 0x80]; + *scanline++ = pens[(pixels << 5) & 0x80]; + *scanline++ = pens[(pixels << 6) & 0x80]; + *scanline++ = pens[(pixels << 7) & 0x80]; + } + } + } + break; + + case 1: // 2bpp + { + for (int y = 0; y < vres; y++) + { + u32 *scanline = &bitmap.pix(y + dispoffs); + for (int x = 0; x < 640 / 4; x++) + { + u8 const pixels = vram8[(y * 160) + x]; + + *scanline++ = pens[(pixels & 0xc0)]; + *scanline++ = pens[((pixels << 2) & 0xc0)]; + *scanline++ = pens[((pixels << 4) & 0xc0)]; + *scanline++ = pens[((pixels << 6) & 0xc0)]; + } + } + } + break; + + case 2: // 4bpp + { + for (int y = 0; y < vres; y++) + { + u32 *scanline = &bitmap.pix(y + dispoffs); + + for (int x = 0; x < 640 / 2; x++) + { + u8 const pixels = vram8[(y * 320) + x]; + *scanline++ = pens[(pixels & 0xf0)]; + *scanline++ = pens[((pixels << 4) & 0xf0)]; + } + } + } + break; + + case 3: // 8bpp + { + for (int y = 0; y < vres; y++) + { + u32 *scanline = &bitmap.pix(y + dispoffs); + + for (int x = 0; x < 640; x++) + { + u8 const pixels = vram8[(y * 640) + x]; + *scanline++ = pens[pixels]; + } + } + } + break; + + case 4: // 16bpp + { + for (int y = 0; y < vres; y++) + { + u32 *scanline = &bitmap.pix(y + dispoffs); + for (int x = 0; x < 640; x++) + { + u16 const pixels = vram16[(y * 640) + x]; + *scanline++ = rgb_t(((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, (pixels & 0x1f) << 3); + } + } + } + break; + } + + return 0; +} + +u32 csc_device::vram_r(offs_t offset) +{ + return m_vram[offset & 0x7ffff]; +} + +void csc_device::vram_w(offs_t offset, u32 data, u32 mem_mask) +{ + COMBINE_DATA(&m_vram[offset & 0x7ffff]); +} diff --git a/src/mame/apple/csc.h b/src/mame/apple/csc.h new file mode 100644 index 00000000000..cefc305fdf2 --- /dev/null +++ b/src/mame/apple/csc.h @@ -0,0 +1,55 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont + +#ifndef MAME_APPLE_CSC_H +#define MAME_APPLE_CSC_H + +#pragma once + +#include "emupal.h" +#include "screen.h" + +class csc_device : public device_t +{ +public: + csc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + virtual ~csc_device() = default; + + auto write_irq() { return m_irq.bind(); } + + void map(address_map &map) ATTR_COLD; + + void set_panel_id(int panel_id); + void set_pmu_blank(bool blank) { m_pmu_blank_display = blank; } + +protected: + // device_t overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + +private: + required_device m_screen; + required_device m_palette; + devcb_write_line m_irq; + + std::unique_ptr m_vram; + + u8 m_csc_regs[0x50]; + u8 m_csc_panel_id; + int m_pal_idx; + bool m_pmu_blank_display; + + u32 screen_update_csc(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + + u8 csc_r(offs_t offset); + void csc_w(offs_t offset, u8 data); + void csc_irq_w(int state); + + u32 vram_r(offs_t offset); + void vram_w(offs_t offset, u32 data, u32 mem_mask); +}; + +DECLARE_DEVICE_TYPE(CSC, csc_device) + +#endif /* MAME_APPLE_CSC_H */ diff --git a/src/mame/apple/gsc.cpp b/src/mame/apple/gsc.cpp new file mode 100644 index 00000000000..06ecf7a9bd5 --- /dev/null +++ b/src/mame/apple/gsc.cpp @@ -0,0 +1,176 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Apple Gray Scale Controller (GSC) video, aka Chips & Technologies 65210 + Emulation by R. Belmont + + Reference: + https://github.com/elliotnunn/supermario/blob/master/base/SuperMarioProj.1994-02-09/Internal/Asm/HardwarePrivateEqu.a#L1201 + https://github.com/elliotnunn/supermario/tree/master/base/SuperMarioProj.1994-02-09/DeclData/DeclVideo/DBLite + + panel 4 = 640x480 JeDI (PowerBook 150) + panel 5 = 640x400 Dartanian (PowerBook 160/180) + panel 6 = 640x400 DBLite (PowerBook Duo 210/230/250) +*/ + +#include "emu.h" +#include "gsc.h" + +DEFINE_DEVICE_TYPE(GSC, gsc_device, "applegsc", "Apple Gray Scale Controller video") + +static constexpr u8 GSC_GRAYSCALE = 0x04; + +static constexpr u8 GSC_GS_DISPLAY_ENABLED = 5; +static constexpr u8 GSC_GS_MODE_MASK = 0x03; + +//------------------------------------------------- +// ADDRESS_MAP +//------------------------------------------------- + +void gsc_device::map(address_map &map) +{ + map(0x00f20000, 0x00f21fff).rw(FUNC(gsc_device::gsc_r), FUNC(gsc_device::gsc_w)); + map(0x10000000, 0x1001ffff).rw(FUNC(gsc_device::vram_r), FUNC(gsc_device::vram_w)).mirror(0x0ffe0000); +} + +gsc_device::gsc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, GSC, tag, owner, clock), + m_screen(*this, "screen"), + m_palette(*this, "palette") +{ + std::fill(std::begin(m_gsc_regs), std::end(m_gsc_regs), 0); +} + +void gsc_device::device_start() +{ + m_vram = std::make_unique(0x20000); + + save_item(NAME(m_gsc_regs)); + save_item(NAME(m_pmu_blank_display)); + save_pointer(NAME(m_vram), 0x20000); +} + +void gsc_device::device_reset() +{ + if (m_gsc_panel_id == 4) + { + m_screen->set_raw(25175000, 800, 0, 640, 525, 0, 480); + } +} + +void gsc_device::device_add_mconfig(machine_config &config) +{ + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + // these parameters are not real; we know the refresh rate is ~60.15 Hz and that's it + m_screen->set_raw(21604953, 800, 0, 640, 449, 0, 400); + + m_screen->set_palette(m_palette); + m_screen->set_screen_update(FUNC(gsc_device::screen_update_gsc)); + + PALETTE(config, m_palette, FUNC(gsc_device::macgsc_palette), 16); +} + +void gsc_device::macgsc_palette(palette_device &palette) const +{ + for (u8 i = 0; i < 16; i++) + { + palette.set_pen_color(i, ((15 - i) << 4) | 0xf, ((15 - i) << 4) | 0xf, ((15 - i) << 4) | 0xf); + } +} + +u32 gsc_device::screen_update_gsc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + auto const vram8 = util::big_endian_cast(&m_vram[0]); + int height = (m_gsc_panel_id == 4) ? 480 : 400; + + // is the display enabled? + if (!BIT(m_gsc_regs[GSC_GRAYSCALE], GSC_GS_DISPLAY_ENABLED) && !m_pmu_blank_display) + { + bitmap.fill(0xf, cliprect); + return 0; + } + + switch (m_gsc_regs[4] & GSC_GS_MODE_MASK) + { + case 0: + for (int y = 0; y < height; y++) + { + u16 *line = &bitmap.pix(y); + + for (int x = 0; x < 640; x += 8) + { + u8 const pixels = vram8[(y * 640 / 8) + (x / 8)]; + *line++ = ((pixels >> 7) & 1) ? 0xf : 0; + *line++ = ((pixels >> 6) & 1) ? 0xf : 0; + *line++ = ((pixels >> 5) & 1) ? 0xf : 0; + *line++ = ((pixels >> 4) & 1) ? 0xf : 0; + *line++ = ((pixels >> 3) & 1) ? 0xf : 0; + *line++ = ((pixels >> 2) & 1) ? 0xf : 0; + *line++ = ((pixels >> 1) & 1) ? 0xf : 0; + *line++ = (pixels & 1) ? 0xf : 0; + } + } + break; + + case 1: + for (int y = 0; y < height; y++) + { + u16 *line = &bitmap.pix(y); + + for (int x = 0; x < 640; x += 4) + { + u8 const pixels = vram8[(y * 640 / 4) + (x / 4)]; + *line++ = ((pixels >> 4) & 0xc); + *line++ = ((pixels >> 2) & 0xc); + *line++ = (pixels & 0xc); + *line++ = ((pixels << 2) & 0xc); + } + } + break; + + case 2: + for (int y = 0; y < height; y++) + { + u16 *line = &bitmap.pix(y); + + for (int x = 0; x < 640; x += 2) + { + u8 const pixels = vram8[(y * 640 / 2) + (x / 2)]; + *line++ = pixels >> 4; + *line++ = pixels & 0xf; + } + } + break; + } + return 0; +} + +void gsc_device::set_panel_id(int panel_id) +{ + m_gsc_panel_id = panel_id; +} + +u8 gsc_device::gsc_r(offs_t offset) +{ + if (offset == 1) + { + return m_gsc_panel_id; + } + + return m_gsc_regs[offset & 0x1f]; +} + +void gsc_device::gsc_w(offs_t offset, u8 data) +{ + m_gsc_regs[offset & 0x1f] = data; +} + +u32 gsc_device::vram_r(offs_t offset) +{ + return m_vram[offset & 0x1ffff]; +} + +void gsc_device::vram_w(offs_t offset, u32 data, u32 mem_mask) +{ + COMBINE_DATA(&m_vram[offset & 0x1ffff]); +} diff --git a/src/mame/apple/gsc.h b/src/mame/apple/gsc.h new file mode 100644 index 00000000000..c9ca1c1fa5e --- /dev/null +++ b/src/mame/apple/gsc.h @@ -0,0 +1,51 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont + +#ifndef MAME_APPLE_GSC_H +#define MAME_APPLE_GSC_H + +#pragma once + +#include "emupal.h" +#include "screen.h" + +class gsc_device : public device_t +{ +public: + gsc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + virtual ~gsc_device() = default; + + void map(address_map &map) ATTR_COLD; + + void set_panel_id(int panel_id); + void set_pmu_blank(bool blank) { m_pmu_blank_display = blank; } + +protected: + // device_t overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + +private: + required_device m_screen; + required_device m_palette; + + std::unique_ptr m_vram; + + u8 m_gsc_regs[0x20]; + u8 m_gsc_panel_id; + bool m_pmu_blank_display; + + u32 screen_update_gsc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + + u8 gsc_r(offs_t offset); + void gsc_w(offs_t offset, u8 data); + u32 vram_r(offs_t offset); + void vram_w(offs_t offset, u32 data, u32 mem_mask); + + void macgsc_palette(palette_device &palette) const; +}; + +DECLARE_DEVICE_TYPE(GSC, gsc_device) + +#endif /* MAME_APPLE_GSC_H */ diff --git a/src/mame/apple/macpwrbk030.cpp b/src/mame/apple/macpwrbk030.cpp index 64d3edf97df..258261ee79c 100644 --- a/src/mame/apple/macpwrbk030.cpp +++ b/src/mame/apple/macpwrbk030.cpp @@ -121,19 +121,12 @@ INT1: 60 Hz clock INT2: INT2 PULLUP (pulled up and otherwise N/C) - PG&E (68HC05 PMU) version spotting: - (find the text "BORG" in the system ROM, the next 32768 bytes are the PG&E image. - offset +4 in the image is the version byte). - 01 - PowerBook Duo 210/230/250 - 02 - PowerBook 540c, PBDuo 270C, PBDuo 280/280C - 03 - PowerBook 150 - 08 - PB190cs, PowerBook 540c PPC update, all PowerPC PowerBooks through WallStreet G3s - ****************************************************************************/ #include "emu.h" #include "dfac.h" +#include "gsc.h" #include "macadb.h" #include "macrtc.h" #include "macscsi.h" @@ -180,6 +173,7 @@ public: m_swim(*this, "fdc"), m_floppy(*this, "fdc:%d", 0U), m_rtc(*this, "rtc"), + m_gsc(*this, "gsc"), m_screen(*this, "screen"), m_palette(*this, "palette"), m_asc(*this, "asc"), @@ -212,7 +206,6 @@ public: m_ponti_SPI_SR(0), m_ponti_backlight_ctl(0) { - std::fill(std::begin(m_gsc_regs), std::end(m_gsc_regs), 0); } void macpb140(machine_config &config); @@ -224,11 +217,9 @@ public: void macpb170(machine_config &config); void macpb180(machine_config &config); void macpb180c(machine_config &config); - void macpd210(machine_config &config); void macpb140_map(address_map &map) ATTR_COLD; void macpb160_map(address_map &map) ATTR_COLD; void macpb165c_map(address_map &map) ATTR_COLD; - void macpd210_map(address_map &map) ATTR_COLD; private: required_device m_maincpu; @@ -242,8 +233,9 @@ private: required_device m_swim; required_device_array m_floppy; required_device m_rtc; - required_device m_screen; - required_device m_palette; + optional_device m_gsc; + optional_device m_screen; + optional_device m_palette; required_device m_asc; required_device m_scc; optional_shared_ptr m_vram, m_ext_vram; @@ -263,7 +255,6 @@ private: floppy_image_device *m_cur_floppy; int m_hdsel; - u8 m_gsc_regs[0x20]; bool m_pmu_blank_display; u8 m_pmu_from_via, m_pmu_to_via, m_pmu_ack, m_pmu_req; @@ -275,7 +266,6 @@ private: virtual void machine_reset() override ATTR_COLD; u32 screen_update_ddc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - u32 screen_update_gsc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); u32 screen_update_vga(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); u16 via_r(offs_t offset); @@ -311,10 +301,6 @@ private: void swim_w(offs_t offset, u16 data, u16 mem_mask); u32 buserror_r(); - u8 gsc_r(offs_t offset); - void gsc_w(offs_t offset, u8 data); - void macgsc_palette(palette_device &palette) const; - u32 jaws_r(offs_t offset, u32 mem_mask); void jaws_w(offs_t offset, u32 data, u32 mem_mask); u32 niagra_r(offs_t offset, u32 mem_mask); @@ -342,9 +328,6 @@ private: u8 battery2_r(); u8 battery3_r(); u8 brightness_r(); - - // ID for PowerBook Duo 210 - u32 pd210_id_r() { return 0xa55a1004; } }; void macpb030_state::machine_start() @@ -590,12 +573,12 @@ void macpb030_state::niagra_w(offs_t offset, u32 data, u32 mem_mask) u16 macpb030_state::pangola_r() { return m_pangola_data; + // TODO: trace pins, 0x13 -> 0x17 -> 0x16 sequence written before waking up VGA core } void macpb030_state::pangola_w(u16 data) { m_pangola_data = data; - // TODO: trace pins, 0x13 -> 0x17 -> 0x16 sequence written before waking up VGA core } u8 macpb030_state::pangola_vram_r(offs_t offset) @@ -753,7 +736,6 @@ u8 macpb030_state::pmu_comms_r() return (m_pmu_req << 7); } -u8 last_comms = 0xff; void macpb030_state::pmu_comms_w(u8 data) { if (!BIT(data, 1)) @@ -767,10 +749,7 @@ void macpb030_state::pmu_comms_w(u8 data) space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr); m_overlay = true; } - if ((data & 3) != (last_comms & 3)) - { - last_comms = data; - } + m_maincpu->set_input_line(INPUT_LINE_HALT, BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE); m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 1) ? CLEAR_LINE : ASSERT_LINE); @@ -791,14 +770,9 @@ void macpb030_state::pmu_p4_w(u8 data) { m_macadb->adb_linechange_w((data & 1) ^ 1); m_pmu_blank_display = BIT(data, 2) ^ 1; -} - -// 16-level grayscale -void macpb030_state::macgsc_palette(palette_device &palette) const -{ - for (u8 i = 0; i < 16; i++) + if (m_gsc) { - palette.set_pen_color(i, ((15 - i) << 4) | 0xf, ((15 - i) << 4) | 0xf, ((15 - i) << 4) | 0xf); + m_gsc->set_pmu_blank(m_pmu_blank_display); } } @@ -947,72 +921,6 @@ u32 macpb030_state::screen_update_ddc(screen_device &screen, bitmap_ind16 &bitma return 0; } -u32 macpb030_state::screen_update_gsc(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) -{ - u8 const *const vram8 = (u8 *)m_vram.target(); - - // is the display enabled? - if (!(m_gsc_regs[4] & 0x20) || m_pmu_blank_display) - { - bitmap.fill(0xf, cliprect); - return 0; - } - - switch (m_gsc_regs[4] & 3) - { - case 0: - for (int y = 0; y < 400; y++) - { - u16 *line = &bitmap.pix(y); - - for (int x = 0; x < 640; x += 8) - { - u8 const pixels = vram8[(y * 640 / 8) + (BYTE4_XOR_BE(x / 8))]; - *line++ = ((pixels >> 7) & 1) ? 0xf : 0; - *line++ = ((pixels >> 6) & 1) ? 0xf : 0; - *line++ = ((pixels >> 5) & 1) ? 0xf : 0; - *line++ = ((pixels >> 4) & 1) ? 0xf : 0; - *line++ = ((pixels >> 3) & 1) ? 0xf : 0; - *line++ = ((pixels >> 2) & 1) ? 0xf : 0; - *line++ = ((pixels >> 1) & 1) ? 0xf : 0; - *line++ = (pixels & 1) ? 0xf : 0; - } - } - break; - - case 1: - for (int y = 0; y < 400; y++) - { - u16 *line = &bitmap.pix(y); - - for (int x = 0; x < 640; x += 4) - { - u8 const pixels = vram8[(y * 640 / 4) + (BYTE4_XOR_BE(x / 4))]; - *line++ = ((pixels >> 4) & 0xc); - *line++ = ((pixels >> 2) & 0xc); - *line++ = (pixels & 0xc); - *line++ = ((pixels << 2) & 0xc); - } - } - break; - - case 2: - for (int y = 0; y < 400; y++) - { - u16 *line = &bitmap.pix(y); - - for (int x = 0; x < 640; x += 2) - { - u8 const pixels = vram8[(y * 640 / 2) + (BYTE4_XOR_BE(x / 2))]; - *line++ = pixels >> 4; - *line++ = pixels & 0xf; - } - } - break; - } - return 0; -} - u32 macpb030_state::screen_update_vga(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { if (m_pmu_blank_display) @@ -1179,21 +1087,6 @@ void macpb030_state::scsi_berr_w(u8 data) m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero); } -u8 macpb030_state::gsc_r(offs_t offset) -{ - if (offset == 1) - { - return 5; - } - - return m_gsc_regs[offset & 0x1f]; -} - -void macpb030_state::gsc_w(offs_t offset, u8 data) -{ - m_gsc_regs[offset & 0x1f] = data; -} - /*************************************************************************** ADDRESS MAPS ****************************************************************************/ @@ -1223,6 +1116,8 @@ void macpb030_state::macpb160_map(address_map &map) { map(0x40000000, 0x400fffff).r(FUNC(macpb030_state::rom_switch_r)).mirror(0x0ff00000); + map(0x50000000, 0x6fffffff).m(m_gsc, FUNC(gsc_device::map)); + map(0x50f00000, 0x50f01fff).rw(FUNC(macpb030_state::via_r), FUNC(macpb030_state::via_w)); map(0x50f02000, 0x50f03fff).rw(FUNC(macpb030_state::via2_r), FUNC(macpb030_state::via2_w)); map(0x50f04000, 0x50f05fff).rw(FUNC(macpb030_state::scc_r), FUNC(macpb030_state::scc_w)); @@ -1231,12 +1126,9 @@ void macpb030_state::macpb160_map(address_map &map) map(0x50f12060, 0x50f12063).r(FUNC(macpb030_state::scsi_drq_r)); map(0x50f14000, 0x50f15fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)); map(0x50f16000, 0x50f17fff).rw(FUNC(macpb030_state::swim_r), FUNC(macpb030_state::swim_w)); - map(0x50f20000, 0x50f21fff).rw(FUNC(macpb030_state::gsc_r), FUNC(macpb030_state::gsc_w)); map(0x50f24000, 0x50f27fff).r(FUNC(macpb030_state::buserror_r)); // bus error here to make sure we aren't mistaken for another decoder map(0x50f80000, 0x50fbffff).rw(FUNC(macpb030_state::niagra_r), FUNC(macpb030_state::niagra_w)); - map(0x60000000, 0x6001ffff).ram().share("vram").mirror(0x0ffe0000); - // external video on 160/180 map(0xfe0fe000, 0xfe0fe0ff).rw(FUNC(macpb030_state::ext_video_r), FUNC(macpb030_state::ext_video_w)); map(0xfe100000, 0xfe17ffff).ram().share("ext_vram"); @@ -1260,7 +1152,7 @@ void macpb030_state::macpb165c_map(address_map &map) // on-board color video on 165c/180c, presumably under ISA bus map(0xfc000000, 0xfc07ffff).rw(FUNC(macpb030_state::pangola_vram_r), FUNC(macpb030_state::pangola_vram_w)).mirror(0x00380000); -// map(0xfc400102, 0xfc400102).w(wd90c26_vga_device::wakeup_w)); + //map(0xfc400102, 0xfc400102).w(wd90c26_vga_device::wakeup_w)); map(0xfc4003b0, 0xfc4003df).m(m_vga, FUNC(wd90c26_vga_device::io_map)); // TODO: trace $3d0 writes (doesn't belong to WD90C26 core, RAMDAC overlay?) map(0xfc4046e8, 0xfc4046e8).mirror(0x3000).w(m_vga, FUNC(wd90c26_vga_device::mode_setup_w)); @@ -1273,26 +1165,6 @@ void macpb030_state::macpb165c_map(address_map &map) map(0xfe100000, 0xfe17ffff).ram().share("ext_vram"); } -void macpb030_state::macpd210_map(address_map &map) -{ - map(0x40000000, 0x400fffff).r(FUNC(macpb030_state::rom_switch_r)).mirror(0x0ff00000); - - map(0x50f00000, 0x50f01fff).rw(FUNC(macpb030_state::via_r), FUNC(macpb030_state::via_w)); - map(0x50f02000, 0x50f03fff).rw(FUNC(macpb030_state::via2_r), FUNC(macpb030_state::via2_w)); - map(0x50f04000, 0x50f05fff).rw(FUNC(macpb030_state::scc_r), FUNC(macpb030_state::scc_w)); - map(0x50f06000, 0x50f07fff).rw(FUNC(macpb030_state::scsi_drq_r), FUNC(macpb030_state::scsi_drq_w)); - map(0x50f10000, 0x50f11fff).rw(FUNC(macpb030_state::scsi_r), FUNC(macpb030_state::scsi_w)); - map(0x50f12060, 0x50f12063).r(FUNC(macpb030_state::scsi_drq_r)); - map(0x50f14000, 0x50f15fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)); - map(0x50f16000, 0x50f17fff).rw(FUNC(macpb030_state::swim_r), FUNC(macpb030_state::swim_w)); - map(0x50f20000, 0x50f21fff).rw(FUNC(macpb030_state::gsc_r), FUNC(macpb030_state::gsc_w)); - map(0x50f24000, 0x50f27fff).r(FUNC(macpb030_state::buserror_r)); // bus error here to make sure we aren't mistaken for another decoder - - map(0x5ffffffc, 0x5fffffff).r(FUNC(macpb030_state::pd210_id_r)); - - map(0x60000000, 0x6001ffff).ram().share("vram").mirror(0x0ffe0000); -} - u8 macpb030_state::via_in_a() { return 0x81 | 0x12; // ID for 140/160 @@ -1404,7 +1276,7 @@ void macpb030_state::macpb140(machine_config &config) applefdintf_device::add_35_nc(config, m_floppy[1]); NSCSI_BUS(config, "scsi"); - NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, "harddisk"); NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr); NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr); NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config( @@ -1415,7 +1287,7 @@ void macpb030_state::macpb140(machine_config &config) }); NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr); NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr); - NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk"); + NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, nullptr); NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) { ncr53c80_device &adapter = downcast(*device); adapter.irq_handler().set(m_pseudovia, FUNC(pseudovia_device::scsi_irq_w)); @@ -1499,8 +1371,11 @@ void macpb030_state::macpb160(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpb160_map); m_maincpu->set_fpu_enable(false); - PALETTE(config.replace(), m_palette, FUNC(macpb030_state::macgsc_palette), 16); - m_screen->set_screen_update(FUNC(macpb030_state::screen_update_gsc)); + config.device_remove("screen"); + config.device_remove("palette"); + + GSC(config, m_gsc, 31.3344_MHz_XTAL); + m_gsc->set_panel_id(5); m_ram->set_extra_options("4M,6M,8M,12M,14M"); } @@ -1520,13 +1395,14 @@ void macpb030_state::macpb180(machine_config &config) void macpb030_state::macpb165c(machine_config &config) { - macpb180(config); + macpb140(config); + m_maincpu->set_clock(33_MHz_XTAL); + m_maincpu->set_fpu_enable(true); m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpb165c_map); m_screen->set_raw(25.175_MHz_XTAL, 800, 0, 640, 524, 0, 480); m_screen->set_screen_update(FUNC(macpb030_state::screen_update_vga)); m_screen->set_no_palette(); - m_screen->set_type(SCREEN_TYPE_LCD); WD90C26(config, m_vga, 0); m_vga->set_screen(m_screen); @@ -1542,16 +1418,6 @@ void macpb030_state::macpb180c(machine_config &config) m_vga->read_cnf15_callback().set_constant(0); } -void macpb030_state::macpd210(machine_config &config) -{ - macpb160(config); - m_maincpu->set_addrmap(AS_PROGRAM, &macpb030_state::macpd210_map); - - M50753(config.replace(), m_pmu, 3.93216_MHz_XTAL).set_disable(); - - m_ram->set_extra_options("8M,12M,16M,20M,24M"); -} - ROM_START(macpb140) ROM_REGION32_BE(0x100000, "bootrom", 0) ROM_LOAD("420dbff3.rom", 0x000000, 0x100000, CRC(88ea2081) SHA1(7a8ee468d16e64f2ad10cb8d1a45e6f07cc9e212)) @@ -1588,13 +1454,6 @@ ROM_END #define rom_macpb165c rom_macpb180c -ROM_START(macpd210) - ROM_REGION32_BE(0x100000, "bootrom", 0) - ROM_LOAD("ecfa989b.rom", 0x000000, 0x100000, CRC(b86ed854) SHA1(ed1371c97117a5884da4a6605ecfc5abed48ae5a)) - - ROM_REGION(0x1800, "pmu", ROMREGION_ERASE00) -ROM_END - } // anonymous namespace @@ -1607,6 +1466,3 @@ COMP(1993, macpb165, macpb160, 0, macpb165, macadb, macpb030_state, empty_init, COMP(1993, macpb165c, macpb180c, 0, macpb165c, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 165c", MACHINE_SUPPORTS_SAVE) COMP(1992, macpb180, macpb160, 0, macpb180, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 180", MACHINE_SUPPORTS_SAVE) COMP(1993, macpb180c, 0, 0, macpb180c, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook 180c", MACHINE_SUPPORTS_SAVE) - -// PowerBook Duos (probably will not belong in this driver ultimately) -COMP(1992, macpd210, 0, 0, macpd210, macadb, macpb030_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 210", MACHINE_NOT_WORKING) diff --git a/src/mame/apple/macpwrbkmsc.cpp b/src/mame/apple/macpwrbkmsc.cpp new file mode 100644 index 00000000000..dfc23ab452e --- /dev/null +++ b/src/mame/apple/macpwrbkmsc.cpp @@ -0,0 +1,937 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/**************************************************************************** + + macpwrbkmsc.cpp + 68K Mac PowerBooks based on the MSC/MSC II system ASICs and the PG&E power manager + By R. Belmont + + Supported machines: + PowerBook Duo 210: 68030 @ 25 MHz, 640x400 passive-matrix 4bpp grayscale screen, 4 MiB RAM (24 max) + PowerBook Duo 230: 68030 @ 33 MHz, 640x400 passive-matrix 4bpp grayscale screen, 4 MiB RAM (24 max) + PowerBook Duo 250: 68030 @ 33 MHz, 640x400 active-matrix 4bpp grayscale screen, 4 MiB RAM (24 max) + PowerBook Duo 270c: 68030 @ 33 MHz, FPU, 640x480 active-matrix 16bpp color screen, 4 MiB RAM (32 max) + PowerBook Duo 280: 68040 @ 33 MHz, 640x480 active-matrix 4bpp grayscale screen, 4 MiB RAM (40 max) + PowerBook Duo 280c: 68040 @ 33 MHz, 640x480 active-matrix 16bpp color screen, 4 MiB RAM (40 max) + + Future: + PowerBook 150: '030 @ 33 MHz, 640x480 grayscale screen, 4 MiB RAM (40 max), IDE HDD, ADB trackpad, PG&E matrix keyboard + + ============================================================================ + Technical info + + Pseudo-VIA2 Port B bits 1 and 2 are /PMU_ACK and /PMU_REQ, respectively. + Main PMU comms are through the VIA shifter, but using a hardware SPI block + on the PG&E end instead of the 68HC05 losing cycles doing bit-banging. + + Brightness: PLM 1 7F (all the way down) to 26 (all the way up) + PLM 2 01 " " " " to 5A + Total of the 2 PLM timers is always 0x80. Timer 1 is off period, timer 2 is on period. + + PWM A0 - charging current control + PWM B0 - screen contrast, 0x33 to 0xc5 range + + Temperature sensors read on a non-linear scale, probably a commercial part. + Here are selected points from the lookup table the 68HC05 uses to convert it. + + Sensor val Temperature (Celsius) + --------------------------------- + 0 - 9: invalid (high) + 10 115 + 20 92 + 30 78 + 40 68 + 50 60 + 60 54 + 70 49 + 80 44 + 90 39 + 100 35 + 110 31 + 120 28 + 130 24 + 140 21 + 150 17 + 160 14 + 170 10 + 180 6 + 190 2 + 195 & 196 0 + 200 -2 + 210 -7 + 220 -12 + 230 -19 + 240 -28 + 250 -48 + 252 -55 + 253+ invalid (low) +****************************************************************************/ + +#include "emu.h" + +#include "csc.h" +#include "dfac.h" +#include "gsc.h" +#include "macscsi.h" +#include "mactoolbox.h" +#include "msc.h" + +#include "bus/nscsi/cd.h" +#include "bus/nscsi/devices.h" +#include "cpu/m6805/m68hc05pge.h" +#include "cpu/m68000/m68030.h" +#include "cpu/m68000/m68040.h" +#include "machine/ds2401.h" +#include "machine/ram.h" +#include "machine/timer.h" +#include "machine/z80scc.h" +#include "machine/ncr5380.h" +#include "machine/nscsi_bus.h" +#include "bus/nscsi/devices.h" + +#include "softlist_dev.h" +#include "speaker.h" +#include "utf8.h" + +namespace { +class macpbmsc_state : public driver_device +{ +public: + macpbmsc_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_pmu(*this, "pge"), + m_msc(*this, "msc"), + m_dfac(*this, "dfac"), + m_ncr5380(*this, "scsi:7:ncr5380"), + m_scsihelp(*this, "scsihelp"), + m_ram(*this, RAM_TAG), + m_gsc(*this, "gsc"), + m_csc(*this, "csc"), + m_scc(*this, "scc"), + m_battserial(*this, "ds2400"), + m_mouse0(*this, "MOUSE0"), + m_mouse1(*this, "MOUSE1"), + m_mouse2(*this, "MOUSE2"), + m_keys(*this, "Y%u", 0), + m_kbspecial(*this, "keyb_special"), + m_ca1_data(0), + m_cb1_data(0), + m_pmu_blank_display(true), + m_portc(0), + m_last_porte(0xff), + m_last_portf(0xff), + m_last_portg(0xff), + m_last_porth(0x00), // bit 0 must start as 0 for the PG&E bootrom to configure the DFAC + m_last_portl(0xff), + m_lastmousex(0), m_lastmousey(0), m_lastbutton(0), + m_mouseX(0), m_mouseY(0), + m_matrix_row(0) + { + } + + void macpd2xx_base_map(address_map &map) ATTR_COLD; + void macpd210(machine_config &config); + void macpd210_map(address_map &map) ATTR_COLD; + void macpd230(machine_config &config); + void macpd230_map(address_map &map) ATTR_COLD; + void macpd250(machine_config &config); + void macpd250_map(address_map &map) ATTR_COLD; + void macpd270c(machine_config &config); + void macpd270c_map(address_map &map) ATTR_COLD; + void macpd280(machine_config &config); + void macpd280c(machine_config &config); + void macpd280_map(address_map &map) ATTR_COLD; + +private: + required_device m_maincpu; + required_device m_pmu; + required_device m_msc; + required_device m_dfac; + required_device m_ncr5380; + required_device m_scsihelp; + required_device m_ram; + optional_device m_gsc; + optional_device m_csc; + required_device m_scc; + required_device m_battserial; + required_ioport m_mouse0, m_mouse1, m_mouse2; + required_ioport_array<8> m_keys; + required_ioport m_kbspecial; + int m_ca1_data; + int m_cb1_data; + + bool m_pmu_blank_display; + + u8 m_portc, m_last_porte, m_last_portf, m_last_portg, m_last_porth, m_last_portl; + + s32 m_lastmousex, m_lastmousey, m_lastbutton; + u8 m_mouseX, m_mouseY; + u8 m_matrix_row; + + virtual void machine_start() override ATTR_COLD; + virtual void machine_reset() override ATTR_COLD; + + void scc_irq_w(int state); + void via_irq_w(int state); + + u16 scsi_r(offs_t offset, u16 mem_mask); + void scsi_w(offs_t offset, u16 data, u16 mem_mask); + u32 scsi_drq_r(offs_t offset, u32 mem_mask = ~0); + void scsi_drq_w(offs_t offset, u32 data, u32 mem_mask = ~0); + void scsi_berr_w(u8 data); + u16 scc_r(offs_t offset); + void scc_w(offs_t offset, u16 data); + void vbl_w(int state); + + u8 pmu_porta_r(); + u8 pmu_portb_r(); + u8 pmu_portc_r(); + void pmu_portc_w(u8 data); + u8 pmu_portd_r(); + u8 pmu_porte_r(); + void pmu_porte_w(u8 data); + u8 pmu_portf_r(); + void pmu_portf_w(u8 data); + u8 pmu_portg_r(); + void pmu_portg_w(u8 data); + u8 pmu_porth_r(); + void pmu_porth_w(u8 data); + void pmu_portj_w(u8 data); + void pmu_portl_w(u8 data); + u8 pmu_read_mouseX(); + u8 pmu_read_mouseY(); + int pmu_read_mouseButton(); + u8 pmu_bat_low(); + u8 pmu_bat_high(); + u8 pmu_bat_current(); + u8 pmu_bat_temp(); + u8 pmu_ambient_temp(); +}; + +void macpbmsc_state::machine_start() +{ + m_msc->set_ram_info((u32 *)m_ram->pointer(), m_ram->size()); + + m_ca1_data = 0; + + save_item(NAME(m_ca1_data)); + save_item(NAME(m_cb1_data)); + save_item(NAME(m_pmu_blank_display)); + save_item(NAME(m_portc)); + save_item(NAME(m_last_porte)); + save_item(NAME(m_last_portf)); + save_item(NAME(m_last_portg)); + save_item(NAME(m_last_porth)); + save_item(NAME(m_last_portl)); + save_item(NAME(m_lastmousex)); + save_item(NAME(m_lastmousey)); + save_item(NAME(m_lastbutton)); + save_item(NAME(m_mouseX)); + save_item(NAME(m_mouseY)); + save_item(NAME(m_matrix_row)); +} + +void macpbmsc_state::machine_reset() +{ + m_ca1_data = 0; + m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); +} + +u16 macpbmsc_state::scc_r(offs_t offset) +{ + m_msc->via_sync(); + const u16 result = m_scc->dc_ab_r(offset); + return (result << 8) | result; +} + +void macpbmsc_state::scc_w(offs_t offset, u16 data) +{ + m_scc->dc_ab_w(offset, data >> 8); +} + +void macpbmsc_state::vbl_w(int state) +{ + int MouseCountX = 0, MouseCountY = 0; + int NewX, NewY; + + NewX = m_mouse1->read(); + NewY = m_mouse2->read(); + + // printf("pollmouse: X %d Y %d\n", NewX, NewY); + + /* see if it moved in the x coord */ + if (NewX != m_lastmousex) + { + int diff = (NewX - m_lastmousex); + + /* check for wrap */ + if (diff > 0x80) + diff = 0x100 - diff; + if (diff < -0x80) + diff = -0x100 - diff; + + MouseCountX += diff; + m_lastmousex = NewX; + } + + /* see if it moved in the y coord */ + if (NewY != m_lastmousey) + { + int diff = (NewY - m_lastmousey); + + /* check for wrap */ + if (diff > 0x80) + diff = 0x100 - diff; + if (diff < -0x80) + diff = -0x100 - diff; + + MouseCountY += diff; + m_lastmousey = NewY; + } + + m_lastbutton = m_mouse0->read() & 0x01; + m_mouseX = MouseCountX; + m_mouseY = MouseCountY; +// printf("X %02x Y %02x\n", m_mouseX, m_mouseY); +} + +u16 macpbmsc_state::scsi_r(offs_t offset, u16 mem_mask) +{ + const int reg = (offset >> 3) & 0xf; + const bool pseudo_dma = (reg == 6) && (offset >= 0x130); + + return m_scsihelp->read_wrapper(pseudo_dma, reg) << 8; +} + +void macpbmsc_state::scsi_w(offs_t offset, u16 data, u16 mem_mask) +{ + const int reg = (offset >> 3) & 0xf; + const bool pseudo_dma = (reg == 0) && (offset >= 0x100); + + m_scsihelp->write_wrapper(pseudo_dma, reg, data>>8); +} + +u32 macpbmsc_state::scsi_drq_r(offs_t offset, u32 mem_mask) +{ + switch (mem_mask) + { + case 0xff000000: + return m_scsihelp->read_wrapper(true, 6)<<24; + + case 0xffff0000: + return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16); + + case 0xffffffff: + return (m_scsihelp->read_wrapper(true, 6)<<24) | (m_scsihelp->read_wrapper(true, 6)<<16) | (m_scsihelp->read_wrapper(true, 6)<<8) | m_scsihelp->read_wrapper(true, 6); + + default: + logerror("scsi_drq_r: unknown mem_mask %08x\n", mem_mask); + } + + return 0; +} + +void macpbmsc_state::scsi_drq_w(offs_t offset, u32 data, u32 mem_mask) +{ + switch (mem_mask) + { + case 0xff000000: + m_scsihelp->write_wrapper(true, 0, data>>24); + break; + + case 0xffff0000: + m_scsihelp->write_wrapper(true, 0, data>>24); + m_scsihelp->write_wrapper(true, 0, data>>16); + break; + + case 0xffffffff: + m_scsihelp->write_wrapper(true, 0, data>>24); + m_scsihelp->write_wrapper(true, 0, data>>16); + m_scsihelp->write_wrapper(true, 0, data>>8); + m_scsihelp->write_wrapper(true, 0, data&0xff); + break; + + default: + logerror("scsi_drq_w: unknown mem_mask %08x\n", mem_mask); + break; + } +} + +void macpbmsc_state::scsi_berr_w(u8 data) +{ + m_maincpu->pulse_input_line(M68K_LINE_BUSERROR, attotime::zero); +} + +u8 macpbmsc_state::pmu_porta_r() +{ + if (m_portc == 0) // power key + { + return 0xdf | ((m_kbspecial->read() & 1) << 5); + } + + // matrix X0-X7 (bits 0-7) + return m_keys[m_matrix_row]->read() & 0xff; +} + +u8 macpbmsc_state::pmu_portb_r() +{ + // matrix X8-X10 (bits 0-2), modifiers (bits 3-7) + return (m_kbspecial->read() & 0xf8) | ((m_keys[m_matrix_row]->read() >> 8) & 7); +} + +u8 macpbmsc_state::pmu_portc_r() +{ + return m_portc ^ 0xff; +} + +void macpbmsc_state::pmu_portc_w(u8 data) +{ + m_portc = data ^ 0xff; + + // matrix row select + m_matrix_row = 0; + for (u8 i = 0; i < 8; i++) + { + if (BIT(m_portc, i)) + { + m_matrix_row = i; + return; + } + } +} + +// bit 4 = 1 for US keyboard, 0 for ISO +// bit 5 = 1 for sound power off +// bit 6 = 1 for docking station NOT present +// bit 7 = 1 for second mouse button NOT pressed +u8 macpbmsc_state::pmu_portd_r() +{ + return (1 << 7) | (1 << 6) | (1 << 4); // no docking station, US keyboard +} + +// bit 1 = screen power on/off +// bit 2 = MSC /reset +// bit 7 = data line for 1-wire Dallas comms with the battery +u8 macpbmsc_state::pmu_porte_r() +{ + if (!machine().side_effects_disabled()) + { + return (m_last_porte & 0x7f) | (m_battserial->read() << 7); + } + + return m_last_porte; +} + +void macpbmsc_state::pmu_porte_w(u8 data) +{ + if (BIT(data, 2) != BIT(m_last_porte, 2)) + { + if (BIT(data, 2)) + { + m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); + } + m_msc->pmu_reset_w(BIT(data, 2) ^ 1); + } + m_maincpu->set_input_line(INPUT_LINE_RESET, BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE); + if (BIT(data, 1) != BIT(m_last_porte, 1)) + { + m_pmu_blank_display = BIT(data, 1); + if (m_gsc) + { + m_gsc->set_pmu_blank(m_pmu_blank_display); + } + else if (m_gsc) + { + m_csc->set_pmu_blank(m_pmu_blank_display); + } + } + if (BIT(data, 7) != BIT(m_last_porte, 7)) + { + m_battserial->write(BIT(data, 7)); + } + m_last_porte = data; +} + +// bit 0 = Power (1 = off, 0 = on) +// bit 2 = 1 for +5V present when input, cause level 1 interrupt when output (VIA CB2?) +// bit 3 = clamshell open (1) or closed (0) +// bit 6 = /PMREQ +u8 macpbmsc_state::pmu_portf_r() +{ + u8 retval = (1 << 2); // indicate +5V present + retval |= (1 << 3); // indicate clamshell open + retval |= (m_msc->get_pmu_req() << 6); + return retval; +} + +u8 macpbmsc_state::pmu_bat_low() +{ + return 0xff; +} + +u8 macpbmsc_state::pmu_bat_high() +{ + return 0x7f; +} + +u8 macpbmsc_state::pmu_bat_current() +{ + return 0x40; +} + +u8 macpbmsc_state::pmu_bat_temp() +{ + return 131; // ~24 degrees C +} + +u8 macpbmsc_state::pmu_ambient_temp() +{ + return 131; // ~24 degrees C +} + +void macpbmsc_state::pmu_portf_w(u8 data) +{ + if (!BIT(data, 2) && BIT(m_last_portf, 2)) + { + m_msc->cb1_int_hack(ASSERT_LINE); + } + else if (BIT(data, 2) && !BIT(m_last_portf, 2)) + { + m_msc->cb1_int_hack(CLEAR_LINE); + } + + m_last_portf = data; +} + +// bit 3 = 1 for docking station powered up +// bit 4 = caps lock LED +// bit 5 = sleep LED +// bit 6 = charger present (1 = present) +u8 macpbmsc_state::pmu_portg_r() +{ + return (1 << 6); // indicate we're on a charger +} + +// bit 1 set turns on the main battery power +// bit 5 is sleep: 0 = normal operation, 1 = turn off 31.whatever MHz master clock +void macpbmsc_state::pmu_portg_w(u8 data) +{ + if (BIT(data, 5) != BIT(m_last_portg, 5)) + { + if (!BIT(data, 5)) + { + m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); + m_msc->pmu_reset_w(ASSERT_LINE); + m_msc->pmu_reset_w(CLEAR_LINE); + } + } + + m_last_portg = data; +} + +// bit 0 = DFAC reset +// bit 1 = sleep LED (270c, maybe 280/280c also?) +// bit 2 = NMI +// bit 5 = DFAC latch +// bit 6 = /PMACK +u8 macpbmsc_state::pmu_porth_r() +{ + return m_last_porth; +} + +void macpbmsc_state::pmu_porth_w(u8 data) +{ + m_dfac->latch_write(BIT(data, 5)); + m_msc->pmu_ack_w(BIT(data, 6)); + m_last_porth = data; +} + +// bit 6 = DFAC clock +// bit 7 = DFAC data +void macpbmsc_state::pmu_portj_w(u8 data) +{ + m_dfac->clock_write(BIT(data, 6)); + m_dfac->data_write(BIT(data, 7)); +} + +// bit 1 = main power to the CPU (1 = off, 0 = on) +void macpbmsc_state::pmu_portl_w(u8 data) +{ + m_last_portl = data; +} + +u8 macpbmsc_state::pmu_read_mouseX() +{ + return m_mouseX; +} + +u8 macpbmsc_state::pmu_read_mouseY() +{ + return m_mouseY; +} + +int macpbmsc_state::pmu_read_mouseButton() +{ + return m_lastbutton; +} + +/*************************************************************************** + ADDRESS MAPS +****************************************************************************/ + +void macpbmsc_state::macpd2xx_base_map(address_map &map) +{ + map(0x40000000, 0x600fffff).m(m_msc, FUNC(msc_device::map)); + + map(0x50f04000, 0x50f05fff).rw(FUNC(macpbmsc_state::scc_r), FUNC(macpbmsc_state::scc_w)); + map(0x50f06000, 0x50f07fff).rw(FUNC(macpbmsc_state::scsi_drq_r), FUNC(macpbmsc_state::scsi_drq_w)); + map(0x50f10000, 0x50f11fff).rw(FUNC(macpbmsc_state::scsi_r), FUNC(macpbmsc_state::scsi_w)); + map(0x50f12060, 0x50f12063).r(FUNC(macpbmsc_state::scsi_drq_r)); +} + +void macpbmsc_state::macpd210_map(address_map &map) +{ + macpd2xx_base_map(map); + + map(0x50000000, 0x6fffffff).m(m_gsc, FUNC(gsc_device::map)); + + map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1004; })); +} + +void macpbmsc_state::macpd230_map(address_map &map) +{ + macpd210_map(map); + + map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1005; })); +} + +void macpbmsc_state::macpd250_map(address_map &map) +{ + macpd210_map(map); + + map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1006; })); +} + +void macpbmsc_state::macpd270c_map(address_map &map) +{ + macpd2xx_base_map(map); + + map(0x50000000, 0x6fffffff).m(m_csc, FUNC(csc_device::map)); + + map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1002; })); +} + +void macpbmsc_state::macpd280_map(address_map &map) +{ + macpd270c_map(map); + + map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a1000; })); +} + +static INPUT_PORTS_START( dblite ) + PORT_START("MOUSE0") /* Mouse - button */ + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1) + + PORT_START("MOUSE1") /* Mouse - X AXIS */ + PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) + + PORT_START("MOUSE2") /* Mouse - Y AXIS */ + PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) + + PORT_START("Y0") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9) + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y1") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('\"') + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(10) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y2") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27) + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Brightness Up") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Brightness Down") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y3") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Contrast Down") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Contrast Up") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y4") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a') + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y5") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y6") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('#') PORT_CHAR(U'^') // (actually to the left of the return key on the ASDF row) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("Y7") + PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') + PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') + PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') + PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') + PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') + PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') + PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) + PORT_BIT(0x200, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x400, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("keyb_special") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Power") PORT_CODE(KEYCODE_F12) + + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Command") PORT_CODE(KEYCODE_LALT) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Option") PORT_CODE(KEYCODE_RALT) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE +INPUT_PORTS_END + +/*************************************************************************** + MACHINE DRIVERS +***************************************************************************/ + +void macpbmsc_state::macpd210(machine_config &config) +{ + M68030(config, m_maincpu, 25_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd210_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + m_maincpu->set_fpu_enable(false); + + M68HC05PGE(config, m_pmu, 4.194304_MHz_XTAL); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_porta_r)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_portb_r)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_portc_r)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_portc_w)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_portd_r)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_porte_r)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_porte_w)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_portf_r)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_portf_w)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_portg_r)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_portg_w)); + m_pmu->read_p().set(FUNC(macpbmsc_state::pmu_porth_r)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_porth_w)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_portj_w)); + m_pmu->write_p().set(FUNC(macpbmsc_state::pmu_portl_w)); + m_pmu->set_pullups(0xff); + m_pmu->set_pullups(0x80); // bit 7 of port E is the 1-Wire bus + m_pmu->ad_in<0>().set(FUNC(macpbmsc_state::pmu_bat_low)); + m_pmu->ad_in<1>().set(FUNC(macpbmsc_state::pmu_bat_high)); + m_pmu->ad_in<2>().set(FUNC(macpbmsc_state::pmu_bat_current)); + m_pmu->ad_in<3>().set(FUNC(macpbmsc_state::pmu_bat_temp)); + m_pmu->ad_in<4>().set(FUNC(macpbmsc_state::pmu_ambient_temp)); + m_pmu->spi_clock_callback().set(m_msc, FUNC(msc_device::cb1_w)); + m_pmu->spi_mosi_callback().set(m_msc, FUNC(msc_device::cb2_w)); + m_pmu->read_tbB().set(FUNC(macpbmsc_state::pmu_read_mouseButton)); + m_pmu->read_tbX().set(FUNC(macpbmsc_state::pmu_read_mouseX)); + m_pmu->read_tbY().set(FUNC(macpbmsc_state::pmu_read_mouseY)); + + MSC(config, m_msc, 31.3344_MHz_XTAL); + m_msc->set_maincpu_tag("maincpu"); + m_msc->set_pmu_tag("pge"); + m_msc->set_rom_tag("bootrom"); + m_msc->set_cpu_clock(25_MHz_XTAL); + m_msc->add_route(0, m_dfac, 1.0); + m_msc->add_route(1, m_dfac, 1.0); + m_msc->cb2_callback().set(m_pmu, FUNC(m68hc05pge_device::spi_miso_w)); + m_msc->vbl_callback().set(FUNC(macpbmsc_state::vbl_w)); + + APPLE_DFAC(config, m_dfac, 22257); + m_dfac->add_route(0, "lspeaker", 1.0); + m_dfac->add_route(1, "rspeaker", 1.0); + + GSC(config, m_gsc, 31.3344_MHz_XTAL); + m_gsc->set_panel_id(6); + + NSCSI_BUS(config, "scsi"); + NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, "harddisk"); + NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:3").option_set("cdrom", NSCSI_CDROM_APPLE).machine_config( + [](device_t *device) + { + device->subdevice("cdda")->add_route(0, "^^lspeaker", 1.0); + device->subdevice("cdda")->add_route(1, "^^rspeaker", 1.0); + }); + NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr5380", NCR53C80).machine_config([this](device_t *device) { + ncr53c80_device &adapter = downcast(*device); + adapter.irq_handler().set(m_msc, FUNC(msc_device::scsi_irq_w)); + adapter.drq_handler().set(m_scsihelp, FUNC(mac_scsi_helper_device::drq_w)); + }); + + MAC_SCSI_HELPER(config, m_scsihelp); + m_scsihelp->scsi_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::read)); + m_scsihelp->scsi_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::write)); + m_scsihelp->scsi_dma_read_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_r)); + m_scsihelp->scsi_dma_write_callback().set(m_ncr5380, FUNC(ncr53c80_device::dma_w)); + m_scsihelp->cpu_halt_callback().set_inputline(m_maincpu, INPUT_LINE_HALT); + m_scsihelp->timeout_error_callback().set(FUNC(macpbmsc_state::scsi_berr_w)); + + SCC85C30(config, m_scc, 31.3344_MHz_XTAL / 4); + m_scc->out_int_callback().set(m_msc, FUNC(msc_device::scc_irq_w)); + + DS2401(config, m_battserial, 0); // actually DS2400, but 2400/2401 are compatible + + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + + RAM(config, m_ram); + m_ram->set_default_size("4M"); + m_ram->set_extra_options("8M,12M,16M,24M"); + + SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd"); + SOFTWARE_LIST(config, "cd_list").set_original("mac_cdrom").set_filter("MC68030,MC68030_32"); +} + +void macpbmsc_state::macpd230(machine_config &config) +{ + macpd210(config); + m_maincpu->set_clock(33_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd230_map); + m_msc->set_cpu_clock(33_MHz_XTAL); +} + +void macpbmsc_state::macpd250(machine_config &config) +{ + macpd230(config); + m_maincpu->set_fpu_enable(true); + m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd250_map); +} + +void macpbmsc_state::macpd270c(machine_config &config) +{ + macpd230(config); + config.device_remove("gsc"); + + CSC(config, m_csc, 31.3344_MHz_XTAL); + m_csc->write_irq().set(m_msc, FUNC(msc_device::lcd_irq_w)); + m_csc->set_panel_id(0); + + m_maincpu->set_fpu_enable(true); + m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd270c_map); +} + +void macpbmsc_state::macpd280(machine_config &config) +{ + macpd270c(config); + + m_csc->set_panel_id(4); + + M68040(config.replace(), m_maincpu, 33_MHz_XTAL/2); + m_maincpu->set_addrmap(AS_PROGRAM, &macpbmsc_state::macpd280_map); +} + +void macpbmsc_state::macpd280c(machine_config &config) +{ + macpd280(config); + + m_csc->set_panel_id(0); +} + +ROM_START(macpd210) + ROM_REGION32_BE(0x100000, "bootrom", 0) + ROM_LOAD("ecfa989b.rom", 0x000000, 0x100000, CRC(b86ed854) SHA1(ed1371c97117a5884da4a6605ecfc5abed48ae5a)) + + // battery serial number, read from an embedded Dallas DS2400 + ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00) + ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) ) +ROM_END + +ROM_START(macpd270c) + ROM_REGION32_BE(0x100000, "bootrom", 0) + ROM_LOAD( "0024d346.rom", 0x000000, 0x100000, CRC(94c4d04a) SHA1(be7bd9637203e4513b896146ddfc85c37817d131) ) + + ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00) + ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) ) +ROM_END + +ROM_START(macpd280) + ROM_REGION32_BE(0x100000, "bootrom", 0) + ROM_LOAD("015621d7.rom", 0x000000, 0x100000, CRC(568d28eb) SHA1(d49dd69cf038784b8849793ad3c0e62c2d11f653)) + + ROM_REGION(0x8, "ds2400", ROMREGION_ERASE00) + ROM_LOAD( "duobatid.bin", 0x000000, 0x000008, CRC(7545c341) SHA1(61b094ee5b398077f70eaa1887921c8366f7abfe) ) +ROM_END + +#define rom_macpd230 rom_macpd210 +#define rom_macpd250 rom_macpd210 +#define rom_macpd280c rom_macpd280 + +} // anonymous namespace + +COMP(1992, macpd210, 0, 0, macpd210, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 210", MACHINE_SUPPORTS_SAVE) +COMP(1992, macpd230, macpd210, 0, macpd230, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 230", MACHINE_SUPPORTS_SAVE) +COMP(1993, macpd250, macpd210, 0, macpd250, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 250", MACHINE_SUPPORTS_SAVE) +COMP(1993, macpd270c, 0, 0, macpd270c, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 270c", MACHINE_SUPPORTS_SAVE) +COMP(1994, macpd280, 0, 0, macpd280, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 280", MACHINE_SUPPORTS_SAVE) +COMP(1994, macpd280c, macpd280, 0, macpd280c, dblite, macpbmsc_state, empty_init, "Apple Computer", "Macintosh PowerBook Duo 280c", MACHINE_SUPPORTS_SAVE) diff --git a/src/mame/apple/msc.cpp b/src/mame/apple/msc.cpp new file mode 100644 index 00000000000..b5c9aeff0d3 --- /dev/null +++ b/src/mame/apple/msc.cpp @@ -0,0 +1,508 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Apple "MSC" (Main System Controller) system ASIC + Emulation by R. Belmont + + MSC contains the following: + - A memory controller for up to 36MB (up to 4MB soldered and 32MB of SIMMs) + - A modified VIA (VIA1) and a "pseudo-VIA", which is basically a combination GPIO and + interrupt controller that looks somewhat like a VIA with no timers and no shift register. + - An EASC-like 4-channel audio controller + - Power management support functions + + MSC II is identical, but with a 68040 bus interface instead of 68030. +*/ + +#include "emu.h" +#include "msc.h" + +static constexpr u32 C7M = 7833600; +static constexpr u32 C15M = (C7M * 2); + +[[maybe_unused]] static constexpr u8 SOUND_POWER = 0; // 0 = DFAC power off, 1 = DFAC power on +static constexpr u8 SOUND_BUSY = 6; // 1 = ASC FIFO accessed since last read of this register +[[maybe_unused]] static constexpr u8 SOUND_LATCH = 7; // 1 = DFAC is powered up + +DEFINE_DEVICE_TYPE(MSC, msc_device, "msc", "Apple MSC system ASIC") +DEFINE_DEVICE_TYPE(MSC_VIA, mscvia_device, "mscvia", "Apple MSC integrated VIA") + +void msc_device::map(address_map &map) +{ + map(0x00000000, 0x000fffff).r(FUNC(msc_device::rom_switch_r)).mirror(0x0ff00000); + + map(0x10f00000, 0x10f01fff).rw(FUNC(msc_device::via_r), FUNC(msc_device::via_w)); + map(0x10f14000, 0x10f15fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)); + map(0x10f26000, 0x10f27fff).rw(FUNC(msc_device::via2_r), FUNC(msc_device::via2_w)); + map(0x10fa0000, 0x10fa0003).w(FUNC(msc_device::power_cycle_w)); +} + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void msc_device::device_add_mconfig(machine_config &config) +{ + MSC_VIA(config, m_via1, C7M / 10); + m_via1->readpa_handler().set(FUNC(msc_device::via_in_a)); + m_via1->readpb_handler().set(FUNC(msc_device::via_in_b)); + m_via1->writepa_handler().set(FUNC(msc_device::via_out_a)); + m_via1->writepb_handler().set(FUNC(msc_device::via_out_b)); + m_via1->cb2_handler().set(FUNC(msc_device::via_out_cb2)); + m_via1->irq_handler().set(FUNC(msc_device::via1_irq)); + + APPLE_PSEUDOVIA(config, m_pseudovia, C15M); + m_pseudovia->irq_callback().set(FUNC(msc_device::via2_irq)); + m_pseudovia->readpa_handler().set(FUNC(msc_device::via2_in_a)); + m_pseudovia->readpb_handler().set(FUNC(msc_device::via2_in_b)); + m_pseudovia->writepa_handler().set(FUNC(msc_device::via2_out_a)); + m_pseudovia->writepb_handler().set(FUNC(msc_device::via2_out_b)); + m_pseudovia->readmsc_handler().set(FUNC(msc_device::msc_pseudovia_r)); + m_pseudovia->writemsc_handler().set(FUNC(msc_device::msc_pseudovia_w)); + m_pseudovia->readvideo_handler().set(FUNC(msc_device::msc_config_r)); + m_pseudovia->writevideo_handler().set(FUNC(msc_device::msc_config_w)); + + ASC(config, m_asc, C15M, asc_device::asc_type::EASC); + m_asc->add_route(0, tag(), 1.0); + m_asc->add_route(1, tag(), 1.0); + m_asc->irqf_callback().set(m_pseudovia, FUNC(pseudovia_device::asc_irq_w)); +} + +mscvia_device::mscvia_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : via6522_device(mconfig, MSC_VIA, tag, owner, clock) +{ +} + +msc_device::msc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, MSC, tag, owner, clock), + device_sound_interface(mconfig, *this), + write_pb4(*this), + write_pb5(*this), + write_cb2(*this), + write_vbl(*this), + read_pb3(*this, 0), + m_maincpu(*this, finder_base::DUMMY_TAG), + m_pmu(*this, finder_base::DUMMY_TAG), + m_via1(*this, "via1"), + m_pseudovia(*this, "pseudovia"), + m_asc(*this, "asc"), + m_rom(*this, finder_base::DUMMY_TAG), + m_via_interrupt(0), + m_pmu_interrupt(0), + m_via2_interrupt(0), + m_scc_interrupt(0), + m_overlay(false), + m_pmu_req(1), + m_pmu_ack(1), + m_msc_config(0), + m_msc_clock_ctrl(0), + m_msc_sound_ctrl(0) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void msc_device::device_start() +{ + m_stream = stream_alloc(8, 2, m_asc->clock(), STREAM_SYNCHRONOUS); + + m_6015_timer = timer_alloc(FUNC(msc_device::msc_6015_tick), this); + m_6015_timer->adjust(attotime::never); + + save_item(NAME(m_via_interrupt)); + save_item(NAME(m_pmu_interrupt)); + save_item(NAME(m_via2_interrupt)); + save_item(NAME(m_scc_interrupt)); + save_item(NAME(m_overlay)); + save_item(NAME(m_last_taken_interrupt)); + save_item(NAME(m_pmu_ack)); + save_item(NAME(m_pmu_req)); + save_item(NAME(m_msc_config)); + save_item(NAME(m_msc_clock_ctrl)); + save_item(NAME(m_msc_sound_ctrl)); + + m_rom_ptr = &m_rom[0]; + m_rom_size = m_rom.length() << 2; + + // power management needs to know if the user is idle but sound is playing, so as not to put the machine to sleep + m_maincpu->space(AS_PROGRAM).install_write_tap(0x50f14000, 0x50f15fff, "snd_latch_mon", [this](offs_t offset, u32 &data, u32 mem_mask) + { + this->m_msc_sound_ctrl |= (1 << SOUND_BUSY); + }); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void msc_device::device_reset() +{ + // start 60.15 Hz timer + m_6015_timer->adjust(attotime::from_hz(60.15), 0, attotime::from_hz(60.15)); + + m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0; + m_last_taken_interrupt = -1; + + // main cpu shouldn't start until PMU wakes it up + m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); + + m_overlay = true; + + // put ROM mirror at 0 + address_space &space = m_maincpu->space(AS_PROGRAM); + const u32 memory_size = std::min((u32)0x3fffff, m_rom_size); + const u32 memory_end = memory_size - 1; + offs_t memory_mirror = memory_end & ~(memory_size - 1); + + space.unmap_write(0x00000000, memory_end); + space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr); +} + +void msc_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + for (int i = 0; i < inputs[0].samples(); i++) + { + outputs[0].put(i, inputs[0].get(i)); + outputs[1].put(i, inputs[1].get(i)); + } +} + +u32 msc_device::rom_switch_r(offs_t offset) +{ + // disable the overlay + if (m_overlay && !machine().side_effects_disabled()) + { + address_space &space = m_maincpu->space(AS_PROGRAM); + const u32 memory_end = m_ram_size - 1; + void *memory_data = m_ram_ptr; + offs_t memory_mirror = memory_end & ~memory_end; + + space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data); + m_overlay = false; + } + + return m_rom_ptr[offset & ((m_rom_size - 1) >> 2)]; +} + +void msc_device::set_cpu_clock(XTAL clock) +{ + m_cpu_clock = clock.value(); +} + +void msc_device::power_cycle_w(u32 data) +{ + // CPU power down control. Expects the CPU to reset after a short delay and resume. + // We make that no delay, since we don't care about saving power. + // Called with 0 for normal delay and resume, 0xffffffff for actual full system sleep on 210/230, + // ff000000 for full system sleep on later machines, and 5a000000 for power cycle. + if ((data == 0) || (data == 0x5a000000)) + { + m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + } + else + { + m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); + } +} + +u8 msc_device::msc_config_r() +{ + return m_msc_config; +} + +void msc_device::msc_config_w(u8 data) +{ + if (BIT(data, 1) != BIT(m_msc_clock_ctrl, 1)) + { + m_maincpu->set_clock(BIT(data, 1) ? clock() / 2 : m_cpu_clock); + } + + m_msc_config = data; +} + +u8 msc_device::msc_pseudovia_r(offs_t offset) +{ + switch (offset) + { + case 1: + return m_msc_clock_ctrl; + + case 2: + { + const u8 retval = m_msc_sound_ctrl; + m_msc_sound_ctrl &= ~(1 << SOUND_BUSY); + return retval; + } + } + + return 0; +} + +void msc_device::msc_pseudovia_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 1: + m_msc_clock_ctrl = data; + break; + + case 2: + m_msc_sound_ctrl = data; + break; + } +} + +void msc_device::set_ram_info(u32 *ram, u32 size) +{ + m_ram_ptr = ram; + m_ram_size = size; +} + +TIMER_CALLBACK_MEMBER(msc_device::msc_6015_tick) +{ + m_via1->write_ca1(CLEAR_LINE); + m_via1->write_ca1(ASSERT_LINE); + + write_vbl(ASSERT_LINE); +} + +u8 msc_device::via_in_a() +{ + return 0x07; +} + +u8 msc_device::via_in_b() +{ + return 0; +} + +void msc_device::via_out_cb2(int state) +{ + write_cb2(state & 1); +} + +void msc_device::via_out_a(u8 data) +{ +} + +void msc_device::via_out_b(u8 data) +{ +} + +void msc_device::via1_irq(int state) +{ + m_via_interrupt = state; + field_interrupts(); +} + +u8 msc_device::via2_r(offs_t offset) +{ + if (!machine().side_effects_disabled()) + { + via_sync(); + via_sync(); + via_sync(); + via_sync(); + } + + return m_pseudovia->read(offset); +} + +void msc_device::via2_w(offs_t offset, u8 data) +{ + via_sync(); + via_sync(); + via_sync(); + via_sync(); + m_pseudovia->write(offset, data); +} + +u8 msc_device::via2_in_a() +{ + return 0; +} + +u8 msc_device::via2_in_b() +{ + return ((m_pmu_ack & 1) << 1) | (m_pmu_req << 2); +} + +void msc_device::via2_out_a(u8 data) +{ +} + +void msc_device::via2_out_b(u8 data) +{ + if (m_pmu_req != BIT(data, 2)) + { + m_pmu_req = BIT(data, 2); + m_maincpu->spin_until_time(attotime::from_usec(80)); + } +} + +void msc_device::via2_irq(int state) +{ + m_via2_interrupt = state; + field_interrupts(); +} + +int msc_device::get_pmu_req() +{ + return m_pmu_req; +} + +void msc_device::pmu_ack_w(int state) +{ + if (m_pmu_ack != state) + { + m_pmu_ack = state; + m_pmu->spin_until_time(attotime::from_usec(20)); + } +} + +void msc_device::pmu_reset_w(int state) +{ + if (!state) + { + // put ROM mirror at 0 for reset + address_space &space = m_maincpu->space(AS_PROGRAM); + const u32 memory_size = std::min((u32)0x3fffff, m_rom_size); + const u32 memory_end = memory_size - 1; + offs_t memory_mirror = memory_end & ~(memory_size - 1); + + space.unmap_write(0x00000000, memory_end); + space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr); + m_overlay = true; + } + m_maincpu->set_input_line(INPUT_LINE_RESET, state); +} + +void msc_device::cb1_int_hack(int state) +{ + if (state) + { + m_via1->cb1_int_hack(ASSERT_LINE); + } +} + +void msc_device::field_interrupts() +{ + int take_interrupt = -1; + + if (m_scc_interrupt) + { + take_interrupt = 4; + } + else if (m_via2_interrupt) + { + take_interrupt = 2; + } + else if (m_pmu_interrupt && ((m_via1->read(via6522_device::VIA_IER) & 0x90) == 0x90)) + { + take_interrupt = 1; + } + else if (m_via_interrupt) + { + take_interrupt = 1; + } + + if (m_last_taken_interrupt > -1) + { + m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE); + m_last_taken_interrupt = -1; + } + + if (take_interrupt > -1) + { + m_maincpu->set_input_line(take_interrupt, ASSERT_LINE); + m_last_taken_interrupt = take_interrupt; + } +} + +void msc_device::scc_irq_w(int state) +{ + m_scc_interrupt = (state == ASSERT_LINE); + field_interrupts(); +} + +void msc_device::scsi_irq_w(int state) +{ + m_pseudovia->scsi_irq_w(state); +} + +void msc_device::slot0_irq_w(int state) +{ + m_pseudovia->slot_irq_w<0x08>(state); +} + +void msc_device::slot1_irq_w(int state) +{ + m_pseudovia->slot_irq_w<0x10>(state); +} + +void msc_device::slot2_irq_w(int state) +{ + m_pseudovia->slot_irq_w<0x20>(state); +} + +void msc_device::lcd_irq_w(int state) +{ + m_pseudovia->slot_irq_w<0x40>(state); +} + +void msc_device::cb1_w(int state) +{ + m_via1->write_cb1(state); +} + +void msc_device::cb2_w(int state) +{ + m_via1->write_cb2(state); +} + +u16 msc_device::via_r(offs_t offset) +{ + offset >>= 8; + offset &= 0x0f; + + if (!machine().side_effects_disabled()) + via_sync(); + + return m_via1->read(offset) << 8; +} + +void msc_device::via_w(offs_t offset, u16 data, u16 mem_mask) +{ + offset >>= 8; + offset &= 0x0f; + data >>= 8; + + via_sync(); + m_via1->write(offset, data & 0xff); +} + +void msc_device::via_sync() +{ + // The via runs at 783.36KHz while the main cpu runs at 15MHz or + // more, so we need to sync the access with the via clock. Plus + // the whole access takes half a (via) cycle and ends when synced + // with the main cpu again. + + // Get the main cpu time + u64 cycle = m_maincpu->total_cycles(); + + // Get the number of the cycle the via is in at that time + u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock(); + + // The access is going to start at via_cycle+1 and end at + // via_cycle+1.5, compute what that means in maincpu cycles (the + // +1 rounds up, since the clocks are too different to ever be + // synced). + u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1; + + // Finally adjust the main cpu icount as needed. + m_maincpu->adjust_icount(-int(main_cycle - cycle)); +} diff --git a/src/mame/apple/msc.h b/src/mame/apple/msc.h new file mode 100644 index 00000000000..1b51a4cc11a --- /dev/null +++ b/src/mame/apple/msc.h @@ -0,0 +1,128 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont + +#ifndef MAME_APPLE_MSC_H +#define MAME_APPLE_MSC_H + +#pragma once + +#include "pseudovia.h" + +#include "machine/6522via.h" +#include "sound/asc.h" +#include "speaker.h" + +class mscvia_device : public via6522_device +{ +public: + mscvia_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // Something is definitely customized with how PB1 interrupts work in the MSC's internal VIA1. + // This makes CPU/PMU comms work properly and fits with what we see in the leaked System 7.1 + // source tree, but I'm not sure it's the exact behavior of the real MSC. + void cb1_int_hack(int state) + { + if (state == ASSERT_LINE) + { + set_int(0x10); // INT_CB1 + } + } +}; + +class msc_device : public device_t, public device_sound_interface +{ +public: + msc_device(const machine_config &mconfig, const char *tag, device_t *owner) + : msc_device(mconfig, tag, owner, (uint32_t)0) + { + } + + msc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + auto pb4_callback() { return write_pb4.bind(); } + auto pb5_callback() { return write_pb5.bind(); } + auto cb2_callback() { return write_cb2.bind(); } + auto vbl_callback() { return write_vbl.bind(); } + auto pb3_callback() { return read_pb3.bind(); } + + void map(address_map &map) ATTR_COLD; + + template void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward(args)...); } + template void set_pmu_tag(T &&... args) { m_pmu.set_tag(std::forward(args)...); } + template void set_rom_tag(T &&... args) { m_rom.set_tag(std::forward(args)...); } + void set_ram_info(u32 *ram, u32 size); + void set_cpu_clock(XTAL clock); + + void cb1_w(int state); + void cb2_w(int state); + void scc_irq_w(int state); + void scsi_irq_w(int state); + void slot0_irq_w(int state); + void slot1_irq_w(int state); + void slot2_irq_w(int state); + void lcd_irq_w(int state); + void pmu_reset_w(int state); + + void via_sync(); + + int get_pmu_req(); + void pmu_ack_w(int state); + void cb1_int_hack(int state); + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + devcb_write_line write_pb4, write_pb5, write_cb2, write_vbl; + devcb_read_line read_pb3; + + required_device m_maincpu, m_pmu; + required_device m_via1; + required_device m_pseudovia; + required_device m_asc; + required_region_ptr m_rom; + + sound_stream *m_stream; + emu_timer *m_6015_timer; + u32 m_cpu_clock; + int m_via_interrupt, m_pmu_interrupt, m_via2_interrupt, m_scc_interrupt, m_last_taken_interrupt; + bool m_overlay; + u32 *m_ram_ptr, *m_rom_ptr; + u32 m_ram_size, m_rom_size; + u8 m_pmu_req, m_pmu_ack; + u8 m_msc_config, m_msc_clock_ctrl, m_msc_sound_ctrl; + + u32 rom_switch_r(offs_t offset); + void power_cycle_w(u32 data); + u8 msc_config_r(); + void msc_config_w(u8 data); + u8 msc_pseudovia_r(offs_t offset); + void msc_pseudovia_w(offs_t offset, u8 data); + + u16 via_r(offs_t offset); + void via_w(offs_t offset, u16 data, u16 mem_mask); + u8 via2_r(offs_t offset); + void via2_w(offs_t offset, u8 data); + + u8 via_in_a(); + u8 via_in_b(); + void via_out_a(u8 data); + void via_out_b(u8 data); + u8 via2_in_a(); + u8 via2_in_b(); + void via2_out_a(u8 data); + void via2_out_b(u8 data); + void field_interrupts(); + void via_out_cb2(int state); + void via1_irq(int state); + void via2_irq(int state); + TIMER_CALLBACK_MEMBER(msc_6015_tick); +}; + +DECLARE_DEVICE_TYPE(MSC, msc_device) + +#endif // MAME_APPLE_MSC_H diff --git a/src/mame/apple/pseudovia.cpp b/src/mame/apple/pseudovia.cpp index 92743e229f8..4474efb01ff 100644 --- a/src/mame/apple/pseudovia.cpp +++ b/src/mame/apple/pseudovia.cpp @@ -33,10 +33,12 @@ pseudovia_device::pseudovia_device(const machine_config &mconfig, const char *ta m_in_b_handler(*this, 0), m_in_config_handler(*this, 0), m_in_video_handler(*this, 0), + m_in_msc_handler(*this, 0), m_out_a_handler(*this), m_out_b_handler(*this), m_out_config_handler(*this), m_out_video_handler(*this), + m_out_msc_handler(*this), m_pseudovia_ier(0), m_pseudovia_ifr(0) { @@ -181,6 +183,11 @@ uint8_t pseudovia_device::read(offs_t offset) { data &= ~0x80; } + + if ((offset >= 0x20) && (offset <= 0x2f)) + { + data = m_in_msc_handler(offset & 0xf); + } } else { @@ -272,6 +279,10 @@ void pseudovia_device::write(offs_t offset, uint8_t data) break; default: + if ((offset >= 0x20) && (offset <= 0x2f)) + { + m_out_msc_handler(offset & 0xf, data); + } m_pseudovia_regs[offset] = data; break; } diff --git a/src/mame/apple/pseudovia.h b/src/mame/apple/pseudovia.h index 5887162736e..31154def859 100644 --- a/src/mame/apple/pseudovia.h +++ b/src/mame/apple/pseudovia.h @@ -16,10 +16,12 @@ public: auto readpb_handler() { return m_in_b_handler.bind(); } auto readconfig_handler() { return m_in_config_handler.bind(); } auto readvideo_handler() { return m_in_video_handler.bind(); } + auto readmsc_handler() { return m_in_msc_handler.bind(); } auto writepa_handler() { return m_out_a_handler.bind(); } auto writepb_handler() { return m_out_b_handler.bind(); } auto writeconfig_handler() { return m_out_config_handler.bind(); } auto writevideo_handler() { return m_out_video_handler.bind(); } + auto writemsc_handler() { return m_out_msc_handler.bind(); } auto irq_callback() { return m_write_irq.bind(); } uint8_t read(offs_t offset); @@ -38,8 +40,8 @@ protected: private: devcb_write_line m_write_irq; - devcb_read8 m_in_a_handler, m_in_b_handler, m_in_config_handler, m_in_video_handler; - devcb_write8 m_out_a_handler, m_out_b_handler, m_out_config_handler, m_out_video_handler; + devcb_read8 m_in_a_handler, m_in_b_handler, m_in_config_handler, m_in_video_handler, m_in_msc_handler; + devcb_write8 m_out_a_handler, m_out_b_handler, m_out_config_handler, m_out_video_handler, m_out_msc_handler; uint8_t m_pseudovia_regs[256], m_pseudovia_ier, m_pseudovia_ifr; diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 33d888c5f38..e8ea80c11c4 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -958,7 +958,14 @@ macpb165c macpb170 macpb180 macpb180c + +@source:apple/macpwrbkmsc.cpp macpd210 +macpd230 +macpd250 +macpd270c +macpd280 +macpd280c @source:apple/macquadra605.cpp macqd605