mirror of
https://github.com/holub/mame
synced 2025-07-04 09:28:51 +03:00
commit
2143bef4f7
@ -10,6 +10,10 @@
|
|||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "cpu/capricorn/capricorn.h"
|
#include "cpu/capricorn/capricorn.h"
|
||||||
|
#include "speaker.h"
|
||||||
|
#include "sound/beep.h"
|
||||||
|
#include "sound/dac.h"
|
||||||
|
#include "sound/volt_reg.h"
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
#define VERBOSE 1
|
#define VERBOSE 1
|
||||||
@ -50,11 +54,15 @@ static constexpr unsigned ALPHA_MEM_SIZE= 4096;
|
|||||||
static constexpr unsigned GRAPH_MEM_SIZE= 16384;
|
static constexpr unsigned GRAPH_MEM_SIZE= 16384;
|
||||||
static constexpr unsigned CRT_STS_READY_BIT = 0;
|
static constexpr unsigned CRT_STS_READY_BIT = 0;
|
||||||
static constexpr unsigned CRT_STS_DISPLAY_BIT = 1;
|
static constexpr unsigned CRT_STS_DISPLAY_BIT = 1;
|
||||||
//static constexpr unsigned CRT_STS_BUSY_BIT = 7;
|
static constexpr unsigned CRT_STS_BUSY_BIT = 7;
|
||||||
static constexpr unsigned CRT_CTL_RD_RQ_BIT = 0;
|
static constexpr unsigned CRT_CTL_RD_RQ_BIT = 0;
|
||||||
static constexpr unsigned CRT_CTL_WIPEOUT_BIT = 1;
|
static constexpr unsigned CRT_CTL_WIPEOUT_BIT = 1;
|
||||||
static constexpr unsigned CRT_CTL_POWERDN_BIT = 2;
|
static constexpr unsigned CRT_CTL_POWERDN_BIT = 2;
|
||||||
static constexpr unsigned CRT_CTL_GRAPHICS_BIT = 7;
|
static constexpr unsigned CRT_CTL_GRAPHICS_BIT = 7;
|
||||||
|
// Time to read/write a byte in video memory (in master clock cycles)
|
||||||
|
static constexpr unsigned CRT_RW_TIME = 96;
|
||||||
|
// Time taken by hw timer updating (semi-made up) (in usec)
|
||||||
|
static constexpr unsigned TIMER_BUSY_USEC = 128;
|
||||||
static constexpr unsigned IRQ_KEYBOARD_BIT = 0;
|
static constexpr unsigned IRQ_KEYBOARD_BIT = 0;
|
||||||
static constexpr unsigned IRQ_TIMER0_BIT = 1;
|
static constexpr unsigned IRQ_TIMER0_BIT = 1;
|
||||||
static constexpr unsigned TIMER_COUNT = 4;
|
static constexpr unsigned TIMER_COUNT = 4;
|
||||||
@ -87,13 +95,24 @@ public:
|
|||||||
DECLARE_WRITE8_MEMBER(keycod_w);
|
DECLARE_WRITE8_MEMBER(keycod_w);
|
||||||
DECLARE_READ8_MEMBER(crtc_r);
|
DECLARE_READ8_MEMBER(crtc_r);
|
||||||
DECLARE_WRITE8_MEMBER(crtc_w);
|
DECLARE_WRITE8_MEMBER(crtc_w);
|
||||||
|
DECLARE_READ8_MEMBER(clksts_r);
|
||||||
|
DECLARE_WRITE8_MEMBER(clksts_w);
|
||||||
|
DECLARE_READ8_MEMBER(clkdat_r);
|
||||||
|
DECLARE_WRITE8_MEMBER(clkdat_w);
|
||||||
DECLARE_WRITE8_MEMBER(rselec_w);
|
DECLARE_WRITE8_MEMBER(rselec_w);
|
||||||
|
|
||||||
TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
|
TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(vm_timer);
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(timer_update);
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(clk_busy_timer);
|
||||||
protected:
|
protected:
|
||||||
required_device<capricorn_cpu_device> m_cpu;
|
required_device<capricorn_cpu_device> m_cpu;
|
||||||
required_device<screen_device> m_screen;
|
required_device<screen_device> m_screen;
|
||||||
required_device<palette_device> m_palette;
|
required_device<palette_device> m_palette;
|
||||||
|
required_device<timer_device> m_vm_timer;
|
||||||
|
required_device<timer_device> m_clk_busy_timer;
|
||||||
|
required_device<beep_device> m_beep;
|
||||||
|
required_device<dac_1bit_device> m_dac;
|
||||||
required_region_ptr<uint8_t> m_rom00;
|
required_region_ptr<uint8_t> m_rom00;
|
||||||
required_memory_bank m_rombank;
|
required_memory_bank m_rombank;
|
||||||
required_ioport m_io_key0;
|
required_ioport m_io_key0;
|
||||||
@ -111,6 +130,7 @@ protected:
|
|||||||
uint8_t m_crt_sts;
|
uint8_t m_crt_sts;
|
||||||
uint8_t m_crt_ctl;
|
uint8_t m_crt_ctl;
|
||||||
uint8_t m_crt_read_byte;
|
uint8_t m_crt_read_byte;
|
||||||
|
uint8_t m_crt_write_byte;
|
||||||
uint8_t m_empty_bank[ 0x2000 ];
|
uint8_t m_empty_bank[ 0x2000 ];
|
||||||
bool m_global_int_en;
|
bool m_global_int_en;
|
||||||
uint16_t m_int_req;
|
uint16_t m_int_req;
|
||||||
@ -126,11 +146,24 @@ protected:
|
|||||||
bool m_kb_flipped;
|
bool m_kb_flipped;
|
||||||
uint8_t m_kb_keycode;
|
uint8_t m_kb_keycode;
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
typedef struct {
|
||||||
|
uint8_t m_timer_cnt[ 4 ];
|
||||||
|
uint8_t m_timer_reg[ 4 ];
|
||||||
|
bool m_timer_en;
|
||||||
|
bool m_timer_clr;
|
||||||
|
uint8_t m_digit_to_match;
|
||||||
|
} hw_timer_t;
|
||||||
|
hw_timer_t m_hw_timer[ TIMER_COUNT ];
|
||||||
|
uint8_t m_timer_idx;
|
||||||
|
bool m_clk_busy;
|
||||||
|
|
||||||
|
attotime time_to_video_mem_availability() const;
|
||||||
static void get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble);
|
static void get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble);
|
||||||
uint8_t video_mem_r(uint16_t addr , uint16_t addr_mask) const;
|
uint8_t video_mem_r(uint16_t addr , uint16_t addr_mask) const;
|
||||||
void video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data);
|
void video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data);
|
||||||
void video_mem_read();
|
void video_mem_read();
|
||||||
void video_mem_write(uint8_t data);
|
void video_mem_write();
|
||||||
|
|
||||||
bool kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& keycode);
|
bool kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& keycode);
|
||||||
|
|
||||||
@ -145,6 +178,10 @@ hp85_state::hp85_state(const machine_config &mconfig, device_type type, const ch
|
|||||||
m_cpu(*this , "cpu"),
|
m_cpu(*this , "cpu"),
|
||||||
m_screen(*this , "screen"),
|
m_screen(*this , "screen"),
|
||||||
m_palette(*this , "palette"),
|
m_palette(*this , "palette"),
|
||||||
|
m_vm_timer(*this , "vm_timer"),
|
||||||
|
m_clk_busy_timer(*this , "clk_busy_timer"),
|
||||||
|
m_beep(*this , "beeper"),
|
||||||
|
m_dac(*this , "dac"),
|
||||||
m_rom00(*this , "rom00"),
|
m_rom00(*this , "rom00"),
|
||||||
m_rombank(*this , "rombank"),
|
m_rombank(*this , "rombank"),
|
||||||
m_io_key0(*this , "KEY0"),
|
m_io_key0(*this , "KEY0"),
|
||||||
@ -176,6 +213,7 @@ void hp85_state::machine_reset()
|
|||||||
m_crt_sts = 0x7c;
|
m_crt_sts = 0x7c;
|
||||||
m_crt_ctl = BIT_MASK(CRT_CTL_POWERDN_BIT) | BIT_MASK(CRT_CTL_WIPEOUT_BIT);
|
m_crt_ctl = BIT_MASK(CRT_CTL_POWERDN_BIT) | BIT_MASK(CRT_CTL_WIPEOUT_BIT);
|
||||||
m_crt_read_byte = 0;
|
m_crt_read_byte = 0;
|
||||||
|
m_crt_write_byte = 0;
|
||||||
// Clear RSELEC
|
// Clear RSELEC
|
||||||
m_rombank->set_entry(0xff);
|
m_rombank->set_entry(0xff);
|
||||||
|
|
||||||
@ -192,6 +230,17 @@ void hp85_state::machine_reset()
|
|||||||
m_kb_enable = true;
|
m_kb_enable = true;
|
||||||
m_kb_pressed = false;
|
m_kb_pressed = false;
|
||||||
m_kb_flipped = false;
|
m_kb_flipped = false;
|
||||||
|
for (auto& timer : m_hw_timer) {
|
||||||
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
|
timer.m_timer_cnt[ i ] = 0;
|
||||||
|
timer.m_timer_reg[ i ] = 0;
|
||||||
|
}
|
||||||
|
timer.m_timer_en = false;
|
||||||
|
timer.m_timer_clr = false;
|
||||||
|
timer.m_digit_to_match = 0;
|
||||||
|
}
|
||||||
|
m_timer_idx = 0;
|
||||||
|
m_clk_busy = false;
|
||||||
update_irl();
|
update_irl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,6 +309,7 @@ static const uint8_t vector_table[] = {
|
|||||||
|
|
||||||
IRQ_CALLBACK_MEMBER(hp85_state::irq_callback)
|
IRQ_CALLBACK_MEMBER(hp85_state::irq_callback)
|
||||||
{
|
{
|
||||||
|
logerror("IRQ ACK %u\n" , m_top_pending);
|
||||||
BIT_SET(m_int_acked , m_top_pending);
|
BIT_SET(m_int_acked , m_top_pending);
|
||||||
update_irl();
|
update_irl();
|
||||||
return vector_table[ m_top_pending ];
|
return vector_table[ m_top_pending ];
|
||||||
@ -297,13 +347,13 @@ READ8_MEMBER(hp85_state::keysts_r)
|
|||||||
|
|
||||||
WRITE8_MEMBER(hp85_state::keysts_w)
|
WRITE8_MEMBER(hp85_state::keysts_w)
|
||||||
{
|
{
|
||||||
logerror("KEYSTS W=%02x\n" , data);
|
|
||||||
if (BIT(data , 0)) {
|
if (BIT(data , 0)) {
|
||||||
irq_en_w(IRQ_KEYBOARD_BIT , true);
|
irq_en_w(IRQ_KEYBOARD_BIT , true);
|
||||||
} else if (BIT(data , 1)) {
|
} else if (BIT(data , 1)) {
|
||||||
irq_en_w(IRQ_KEYBOARD_BIT , false);
|
irq_en_w(IRQ_KEYBOARD_BIT , false);
|
||||||
}
|
}
|
||||||
// TODO: beeper
|
m_dac->write(BIT(data , 5));
|
||||||
|
m_beep->set_state(BIT(data , 6));
|
||||||
if (BIT(data , 7)) {
|
if (BIT(data , 7)) {
|
||||||
m_kb_flipped = !m_kb_flipped;
|
m_kb_flipped = !m_kb_flipped;
|
||||||
}
|
}
|
||||||
@ -316,7 +366,6 @@ READ8_MEMBER(hp85_state::keycod_r)
|
|||||||
|
|
||||||
WRITE8_MEMBER(hp85_state::keycod_w)
|
WRITE8_MEMBER(hp85_state::keycod_w)
|
||||||
{
|
{
|
||||||
logerror("KEYCOD W=%02x\n" , data);
|
|
||||||
if (data == 1) {
|
if (data == 1) {
|
||||||
irq_w(IRQ_KEYBOARD_BIT , false);
|
irq_w(IRQ_KEYBOARD_BIT , false);
|
||||||
m_kb_enable = true;
|
m_kb_enable = true;
|
||||||
@ -347,14 +396,12 @@ READ8_MEMBER(hp85_state::crtc_r)
|
|||||||
res = m_crt_read_byte;
|
res = m_crt_read_byte;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
logerror("RD @%u = %02x\n" , offset , res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE8_MEMBER(hp85_state::crtc_w)
|
WRITE8_MEMBER(hp85_state::crtc_w)
|
||||||
{
|
{
|
||||||
// Write to CRT controller (1MA5)
|
// Write to CRT controller (1MA5)
|
||||||
logerror("WR @%u = %02x\n" , offset , data);
|
|
||||||
uint8_t burst_idx = m_cpu->flatten_burst();
|
uint8_t burst_idx = m_cpu->flatten_burst();
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -364,7 +411,6 @@ WRITE8_MEMBER(hp85_state::crtc_w)
|
|||||||
} else if (burst_idx == 0) {
|
} else if (burst_idx == 0) {
|
||||||
m_crt_sad = (m_crt_sad & 0xff00) | data;
|
m_crt_sad = (m_crt_sad & 0xff00) | data;
|
||||||
}
|
}
|
||||||
logerror("SAD=%04x m=%u\n" , m_crt_sad , burst_idx);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
@ -374,24 +420,117 @@ WRITE8_MEMBER(hp85_state::crtc_w)
|
|||||||
} else if (burst_idx == 0) {
|
} else if (burst_idx == 0) {
|
||||||
m_crt_bad = (m_crt_bad & 0xff00) | data;
|
m_crt_bad = (m_crt_bad & 0xff00) | data;
|
||||||
}
|
}
|
||||||
logerror("BAD=%04x m=%u\n" , m_crt_bad , burst_idx);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
// CRTCTL
|
// CRTCTL
|
||||||
m_crt_ctl = data;
|
m_crt_ctl = data;
|
||||||
if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) {
|
if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) {
|
||||||
video_mem_read();
|
BIT_CLR(m_crt_sts , CRT_STS_READY_BIT);
|
||||||
|
BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT);
|
||||||
|
attotime vm_av = time_to_video_mem_availability();
|
||||||
|
m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// CRTDAT
|
// CRTDAT
|
||||||
video_mem_write(data);
|
{
|
||||||
|
m_crt_write_byte = data;
|
||||||
|
BIT_CLR(m_crt_sts , CRT_STS_READY_BIT);
|
||||||
|
BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT);
|
||||||
|
attotime vm_av = time_to_video_mem_availability();
|
||||||
|
m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
READ8_MEMBER(hp85_state::clksts_r)
|
||||||
|
{
|
||||||
|
uint8_t res = 0;
|
||||||
|
for (unsigned i = 0; i < TIMER_COUNT; i++) {
|
||||||
|
if (BIT(m_int_en , IRQ_TIMER0_BIT + i)) {
|
||||||
|
BIT_SET(res , i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_clk_busy) {
|
||||||
|
BIT_SET(res , 7);
|
||||||
|
}
|
||||||
|
//logerror("CLKSTS R=%02x\n" , res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE8_MEMBER(hp85_state::clksts_w)
|
||||||
|
{
|
||||||
|
// logerror("CLKSTS W=%02x\n" , data);
|
||||||
|
if (data == 0x0c) {
|
||||||
|
// Set test mode (see timer_update)
|
||||||
|
auto& timer = m_hw_timer[ m_timer_idx ];
|
||||||
|
timer.m_digit_to_match = 1;
|
||||||
|
timer.m_timer_cnt[ 0 ] = timer.m_timer_reg[ 0 ];
|
||||||
|
timer.m_timer_cnt[ 1 ] = timer.m_timer_reg[ 1 ];
|
||||||
|
timer.m_timer_cnt[ 2 ] = timer.m_timer_reg[ 2 ];
|
||||||
|
timer.m_timer_cnt[ 3 ] = timer.m_timer_reg[ 3 ];
|
||||||
|
logerror("Test mode enabled for timer %u\n" , m_timer_idx);
|
||||||
|
} else {
|
||||||
|
m_timer_idx = (data >> 6) & 3;
|
||||||
|
auto& timer = m_hw_timer[ m_timer_idx ];
|
||||||
|
if (BIT(data , 0)) {
|
||||||
|
// Disable timer irq
|
||||||
|
irq_en_w(IRQ_TIMER0_BIT + m_timer_idx , false);
|
||||||
|
} else if (BIT(data , 1)) {
|
||||||
|
// Enable timer irq
|
||||||
|
irq_en_w(IRQ_TIMER0_BIT + m_timer_idx , true);
|
||||||
|
}
|
||||||
|
if (BIT(data , 2)) {
|
||||||
|
// Stop timer
|
||||||
|
timer.m_timer_en = false;
|
||||||
|
} else if (BIT(data , 3)) {
|
||||||
|
// Start timer
|
||||||
|
timer.m_timer_en = true;
|
||||||
|
}
|
||||||
|
if (BIT(data , 4) || (BIT(data , 3) && timer.m_digit_to_match)) {
|
||||||
|
// Clear timer
|
||||||
|
timer.m_timer_clr = true;
|
||||||
|
// Disable test mode
|
||||||
|
timer.m_digit_to_match = 0;
|
||||||
|
}
|
||||||
|
if (BIT(data , 5)) {
|
||||||
|
// Clear timer irq
|
||||||
|
irq_w(IRQ_TIMER0_BIT + m_timer_idx , false);
|
||||||
|
}
|
||||||
|
update_int_bits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
READ8_MEMBER(hp85_state::clkdat_r)
|
||||||
|
{
|
||||||
|
uint8_t res;
|
||||||
|
unsigned burst_idx = m_cpu->flatten_burst();
|
||||||
|
if (burst_idx < 4) {
|
||||||
|
res = m_hw_timer[ m_timer_idx ].m_timer_cnt[ burst_idx ];
|
||||||
|
} else {
|
||||||
|
// What happens when loading more than 4 bytes from timers?
|
||||||
|
logerror("Reading more than 4 bytes from timer %u\n" , m_timer_idx);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
//logerror("CLKDAT R %u=%02x\n" , burst_idx , res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE8_MEMBER(hp85_state::clkdat_w)
|
||||||
|
{
|
||||||
|
unsigned burst_idx = m_cpu->flatten_burst();
|
||||||
|
//logerror("CLKDAT W %u=%02x\n" , burst_idx , data);
|
||||||
|
if (burst_idx < 4) {
|
||||||
|
m_hw_timer[ m_timer_idx ].m_timer_reg[ burst_idx ] = data;
|
||||||
|
} else {
|
||||||
|
// What happens when storing more than 4 bytes into timers?
|
||||||
|
logerror("Writing more than 4 bytes into timer %u\n" , m_timer_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WRITE8_MEMBER(hp85_state::rselec_w)
|
WRITE8_MEMBER(hp85_state::rselec_w)
|
||||||
{
|
{
|
||||||
m_rombank->set_entry(data);
|
m_rombank->set_entry(data);
|
||||||
@ -525,7 +664,6 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::kb_scan)
|
|||||||
kb_scan_ioport(input[ 2 ] & ~m_kb_state[ 2 ] , 64 , keycode);
|
kb_scan_ioport(input[ 2 ] & ~m_kb_state[ 2 ] , 64 , keycode);
|
||||||
|
|
||||||
if (got_key) {
|
if (got_key) {
|
||||||
logerror("key %02x\n" , keycode);
|
|
||||||
m_kb_keycode = keycode;
|
m_kb_keycode = keycode;
|
||||||
irq_w(IRQ_KEYBOARD_BIT , true);
|
irq_w(IRQ_KEYBOARD_BIT , true);
|
||||||
m_kb_enable = false;
|
m_kb_enable = false;
|
||||||
@ -540,6 +678,129 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::kb_scan)
|
|||||||
m_kb_state[ 2 ] = input[ 2 ];
|
m_kb_state[ 2 ] = input[ 2 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::vm_timer)
|
||||||
|
{
|
||||||
|
if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) {
|
||||||
|
video_mem_read();
|
||||||
|
} else {
|
||||||
|
video_mem_write();
|
||||||
|
}
|
||||||
|
BIT_CLR(m_crt_sts , CRT_STS_BUSY_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::timer_update)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < TIMER_COUNT; i++) {
|
||||||
|
auto& timer = m_hw_timer[ i ];
|
||||||
|
if (timer.m_timer_clr) {
|
||||||
|
timer.m_timer_clr = false;
|
||||||
|
timer.m_timer_cnt[ 0 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 1 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 2 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 3 ] = 0;
|
||||||
|
} else if (timer.m_timer_en) {
|
||||||
|
if (timer.m_digit_to_match) {
|
||||||
|
// Timers have an undocumented mode (used by test "J" of service ROM)
|
||||||
|
// where the counter has to match in sequence all digits of register
|
||||||
|
// in order to raise an interrupt. In other words interrupt is generated
|
||||||
|
// after a number of updates that's equal to the sum of all digits in
|
||||||
|
// register + 1. My opinion is that people at HP designed this mode to
|
||||||
|
// allow all digits in a timer to be tested quickly. Without this special
|
||||||
|
// mode it takes more than 27 hours to check that all digits increment
|
||||||
|
// correctly and that there are no stuck bits.
|
||||||
|
// From an operative point of view, we copy register into counter when
|
||||||
|
// this special mode is activated (see clksts_w). Then, at each update,
|
||||||
|
// we decrement the digit of counter pointed to by m_digit_to_match (1 =
|
||||||
|
// least significant digit). Each time a digit "borrows" (i.e. it decrements
|
||||||
|
// from 0 to 9), we move on to digit at left. When m_digit_to_match reaches
|
||||||
|
// 9, interrupt is raised and the timer stops.
|
||||||
|
// At this point counter is always "99999999".
|
||||||
|
if (timer.m_digit_to_match < 9) {
|
||||||
|
while (true) {
|
||||||
|
bool borrow = false;
|
||||||
|
uint8_t b = timer.m_timer_cnt[ (timer.m_digit_to_match - 1) / 2 ];
|
||||||
|
if (BIT(timer.m_digit_to_match , 0)) {
|
||||||
|
// Least significant digit in b
|
||||||
|
if (b & 0x0f) {
|
||||||
|
b--;
|
||||||
|
} else {
|
||||||
|
b = (b & 0xf0) | 9;
|
||||||
|
borrow = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Most significant digit in b
|
||||||
|
if (b & 0xf0) {
|
||||||
|
b -= 0x10;
|
||||||
|
} else {
|
||||||
|
b = 0x99;
|
||||||
|
borrow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.m_timer_cnt[ (timer.m_digit_to_match - 1) / 2 ] = b;
|
||||||
|
if (borrow) {
|
||||||
|
timer.m_digit_to_match++;
|
||||||
|
if (timer.m_digit_to_match == 9) {
|
||||||
|
irq_w(IRQ_TIMER0_BIT + i , true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Standard timer mode
|
||||||
|
// Increment all active timers by 1
|
||||||
|
bool carry = true;
|
||||||
|
for (unsigned idx = 0; idx < 4 && carry; idx++) {
|
||||||
|
carry = false;
|
||||||
|
uint8_t b = timer.m_timer_cnt[ idx ];
|
||||||
|
b++;
|
||||||
|
if ((b & 0xf) > 9) {
|
||||||
|
b += 6;
|
||||||
|
if (b >= 0xa0) {
|
||||||
|
b += 0x60;
|
||||||
|
carry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.m_timer_cnt[ idx ] = b;
|
||||||
|
}
|
||||||
|
if (timer.m_timer_cnt[ 0 ] == timer.m_timer_reg[ 0 ] &&
|
||||||
|
timer.m_timer_cnt[ 1 ] == timer.m_timer_reg[ 1 ] &&
|
||||||
|
timer.m_timer_cnt[ 2 ] == timer.m_timer_reg[ 2 ] &&
|
||||||
|
timer.m_timer_cnt[ 3 ] == timer.m_timer_reg[ 3 ]) {
|
||||||
|
timer.m_timer_cnt[ 0 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 1 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 2 ] = 0;
|
||||||
|
timer.m_timer_cnt[ 3 ] = 0;
|
||||||
|
irq_w(IRQ_TIMER0_BIT + i , true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_clk_busy = true;
|
||||||
|
m_clk_busy_timer->adjust(attotime::from_usec(TIMER_BUSY_USEC));
|
||||||
|
}
|
||||||
|
|
||||||
|
TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::clk_busy_timer)
|
||||||
|
{
|
||||||
|
m_clk_busy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attotime hp85_state::time_to_video_mem_availability() const
|
||||||
|
{
|
||||||
|
if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) {
|
||||||
|
// Blank video, immediate access
|
||||||
|
return attotime::zero;
|
||||||
|
} else if (m_screen->vblank()) {
|
||||||
|
// Vertical blanking, immediate access
|
||||||
|
return attotime::zero;
|
||||||
|
} else {
|
||||||
|
// In the active part, wait until vertical blanking
|
||||||
|
return m_screen->time_until_vblank_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void hp85_state::get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble)
|
void hp85_state::get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble)
|
||||||
{
|
{
|
||||||
byte_addr = (addr / 2) & (VIDEO_MEM_SIZE - 1);
|
byte_addr = (addr / 2) & (VIDEO_MEM_SIZE - 1);
|
||||||
@ -597,20 +858,20 @@ void hp85_state::video_mem_read()
|
|||||||
}
|
}
|
||||||
m_crt_read_byte = video_mem_r(m_crt_bad , mask);
|
m_crt_read_byte = video_mem_r(m_crt_bad , mask);
|
||||||
m_crt_bad += 2;
|
m_crt_bad += 2;
|
||||||
|
BIT_CLR(m_crt_ctl , CRT_CTL_RD_RQ_BIT);
|
||||||
BIT_SET(m_crt_sts , CRT_STS_READY_BIT);
|
BIT_SET(m_crt_sts , CRT_STS_READY_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hp85_state::video_mem_write(uint8_t data)
|
void hp85_state::video_mem_write()
|
||||||
{
|
{
|
||||||
uint16_t mask;
|
uint16_t mask;
|
||||||
|
|
||||||
logerror("VM @%04x=%02x\n" , m_crt_bad , data);
|
|
||||||
if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) {
|
if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) {
|
||||||
mask = GRAPH_MEM_SIZE / 2 - 1;
|
mask = GRAPH_MEM_SIZE / 2 - 1;
|
||||||
} else {
|
} else {
|
||||||
mask = ALPHA_MEM_SIZE / 2 - 1;
|
mask = ALPHA_MEM_SIZE / 2 - 1;
|
||||||
}
|
}
|
||||||
video_mem_w(m_crt_bad , mask , data);
|
video_mem_w(m_crt_bad , mask , m_crt_write_byte);
|
||||||
m_crt_bad += 2;
|
m_crt_bad += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,6 +1017,8 @@ static ADDRESS_MAP_START(cpu_mem_map , AS_PROGRAM , 8 , hp85_state)
|
|||||||
AM_RANGE(0xff02 , 0xff02) AM_READWRITE(keysts_r , keysts_w)
|
AM_RANGE(0xff02 , 0xff02) AM_READWRITE(keysts_r , keysts_w)
|
||||||
AM_RANGE(0xff03 , 0xff03) AM_READWRITE(keycod_r , keycod_w)
|
AM_RANGE(0xff03 , 0xff03) AM_READWRITE(keycod_r , keycod_w)
|
||||||
AM_RANGE(0xff04 , 0xff07) AM_READWRITE(crtc_r , crtc_w)
|
AM_RANGE(0xff04 , 0xff07) AM_READWRITE(crtc_r , crtc_w)
|
||||||
|
AM_RANGE(0xff0a , 0xff0a) AM_READWRITE(clksts_r , clksts_w)
|
||||||
|
AM_RANGE(0xff0b , 0xff0b) AM_READWRITE(clkdat_r , clkdat_w)
|
||||||
AM_RANGE(0xff18 , 0xff18) AM_WRITE(rselec_w)
|
AM_RANGE(0xff18 , 0xff18) AM_WRITE(rselec_w)
|
||||||
ADDRESS_MAP_END
|
ADDRESS_MAP_END
|
||||||
|
|
||||||
@ -769,9 +1032,25 @@ static MACHINE_CONFIG_START(hp85)
|
|||||||
MCFG_SCREEN_UPDATE_DRIVER(hp85_state , screen_update)
|
MCFG_SCREEN_UPDATE_DRIVER(hp85_state , screen_update)
|
||||||
MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(hp85_state, vblank_w))
|
MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(hp85_state, vblank_w))
|
||||||
MCFG_PALETTE_ADD_MONOCHROME("palette")
|
MCFG_PALETTE_ADD_MONOCHROME("palette")
|
||||||
|
MCFG_TIMER_DRIVER_ADD("vm_timer", hp85_state, vm_timer)
|
||||||
|
|
||||||
// No idea at all about the actual keyboard scan frequency
|
// No idea at all about the actual keyboard scan frequency
|
||||||
MCFG_TIMER_DRIVER_ADD_PERIODIC("kb_timer" , hp85_state , kb_scan , attotime::from_hz(100))
|
MCFG_TIMER_DRIVER_ADD_PERIODIC("kb_timer" , hp85_state , kb_scan , attotime::from_hz(100))
|
||||||
|
|
||||||
|
// Hw timers are updated at 1 kHz rate
|
||||||
|
MCFG_TIMER_DRIVER_ADD_PERIODIC("hw_timer" , hp85_state , timer_update , attotime::from_hz(1000))
|
||||||
|
MCFG_TIMER_DRIVER_ADD("clk_busy_timer", hp85_state, clk_busy_timer)
|
||||||
|
|
||||||
|
// Beeper
|
||||||
|
MCFG_SPEAKER_STANDARD_MONO("mono")
|
||||||
|
MCFG_SOUND_ADD("dac" , DAC_1BIT , 0)
|
||||||
|
MCFG_MIXER_ROUTE(ALL_OUTPUTS , "mono" , 0.5 , 0)
|
||||||
|
MCFG_DEVICE_ADD("vref", VOLTAGE_REGULATOR, 0)
|
||||||
|
MCFG_VOLTAGE_REGULATOR_OUTPUT(5.0)
|
||||||
|
MCFG_SOUND_ROUTE_EX(0, "dac", 1.0, DAC_VREF_POS_INPUT)
|
||||||
|
MCFG_SOUND_ADD("beeper" , BEEP , MASTER_CLOCK / 8192)
|
||||||
|
MCFG_MIXER_ROUTE(ALL_OUTPUTS , "mono" , 0.5 , 0)
|
||||||
|
|
||||||
MACHINE_CONFIG_END
|
MACHINE_CONFIG_END
|
||||||
|
|
||||||
ROM_START(hp85)
|
ROM_START(hp85)
|
||||||
@ -787,4 +1066,4 @@ ROM_START(hp85)
|
|||||||
ROM_LOAD("chrgen.bin" , 0 , 0x400 , CRC(9c402544) SHA1(32634fc73c1544aeeefda62ebb10349c5b40729f))
|
ROM_LOAD("chrgen.bin" , 0 , 0x400 , CRC(9c402544) SHA1(32634fc73c1544aeeefda62ebb10349c5b40729f))
|
||||||
ROM_END
|
ROM_END
|
||||||
|
|
||||||
COMP(1980 , hp85 , 0 , 0 , hp85 , hp85 , hp85_state , 0 , "HP" , "HP 85" , MACHINE_NO_SOUND)
|
COMP(1980 , hp85 , 0 , 0 , hp85 , hp85 , hp85_state , 0 , "HP" , "HP 85" , 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user