hp9845b: first version (implemented devices: 2 CPUs, RAMs, test ROM, text mode video)

This commit is contained in:
fulivi 2015-12-11 14:37:23 +01:00
parent 03b2a58b94
commit 26fcc6f7ae
2 changed files with 276 additions and 12 deletions

View File

@ -1232,7 +1232,7 @@ void hp_5061_3001_cpu_device::do_mpy(void)
m_reg_A = (UINT16)(p & 0xffff);
m_reg_B = (UINT16)((p >> 16) & 0xffff);
// Not entirely correct
// Not entirely correct, timing depends on initial content of A register
m_icount -= 65;
}

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Curt Coder
// copyright-holders:Curt Coder, F. Ulivi
/*
HP 9845
@ -11,6 +11,18 @@
#include "emu.h"
#include "cpu/z80/z80.h"
#include "softlist.h"
#include "cpu/hphybrid/hphybrid.h"
// Base address of video buffer
#define VIDEO_BUFFER_BASE 0x17000
#define MAX_WORD_PER_ROW 600
#define VIDEO_CHAR_WIDTH 9
#define VIDEO_CHAR_HEIGHT 15
#define VIDEO_CHAR_COLUMNS 80
#define VIDEO_CHAR_ROWS 25
#define VIDEO_ACTIVE_SCANLINES (VIDEO_CHAR_HEIGHT * VIDEO_CHAR_ROWS)
class hp9845_state : public driver_device
{
@ -22,6 +34,57 @@ public:
UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
};
class hp9845b_state : public driver_device
{
public:
hp9845b_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_lpu(*this , "lpu"),
m_ppu(*this , "ppu"),
m_palette(*this , "palette")
{ }
UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
virtual void machine_start();
virtual void machine_reset();
TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
void vblank_w(screen_device &screen, bool state);
private:
required_device<hp_5061_3001_cpu_device> m_lpu;
required_device<hp_5061_3001_cpu_device> m_ppu;
required_device<palette_device> m_palette;
void set_video_mar(UINT16 mar);
void video_fill_buff(bool buff_idx);
void video_render_buff(unsigned line_in_row, bool buff_idx);
// Character generator
const UINT8 *m_chargen;
// Text mode video I/F
typedef struct {
UINT8 chars[ 80 ];
UINT8 attrs[ 80 ];
bool full;
} video_buffer_t;
bitmap_rgb32 m_bitmap;
unsigned m_video_scanline;
offs_t m_video_mar;
UINT16 m_video_word;
bool m_video_load_mar;
bool m_video_byte_idx;
UINT8 m_video_attr;
bool m_video_buff_idx;
bool m_video_blanked;
UINT8 m_video_frame;
video_buffer_t m_video_buff[ 2 ];
};
static INPUT_PORTS_START( hp9845 )
INPUT_PORTS_END
@ -30,6 +93,181 @@ UINT32 hp9845_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap,
return 0;
}
UINT32 hp9845b_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
return 0;
}
void hp9845b_state::machine_start()
{
machine().first_screen()->register_screen_bitmap(m_bitmap);
m_chargen = memregion("chargen")->base();
}
void hp9845b_state::machine_reset()
{
m_lpu->halt_w(1);
m_ppu->halt_w(0);
// Some sensible defaults
m_video_mar = VIDEO_BUFFER_BASE;
m_video_load_mar = false;
m_video_byte_idx = false;
m_video_attr = 0;
m_video_buff_idx = false;
m_video_blanked = false;
m_video_frame = 0;
}
void hp9845b_state::set_video_mar(UINT16 mar)
{
m_video_mar = (mar & 0xfff) | VIDEO_BUFFER_BASE;
}
void hp9845b_state::video_fill_buff(bool buff_idx)
{
unsigned char_idx = 0;
unsigned iters = 0;
UINT8 byte;
address_space& prog_space = m_ppu->space(AS_PROGRAM);
m_video_buff[ buff_idx ].full = false;
while (1) {
if (!m_video_byte_idx) {
if (iters++ >= MAX_WORD_PER_ROW) {
// Limit on accesses per row reached
break;
}
m_video_word = prog_space.read_word(m_video_mar << 1);
if (m_video_load_mar) {
// Load new address into MAR after start of a new frame or NWA instruction
// TODO: decode graphic/alpha mode bit
set_video_mar(~m_video_word);
m_video_load_mar = false;
continue;
} else {
// Read normal word from frame buffer, start parsing at MSB
set_video_mar(m_video_mar + 1);
byte = (UINT8)(m_video_word >> 8);
m_video_byte_idx = true;
}
} else {
// Parse LSB
byte = (UINT8)(m_video_word & 0xff);
m_video_byte_idx = false;
}
if ((byte & 0xc0) == 0x80) {
// Attribute command
m_video_attr = byte & 0x1f;
} else if ((byte & 0xc1) == 0xc0) {
// New Word Address (NWA)
m_video_load_mar = true;
m_video_byte_idx = false;
} else if ((byte & 0xc1) == 0xc1) {
// End of line (EOL)
// Fill rest of buffer with spaces
memset(&m_video_buff[ buff_idx ].chars[ char_idx ] , 0x20 , 80 - char_idx);
memset(&m_video_buff[ buff_idx ].attrs[ char_idx ] , m_video_attr , 80 - char_idx);
m_video_buff[ buff_idx ].full = true;
break;
} else {
// Normal character
m_video_buff[ buff_idx ].chars[ char_idx ] = byte;
m_video_buff[ buff_idx ].attrs[ char_idx ] = m_video_attr;
char_idx++;
if (char_idx == 80) {
m_video_buff[ buff_idx ].full = true;
break;
}
}
}
}
void hp9845b_state::video_render_buff(unsigned line_in_row, bool buff_idx)
{
if (!m_video_buff[ buff_idx ].full) {
m_video_blanked = true;
}
if (m_video_blanked) {
// TODO: blank scanline
} else {
const rgb_t *palette = m_palette->palette()->entry_list_raw();
bool cursor_line = line_in_row == 12;
bool ul_line = line_in_row == 14;
bool cursor_blink = BIT(m_video_frame , 3);
bool char_blink = BIT(m_video_frame , 4);
for (unsigned i = 0; i < 80; i++) {
UINT8 charcode = m_video_buff[ buff_idx ].chars[ i ];
UINT8 attrs = m_video_buff[ buff_idx ].attrs[ i ];
UINT8 chargen_byte = m_chargen[ line_in_row | ((unsigned)charcode << 4) ];
UINT16 pixels;
// TODO: Handle selection of 2nd chargen
// TODO: Check if order of bits in "pixels" is ok
if ((ul_line && BIT(attrs , 3)) ||
(cursor_line && cursor_blink && BIT(attrs , 0))) {
pixels = ~0;
} else if (char_blink && BIT(attrs , 2)) {
pixels = 0;
} else {
pixels = (UINT16)(chargen_byte & 0x7f) << 2;
}
if (BIT(attrs , 1)) {
pixels = ~pixels;
}
for (unsigned j = 0; j < 9; j++) {
bool pixel = (pixels & (1U << (8 - j))) != 0;
m_bitmap.pix32(m_video_scanline , i * 9 + j) = palette[ pixel ? 1 : 0 ];
}
}
}
}
TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::scanline_timer)
{
m_video_scanline = param;
if (m_video_scanline < VIDEO_ACTIVE_SCANLINES) {
unsigned row = m_video_scanline / 15;
unsigned line_in_row = m_video_scanline - row * 15;
if (line_in_row == 0) {
// Start of new row, swap buffers
m_video_buff_idx = !m_video_buff_idx;
video_fill_buff(!m_video_buff_idx);
}
video_render_buff(line_in_row , m_video_buff_idx);
}
}
void hp9845b_state::vblank_w(screen_device &screen, bool state)
{
// VBlank signal is fed into HALT flag of PPU
m_ppu->halt_w(state);
if (state) {
// Start of V blank
set_video_mar(0);
m_video_load_mar = true;
m_video_byte_idx = false;
m_video_blanked = false;
m_video_frame++;
m_video_buff_idx = !m_video_buff_idx;
video_fill_buff(!m_video_buff_idx);
}
}
static MACHINE_CONFIG_START( hp9845a, hp9845_state )
//MCFG_CPU_ADD("lpu", HP_5061_3010, XTAL_11_4MHz)
//MCFG_CPU_ADD("ppu", HP_5061_3011, XTAL_11_4MHz)
@ -60,19 +298,35 @@ static MACHINE_CONFIG_START( hp9835a, hp9845_state )
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9835a_rom")
MACHINE_CONFIG_END
static MACHINE_CONFIG_START( hp9845b, hp9845_state )
//MCFG_CPU_ADD("lpu", HP_5061_3001, XTAL_11_4MHz)
//MCFG_CPU_ADD("ppu", HP_5061_3001, XTAL_11_4MHz)
static ADDRESS_MAP_START(global_mem_map , AS_PROGRAM , 16 , hp9845b_state)
ADDRESS_MAP_GLOBAL_MASK(0x3f7fff)
ADDRESS_MAP_UNMAP_LOW
AM_RANGE(0x000000 , 0x007fff) AM_RAM AM_SHARE("lpu_ram")
AM_RANGE(0x014000 , 0x017fff) AM_RAM AM_SHARE("ppu_ram")
AM_RANGE(0x250000 , 0x251fff) AM_ROM AM_REGION("test_rom" , 0)
ADDRESS_MAP_END
static ADDRESS_MAP_START(ppu_io_map , AS_IO , 16 , hp9845b_state)
ADDRESS_MAP_UNMAP_LOW
ADDRESS_MAP_END
static MACHINE_CONFIG_START( hp9845b, hp9845b_state )
MCFG_CPU_ADD("lpu", HP_5061_3001, 5700000)
MCFG_CPU_PROGRAM_MAP(global_mem_map)
MCFG_CPU_ADD("ppu", HP_5061_3001, 5700000)
MCFG_CPU_PROGRAM_MAP(global_mem_map)
MCFG_CPU_IO_MAP(ppu_io_map)
// video hardware
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_UPDATE_DRIVER(hp9845_state, screen_update)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500))
MCFG_SCREEN_SIZE(560, 455)
MCFG_SCREEN_VISIBLE_AREA(0, 560-1, 0, 455-1)
MCFG_SCREEN_UPDATE_DRIVER(hp9845b_state, screen_update)
MCFG_SCREEN_RAW_PARAMS(20849400 , 99 * 9 , 0 , 80 * 9 , 26 * 15 , 0 , 25 * 15)
MCFG_SCREEN_VBLANK_DRIVER(hp9845b_state, vblank_w)
MCFG_PALETTE_ADD_MONOCHROME_GREEN("palette")
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
MCFG_TIMER_DRIVER_ADD_SCANLINE("scantimer", hp9845b_state, scanline_timer, "screen", 0, 1)
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
MACHINE_CONFIG_END
ROM_START( hp9845a )
@ -108,6 +362,15 @@ ROM_END
#define rom_hp9835b rom_hp9835a
ROM_START( hp9845b )
ROM_REGION(0x4000 , "test_rom" , ROMREGION_16BIT | ROMREGION_BE)
ROM_LOAD("09845-66520-45_00-Test_ROM.bin" , 0x0000 , 0x2000 , CRC(95a5b299))
ROM_LOAD("09845-66520-45_10-Test_ROM.bin" , 0x2000 , 0x2000 , CRC(257e4c66))
ROM_REGION(0x800 , "chargen" , 0)
// Don't have the real character generator from HP9845, use HP64000's own for now
ROM_LOAD("1816_1496_82S191.bin" , 0 , 0x800 , BAD_DUMP CRC(32a52664) SHA1(8b2a49a32510103ff424e8481d5ed9887f609f2f))
#if 0
ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
ROM_LOAD( "1818-0823-0827-03_00-revb-system_lpu.bin", 0000000, 020000, CRC(7e49c781) SHA1(866c9ebd98d94bb6f99692e29d2d83f55b38c4b6) )
ROM_LOAD( "1818-0823-0827-03_10-revb-system_lpu.bin", 0020000, 020000, CRC(2f819e3d) SHA1(250886378c3ce2253229997007c7bf0be80a8d1d) )
@ -159,6 +422,7 @@ ROM_START( hp9845b )
ROM_LOAD( "1818-0841-0846-05_20-revd-keyboard_german.bin", 0060000, 020000, CRC(3bf6268a) SHA1(65d7dfeaf34c74dbc86ebe5d3bb65c6bd10163cb) )
ROM_LOAD( "1818-0841-0846-05_30-revc-keyboard_german.bin", 0040000, 020000, CRC(2b83db22) SHA1(6eda714ce05d2d75f4c041e36b6b6df40697d94a) )
ROM_LOAD( "1818-0841-0846-05_30-revd-keyboard_german.bin", 0060000, 020000, CRC(b4006959) SHA1(584a85f746a3b0c262fdf9e4be8e696c80cfd429) )
#endif
ROM_END
#define rom_hp9845t rom_hp9845b
@ -168,6 +432,6 @@ COMP( 1978, hp9845a, 0, 0, hp9845a, hp9845, driver_device, 0,
COMP( 1978, hp9845s, hp9845a, 0, hp9845a, hp9845, driver_device, 0, "Hewlett-Packard", "9845S", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1979, hp9835a, 0, 0, hp9835a, hp9845, driver_device, 0, "Hewlett-Packard", "9835A", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1979, hp9835b, hp9835a, 0, hp9835a, hp9845, driver_device, 0, "Hewlett-Packard", "9835B", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1980, hp9845b, 0, 0, hp9845b, hp9845, driver_device, 0, "Hewlett-Packard", "9845B", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1980, hp9845b, 0, 0, hp9845b, hp9845, driver_device, 0, "Hewlett-Packard", "9845B", MACHINE_NO_SOUND )
COMP( 1980, hp9845t, hp9845b, 0, hp9845b, hp9845, driver_device, 0, "Hewlett-Packard", "9845T", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1981, hp9845c, hp9845b, 0, hp9845b, hp9845, driver_device, 0, "Hewlett-Packard", "9845C", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )