From 9a8cac8d1e0382d47ff2795849d8cb1b1f89cdc0 Mon Sep 17 00:00:00 2001 From: Angelo Salese Date: Wed, 14 Dec 2022 04:30:45 +0100 Subject: [PATCH] nec/pc88va.cpp: overhaul, make most software to start running with pc88va2 (#10656) - nec/v5x.cpp: add ICU slave ack readback; --- hash/pc88va.xml | 242 ++++- src/devices/cpu/nec/v5x.cpp | 2 + src/devices/cpu/nec/v5x.h | 14 +- src/mame/nec/pc8801.cpp | 4 +- src/mame/nec/pc88va.cpp | 1899 ++++++++++++++--------------------- src/mame/nec/pc88va.h | 281 ++++-- src/mame/nec/pc88va_sgp.cpp | 403 ++++++++ src/mame/nec/pc88va_sgp.h | 59 ++ src/mame/nec/pc88va_v.cpp | 1654 ++++++++++++++++++++++++++++++ 9 files changed, 3273 insertions(+), 1285 deletions(-) create mode 100644 src/mame/nec/pc88va_sgp.cpp create mode 100644 src/mame/nec/pc88va_sgp.h create mode 100644 src/mame/nec/pc88va_v.cpp diff --git a/hash/pc88va.xml b/hash/pc88va.xml index 177a40b522c..7b9b1114185 100644 --- a/hash/pc88va.xml +++ b/hash/pc88va.xml @@ -17,6 +17,7 @@ license:CC0 * Igo-mei-kyoku Shuu '87 ~ 囲碁名局集'87, by GAM * Death Bringer ~ デス・ブリンガー, by Telenet Japan * Arantia (Atlantia?) VA Special ~ アランティアVAスペシャル, by Victor Music Industry + * Go Kichi-kun ~ 碁キチくん, by GAM * Go Kichi-kun 2 ~ 碁キチくん2, by GAM * Ultima V ~ ウルティマⅤ, by Pony Canyon * The Bard's Tale ~ ザ バーズテイル, by Pony Canyon @@ -28,7 +29,7 @@ license:CC0 * Ishin no Arashi ~ 維新の嵐, by Koei * Teitoku no Ketsudan ~ 提督の決断, by Koei * Nobunaga no Yabou - Sengoku Gunyou Den ~ 信長の野望・戦国群雄伝, by Koei - * Suikoden ~ 水滸伝, by Koei + * Suikoden - Tenmei no Chikai ~ 水滸伝 天命の誓い, by Koei * Kawaiso Monogatari ~ かわいそう物語, by System Software * Fantasy III ~ ファンタジーⅢ, by Starcraft * Lodoss Shima Senki ~ ロードス島戦記, by Humming Bird Soft @@ -37,6 +38,10 @@ license:CC0 * Ultima I-IV ~ ウルティマI~IV, by Pony Canyon * Heroes of the Lance ~ ヒーローオブランス, by Pony Canyon * Lightning Bacchus? ~ ライトニングバッカス, by NCS + * Record of Lodoss War ~ ロードス島戦記 by Humming Bird Soft + +PC88VA software + * The Print Shop II ~ プリントショップ Ⅱ (Broderbund Japan) PC88VA doujin games (mostly undumped) @@ -67,15 +72,27 @@ PC88VA doujin games (mostly undumped) * 翼君の宝箱14 (by Wing software, 1999) * くるッ★彡 (by Cenkeil, 1999) * Frail Rulers (by Fredia, 1999) + * Voltige Aerienne / ディスクイメージ (2008?) + +Operating Systems + * MS-DOS 2.11 + * MS-DOS 3.1 + * CDOS + --> + + PC88-VA Demo 19?? - <unknown> + NEC + @@ -89,7 +106,10 @@ PC88VA doujin games (mostly undumped) 88VA1 Tentou Demo 19?? - <unknown> + NEC + @@ -101,7 +121,11 @@ PC88VA doujin games (mostly undumped) 88VA2 Demo 19?? - <unknown> + NEC + @@ -110,9 +134,13 @@ PC88VA doujin games (mostly undumped) - AnimeFramer - 19?? - <unknown> + + Anime Framer (v1.0) + 1987 + NEC + @@ -127,33 +155,41 @@ PC88VA doujin games (mostly undumped) + + - - - Paint? - 19?? - <unknown> + + Anime Framer (v1.0 alt) + 1987 + NEC + + - + - + - - Micromusician VA - 19?? - <unknown> + Micro Musician VA + 1989 + Music Network + @@ -164,7 +200,10 @@ PC88VA doujin games (mostly undumped) PC-Engine (VA2 Tenpu-ban) 19?? - <unknown> + NEC + @@ -191,6 +230,10 @@ PC88VA doujin games (mostly undumped) Family Stadium 19?? ゲームアーツ (Game Arts) + @@ -214,10 +257,16 @@ PC88VA doujin games (mostly undumped) + Family Stadium '89 - Pennant Race Hen 19?? ゲームアーツ (Game Arts) + @@ -238,9 +287,12 @@ PC88VA doujin games (mostly undumped) - Family Stadium '89 (Alt?) + Family Stadium '89 (alt?) 19?? ゲームアーツ (Game Arts) + @@ -267,11 +319,16 @@ PC88VA doujin games (mostly undumped) Might & Magic 198? スタークラフト (Starcraft) + + + - - - + + + @@ -283,9 +340,9 @@ PC88VA doujin games (mostly undumped) - - - + + + @@ -294,6 +351,9 @@ PC88VA doujin games (mostly undumped) Might & Magic Book 2 1984 スタークラフト (Starcraft) + @@ -328,6 +388,12 @@ PC88VA doujin games (mostly undumped) Olteus 1987 ウインキーソフト (Winky Soft) + @@ -360,9 +426,15 @@ PC88VA doujin games (mostly undumped) - Olteus (Alt Disk D) + Olteus (alt disk D) 1987 ウインキーソフト (Winky Soft) + @@ -395,8 +467,13 @@ PC88VA doujin games (mostly undumped) R-Type - 1988? + 1988 NEC + @@ -414,6 +491,9 @@ PC88VA doujin games (mostly undumped) Rogue Alliance 198? スタークラフト (Starcraft) + @@ -442,6 +522,12 @@ PC88VA doujin games (mostly undumped) Shanghai 1986 システムソフト (System Soft) + @@ -454,7 +540,14 @@ PC88VA doujin games (mostly undumped) Shinra Bansho 1987 日本テレネット (Nihon Telenet) + + @@ -474,6 +567,9 @@ PC88VA doujin games (mostly undumped) Sorcerian 1988 日本ファルコム (Nihon Falcom) + @@ -660,6 +756,11 @@ PC88VA doujin games (mostly undumped) Tetris 1988 B·P·S (Bullet-Proof Software) + @@ -670,11 +771,17 @@ PC88VA doujin games (mostly undumped) + Illusion City - Genei Toshi 1992 マイクロキャビン (Micro Cabin) + @@ -732,23 +839,32 @@ PC88VA doujin games (mostly undumped) Xak II 1990 マイクロキャビン (Micro Cabin) + + + - + - + - + @@ -757,8 +873,16 @@ PC88VA doujin games (mostly undumped) Fray - In Magical Adventure 1991 マイクロキャビン (Micro Cabin) + + @@ -786,11 +910,16 @@ PC88VA doujin games (mostly undumped) + + Abunai Tengu Densetsu 1989 アリスソフト (Alicesoft) + @@ -820,6 +949,9 @@ PC88VA doujin games (mostly undumped) Crescent Moon Girl 1989 アリスソフト (Alicesoft) + @@ -849,6 +981,9 @@ PC88VA doujin games (mostly undumped) D.P.S - Dream Program System 1989 アリスソフト (Alicesoft) + @@ -873,7 +1008,8 @@ PC88VA doujin games (mostly undumped) アリスソフト (Alicesoft) - + + @@ -909,6 +1045,7 @@ PC88VA doujin games (mostly undumped) アリスソフト (Alicesoft) + @@ -945,6 +1082,7 @@ PC88VA doujin games (mostly undumped) アリスソフト (Alicesoft) + @@ -979,6 +1117,9 @@ PC88VA doujin games (mostly undumped) Rance - Hikari wo Motomete 1989 アリスソフト (Alicesoft) + @@ -1015,6 +1156,9 @@ PC88VA doujin games (mostly undumped) Rance 2 - Hangyaku no Shojo-tachi 1990 アリスソフト (Alicesoft) + @@ -1067,6 +1211,10 @@ PC88VA doujin games (mostly undumped) Balloon Breaker 1996 <doujin> + @@ -1080,6 +1228,10 @@ PC88VA doujin games (mostly undumped) Boomering 1996 <doujin> + @@ -1090,8 +1242,14 @@ PC88VA doujin games (mostly undumped) Hare Hare Yukai - 19?? + + 2006 <doujin> + @@ -1111,6 +1269,10 @@ PC88VA doujin games (mostly undumped) Hati no Sora de 1994 <doujin> + @@ -1124,6 +1286,10 @@ PC88VA doujin games (mostly undumped) Saishuu Heiki UPO (Pre-Release) 1992 <doujin> + @@ -1133,12 +1299,12 @@ PC88VA doujin games (mostly undumped) - Pac-Man 19?? <doujin> + @@ -1146,11 +1312,14 @@ PC88VA doujin games (mostly undumped) - + - Pac-Man (Alt?) + Pac-Man (auto-bootable) 19?? <doujin> + @@ -1162,6 +1331,7 @@ PC88VA doujin games (mostly undumped) + Mid Garts (VA patched?) 19?? diff --git a/src/devices/cpu/nec/v5x.cpp b/src/devices/cpu/nec/v5x.cpp index 9faeed6bfd9..2c2064e4bf2 100644 --- a/src/devices/cpu/nec/v5x.cpp +++ b/src/devices/cpu/nec/v5x.cpp @@ -347,6 +347,7 @@ void v50_base_device::device_start() m_internal_io = &space(AS_INTERNAL_IO); m_tout1_callback.resolve_safe(); + m_icu_slave_ack.resolve_safe(0); set_irq_acknowledge_callback(*m_icu, FUNC(v5x_icu_device::inta_cb)); @@ -520,6 +521,7 @@ v50_base_device::v50_base_device(const machine_config &mconfig, device_type type : nec_common_device(mconfig, type, tag, owner, clock, is_16bit, prefetch_size, prefetch_cycles, chip_type, address_map_constructor(FUNC(v50_base_device::internal_port_map), this)) , device_v5x_interface(mconfig, *this, is_16bit) , m_tout1_callback(*this) + , m_icu_slave_ack(*this) , m_OPCN(0) , m_tout1(false) , m_intp1(false) diff --git a/src/devices/cpu/nec/v5x.h b/src/devices/cpu/nec/v5x.h index 23fcdc265a9..f51620851b9 100644 --- a/src/devices/cpu/nec/v5x.h +++ b/src/devices/cpu/nec/v5x.h @@ -105,7 +105,7 @@ protected: void OPHA_w(u8 data); u8 OPSEL_r(); void OPSEL_w(u8 data); - u8 get_pic_ack() { return 0; } + virtual u8 get_pic_ack(offs_t offset) { return 0; } DECLARE_WRITE_LINE_MEMBER(internal_irq_w); void tcu_clock_update(); @@ -148,6 +148,8 @@ public: auto tout1_cb() { return m_tout1_callback.bind(); } auto tout2_cb() { return subdevice("tcu")->out_handler<2>(); } + auto icu_slave_ack_cb() { return m_icu_slave_ack.bind(); } + protected: v50_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, bool is_16bit, u8 prefetch_size, u8 prefetch_cycles, u32 chip_type); @@ -177,10 +179,20 @@ protected: u8 OPCN_r(); void OPCN_w(u8 data); + // TODO: non-offset 7 configuration + // Currently used by pc88va only, which uses the canonical IRQ7 for cascading an external PIC to the internal one. + virtual u8 get_pic_ack(offs_t offset) override + { + if (offset == 7) + return m_icu_slave_ack(0); + return 0; + } + private: DECLARE_WRITE_LINE_MEMBER(tout1_w); devcb_write_line m_tout1_callback; + devcb_read8 m_icu_slave_ack; u8 m_OPCN; bool m_tout1; diff --git a/src/mame/nec/pc8801.cpp b/src/mame/nec/pc8801.cpp index 04dbc246fa6..e2fccaff0ad 100644 --- a/src/mame/nec/pc8801.cpp +++ b/src/mame/nec/pc8801.cpp @@ -676,7 +676,7 @@ void pc8801_state::port40_w(uint8_t data) if(((m_device_ctrl_data & 0x20) == 0x20) && ((data & 0x20) == 0x00)) m_beeper->set_state(0); - // TODO: send to joyport implementation + // TODO: send to joyport DE-9 implementation if((m_device_ctrl_data & 0x40) != (data & 0x40)) { attotime new_time = machine().time(); @@ -1592,7 +1592,7 @@ void pc8801mc_state::machine_reset() m_cdrom_bank = true; } -// TODO: to joyport DB9 option slot +// TODO: to joyport DE-9 option slot uint8_t pc8801mk2sr_state::opn_porta_r() { if(ioport("BOARD_CONFIG")->read() & 2) diff --git a/src/mame/nec/pc88va.cpp b/src/mame/nec/pc88va.cpp index 68671bc1560..2348b78b49d 100644 --- a/src/mame/nec/pc88va.cpp +++ b/src/mame/nec/pc88va.cpp @@ -1,733 +1,223 @@ // license:BSD-3-Clause // copyright-holders:Angelo Salese -/******************************************************************************************** +// thanks-to: Fujix +/************************************************************************************************** PC-88VA (c) 1987 NEC - A follow up of the regular PC-8801. It can also run PC-8801 software in compatible mode + Here be dragons, a mostly compatible PC-8801 with extra V3 Mode for superset. preliminary driver by Angelo Salese Special thanks to Fujix for his documentation translation help TODO: - - What exact kind of garbage happens if you try to enable both direct and palette color - modes to a graphic layer? - - unemulated upd71071 demand mode; - - What is exactly supposed to be a "bus slot"? Does it have an official name? + - pc88va (stock version) has two bogus opcodes. + One is at 0xf0b15 (0x0f 0xfe), another at 0xf0b31 (br 1000h:0c003h). + Latter will make the program flow to jump to lalaland. + This also happens if you load a regular V1/V2 game assuming you have FDC PIO properly + hooked up, is the first opcode actually a Z80 mode switch? + - pc88va is also known to have a slightly different banking scheme and + regular YM2203 as default sound board. + - video emulation is lacking many features, cfr. pc88va_v.cpp; + - keyboard runs on undumped MCU, we currently stick irqs together on + selected keys in order to have an easier QoL while testing this. + - Backport from PC-8801 main map, apply supersets where applicable; + \- IDP has EMUL for upd3301 + \- In emulation mode HW still relies to a i8214, so it bridges thru + main ICU in cascaded mode via IRQ7; + \- beeper or dac1bit (to be confirmed); + \- (other stuff ...) + - Convert FDC usage to pc88va2_fd_if_device, we also need PIO comms for sorcer anyway; + - irq dispatch needs to be revisited, too many instances of sound irq failing for example. + The current hook-ups aren't legal, V50 core bug? + - Very inconsistent SW boot behaviours, either down to: + \- the current hack in FDC PIO port returning RNG; + \- V50 timings; + \- FDC; + - Every PC Engine OS boot tries to write TVRAM ASCII data on every boot to + $exxxx ROM region, banking bug? + - all N88 BASIC entries tries to do stuff with EMM, more banking? + - Convert SASI from PC-9801 to a shared device, apparently it's same i/f; + - Implement bus slot, which should still be PC-8801 EXPansion bus. + + (old notes, to be reordered) - fdc "intelligent mode" has 0x7f as irq vector ... 0x7f is ld a,a and it IS NOT correctly hooked up by the current z80 core - - PC-88VA stock version has two bogus opcodes. One is at 0xf0b15, another at 0xf0b31. - Making a patch for the latter makes the system to jump into a "DIP-Switch" display. - bp f0b31,pc=0xf0b36,g - Update: it never reaches latter with V30->V50 CPU switch fix; - Fix floppy motor hook-up (floppy believes to be always in even if empty drive); - Support for PC8801 compatible mode & PC80S31K (floppy interface); -********************************************************************************************/ + Notes: + - hold F8 at POST to bring software dip settings menu + - PC-88VA-91 is a ROM upgrade kit for a PC-88VA -> VA2/VA3. + Has four roms, marked by VAEG as VUROM00.ROM, VUROM08.ROM, VUROM1.ROM, VUDIC.ROM. + + References: + - PC-88VAテクニカルマニュアル + - http://www.pc88.gr.jp/vafaq/view.php/articlelist/88va/vafaq + +=================================================================================================== + +irq table (line - vector - source): +ICU +irq 0 - 08h - timer 1 +irq 1 - 09h - keyboard irq +irq 2 - 0Ah - VRTC +irq 3 - 0Bh - UINT0 (B24) +irq 4 - 0Ch - RS-232C +irq 5 - 0Dh - UINT1 (B25) +irq 6 - 0Eh - UINT2 (B26) +irq 7 - N/A - Slave (either secondary i8259 or i8214) +i8259 slave +irq 8 - 10H - SGP +irq 9 - 11H - UINT3 (HDD, B27) +irq 10 - 12H - UINT4 (B28) +irq 11 - 13H - FDC +irq 12 - 14H - Sound +irq 13 - 15H - General timer 3 (mouse) +irq 14 - 16H - +irq 15 - 17H - + +trap list (brief, for quick consultation): +brk 82h AH=01h , animefrm uses it +brk 8Ch AH=02h read calendar clock -> CH = hour, CL = minutes, DH = seconds, DL = 0 + +**************************************************************************************************/ #include "emu.h" #include "pc88va.h" #include "softlist_dev.h" +#include +#include "utf8.h" + +#define LOG_FDC (1U << 2) // $1b0-$1b2 accesses +#define LOG_FDC2 (1U << 3) // $1b4-$1b6 accesses (verbose) + +#define VERBOSE (LOG_GENERAL | LOG_FDC) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGFDC(...) LOGMASKED(LOG_FDC, __VA_ARGS__) +#define LOGFDC2(...) LOGMASKED(LOG_FDC2, __VA_ARGS__) // TODO: verify clocks #define MASTER_CLOCK XTAL(8'000'000) // may be XTAL(31'948'800) / 4? (based on PC-8801 and PC-9801) -#define FM_CLOCK (XTAL(31'948'800) / 8) // 3993600 +#define FM_CLOCK (XTAL(31'948'800) / 4) // 3993600 -void pc88va_state::video_start() -{ - m_kanjiram = std::make_unique(0x4000); - m_gfxdecode->gfx(2)->set_source(m_kanjiram.get()); - m_gfxdecode->gfx(3)->set_source(m_kanjiram.get()); -} -void pc88va_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - uint16_t const *const tvram = m_tvram; - - int offs = m_tsp.spr_offset; - for(int i=0;i<(0x100);i+=(8)) - { - int spr_count; - - int ysize = (tvram[(offs + i + 0) / 2] & 0xfc00) >> 10; - int sw = (tvram[(offs + i + 0) / 2] & 0x200) >> 9; - int yp = (tvram[(offs + i + 0) / 2] & 0x1ff); - int xsize = (tvram[(offs + i + 2) / 2] & 0xf800) >> 11; - int md = (tvram[(offs + i + 2) / 2] & 0x400) >> 10; - int xp = (tvram[(offs + i + 2) / 2] & 0x3ff); - int spda = (tvram[(offs + i + 4) / 2] & 0xffff); - int fg_col = (tvram[(offs + i + 6) / 2] & 0xf0) >> 4; - int bc = (tvram[(offs + i + 6) / 2] & 0x08) >> 3; - - if(!sw) - continue; - - if(yp & 0x100) - { - yp &= 0xff; - yp = 0x100 - yp; - } - - if(0) // uhm, makes more sense without the sign? - if(xp & 0x200) - { - xp &= 0x1ff; - xp = 0x200 - xp; - } - - if(md) // 1bpp mode - { - xsize = (xsize + 1) * 32; - ysize = (ysize + 1) * 4; - - if(!(spda & 0x8000)) // correct? - spda *= 2; - - spr_count = 0; - - for(int y_i=0;y_i(tvram[(spda+spr_count) / 2],7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8) >> (15-x_s)) & 1; - - pen = pen & 1 ? fg_col : (bc) ? 8 : -1; - - if(pen != -1) //transparent pen - bitmap.pix(yp+y_i, xp+x_i+(x_s)) = m_palette->pen(pen); - } - spr_count+=2; - } - } - } - else // 4bpp mode (UNTESTED) - { - xsize = (xsize + 1) * 8; - ysize = (ysize + 1) * 4; - - if(!(spda & 0x8000)) // correct? - spda *= 2; - - spr_count = 0; - - for(int y_i=0;y_i(tvram[(spda+spr_count) / 2],7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8)) >> (16-(x_s*8)) & 0xf; - - //if(bc != -1) //transparent pen - bitmap.pix(yp+y_i, xp+x_i+(x_s)) = m_palette->pen(pen); - } - spr_count+=2; - } - } - } - } -} - -/* TODO: this is either a result of an hand-crafted ROM or the JIS stuff is really attribute related ... */ -uint32_t pc88va_state::calc_kanji_rom_addr(uint8_t jis1,uint8_t jis2,int x,int y) -{ - if(jis1 < 0x30) - return ((jis2 & 0x60) << 8) + ((jis1 & 0x07) << 10) + ((jis2 & 0x1f) << 5); - else if(jis1 >= 0x30 && jis1 < 0x3f) - return ((jis2 & 0x60) << 10) + ((jis1 & 0x0f) << 10) + ((jis2 & 0x1f) << 5); - else if(jis1 >= 0x40 && jis1 < 0x50) - return 0x4000 + ((jis2 & 0x60) << 10) + ((jis1 & 0x0f) << 10) + ((jis2 & 0x1f) << 5); - else if(x == 0 && y == 0 && jis1 != 0) // debug stuff, to be nuked in the end - printf("%02x\n",jis1); - - return 0; -} - -void pc88va_state::draw_text(bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - uint16_t const *const tvram = m_tvram; - // TODO: PCG select won't work with this arrangement - uint8_t const *const kanji = memregion("kanji")->base(); - - uint32_t count = tvram[m_tsp.tvram_vreg_offset/2]; - - uint8_t attr_mode = tvram[(m_tsp.tvram_vreg_offset+0xa) / 2] & 0x1f; - /* Note: bug in docs has the following two reversed */ - uint8_t screen_fg_col = (tvram[(m_tsp.tvram_vreg_offset+0xa) / 2] & 0xf000) >> 12; - uint8_t screen_bg_col = (tvram[(m_tsp.tvram_vreg_offset+0xa) / 2] & 0x0f00) >> 8; - - for(int y=0;y<13;y++) - { - for(int x=0;x<80;x++) - { - uint8_t jis1 = (tvram[count] & 0x7f) + 0x20; - uint8_t jis2 = (tvram[count] & 0x7f00) >> 8; - uint16_t lr_half_gfx = ((tvram[count] & 0x8000) >> 15); - - uint32_t tile_num = calc_kanji_rom_addr(jis1,jis2,x,y); - - uint16_t attr = (tvram[count+(m_tsp.attr_offset/2)] & 0x00ff); - - uint8_t fg_col,bg_col,secret,reverse; - //uint8_t blink,dwidc,dwid,uline,hline; - fg_col = bg_col = reverse = secret = 0; //blink = dwidc = dwid = uline = hline = 0; - - switch(attr_mode) - { - /* - xxxx ---- foreground color - ---- xxxx background color - */ - case 0: - fg_col = (attr & 0xf0) >> 4; - bg_col = (attr & 0x0f) >> 0; - break; - /* - xxxx ---- foreground color - ---- x--- horizontal line - ---- -x-- reverse - ---- --x- blink - ---- ---x secret (hide text) - background color is defined by screen control table values - */ - case 1: - fg_col = (attr & 0xf0) >> 4; - bg_col = screen_bg_col; - //hline = (attr & 0x08) >> 3; - reverse = (attr & 0x04) >> 2; - //blink = (attr & 0x02) >> 1; - secret = (attr & 0x01) >> 0; - break; - /* - x--- ---- dwidc - -x-- ---- dwid - --x- ---- uline - ---x ---- hline - ---- -x-- reverse - ---- --x- blink - ---- ---x secret (hide text) - background and foreground colors are defined by screen control table values - */ - case 2: - fg_col = screen_fg_col; - bg_col = screen_bg_col; - //dwidc = (attr & 0x80) >> 7; - //dwid = (attr & 0x40) >> 6; - //uline = (attr & 0x20) >> 5; - //hline = (attr & 0x10) >> 4; - reverse = (attr & 0x04) >> 2; - //blink = (attr & 0x02) >> 1; - secret = (attr & 0x01) >> 0; - break; - /* - ---- x--- mixes between mode 0 and 2 - - xxxx 1--- foreground color - ---- 1xxx background color - 2) - x--- 0--- dwidc - -x-- 0--- dwid - --x- 0--- uline - ---x 0--- hline - ---- 0x-- reverse - ---- 0-x- blink - ---- 0--x secret (hide text) - background and foreground colors are defined by screen control table values - */ - case 3: - { - if(attr & 0x8) - { - fg_col = (attr & 0xf0) >> 4; - bg_col = (attr & 0x07) >> 0; - } - else - { - fg_col = screen_fg_col; - bg_col = screen_bg_col; - //dwidc = (attr & 0x80) >> 7; - //dwid = (attr & 0x40) >> 6; - //uline = (attr & 0x20) >> 5; - //hline = (attr & 0x10) >> 4; - reverse = (attr & 0x04) >> 2; - //blink = (attr & 0x02) >> 1; - secret = (attr & 0x01) >> 0; - } - } - break; - /* - x--- ---- blink - -xxx ---- background color - ---- xxxx foreground color - */ - case 4: - fg_col = (attr & 0x0f) >> 0; - bg_col = (attr & 0x70) >> 4; - //blink = (attr & 0x80) >> 7; - break; - /* - x--- ---- blink - -xxx ---- background color - ---- xxxx foreground color - hline is enabled if foreground color is 1 or 9 - */ - case 5: - fg_col = (attr & 0x0f) >> 0; - bg_col = (attr & 0x70) >> 4; - //blink = (attr & 0x80) >> 7; - //if((fg_col & 7) == 1) - //hline = 1; - break; - default: - popmessage("Illegal text tilemap attribute mode %02x, contact MESSdev",attr_mode); - return; - } - - for(int yi=0;yi<16;yi++) - { - for(int xi=0;xi<8;xi++) - { - int res_x = x*8+xi; - int res_y = y*16+yi; - - if(!cliprect.contains(res_x, res_y)) - continue; - - int pen = kanji[((yi*2)+lr_half_gfx)+tile_num] >> (7-xi) & 1; - - if(reverse) - pen = pen & 1 ? bg_col : fg_col; - else - pen = pen & 1 ? fg_col : bg_col; - - if(secret) { pen = 0; } //hide text - - if(pen != -1) //transparent - bitmap.pix(res_y, res_x) = m_palette->pen(pen); - } - } - - count++; - count&=0xffff; - } - } -} - -uint32_t pc88va_state::screen_update_pc88va(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - uint8_t pri,cur_pri_lv; - uint32_t screen_pri; - bitmap.fill(0, cliprect); - - if(m_tsp.disp_on == 0) // don't bother if we are under DSPOFF command - return 0; - - /* - m_video_pri_reg[0] - xxxx ---- ---- ---- priority 3 - ---- xxxx ---- ---- priority 2 - ---- ---- xxxx ---- priority 1 - ---- ---- ---- xxxx priority 0 - m_video_pri_reg[1] - ---- ---- xxxx ---- priority 5 - ---- ---- ---- xxxx priority 4 - - Note that orthogonality level is actually REVERSED than the level number it indicates, so we have to play a little with the data for an easier usage ... - */ - - screen_pri = (m_video_pri_reg[1] & 0x00f0) >> 4; // priority 5 - screen_pri|= (m_video_pri_reg[1] & 0x000f) << 4; // priority 4 - screen_pri|= (m_video_pri_reg[0] & 0xf000) >> 4; // priority 3 - screen_pri|= (m_video_pri_reg[0] & 0x0f00) << 4; // priority 2 - screen_pri|= (m_video_pri_reg[0] & 0x00f0) << 12; // priority 1 - screen_pri|= (m_video_pri_reg[0] & 0x000f) << 20; // priority 0 - - for(pri=0;pri<6;pri++) - { - cur_pri_lv = (screen_pri >> (pri*4)) & 0xf; - - if(cur_pri_lv & 8) // enable layer - { - if(pri <= 1) // (direct color mode, priority 5 and 4) - { - // 8 = graphic 0 - // 9 = graphic 1 - } - else - { - switch(cur_pri_lv & 3) // (palette color mode) - { - case 0: draw_text(bitmap,cliprect); break; - case 1: if(m_tsp.spr_on) { draw_sprites(bitmap,cliprect); } break; - case 2: /* A = graphic 0 */ break; - case 3: /* B = graphic 1 */ break; - } - } - } - } - - return 0; -} - -void pc88va_state::pc88va_map(address_map &map) -{ - map(0x00000, 0x7ffff).ram(); -// map(0x80000, 0x9ffff).ram(); // EMM - map(0xa0000, 0xdffff).m(m_sysbank, FUNC(address_map_bank_device::amap16)); - map(0xe0000, 0xeffff).bankr("rom00_bank"); - map(0xf0000, 0xfffff).bankr("rom10_bank"); -} - -/* 0x00000 - 0x3ffff Kanji ROM 1*/ -/* 0x40000 - 0x4ffff Kanji ROM 2*/ -/* 0x50000 - 0x53fff Backup RAM */ -/* above that is a NOP presumably */ uint8_t pc88va_state::kanji_ram_r(offs_t offset) { return m_kanjiram[offset]; } +// TODO: settings area should be write protected depending on the m_backupram_wp bit, separate from this void pc88va_state::kanji_ram_w(offs_t offset, uint8_t data) { - // TODO: there's an area that can be write protected m_kanjiram[offset] = data; m_gfxdecode->gfx(2)->mark_dirty(offset / 8); m_gfxdecode->gfx(3)->mark_dirty(offset / 32); } - -void pc88va_state::sysbank_map(address_map &map) +u8 pc88va_state::port40_r() { - // 0 select bus slot (?) - // 1 tvram - map(0x040000, 0x04ffff).ram().share("tvram"); - // 4 gvram - map(0x100000, 0x13ffff).ram().share("gvram"); - // 8-9 kanji - map(0x200000, 0x23ffff).rom().region("kanji", 0x00000); - map(0x240000, 0x24ffff).rom().region("kanji", 0x40000); - map(0x250000, 0x253fff).rw(FUNC(pc88va_state::kanji_ram_r),FUNC(pc88va_state::kanji_ram_w)); - // c-d dictionary - map(0x300000, 0x37ffff).rom().region("dictionary", 0); + u8 data = 0; + // vrtc + data = m_screen->vblank() << 5; + data |= m_rtc->data_out_r() << 4; + data |= (ioport("DSW")->read() & 1) ? 2 : 0; + + return data | 0xc0; } -/* IDP = NEC uPD72022 */ -uint8_t pc88va_state::idp_status_r() +void pc88va_state::port40_w(offs_t offset, u8 data) { -/* - x--- ---- LP Light-pen signal detection (with VA use failure) - -x-- ---- VB Vertical elimination period - --x- ---- SC Sprite control (sprite over/collision) - ---x ---- ER Error occurrence - ---- x--- In the midst of execution of EMEN emulation development - ---- -x-- In the midst of BUSY command execution - ---- --x- OBF output data buffer full - ---- ---x IBF input data buffer full (command/parameter commonness) -*/ - return 0x00; -} + m_rtc->stb_w((data & 2) >> 1); + m_rtc->clk_w((data & 4) >> 2); - -#define SYNC 0x10 -#define DSPON 0x12 -#define DSPOFF 0x13 -#define DSPDEF 0x14 -#define CURDEF 0x15 -#define ACTSCR 0x16 -#define CURS 0x1e -#define EMUL 0x8c -#define EXIT 0x88 -#define SPRON 0x82 -#define SPROFF 0x83 -#define SPRSW 0x85 -#define SPROV 0x81 - -void pc88va_state::idp_command_w(uint8_t data) -{ - switch(data) + if((m_device_ctrl_data & 0x40) != (data & 0x40)) { - /* 0x10 - SYNC: sets CRTC values */ - case SYNC: m_cmd = SYNC; m_buf_size = 14; m_buf_index = 0; break; + attotime new_time = machine().time(); - /* 0x12 - DSPON: set DiSPlay ON and set up tvram table vreg */ - case DSPON: m_cmd = DSPON; m_buf_size = 3; m_buf_index = 0; break; - - /* 0x13 - DSPOFF: set DiSPlay OFF */ - case DSPOFF: m_cmd = DSPOFF; m_tsp.disp_on = 0; break; - - /* 0x14 - DSPDEF: set DiSPlay DEFinitions */ - case DSPDEF: m_cmd = DSPDEF; m_buf_size = 6; m_buf_index = 0; break; - - /* 0x15 - CURDEF: set CURsor DEFinition */ - case CURDEF: m_cmd = CURDEF; m_buf_size = 1; m_buf_index = 0; break; - - /* 0x16 - ACTSCR: ??? */ - case ACTSCR: m_cmd = ACTSCR; m_buf_size = 1; m_buf_index = 0; break; - - /* 0x15 - CURS: set CURSor position */ - case CURS: m_cmd = CURS; m_buf_size = 4; m_buf_index = 0; break; - - /* 0x8c - EMUL: set 3301 EMULation */ - case EMUL: m_cmd = EMUL; m_buf_size = 4; m_buf_index = 0; break; - - /* 0x88 - EXIT: ??? */ - case EXIT: m_cmd = EXIT; break; - - /* 0x82 - SPRON: set SPRite ON */ - case SPRON: m_cmd = SPRON; m_buf_size = 3; m_buf_index = 0; break; - - /* 0x83 - SPROFF: set SPRite OFF */ - case SPROFF: m_cmd = SPROFF; m_tsp.spr_on = 0; break; - - /* 0x85 - SPRSW: ??? */ - case SPRSW: m_cmd = SPRSW; m_buf_size = 1; m_buf_index = 0; break; - - /* 0x81 - SPROV: set SPRite OVerflow information */ - /* - -x-- ---- Sprite Over flag - --x- ---- Sprite Collision flag - ---x xxxx First sprite that caused Sprite Over event - */ - case SPROV: m_cmd = SPROV; /* TODO: where it returns the info? */ break; - - /* TODO: 0x89 shouldn't trigger, should be one of the above commands */ - /* Update: actually 0x89 is mask command */ - default: m_cmd = 0x00; printf("PC=%05x: Unknown IDP %02x cmd set\n",m_maincpu->pc(),data); break; - } -} - -// TODO: checkout this one -void pc88va_state::tsp_sprite_enable(uint32_t spr_offset, uint16_t sw_bit) -{ - uint32_t target_offset = (spr_offset & 0xffff)/2; -// address_space &space = m_maincpu->space(AS_PROGRAM); - -// space.write_word(spr_offset, space.read_word(spr_offset) & ~0x200); -// space.write_word(spr_offset, space.read_word(spr_offset) | (sw_bit & 0x200)); - m_tvram[target_offset] = (m_tvram[target_offset] & ~0x200) | (sw_bit & 0x200); -} - -/* TODO: very preliminary, needs something showable first */ -void pc88va_state::execute_sync_cmd() -{ - /* - ???? ???? [0] - unknown - ???? ???? [1] - unknown - --xx xxxx [2] - h blank start - --xx xxxx [3] - h border start - xxxx xxxx [4] - h visible area - --xx xxxx [5] - h border end - --xx xxxx [6] - h blank end - --xx xxxx [7] - h sync - --xx xxxx [8] - v blank start - --xx xxxx [9] - v border start - xxxx xxxx [A] - v visible area - -x-- ---- [B] - v visible area (bit 9) - --xx xxxx [C] - v border end - --xx xxxx [D] - v blank end - --xx xxxx [E] - v sync - */ - rectangle visarea; - attoseconds_t refresh; - uint16_t x_vis_area,y_vis_area; - - //printf("V blank start: %d\n",(sync_cmd[0x8])); - //printf("V border start: %d\n",(sync_cmd[0x9])); - //printf("V Visible Area: %d\n",(sync_cmd[0xa])|((sync_cmd[0xb] & 0x40)<<2)); - //printf("V border end: %d\n",(sync_cmd[0xc])); - //printf("V blank end: %d\n",(sync_cmd[0xd])); - - x_vis_area = m_buf_ram[4] * 4; - y_vis_area = (m_buf_ram[0xa])|((m_buf_ram[0xb] & 0x40)<<2); - - visarea.set(0, x_vis_area - 1, 0, y_vis_area - 1); - - //if(y_vis_area == 400) - // refresh = HZ_TO_ATTOSECONDS(24800) * x_vis_area * y_vis_area; //24.8 KHz - //else - // refresh = HZ_TO_ATTOSECONDS(15730) * x_vis_area * y_vis_area; //15.73 KHz - - refresh = HZ_TO_ATTOSECONDS(60); - - m_screen->configure(640, 480, visarea, refresh); -} - -void pc88va_state::execute_dspon_cmd() -{ - /* - [0] text table offset (hi word) - [1] unknown - [2] unknown - */ - m_tsp.tvram_vreg_offset = m_buf_ram[0] << 8; - m_tsp.disp_on = 1; -} - -void pc88va_state::execute_dspdef_cmd() -{ - /* - [0] attr offset (lo word) - [1] attr offset (hi word) - [2] pitch (character code interval x 16, i.e. 0x20 = 2 bytes - [3] line height - [4] hline vertical position - [5] blink number - */ - m_tsp.attr_offset = m_buf_ram[0] | m_buf_ram[1] << 8; - m_tsp.pitch = (m_buf_ram[2] & 0xf0) >> 4; - m_tsp.line_height = m_buf_ram[3] + 1; - m_tsp.h_line_pos = m_buf_ram[4]; - m_tsp.blink = (m_buf_ram[5] & 0xf8) >> 3; -} - -void pc88va_state::execute_curdef_cmd() -{ - /* - xxxx x--- [0] Sprite Cursor number (sprite RAM entry) - ---- --x- [0] show cursor bit (actively modifies the spriteram entry) - ---- ---x [0] Blink Enable - */ - - /* TODO: needs basic sprite emulation */ - m_tsp.curn = (m_buf_ram[0] & 0xf8); - m_tsp.curn_blink = (m_buf_ram[0] & 1); - - tsp_sprite_enable(m_tsp.spr_offset + m_tsp.curn, (m_buf_ram[0] & 2) << 8); -} - -void pc88va_state::execute_actscr_cmd() -{ - /* - This command assigns a strip where the cursor is located. - xxxx xxxx [0] strip ID * 32 (???) - */ - - /* TODO: no idea about this command */ - //printf("ACTSCR: %02x\n",m_buf_ram[0]); -} - -void pc88va_state::execute_curs_cmd() -{ - /* - [0] Cursor Position Y (lo word) - [1] Cursor Position Y (hi word) - [2] Cursor Position X (lo word) - [3] Cursor Position X (hi word) - */ - - m_tsp.cur_pos_y = m_buf_ram[0] | m_buf_ram[1] << 8; - m_tsp.cur_pos_x = m_buf_ram[2] | m_buf_ram[3] << 8; -} - -void pc88va_state::execute_emul_cmd() -{ - /* - [0] Emulate target strip ID x 32 - [1] The number of chars - [2] The number of attributes - [3] The number of lines - */ - - // TODO: this starts 3301 video emulation - //popmessage("Warning: TSP executes EMUL command, contact MESSdev"); -} - -void pc88va_state::execute_spron_cmd() -{ - /* - [0] Sprite Table Offset (hi word) - [1] (unknown / reserved) - xxxx x--- [2] HSPN: Maximum number of sprites in one raster (num + 1) for Sprite Over - ---- --x- [2] MG: all sprites are 2x zoomed vertically when 1 - ---- ---x [2] GR: 1 to enable the group collision detection - */ - m_tsp.spr_offset = m_buf_ram[0] << 8; - m_tsp.spr_on = 1; - printf("SPR TABLE %02x %02x %02x\n",m_buf_ram[0],m_buf_ram[1],m_buf_ram[2]); -} - -void pc88va_state::execute_sprsw_cmd() -{ - /* - Toggle an individual sprite in the sprite ram entry - [0] xxxx x--- target sprite number - [0] ---- --x- sprite off/on switch - */ - - tsp_sprite_enable(m_tsp.spr_offset + (m_buf_ram[0] & 0xf8), (m_buf_ram[0] & 2) << 8); -} - -void pc88va_state::idp_param_w(uint8_t data) -{ - if(m_cmd == DSPOFF || m_cmd == EXIT || m_cmd == SPROFF || m_cmd == SPROV) // no param commands - return; - - m_buf_ram[m_buf_index] = data; - m_buf_index++; - - if(m_buf_index >= m_buf_size) - { - m_buf_index = 0; - switch(m_cmd) + if(data & 0x40 && (new_time - m_mouse.time) > mouse_limit_hz()) { - case SYNC: execute_sync_cmd(); break; - case DSPON: execute_dspon_cmd(); break; - case DSPDEF: execute_dspdef_cmd(); break; - case CURDEF: execute_curdef_cmd(); break; - case ACTSCR: execute_actscr_cmd(); break; - case CURS: execute_curs_cmd(); break; - case EMUL: execute_emul_cmd(); break; - case SPRON: execute_spron_cmd(); break; - case SPRSW: execute_sprsw_cmd(); break; - - default: - //printf("%02x\n",data); - break; + m_mouse.phase = 0; } + else + { + m_mouse.phase ++; + m_mouse.phase &= 3; + } + + if(m_mouse.phase == 0) + { + const u8 mouse_x = ioport("MOUSEX")->read(); + const u8 mouse_y = ioport("MOUSEY")->read(); + + m_mouse.lx = (mouse_x - m_mouse.prev_dx) & 0xff; + m_mouse.ly = (mouse_y - m_mouse.prev_dy) & 0xff; + + m_mouse.prev_dx = mouse_x; + m_mouse.prev_dy = mouse_y; + } + + m_mouse.time = machine().time(); } + + m_device_ctrl_data = data; } -void pc88va_state::palette_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask) +inline attotime pc88va_state::mouse_limit_hz() { - int r,g,b; - COMBINE_DATA(&m_palram[offset]); - - b = (m_palram[offset] & 0x001e) >> 1; - r = (m_palram[offset] & 0x03c0) >> 6; - g = (m_palram[offset] & 0x7800) >> 11; - - m_palette->set_pen_color(offset,pal4bit(r),pal4bit(g),pal4bit(b)); + return attotime::from_hz(1800); } -uint16_t pc88va_state::sys_port4_r() + +u8 pc88va_state::opn_porta_r() { - uint8_t vrtc,sw1; - vrtc = (m_screen->vpos() < 200) ? 0x20 : 0x00; // vblank + if(ioport("BOARD_CONFIG")->read() & 2) + { + u8 shift, res; - sw1 = (ioport("DSW")->read() & 1) ? 2 : 0; + shift = (m_mouse.phase & 1) ? 0 : 4; + res = (m_mouse.phase & 2) ? m_mouse.ly : m_mouse.lx; - return vrtc | sw1 | 0xc0; +// logerror("%d\n",m_mouse.phase); + + return ((res >> shift) & 0x0f) | 0xf0; + } + + return ioport("OPN_PA")->read(); } -uint16_t pc88va_state::bios_bank_r() +void pc88va_state::rtc_w(offs_t offset, u8 data) { - return m_bank_reg; + m_rtc->c0_w((data & 1) >> 0); + m_rtc->c1_w((data & 2) >> 1); + m_rtc->c2_w((data & 4) >> 2); + m_rtc->data_in_w((data & 8) >> 3); + + // TODO: remaining bits } +/* + * $152 + * -x-- ---- ---- ---- SMM compatibility mode (1) V3 (0) V1/V2 + * ---x ---- ---- ---- GMSP VRAM drawing mode (0) multiplane (1) single plane + * ---- xxxx ---- ---- SMBC (0xa0000 - 0xdffff RAM bank) + * ---- ---- xxxx ---- RBC13-RBC10 (0xf0000 - 0xfffff ROM bank) + * ---- ---- 0xxx ---- internal ROM entry + * \- settings 2 to 5 are + * \- settings 6 and 7 are reserved + * ---- ---- 1xxx ---- select bus slot ROM + * ---- ---- ---- xxxx RBC03-RBC00 (0xe0000 - 0xeffff ROM bank) + * ---- ---- ---- 0xxx internal ROM entry + * ---- ---- ---- 1xxx select bus slot ROM + */ void pc88va_state::bios_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask) { - /* - -x-- ---- ---- ---- SMM (compatibility mode) - ---x ---- ---- ---- GMSP (VRAM drawing mode) - ---- xxxx ---- ---- SMBC (0xa0000 - 0xdffff RAM bank) - ---- ---- xxxx ---- RBC1 (0xf0000 - 0xfffff ROM bank) - ---- ---- ---- xxxx RBC0 (0xe0000 - 0xeffff ROM bank) - */ COMBINE_DATA(&m_bank_reg); /* SMBC */ @@ -744,19 +234,24 @@ void pc88va_state::bios_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask) /* RBC0 */ { uint8_t *ROM00 = memregion("rom00")->base(); - - membank("rom00_bank")->set_base(&ROM00[(m_bank_reg & 0xf)*0x10000]); // TODO: docs says that only 0 - 5 are used, dunno why ... + membank("rom00_bank")->set_base(&ROM00[(m_bank_reg & 0xf) * 0x10000]); } } +uint16_t pc88va_state::bios_bank_r() +{ + return m_bank_reg; +} + +// TODO: status for bus slot ROM banking, at 0xf0000-0xfffff uint8_t pc88va_state::rom_bank_r() { - return 0xff; // bit 7 low is va91 rom bank status + // bit 7 low is PC-88VA-91 rom bank status + return 0xff; } uint8_t pc88va_state::key_r(offs_t offset) { - // note row D bit 2 does something at POST ... some kind of test mode? static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7", "KEY8", "KEY9", "KEYA", "KEYB", @@ -775,16 +270,44 @@ void pc88va_state::backupram_wp_0_w(uint16_t data) m_backupram_wp = 0; } +/* + * $190 system port 5 + * ---x ---- FBEEP Force BEEP (1) allow + * ---- xx-- AVC2/AVC1 video output control + * ---- 00-- TV/video mode, offline (?) + * ---- 10-- Analog RGB mode (at reset, default) + * ---- x1-- + * ---- ---x RSTMD reset status + * \- works as software Power On Reset flag during VA POST + */ +void pc88va_state::sys_port5_w(u8 data) +{ + m_rstmd = bool(BIT(data, 0)); + LOG("I/O $190 %02x\n", data); +} + +u8 pc88va_state::sys_port5_r() +{ + return m_rstmd | 8; +} + uint8_t pc88va_state::hdd_status_r() { return 0x20; } +// TODO: convert to pc80s31k family +uint8_t pc88va_state::fake_subfdc_r() +{ + return machine().rand(); +} + uint8_t pc88va_state::pc88va_fdc_r(offs_t offset) { - printf("%08x\n",offset); + if (!machine().side_effects_disabled()) + LOGFDC("Unhandled read $%04x\n", (offset << 1) + 0x1b0); - switch(offset*2) + switch(offset << 1) { case 0x00: return 0; // FDC mode register case 0x02: return 0; // FDC control port 0 @@ -799,53 +322,67 @@ uint8_t pc88va_state::pc88va_fdc_r(offs_t offset) TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_timer) { - if(m_fdc_ctrl_2 & 4) // XTMASK + if(m_xtmask) { m_pic2->ir3_w(0); m_pic2->ir3_w(1); } + + m_fdc_timer->adjust(attotime::from_msec(100)); } TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_motor_start_0) { m_fdd[0]->get_device()->mon_w(0); - m_fdc_motor_status[0] = 1; } TIMER_CALLBACK_MEMBER(pc88va_state::pc88va_fdc_motor_start_1) { m_fdd[1]->get_device()->mon_w(0); - m_fdc_motor_status[1] = 1; } void pc88va_state::pc88va_fdc_update_ready(floppy_image_device *, int) { - bool ready = m_fdc_ctrl_2 & 0x40; + if (!BIT(m_fdc_ctrl_2, 5)) + return; + bool force_ready = (BIT(m_fdc_ctrl_2, 6)); - floppy_image_device *floppy; - floppy = m_fdd[0]->get_device(); - if(floppy && ready) - ready = floppy->ready_r(); - floppy = m_fdd[1]->get_device(); - if(floppy && ready) - ready = floppy->ready_r(); + floppy_image_device *floppy0, *floppy1; + floppy0 = m_fdd[0]->get_device(); + floppy1 = m_fdd[1]->get_device(); + if (!floppy0 && !floppy1) + force_ready = false; - m_fdc->ready_w(ready); + //if(floppy && force_ready) + // ready = floppy->ready_r(); + + //if(floppy && force_ready) + // ready = floppy->ready_r(); + + LOGFDC2("Force ready signal %d\n", force_ready); + + if (force_ready) + { + m_fdc->set_ready_line_connected(0); + m_fdc->ready_w(0); + } + else + m_fdc->set_ready_line_connected(1); } void pc88va_state::pc88va_fdc_w(offs_t offset, uint8_t data) { - printf("%08x %02x\n",offset<<1,data); - switch(offset<<1) + switch(offset << 1) { /* ---- ---x MODE: FDC op mode (0) Intelligent (1) DMA */ case 0x00: // FDC mode register m_fdc_mode = data & 1; - #if TEST_SUBFDC - m_fdccpu->set_input_line(INPUT_LINE_HALT, (m_fdc_mode) ? ASSERT_LINE : CLEAR_LINE); - #endif + LOGFDC("$1b0 FDC op mode (%02x) %s mode\n" + , data + , m_fdc_mode ? "DMA" : "Intelligent (PIO)" + ); break; /* --x- ---- CLK: FDC clock selection (0) 4.8MHz (1) 8 MHz @@ -854,58 +391,110 @@ void pc88va_state::pc88va_fdc_w(offs_t offset, uint8_t data) ---- --xx RV1/RV0: Drive 1/0 mode selection (0) 2D and 2DD mode (1) 2HD mode */ case 0x02: // FDC control port 0 - m_fdd[0]->get_device()->set_rpm(data & 0x01 ? 360 : 300); - m_fdd[1]->get_device()->set_rpm(data & 0x02 ? 360 : 300); + { + const bool clk = bool(BIT(data, 5)); + const bool rv1 = bool(BIT(data, 1)); + const bool rv0 = bool(BIT(data, 0)); + LOGFDC("$1b2 FDC control port 0 (%02x) %s CLK| %d DS1| %d%d TD1/TD0| %d%d RV1/RV0\n" + , data + , clk ? " 8 MHz" : "4.8 MHz" + , !bool(BIT(data, 4)) + , bool(BIT(data, 3)) + , bool(BIT(data, 2)) + , rv1 + , rv0 + ); + m_fdd[0]->get_device()->set_rpm(rv0 ? 360 : 300); + m_fdd[1]->get_device()->set_rpm(rv1 ? 360 : 300); - m_fdc->set_rate(data & 0x20 ? 500000 : 250000); + //m_fdd[0]->get_device()->ds_w(!BIT(data, 4)); + //m_fdd[1]->get_device()->ds_w(!BIT(data, 4)); + + // TODO: is this correct? sounds more like a controller clock change, while TD1/TD0 should do the rate change + m_fdc->set_rate(clk ? 500000 : 250000); break; + } /* - ---- x--- PCM: ? + ---- x--- PCM: precompensation control (1) on ---- --xx M1/M0: Drive 1/0 motor control (0) NOP (1) Change motor status */ case 0x04: - if(data & 1) - { - m_fdd[0]->get_device()->mon_w(1); - if(m_fdc_motor_status[0] == 0) - m_motor_start_timer[0]->adjust(attotime::from_msec(505)); - else - m_fdc_motor_status[0] = 0; - } + { + const bool m0 = bool(BIT(data, 0)); + const bool m1 = bool(BIT(data, 1)); - if(data & 2) - { + LOGFDC2("$1b4 FDC control port 1 (%02x) %d PCM| %d%d M1/M0\n" + , data + , bool(BIT(data, 3)) + , m1 + , m0 + ); + + // TODO: fine grain motor timings + // docs claims 600 msecs, must be more complex than that + if( m0 ) + m_motor_start_timer[0]->adjust(attotime::from_msec(505)); + else + m_fdd[0]->get_device()->mon_w(1); + + + if( m1 ) + m_motor_start_timer[1]->adjust(attotime::from_msec(505)); + else m_fdd[1]->get_device()->mon_w(1); - if(m_fdc_motor_status[1] == 0) - m_motor_start_timer[1]->adjust(attotime::from_msec(505)); - else - m_fdc_motor_status[1] = 0; - } break; + } + /* - x--- ---- FDCRST: FDC Reset - -xx- ---- FDCFRY FRYCEN: FDC force ready control - ---x ---- DMAE: DMA Enable (0) Prohibit DMA (1) Enable DMA - ---- -x-- XTMASK: FDC timer IRQ mask (0) Disable (1) Enable - ---- ---x TTRG: FDC timer trigger (0) FDC timer clearing (1) FDC timer start - */ + * FDC control port 2 + * x--- ---- FDCRST: FDC Reset + * -xx- ---- FDCFRY FRYCEN: FDC force ready control + * -x0- ---- ignored + * -01- ---- force ready release + * -11- ---- force ready assert + * ---x ---- DMAE: DMA Enable (0) Prohibit DMA (1) Enable DMA + * ---- -x-- XTMASK: FDC timer IRQ mask (0) Disable (1) Enable + * ---- ---x TTRG: FDC timer trigger (0) FDC timer clearing (1) FDC timer start + */ case 0x06: - //printf("%02x\n",data); - if(data & 1) + { + const bool fdcrst = bool(BIT(data, 7)); + const bool ttrg = bool(BIT(data, 0)); + const bool cur_xtmask = bool(BIT(data, 2)); + LOGFDC2("$1b6 FDC control port 2 (%02x) %d FDCRST| %d%d FDCFRY| %d DMAE| %d XTMASK| %d TTRG\n" + , data + , fdcrst + , bool(BIT(data, 6)) + , bool(BIT(data, 5)) + , bool(BIT(data, 4)) + , cur_xtmask + , ttrg + ); + + if( ttrg && !BIT(m_fdc_ctrl_2, 0) ) m_fdc_timer->adjust(attotime::from_msec(100)); + else if (!ttrg && BIT(m_fdc_ctrl_2, 0) ) + m_fdc_timer->adjust(attotime::never); - if((m_fdc_ctrl_2 & 0x10) != (data & 0x10)) - m_dmac->dreq2_w(1); + m_xtmask = cur_xtmask; - if(data & 0x80) // correct? + //if (!BIT(m_fdc_ctrl_2, 4) && BIT(data, 4)) + // m_maincpu->dreq_w<2>(1); + //m_dmac->dreq2_w(1); + + // TODO: 0 -> 1 transition? + if( fdcrst ) m_fdc->reset(); m_fdc_ctrl_2 = data; + //m_fdd[0]->get_device()->mon_w(!(BIT(data, 5))); + pc88va_fdc_update_ready(nullptr, 0); - break; // FDC control port 2 + break; + } } } @@ -919,14 +508,26 @@ uint16_t pc88va_state::sysop_r() return 0xfffc | sys_op; // docs says all the other bits are high } -uint16_t pc88va_state::screen_ctrl_r() +/* + * x--- ---- MINTEN (TCU irq enable) + * ---- --xx MTP1/MTP0 general purpose timer 3 interval + * ---- --00 120 Hz + * ---- --01 60 Hz + * ---- --10 30 Hz + * ---- --11 15 Hz + */ +void pc88va_state::timer3_ctrl_reg_w(uint8_t data) { - return m_screen_ctrl_reg; -} + m_timer3_io_reg = data; -void pc88va_state::screen_ctrl_w(uint16_t data) -{ - m_screen_ctrl_reg = data; + if(data & 0x80) + m_t3_mouse_timer->adjust(attotime::from_hz(120 >> (m_timer3_io_reg & 3))); + else + { + // TODO: confirm me + //m_pic2->ir5_w(0); + m_t3_mouse_timer->adjust(attotime::never); + } } TIMER_CALLBACK_MEMBER(pc88va_state::t3_mouse_callback) @@ -939,27 +540,6 @@ TIMER_CALLBACK_MEMBER(pc88va_state::t3_mouse_callback) } } -void pc88va_state::timer3_ctrl_reg_w(uint8_t data) -{ - /* - x--- ---- MINTEN (TCU irq enable) - ---- --xx general purpose timer 3 interval (120, 60, 30, 15) - */ - m_timer3_io_reg = data; - - if(data & 0x80) - m_t3_mouse_timer->adjust(attotime::from_hz(120 >> (m_timer3_io_reg & 3))); - else - { - m_pic2->ir5_w(0); - m_t3_mouse_timer->adjust(attotime::never); - } -} - -void pc88va_state::video_pri_w(offs_t offset, uint16_t data, uint16_t mem_mask) -{ - COMBINE_DATA(&m_video_pri_reg[offset]); -} uint8_t pc88va_state::backupram_dsw_r(offs_t offset) { @@ -969,31 +549,91 @@ uint8_t pc88va_state::backupram_dsw_r(offs_t offset) return m_kanjiram[0x1fc6 / 2] & 0xff; } +// TODO: pc8801_state::port31_w void pc88va_state::sys_port1_w(uint8_t data) { - // ... + LOG("I/O $31 %02x\n", data); } -#if !TEST_SUBFDC -uint8_t pc88va_state::no_subfdc_r() +uint8_t pc88va_state::misc_ctrl_r() { - return machine().rand(); + return m_misc_ctrl; } -#endif -void pc88va_state::pc88va_io_map(address_map &map) +void pc88va_state::misc_ctrl_w(uint8_t data) +{ + m_misc_ctrl = data; + + m_sound_irq_enable = ((data & 0x80) == 0); + + if (m_sound_irq_enable) + int4_irq_w(m_sound_irq_pending); +} + + +/**************************************** + * Address maps + ***************************************/ + +void pc88va_state::main_map(address_map &map) +{ + map(0x00000, 0x7ffff).ram().share("workram"); +// map(0x80000, 0x9ffff).ram(); // EMM + map(0xa0000, 0xdffff).m(m_sysbank, FUNC(address_map_bank_device::amap16)); + map(0xe0000, 0xeffff).bankr("rom00_bank"); + map(0xf0000, 0xfffff).bankr("rom10_bank"); +} + +void pc88va_state::sysbank_map(address_map &map) +{ + // 0 select bus slot + // 1 tvram + map(0x040000, 0x04ffff).ram().share("tvram"); + // FIXME: BASIC and pacmana expects to r/w to 0x60000-0x7ffff on loading, assume mirror if not a core bug. + map(0x050000, 0x07ffff).ram(); + // 4 gvram + map(0x100000, 0x13ffff).ram().share("gvram"); + // 8-9 kanji + // Kanji ROM + map(0x200000, 0x23ffff).rom().region("kanji", 0x00000); + // ANK ROM + map(0x240000, 0x24ffff).rom().region("kanji", 0x40000); + // Backup RAM & PCG + map(0x250000, 0x253fff).rw(FUNC(pc88va_state::kanji_ram_r),FUNC(pc88va_state::kanji_ram_w)); + // c-d dictionary + map(0x300000, 0x37ffff).rom().region("dictionary", 0); +} + +// SGP has its own window space about how and what it can see on RMW +void pc88va_state::sgp_map(address_map &map) +{ + map(0x000000, 0x07ffff).ram().share("workram"); +// map(0x080000, 0x09ffff) more main RAM or EMM +// map(0x0a0000, 0x0fffff) EMM $a0000 to $fffff (?) + map(0x100000, 0x13ffff).rom().region("kanji", 0x00000); + map(0x140000, 0x14ffff).rom().region("kanji", 0x40000); + map(0x150000, 0x153fff).rw(FUNC(pc88va_state::kanji_ram_r),FUNC(pc88va_state::kanji_ram_w)); + map(0x180000, 0x18ffff).ram().share("tvram"); + map(0x200000, 0x23ffff).ram().share("gvram"); +} + +// TODO: I/O 0x00xx is almost same as pc8801 +// (*) are specific N88 V1 / V2 ports +void pc88va_state::io_map(address_map &map) { map(0x0000, 0x000f).r(FUNC(pc88va_state::key_r)); // Keyboard ROW reading -// map(0x0010, 0x0010) Printer / Calendar Clock Interface + map(0x0010, 0x0010).w(FUNC(pc88va_state::rtc_w)); // Printer / Calendar Clock Interface map(0x0020, 0x0021).noprw(); // RS-232C map(0x0030, 0x0031).rw(FUNC(pc88va_state::backupram_dsw_r), FUNC(pc88va_state::sys_port1_w)); // 0x30 (R) DSW1 (W) Text Control Port 0 / 0x31 (R) DSW2 (W) System Port 1 -// map(0x0032, 0x0032) (R) ? (W) System Port 2 + map(0x0032, 0x0032).rw(FUNC(pc88va_state::misc_ctrl_r), FUNC(pc88va_state::misc_ctrl_w)); // map(0x0034, 0x0034) GVRAM Control Port 1 // map(0x0035, 0x0035) GVRAM Control Port 2 - map(0x0040, 0x0041).r(FUNC(pc88va_state::sys_port4_r)); // (R) System Port 4 (W) System port 3 (strobe port) - map(0x0044, 0x0045).mirror(0x0002).rw("ym", FUNC(ym2203_device::read), FUNC(ym2203_device::write)); + map(0x0040, 0x0040).rw(FUNC(pc88va_state::port40_r), FUNC(pc88va_state::port40_w)); // (R) System Port 4 (W) System port 3 (strobe port) + map(0x0044, 0x0047).rw(m_opna, FUNC(ym2608_device::read), FUNC(ym2608_device::write)); +// map(0x0050, 0x005b) CRTC/backdrop on PC8801, causes HW trap on VA // map(0x005c, 0x005c) (R) GVRAM status // map(0x005c, 0x005f) (W) GVRAM selection +// map(0x0060, 0x0068) DMA on PC8801, causes HW trap on VA // map(0x0070, 0x0070) ? (*) // map(0x0071, 0x0071) Expansion ROM select (*) // map(0x0078, 0x0078) Memory offset increment (*) @@ -1005,243 +645,248 @@ void pc88va_state::pc88va_io_map(address_map &map) // map(0x00e6, 0x00e6) 8214 IRQ mask (*) // map(0x00e8, 0x00e9) ? (*) // map(0x00ec, 0x00ed) ? (*) - #if TEST_SUBFDC - map(0x00fc, 0x00ff).rw("d8255_2", FUNC(i8255_device::read), FUNC(i8255_device::write)); // d8255 2, FDD - #else - map(0x00fc, 0x00ff).r(FUNC(pc88va_state::no_subfdc_r)).nopw(); - #endif + map(0x00fc, 0x00ff).r(FUNC(pc88va_state::fake_subfdc_r)).nopw(); map(0x0100, 0x0101).rw(FUNC(pc88va_state::screen_ctrl_r), FUNC(pc88va_state::screen_ctrl_w)); // Screen Control Register -// map(0x0102, 0x0103) Graphic Screen Control Register + map(0x0102, 0x0103).rw(FUNC(pc88va_state::gfx_ctrl_r), FUNC(pc88va_state::gfx_ctrl_w)); map(0x0106, 0x0109).w(FUNC(pc88va_state::video_pri_w)); // Palette Control Register (priority) / Direct Color Control Register (priority) // map(0x010a, 0x010b) Picture Mask Mode Register -// map(0x010c, 0x010d) Color Palette Mode Register + map(0x010c, 0x010d).w(FUNC(pc88va_state::color_mode_w)); // Color Palette Mode Register // map(0x010e, 0x010f) Backdrop Color Register // map(0x0110, 0x0111) Color Code/Plain Mask Register // map(0x0124, 0x0125) ? (related to Transparent Color of Graphic Screen 0) // map(0x0126, 0x0127) ? (related to Transparent Color of Graphic Screen 1) -// map(0x012e, 0x012f) ? (related to Transparent Color of Text/Sprite) -// map(0x0130, 0x0137) Picture Mask Parameter + map(0x012e, 0x012f).w(FUNC(pc88va_state::text_transpen_w)); +// map(0x0130, 0x0137) Picture Mask Parameter (global cliprect, olteus gameplay) map(0x0142, 0x0142).rw(FUNC(pc88va_state::idp_status_r), FUNC(pc88va_state::idp_command_w)); //Text Controller (IDP) - (R) Status (W) command map(0x0146, 0x0146).w(FUNC(pc88va_state::idp_param_w)); //Text Controller (IDP) - (R/W) Parameter -// map(0x0148, 0x0149) Text control port 1 -// map(0x014c, 0x014f) ? CG Port + map(0x0148, 0x0148).w(FUNC(pc88va_state::text_control_1_w)); +// map(0x014c, 0x014f) Kanji CG Port, animefrm + map(0x014c, 0x014d).w(FUNC(pc88va_state::kanji_cg_address_w)); + map(0x014e, 0x014e).r(FUNC(pc88va_state::kanji_cg_r)); + map(0x014f, 0x014f).w(FUNC(pc88va_state::kanji_cg_raster_w)); map(0x0150, 0x0151).r(FUNC(pc88va_state::sysop_r)); // System Operational Mode map(0x0152, 0x0153).rw(FUNC(pc88va_state::bios_bank_r), FUNC(pc88va_state::bios_bank_w)); // Memory Map Register // map(0x0154, 0x0155) Refresh Register (wait states) map(0x0156, 0x0156).r(FUNC(pc88va_state::rom_bank_r)); // ROM bank status -// map(0x0158, 0x0159) Interruption Mode Modification +// map(0x0158, 0x0159) Interruption Mode Modification (strobe), changes i8214 mode to i8259, cannot be changed back // map(0x015c, 0x015f) NMI mask port (strobe port) - map(0x0160, 0x016f).rw(m_dmac, FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // DMA Controller +// map(0x0160, 0x016f) V50 DMAC +// map(0x0180, 0x0180) read by Olteus map(0x0184, 0x0187).rw("pic8259_slave", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff); - map(0x0188, 0x018b).rw("pic8259_master", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff); // ICU, also controls 8214 emulation +// map(0x0188, 0x018b) V50 ICU // map(0x0190, 0x0191) System Port 5 + map(0x0190, 0x0190).rw(FUNC(pc88va_state::sys_port5_r), FUNC(pc88va_state::sys_port5_w)); // map(0x0196, 0x0197) Keyboard sub CPU command port map(0x0198, 0x0199).w(FUNC(pc88va_state::backupram_wp_1_w)); //Backup RAM write inhibit map(0x019a, 0x019b).w(FUNC(pc88va_state::backupram_wp_0_w)); //Backup RAM write permission - map(0x01a0, 0x01a7).rw("pit8253", FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);// vTCU (timer counter unit) +// map(0x01a0, 0x01a7) V50 TCU map(0x01a8, 0x01a8).w(FUNC(pc88va_state::timer3_ctrl_reg_w)); // General-purpose timer 3 control port - map(0x01b0, 0x01b7).rw(FUNC(pc88va_state::pc88va_fdc_r), FUNC(pc88va_state::pc88va_fdc_w)).umask16(0x00ff);// FDC related (765) + map(0x01b0, 0x01b7).rw(FUNC(pc88va_state::pc88va_fdc_r), FUNC(pc88va_state::pc88va_fdc_w)).umask16(0x00ff); // FDC related (765) map(0x01b8, 0x01bb).m(m_fdc, FUNC(upd765a_device::map)).umask16(0x00ff); -// map(0x01c0, 0x01c1) ? +// map(0x01c0, 0x01c1) keyboard scan code, polled thru IRQ1 ... + map(0x01c1, 0x01c1).lr8(NAME([this] () { return m_keyb.data; })); map(0x01c6, 0x01c7).nopw(); // ??? map(0x01c8, 0x01cf).rw("d8255_3", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00); //i8255 3 (byte access) // map(0x01d0, 0x01d1) Expansion RAM bank selection - map(0x0200, 0x021f).ram(); // Frame buffer 0 control parameter - map(0x0220, 0x023f).ram(); // Frame buffer 1 control parameter - map(0x0240, 0x025f).ram(); // Frame buffer 2 control parameter - map(0x0260, 0x027f).ram(); // Frame buffer 3 control parameter + map(0x0200, 0x027f).ram().share("fb_regs"); // Frame buffer 0-1-2-3 control parameter + // TODO: shinraba writes to 0x340-0x37f on transition between opening and title screens (mirror? core bug?) map(0x0300, 0x033f).ram().w(FUNC(pc88va_state::palette_ram_w)).share("palram"); // Palette RAM (xBBBBxRRRRxGGGG format) -// map(0x0500, 0x05ff) GVRAM -// map(0x1000, 0xfeff) user area (???) - map(0xff00, 0xffff).noprw(); // CPU internal use -} -// (*) are specific N88 V1 / V2 ports + map(0x0500, 0x0507).m(m_sgp, FUNC(pc88va_sgp_device::sgp_io)); + // GVRAM multiplane access regs (ROP section) +// map(0x0510, 0x0510) AACC extend access mode +// map(0x0512, 0x0512) GMAP block switch +// map(0x0514, 0x0514) XRPMn plane readback select +// map(0x0516, 0x0516) XWPMn plane write select +// map(0x0518, 0x0518) multiplane enable +// map(0x0520, 0x0527).umask16(0x00ff) extended access bit comparison +// map(0x0528, 0x0528) extended access plane comparison +// map(0x0530, 0x0537).umask16(0x00ff) extended access pattern low byte +// map(0x0540, 0x0547).umask16(0x00ff) extended access pattern high byte +// map(0x0550, 0x0550) PRRPn plane pattern usage start byte on read +// map(0x0552, 0x0552) PRWPn plane pattern usage start byte on write +// map(0x0560, 0x0567).umask16(0x00ff) ROP plane code + // GVRAM single plane access regs +// map(0x0580, 0x0580) single plane enable +// map(0x0590, 0x0593) GVRAM pattern register settings +// map(0x05a0, 0x05a3) ROP plane code -TIMER_CALLBACK_MEMBER(pc88va_state::pc8801fd_upd765_tc_to_zero) +// map(0x1000, 0xfeff) PC-88VA expansion boards +// map(0xe2d2, 0xe2d2) MIDI status in micromus +// map(0xff00, 0xffff).noprw(); // CPU internal use +} + +void pc88va_state::opna_map(address_map &map) { - m_fdc->tc_w(false); + // TODO: confirm it really is ROMless + // TODO: confirm size + map(0x000000, 0x1fffff).ram(); } -/* FDC subsytem CPU */ -#if TEST_SUBFDC -void pc88va_state::pc88va_z80_map(address_map &map) +// TODO: quick and dirty support +// should really inherit from the PC8001/PC8801 family as a device, applying the fact that is running on (undumped) MCU instead +INPUT_CHANGED_MEMBER(pc88va_state::key_stroke) { - map(0x0000, 0x1fff).rom(); - map(0x4000, 0x7fff).ram(); + if(newval && !oldval) + { + m_keyb.data = uint8_t(param & 0xff); + //m_keyb.status &= ~1; + m_maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE); + m_maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE); + } + + // TODO: eventually thrown away by the MCU after set time + if(oldval && !newval) + { + m_keyb.data = 0xff; + //m_keyb.status |= 1; + } } -uint8_t pc88va_state::upd765_tc_r() -{ - m_fdc->tc_w(true); - m_tc_clear_timer->adjust(attotime::from_usec(50)); - return 0; -} +#define VA_PORT_SCAN(_scancode_) \ + PORT_CHANGED_MEMBER(DEVICE_SELF, pc88va_state, key_stroke, _scancode_) -void pc88va_state::fdc_irq_vector_w(uint8_t data) -{ - m_fdc_irq_opcode = data; -} - -void pc88va_state::upd765_mc_w(uint8_t data) -{ - m_fdd[0]->get_device()->mon_w(!(data & 1)); - m_fdd[1]->get_device()->mon_w(!(data & 2)); -} - -void pc88va_state::pc88va_z80_io_map(address_map &map) -{ - map.global_mask(0xff); - map(0xf0, 0xf0).w(FUNC(pc88va_state::fdc_irq_vector_w)); // Interrupt Opcode Port -// map(0xf4, 0xf4) // Drive Control Port - map(0xf8, 0xf8).rw(FUNC(pc88va_state::upd765_tc_r), FUNC(pc88va_state::upd765_mc_w)); // (R) Terminal Count Port (W) Motor Control Port - map(0xfa, 0xfb).m(m_fdc, FUNC(upd765a_device::map)); - map(0xfc, 0xff).rw("d8255_2s", FUNC(i8255_device::read), FUNC(i8255_device::write)); -} -#endif - -/* TODO: active low or active high? */ static INPUT_PORTS_START( pc88va ) PORT_START("KEY0") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0 (PAD)") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1 (PAD)") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2 (PAD)") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3 (PAD)") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4 (PAD)") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5 (PAD)") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6 (PAD)") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7 (PAD)") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) VA_PORT_SCAN(0x4e) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) VA_PORT_SCAN(0x4a) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) VA_PORT_SCAN(0x4b) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) VA_PORT_SCAN(0x4c) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) VA_PORT_SCAN(0x46) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) VA_PORT_SCAN(0x47) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) VA_PORT_SCAN(0x48) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) VA_PORT_SCAN(0x42) PORT_START("KEY1") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8 (PAD)") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9 (PAD)") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("* (PAD)") PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("+ (PAD)") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("= (PAD)") // PORT_CODE(KEYCODE_EQUAL_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUAL_PAD)) - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(", (PAD)") // PORT_CODE(KEYCODE_EQUAL_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUAL_PAD)) - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(". (PAD)") // PORT_CODE(KEYCODE_EQUAL_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUAL_PAD)) - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("RETURN (PAD)") //PORT_CODE(KEYCODE_RETURN_PAD) PORT_CHAR(UCHAR_MAMEKEY(RETURN_PAD)) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) VA_PORT_SCAN(0x43) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) VA_PORT_SCAN(0x44) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) VA_PORT_SCAN(0x45) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) VA_PORT_SCAN(0x49) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD)) VA_PORT_SCAN(0x4d) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) VA_PORT_SCAN(0x4f) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) VA_PORT_SCAN(0x39) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) VA_PORT_SCAN(0x1c) PORT_START("KEY2") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("@") // PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') VA_PORT_SCAN(0x1a) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') VA_PORT_SCAN(0x1d) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') VA_PORT_SCAN(0x2d) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') VA_PORT_SCAN(0x2b) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') VA_PORT_SCAN(0x1f) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') VA_PORT_SCAN(0x12) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') VA_PORT_SCAN(0x20) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') VA_PORT_SCAN(0x21) PORT_START("KEY3") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') VA_PORT_SCAN(0x22) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') VA_PORT_SCAN(0x17) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') VA_PORT_SCAN(0x23) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') VA_PORT_SCAN(0x24) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') VA_PORT_SCAN(0x25) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') VA_PORT_SCAN(0x2f) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') VA_PORT_SCAN(0x2e) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') VA_PORT_SCAN(0x18) PORT_START("KEY4") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') VA_PORT_SCAN(0x19) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') VA_PORT_SCAN(0x10) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') VA_PORT_SCAN(0x13) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') VA_PORT_SCAN(0x1e) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') VA_PORT_SCAN(0x14) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') VA_PORT_SCAN(0x16) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') VA_PORT_SCAN(0x2c) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') VA_PORT_SCAN(0x11) PORT_START("KEY5") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("[") - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("\xC2\xA5") /* Yen */ - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("]") - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("^") - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("-") + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') VA_PORT_SCAN(0x2a) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') VA_PORT_SCAN(0x15) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') VA_PORT_SCAN(0x29) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{') + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR(0xA5) PORT_CHAR('|') + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}') + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('^') + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=') PORT_START("KEY6") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("2 \"") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("6 &") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("7 '") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') PORT_START("KEY7") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("8 (") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("9 )") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(": *") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("; +") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(", <") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME(". >") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("/ ?") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("/ ?") //PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*') VA_PORT_SCAN(0x27) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+') VA_PORT_SCAN(0x26) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') VA_PORT_SCAN(0x50) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(" _") PORT_CODE(KEYCODE_DEL) PORT_CHAR(0) PORT_CHAR('_') PORT_START("KEY8") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CLR") // PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP) /* Up */ - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT) /* Right */ - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("INSDEL") - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("GRPH") - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("KANA") PORT_CODE(KEYCODE_LALT) - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Clr Home") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) VA_PORT_SCAN(0x3a) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) VA_PORT_SCAN(0x3c) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del Ins") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PORT_CHAR(UCHAR_MAMEKEY(INSERT)) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Grph") PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(F7)) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Kana") PORT_CODE(KEYCODE_LCONTROL) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(F6)) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2) PORT_START("KEY9") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("STOP") // PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F1 (mirror)") PORT_CODE(KEYCODE_F1) - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F2 (mirror)") PORT_CODE(KEYCODE_F2) - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F3 (mirror)") PORT_CODE(KEYCODE_F3) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F4 (mirror)") PORT_CODE(KEYCODE_F4) - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F5 (mirror)") PORT_CODE(KEYCODE_F5) - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("SPACES") // PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Stop") PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) VA_PORT_SCAN(0x60) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) VA_PORT_SCAN(0x66) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') VA_PORT_SCAN(0x34) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_START("KEYA") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN) /* Down */ - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT) /* Left */ - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("HELP") // PORT_CODE(KEYCODE_TAB) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("COPY") // PORT_CODE(KEYCODE_TAB) - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("- (mirror)") // PORT_CODE(KEYCODE_TAB) - PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("/") // PORT_CODE(KEYCODE_TAB) - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t') + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) VA_PORT_SCAN(0x3d) + PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) VA_PORT_SCAN(0x3b) + PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Help") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(F8)) + PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Copy") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) + PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) + PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) + PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_START("KEYB") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Roll Up") PORT_CODE(KEYCODE_PGUP) /* Roll Up */ - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Roll Down") PORT_CODE(KEYCODE_PGDN) /* Roll Down */ - PORT_BIT(0xfc,IP_ACTIVE_LOW,IPT_UNUSED) + PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Up") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(PGUP)) + PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Roll Down") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(PGDN)) + // TODO: definitely other bits in here, cfr. pc8801ma extra keys + PORT_BIT(0xfc,IP_ACTIVE_LOW, IPT_UNUSED) PORT_START("KEYC") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) - PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) + PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) VA_PORT_SCAN(0x62) + PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) VA_PORT_SCAN(0x63) + PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) VA_PORT_SCAN(0x64) + PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) VA_PORT_SCAN(0x65) + PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) VA_PORT_SCAN(0x66) + PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) VA_PORT_SCAN(0x0e) PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT) PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL) PORT_START("KEYD") - PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) - PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) - PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) - PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) + PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) VA_PORT_SCAN(0x67) + PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) VA_PORT_SCAN(0x68) + PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) VA_PORT_SCAN(0x69) + PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) VA_PORT_SCAN(0x6a) + PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) VA_PORT_SCAN(0x6b) PORT_BIT(0x20,IP_ACTIVE_LOW,IPT_KEYBOARD) // Conversion? PORT_BIT(0x40,IP_ACTIVE_LOW,IPT_KEYBOARD) // Decision? - PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) + PORT_BIT(0x80,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Space") // ? - /* TODO: I don't understand the meaning of several of these */ PORT_START("KEYE") PORT_BIT(0x01,IP_ACTIVE_LOW,IPT_KEYBOARD) - PORT_BIT(0x02,IP_ACTIVE_LOW,IPT_KEYBOARD) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Keypad Enter") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13) VA_PORT_SCAN(0x79) PORT_BIT(0x04,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_BIT(0x08,IP_ACTIVE_LOW,IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_BIT(0x10,IP_ACTIVE_LOW,IPT_KEYBOARD) @@ -1252,7 +897,7 @@ static INPUT_PORTS_START( pc88va ) PORT_BIT(0xff,IP_ACTIVE_LOW,IPT_UNUSED) PORT_START("DSW") - PORT_DIPNAME( 0x01, 0x01, "CRT Mode" ) + PORT_DIPNAME( 0x01, 0x00, "CRT Mode" ) PORT_DIPSETTING( 0x01, "15.7 KHz" ) PORT_DIPSETTING( 0x00, "24.8 KHz" ) PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) @@ -1288,6 +933,31 @@ static INPUT_PORTS_START( pc88va ) PORT_DIPSETTING( 0x01, "N88 V2 Mode" ) PORT_DIPSETTING( 0x02, "N88 V1 Mode" ) // PORT_DIPSETTING( 0x03, "???" ) + + PORT_START("OPN_PA") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("OPN_PB") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Joystick Button 1") PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Joystick Button 2") PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x00) + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Mouse Button 1") PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x02) + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Mouse Button 2") PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x02) + PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("MOUSEX") + PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_REVERSE PORT_SENSITIVITY(20) PORT_KEYDELTA(20) PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x02) + + PORT_START("MOUSEY") + PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_REVERSE PORT_SENSITIVITY(20) PORT_KEYDELTA(20) PORT_PLAYER(1) PORT_CONDITION("BOARD_CONFIG", 0x02, EQUALS, 0x02) + + PORT_START("BOARD_CONFIG") + PORT_CONFNAME( 0x02, 0x00, "Port 1 Connection" ) + PORT_CONFSETTING( 0x00, "Joystick" ) + PORT_CONFSETTING( 0x02, "Mouse" ) INPUT_PORTS_END static const gfx_layout pc88va_chars_8x8 = @@ -1335,33 +1005,14 @@ static const gfx_layout pc88va_kanji_16x16 = 16*16 }; -// TODO: decoded for debugging purpose, this will be nuked in the end ... +// debug only static GFXDECODE_START( gfx_pc88va ) - GFXDECODE_ENTRY( "kanji", 0x00000, pc88va_chars_8x8, 0, 1 ) - GFXDECODE_ENTRY( "kanji", 0x00000, pc88va_chars_16x16, 0, 1 ) - GFXDECODE_ENTRY( nullptr, 0x00000, pc88va_kanji_8x8, 0, 1 ) - GFXDECODE_ENTRY( nullptr, 0x00000, pc88va_kanji_16x16, 0, 1 ) + GFXDECODE_ENTRY( "kanji", 0x00000, pc88va_chars_8x8, 0, 16 ) + GFXDECODE_ENTRY( "kanji", 0x00000, pc88va_chars_16x16, 0, 16 ) + GFXDECODE_ENTRY( nullptr, 0x00000, pc88va_kanji_8x8, 0, 16 ) + GFXDECODE_ENTRY( nullptr, 0x00000, pc88va_kanji_16x16, 0, 16 ) GFXDECODE_END -uint8_t pc88va_state::cpu_8255_c_r() -{ - return m_i8255_1_pc >> 4; -} - -void pc88va_state::cpu_8255_c_w(uint8_t data) -{ - m_i8255_0_pc = data; -} - -uint8_t pc88va_state::fdc_8255_c_r() -{ - return m_i8255_0_pc >> 4; -} - -void pc88va_state::fdc_8255_c_w(uint8_t data) -{ - m_i8255_1_pc = data; -} uint8_t pc88va_state::r232_ctrl_porta_r() { @@ -1405,18 +1056,79 @@ void pc88va_state::r232_ctrl_portc_w(uint8_t data) // ... } +/**************************************** + * IRQ lines + ***************************************/ + uint8_t pc88va_state::get_slave_ack(offs_t offset) { - if (offset==7) { // IRQ = 7 + if (offset == 7) { return m_pic2->acknowledge(); } return 0x00; } +TIMER_DEVICE_CALLBACK_MEMBER(pc88va_state::vrtc_irq) +{ + int scanline = param; + + // upo and ballbrkr have a working vrtc irq routine and a vblank $40 readback at same time. + // there's no clear /VRMF write to $e6 so presuming the irq happening later than default + // screen_device first line after visible. + if (scanline == m_vrtc_irq_line) + { + // TODO: verify when ack should happen + m_maincpu->set_input_line(INPUT_LINE_IRQ2, CLEAR_LINE); + m_maincpu->set_input_line(INPUT_LINE_IRQ2, ASSERT_LINE); + } +} + +WRITE_LINE_MEMBER( pc88va_state::fdc_irq ) +{ + if(m_fdc_mode && state) + { + // TODO: ugh why it doesn't follow state? + //printf("%d\n",state); + m_pic2->ir3_w(0); + m_pic2->ir3_w(1); + } +} + +// TODO: often dies, which implicitly means "make the full system to hang" in PC-88 land ... +WRITE_LINE_MEMBER(pc88va_state::int4_irq_w) +{ + bool irq_state = m_sound_irq_enable & state; + + if (irq_state) + { + m_pic2->ir4_w(0); + m_pic2->ir4_w(1); + } +// m_pic->r_w(7 ^ INT4_IRQ_LEVEL, !irq_state); + m_sound_irq_pending = state; +} + +WRITE_LINE_MEMBER( pc88va_state::tc_w ) +{ + m_fdc->tc_w(state); +} + +void pc88va_state::floppy_formats(format_registration &fr) +{ + fr.add_mfm_containers(); + fr.add(FLOPPY_XDF_FORMAT); + fr.add(FLOPPY_PC98FDI_FORMAT); +} + +static void pc88va_floppies(device_slot_interface &device) +{ + device.option_add("525hd", FLOPPY_525_HD); +} + void pc88va_state::machine_start() { - m_tc_clear_timer = timer_alloc(FUNC(pc88va_state::pc8801fd_upd765_tc_to_zero), this); - m_tc_clear_timer->adjust(attotime::never); + m_rtc->cs_w(1); + m_rtc->oe_w(1); m_fdc_timer = timer_alloc(FUNC(pc88va_state::pc88va_fdc_timer), this); m_fdc_timer->adjust(attotime::never); @@ -1441,7 +1153,6 @@ void pc88va_state::machine_start() m_fdd[0]->get_device()->set_rpm(300); m_fdd[1]->get_device()->set_rpm(300); m_fdc->set_rate(250000); - } void pc88va_state::machine_reset() @@ -1455,146 +1166,49 @@ void pc88va_state::machine_reset() m_bank_reg = 0x4100; m_sysbank->set_bank(1); m_backupram_wp = 1; - - /* default palette */ - { - uint8_t i; - for(i=0;i<32;i++) - m_palette->set_pen_color(i,pal1bit((i & 2) >> 1),pal1bit((i & 4) >> 2),pal1bit(i & 1)); - } + m_rstmd = false; m_tsp.tvram_vreg_offset = 0; m_fdc_mode = 0; - m_fdc_irq_opcode = 0x00; //0x7f ld a,a ! + m_xtmask = false; - #if TEST_SUBFDC - m_fdccpu->set_input_line_vector(0, 0); // Z80 - #endif - - m_fdc_motor_status[0] = 0; - m_fdc_motor_status[1] = 0; + // shinraba never write to port $32, + // and it expects that the sound irq actually runs otherwise it enters in debug mode + m_misc_ctrl = 0x00; + m_sound_irq_enable = true; + m_sound_irq_pending = false; } -INTERRUPT_GEN_MEMBER(pc88va_state::pc88va_vrtc_irq) -{ - m_pic1->ir2_w(0); - m_pic1->ir2_w(1); -} - -WRITE_LINE_MEMBER(pc88va_state::pc88va_pit_out0_changed) -{ - if(state) - { - m_pic1->ir0_w(0); - m_pic1->ir0_w(1); - } -} - -WRITE_LINE_MEMBER( pc88va_state::fdc_drq ) -{ - printf("%02x DRQ\n",state); - m_dmac->dreq2_w(state); -} - -WRITE_LINE_MEMBER( pc88va_state::fdc_irq ) -{ - if(m_fdc_mode && state) - { - //printf("%d\n",state); - m_pic2->ir3_w(0); - m_pic2->ir3_w(1); - } - #if TEST_SUBFDC - else - m_fdccpu->set_input_line(0, HOLD_LINE); - #endif -} - -WRITE_LINE_MEMBER(pc88va_state::pc88va_hlda_w) -{ -// m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE); - - m_dmac->hack_w(state); - -// printf("%02x HLDA\n",state); -} - -WRITE_LINE_MEMBER( pc88va_state::pc88va_tc_w ) -{ - /* floppy terminal count */ - m_fdc->tc_w(state); - -// printf("TC %02x\n",state); -} - - -uint8_t pc88va_state::fdc_dma_r() -{ - printf("R DMA\n"); - return m_fdc->dma_r(); -} - -void pc88va_state::fdc_dma_w(uint8_t data) -{ - printf("W DMA %08x\n",data); - m_fdc->dma_w(data); -} - -void pc88va_state::floppy_formats(format_registration &fr) -{ - fr.add_mfm_containers(); - fr.add(FLOPPY_XDF_FORMAT); -} - -static void pc88va_floppies(device_slot_interface &device) -{ - device.option_add("525hd", FLOPPY_525_HD); -} - -uint8_t pc88va_state::dma_memr_cb(offs_t offset) -{ - printf("%08x\n",offset); - return 0; -} - -void pc88va_state::dma_memw_cb(offs_t offset, uint8_t data) -{ - printf("%08x %02x\n",offset,data); -} - - void pc88va_state::pc88va(machine_config &config) { - V50(config, m_maincpu, MASTER_CLOCK); // μPD9002, aka V30 + μPD70008AC (for PC8801 compatibility mode) - m_maincpu->set_addrmap(AS_PROGRAM, &pc88va_state::pc88va_map); - m_maincpu->set_addrmap(AS_IO, &pc88va_state::pc88va_io_map); - m_maincpu->set_vblank_int("screen", FUNC(pc88va_state::pc88va_vrtc_irq)); - m_maincpu->set_irq_acknowledge_callback("pic8259_master", FUNC(pic8259_device::inta_cb)); + V50(config, m_maincpu, MASTER_CLOCK); // μPD9002, aka V50 + μPD70008AC (for PC8801 compatibility mode) in place of 8080 + m_maincpu->set_addrmap(AS_PROGRAM, &pc88va_state::main_map); + m_maincpu->set_addrmap(AS_IO, &pc88va_state::io_map); + TIMER(config, "scantimer").configure_scanline(FUNC(pc88va_state::vrtc_irq), "screen", 0, 1); + m_maincpu->icu_slave_ack_cb().set(m_pic2, FUNC(pic8259_device::acknowledge)); + m_maincpu->set_tclk(MASTER_CLOCK); + // "timer 1" + m_maincpu->tout1_cb().set_inputline(m_maincpu, INPUT_LINE_IRQ0); + // ch2 is FDC, ch0/3 are "user". ch1 is unused + m_maincpu->out_hreq_cb().set(m_maincpu, FUNC(v50_device::hack_w)); + m_maincpu->out_eop_cb().set(FUNC(pc88va_state::tc_w)); + m_maincpu->in_ior_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_r)); + m_maincpu->out_iow_cb<2>().set(m_fdc, FUNC(upd765a_device::dma_w)); + m_maincpu->in_memr_cb().set([this] (offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_byte(offset); }); + m_maincpu->out_memw_cb().set([this] (offs_t offset, u8 data) { m_maincpu->space(AS_PROGRAM).write_byte(offset, data); }); -#if TEST_SUBFDC - z80_device &fdccpu(Z80(config, "fdccpu", 8000000)); /* 8 MHz */ - fdccpu.set_addrmap(AS_PROGRAM, &pc88va_state::pc88va_z80_map); - fdccpu.set_addrmap(AS_IO, &pc88va_state::pc88va_z80_io_map); - - config.m_perfect_cpu_quantum = subtag("maincpu"); -#endif + // TODO: pc80s31k here SCREEN(config, m_screen, SCREEN_TYPE_RASTER); - m_screen->set_refresh_hz(60); - m_screen->set_size(640, 480); - m_screen->set_visarea(0, 640-1, 0, 200-1); - m_screen->set_screen_update(FUNC(pc88va_state::screen_update_pc88va)); + m_screen->set_raw(XTAL(42'105'200) / 2, 848, 0, 640, 448, 0, 400); + m_screen->set_screen_update(FUNC(pc88va_state::screen_update)); - PALETTE(config, m_palette).set_entries(32); -// m_palette->set_init(FUNC(pc88va_state::pc8801)); + PALETTE(config, m_palette, FUNC(pc88va_state::palette_init)).set_entries(32); GFXDECODE(config, m_gfxdecode, m_palette, gfx_pc88va); - i8255_device &d8255_2(I8255(config, "d8255_2")); - d8255_2.in_pa_callback().set("d8255_2s", FUNC(i8255_device::pb_r)); - d8255_2.in_pb_callback().set("d8255_2s", FUNC(i8255_device::pa_r)); - d8255_2.in_pc_callback().set(FUNC(pc88va_state::cpu_8255_c_r)); - d8255_2.out_pc_callback().set(FUNC(pc88va_state::cpu_8255_c_w)); + PC88VA_SGP(config, m_sgp); + m_sgp->set_map(&pc88va_state::sgp_map); i8255_device &d8255_3(I8255(config, "d8255_3")); d8255_3.in_pa_callback().set(FUNC(pc88va_state::r232_ctrl_porta_r)); @@ -1604,94 +1218,65 @@ void pc88va_state::pc88va(machine_config &config) d8255_3.in_pc_callback().set(FUNC(pc88va_state::r232_ctrl_portc_r)); d8255_3.out_pc_callback().set(FUNC(pc88va_state::r232_ctrl_portc_w)); - i8255_device &d8255_2s(I8255(config, "d8255_2s")); - d8255_2s.in_pa_callback().set("d8255_2", FUNC(i8255_device::pb_r)); - d8255_2s.in_pb_callback().set("d8255_2", FUNC(i8255_device::pa_r)); - d8255_2s.in_pc_callback().set(FUNC(pc88va_state::fdc_8255_c_r)); - d8255_2s.out_pc_callback().set(FUNC(pc88va_state::fdc_8255_c_w)); - - PIC8259(config, m_pic1, 0); - m_pic1->out_int_callback().set_inputline(m_maincpu, 0); - m_pic1->in_sp_callback().set_constant(1); - m_pic1->read_slave_ack_callback().set(FUNC(pc88va_state::get_slave_ack)); - + // external PIC PIC8259(config, m_pic2, 0); - m_pic2->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir7_w)); + m_pic2->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ7); m_pic2->in_sp_callback().set_constant(0); - AM9517A(config, m_dmac, MASTER_CLOCK); // ch2 is FDC, ch0/3 are "user". ch1 is unused - m_dmac->out_hreq_callback().set(FUNC(pc88va_state::pc88va_hlda_w)); - m_dmac->out_eop_callback().set(FUNC(pc88va_state::pc88va_tc_w)); - m_dmac->in_ior_callback<2>().set(FUNC(pc88va_state::fdc_dma_r)); - m_dmac->out_iow_callback<2>().set(FUNC(pc88va_state::fdc_dma_w)); - m_dmac->in_memr_callback().set(FUNC(pc88va_state::dma_memr_cb)); - m_dmac->out_memw_callback().set(FUNC(pc88va_state::dma_memw_cb)); - - UPD765A(config, m_fdc, 8000000, false, true); + UPD765A(config, m_fdc, 4000000, true, true); m_fdc->intrq_wr_callback().set(FUNC(pc88va_state::fdc_irq)); - m_fdc->drq_wr_callback().set(FUNC(pc88va_state::fdc_drq)); - FLOPPY_CONNECTOR(config, m_fdd[0], pc88va_floppies, "525hd", pc88va_state::floppy_formats); - FLOPPY_CONNECTOR(config, m_fdd[1], pc88va_floppies, "525hd", pc88va_state::floppy_formats); + m_fdc->drq_wr_callback().set(m_maincpu, FUNC(v50_device::dreq_w<2>)); + FLOPPY_CONNECTOR(config, m_fdd[0], pc88va_floppies, "525hd", pc88va_state::floppy_formats).enable_sound(true); + FLOPPY_CONNECTOR(config, m_fdd[1], pc88va_floppies, "525hd", pc88va_state::floppy_formats).enable_sound(true); + + // TODO: set pc98 compatible + // Needs a MS-Engine disk dump first, that applies an overlay on PC Engine OS so that it can run PC-98 software SOFTWARE_LIST(config, "disk_list").set_original("pc88va"); - pit8253_device &pit8253(PIT8253(config, "pit8253", 0)); - pit8253.set_clk<0>(MASTER_CLOCK); /* general purpose timer 1 */ - pit8253.out_handler<0>().set(FUNC(pc88va_state::pc88va_pit_out0_changed)); - pit8253.set_clk<1>(MASTER_CLOCK); /* BEEP frequency setting */ - pit8253.set_clk<2>(MASTER_CLOCK); /* RS232C baud rate setting */ + UPD4990A(config, m_rtc); - ADDRESS_MAP_BANK(config, "sysbank").set_map(&pc88va_state::sysbank_map).set_options(ENDIANNESS_LITTLE, 16, 18+4, 0x40000); + ADDRESS_MAP_BANK(config, "sysbank").set_map(&pc88va_state::sysbank_map).set_options(ENDIANNESS_LITTLE, 16, 22, 0x40000); - SPEAKER(config, "mono").front_center(); - ym2203_device &ym(YM2203(config, "ym", FM_CLOCK)); //unknown clock / divider - ym.add_route(0, "mono", 0.25); - ym.add_route(1, "mono", 0.25); - ym.add_route(2, "mono", 0.50); - ym.add_route(3, "mono", 0.50); + SPEAKER(config, m_lspeaker).front_left(); + SPEAKER(config, m_rspeaker).front_right(); + + // PC-88VA-12 "Sound Board II", YM2608B + YM2608(config, m_opna, FM_CLOCK); + m_opna->set_addrmap(0, &pc88va_state::opna_map); + m_opna->irq_handler().set(FUNC(pc88va_state::int4_irq_w)); + // TODO: DE-9 + m_opna->port_a_read_callback().set(FUNC(pc88va_state::opn_porta_r)); + m_opna->port_b_read_callback().set_ioport("OPN_PB"); + // TODO: per-channel mixing is unconfirmed + m_opna->add_route(0, m_lspeaker, 0.25); + m_opna->add_route(0, m_rspeaker, 0.25); + m_opna->add_route(1, m_lspeaker, 0.75); + m_opna->add_route(2, m_rspeaker, 0.75); } -ROM_START( pc88va2 ) - ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF ) - - ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF ) - ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) ) - - ROM_REGION( 0x100000, "rom00", ROMREGION_ERASEFF ) // 0xe0000 - 0xeffff - ROM_LOAD( "varom00_va2.rom", 0x00000, 0x80000, CRC(98c9959a) SHA1(bcaea28c58816602ca1e8290f534360f1ca03fe8) ) - ROM_LOAD( "varom08_va2.rom", 0x80000, 0x20000, CRC(eef6d4a0) SHA1(47e5f89f8b0ce18ff8d5d7b7aef8ca0a2a8e3345) ) - - ROM_REGION( 0x20000, "rom10", 0 ) // 0xf0000 - 0xfffff - ROM_LOAD( "varom1_va2.rom", 0x00000, 0x20000, CRC(7e767f00) SHA1(dd4f4521bfbb068f15ab3bcdb8d47c7d82b9d1d4) ) - - /* No idea of the proper size: it has never been dumped */ - ROM_REGION( 0x2000, "audiocpu", 0) - ROM_LOAD( "soundbios.rom", 0x0000, 0x2000, NO_DUMP ) - - ROM_REGION16_LE( 0x80000, "kanji", ROMREGION_ERASEFF ) - ROM_LOAD( "vafont_va2.rom", 0x00000, 0x50000, BAD_DUMP CRC(b40d34e4) SHA1(a0227d1fbc2da5db4b46d8d2c7e7a9ac2d91379f) ) // should be splitted - - ROM_REGION16_LE( 0x80000, "dictionary", 0 ) - ROM_LOAD( "vadic_va2.rom", 0x00000, 0x80000, CRC(a6108f4d) SHA1(3665db538598abb45d9dfe636423e6728a812b12) ) -ROM_END - ROM_START( pc88va ) ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF ) - ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF ) - ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) ) + // In pc80s31k device +// ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF ) +// ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) ) ROM_REGION( 0x100000, "rom00", ROMREGION_ERASEFF ) // 0xe0000 - 0xeffff ROM_LOAD( "varom00.rom", 0x00000, 0x80000, CRC(8a853b00) SHA1(1266ba969959ff25433ecc900a2caced26ef1a9e)) ROM_LOAD( "varom08.rom", 0x80000, 0x20000, CRC(154803cc) SHA1(7e6591cd465cbb35d6d3446c5a83b46d30fafe95)) - ROM_REGION( 0x20000, "rom10", 0 ) // 0xf0000 - 0xfffff + ROM_REGION( 0x20000, "rom10", ROMREGION_ERASEFF ) // 0xf0000 - 0xfffff ROM_LOAD( "varom1.rom", 0x00000, 0x20000, CRC(0783b16a) SHA1(54536dc03238b4668c8bb76337efade001ec7826)) - /* No idea of the proper size: it has never been dumped */ + // TODO: identify this, likely don't even belong to pc88va internals ROM_REGION( 0x2000, "audiocpu", 0) ROM_LOAD( "soundbios.rom", 0x0000, 0x2000, NO_DUMP ) + // TODO: identify this + ROM_REGION( 0x1000, "mcu", 0) + ROM_LOAD( "kbd.rom", 0x0000, 0x1000, NO_DUMP ) + ROM_REGION16_LE( 0x80000, "kanji", ROMREGION_ERASEFF ) ROM_LOAD( "vafont.rom", 0x0000, 0x50000, BAD_DUMP CRC(faf7c466) SHA1(196b3d5b7407cb4f286ffe5c1e34ebb1f6905a8c)) // should be splitted @@ -1699,9 +1284,35 @@ ROM_START( pc88va ) ROM_LOAD( "vadic.rom", 0x0000, 0x80000, CRC(f913c605) SHA1(5ba1f3578d0aaacdaf7194a80e6d520c81ae55fb)) ROM_END +ROM_START( pc88va2 ) + ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASEFF ) +// ROM_REGION( 0x100000, "fdccpu", ROMREGION_ERASEFF ) +// ROM_LOAD( "vasubsys.rom", 0x0000, 0x2000, CRC(08962850) SHA1(a9375aa480f85e1422a0e1385acb0ea170c5c2e0) ) + ROM_REGION( 0x100000, "rom00", ROMREGION_ERASEFF ) // 0xe0000 - 0xeffff + ROM_LOAD( "varom00_va2.rom", 0x00000, 0x80000, CRC(98c9959a) SHA1(bcaea28c58816602ca1e8290f534360f1ca03fe8) ) + ROM_LOAD( "varom08_va2.rom", 0x80000, 0x20000, CRC(eef6d4a0) SHA1(47e5f89f8b0ce18ff8d5d7b7aef8ca0a2a8e3345) ) -COMP( 1987, pc88va, 0, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA", MACHINE_NOT_WORKING | MACHINE_NO_SOUND) -COMP( 1988, pc88va2, pc88va, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA2", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) + ROM_REGION( 0x20000, "rom10", ROMREGION_ERASEFF ) // 0xf0000 - 0xfffff + ROM_LOAD( "varom1_va2.rom", 0x00000, 0x20000, CRC(7e767f00) SHA1(dd4f4521bfbb068f15ab3bcdb8d47c7d82b9d1d4) ) + + // TODO: identify this, likely don't even belong to pc88va internals + ROM_REGION( 0x2000, "audiocpu", 0) + ROM_LOAD( "soundbios.rom", 0x0000, 0x2000, NO_DUMP ) + + // TODO: identify this + ROM_REGION( 0x1000, "mcu", 0) + ROM_LOAD( "kbd.rom", 0x0000, 0x1000, NO_DUMP ) + + ROM_REGION16_LE( 0x80000, "kanji", ROMREGION_ERASEFF ) + ROM_LOAD( "vafont_va2.rom", 0x00000, 0x50000, BAD_DUMP CRC(b40d34e4) SHA1(a0227d1fbc2da5db4b46d8d2c7e7a9ac2d91379f) ) // should be splitted + + ROM_REGION16_LE( 0x80000, "dictionary", 0 ) + ROM_LOAD( "vadic_va2.rom", 0x00000, 0x80000, CRC(a6108f4d) SHA1(3665db538598abb45d9dfe636423e6728a812b12) ) +ROM_END + +COMP( 1987, pc88va, 0, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND ) +COMP( 1988, pc88va2, pc88va, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA2", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_SOUND ) +// VA3 has 3.5" 2TD drives with about 9.3 MB capacity //COMP( 1988, pc88va3, pc88va, 0, pc88va, pc88va, pc88va_state, empty_init, "NEC", "PC-88VA3", MACHINE_NOT_WORKING ) diff --git a/src/mame/nec/pc88va.h b/src/mame/nec/pc88va.h index 0bb75fb62b5..d9d162aee4a 100644 --- a/src/mame/nec/pc88va.h +++ b/src/mame/nec/pc88va.h @@ -14,115 +14,135 @@ #include "cpu/nec/v5x.h" #include "cpu/z80/z80.h" #include "imagedev/floppy.h" -#include "machine/am9517a.h" +#include "machine/bankdev.h" #include "machine/i8255.h" #include "machine/pic8259.h" -#include "machine/pit8253.h" -//#include "machine/upd71071.h" +#include "machine/timer.h" +#include "machine/upd1990a.h" #include "machine/upd765.h" -#include "machine/bankdev.h" #include "sound/ymopn.h" +//#include "pc80s31k.h" +#include "pc88va_sgp.h" + #include "emupal.h" #include "screen.h" #include "softlist.h" #include "speaker.h" +#include "formats/pc98fdi_dsk.h" #include "formats/xdf_dsk.h" -// TODO: for the time being, just disable FDC CPU, it's for PC-8801 compatibility mode anyway. -// the whole FDC device should be converted (it's also used by PC-9801) -#define TEST_SUBFDC 0 - - - class pc88va_state : public driver_device { public: - pc88va_state(const machine_config &mconfig, device_type type, const char *tag) : - driver_device(mconfig, type, tag), - m_maincpu(*this, "maincpu"), - m_screen(*this, "screen"), - m_fdc(*this, "upd765"), - m_fdd(*this, "upd765:%u", 0U), - m_dmac(*this, "dmac"), - m_pic1(*this, "pic8259_master"), - m_pic2(*this, "pic8259_slave"), - m_palram(*this, "palram"), - m_sysbank(*this, "sysbank"), - m_tvram(*this, "tvram"), - m_gvram(*this, "gvram"), - m_gfxdecode(*this, "gfxdecode"), - m_palette(*this, "palette") + pc88va_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_screen(*this, "screen") + , m_fdc(*this, "upd765") + , m_fdd(*this, "upd765:%u", 0U) + , m_pic2(*this, "pic8259_slave") + , m_rtc(*this, "rtc") + , m_opna(*this, "opna") + , m_lspeaker(*this, "lspeaker") + , m_rspeaker(*this, "rspeaker") + , m_palram(*this, "palram") + , m_sysbank(*this, "sysbank") + , m_workram(*this, "workram") + , m_tvram(*this, "tvram") + , m_gvram(*this, "gvram") + , m_fb_regs(*this, "fb_regs") + , m_kanji_rom(*this, "kanji") + , m_sgp(*this, "sgp") + , m_gfxdecode(*this, "gfxdecode") + , m_palette(*this, "palette") { } void pc88va(machine_config &config); + DECLARE_INPUT_CHANGED_MEMBER(key_stroke); + protected: struct tsp_t { - uint16_t tvram_vreg_offset = 0; - uint16_t attr_offset = 0; - uint16_t spr_offset = 0; - uint8_t disp_on = 0; - uint8_t spr_on = 0; - uint8_t pitch = 0; - uint8_t line_height = 0; - uint8_t h_line_pos = 0; - uint8_t blink = 0; - uint16_t cur_pos_x = 0, cur_pos_y = 0; - uint8_t curn = 0; - uint8_t curn_blink = 0; + u16 tvram_vreg_offset = 0; + u32 attr_offset = 0; + u32 spr_offset = 0; + u8 spr_mg = 0; + bool disp_on = false; + bool spr_on = false; + u8 pitch = 0; + u8 line_height = 0; + u8 h_line_pos = 0; + u16 blink = 0; + u16 cur_pos_x = 0, cur_pos_y = 0; + u8 curn = 0; + bool curn_blink = false; + bool spwr_define = false; + u8 spwr_offset = 0; }; + struct keyb_t + { + u8 data = 0; + }; + keyb_t m_keyb; + virtual void machine_start() override; virtual void machine_reset() override; virtual void video_start() override; + void palette_init(palette_device &palette) const; private: - required_device m_maincpu; + required_device m_maincpu; required_device m_screen; required_device m_fdc; required_device_array m_fdd; - required_device m_dmac; - required_device m_pic1; +// required_device m_dmac; +// required_device m_pic1; required_device m_pic2; + required_device m_rtc; + required_device m_opna; + required_device m_lspeaker; + required_device m_rspeaker; required_shared_ptr m_palram; required_device m_sysbank; + required_shared_ptr m_workram; required_shared_ptr m_tvram; required_shared_ptr m_gvram; + required_shared_ptr m_fb_regs; + required_region_ptr m_kanji_rom; + required_device m_sgp; std::unique_ptr m_kanjiram; uint16_t m_bank_reg = 0; - uint16_t m_screen_ctrl_reg = 0; uint8_t m_timer3_io_reg = 0; emu_timer *m_t3_mouse_timer = nullptr; - tsp_t m_tsp; - uint16_t m_video_pri_reg[2]{}; uint8_t m_backupram_wp = 0; - uint8_t m_cmd = 0; - uint8_t m_buf_size = 0; - uint8_t m_buf_index = 0; - uint8_t m_buf_ram[16]{}; - uint8_t m_portc_test = 0; - uint8_t m_fdc_motor_status[2]{}; + bool m_rstmd = false; - /* timers */ - emu_timer *m_tc_clear_timer = nullptr; + // FDC emu_timer *m_fdc_timer = nullptr; emu_timer *m_motor_start_timer[2]{}; - /* floppy state */ - uint8_t m_i8255_0_pc = 0; - uint8_t m_i8255_1_pc = 0; uint8_t m_fdc_mode = 0; - uint8_t m_fdc_irq_opcode = 0; - uint8_t idp_status_r(); - void idp_command_w(uint8_t data); - void idp_param_w(uint8_t data); - void palette_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - uint16_t sys_port4_r(); + uint8_t m_fdc_ctrl_2 = 0; + bool m_xtmask = false; + TIMER_CALLBACK_MEMBER(t3_mouse_callback); + TIMER_CALLBACK_MEMBER(pc88va_fdc_timer); + TIMER_CALLBACK_MEMBER(pc88va_fdc_motor_start_0); + TIMER_CALLBACK_MEMBER(pc88va_fdc_motor_start_1); + DECLARE_WRITE_LINE_MEMBER(tc_w); + + DECLARE_WRITE_LINE_MEMBER(fdc_irq); + static void floppy_formats(format_registration &fr); + void pc88va_fdc_update_ready(floppy_image_device *, int); + uint8_t fake_subfdc_r(); + uint8_t pc88va_fdc_r(offs_t offset); + void pc88va_fdc_w(offs_t offset, uint8_t data); + uint16_t bios_bank_r(); void bios_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); uint8_t rom_bank_r(); @@ -131,28 +151,18 @@ private: void backupram_wp_0_w(uint16_t data); uint8_t kanji_ram_r(offs_t offset); void kanji_ram_w(offs_t offset, uint8_t data); + uint8_t hdd_status_r(); - #if TEST_SUBFDC - uint8_t upd765_tc_r(); - void upd765_mc_w(uint8_t data); - #else - uint8_t no_subfdc_r(); - #endif - uint8_t pc88va_fdc_r(offs_t offset); - void pc88va_fdc_w(offs_t offset, uint8_t data); + uint16_t sysop_r(); - uint16_t screen_ctrl_r(); - void screen_ctrl_w(uint16_t data); void timer3_ctrl_reg_w(uint8_t data); - void video_pri_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); uint8_t backupram_dsw_r(offs_t offset); void sys_port1_w(uint8_t data); - uint32_t screen_update_pc88va(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - INTERRUPT_GEN_MEMBER(pc88va_vrtc_irq); - uint8_t cpu_8255_c_r(); - void cpu_8255_c_w(uint8_t data); - uint8_t fdc_8255_c_r(); - void fdc_8255_c_w(uint8_t data); + u8 sys_port5_r(); + void sys_port5_w(u8 data); + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + TIMER_DEVICE_CALLBACK_MEMBER(vrtc_irq); + uint8_t r232_ctrl_porta_r(); uint8_t r232_ctrl_portb_r(); uint8_t r232_ctrl_portc_r(); @@ -160,31 +170,70 @@ private: void r232_ctrl_portb_w(uint8_t data); void r232_ctrl_portc_w(uint8_t data); uint8_t get_slave_ack(offs_t offset); - DECLARE_WRITE_LINE_MEMBER(pc88va_pit_out0_changed); -// DECLARE_WRITE_LINE_MEMBER(pc88va_upd765_interrupt); - uint8_t m_fdc_ctrl_2; - TIMER_CALLBACK_MEMBER(pc8801fd_upd765_tc_to_zero); - TIMER_CALLBACK_MEMBER(t3_mouse_callback); - TIMER_CALLBACK_MEMBER(pc88va_fdc_timer); - TIMER_CALLBACK_MEMBER(pc88va_fdc_motor_start_0); - TIMER_CALLBACK_MEMBER(pc88va_fdc_motor_start_1); -// uint16_t m_fdc_dma_r(); -// void m_fdc_dma_w(uint16_t data); - DECLARE_WRITE_LINE_MEMBER(pc88va_hlda_w); - DECLARE_WRITE_LINE_MEMBER(pc88va_tc_w); - uint8_t fdc_dma_r(); - void fdc_dma_w(uint8_t data); - uint8_t dma_memr_cb(offs_t offset); - void dma_memw_cb(offs_t offset, uint8_t data); - DECLARE_WRITE_LINE_MEMBER(fdc_irq); - DECLARE_WRITE_LINE_MEMBER(fdc_drq); - static void floppy_formats(format_registration &fr); - void pc88va_fdc_update_ready(floppy_image_device *, int); + uint16_t m_video_pri_reg[2]{}; + + u16 m_screen_ctrl_reg = 0; + bool m_dm = false; + bool m_ymmd = false; + u16 m_gfx_ctrl_reg = 0; + + u16 m_color_mode = 0; + u8 m_pltm, m_pltp = 0; + + u16 m_text_transpen = 0; + bool m_td = false; + bitmap_rgb32 m_graphic_bitmap[2]; + + u16 screen_ctrl_r(); + void screen_ctrl_w(offs_t offset, u16 data, u16 mem_mask = ~0); + u16 gfx_ctrl_r(); + void gfx_ctrl_w(offs_t offset, u16 data, u16 mem_mask = ~0); + void video_pri_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + void color_mode_w(offs_t offset, u16 data, u16 mem_mask = ~0); + void text_transpen_w(offs_t offset, u16 data, u16 mem_mask = ~0); + void text_control_1_w(u8 data); + + u8 m_kanji_cg_line = 0; + u8 m_kanji_cg_jis[2]{}; + u8 m_kanji_cg_lr = 0; + + u8 kanji_cg_r(); + void kanji_cg_raster_w(u8 data); + void kanji_cg_address_w(offs_t offset, u8 data); + + void palette_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + u8 get_layer_pal_bank(u8 which); void draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect); + void draw_graphic_layer(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 which); + + void draw_indexed_gfx_1bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u8 pal_base); + void draw_indexed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height); + void draw_direct_gfx_8bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u16 fb_width, u16 fb_height); + void draw_direct_gfx_rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u16 fb_width, u16 fb_height); + + void draw_packed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height); + void draw_packed_gfx_5bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height); + uint32_t calc_kanji_rom_addr(uint8_t jis1,uint8_t jis2,int x,int y); void draw_text(bitmap_rgb32 &bitmap, const rectangle &cliprect); - void tsp_sprite_enable(uint32_t spr_offset, uint16_t sw_bit); + + // IDP + tsp_t m_tsp; + + uint8_t m_cmd = 0; + uint8_t m_buf_size = 0; + uint8_t m_buf_index = 0; + uint8_t m_buf_ram[16]{}; + u16 m_vrtc_irq_line = 432; + + uint8_t idp_status_r(); + void idp_command_w(uint8_t data); + void idp_param_w(uint8_t data); + + void tsp_sprite_enable(u32 sprite_number, bool sprite_enable, bool blink_enable); void execute_sync_cmd(); void execute_dspon_cmd(); void execute_dspdef_cmd(); @@ -194,16 +243,44 @@ private: void execute_emul_cmd(); void execute_spron_cmd(); void execute_sprsw_cmd(); + void execute_spwr_cmd(u8 data); - void pc88va_map(address_map &map); - void pc88va_io_map(address_map &map); + void main_map(address_map &map); + void io_map(address_map &map); void sysbank_map(address_map &map); + void opna_map(address_map &map); + + void sgp_map(address_map &map); - void pc88va_z80_io_map(address_map &map); - void pc88va_z80_map(address_map &map); protected: required_device m_gfxdecode; required_device m_palette; + +// TODO: stuff backported from PC8801 as QoL that should really be common +private: + uint8_t misc_ctrl_r(); + void misc_ctrl_w(uint8_t data); + uint8_t port40_r(); + void port40_w(offs_t offset, u8 data); + void rtc_w(offs_t offset, u8 data); + u8 opn_porta_r(); + + u8 m_device_ctrl_data = 0; + u8 m_misc_ctrl = 0x80; + bool m_sound_irq_enable = false; + bool m_sound_irq_pending = false; + DECLARE_WRITE_LINE_MEMBER(int4_irq_w); + + struct mouse_t { + uint8_t phase = 0; + int8_t prev_dx = 0, prev_dy = 0; + uint8_t lx = 0, ly = 0; + + attotime time = attotime::never; + }; + attotime mouse_limit_hz(); + + mouse_t m_mouse; }; diff --git a/src/mame/nec/pc88va_sgp.cpp b/src/mame/nec/pc88va_sgp.cpp new file mode 100644 index 00000000000..4e7f3cc74d2 --- /dev/null +++ b/src/mame/nec/pc88va_sgp.cpp @@ -0,0 +1,403 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese +/************************************************************************************************** + +NEC SGP (スーパーグラフィックプロセッサ / Super Graphic Processor) + +Unknown part number, used as GPU for PC88VA + +TODO: +- timing details +- specifics about what exactly happens in work area when either SGP runs or is idle. +- famista: during gameplay it BITBLT same source to destination 0x00037076 + with tp_mode = 3 and pitch = 0 (!?); +- rtype: during gameplay it does transfers with Pitch = 0xfff0, alias for negative draw? +- basic fires a VABOT on loading; + +**************************************************************************************************/ + +#include "emu.h" +#include "pc88va_sgp.h" + +#include + + +#define LOG_COMMAND (1U << 2) + +#define VERBOSE (LOG_GENERAL) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGCOMMAND(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__) + +// device type definition +DEFINE_DEVICE_TYPE(PC88VA_SGP, pc88va_sgp_device, "pc88va_sgp", "NEC PC88VA Super Graphic Processor") + +pc88va_sgp_device::pc88va_sgp_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, PC88VA_SGP, tag, owner, clock), + device_memory_interface(mconfig, *this), + m_data(nullptr) +{ +} + +device_memory_interface::space_config_vector pc88va_sgp_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_DATA, &m_data_config) + }; +} + + +void pc88va_sgp_device::device_config_complete() +{ + m_data_config = address_space_config( "data", ENDIANNESS_LITTLE, 16, 22, 0 ); +} + +void pc88va_sgp_device::device_start() +{ + m_data = &space(AS_DATA); +} + +void pc88va_sgp_device::device_reset() +{ + +} + +/**************************************** + * I/Os + ***************************************/ + +void pc88va_sgp_device::sgp_io(address_map &map) +{ + // TODO: check if readable + map(0x00, 0x03).w(FUNC(pc88va_sgp_device::vdp_address_w)); + map(0x04, 0x04).w(FUNC(pc88va_sgp_device::control_w)); + map(0x06, 0x06).rw(FUNC(pc88va_sgp_device::status_r), FUNC(pc88va_sgp_device::trigger_w)); +} + +void pc88va_sgp_device::vdp_address_w(offs_t offset, u16 data, u16 mem_mask) +{ + COMBINE_DATA(&m_vdp_address[offset]); +} + +/* + * ---- -x-- VINTF (1) enable irq on END execution, (0) clear irq + * ---- --x- VABOT suspend SGP execution (1) + */ +void pc88va_sgp_device::control_w(u8 data) +{ + if (data) + popmessage("SGP warning write %02x", data); +} + +/* + * ---- ---x VBUSY (1) busy flag + */ +u8 pc88va_sgp_device::status_r() +{ + return 0; +} + +/* + * ---- ---x SGPCA start SGP execution (1) + */ +void pc88va_sgp_device::trigger_w(u8 data) +{ + // TODO: under a timer + if (BIT(data, 0)) + start_exec(); + + if (data != 1) + LOG("Warning: SGP trigger write %02x\n", data); +} + +void pc88va_sgp_device::start_exec() +{ + u32 vdp_pointer = (m_vdp_address[0]) | (m_vdp_address[1] << 16); + // TODO: the SGP should go indefinitely until an END is issued + // for now punt early until we find something that warrants a parallel execution + // boomer is the current upper limit, uses SGP to transfer rows on vertical scrolling. + const u32 end_pointer = vdp_pointer + 0x800; + LOGCOMMAND("SGP: trigger start %08x\n", vdp_pointer); + + bool end_issued = false; + + while( vdp_pointer < end_pointer ) + { + const u16 cur_opcode = m_data->read_word(vdp_pointer); + u16 next_pc = 2; + + switch(cur_opcode) + { + // END + case 0x0001: + LOGCOMMAND("SGP: (PC=%08x) END\n", vdp_pointer); + end_issued = true; + break; + // NOP + case 0x0002: + LOGCOMMAND("SGP: (PC=%08x) NOP\n", vdp_pointer); + break; + // SET WORK + case 0x0003: + { + // This sets a work RAM address so that SGP can store internal data + // This buffer needs 58 bytes, and nothing else is known other than + // SGP needing this command issued as first before it can work with + // any meaningful command. + // Speculation is that assuming the host can r/w this area without + // huge penalties it can also observe or even override the + // SGP internals ... + const u16 lower_offset = m_data->read_word(vdp_pointer + 2); + const u16 upper_offset = m_data->read_word(vdp_pointer + 4); + + m_work_address = lower_offset | upper_offset << 16; + LOGCOMMAND("SGP: (PC=%08x) SET WORK %08x\n", + vdp_pointer, + m_work_address + ); + + next_pc += 4; + break; + } + // SET SOURCE + case 0x0004: + // SET DESTINATION + case 0x0005: + { + const bool mode = cur_opcode == 5; + BufferArea *ptr = mode ? &m_dst : &m_src; + + /* + * ---- ---- xxxx ---- start dot position + * ---- ---- ---- --xx SCRN_M: pixel mode + * ---- ---- ---- --00 1bpp + * ---- ---- ---- --01 4bpp + * ---- ---- ---- --10 8bpp + * ---- ---- ---- --11 RGB565 + */ + const u16 param1 = m_data->read_word(vdp_pointer + 2); + + ptr->start_dot = (param1 & 0xf0) >> 4; + ptr->pixel_mode = (param1 & 0x03); + ptr->hsize = m_data->read_word(vdp_pointer + 4) & 0x0fff; + ptr->vsize = m_data->read_word(vdp_pointer + 6) & 0x0fff; + ptr->fb_pitch = m_data->read_word(vdp_pointer + 8) & 0xfffc; + ptr->address = (m_data->read_word(vdp_pointer + 10) & 0xfffe) + | (m_data->read_word(vdp_pointer + 12) << 16); + + LOGCOMMAND("SGP: (PC=%08x) SET %s %02x|H %4u|V %4u|Pitch %5u| address %08x\n" + , vdp_pointer + , mode ? "DESTINATION" : "SOURCE " + , param1 + , ptr->hsize + , ptr->vsize + , ptr->fb_pitch + , ptr->address + ); + + next_pc += 12; + break; + } + case 0x0006: + { + const u16 color_code = m_data->read_word(vdp_pointer + 2); + LOGCOMMAND("SGP: (PC=%08x) SET COLOR %04x\n" + , vdp_pointer + , color_code + ); + next_pc += 2; + break; + } + // BitBLT + case 0x0007: + // PATBLT + case 0x0008: + { + const bool cmd_mode = cur_opcode == 0x0008; + const u16 draw_mode = m_data->read_word(vdp_pointer + 2); + + LOGCOMMAND("SGP: (PC=%08x) %s %04x\n" + , vdp_pointer + , cmd_mode ? "PATBLT" : "BITBLT" + , draw_mode + ); + cmd_blit(draw_mode, cmd_mode); + + next_pc += 2; + break; + } + // LINE + case 0x0009: + { + // mostly same as above + const u16 draw_mode = m_data->read_word(vdp_pointer + 2); + // documentation omits there's an extra parameter here + // cfr. animefrm, hardcoded to 2 there + const u16 unk_param = m_data->read_word(vdp_pointer + 4); + // in pixels + const u16 h_size = m_data->read_word(vdp_pointer + 6); + const u16 v_size = m_data->read_word(vdp_pointer + 8); + const u16 fb_pitch = m_data->read_word(vdp_pointer + 10) & 0xfffc; + const u32 src_address = (m_data->read_word(vdp_pointer + 12) & 0xfffe) + | (m_data->read_word(vdp_pointer + 14) << 16); + + // Note: start dot position and pixel mode set by SET SOURCE + LOGCOMMAND("SGP: (PC=%08x) LINE %04x %04x %04x %04x %04x %08x\n" + , vdp_pointer + , draw_mode + , unk_param + , h_size + , v_size + , fb_pitch + , src_address + ); + next_pc += 14; + break; + } + // CLS + case 0x000a: + { + const u32 src_address = (m_data->read_word(vdp_pointer + 2) & 0xfffe) + | (m_data->read_word(vdp_pointer + 4) << 16); + const u32 word_size = (m_data->read_word(vdp_pointer + 6)) + | (m_data->read_word(vdp_pointer + 8) << 16); + + LOGCOMMAND("SGP: (PC=%08x) CLS %08x %08x\n" + , vdp_pointer + , src_address + , word_size + ); + next_pc += 8; + break; + } + // SCAN RIGHT + case 0x000b: + // SCAN LEFT + case 0x000c: + { + // This uses the destination block data to find a specific pixel + // thru the SET COLOR command. + // It updates the horizontal size of destination if the color is found, + // returns 0 if the pixel is at origin, doesn't update if not found. + const u8 mode = cur_opcode == 0x000c; + LOGCOMMAND("SGP: (PC=%08x) %s\n" + , vdp_pointer + , mode ? "SCAN LEFT" : "SCAN RIGHT" + ); + break; + } + default: + LOGCOMMAND("SGP: (PC=%08x) %04x???\n", vdp_pointer, cur_opcode); + } + + if (end_issued == true) + break; + + vdp_pointer += next_pc; + } + + if (vdp_pointer >= end_pointer) + LOG("Warning: execution punt without an END issued\n"); +} + +/**************************************** + * Commands + ***************************************/ + + +/* + * ---x ---- ---- ---- SF (0) shift source according to destination position + * ---- x--- ---- ---- VD vertical transfer direction (1) negative (0) positive + * ---- -x-- ---- ---- HD horizontal transfer direction (1) negative (0) positive + * ---- --xx ---- ---- TP/MOD + * ---- --00 ---- ---- transfer source as-is + * ---- --01 ---- ---- do not transfer if source is 0 (transparent pen) + * ---- --10 ---- ---- transfer only if destination is 0 + * ---- --11 ---- ---- + * ---- ---- ---- xxxx LOGICAL OP + * ---- ---- ---- 0000 0 + * ---- ---- ---- 0001 Src AND Dst + * ---- ---- ---- 0010 /Src AND Dst + * ---- ---- ---- 0011 NOP + * ---- ---- ---- 0100 Src AND /Dst + * ---- ---- ---- 0101 Src + * ---- ---- ---- 0110 Src XOR Dst + * ---- ---- ---- 0111 Src OR Dst + * ---- ---- ---- 1000 /(Src OR Dst) + * ---- ---- ---- 1001 /(Src XOR Dst) + * ---- ---- ---- 1010 /Src + * ---- ---- ---- 1011 /Src OR Dst + * ---- ---- ---- 1100 /Dst + * ---- ---- ---- 1101 Src OR /Dst + * ---- ---- ---- 1110 /(Src AND Dst) + * ---- ---- ---- 1111 1 + * + * PATBLT is identical to BITBLT except it repeats source copy + * if it exceeds the clipping range. + */ +void pc88va_sgp_device::cmd_blit(u16 draw_mode, bool is_patblt) +{ + const u8 logical_op = draw_mode & 0xf; + const u8 tp_mod = (draw_mode >> 8) & 0x3; + + // TODO: boomer title screen + if (is_patblt == true) + { + LOG("PATBLT\n"); + // return; + } + + // ballbrkr: 6 + if (logical_op != 5) + { + LOG("BITBLT logical_op == %d\n", logical_op); + return; + } + + if (tp_mod > 1) + { + LOG("BITBLT tp_mod == %d\n", tp_mod); + return; + } + + if (m_src.hsize != m_dst.hsize || m_src.vsize != m_dst.vsize) + { + LOG("BITBLT non-even sizes (%d x %d) x (%d x %d)\n", m_src.hsize, m_src.vsize, m_dst.hsize, m_dst.vsize); + return; + } + + if (m_src.pixel_mode == 0 || m_src.pixel_mode == 3 || m_src.pixel_mode != m_dst.pixel_mode) + { + LOG("BITBLT pixel mode %d x %d\n", m_src.pixel_mode, m_dst.pixel_mode); + return; + } + + for (int yi = 0; yi < m_src.vsize; yi ++) + { + u32 src_address = m_src.address + (yi * m_src.fb_pitch); + u32 dst_address = m_dst.address + (yi * m_dst.fb_pitch); + + for (int xi = 0; xi < (m_src.hsize >> 2); xi ++) + { + // TODO: not very efficient, we need a cleaner per-pixel RMW phase + const u16 src_dot = m_data->read_word(src_address); + const u16 dst_dot = m_data->read_word(dst_address); + u16 result = 0; + + for (int pixi = 0; pixi < 4; pixi ++) + { + u8 cur_pixel = (src_dot & 0xf); + if (cur_pixel || tp_mod == 0) + result |= (src_dot & 0xf) << (pixi * 4); + else + result |= (dst_dot & 0xf) << (pixi * 4); + } + + m_data->write_word(dst_address, result); + src_address += 2; + dst_address += 2; + } + } +} diff --git a/src/mame/nec/pc88va_sgp.h b/src/mame/nec/pc88va_sgp.h new file mode 100644 index 00000000000..7a0b2029225 --- /dev/null +++ b/src/mame/nec/pc88va_sgp.h @@ -0,0 +1,59 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese + +#ifndef MAME_NEC_PC88VA_SGP_H +#define MAME_NEC_PC88VA_SGP_H + +#pragma once + +class pc88va_sgp_device : + public device_t, + public device_memory_interface +{ +public: + pc88va_sgp_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + template pc88va_sgp_device& set_map(T &&... args) { set_addrmap(AS_DATA, std::forward(args)...); return *this; } + + void sgp_io(address_map &map); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_config_complete() override; + + virtual space_config_vector memory_space_config() const override; + +private: + address_space_config m_data_config; + address_space *m_data; + + u16 m_vdp_address[2]{}; + u32 m_work_address = 0; + + struct BufferArea { + u8 start_dot = 0; + u8 pixel_mode = 0; + u16 hsize = 0; + u16 vsize = 0; + u16 fb_pitch = 0; + u32 address = 0; + }; + + BufferArea m_src, m_dst; + + void vdp_address_w(offs_t offset, u16 data, u16 mem_mask = ~0); + u8 status_r(); + void control_w(u8 data); + void trigger_w(u8 data); + + void start_exec(); + + // commands + void cmd_blit(u16 draw_mode, bool is_patblt); +}; + + +DECLARE_DEVICE_TYPE(PC88VA_SGP, pc88va_sgp_device) + +#endif // MAME_NEC_PC88VA_SGP_H diff --git a/src/mame/nec/pc88va_v.cpp b/src/mame/nec/pc88va_v.cpp new file mode 100644 index 00000000000..3dd41cc341f --- /dev/null +++ b/src/mame/nec/pc88va_v.cpp @@ -0,0 +1,1654 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese + +#include "emu.h" +#include "pc88va.h" + +#include + + +#define LOG_IDP (1U << 2) // TSP data +#define LOG_FB (1U << 3) // framebuffer strips (verbose) +#define LOG_KANJI (1U << 4) // empty Kanji data warnings +#define LOG_CRTC (1U << 5) +#define LOG_COLOR (1U << 6) // current color mode +#define LOG_TEXT (1U << 7) // text strips (verbose) + +#define VERBOSE (LOG_GENERAL | LOG_IDP) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGIDP(...) LOGMASKED(LOG_IDP, __VA_ARGS__) +#define LOGFB(...) LOGMASKED(LOG_FB, __VA_ARGS__) +#define LOGKANJI(...) LOGMASKED(LOG_KANJI, __VA_ARGS__) +#define LOGCRTC(...) LOGMASKED(LOG_CRTC, __VA_ARGS__) +#define LOGCOLOR(...) LOGMASKED(LOG_COLOR, __VA_ARGS__) +#define LOGTEXT(...) LOGMASKED(LOG_TEXT, __VA_ARGS__) + +void pc88va_state::video_start() +{ + const u32 kanjiram_size = 0x4000; + m_kanjiram = std::make_unique(kanjiram_size); + m_gfxdecode->gfx(2)->set_source(m_kanjiram.get()); + m_gfxdecode->gfx(3)->set_source(m_kanjiram.get()); + m_vrtc_irq_line = 432; + + for (int i = 0; i < 2; i++) + m_screen->register_screen_bitmap(m_graphic_bitmap[i]); + + save_item(NAME(m_screen_ctrl_reg)); + save_item(NAME(m_gfx_ctrl_reg)); + save_item(NAME(m_color_mode)); + save_item(NAME(m_pltm)); + save_item(NAME(m_pltp)); + + save_item(NAME(m_text_transpen)); + save_pointer(NAME(m_video_pri_reg), 2); + save_pointer(NAME(m_kanjiram), kanjiram_size); + + save_item(NAME(m_vrtc_irq_line)); +} + +void pc88va_state::palette_init(palette_device &palette) const +{ + // default palette + const u16 default_palette[16] = { + 0x0000, 0x001f, 0x03e0, 0x03ff, 0xfc00, 0xfc1f, 0xffe0, 0xffff, + 0x7def, 0x0015, 0x02a0, 0x02b5, 0xac00, 0xac15, 0xaea0, 0xaeb5 + }; + int i, pal_base; + for (i = 0; i < 16; i++) + { + const u16 color = default_palette[i]; + u8 b = pal5bit((color & 0x001f)); + u8 r = pal5bit((color & 0x03e0) >> 5); + u8 g = pal6bit((color & 0xfc00) >> 10); + for (pal_base = 0; pal_base < 2; pal_base ++) + palette.set_pen_color(i + pal_base * 16, r, g, b); + } +} + + +uint32_t pc88va_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + uint8_t pri, cur_pri_lv; + uint32_t screen_pri; + bitmap.fill(0, cliprect); + + // don't bother if we are under DSPOFF command + if(m_tsp.disp_on == false) + return 0; + + // rollback to V1/V2 mode + if (!BIT(m_pltm, 2)) + { + // BIOS doesn't explicitly set a color mode for the V1/V2 Mode printout at POST + // pc8801_state::screen_update(bitmap, cliprect); + //return 0; + } + + /* + m_video_pri_reg[0] + xxxx ---- ---- ---- priority 3 + ---- xxxx ---- ---- priority 2 + ---- ---- xxxx ---- priority 1 + ---- ---- ---- xxxx priority 0 + m_video_pri_reg[1] + ---- ---- xxxx ---- priority 5 + ---- ---- ---- xxxx priority 4 + + Note that orthogonality level is actually REVERSED than the level number it indicates, so we have to play a little with the data for an easier usage ... + */ + + // TODO: this should be calculated only in video_pri_w for cache + screen_pri = (m_video_pri_reg[1] & 0x00f0) >> 4; // priority 5 + screen_pri|= (m_video_pri_reg[1] & 0x000f) << 4; // priority 4 + screen_pri|= (m_video_pri_reg[0] & 0xf000) >> 4; // priority 3 + screen_pri|= (m_video_pri_reg[0] & 0x0f00) << 4; // priority 2 + screen_pri|= (m_video_pri_reg[0] & 0x00f0) << 12; // priority 1 + screen_pri|= (m_video_pri_reg[0] & 0x000f) << 20; // priority 0 + + //popmessage("%08x", screen_pri); + + for(pri = 0; pri < 6; pri++) + { + cur_pri_lv = (screen_pri >> (pri * 4)) & 0xf; + + if(cur_pri_lv & 8) // enable layer + { + // TODO: ballbrkr and tetris definitely wants this to have an higher priority + if(pri <= 1) // (direct color mode, priority 5 and 4) + { + switch(cur_pri_lv & 3) + { + // 8 = graphic 0 + case 0: + draw_graphic_layer(bitmap, cliprect, 0); + break; + // 9 = graphic 1 + case 1: + draw_graphic_layer(bitmap, cliprect, 1); + break; + default: + popmessage("Undocumented pri level %d", cur_pri_lv); + } + } + else + { + switch(cur_pri_lv & 3) // (palette color mode) + { + case 0: draw_text(bitmap, cliprect); break; + case 1: draw_sprites(bitmap,cliprect); break; + /* A = graphic 0 */ + case 2: + draw_graphic_layer(bitmap, cliprect, 0); + break; + /* B = graphic 1 */ + case 3: + draw_graphic_layer(bitmap, cliprect, 1); + break; + } + } + } + } + + return 0; +} + +/**************************************** + * Drawing fns + ***************************************/ + +inline u8 pc88va_state::get_layer_pal_bank(u8 which) +{ + if (!(BIT(m_pltm, 1))) + return (m_pltm & 1) << 4; + + if (m_pltm == 3) + return 0; + + // TODO: text and sprites may be joined when either one is selected in PLTP + // olteus sets sprite = 1, wants text to follow with the other bank too + // ballbrkr goes the other way around: writes 0, wants sprites to be 1 + return (m_pltp == which) << 4; +} + +void pc88va_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + // punt if we are under SPROFF + if(m_tsp.spr_on == false) + return; + + uint16_t const *const tvram = m_tvram; + + int offs = m_tsp.spr_offset; + const u8 layer_pal_bank = get_layer_pal_bank(1); + + // top to bottom for priority (shanghai menuing) + for(int i = 0x100 - 8; i >= 0; i -= 8) + { + int spr_count; + + int ysize = (tvram[(offs + i + 0) / 2] & 0xfc00) >> 10; + int sw = (tvram[(offs + i + 0) / 2] & 0x200) >> 9; + int yp = (tvram[(offs + i + 0) / 2] & 0x1ff); + int xsize = (tvram[(offs + i + 2) / 2] & 0xf800) >> 11; + int md = (tvram[(offs + i + 2) / 2] & 0x400) >> 10; + int xp = (tvram[(offs + i + 2) / 2] & 0x3ff); + int spda = (tvram[(offs + i + 4) / 2] & 0xffff); + + const bool is_cursor_sprite = + i == m_tsp.curn + && m_tsp.curn_blink == true + && (m_screen->frame_number() % (m_tsp.blink * 2)) >= m_tsp.blink; + + // assume cursor sprite just going transparent with blink effect + if(!sw || is_cursor_sprite) + continue; + + // shanghai hand (4bpp) and rtype beam sparks (1bpp) wants this arrangement + // Apparently TSP runs on its own translation unit for addresses + if (spda & 0x8000) + spda -= 0x4000; + + spda <<= 1; + + // TODO: verify this disabled code path + // makes more sense without the sign? + if(0) + { + // olteus wants this off + if(yp & 0x100) + { + yp &= 0xff; + yp = 0x100 - yp; + } + + // TODO: shinraba needs wrap-around during gameplay (touching left edge of screen). + if(xp & 0x200) + { + xp &= 0x1ff; + xp = 0x200 - xp; + } + } + + if(md) // 1bpp mode + { + int fg_col = (tvram[(offs + i + 6) / 2] & 0xf0) >> 4; + int bc = (tvram[(offs + i + 6) / 2] & 0x08) >> 3; + + // sprites don't draw if color is zero + // (shanghai exit from menuing) + if (!fg_col && !bc) + continue; + + xsize = (xsize + 1) * 32; + ysize = (ysize + 1) * 4; + + spr_count = 0; + + for(int y_i = 0; y_i < ysize; y_i++) + { + for(int x_i = 0; x_i < xsize; x_i+=16) + { + for(int x_s = 0; x_s < 16; x_s++) + { + int res_x = xp + x_i + x_s; + // TODO: MG actually doubles Y size + int res_y = (yp + y_i) << m_tsp.spr_mg; + + if (!cliprect.contains(res_x, res_y)) + continue; + + const u32 data_offset = ((spda + spr_count) & 0xffff) / 2; + u8 pen = (bitswap<16>(tvram[data_offset],7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8) >> (15 - x_s)) & 1; + + pen = pen & 1 ? fg_col : (bc) ? 8 : 0; + + if(pen != 0) + bitmap.pix(res_y, res_x) = m_palette->pen(pen + layer_pal_bank); + } + + spr_count += 2; + } + } + } + else + { + // 4bpp mode + xsize = (xsize + 1) * 8; + ysize = (ysize + 1) * 4; + + spr_count = 0; + + for(int y_i = 0; y_i < ysize; y_i++) + { + for(int x_i = 0; x_i < xsize; x_i += 4) + { + for(int x_s = 0; x_s < 4; x_s ++) + { + int res_x = xp + x_i + x_s; + int res_y = (yp + y_i) << m_tsp.spr_mg; + + if (!cliprect.contains(res_x, res_y)) + continue; + + const u32 data_offset = ((spda + spr_count) & 0xffff) / 2; + + int pen = (bitswap<16>(tvram[data_offset],7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8)) >> (12 - (x_s * 4)) & 0xf; + + //if (pen != 0 && pen != m_text_transpen) + if (pen != 0) + bitmap.pix(res_y, res_x) = m_palette->pen(pen + layer_pal_bank); + } + + spr_count += 2; + } + } + } + } +} + +// TODO: handcrafted kanji ROM causes this, should be simplified by a more accurate dump +// (or a better rearrange from driver_init, but that implies getting everything in place). +uint32_t pc88va_state::calc_kanji_rom_addr(uint8_t jis1, uint8_t jis2, int x, int y) +{ + // famista uses jis1 = 0x2c for the text box lines + // xak2 also wants jis1 bit 3 arranged this way for 8x16 English menuing plus other stuff + if(jis1 < 0x30) + return ((jis2 & 0x60) << 8) + ((jis1 & 0x07) << 10) + ((jis2 & 0x1f) << 5) + ((jis1 & 0x8) << 15); + + if((jis1 & 0xf0) == 0x30) + return ((jis2 & 0x60) << 10) + ((jis1 & 0x0f) << 10) + ((jis2 & 0x1f) << 5); + + if((jis1 & 0xf0) == 0x40) + return 0x4000 + ((jis2 & 0x60) << 10) + ((jis1 & 0x0f) << 10) + ((jis2 & 0x1f) << 5); + + LOGKANJI("%d %d %02x %02x\n",x, y, jis1, jis2); + + return 0; +} + +/* + * Text is handled in strips at the location stored in TSP + * [+00] Frame buffer start address (VSA) + * [+02] ---- ---- ---- -xxx Upper VSA + * [+04] ---- -xxx xxxx xxxx Frame buffer height (VH) + * [+06] + * [+08] ---- --xx xxxx xxxx Frame buffer width (VW), in bytes + * [+0a] xxxx ---- ---- ---- background color + * ---- xxxx ---- ---- foreground color + * ---- ---- ---x xxxx display mode + * [+0c] ---x xxxx ---- ---- Raster address offset + * [+0e] + * [+10] Split screen start address (RSA) + * [+12] / Upper RSA + * [+14] Split screen height (RH) + * [+16] Split screen width (RW) + * [+18] Split screen vertical start position (RYP) + * [+1a] Split screen horizontal start position (RXP) + * [+1c] + * [+1e] + */ +void pc88va_state::draw_text(bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + // punt if text disable is on + if(m_td == true) + return; + + uint16_t const *const tvram = m_tvram; + uint8_t const *const kanji = memregion("kanji")->base(); + + LOGTEXT("=== Start TEXT frame\n"); + + // four layers + for (int layer_n = 0; layer_n < 4; layer_n ++) + { + uint16_t const *const tsp_regs = &tvram[(layer_n * 0x20 + m_tsp.tvram_vreg_offset) / 2]; + + const u32 vsa = tsp_regs[0 / 2]; // | (tsp_regs[2 / 2] & 7) << 16; + const u32 rsa = tsp_regs[0x10 / 2]; // | (tsp_regs[0x12 / 2] & 7) << 16; + + // as-is it doesn't make much sense to enable either of these two, + // since TVRAM is really 16-bit address lines here. + // Since we are in IDP domain it may be used on another system ... + if (tsp_regs[2 / 2] || tsp_regs[0x12 / 2]) + popmessage("Upper VSA enabled!"); + + const u8 layer_pal_bank = get_layer_pal_bank(0); + + const u8 attr_mode = tsp_regs[0xa / 2] & 0x1f; + // TODO: check this out, diverges with documentation + const u8 screen_fg_col = (tsp_regs[0xa / 2] & 0xf000) >> 12; + const u8 screen_bg_col = (tsp_regs[0xa / 2] & 0x0f00) >> 8; + + // TODO: how even vh/vw can run have all these bytes? + const u8 vh = (tsp_regs[4 / 2] & 0x7ff); + const u16 vw = (tsp_regs[8 / 2] & 0x3ff) / 2; + + + if (vh == 0 || vw == 0) + { + LOGTEXT("\t%d skip VW = %d VH = %d\n" + , layer_n + , vw + , vh + ); + continue; + } + + LOGTEXT("\t%d %08x VSA - %08x RSA| %02x DISPLAY MODE|%d VW x %d VH\n" + , layer_n + , vsa + , rsa + , attr_mode + , vw + , vh + ); + + const u16 rh = tsp_regs[0x14 /2]; + const u16 rw = tsp_regs[0x16 /2]; + const u16 ryp = tsp_regs[0x18 / 2]; + const u16 rxp = tsp_regs[0x1a / 2]; + + const int raster_offset = (tsp_regs[0x0c / 2] >> 8) & 0x1f; + + LOGTEXT("\t%d RXP x %d RYP|%d RW x %d RH %d\n", rxp, ryp, rw, rh, raster_offset); + + rectangle split_cliprect(rxp, rxp + rw - 1, ryp, ryp + rh - 1); + split_cliprect &= cliprect; + + for(int y = 0; y < vh; y++) + { + int y_base = y * 16 + ryp - raster_offset; + + // TODO: consult with OG + if (!split_cliprect.contains(rxp, y_base) && + !split_cliprect.contains(rxp, y_base + 16)) + continue; + + for(int x = 0; x < vw; x++) + { + int x_base = x * 8; + if (!split_cliprect.contains(x_base, y_base) && + !split_cliprect.contains(x_base, y_base + 16)) + continue; + + // TODO: understand where VSA comes into equation + const u32 cur_offset = ((rsa >> 1) + x + (y * vw)); + + uint16_t attr = (tvram[cur_offset + (m_tsp.attr_offset / 2)] & 0x00ff); + + uint8_t fg_col, bg_col, secret, reverse; + //uint8_t blink,dwidc,dwid,uline,hline; + fg_col = bg_col = reverse = secret = 0; //blink = dwidc = dwid = uline = hline = 0; + + // TODO: convert to functional programming + switch(attr_mode) + { + /* + xxxx ---- foreground color + ---- xxxx background color + */ + case 0: + fg_col = (attr & 0x0f) >> 0; + bg_col = (attr & 0xf0) >> 4; + break; + /* + xxxx ---- foreground color + ---- x--- horizontal line + ---- -x-- reverse + ---- --x- blink + ---- ---x secret (hide text) + background color is defined by screen control table values + */ + case 1: + fg_col = (attr & 0xf0) >> 4; + bg_col = screen_bg_col; + //hline = (attr & 0x08) >> 3; + reverse = (attr & 0x04) >> 2; + //blink = (attr & 0x02) >> 1; + secret = (attr & 0x01) >> 0; + break; + /* + x--- ---- dwidc + -x-- ---- dwid + --x- ---- uline + ---x ---- hline + ---- -x-- reverse + ---- --x- blink + ---- ---x secret (hide text) + background and foreground colors are defined by screen control table values + */ + case 2: + fg_col = screen_fg_col; + bg_col = screen_bg_col; + //dwidc = (attr & 0x80) >> 7; + //dwid = (attr & 0x40) >> 6; + //uline = (attr & 0x20) >> 5; + //hline = (attr & 0x10) >> 4; + reverse = (attr & 0x04) >> 2; + //blink = (attr & 0x02) >> 1; + secret = (attr & 0x01) >> 0; + break; + /* + ---- x--- mixes between mode 0 and 2 + + xxxx 1--- foreground color + ---- 1xxx background color + 2) + x--- 0--- dwidc + -x-- 0--- dwid + --x- 0--- uline + ---x 0--- hline + ---- 0x-- reverse + ---- 0-x- blink + ---- 0--x secret (hide text) + background and foreground colors are defined by screen control table values + */ + case 3: + { + // TODO: similar to 3301 drawing (where it should save previous attribute setup) + if(attr & 0x8) + { + fg_col = (attr & 0xf0) >> 4; + bg_col = (attr & 0x07) >> 0; + } + else + { + fg_col = screen_fg_col; + bg_col = screen_bg_col; + //dwidc = (attr & 0x80) >> 7; + //dwid = (attr & 0x40) >> 6; + //uline = (attr & 0x20) >> 5; + //hline = (attr & 0x10) >> 4; + reverse = (attr & 0x04) >> 2; + //blink = (attr & 0x02) >> 1; + secret = (attr & 0x01) >> 0; + } + } + break; + /* + x--- ---- blink + -xxx ---- background color + ---- xxxx foreground color + */ + case 4: + fg_col = (attr & 0x0f) >> 0; + bg_col = (attr & 0x70) >> 4; + //blink = (attr & 0x80) >> 7; + break; + /* + x--- ---- blink + -xxx ---- background color + ---- xxxx foreground color + hline is enabled if foreground color is 1 or 9 + */ + case 5: + fg_col = (attr & 0x0f) >> 0; + bg_col = (attr & 0x70) >> 4; + //blink = (attr & 0x80) >> 7; + //if((fg_col & 7) == 1) + //hline = 1; + break; + default: + popmessage("Illegal text tilemap attribute mode %02x",attr_mode); + return; + } + + // TODO: more functional programming + if ((tvram[cur_offset] & 0xff00) == 0) // ANK + { + u32 tile_num = ((tvram[cur_offset] & 0xff) * 16) | 0x40000; + + for(int yi = 0; yi < 16; yi++) + { + for(int xi = 0; xi < 8; xi++) + { + int res_x = x_base + xi; + int res_y = y_base + yi; + + if(!split_cliprect.contains(res_x, res_y)) + continue; + + int pen = kanji[yi + tile_num] >> (7-xi) & 1; + + if(reverse) + pen = pen & 1 ? bg_col : fg_col; + else + pen = pen & 1 ? fg_col : bg_col; + + if(secret) { pen = 0; } //hide text + + if(pen != 0 && pen != m_text_transpen) + bitmap.pix(res_y, res_x) = m_palette->pen(pen + layer_pal_bank); + } + } + } + else if ((tvram[cur_offset] & 0x00ff) == 0x56) + { + // famista draws cursor/team letters/referee calls with PCG + // boomer draws HP bar on gameplay + // both shifts bits 5-6 by 1 for upper tiles to be properly accessed, + // cfr. gfxdecoding where there's an hole every 0x20 chars + // (no way to access these?) + const u8 base_tile = (tvram[cur_offset] & 0xff00) >> 8; + u32 tile_num = ((base_tile & 0x1f) + ((base_tile & 0x60) << 1)) * 0x20; + u8 lr_half_gfx = BIT(base_tile, 7); + + for(int yi = 0; yi < 16; yi++) + { + for(int xi = 0; xi < 8; xi++) + { + int res_x = x_base + xi; + int res_y = y_base + yi; + + if(!split_cliprect.contains(res_x, res_y)) + continue; + + int pen = m_kanjiram[(( yi * 2 ) + lr_half_gfx) + tile_num] >> (7 - xi) & 1; + + if(reverse) + pen = pen & 1 ? bg_col : fg_col; + else + pen = pen & 1 ? fg_col : bg_col; + + if(secret) { pen = 0; } //hide text + + if(pen != 0 && pen != m_text_transpen) + bitmap.pix(res_y, res_x) = m_palette->pen(pen + layer_pal_bank); + } + } + } + else // kanji + { + uint8_t jis1 = (tvram[cur_offset] & 0x7f) + 0x20; + uint8_t jis2 = (tvram[cur_offset] & 0x7f00) >> 8; + uint16_t lr_half_gfx = ((tvram[cur_offset] & 0x8000) >> 15); + + uint32_t tile_num = calc_kanji_rom_addr(jis1, jis2, x, y); + + for(int yi = 0; yi < 16; yi++) + { + for(int xi = 0; xi < 8; xi++) + { + int res_x = x_base + xi; + int res_y = y_base + yi; + + if(!split_cliprect.contains(res_x, res_y)) + continue; + + int pen = kanji[((yi*2)+lr_half_gfx)+tile_num] >> (7-xi) & 1; + + if(reverse) + pen = pen & 1 ? bg_col : fg_col; + else + pen = pen & 1 ? fg_col : bg_col; + + if(secret) { pen = 0; } //hide text + + if(pen != 0) + bitmap.pix(res_y, res_x) = m_palette->pen(pen + layer_pal_bank); + } + } + } + } + } + } +} + +/* + * as above, graphics are stored in 4 strips in I/O $200-$27f + * [+00] xxxx xxxx xxxx xx-- frame buffer start address (FSA), 4 byte boundary + * [+02] ---- ---- ---- --xx FSA upper bit address + * \- fixed at 0x20000 for frame buffer 1 + * [+04] ---- -xxx xxxx xx-- frame buffer width (FBW), 4 byte boundary + * [+06] ---- --xx xxxx xxxx frame buffer height (FBL) - 1 + * \- cannot be set for frame buffer 1 (?) + * [+08] ---- ---- ---x xxxx X dot offset (fractional shift) + * \- changes depending on single/multiplane mode + * [+0a] ---- -xxx xxxx xx-- X scroll offset (OFX), 4 byte boundary + * [+0c] ---- --xx xxxx xxxx Y scroll offset (OFY) + * [+0e] xxxx xxxx xxxx xx-- Display start address (DSA) + * [+10] ---- ---- ---- --xx DSA upper bit address + * [+12] ---- ---x xxxx xxxx Sub screen height (DSH) + * [+14] + * [+16] ---- ---x xxxx xxxx Sub screen display position (DSP) + */ + +void pc88va_state::draw_graphic_layer(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 which) +{ + // disable graphic B if screen 0 only setting is enabled + if (which > m_ymmd) + return; + + // ditto for 5bpp color mode + const bool is_5bpp = m_pltm == 7; + if (is_5bpp && which == 1) + return; + + uint16_t const *const fb_regs = m_fb_regs; + + const u8 gfx_ctrl = (m_gfx_ctrl_reg >> (which * 8)) & 0x13; + + // TODO: xak2 wants independent doubled Y axis on setup menu & Micro Cabin logo + // i.e. 200 lines draw on a 400 lines canvas + const u32 pixel_size = 0x10000 >> BIT(gfx_ctrl, 4); + + const u8 layer_pal_bank = get_layer_pal_bank(2 + which); + + LOGFB("=== %02x GFX MODE graphic %s color bank %02x\n" + , gfx_ctrl + , which ? "B" : "A" + , layer_pal_bank + ); + + m_graphic_bitmap[which].fill(0, cliprect); + + const int layer_inc = (!is_5bpp) + 1; + const int layer_fixed = is_5bpp + 1; + + for (int layer_n = which; layer_n < 4; layer_n += layer_inc) + { + uint16_t const *const fb_strip_regs = &fb_regs[(layer_n * 0x20) / 2]; + const u16 fbw = fb_strip_regs[0x04 / 2] & 0x7fc; + + // assume that a fbw = 0 don't generate a layer at all + if (fbw == 0) + { + LOGFB("%d FBW = 0\n", layer_n); + continue; + } + + // on layer = 1 fsa is always 0x20000, cfr. shanghai + // (almost likely an HW quirk, described in the docs) + // also animefrm swaps this with layer 2 (main canvas) + const u32 fsa = (layer_n == layer_fixed) ? 0x20000 + : (fb_strip_regs[0x00 / 2] & 0xfffc) | ((fb_strip_regs[0x02 / 2] & 0x3) << 16) >> 1; + + const u16 fbl = (fb_strip_regs[0x06 / 2] & 0x3ff) + 1; + const u8 x_dot_offs = fb_strip_regs[0x08 / 2]; + const u16 ofx = fb_strip_regs[0x0a / 2] & 0x7fc; + const u16 ofy = fb_strip_regs[0x0c / 2] & 0x3ff; + const u32 dsa = ((fb_strip_regs[0x0e / 2] & 0xfffc) | ((fb_strip_regs[0x10 / 2] & 0x3) << 16)); + const u16 dsh = fb_strip_regs[0x12 / 2] & 0x1ff; + const u16 dsp = fb_strip_regs[0x16 / 2] & 0x1ff; + + LOGFB("%d %08x FSA|\n\t%d FBW | %d FBL |\n\t %d OFX (%d dot)| %d OFY|\n\t %08x DSA|\n\t %04x (%d) DSH | %04x (%d) DSP\n" + , layer_n + , fsa << 1 + , fbw + , fbl + , ofx + , x_dot_offs + , ofy + , dsa + , dsh + , dsh + , dsp + , dsp + ); + + rectangle split_cliprect(cliprect.min_x, cliprect.max_x, dsp, dsh + dsp - 1); + split_cliprect &= cliprect; + + // animefrm uses layer 1 with fbl = 1 for the color cycling bar on top + // rtype definitely don't want to be clipped on fbw, assume valid for pitch calc only. + rectangle fb_cliprect(cliprect.min_x, cliprect.max_x, dsp, dsp + fbl - 1); + split_cliprect &= fb_cliprect; + + if (!m_dm) + { + switch(gfx_ctrl & 3) + { + case 1: draw_packed_gfx_4bpp(m_graphic_bitmap[which], split_cliprect, fsa, dsa, layer_pal_bank, fbw, fbl); break; + } + } + else + { + switch(gfx_ctrl & 3) + { + //case 0: draw_indexed_gfx_1bpp(bitmap, cliprect, dsa, layer_pal_bank); break; + case 1: draw_indexed_gfx_4bpp(m_graphic_bitmap[which], split_cliprect, fsa, dsa, layer_pal_bank, fbw, fbl); break; + case 2: + if (m_pltm == 7) + { + draw_packed_gfx_5bpp(m_graphic_bitmap[which], split_cliprect, fsa, dsa, layer_pal_bank, fbw, fbl); + } + else + draw_direct_gfx_8bpp(m_graphic_bitmap[which], split_cliprect, fsa, fbw, fbl); + break; + case 3: draw_direct_gfx_rgb565(m_graphic_bitmap[which], split_cliprect, fsa, fbw, fbl); break; + } + } + } + + // TODO: we eventually need primask_copyrozbitmap_trans here, or a custom copy, depending on what the "transpen" registers really do. + copyrozbitmap_trans( + bitmap, cliprect, m_graphic_bitmap[which], + 0, 0, + pixel_size, 0, 0, pixel_size, + false, 0 + ); +} + +void pc88va_state::draw_indexed_gfx_1bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u8 pal_base) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = (((y * 640) / 8) + fb_start_offset) & 0x3ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x += 8) + { + u16 x_char = (x >> 3); + u32 bitmap_offset = line_offset + x_char; + + for (int xi = 0; xi < 8; xi ++) + { + uint32_t color = (gvram[bitmap_offset] >> (7 - xi)) & 1; + int res_x = x + xi; + + if(color && cliprect.contains(res_x, y)) + bitmap.pix(y, res_x) = m_palette->pen(color + pal_base); + } + } + } +} + +void pc88va_state::draw_indexed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + +// const u16 y_min = std::max(cliprect.min_y, y_start); +// const u16 y_max = std::min(cliprect.max_y, y_min + fb_height); + + //printf("%d %d %d %08x %d\n", y_min, y_max, fb_width, start_offset, fb_height); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = ((y * fb_width) + fb_start_offset) & 0x3ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x += 2) + { + u16 x_char = (x >> 1); + u32 bitmap_offset = line_offset + x_char; + + for (int xi = 0; xi < 2; xi ++) + { + u8 color = (gvram[bitmap_offset] >> (xi ? 0 : 4)) & 0xf; + + if(color && cliprect.contains(x + xi, y)) + bitmap.pix(y, x + xi) = m_palette->pen(color + pal_base); + } + } + } +} + +void pc88va_state::draw_packed_gfx_5bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + +// const u16 y_min = std::max(cliprect.min_y, y_start); +// const u16 y_max = std::min(cliprect.max_y, y_min + fb_height); + + //printf("%d %d %d %08x %d\n", y_min, y_max, fb_width, start_offset, fb_height); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = ((y * fb_width) + fb_start_offset) & 0x3ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + u32 bitmap_offset = line_offset + x; + + u8 color = gvram[bitmap_offset] & 0x1f; + + if(color && cliprect.contains(x, y)) + bitmap.pix(y, x) = m_palette->pen(color); + } + } +} + +void pc88va_state::draw_direct_gfx_8bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u16 fb_width, u16 fb_height) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + +// const u16 y_min = std::max(cliprect.min_y, y_start); +// const u16 y_max = std::min(cliprect.max_y, y_min + fb_height); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = ((y * fb_width) + fb_start_offset) & 0x3ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + u32 bitmap_offset = line_offset + x; + + uint32_t color = (gvram[bitmap_offset] & 0xff); + + // boomer suggests that transparency is calculated over just color = 0, may be settable? + // TODO: may not be clamped to palNbit + if(color && cliprect.contains(x, y)) + { + u8 b = pal2bit(color & 0x03); + u8 r = pal3bit((color & 0x1c) >> 2); + u8 g = pal3bit((color & 0xe0) >> 5); + bitmap.pix(y, x) = (b) | (g << 8) | (r << 16); + } + } + } +} + +void pc88va_state::draw_direct_gfx_rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u16 fb_width, u16 fb_height) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + +// const u16 y_min = std::max(cliprect.min_y, y_start); +// const u16 y_max = std::min(cliprect.max_y, y_min + fb_height); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = ((y * fb_width) + fb_start_offset) & 0x3ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + u32 bitmap_offset = (line_offset + x) << 1; + + uint16_t color = (gvram[bitmap_offset] & 0xff) | (gvram[bitmap_offset + 1] << 8); + + if(cliprect.contains(x, y)) + { + u8 b = pal5bit((color & 0x001f)); + u8 r = pal5bit((color & 0x03e0) >> 5); + u8 g = pal6bit((color & 0xfc00) >> 10); + bitmap.pix(y, x) = (b) | (g << 8) | (r << 16); + } + } + } +} + +void pc88va_state::draw_packed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 fb_start_offset, u32 display_start_offset, u8 pal_base, u16 fb_width, u16 fb_height) +{ + uint8_t *gvram = (uint8_t *)m_gvram.target(); + +// const u16 y_min = std::max(cliprect.min_y, y_start); +// const u16 y_max = std::min(cliprect.max_y, y_min + fb_height); + + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + const u32 line_offset = ((y * (fb_width >> 2)) + fb_start_offset) & 0x0ffff; + + for(int x = cliprect.min_x; x <= cliprect.max_x; x += 8) + { + u16 x_char = (x >> 3); + u32 bitmap_offset = line_offset + x_char; + + for (int xi = 0; xi < 8; xi ++) + { + u8 color = 0; + for (int bank_num = 0; bank_num < 4; bank_num ++) + color |= ((gvram[bitmap_offset + bank_num * 0x10000] >> (7 - xi)) & 1) << bank_num; + + if(color && cliprect.contains(x + xi, y)) + bitmap.pix(y, x + xi) = m_palette->pen(color + pal_base); + } + } + } +} + +/**************************************** + * IDP - NEC μPD72022 + * "Intelligent Display Processor" + ***************************************/ + +/* + * x--- ---- LP Light-Pen signal detection + * -x-- ---- VB Vertical Blank + * --x- ---- SC Sprite control (sprite over/collision) + * ---x ---- ER IDP Error + * \- set when required parameters aren't entered or are malformed, + * or mishandling of command/parameter ports. IDP won't accept any + * further commands and requires an EXIT to be issued to resume operations. + * ---- x--- In the midst of execution of EMEN emulation development + * \- undocumented, PC88VA specific? + * ---- -x-- BUSY set high when IDP is processing commands, + * clear when all commands in FIFO have been completed + * ---- --x- OBF output data buffer full + * \- output FIFO from IDP to host (for commands that implies reading to param port), + * reset on host reading, causes ready pin low when flag is (un?)set. + * ---- ---x IBF input data buffer full + * \- command/parameter FIFO, high when IDP cannot accept any more from host, + * causes ready pin low if any further param write is written (host stalls?). + */ +uint8_t pc88va_state::idp_status_r() +{ + u8 data = 0; + + data |= (m_screen->vblank()) ? 0x40 : 0x00; + + return data; +} + + +#define SYNC 0x10 +#define DSPON 0x12 +#define DSPOFF 0x13 +#define DSPDEF 0x14 +#define CURDEF 0x15 +#define ACTSCR 0x16 +#define CURS 0x1e +#define EMUL 0x8c +#define EXIT 0x88 +#define SPRON 0x82 +#define SPROFF 0x83 +#define SPRSW 0x85 +#define SPROV 0x81 +#define SPWR 0x84 + +void pc88va_state::idp_command_w(uint8_t data) +{ + switch(data) + { + /* 0x10 - SYNC: sets CRTC values */ + case SYNC: m_cmd = SYNC; m_buf_size = 14; m_buf_index = 0; break; + + /* 0x12 - DSPON: set DiSPlay ON and set up tvram table vreg */ + case DSPON: m_cmd = DSPON; m_buf_size = 3; m_buf_index = 0; break; + + /* 0x13 - DSPOFF: set DiSPlay OFF */ + case DSPOFF: m_cmd = DSPOFF; m_tsp.disp_on = false; break; + + /* 0x14 - DSPDEF: set DiSPlay DEFinitions */ + case DSPDEF: m_cmd = DSPDEF; m_buf_size = 6; m_buf_index = 0; break; + + /* 0x15 - CURDEF: set CURsor DEFinition */ + case CURDEF: m_cmd = CURDEF; m_buf_size = 1; m_buf_index = 0; break; + + /* 0x16 - ACTSCR: set ACTive SCReen for CURSor command */ + case ACTSCR: m_cmd = ACTSCR; m_buf_size = 1; m_buf_index = 0; break; + + /* 0x15 - CURS: set CURSor position */ + case CURS: m_cmd = CURS; m_buf_size = 4; m_buf_index = 0; break; + + /* 0x8c - EMUL: set 3301 EMULation (PC88VA specific, undocumented in 72022 specs) */ + case EMUL: m_cmd = EMUL; m_buf_size = 4; m_buf_index = 0; break; + + /* 0x88 - EXIT: aborts current command attribute selection, or clear an ERror state */ + case EXIT: m_cmd = EXIT; break; + + /* 0x82 - SPRON: set SPRite ON */ + case SPRON: m_cmd = SPRON; m_buf_size = 3; m_buf_index = 0; break; + + /* 0x83 - SPROFF: set SPRite OFF */ + case SPROFF: m_cmd = SPROFF; m_tsp.spr_on = false; break; + + /* 0x85 - SPRSW: flip SPRite SW bit */ + case SPRSW: m_cmd = SPRSW; m_buf_size = 1; m_buf_index = 0; break; + + /* 0x81 - SPROV: set SPRite OVerflow information */ + /* + -x-- ---- Sprite Over flag + --x- ---- Sprite Collision flag + ---x xxxx First sprite that caused Sprite Over event + */ + // TODO: data is readback in $146 + case SPROV: m_cmd = SPROV; break; + + /* 0x84 - SPRW: SPRite Write */ + case SPWR: m_cmd = SPWR; m_tsp.spwr_define = true; break; + + // TODO: 0x80 - SPRRD (same as SPWR but on read) + // TODO: 0x1a - LPNR (returns current light pen latch) + // TODO: 0x89 - MASK (ANDs successive VRAM writes for block write commands) + // TODO: 0x8e/0x8f - DPLD (set internal DPTR0/DPTR1 address variables) + // TODO: 0x8a - DPRD (get internal DPTR0 address) + // TODO: 0x90/0x91/0x92/0x93 - RDAT (Read DATa) + // TODO: 0x94/0x95/0x96/0x97 - WDAT (Write DATa) + // TODO: 0x99/0x9a/0x9b - BLKTOT (DMA block output) + // TODO: 0x9d/0x9e/0x9f - BLKTIN (DMA block input) + + default: + m_cmd = 0x00; + LOG("Unemulated IDP %02x cmd set\n", data); + break; + } +} + +void pc88va_state::tsp_sprite_enable(u32 sprite_number, bool sprite_enable, bool blink_enable) +{ + const u32 target_offset = (m_tsp.spr_offset + sprite_number) / 2; + + m_tvram[target_offset] = (m_tvram[target_offset] & ~0x200) | (sprite_enable << 9); + // disable blink if an override of CURN occurs + if (m_tsp.spr_offset == m_tsp.curn) + m_tsp.curn_blink = blink_enable; +} + +/* + * IDP SYNC command + * xx-- ---- [0] RM raster mode + * 00-- ---- \- non-interlace 640 x 400 24kHz + * 01-- ---- \- interlace 640 x 400 15kHz + * 10-- ---- \- vertical magnify 640 x 200 24kHz + * 11-- ---- \- normal 640 x 200 15kHz + * \- PC88VA POST sets either 00 or 11, depending on the monitor setting + * --x- ---- [0] EL Enable Light Pen IRQ + * ---x ---- [0] EV Enable Vertical Blank IRQ + * \- Generated to INT pin line, assume same as uPD3301 where VRTC != INT + * (therefore unconnected in PC88VA) + * ---- x--- [0] VM VRAM Access Mode (0) Random access mode (1) serial access mode + * ---- --xx [0] ILM, DPM Interleave and Dual Port Modes + * ---- --00 \- standalone mode + * ---- --01 \- interleave mode + * ---- --10 \- dual-port mode (VM must be 1) + * ---- --11 \- disabled + * \- Related to comms between host, IDP and VRAM, PC88VA POST sets 01. + * -x-- ---- [1] RF video memory refresh (0) no refresh (1) refresh + * \- enabled by PC88VA POST + * --x- ---- [1] EC external clock (1) output to DTCLK pin + * ---x ---- [1] ES external sync (1) output sync signals to /HSYN and /VSYN pins + * \- enabled by PC88VA POST + * ---- x--- [1] RV reverse screen (1) reverse color in text display + * \- sounds similar to the correlated function in uPD3301 + * ---- -xxx [1] RS Resolution Select, divides dot clock + * ---- -000 \- divide by 4 + * ---- -001 \- divide by 3 + * ---- -010 \- divide by 2 + * ---- -011 \- divide by 1.5 + * ---- -100 \- divide by 1 + * ---- -1xx \- disabled, no dot clock output + * \- NB: PC88VA POST sets 7 + * --xx xxxx [2] - LBL h blank start + * --xx xxxx [3] - LBR h border start + * xxxx xxxx [4] - HAD (h visible area - 1) / 4 + * --xx xxxx [5] - RBR h border end + * --xx xxxx [6] - RBL h blank end + * --xx xxxx [7] - HS h sync + * \- assume all params to be val - 1 / 4 + * --xx xxxx [8] - TBL v blank start + * --xx xxxx [9] - TBR v border start + * xxxx xxxx [A] - VAD (L) v visible area + * -x-- ---- [B] - VAD (H) v visible area (bit 9) + * --xx xxxx [B] - BBR v border end + * --xx xxxx [C] - BBL v blank end + * --xx xxxx [D] - VS v sync + */ +void pc88va_state::execute_sync_cmd() +{ + // olteus will punt loading on PC Engine OS if the vblank bit is completely off + // illcity expects the actual IDP vblank bit to work, from setup menu to opening transition PC=0x418f6 + // upo wants precise vblank bit readouts plus something else (SGP irq?) + + rectangle visarea; + attoseconds_t refresh; + + LOGCRTC("IDP SYNC: "); + + for (int i = 0; i < 15; i++) + LOGCRTC("%02x ", m_buf_ram[i]); + + const u8 h_blank_start = (m_buf_ram[0x02] & 0x3f) + 1; + const u8 h_border_start = (m_buf_ram[0x03] & 0x3f) + 1; + const u16 h_vis_area = (m_buf_ram[0x04] + 1) * 4; + const u8 h_border_end = (m_buf_ram[0x05] & 0x3f) + 1; + const u8 h_blank_end = (m_buf_ram[0x06] & 0x3f) + 1; + const u8 h_sync = (m_buf_ram[0x07] & 0x3f) + 1; + + LOGCRTC("\n\t"); + LOGCRTC("H blank start %d - end %d|", h_blank_start, h_blank_end); + LOGCRTC("H visible area: %d|", h_vis_area); + LOGCRTC("H border start %d - end %d|", h_border_start, h_border_end); + LOGCRTC("H sync: %d", h_sync); + + LOGCRTC("\n\t"); + const u16 h_total = + (h_blank_start + h_blank_end + h_border_start + h_border_end + h_sync) * 4 + h_vis_area; + + LOGCRTC("H Total calc = %d", h_total); + LOGCRTC("\n\t"); + + const u8 v_blank_start = m_buf_ram[0x08] & 0x3f; + const u8 v_border_start = m_buf_ram[0x09] & 0x3f; + const u16 v_vis_area = (m_buf_ram[0x0a]) | ((m_buf_ram[0x0b] & 0x40) << 2); + const u8 v_border_end = m_buf_ram[0x0b] & 0x3f; + const u8 v_blank_end = m_buf_ram[0x0c] & 0x3f; + const u8 v_sync = (m_buf_ram[0x0d] & 0x3f); + + LOGCRTC("V blank start %d - end %d|", v_blank_start, v_blank_end); + LOGCRTC("V visible area: %d|", v_vis_area); + LOGCRTC("V border start: %d - end %d|", v_border_start, v_border_end); + LOGCRTC("V sync: %d", v_sync); + + LOGCRTC("\n\t"); + m_vrtc_irq_line = v_blank_start + v_blank_end + v_vis_area + v_border_start + v_border_end; + const u16 v_total = m_vrtc_irq_line + v_sync; + + LOGCRTC("V Total calc = %d (VRTC %d)\n", v_total, m_vrtc_irq_line); + + // punt with message if values are off (shouldn't happen) + // TODO: more validation: + // HS >= 4 + // LBL >= 3 + // HAD & 1 == 1 + // VS >= 4 + // TBL + TBR >= 16 for non-sprite display, >= 32 for sprite display + // VAD >= 4 + // BBR + BBL >= 2 + if (h_vis_area <= 1 || v_vis_area <= 1 || h_total <= h_vis_area || v_total <= v_vis_area) + { + popmessage("CRTC assertion failed total (%d x %d) visible (%d x %d)", h_total, v_total, h_vis_area, v_vis_area); + return; + } + + visarea.set(0, h_vis_area - 1, 0, v_vis_area - 1); + + // TODO: interlace / vertical magnify, bit 7 + // TODO: actual clock source must be external, assume known PC-88 XTALs, a bit off compared to PC-88 with the values above + const int clock_speed = BIT(m_buf_ram[0x00], 6) ? (31'948'800 / 4) : (28'636'363 / 2); + + refresh = HZ_TO_ATTOSECONDS(clock_speed) * h_vis_area * v_vis_area; + + m_screen->configure(h_total, v_total, visarea, refresh); +} + +/* + * xxxx x??? [0] lower text attribute table offset + * ---- -xxx [1] upper text attribute, unused on PC88VA + * \- Multiplied by 256, lower 3 bits of [0] separated on datasheet (typo?) + * ---- xxxx [2] BC Backdrop Color + */ +void pc88va_state::execute_dspon_cmd() +{ + m_tsp.tvram_vreg_offset = m_buf_ram[0] << 8; + m_tsp.disp_on = true; + LOGIDP("DSPON (%02x %02x %02x) %05x\n" + , m_buf_ram[0] + , m_buf_ram[1] + , m_buf_ram[2] + , m_tsp.tvram_vreg_offset | 0x40000 + ); +} + +/* + * xxxx xxxx [0] ATROFF attribute offset (L) + * xxxx xxxx [1] ^ (M) + * ---- -xxx [2] ^ (H), possibly unused on PC88VA, signed in two's complement (!) + * -xxx ---- [2] PITCH for character code + * x--- ---- [2] (undocumented pitch bit?) + * ---x xxxx [3] MRA Maximum Raster Address, line height + 1 + * \- affects underline, disables layer if setting < 7 + * ---x xxxx [4] HRA Horizontal Raster Address, horizontal position for hline vertical attribute + * ---x xxxx [5] BR Blinking Rate, affects both attribute and cursor in number of frames, max value on 0 + * \- attribute x24 on, x8 off + * \- cursor x8 on, x8 off + */ +void pc88va_state::execute_dspdef_cmd() +{ + m_tsp.attr_offset = m_buf_ram[0] | m_buf_ram[1] << 8; + // m_buf_ram[2] & 1 used on POST (?) + if (m_buf_ram[2] & 0x6) + popmessage("TSP: unimplemented (H) attribute "); + + m_tsp.pitch = (m_buf_ram[2] & 0x70) >> 4; + if (BIT(m_buf_ram[2], 7)) + popmessage("TSP: undocumented PITCH bit set"); + + m_tsp.line_height = (m_buf_ram[3] & 0x1f) + 1; + if (m_tsp.line_height < 8) + popmessage("TSP: line height disable"); + m_tsp.h_line_pos = m_buf_ram[4]; + m_tsp.blink = (m_buf_ram[5] & 0xf8); + if (m_tsp.blink == 0) + m_tsp.blink = 0x100; + LOGIDP("DSPDEF (%02x %02x %02x %02x %02x %02x) %05x ATTR | %02x pitch | %02x line height| %02x hline | %d blink rate\n" + , m_buf_ram[0], m_buf_ram[1], m_buf_ram[2], m_buf_ram[3], m_buf_ram[4], m_buf_ram[5] + , m_tsp.attr_offset | 0x40000 + , m_tsp.pitch + , m_tsp.line_height + , m_tsp.h_line_pos + , m_tsp.blink + ); +} + +/* + * xxxx x--- [0] CURN Sprite Cursor number (sprite RAM entry) + * ---- --x- [0] CE show cursor bit (actively modifies the spriteram entry) + * ---- ---x [0] BE Blink Enable + */ +void pc88va_state::execute_curdef_cmd() +{ + m_tsp.curn = (m_buf_ram[0] & 0xf8); + const bool cursor_enable = bool(BIT(m_buf_ram[0], 1)); + const bool blink_enable = bool(BIT(m_buf_ram[0], 0)); + LOGIDP("CURDEF %02x|%d show|%d blink\n" + , m_buf_ram[0] & 0xf8 + , cursor_enable + , blink_enable + ); + tsp_sprite_enable(m_tsp.curn, cursor_enable, blink_enable); +} + +/* + * -xx- ---- [0] strip layer ID for cursor (attribute?) and light pen + */ +void pc88va_state::execute_actscr_cmd() +{ + const u8 active_screen_area = (m_buf_ram[0] & 0x60) >> 5; + LOGIDP("ACTSCR (%02x) %d\n", m_buf_ram[0], active_screen_area); +} + +/* + * xxxx xxxx [0] Cursor Position Y (lo word) + * ---- -xxx [1] Cursor Position Y (hi word) + * xxxx xxxx [2] Cursor Position X (lo word) + * ---- --xx [3] Cursor Position X (hi word) + */ +void pc88va_state::execute_curs_cmd() +{ + m_tsp.cur_pos_y = m_buf_ram[0] | (m_buf_ram[1] & 0x7) << 8; + m_tsp.cur_pos_x = m_buf_ram[2] | (m_buf_ram[3] & 0x3) << 8; + LOGIDP("CURS X: %d Y: %d\n", m_tsp.cur_pos_x, m_tsp.cur_pos_y); +} + +/* + * [0] Emulate target strip ID x 32 + * [1] The number of chars + * [2] The number of attributes + * [3] The number of lines + */ +void pc88va_state::execute_emul_cmd() +{ + // TODO: PC88VA specific, starts 3301 video emulation + //popmessage("TSP: executed EMUL command"); +} + +/* + * xxxx xxxx [0] SAB Sprite Table Offset (lo word) + * ---- -xxx [1] SAB hi word + * \- again multiplied by 256 + * xxxx x--- [2] HSPN: Maximum number of sprites in one raster (num + 1) for Sprite Over + * ---- -x-- [2] ESP: Enable Sprite Interrupt + * \- causes INT pin high for sprite collision or maximum sprite detected + * ---- --x- [2] MG: all sprites are 2x zoomed vertically when 1 + * ---- ---x [2] GR: 1 to enable the group collision detection + */ +void pc88va_state::execute_spron_cmd() +{ + + m_tsp.spr_offset = m_buf_ram[0] << 8; + m_tsp.spr_on = true; + m_tsp.spr_mg = BIT(m_buf_ram[2], 1); + LOGIDP("SPRON (%02x %02x %02x) %05x offs| %d max sprites| %d MG| %d GR|\n" + , m_buf_ram[0] + , m_buf_ram[1] + , m_buf_ram[2] + , m_tsp.spr_offset + 0x40000 + , (m_buf_ram[2] & 0xf8) + 1 + , m_tsp.spr_mg + , bool(BIT(m_buf_ram[2], 0)) + ); + // TODO: reset sprite status +} + +/* + * Toggle an individual sprite in the sprite ram entry + * xxxx x--- [0] SPN target sprite number + * ---- --x- [0] SPSW sprite off/on switch + */ +void pc88va_state::execute_sprsw_cmd() +{ + const u8 spn = m_buf_ram[0] & 0xf8; + const bool spsw = bool(BIT(m_buf_ram[0], 1)); + LOGIDP("SPRSW (%02x) %08x offset| %s SW\n" + , m_buf_ram[0] + , m_tsp.spr_offset + 0x40000 + , spsw ? "enable" : "disable" + ); + tsp_sprite_enable(spn, spsw, false); +} + +// 88va2d and friends during transitions +/* + * xxxx x--- [0] target SPN + * ---- -xxx [0] target attribute + * Afterwards, data written to the param port will indefinitely write to sprite area + * until command is cancelled, auto-incrementing per byte and eventually moving on to + * the next SPN when attribute overruns past 7. + */ +void pc88va_state::execute_spwr_cmd(u8 data) +{ + if (m_tsp.spwr_define == true) + { + m_tsp.spwr_define = false; + m_tsp.spwr_offset = data; + } + else + { + // TODO: likely going in FIFO full state + // 88va2d delays on status & 0x1f being all off then checks for IBF before every data dispatch. + const u32 target_offset = (m_tsp.spr_offset + m_tsp.spwr_offset) / 2; + + if (m_tsp.spwr_offset & 1) + m_tvram[target_offset] = (m_tvram[target_offset] & 0x00ff) | (data << 8); + else + m_tvram[target_offset] = (m_tvram[target_offset] & 0xff00) | (data & 0xff); + m_tsp.spwr_offset ++; + } +} + +void pc88va_state::idp_param_w(uint8_t data) +{ + if(m_cmd == DSPOFF || m_cmd == EXIT || m_cmd == SPROFF || m_cmd == SPROV) // no param commands + return; + + // special: until a different command is issued just route to SPWR + if (m_cmd == SPWR) + { + execute_spwr_cmd(data); + return; + } + + m_buf_ram[m_buf_index] = data; + m_buf_index++; + + if(m_buf_index >= m_buf_size) + { + m_buf_index = 0; + switch(m_cmd) + { + case SYNC: execute_sync_cmd(); break; + case DSPON: execute_dspon_cmd(); break; + case DSPDEF: execute_dspdef_cmd(); break; + case CURDEF: execute_curdef_cmd(); break; + case ACTSCR: execute_actscr_cmd(); break; + case CURS: execute_curs_cmd(); break; + case EMUL: execute_emul_cmd(); break; + case SPRON: execute_spron_cmd(); break; + case SPRSW: execute_sprsw_cmd(); break; + + default: + //printf("%02x\n",data); + break; + } + } +} + +/**************************************** + * I/O handlers + ***************************************/ + +/* + * $100 + * x--- ---- ---- ---- GDEN0 graphics display enable + * -x-- ---- ---- ---- GVM superimpose if 1? + * --x- ---- ---- ---- XVSP video signal output mode (0) inhibit scan signals + * ---x ---- ---- ---- SYNCEN horizontal sync output (0) inhibit hsync + * TODO: confirm following two + * ---- x--- ---- ---- YMMD GVRAM mode (0) screen 0 only (1) screens 0 and 1 + * ---- -x-- ---- ---- DM gfx display mode (0) multiplane (1) single plane -> cfr. $153 GMSP + * ---- ---- xx-- ---- RSM CRT raster scan mode + * ---- ---- 0x-- ---- Non-interlace mode 0/1 + * ---- ---- 1x-- ---- Interlace mode 0/1 + * ---- ---- --x- ---- GDEN1 gfx display circuit reset (0) reset + * ---- ---- ---x ---- SYNCM sync signal mode (0) internal (1) external + * ---- ---- ---- --xx VW vertical resolution + * ---- ---- ---- --00 400 lines + * ---- ---- ---- --01 408 lines + * ---- ---- ---- --10 200 lines + * ---- ---- ---- --11 204 lines + */ +void pc88va_state::screen_ctrl_w(offs_t offset, u16 data, u16 mem_mask) +{ + COMBINE_DATA(&m_screen_ctrl_reg); + + m_ymmd = bool(BIT(m_screen_ctrl_reg, 11)); + m_dm = bool(BIT(m_screen_ctrl_reg, 10)); + // YMMD DM + // mightmag 0xb060 (0) screen 0 (0) multiplane +} + +u16 pc88va_state::screen_ctrl_r() +{ + return m_screen_ctrl_reg; +} + +/* + * $102 + * ---x --xx ---- ---- screen 1 regs + * ---x ---- ---- ---- HW1 screen 1 hres (0) 640 dots (1) 320 + * ---- --xx ---- ---- PM1 screen 1 pixel mode + * ---- --00 ---- ---- 1bpp + * ---- --01 ---- ---- 4bpp + * ---- --10 ---- ---- 8bpp + * ---- --11 ---- ---- RGB565 + * ---- ---- ---x ---- HW0 screen 0 hres, as above + * ---- ---- ---- --xx PM0 screen 0 pixel mode, as above + */ +void pc88va_state::gfx_ctrl_w(offs_t offset, u16 data, u16 mem_mask) +{ + COMBINE_DATA(&m_gfx_ctrl_reg); +} + +// upo wants a readback otherwise no layer appears on title/gameplay +u16 pc88va_state::gfx_ctrl_r() +{ + return m_gfx_ctrl_reg; +} + +/* + * $10c + * --xx ---- ---- ---- BDM1/BDM0 color backdrop mode # + * --00 ---- ---- ---- inner background color, outer transparent + * --01 ---- ---- ---- inner transparent, outer background + * --10 ---- ---- ---- inside/outside background color + * --11 ---- ---- ---- inside/outside transparent + * \- mode 0 only available on 24.8KHz monitor + * ---- ---x xx-- ---- PLTM2/PLTM1/PLTM0 color palette mode + * ---- ---0 xx-- ---- + * ---- ---1 00-- ---- use palette bank 0 + * ---- ---1 01-- ---- use palette bank 1 + * ---- ---1 10-- ---- mixed mode + * ---- ---1 11-- ---- combined 32-color mode + * ---- ---- --xx ---- PLTP1/PLTP0 layer select for PLTM mixed mode + * ---- ---- --00 ---- text layer + * ---- ---- --01 ---- sprites + * ---- ---- --10 ---- graphic 0 + * ---- ---- --11 ---- graphic 1 + * ---- ---- ---- xx-- BLKM1/BLKM0 color blink rate + * ---- ---- ---- 00-- blink off + * ---- ---- ---- 01-- blink every 32 frames + * ---- ---- ---- 10-- blink every 64 frames + * ---- ---- ---- 11-- blink every 128 frames + * ---- ---- ---- --xx BLKD blink duty + * ---- ---- ---- --00 12.5% + * ---- ---- ---- --01 25% + * ---- ---- ---- --10 50% + * ---- ---- ---- --11 75% + */ +void pc88va_state::color_mode_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_color_mode); + + const u8 bdm = (m_color_mode & 0x3000) >> 12; + m_pltm = (m_color_mode & 0x01c0) >> 6; + m_pltp = (m_color_mode & 0x0030) >> 4; + const u8 blkm = (m_color_mode & 0x000c) >> 2; + const u8 blkd = (m_color_mode & 0x0003) >> 0; + LOGCOLOR("Color Mode (%04x & %04x)|%02x BDM|%s PLTM2|%02x PLTM1/PLTM0|%02x PLTP|%02x BLKM|%02x BLKD\n" + , m_color_mode, mem_mask + , bdm + , m_pltm & 4 ? "V3 mode" : "V1/V2 mode" + , m_pltm & 3 + , m_pltp + , blkm + , blkd + ); + // PLTM - PLTP + // rtype, shinraba: + // 0x02 (mixed) 0x03 graphics 1 + // micromus, famista, shanghai, ballbrkr: + // 0x02 (mixed) 0x02 graphics 0 + // olteus: + // 0x02 (mixed) 0x01 sprites + // mightmag: + // 0x02 (mixed) 0x00 text + // animefrm: + // 0x03 (combined) 0x00 text + // boomer (gameplay): + // 0x01 (bank 1) 0x02 graphic 0 (left on for previous 0x02 - 0x02 mode) + // illcity, xak2 (pre-loading screens): + // 0x00 (bank 0) 0x00 text + +} + +void pc88va_state::palette_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_palram[offset]); + + const u16 color = m_palram[offset]; + u8 b = pal5bit((color & 0x001f)); + u8 r = pal5bit((color & 0x03e0) >> 5); + u8 g = pal6bit((color & 0xfc00) >> 10); + + // TODO: docs suggests this arrangement but it's wrong + // may be just one bit always on? +// b = (m_palram[offset] & 0x001e) >> 1; +// r = (m_palram[offset] & 0x03c0) >> 6; +// g = (m_palram[offset] & 0x7800) >> 11; + + m_palette->set_pen_color(offset, r, g, b); +} + +void pc88va_state::video_pri_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_video_pri_reg[offset]); +} + +void pc88va_state::text_transpen_w(offs_t offset, u16 data, u16 mem_mask) +{ + // TODO: understand what these are for, docs blabbers about text/sprite color separation? + // cfr. rogueall, olteus on disk swap screen + // shanghai on winning animation (flips 0xf801 / 0x07c1, intentional?) + COMBINE_DATA(&m_text_transpen); + if (m_text_transpen & 0xfff0) + popmessage("text transpen > 15 (%04x)", m_text_transpen); +} + +/* + * $14c-$14f Kanji CG ports + * Alt method for access kanji ROM for drawing to graphic layers + */ +u8 pc88va_state::kanji_cg_r() +{ + uint8_t *kanji_rom = (uint8_t *)m_kanji_rom.target(); + + // ANK + if (m_kanji_cg_jis[1] == 0) + return kanji_rom[0x40000 + (m_kanji_cg_jis[0] * 0x10) + (m_kanji_cg_line)]; + + // PCG + if (m_kanji_cg_jis[0] == 0x56) + { + // jis2 = 0x21 / 0x22 "PC" on hovered top status bar for animefrm + // NB: software reverts the two chars once it gets upped to bitmap layer. + const u32 pcg_addr = ((m_kanji_cg_jis[1] & 0x1f) + ((m_kanji_cg_jis[1] & 0x60) << 1)) * 0x20; + return m_kanjiram[pcg_addr + (m_kanji_cg_line << 1) + (m_kanji_cg_lr ^ 1)]; + } + + const u32 kanji_address = calc_kanji_rom_addr(m_kanji_cg_jis[0] + 0x20, m_kanji_cg_jis[1], 0, 0); + + return kanji_rom[kanji_address + (m_kanji_cg_line << 1) + (m_kanji_cg_lr ^ 1)]; +} + +void pc88va_state::kanji_cg_raster_w(u8 data) +{ + m_kanji_cg_line = data & 0xf; + m_kanji_cg_lr = BIT(data, 5); +} + +void pc88va_state::kanji_cg_address_w(offs_t offset, u8 data) +{ + m_kanji_cg_jis[offset] = data; +} + +/* + * $148 Text Control 1 + * x--- ---- TD Text Disable (1) + * -xxx ---- VALT2/VALT1/VALT0 TVRAM access restriction (?) + * -000 ---- No limit + * -??? ---- value x4 + * ---- x--- ATM text attribute mode (1) V3 Mode (0) V1/V2 + * ---- -x-- ANKM character font mode (1) 16 (0) 8 + * ---- --x- IDP memory mode (1) word mode + * ---- ---1 + */ +void pc88va_state::text_control_1_w(u8 data) +{ + m_td = bool(BIT(data, 7)); + + if ((data & 0x7d) != 1) + LOG("I/O $148 write %02x\n", data); +}