nec/pc88va.cpp: implement GVRAM single/multiplane ROP registers

This commit is contained in:
angelosa 2025-03-04 21:12:55 +01:00
parent c6897b843d
commit c4092afdfa
4 changed files with 424 additions and 89 deletions

View File

@ -82,7 +82,7 @@ Operating Systems
-->
<!-- Demos and Utilities -->
<!-- !Demos -->
<!-- These three demos all have the same sequences, mislabeled? -->
<software name="pc88vad" cloneof="88va2d" supported="no">
@ -140,6 +140,8 @@ Transition between 3rd and 4th sequences don't look right, [IDP]
</part>
</software>
<!-- !Applications -->
<software name="animefrm" supported="no">
<!-- PS88-VA101-HMW subtitle on cover -->
<description>Anime Framer (v1.0)</description>
@ -204,6 +206,8 @@ Has unsupported MIF-201 [MIDI] interface cfr. http://www.pc88.gr.jp/vafaq/view.p
</part>
</software>
<!-- !Operating Systems -->
<software name="pceva2tb" supported="no">
<description>PC-Engine (VA2 Tenpu-ban)</description>
<year>1988</year>
@ -239,7 +243,7 @@ Untested directory NEC_SAMP (cannot type [keyboard] underscore char)
</software>
<!-- Games -->
<!-- !Games -->
<software name="famista" supported="no">
<description>Family Stadium</description>
@ -345,7 +349,7 @@ Winning newspaper screen has [OFX/OFY] bug
<year>198?</year>
<publisher>スタークラフト (Starcraft)</publisher>
<notes><![CDATA[
Main menu clearing artifacts [ROP]
Currently conceals/wipes out [GVRAM] display
]]></notes>
<info name="alt_title" value="マイトアンドマジック" />
<info name="usage" value="Boot with Disk C in drive 1"/>
@ -520,6 +524,7 @@ Needs graphic [OFX/OFY] scroll
<year>198?</year>
<publisher>スタークラフト (Starcraft)</publisher>
<notes><![CDATA[
Currently conceals/wipes out [GVRAM] display
Burps on [FDC] access after disk swap with program and player disks, trying to scan chrn=(38, 0, 1, 256)
]]></notes>
<info name="alt_title" value="ローグアライアンス" />
@ -876,18 +881,21 @@ Optionally wants kana lock enabled for entering a name for new game [keyboard] i
<!-- status: baddump for non-factory save 1 -->
<part name="flop1" interface="floppy_5_25">
<feature name="part_id" value="Disk 1" />
<dataarea name="flop" size="1281968">
<rom name="xak 2 (disk 1).d88" size="1281968" crc="cd855b7a" sha1="508e25ca306631be64e69e476d49116e7ac04bce" />
</dataarea>
</part>
<part name="flop2" interface="floppy_5_25">
<feature name="part_id" value="Disk 2" />
<dataarea name="flop" size="1281968">
<rom name="xak 2 (disk 2).d88" size="1281968" crc="3927cdcf" sha1="555daba18a61ea2219b5685a850b512dea085cf2" status="baddump"/>
</dataarea>
</part>
<part name="flop3" interface="floppy_5_25">
<feature name="part_id" value="Disk 3" />
<dataarea name="flop" size="1281968">
<rom name="xak 2 (disk 3).d88" size="1281968" crc="42e25779" sha1="897b62c7fdb604f5bb729e0aeb183566e08c4e33" status="baddump"/>
</dataarea>
@ -899,7 +907,7 @@ Optionally wants kana lock enabled for entering a name for new game [keyboard] i
<year>1991</year>
<publisher>マイクロキャビン (Micro Cabin)</publisher>
<notes><![CDATA[
Heavy [ROP] artifacts on intro
Heavy [ROP] artifacts on intro, runs incredibly slow
Sprites don't draw properly during gameplay [ROP]
Ugly pitch for [OPNA] voice samples on intro
[gfx]s have halved height
@ -909,24 +917,28 @@ Ugly pitch for [OPNA] voice samples on intro
<info name="usage" value="Boot with disk 2 in drive A: for gameplay"/>
<part name="flop1" interface="floppy_5_25">
<feature name="part_id" value="Disk 1" />
<dataarea name="flop" size="1261568">
<rom name="fray - in magical adventure (disk 1).fdi" size="1261568" crc="3a88990a" sha1="252e2589bb8c12800cdd678508b0abd868d0ab83"/>
</dataarea>
</part>
<part name="flop2" interface="floppy_5_25">
<feature name="part_id" value="Disk 2" />
<dataarea name="flop" size="1261568">
<rom name="fray - in magical adventure (disk 2).fdi" size="1261568" crc="9cbfb7b4" sha1="b040e4ed4e3e516effb1f6997b0f766f4b376ab4"/>
</dataarea>
</part>
<part name="flop3" interface="floppy_5_25">
<feature name="part_id" value="Disk 3" />
<dataarea name="flop" size="1261568">
<rom name="fray - in magical adventure (disk 3).fdi" size="1261568" crc="9f79f4bb" sha1="2d3ebbaa844bd451190d899a7f53829b07b830eb"/>
</dataarea>
</part>
<part name="flop4" interface="floppy_5_25">
<feature name="part_id" value="Disk 4" />
<dataarea name="flop" size="1261568">
<rom name="fray - in magical adventure (disk 4).fdi" size="1261568" crc="c074800a" sha1="bf114221bdcc8e6545c4566ae58cea1af324aa9a"/>
</dataarea>
@ -1229,7 +1241,7 @@ All disks fail initial bootstrap [FDC]
</software>
<!-- Doujin -->
<!-- !Doujinshi -->
<software name="ballbrkr" supported="no">
<description>Balloon Breaker</description>
@ -1334,7 +1346,7 @@ Gameplay don't mask bullets on right side, [cliprect]
[SGP] and [ROP] issues on gameplay
]]></notes>
<info name="developer" value="Shinra" />
<info name="usage" value="Boot a PC Engine OS disk, then swap with this disk and type BASIC PACMAN.BAS to load" />
<info name="usage" value="Boot a PC-Engine OS disk, then swap with this disk and type BASIC PACMAN.BAS to load" />
<part name="flop1" interface="floppy_5_25">
<dataarea name="flop" size="1331888">
<rom name="pacman.d88" size="1331888" crc="9198eae1" sha1="2abf462b72c29e2ff56ea7c1401836f01b97dfe1"/>
@ -1342,7 +1354,7 @@ Gameplay don't mask bullets on right side, [cliprect]
</part>
</software>
<!-- autobootable version of above, with PC Engine OS embedded -->
<!-- autobootable version of above, with PC-Engine OS embedded -->
<software name="pacmana" cloneof="pacman" supported="no">
<description>Pac-Man (auto-bootable)</description>
<year>19??</year>

View File

@ -9,44 +9,44 @@ Here be dragons, a mostly compatible PC-8801 with extra V3 Mode for superset.
TODO:
- 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?
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.
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.
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 ...)
\- 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?
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;
\- 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?
$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 C-Bus device, apparently it's same i/f;
- Is C-Bus I/O space shifted by +$200, as per micromus MIDI access at $e2d2?
(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
hooked up by the current z80 core
- 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.
Has four roms, marked by VAEG as VUROM00.ROM, VUROM08.ROM, VUROM1.ROM, VUDIC.ROM.
References:
- PC-88VAテクニカルマニュアル
@ -89,16 +89,18 @@ brk 8Ch AH=02h read calendar clock -> CH = hour, CL = minutes, DH = seconds, DL
#include <iostream>
#include "utf8.h"
#define LOG_FDC (1U << 2) // $1b0-$1b2 accesses
#define LOG_FDC2 (1U << 3) // $1b4-$1b6 accesses (verbose)
#define LOG_FDC (1U << 2) // $1b0-$1b2 accesses
#define LOG_FDC2 (1U << 3) // $1b4-$1b6 accesses (verbose)
#define LOG_GFXCTRL (1U << 4) // $5xx accesses
#define VERBOSE (LOG_GENERAL | LOG_FDC)
#define VERBOSE (LOG_GENERAL | LOG_FDC | LOG_GFXCTRL)
//#define LOG_OUTPUT_STREAM std::cout
#include "logmacro.h"
#define LOGFDC(...) LOGMASKED(LOG_FDC, __VA_ARGS__)
#define LOGFDC2(...) LOGMASKED(LOG_FDC2, __VA_ARGS__)
#define LOGGFXCTRL(...) LOGMASKED(LOG_GFXCTRL, __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)
@ -108,13 +110,13 @@ brk 8Ch AH=02h read calendar clock -> CH = hour, CL = minutes, DH = seconds, DL
uint8_t pc88va_state::kanji_ram_r(offs_t offset)
{
return m_kanjiram[offset];
return m_kanji_ram[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)
{
m_kanjiram[offset] = data;
m_kanji_ram[offset] = data;
m_gfxdecode->gfx(2)->mark_dirty(offset / 8);
m_gfxdecode->gfx(3)->mark_dirty(offset / 32);
}
@ -185,6 +187,7 @@ void pc88va_state::rtc_w(offs_t offset, u8 data)
void pc88va_state::bios_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
COMBINE_DATA(&m_bank_reg);
m_gmsp_view.select(BIT(m_bank_reg, 12));
/* SMBC */
m_sysbank->set_bank((m_bank_reg & 0xf00) >> 8);
@ -510,9 +513,9 @@ TIMER_CALLBACK_MEMBER(pc88va_state::t3_mouse_callback)
uint8_t pc88va_state::backupram_dsw_r(offs_t offset)
{
if(offset == 0)
return m_kanjiram[0x1fc2 / 2] & 0xff;
return m_kanji_ram[0x1fc2 / 2] & 0xff;
return m_kanjiram[0x1fc6 / 2] & 0xff;
return m_kanji_ram[0x1fc6 / 2] & 0xff;
}
// TODO: pc8801_state::port31_w
@ -552,13 +555,15 @@ void pc88va_state::main_map(address_map &map)
void pc88va_state::sysbank_map(address_map &map)
{
// 0 select bus slot
// 0 select C-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");
map(0x100000, 0x13ffff).view(m_gmsp_view);
m_gmsp_view[0](0x100000, 0x13ffff).rw(FUNC(pc88va_state::gvram_multiplane_r), FUNC(pc88va_state::gvram_multiplane_w));
m_gmsp_view[1](0x100000, 0x13ffff).rw(FUNC(pc88va_state::gvram_singleplane_r), FUNC(pc88va_state::gvram_singleplane_w));
// 8-9 kanji
// Kanji ROM
map(0x200000, 0x23ffff).rom().region("kanji", 0x00000);
@ -580,7 +585,11 @@ void pc88va_state::sgp_map(address_map &map)
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");
// Assume just raw writes to GVRAM
map(0x200000, 0x23ffff).lrw8(
NAME([this] (offs_t offset) { return m_gvram[offset]; }),
NAME([this] (offs_t offset, u8 data) { m_gvram[offset] = data; })
);
}
// TODO: I/O 0x00xx is almost same as pc8801
@ -661,22 +670,155 @@ void pc88va_state::io_map(address_map &map)
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
// TODO: register are locked with GMSP = 1
map(0x0510, 0x0510).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("AACC extend access mode R\n");
return m_multiplane.aacc;
}),
NAME([this] (offs_t offset, u8 data) {
m_multiplane.aacc = !!BIT(data, 0);
LOGGFXCTRL("AACC extend access mode W %02x\n", data);
})
);
map(0x0512, 0x0512).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("GMAP block switch R\n");
return m_multiplane.gmap;
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("GMAP block switch W %02x\n", data);
m_multiplane.gmap = !!BIT(data, 0);
})
);
map(0x0514, 0x0514).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("XRPMn plane readback select R\n");
return m_multiplane.xrpm | 0xf0;
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("XRPMn plane readback select W %02x\n", data);
m_multiplane.xrpm = data & 0xf;
})
);
map(0x0516, 0x0516).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("XWPMn plane write select R\n");
return m_multiplane.xwpm | 0xf0;
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("XWPMn plane write select W %02x\n", data);
m_multiplane.xwpm = data & 0xf;
})
);
map(0x0518, 0x0518).lrw8(
NAME([this] (offs_t offset) {
// TODO: rbusy reads (bit 7)
return (m_multiplane.cmpen << 5) | (m_multiplane.wss << 3) | (m_multiplane.pmod << 0);
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Multiplane Mode W %02x\n", data);
// PMOD bit 2 1 -> 0 transitions resets pattern pointers
if (BIT(m_multiplane.pmod, 2) && !BIT(data, 2))
{
m_multiplane.prrp = 0;
m_multiplane.prwp = 0;
}
m_multiplane.cmpen = !!BIT(data, 5);
m_multiplane.wss = (data >> 3) & 3;
m_multiplane.pmod = (data >> 0) & 7;
})
);
map(0x0520, 0x0527).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("CMPR extended access bit comparison R\n");
return m_multiplane.cmpr[offset];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("CMPR extended access bit comparison W %02x\n", data);
m_multiplane.cmpr[offset] = data;
})
);
// 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
map(0x0530, 0x0537).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("Multiplane PATRL%d R\n", offset);
return m_multiplane.patr[offset][0];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Multiplane PATRL%d W %02x\n", offset, data);
m_multiplane.patr[offset][0] = data;
})
);
map(0x0540, 0x0547).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("PATRH%d R\n", offset);
return m_multiplane.patr[offset][1];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("PATRH%d W %02x\n", offset, data);
m_multiplane.patr[offset][1] = data;
})
);
map(0x0550, 0x0550).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
return m_multiplane.prrp | 0xf0;
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("PRRPn plane pattern usage start byte on read %02x\n", data);
m_multiplane.prrp = data & 0xf;
})
);
map(0x0552, 0x0552).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
return m_multiplane.prwp | 0xf0;
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("PRWPn plane pattern usage start byte on write %02x\n", data);
m_multiplane.prwp = data & 0xf;
})
);
map(0x0560, 0x0567).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
return m_multiplane.rop[offset];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Multiplane ROP %d W %02x\n", offset, data);
m_multiplane.rop[offset] = data;
})
);
// GVRAM single plane access regs
// map(0x0580, 0x0580) single plane enable
// map(0x0590, 0x0593) GVRAM pattern register settings
// map(0x05a0, 0x05a3) ROP plane code
// TODO: register are locked with GMSP = 0
map(0x0580, 0x0580).lrw8(
NAME([this] (offs_t offset) {
// TODO: rbusy reads (bit 7)
return (m_singleplane.wss << 3);
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Singleplane Mode W %02x\n", data);
m_singleplane.wss = (data >> 3) & 3;
})
);
map(0x0590, 0x0593).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
LOGGFXCTRL("Singleplane PATRL%d R\n", offset);
return m_singleplane.patr[offset];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Singleplane PATRL%d W %02x\n", offset, data);
m_singleplane.patr[offset] = data;
})
);
map(0x05a0, 0x05a3).umask16(0x00ff).lrw8(
NAME([this] (offs_t offset) {
return m_singleplane.rop[offset];
}),
NAME([this] (offs_t offset, u8 data) {
LOGGFXCTRL("Singleplane ROP %d W %02x\n", offset, data);
m_singleplane.rop[offset] = data;
})
);
// map(0x1000, 0xfeff) PC-88VA expansion boards
// map(0xe2d2, 0xe2d2) MIDI status in micromus

View File

@ -66,10 +66,10 @@ public:
, 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_gmsp_view(*this, "gmsp_view")
, m_kanji_rom(*this, "kanji")
, m_gfxdecode(*this, "gfxdecode")
, m_palette(*this, "palette")
{ }
@ -107,6 +107,7 @@ protected:
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
virtual void video_start() override ATTR_COLD;
virtual void video_reset() override ATTR_COLD;
void palette_init(palette_device &palette) const;
protected:
@ -131,11 +132,12 @@ private:
required_device<address_map_bank_device> m_sysbank;
required_shared_ptr<uint16_t> m_workram;
required_shared_ptr<uint16_t> m_tvram;
required_shared_ptr<uint16_t> m_gvram;
std::unique_ptr<uint8_t[]> m_gvram;
required_shared_ptr<uint16_t> m_fb_regs;
required_region_ptr<u16> m_kanji_rom;
required_device<pc88va_sgp_device> m_sgp;
std::unique_ptr<uint8_t[]> m_kanjiram;
memory_view m_gmsp_view;
required_region_ptr<u16> m_kanji_rom;
std::unique_ptr<uint8_t[]> m_kanji_ram;
uint16_t m_bank_reg = 0;
uint8_t m_timer3_io_reg = 0;
@ -191,20 +193,48 @@ private:
void r232_ctrl_portc_w(uint8_t data);
uint8_t get_slave_ack(offs_t offset);
uint16_t m_video_pri_reg[2]{};
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_screen_ctrl_reg;
bool m_dm;
bool m_ymmd;
u16 m_gfx_ctrl_reg;
u16 m_color_mode = 0;
u8 m_pltm, m_pltp = 0;
u16 m_color_mode;
u8 m_pltm, m_pltp;
u16 m_text_transpen = 0;
bool m_td = false;
u16 m_text_transpen;
bool m_td;
bitmap_rgb32 m_graphic_bitmap[2];
struct {
bool aacc;
u8 gmap;
u8 xrpm, xwpm;
//bool rbusy;
bool cmpen;
u8 wss;
u8 pmod;
u8 rop[4];
u8 cmpr[4];
u8 patr[4][2];
u8 prrp, prwp;
} m_multiplane;
struct {
//bool rbusy;
u8 wss;
u8 patr[2];
u8 rop[2];
} m_singleplane;
u8 rop_execute(u8 plane_rop, u8 src, u8 dst, u8 pat);
u8 gvram_singleplane_r(offs_t offset);
void gvram_singleplane_w(offs_t offset, u8 data);
u8 gvram_multiplane_r(offs_t offset);
void gvram_multiplane_w(offs_t offset, u8 data);
u16 screen_ctrl_r();
void screen_ctrl_w(offs_t offset, u16 data, u16 mem_mask = ~0);
u16 gfx_ctrl_r();
@ -272,11 +302,11 @@ private:
void sgp_map(address_map &map) ATTR_COLD;
// TODO: stuff backported from PC8801 as QoL that should really be common
protected:
required_device<gfxdecode_device> m_gfxdecode;
required_device<palette_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);

View File

@ -28,10 +28,14 @@
void pc88va_state::video_start()
{
const u32 gvram_size = 0x40000;
m_gvram = std::make_unique<uint8_t[]>(gvram_size);
std::fill_n(m_gvram.get(), gvram_size, 0);
const u32 kanjiram_size = 0x4000;
m_kanjiram = std::make_unique<uint8_t[]>(kanjiram_size);
m_gfxdecode->gfx(2)->set_source(m_kanjiram.get());
m_gfxdecode->gfx(3)->set_source(m_kanjiram.get());
m_kanji_ram = std::make_unique<uint8_t[]>(kanjiram_size);
m_gfxdecode->gfx(2)->set_source(m_kanji_ram.get());
m_gfxdecode->gfx(3)->set_source(m_kanji_ram.get());
m_vrtc_irq_line = 432;
for (int i = 0; i < 2; i++)
@ -45,11 +49,17 @@ void pc88va_state::video_start()
save_item(NAME(m_text_transpen));
save_pointer(NAME(m_video_pri_reg), 2);
save_pointer(NAME(m_kanjiram), kanjiram_size);
save_pointer(NAME(m_gvram), gvram_size);
save_pointer(NAME(m_kanji_ram), kanjiram_size);
save_item(NAME(m_vrtc_irq_line));
}
void pc88va_state::video_reset()
{
m_text_transpen = 0;
}
void pc88va_state::palette_init(palette_device &palette) const
{
// default palette
@ -69,7 +79,6 @@ void pc88va_state::palette_init(palette_device &palette) const
}
}
uint32_t pc88va_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
uint8_t pri, cur_pri_lv;
@ -607,7 +616,7 @@ void pc88va_state::draw_text(bitmap_rgb32 &bitmap, const rectangle &cliprect)
if(!split_cliprect.contains(res_x, res_y))
continue;
int pen = m_kanjiram[(( yi * 2 ) + lr_half_gfx) + tile_num] >> (7 - xi) & 1;
int pen = m_kanji_ram[(( yi * 2 ) + lr_half_gfx) + tile_num] >> (7 - xi) & 1;
if(reverse)
pen = pen & 1 ? bg_col : fg_col;
@ -795,8 +804,6 @@ void pc88va_state::draw_graphic_layer(bitmap_rgb32 &bitmap, const rectangle &cli
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;
@ -808,7 +815,7 @@ void pc88va_state::draw_indexed_gfx_1bpp(bitmap_rgb32 &bitmap, const rectangle &
for (int xi = 0; xi < 8; xi ++)
{
uint32_t color = (gvram[bitmap_offset] >> (7 - xi)) & 1;
uint32_t color = (m_gvram[bitmap_offset] >> (7 - xi)) & 1;
int res_x = x + xi;
if(color && cliprect.contains(res_x, y))
@ -820,8 +827,6 @@ void pc88va_state::draw_indexed_gfx_1bpp(bitmap_rgb32 &bitmap, const rectangle &
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);
@ -838,7 +843,7 @@ void pc88va_state::draw_indexed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &
for (int xi = 0; xi < 2; xi ++)
{
u8 color = (gvram[bitmap_offset] >> (xi ? 0 : 4)) & 0xf;
u8 color = (m_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);
@ -849,8 +854,6 @@ void pc88va_state::draw_indexed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &
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);
@ -864,7 +867,7 @@ void pc88va_state::draw_packed_gfx_5bpp(bitmap_rgb32 &bitmap, const rectangle &c
{
u32 bitmap_offset = line_offset + x;
u8 color = gvram[bitmap_offset] & 0x1f;
u8 color = m_gvram[bitmap_offset] & 0x1f;
if(color && cliprect.contains(x, y))
bitmap.pix(y, x) = m_palette->pen(color);
@ -874,8 +877,6 @@ void pc88va_state::draw_packed_gfx_5bpp(bitmap_rgb32 &bitmap, const rectangle &c
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);
@ -887,7 +888,7 @@ void pc88va_state::draw_direct_gfx_8bpp(bitmap_rgb32 &bitmap, const rectangle &c
{
u32 bitmap_offset = line_offset + x;
uint32_t color = (gvram[bitmap_offset] & 0xff);
uint32_t color = (m_gvram[bitmap_offset] & 0xff);
// boomer suggests that transparency is calculated over just color = 0, may be settable?
// TODO: may not be clamped to palNbit
@ -904,8 +905,6 @@ void pc88va_state::draw_direct_gfx_8bpp(bitmap_rgb32 &bitmap, const rectangle &c
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);
@ -917,7 +916,7 @@ void pc88va_state::draw_direct_gfx_rgb565(bitmap_rgb32 &bitmap, const rectangle
{
u32 bitmap_offset = (line_offset + x) << 1;
uint16_t color = (gvram[bitmap_offset] & 0xff) | (gvram[bitmap_offset + 1] << 8);
uint16_t color = (m_gvram[bitmap_offset] & 0xff) | (m_gvram[bitmap_offset + 1] << 8);
if(cliprect.contains(x, y))
{
@ -932,8 +931,6 @@ void pc88va_state::draw_direct_gfx_rgb565(bitmap_rgb32 &bitmap, const rectangle
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);
@ -950,7 +947,7 @@ void pc88va_state::draw_packed_gfx_4bpp(bitmap_rgb32 &bitmap, const rectangle &c
{
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;
color |= ((m_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);
@ -1615,7 +1612,7 @@ u8 pc88va_state::kanji_cg_r()
// 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)];
return m_kanji_ram[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);
@ -1652,3 +1649,157 @@ void pc88va_state::text_control_1_w(u8 data)
if ((data & 0x7d) != 1)
LOG("I/O $148 write %02x\n", data);
}
/****************************************
* GVRAM
***************************************/
u8 pc88va_state::rop_execute(u8 plane_rop, u8 src, u8 dst, u8 pat)
{
u8 res = 0;
for (int i = 0; i < 8; i++)
{
if (BIT(plane_rop, i))
{
u8 src_data = BIT(i, 0) ? src : ~src;
u8 dst_data = BIT(i, 1) ? dst : ~dst;
u8 pat_data = BIT(i, 2) ? pat : ~pat;
res |= src_data & dst_data & pat_data;
}
}
return res;
}
u8 pc88va_state::gvram_multiplane_r(offs_t offset)
{
if (m_multiplane.aacc)
{
u32 address = (offset & 0x7fff) | (m_multiplane.gmap << 15);
u8 res = 0xff;
for (int plane = 0; plane < 4; plane++)
{
if (!BIT(m_multiplane.xrpm, plane))
{
const u8 src = m_gvram[address | plane * 0x10000];
// Comparison enable
if (m_multiplane.cmpen)
res &= ~(src ^ m_multiplane.cmpr[plane]);
else
res &= src;
// update on reads
if (BIT(m_multiplane.pmod, 0) && !machine().side_effects_disabled())
{
m_multiplane.patr[plane][BIT(m_multiplane.prwp, plane)] = src;
}
}
}
// flip register indices on 16-bit mode
if ((m_multiplane.pmod & 5) == 5 && !machine().side_effects_disabled())
{
m_multiplane.prwp ^= 0xf;
}
return res;
}
return gvram_singleplane_r(offset);
}
void pc88va_state::gvram_multiplane_w(offs_t offset, u8 data)
{
if (m_multiplane.aacc)
{
u32 address = (offset & 0x7fff) | (m_multiplane.gmap << 15);
for (int plane = 0; plane < 4; plane++)
{
if (!BIT(m_multiplane.xwpm, plane))
{
switch(m_multiplane.wss & 3)
{
// ROP
case 0:
{
const u8 src = m_gvram[address | plane * 0x10000];
m_gvram[address | plane * 0x10000] = rop_execute(
m_multiplane.rop[plane],
src,
data,
m_multiplane.patr[plane][BIT(m_multiplane.prrp, plane)]
);
// update pattern on writes
if (BIT(m_multiplane.pmod, 1))
{
m_multiplane.patr[plane][BIT(m_multiplane.prwp, plane)] = src;
}
break;
}
// Pattern
case 1:
m_gvram[address | plane * 0x10000] = m_multiplane.patr[plane][BIT(m_multiplane.prrp, plane)];
break;
// Normal writes
case 2:
m_gvram[address | plane * 0x10000] = data;
break;
// NOP
case 3:
break;
}
}
}
// flip register indices on 16-bit mode
if (BIT(m_multiplane.pmod, 2))
{
m_multiplane.prrp ^= 0xf;
if (BIT(m_multiplane.pmod, 1))
m_multiplane.prwp ^= 0xf;
}
return;
}
gvram_singleplane_w(offset, data);
}
u8 pc88va_state::gvram_singleplane_r(offs_t offset)
{
// apparently no side effects on reads
return m_gvram[offset];
}
void pc88va_state::gvram_singleplane_w(offs_t offset, u8 data)
{
const u8 page_bank = BIT(offset, 17);
switch(m_singleplane.wss & 3)
{
// ROP
case 0:
{
const u8 src = m_gvram[offset];
m_gvram[offset] = rop_execute(
m_singleplane.rop[page_bank],
src,
data,
m_singleplane.patr[page_bank]
);
break;
}
// Pattern
case 1:
m_gvram[offset] = m_singleplane.patr[page_bank];
break;
// Normal writes
case 2:
m_gvram[offset] = data;
break;
// NOP
case 3:
break;
}
}