From 26fcc6f7aea4488a7e3d23a394545a840b1e4546 Mon Sep 17 00:00:00 2001 From: fulivi Date: Fri, 11 Dec 2015 14:37:23 +0100 Subject: [PATCH] hp9845b: first version (implemented devices: 2 CPUs, RAMs, test ROM, text mode video) --- src/devices/cpu/hphybrid/hphybrid.cpp | 2 +- src/mame/drivers/hp9845.cpp | 286 +++++++++++++++++++++++++- 2 files changed, 276 insertions(+), 12 deletions(-) diff --git a/src/devices/cpu/hphybrid/hphybrid.cpp b/src/devices/cpu/hphybrid/hphybrid.cpp index 02ea9a54b80..498ada7d733 100644 --- a/src/devices/cpu/hphybrid/hphybrid.cpp +++ b/src/devices/cpu/hphybrid/hphybrid.cpp @@ -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; } diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp index 22ef8533bab..010e522964c 100644 --- a/src/mame/drivers/hp9845.cpp +++ b/src/mame/drivers/hp9845.cpp @@ -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 m_lpu; + required_device m_ppu; + required_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 )