mirror of
https://github.com/holub/mame
synced 2025-04-16 05:24:54 +03:00
nec/pc88va.cpp: implement GVRAM single/multiplane ROP registers
This commit is contained in:
parent
c6897b843d
commit
c4092afdfa
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user