hp9845: handling of I/O slots added. Implemented HP98035 RTC card.

This commit is contained in:
fulivi 2016-10-12 16:29:02 +02:00
parent d030ab2755
commit 561ded4524
8 changed files with 1080 additions and 2 deletions

View File

@ -2911,6 +2911,18 @@ if (BUSES["HP_OPTROM"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/bus/hp9845_io/hp9845_io.h,BUSES["HP9845_IO"] = true
---------------------------------------------------
if (BUSES["HP9845_IO"]~=null) then
files {
MAME_DIR .. "src/devices/bus/hp9845_io/hp9845_io.cpp",
MAME_DIR .. "src/devices/bus/hp9845_io/98035.cpp"
}
end
---------------------------------------------------
--
--@src/devices/bus/compis/graphics.h,BUSES["COMPIS_GRAPHICS"] = true

View File

@ -650,6 +650,7 @@ BUSES["IQ151"] = true
BUSES["ISA"] = true
BUSES["ISBX"] = true
BUSES["HP_OPTROM"] = true
BUSES["HP9845_IO"] = true
BUSES["KC"] = true
BUSES["LPCI"] = true
BUSES["M5"] = true

View File

@ -0,0 +1,777 @@
// license:BSD-3-Clause
// copyright-holders: F. Ulivi
/*********************************************************************
98035.cpp
98035 module (Real time clock)
This module has two main functions: it is a battery-backed real
time clock and a timer/counter module.
It is based on a HP Nanoprocessor CPU having 2K of firmware ROM
and 256 bytes of RAM. This processor parses and executes commands
that are sent by a HP98xx system and returns the results. All
I/O happens (mostly) through ASCII strings.
A 1 MHz crystal provides both the clock to the CPU (it runs at 500 kHz)
and a periodic 1 kHz interrupt.
When main power is removed, time is counted by an unnamed "clock chip"
that is powered by a NiCd battery. It appears that HP had such an
hard time finding the clock chip that they had to adapt one
made for counting and displaying time on a 7-segment display. This
chip was probably manufactured for alarm clocks and/or wristwatches.
Nanoprocessor parses the digits on the "display" and translates
them into a standard month-day-hours-minutes-seconds time.
The biggest limit of the clock chip is that it lacks a counter
for current year (and so the module cannot recognize leap years).
For further info on this module see also:
http://www.hp9825.com/html/real-time_clock.html
This driver tries to reproduce in C++ the behaviour of the
nanoprocessor because I couldn't find any dump of the firmware ROM
(HP part-no 1818-0469).
The following commands are recognized in my "imitation":
A Halt all timer units
B Warm reset
E Read & clear interface errors
F Activate all timer units
R Read real time clock
S Set real time clock
T Read trigger status
U Control timer units
W Read lost interrupts
The main reference for this module is this manual:
HP, 98035A Real Time Clock Installation and Operation Manual
*********************************************************************/
#include "98035.h"
#include "coreutil.h"
// Debugging
#define VERBOSE 0
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
#define BIT_MASK(n) (1U << (n))
// Macros to clear/set single bits
#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n))
#define BIT_SET(w , n) ((w) |= BIT_MASK(n))
// Possible delimiters
#define DELIM_CH_0 '/'
#define DELIM_CH_1 '\n'
// Error masks in m_error
#define ERR_MASK_INT_MISSED BIT_MASK(0)
#define ERR_MASK_WRONG_INS BIT_MASK(1)
#define ERR_MASK_WRONG_UNIT BIT_MASK(2)
#define ERR_MASK_CANT_EXEC BIT_MASK(3)
// Empty date/time fields
#define EMPTY_FIELD 0xff
// Timers
enum {
MSEC_TMR_ID
};
hp98035_io_card::hp98035_io_card(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: hp9845_io_card_device(mconfig , HP98035_IO_CARD , "HP98035 card" , tag , owner , clock , "hp98035" , __FILE__)
{
}
hp98035_io_card::~hp98035_io_card()
{
}
static INPUT_PORTS_START(hp98035_port)
MCFG_HP9845_IO_SC
INPUT_PORTS_END
ioport_constructor hp98035_io_card::device_input_ports() const
{
return INPUT_PORTS_NAME(hp98035_port);
}
void hp98035_io_card::device_start()
{
m_msec_timer = timer_alloc(MSEC_TMR_ID);
}
void hp98035_io_card::device_reset()
{
hp9845_io_card_device::device_reset();
install_readwrite_handler(read16_delegate(FUNC(hp98035_io_card::reg_r) , this) , write16_delegate(FUNC(hp98035_io_card::reg_w) , this));
m_idr_full = false;
m_idr = 0;
m_odr = 0;
m_ibuffer_ptr = 0;
m_obuffer_len = 0;
m_obuffer_ptr = 0;
sts_w(true);
set_flg(true);
// Set real time from the real world
system_time systime;
machine().base_datetime(systime);
m_msec = 0;
m_sec = systime.local_time.second;
m_min = systime.local_time.minute;
m_hrs = systime.local_time.hour;
m_dom = systime.local_time.mday;
m_mon = systime.local_time.month + 1;
attotime period(attotime::from_msec(1));
m_msec_timer->adjust(period , 0 , period);
half_init();
}
void hp98035_io_card::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
if (id == MSEC_TMR_ID) {
// Update real time
m_msec++;
if (m_msec >= 1000) {
m_msec = 0;
m_sec++;
if (m_sec >= 60) {
m_sec = 0;
m_min++;
if (m_min >= 60) {
m_min = 0;
m_hrs++;
if (m_hrs >= 24) {
m_hrs = 0;
m_dom++;
// Use year 1: this RTC doesn't know the year and so it never advances
// the date to february 29th
if (m_dom > gregorian_days_in_month(m_mon , 1)) {
m_dom = 1;
m_mon++;
if (m_mon > 12) {
m_mon = 1;
}
}
}
}
}
// Every second: check if new real time matches in any active output unit
for (timer_unit_t& unit : m_units) {
if (!unit.m_input && unit.m_state == UNIT_ACTIVE &&
m_sec == unit.m_match_datetime[ 3 ] &&
(unit.m_match_datetime[ 2 ] == EMPTY_FIELD ||
(m_min == unit.m_match_datetime[ 2 ] &&
(unit.m_match_datetime[ 1 ] == EMPTY_FIELD ||
(m_hrs == unit.m_match_datetime[ 1 ] &&
(unit.m_match_datetime[ 0 ] == EMPTY_FIELD ||
m_dom == unit.m_match_datetime[ 0 ])))))) {
// Matched
unit.adv_state();
LOG(("matched %02u:%03u %p %d %u\n" , m_sec , m_msec , &unit , unit.m_state , unit.m_value));
}
}
}
// Every ms: advance active units
UINT8 triggered = 0;
for (timer_unit_t& unit : m_units) {
if (unit.m_state != UNIT_IDLE) {
if (unit.m_input && unit.m_state == UNIT_ACTIVE) {
// Input unit: it just counts msecs
// In the real 98035 there is a limit value of 1E+10: not simulated here
unit.m_value++;
} else if (!unit.m_input && unit.m_state == UNIT_WAIT_FOR_TO &&
(unit.m_value == 0 || --unit.m_value == 0)) {
// Output unit that is not waiting for real time match and that just reached timeout
// Triggered!
LOG(("triggered %02u:%03u %p\n" , m_sec , m_msec , &unit));
BIT_SET(triggered, unit.m_port - 1);
unit.adv_state();
if (unit.m_value == 0) {
LOG(("deact %p\n" , &unit));
unit.deactivate();
}
}
}
}
// Generate IRQ
if (triggered) {
// Store which output(s) triggered
m_triggered = triggered;
if (m_inten) {
m_irq = true;
update_irq();
LOG(("IRQ %02x\n" , m_triggered));
} else if (m_intflag) {
set_error(ERR_MASK_INT_MISSED);
// Record lost interrupts
m_lost_irq = triggered;
LOG(("lost %02x\n" , m_triggered));
}
}
}
}
READ16_MEMBER(hp98035_io_card::reg_r)
{
UINT16 res;
switch (offset) {
case 0:
// R4: ODR
res = m_odr;
break;
case 1:
// R5: Status register
res = 0x20;
if (m_inten) {
BIT_SET(res , 7);
}
if (m_intflag) {
BIT_SET(res , 1);
}
if (m_error) {
BIT_SET(res , 0);
}
break;
default:
res = 0;
break;
}
LOG(("read R%u=%04x\n" , offset + 4 , res));
return res;
}
WRITE16_MEMBER(hp98035_io_card::reg_w)
{
bool new_inten;
switch (offset) {
case 0:
// R4: IDR
m_idr = (UINT8)data;
m_idr_full = true;
update_ibuffer();
break;
case 1:
// R5: interrupt enable
new_inten = BIT(data , 7);
if (!m_inten && new_inten) {
m_intflag = true;
}
m_inten = new_inten;
update_irq();
break;
case 3:
// R7: trigger
set_flg(false);
update_ibuffer();
update_obuffer();
break;
}
LOG(("write R%u=%04x\n" , offset + 4 , data));
}
void hp98035_io_card::half_init(void)
{
m_inten = false;
m_intflag = false;
m_irq = false;
m_error = 0;
m_triggered = 0;
m_lost_irq = 0;
update_irq();
for (timer_unit_t& unit : m_units) {
unit.init();
}
// Unit 1 defaults to output 1
// Unit 2 defaults to input 1
m_units[ 0 ].m_input = false;
m_units[ 0 ].m_port = 1;
m_units[ 1 ].m_input = true;
m_units[ 1 ].m_port = 1;
set_obuffer('\n');
}
void hp98035_io_card::set_flg(bool value)
{
m_flg = value;
flg_w(m_flg);
}
void hp98035_io_card::update_irq(void)
{
if (!m_inten) {
m_irq = false;
}
irq_w(m_inten && m_irq);
}
void hp98035_io_card::update_ibuffer(void)
{
if (m_idr_full && !m_flg) {
// New input byte, put in ibuffer if it's a valid char (A-Z 0-9 = / \n)
if (m_idr == DELIM_CH_0 || m_idr == DELIM_CH_1) {
process_ibuffer();
} else if (((m_idr >= 'A' && m_idr <= 'Z') || (m_idr >= '0' && m_idr <= '9') || m_idr == '=') &&
m_ibuffer_ptr < HP98035_IBUFFER_LEN) {
m_ibuffer[ m_ibuffer_ptr++ ] = m_idr;
}
m_idr_full = false;
set_flg(true);
}
}
void hp98035_io_card::process_ibuffer(void)
{
m_ibuffer[ m_ibuffer_ptr ] = '\0';
const UINT8 *p = &m_ibuffer[ 0 ];
clear_obuffer();
bool get_out = false;
while (*p != '\0' && !get_out) {
std::ostringstream out;
UINT8 datetime[ 5 ];
unsigned unit_no;
switch (*p++) {
case 'A':
// Halt all timer units
for (timer_unit_t& unit : m_units) {
unit.deactivate();
}
m_inten = false;
m_intflag = false;
update_irq();
break;
case 'B':
// Warm reset
half_init();
get_out = true;
break;
case 'E':
// Read and clear errors
set_obuffer(m_error);
m_error = 0;
break;
case 'F':
// Activate all timer units
for (timer_unit_t& unit : m_units) {
if (unit.m_port) {
unit.adv_state(true);
}
}
break;
case 'R':
// Read time
// Assume US format of dates
util::stream_format(out , "%02u:%02u:%02u:%02u:%02u" , m_mon , m_dom , m_hrs , m_min , m_sec);
set_obuffer(out.str().c_str());
break;
case 'S':
// Set time
if (parse_datetime(p , datetime)) {
// Cannot set time when there's one or more active output units
if (std::any_of(std::begin(m_units) , std::end(m_units) , [](const timer_unit_t& u) { return u.m_state != UNIT_IDLE && !u.m_input; })) {
set_error(ERR_MASK_CANT_EXEC);
} else {
m_msec = 0;
m_sec = datetime[ 4 ];
if (datetime[ 3 ] != EMPTY_FIELD) {
m_min = datetime[ 3 ];
if (datetime[ 2 ] != EMPTY_FIELD) {
m_hrs = datetime[ 2 ];
if (datetime[ 1 ] != EMPTY_FIELD) {
m_dom = datetime[ 1 ];
if (datetime[ 0 ] != EMPTY_FIELD) {
m_mon = datetime[ 0 ];
}
}
}
}
}
} else {
set_error(ERR_MASK_WRONG_INS);
get_out = true;
}
break;
case 'T':
// Read triggered outputs
set_obuffer(m_triggered);
m_triggered = 0;
break;
case 'U':
// Control timer units
if (parse_unit_no(p , unit_no)) {
get_out = parse_unit_command(p , unit_no);
} else {
set_error(ERR_MASK_WRONG_INS);
get_out = true;
}
break;
case 'W':
// Read unserviced interrupts
set_obuffer(m_lost_irq);
m_lost_irq = 0;
break;
default:
set_error(ERR_MASK_WRONG_INS);
get_out = true;
break;
}
}
m_ibuffer_ptr = 0;
}
bool hp98035_io_card::assign_unit(timer_unit_t& unit , const UINT8*& p , bool input)
{
unsigned port_no;
if (parse_unit_no(p , port_no)) {
if (unit.m_state == UNIT_IDLE) {
unit.init();
if (std::any_of(std::begin(m_units) , std::end(m_units) , [input , port_no](const timer_unit_t& u) { return u.m_input == input && u.m_port == port_no; })) {
// I/O port already assigned
set_error(ERR_MASK_WRONG_UNIT);
return false;
}
unit.m_input = input;
unit.m_port = port_no;
} else {
set_error(ERR_MASK_CANT_EXEC);
}
return false;
} else {
set_error(ERR_MASK_WRONG_INS);
return true;
}
}
bool hp98035_io_card::parse_unit_command(const UINT8*& p, unsigned unit_no)
{
unit_no--;
timer_unit_t& unit = m_units[ unit_no ];
bool get_out = false;
unsigned msec;
UINT8 to_match[ 5 ];
std::ostringstream out;
LOG(("U %c %u %p\n" , *p , unit_no , &unit));
switch (*p++) {
case '=':
// Assign unit
if (*p == 'I') {
p++;
get_out = assign_unit(unit , p , true);
} else if (*p == 'O') {
p++;
get_out = assign_unit(unit , p , false);
}
break;
case 'C':
// Clear input unit
if (unit.m_input && unit.m_port) {
unit.m_value = 0;
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
break;
case 'D':
// Set delay on output unit
if (parse_msec(p , msec)) {
if (!unit.m_input && unit.m_port) {
if (unit.m_state == UNIT_IDLE) {
unit.m_delay = msec;
} else {
set_error(ERR_MASK_CANT_EXEC);
}
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
} else {
set_error(ERR_MASK_WRONG_INS);
}
get_out = true;
break;
case 'G':
// Activate unit
if (unit.m_port && unit.m_state == UNIT_IDLE) {
unit.adv_state(true);
LOG(("act %p %d %d %u %02u:%02u:%02u:%02u %u %u %u\n" , &unit , unit.m_state , unit.m_input , unit.m_port , unit.m_match_datetime[ 0 ] , unit.m_match_datetime[ 1 ] , unit.m_match_datetime[ 2 ] , unit.m_match_datetime[ 3 ] , unit.m_delay , unit.m_period , unit.m_value));
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
break;
case 'H':
// Halt unit
if (unit.m_port) {
unit.deactivate();
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
break;
case 'M':
// Set date/time to match on output unit
if (!unit.m_input && unit.m_port) {
if (unit.m_state == UNIT_IDLE) {
if (*p == '\0') {
unit.m_match_datetime[ 0 ] = EMPTY_FIELD;
unit.m_match_datetime[ 1 ] = EMPTY_FIELD;
unit.m_match_datetime[ 2 ] = EMPTY_FIELD;
unit.m_match_datetime[ 3 ] = EMPTY_FIELD;
} else if (parse_datetime(p , to_match) && *p == '\0') {
unit.m_match_datetime[ 0 ] = to_match[ 1 ];
unit.m_match_datetime[ 1 ] = to_match[ 2 ];
unit.m_match_datetime[ 2 ] = to_match[ 3 ];
unit.m_match_datetime[ 3 ] = to_match[ 4 ];
} else {
set_error(ERR_MASK_WRONG_INS);
}
} else {
set_error(ERR_MASK_CANT_EXEC);
}
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
get_out = true;
break;
case 'P':
// Set period on output unit
if (parse_msec(p , msec)) {
if (!unit.m_input && unit.m_port) {
if (unit.m_state == UNIT_IDLE) {
unit.m_period = msec;
} else {
set_error(ERR_MASK_CANT_EXEC);
}
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
} else {
set_error(ERR_MASK_WRONG_INS);
}
get_out = true;
break;
case 'V':
// Get value of input unit
if (unit.m_input && unit.m_port) {
util::stream_format(out , "%010u" , unit.m_value);
set_obuffer(out.str().c_str());
} else {
set_error(ERR_MASK_WRONG_UNIT);
}
break;
}
return get_out;
}
void hp98035_io_card::clear_obuffer(void)
{
m_obuffer_len = 0;
m_obuffer_ptr = 0;
}
void hp98035_io_card::set_obuffer(UINT8 b)
{
m_obuffer[ 0 ] = b;
m_obuffer_len = 1;
m_obuffer_ptr = 0;
}
void hp98035_io_card::set_obuffer(const char* s)
{
unsigned s_len = std::min((unsigned)strlen(s) , (unsigned)(HP98035_OBUFFER_LEN - 1));
memcpy(&m_obuffer[ 0 ] , s , s_len);
m_obuffer[ s_len++ ] = '\n';
m_obuffer_len = s_len;
m_obuffer_ptr = 0;
}
void hp98035_io_card::update_obuffer(void)
{
if (!m_idr_full && !m_flg && m_obuffer_ptr < m_obuffer_len) {
m_odr = m_obuffer[ m_obuffer_ptr++ ];
set_flg(true);
}
}
void hp98035_io_card::set_error(UINT8 mask)
{
m_error |= mask;
}
bool hp98035_io_card::parse_datetime(const UINT8*& p, UINT8 *out) const
{
unsigned n_fields = 0;
// Fill all fields with EMPTY_FIELD
for (unsigned i = 0; i < 5; i++) {
out[ i ] = EMPTY_FIELD;
}
while (n_fields < 5 && *p >= '0' && *p <= '9') {
unsigned tmp = *p - '0';
p++;
if (*p < '0' || *p > '9') {
return false;
}
// Shift all fields one position to the left
memmove(&out[ 0 ] , &out[ 1 ] , 4 * sizeof(out[ 0 ]));
// Put the new field in the last position
out[ 4 ] = (UINT8)(tmp * 10 + *p - '0');
n_fields++;
p++;
}
if (n_fields == 0) {
return false;
}
// Seconds
if (out[ 4 ] >= 60) {
return false;
}
if (n_fields == 1) {
return true;
}
// Minutes
if (out[ 3 ] >= 60) {
return false;
}
if (n_fields == 2) {
return true;
}
// Hours
if (out[ 2 ] >= 24) {
return false;
}
if (n_fields == 3) {
return true;
}
// Month
UINT8 month;
if (n_fields == 4) {
month = m_mon;
} else {
if (out[ 0 ] < 1 || out[ 0 ] > 12) {
return false;
}
month = out[ 0 ];
}
// Day of month
// Use year 0 here to allow for February 29th
if (out[ 1 ] < 1 || out[ 1 ] > gregorian_days_in_month(month , 0)) {
return false;
}
return true;
}
bool hp98035_io_card::parse_unit_no(const UINT8*& p, unsigned& unit) const
{
if (*p < '1' || *p > '0' + HP98035_UNIT_COUNT) {
return false;
}
unit = *p - '0';
do {
p++;
} while (*p >= '0' && *p <= '9');
return true;
}
bool hp98035_io_card::parse_msec(const UINT8*& p, unsigned& msec) const
{
msec = 0;
while (*p >= '0' && *p <= '9') {
msec = msec * 10 + *p - '0';
p++;
}
return *p == '\0';
}
void hp98035_io_card::timer_unit_t::init(void)
{
m_state = UNIT_IDLE;
m_port = 0;
m_match_datetime[ 0 ] = EMPTY_FIELD;
m_match_datetime[ 1 ] = EMPTY_FIELD;
m_match_datetime[ 2 ] = EMPTY_FIELD;
m_match_datetime[ 3 ] = EMPTY_FIELD;
m_delay = 0;
m_period = 0;
m_value = 0;
}
void hp98035_io_card::timer_unit_t::deactivate(void)
{
m_state = UNIT_IDLE;
}
void hp98035_io_card::timer_unit_t::adv_state(bool reset)
{
if (reset) {
m_state = UNIT_IDLE;
}
if (m_input) {
m_state = UNIT_ACTIVE;
m_value = 0;
} else {
if (m_state == UNIT_IDLE) {
m_state = UNIT_ACTIVE;
if (m_match_datetime[ 3 ] != EMPTY_FIELD) {
// Seconds field is not empty -> there's a date/time to be matched first
return;
}
}
if (m_state == UNIT_ACTIVE) {
m_value = m_delay;
} else {
m_value = m_period;
}
m_state = UNIT_WAIT_FOR_TO;
}
}
// device type definition
const device_type HP98035_IO_CARD = &device_creator<hp98035_io_card>;

View File

@ -0,0 +1,109 @@
// license:BSD-3-Clause
// copyright-holders: F. Ulivi
/*********************************************************************
98035.h
98035 module (Real time clock)
*********************************************************************/
#pragma once
#ifndef _98035_H_
#define _98035_H_
#include "hp9845_io.h"
#define HP98035_IBUFFER_LEN 16 // Totally arbitrary
#define HP98035_OBUFFER_LEN 16 // Totally arbitrary
#define HP98035_UNIT_COUNT 4 // Count of counter/timer units
class hp98035_io_card : public hp9845_io_card_device
{
public:
// construction/destruction
hp98035_io_card(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~hp98035_io_card();
// device-level overrides
virtual ioport_constructor device_input_ports() const override;
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
DECLARE_READ16_MEMBER(reg_r);
DECLARE_WRITE16_MEMBER(reg_w);
private:
// Interface state
bool m_flg;
bool m_inten;
bool m_intflag;
bool m_irq;
bool m_idr_full;
UINT8 m_idr; // Input Data Register
UINT8 m_odr; // Output Data Register
UINT8 m_error;
UINT8 m_triggered;
UINT8 m_lost_irq;
UINT8 m_ibuffer[ HP98035_IBUFFER_LEN + 1 ];
unsigned m_ibuffer_ptr;
UINT8 m_obuffer[ HP98035_OBUFFER_LEN ];
unsigned m_obuffer_len;
unsigned m_obuffer_ptr;
// Clock/timer state
unsigned m_msec; // Milliseconds
UINT8 m_sec; // Seconds
UINT8 m_min; // Minutes
UINT8 m_hrs; // Hours
UINT8 m_dom; // Day of month
UINT8 m_mon; // Month
// Strangely enough this RTC has no notion of current year
emu_timer *m_msec_timer;
// Timer units
typedef enum {
UNIT_IDLE, // Not active
UNIT_ACTIVE, // Active (output units: waiting for date/time match)
UNIT_WAIT_FOR_TO // Active, output units only: waiting for timeout
} unit_state_t;
typedef struct {
unit_state_t m_state; // State
bool m_input; // Input or output
UINT8 m_port; // Assigned port # (0 if not assigned)
UINT8 m_match_datetime[ 4 ]; // Date&time to match (month is not included)
unsigned m_delay; // Timer delay
unsigned m_period; // Timer period (when != 0)
unsigned m_value; // Current counter value
void init(void);
void deactivate(void);
void adv_state(bool reset = false);
} timer_unit_t;
timer_unit_t m_units[ HP98035_UNIT_COUNT ];
void half_init(void);
void set_flg(bool value);
void update_irq(void);
void update_ibuffer(void);
void process_ibuffer(void);
bool assign_unit(timer_unit_t& unit , const UINT8*& p , bool input);
bool parse_unit_command(const UINT8*& p, unsigned unit_no);
void clear_obuffer(void);
void set_obuffer(UINT8 b);
void set_obuffer(const char* s);
void update_obuffer(void);
void set_error(UINT8 mask);
bool parse_datetime(const UINT8*& p, UINT8 *out) const;
bool parse_unit_no(const UINT8*& p, unsigned& unit) const;
bool parse_msec(const UINT8*& p, unsigned& msec) const;
};
// device type definition
extern const device_type HP98035_IO_CARD;
#endif /* _98035_H_ */

View File

@ -0,0 +1,86 @@
// license:BSD-3-Clause
// copyright-holders: F. Ulivi
/*********************************************************************
hp9845_io.cpp
I/O bus of HP9845 systems
*********************************************************************/
#include "hp9845_io.h"
#include "includes/hp9845.h"
// device type definition
const device_type HP9845_IO_SLOT = &device_creator<hp9845_io_slot_device>;
// +---------------------+
// |hp9845_io_slot_device|
// +---------------------+
hp9845_io_slot_device::hp9845_io_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, HP9845_IO_SLOT, "HP9845 I/O Slot", tag, owner, clock, "hp9845_io_slot", __FILE__),
device_slot_interface(mconfig, *this)
{
//printf("hp9845_io_slot_device %s %p\n" , tag , this);
}
hp9845_io_slot_device::~hp9845_io_slot_device()
{
}
void hp9845_io_slot_device::device_start()
{
//printf("hp9845_io_slot_device::device_start\n");
}
// +---------------------+
// |hp9845_io_card_device|
// +---------------------+
hp9845_io_card_device::hp9845_io_card_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
device_t(mconfig, type, name, tag, owner, clock, shortname, source),
device_slot_card_interface(mconfig, *this),
m_sys(nullptr),
m_select_code_port(*this , "SC"),
m_my_sc(0)
{
}
hp9845_io_card_device::~hp9845_io_card_device()
{
}
void hp9845_io_card_device::device_reset()
{
m_my_sc = m_select_code_port->read() + HP9845_IO_FIRST_SC;
//printf("m_my_sc=%u\n" , m_my_sc);
}
void hp9845_io_card_device::irq_w(int state)
{
m_sys->irq_w(m_my_sc , state);
}
void hp9845_io_card_device::sts_w(int state)
{
m_sys->sts_w(m_my_sc , state);
}
void hp9845_io_card_device::flg_w(int state)
{
m_sys->flg_w(m_my_sc , state);
}
void hp9845_io_card_device::install_readwrite_handler(read16_delegate rhandler, write16_delegate whandler)
{
if (m_sys == nullptr) {
m_sys = dynamic_cast<hp9845b_state*>(&machine().root_device());
//printf("m_sys=%p\n" , m_sys);
m_sys->install_readwrite_handler(m_my_sc , rhandler, whandler);
}
}
#include "98035.h"
SLOT_INTERFACE_START(hp9845_io_slot_devices)
SLOT_INTERFACE("98035_rtc" , HP98035_IO_CARD)
SLOT_INTERFACE_END

View File

@ -0,0 +1,81 @@
// license:BSD-3-Clause
// copyright-holders: F. Ulivi
/*********************************************************************
hp9845_io.h
I/O bus of HP9845 systems
*********************************************************************/
#pragma once
#ifndef _HP9845_IO_H_
#define _HP9845_IO_H_
#include "emu.h"
#define MCFG_HP9845_IO_SLOT_ADD(_tag) \
MCFG_DEVICE_ADD(_tag, HP9845_IO_SLOT, 0) \
MCFG_DEVICE_SLOT_INTERFACE(hp9845_io_slot_devices, nullptr, false)
#define HP9845_IO_FIRST_SC 1 // Lowest SC used by I/O cards
#define MCFG_HP9845_IO_SC\
PORT_START("SC") \
PORT_CONFNAME(0xf , 0 , "Select Code") \
PORT_CONFSETTING(0 , "1")\
PORT_CONFSETTING(1 , "2")\
PORT_CONFSETTING(2 , "3")\
PORT_CONFSETTING(3 , "4")\
PORT_CONFSETTING(4 , "5")\
PORT_CONFSETTING(5 , "6")\
PORT_CONFSETTING(6 , "7")\
PORT_CONFSETTING(7 , "8")\
PORT_CONFSETTING(8 , "9")\
PORT_CONFSETTING(9 , "10")\
PORT_CONFSETTING(10 , "11")\
PORT_CONFSETTING(11 , "12")
class hp9845_io_slot_device : public device_t,
public device_slot_interface
{
public:
// construction/destruction
hp9845_io_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~hp9845_io_slot_device();
// device-level overrides
virtual void device_start() override;
};
class hp9845b_state;
class hp9845_io_card_device : public device_t,
public device_slot_card_interface
{
protected:
// construction/destruction
hp9845_io_card_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
virtual ~hp9845_io_card_device();
// device-level overrides
virtual void device_reset() override;
hp9845b_state *m_sys;
required_ioport m_select_code_port;
UINT8 m_my_sc;
// card device handling
void irq_w(int state);
void sts_w(int state);
void flg_w(int state);
void install_readwrite_handler(read16_delegate rhandler, write16_delegate whandler);
};
// device type definition
extern const device_type HP9845_IO_SLOT;
SLOT_INTERFACE_EXTERN(hp9845_io_slot_devices);
#endif /* _HP9845_IO_H_ */

View File

@ -257,7 +257,8 @@ hp9845b_state::hp9845b_state(const machine_config &mconfig, device_type type, co
m_io_key3(*this , "KEY3"),
m_t15(*this , "t15"),
m_beeper(*this , "beeper"),
m_beep_timer(*this , "beep_timer")
m_beep_timer(*this , "beep_timer"),
m_io_slot0(*this , "slot0")
{
}
@ -867,6 +868,12 @@ void hp9845b_state::flg_w(UINT8 sc , int state)
}
}
void hp9845b_state::install_readwrite_handler(UINT8 sc , read16_delegate rhandler, write16_delegate whandler)
{
// Install r/w handlers to cover all I/O addresses of PPU belonging to "sc" select code
m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler);
}
TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::kb_scan)
{
ioport_value input[ 4 ];
@ -1103,7 +1110,9 @@ static MACHINE_CONFIG_START( hp9845b, hp9845b_state )
MCFG_DEVICE_ADD("drawer8", HP_OPTROM_SLOT, 0)
MCFG_DEVICE_SLOT_INTERFACE(hp_optrom_slot_device, NULL, false)
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
MCFG_HP9845_IO_SLOT_ADD("slot0")
MACHINE_CONFIG_END
ROM_START( hp9845a )

View File

@ -10,6 +10,7 @@
#include "cpu/hphybrid/hphybrid.h"
#include "machine/hp_taco.h"
#include "sound/beep.h"
#include "bus/hp9845_io/hp9845_io.h"
class hp9845b_state : public driver_device
{
@ -40,6 +41,7 @@ public:
void update_flg_sts(void);
void sts_w(UINT8 sc , int state);
void flg_w(UINT8 sc , int state);
void install_readwrite_handler(UINT8 sc , read16_delegate rhandler, write16_delegate whandler);
TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
DECLARE_READ16_MEMBER(kb_scancode_r);
@ -66,6 +68,7 @@ private:
required_device<hp_taco_device> m_t15;
required_device<beep_device> m_beeper;
required_device<timer_device> m_beep_timer;
required_device<hp9845_io_slot_device> m_io_slot0;
void set_video_mar(UINT16 mar);
void video_fill_buff(bool buff_idx);