v810/v810.cpp: separate irqs into individual lines as a PoC (#11088)

Allows pcfx to detect a CD as Audio (needs TOC/Mode Select (10) fixes for actual PC-FX detection, uses t10mmc.cpp under the hood).

- v810/v810.cpp: fix device_reset behaviour;
- v810/v810.cpp: fix MPYHW opcode, makes redalert/redalertj not to crash on attract/gameplay;
- nintendo/vboy.cpp: fix screen type to LCD until we have an actual LED class;
- nintendo/vboy.cpp: fix spaceinv gameplay shots display;
- nintendo/vboy.cpp: fix bg page offsets for hyperfgt;
- video/huc6272.cpp: hookup SCSI cmd readback;
This commit is contained in:
Angelo Salese 2023-04-20 16:24:11 +02:00 committed by GitHub
parent a4732500a5
commit d8f4411f39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 270 additions and 210 deletions

View file

@ -83,7 +83,8 @@ Ball goes out of bounds sometimes on gameplay (verify)
<year>1995</year>
<publisher>Nintendo</publisher>
<notes><![CDATA[
Missing intro and gameplay graphics, uses [VIP] framebuffer
Missing attract and gameplay graphics, uses [VIP] framebuffer
Missing [cart] SRAM
]]></notes>
<info name="developer" value="T&amp;E Soft"/>
<info name="serial" value="VUE-VVGE-USA"/>
@ -100,7 +101,9 @@ Missing intro and gameplay graphics, uses [VIP] framebuffer
<year>2013?</year>
<publisher>&lt;unknown&gt;</publisher>
<notes><![CDATA[
Glitchy [VIP] backgrounds during gameplay
Glitchy [VIP] backgrounds during gameplay (fixed)
[VIP] palette ramps are pretty dim even on max brightness
bp 7020fea,1,{R10=1;g} in Versus Mode to force serial to master mode (and be able to select stage)
]]></notes>
<info name="developer" value="Mr. Anon"/>
<info name="serial" value="VUE ANON"/>
@ -181,7 +184,9 @@ Glitchy [VIP] backgrounds during gameplay
<year>1995</year>
<publisher>Nintendo</publisher>
<notes><![CDATA[
Title screen logo is misplaced if intro isn't skipped
Title screen logo is misplaced if intro isn't skipped, uses [V810] CMPF, CVTS, ADDF, SUBF.
Mario also shots in fault, which definitely indicates bad maths
[VIP] palette ramps are particularly bad, cfr. ROZ layer
]]></notes>
<info name="developer" value="Nintendo R&amp;D1, Tose Software"/>
<info name="serial" value="VUE-VMTJ-JPN, VUE-VMTJ-USA"/>
@ -194,13 +199,12 @@ Title screen logo is misplaced if intro isn't skipped
</part>
</software>
<software name="nesterfb" supported="no">
<software name="nesterfb" supported="partial">
<description>Nester's Funky Bowling (USA)</description>
<year>1996</year>
<publisher>Nintendo</publisher>
<notes><![CDATA[
I/O access (regression)
Once that you hit the pins, animation phase takes a while to start (verify)
Once that you hit the pins, animation phase takes a while to start (can't repro, assume fixed)
]]></notes>
<info name="developer" value="Nintendo IRD"/>
<info name="serial" value="VUE-VNFE-USA"/>
@ -251,8 +255,6 @@ Gameplay [VIP] flickers too much, framebuffer?
<publisher>Nintendo</publisher>
<notes><![CDATA[
Missing graphics, uses [VIP] framebuffer
Triggers [V810] DIVr divide by zero on intro (related to above?)
Triggers [V810] TRAP opcode on gameplay (related to above?)
]]></notes>
<info name="developer" value="T&amp;E Soft"/>
<info name="serial" value="VUE-VREE-USA"/>
@ -270,8 +272,6 @@ Triggers [V810] TRAP opcode on gameplay (related to above?)
<publisher>T&amp;E Soft</publisher>
<notes><![CDATA[
Missing graphics, uses [VIP] framebuffer
Triggers [V810] DIVr divide by zero on intro (related to above?)
[V810] crashes on gameplay (related to above?)
]]></notes>
<info name="developer" value="T&amp;E Soft"/>
<info name="serial" value="VUE-VREJ-JPN"/>
@ -308,8 +308,8 @@ Overdriven [sound] samples when moving outside the allocated space
<year>1995</year>
<publisher>Taito</publisher>
<notes><![CDATA[
No graphics during attract mode, [VIP] framebuffer?
No shots during gameplay, [VIP] framebuffer?
No FMV during attract mode (two of them), [VIP] framebuffer
No shots during gameplay, [VIP] affine GP out of bounds (fixed)
Spams lots of "error reading INTCLR" in error.log
]]></notes>
<info name="developer" value="Taito"/>
@ -357,7 +357,7 @@ I/O access (regression)
<year>1995</year>
<publisher>Nintendo</publisher>
<notes><![CDATA[
Verify player KO graphics
Verify player KO [VIP] graphics
]]></notes>
<info name="developer" value="Nintendo R&amp;D1"/>
<info name="serial" value="VUE-VTBJ-JPN, VUE-VTBJ-USA"/>
@ -406,7 +406,7 @@ Verify player KO graphics
<year>1995</year>
<publisher>Pack-In-Video</publisher>
<notes><![CDATA[
Missing GFXs for gender select in name entry
Missing GFXs for gender select in name entry, [VIP] palette ramps
]]></notes>
<info name="developer" value="Pack-In-Video"/>
<info name="serial" value="VUE-VVFJ-JPN"/>
@ -459,7 +459,8 @@ Missing GFXs for gender select in name entry
<year>1995</year>
<publisher>T&amp;E Soft</publisher>
<notes><![CDATA[
Missing intro and gameplay graphics, uses [VIP] framebuffer
Missing attract and gameplay graphics, uses [VIP] framebuffer
Missing [cart] SRAM
]]></notes>
<info name="developer" value="T&amp;E Soft"/>
<info name="serial" value="VUE-VVGJ-JPN"/>
@ -525,7 +526,7 @@ Unresponsive [pad] inputs on gameplay
</part>
</software>
<software name="vtetris" supported="no">
<software name="vtetris" supported="partial">
<description>V-Tetris (Japan)</description>
<year>1995</year>
<publisher>Bullet-Proof Software</publisher>
@ -550,6 +551,7 @@ Slightly overdriven [sound] samples during gameplay
<notes><![CDATA[
[VIP] brightness dips during intro, [V810] core bug?
Dash doesn't produce any [sound]
[VIP] palette ramps are ugly on default brightness
]]></notes>
<info name="developer" value="Nintendo R&amp;D1"/>
<info name="serial" value="VUE-VWCJ-JPN, VUE-VWCJ-USA"/>

View file

@ -1,7 +1,7 @@
// license:BSD-3-Clause
// copyright-holders: Tomasz Slanina, Angelo Salese
/******************************************************************
NEC V810 (upd70732) core
NEC V810 (μpd70732) core
Tomasz Slanina
Angelo Salese
@ -20,12 +20,13 @@
TODO:
- Verify floating point opcodes (single precision IEEE-754 standard)
- CY flag in few floating point opcodes;
- split maskable interrupt lines into separate entities;
- CY flag in floating point opcodes that supports them;
- Subclass NVC (vboy CPU), few extra opcodes plus onchip peripherals ($0200'0000 area);
- implement trap opcode;
- implement halt opcode;
- implement double exception behaviour;
- implement NP fatal exception;
- implement NMI;
- implement floating point exceptions;
- verify and improve bitstring opcodes;
- cache handling;
@ -189,6 +190,7 @@ std::unique_ptr<util::disasm_interface> v810_device::create_disassembler()
// r0 is literally a "register zero", reading returns 0, writing is ignored.
void v810_device::SETREG(uint32_t reg,uint32_t val)
{
if(reg)
@ -630,13 +632,14 @@ uint32_t v810_device::opJAL(uint32_t op)
return 3;
}
// TODO: specific to NVC
uint32_t v810_device::opEI(uint32_t op)
{
SET_ID(0);
return 1;
}
// TODO: specific to NVC
uint32_t v810_device::opDI(uint32_t op)
{
SET_ID(1);
@ -1057,50 +1060,53 @@ uint32_t v810_device::opCVTW(uint32_t op)
return 18;
}
uint32_t v810_device::opMPYHW(uint32_t op)
{
int val1=(GETREG(GET1) & 0xffff);
int val2=(GETREG(GET2) & 0xffff);
SET_OV(0);
val2*=val1;
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
SETREG(GET2,val2);
// TODO: unknown
return 18;
}
uint32_t v810_device::opXB(uint32_t op)
{
int val=GETREG(GET2);
SET_OV(0);
val = (val & 0xffff0000) | swapendian_int16(val & 0xffff);
SET_Z((val==0.0f)?1:0);
SET_S((val<0.0f)?1:0);
// TODO: verify flags really being unaffected
//SET_OV(0);
//SET_Z((val==0.0f)?1:0);
//SET_S((val<0.0f)?1:0);
SETREG(GET2,val);
// TODO: unknown
return 18;
return 1;
}
uint32_t v810_device::opXH(uint32_t op)
{
int val=GETREG(GET2);
SET_OV(0);
val = ((val & 0xffff0000)>>16) | ((val & 0xffff)<<16);
SET_Z((val==0.0f)?1:0);
SET_S((val<0.0f)?1:0);
// TODO: verify flags really being unaffected
//SET_OV(0);
//SET_Z((val==0.0f)?1:0);
//SET_S((val<0.0f)?1:0);
SETREG(GET2,val);
// TODO: unknown
return 18;
return 1;
}
uint32_t v810_device::opMPYHW(uint32_t op)
{
s16 val1 = (s16)GETREG(GET1);
s16 val2 = (s16)GETREG(GET2);
s32 result = (s32)(val1 * val2);
// TODO: verify flags really being unaffected
//SET_OV(0);
//SET_Z((result == 0) ? 1 : 0);
//SET_S((result < 0) ? 1 : 0);
SETREG(GET2,result);
return 9;
}
uint32_t v810_device::opFpoint(uint32_t op)
{
uint32_t tmp=R_OP(PC);
uint32_t op_cycles = 0;
PC+=2;
switch((tmp&0xfc00)>>10)
const u8 op_type = (tmp&0xfc00)>>10;
PC += 2;
switch(op_type)
{
// TODO: (*) denotes Virtual Boy specific opcodes
// likely needs co-processor override
@ -1113,10 +1119,11 @@ uint32_t v810_device::opFpoint(uint32_t op)
case 0x7: op_cycles = opDIVF(op);break;
case 0x8: op_cycles = opXB(op); break; // (*)
case 0x9: op_cycles = opXH(op); break; // (*)
//case 0xa: REV (*)
case 0xb: op_cycles = opTRNC(op);break;
case 0xc: op_cycles = opMPYHW(op); break; // (*)
default:
throw emu_fatalerror("Floating point unknown type %02x\n",(tmp&0xfc00) >> 10);
throw emu_fatalerror("Floating point unknown type %02x\n", op_type);
break;
}
return op_cycles;
@ -1315,13 +1322,11 @@ void v810_device::device_start()
space(AS_PROGRAM).specific(m_program);
space(has_space(AS_IO) ? AS_IO : AS_PROGRAM).specific(m_io);
m_irq_line = 0;
m_irq_state = CLEAR_LINE;
m_irq_state = 0;
m_nmi_line = CLEAR_LINE;
memset(m_reg, 0x00, sizeof(m_reg));
save_item(NAME(m_reg));
save_item(NAME(m_irq_line));
save_item(NAME(m_irq_state));
save_item(NAME(m_nmi_line));
save_item(NAME(m_PPC));
@ -1398,60 +1403,82 @@ void v810_device::state_string_export(const device_state_entry &entry, std::stri
void v810_device::device_reset()
{
int i;
for(i=0;i<64;i++) m_reg[i]=0;
// everything else is "Undefind" (sic)
m_reg[0] = 0;
PC = 0xfffffff0;
PSW = 0x1000;
ECR = 0x0000fff0;
PSW = 0x00008000;
ECR = 0x0000fff0;
}
// TODO: unsafe on different irq levels asserted at same time
// TODO: sketchy, lacks fatal & double exceptions
void v810_device::take_interrupt()
// TODO: remaining exception types
void v810_device::check_interrupts()
{
EIPC = PC;
EIPSW = PSW;
if (m_irq_state == 0)
return;
PC = 0xfffffe00 | (m_irq_line << 4);
ECR = 0xfe00 | (m_irq_line << 4);
if (GET_NP || GET_EP || GET_ID)
return;
uint8_t num = m_irq_line + 1;
if (num==0x10) num=0x0f;
for (u16 irq_line = 15; irq_line >= (PSW & 0xF0000) >> 16; irq_line --)
{
if (!((1 << irq_line) & m_irq_state))
continue;
PSW &= 0xfff0ffff; // clear interrupt level
SET_EP(1);
SET_ID(1);
PSW |= num << 16;
standard_irq_callback(irq_line, PC);
m_icount -= clkIRQ;
EIPC = PC;
EIPSW = PSW;
PC = 0xfffffe00 | (irq_line << 4);
ECR = 0xfe00 | (irq_line << 4);
uint8_t num = irq_line + 1;
if (num == 0x10) num = 0x0f;
// clear interrupt level
PSW &= 0xfff0ffff;
SET_EP(1);
SET_ID(1);
SET_AE(0);
PSW |= num << 16;
m_icount -= clkIRQ;
return;
}
}
void v810_device::execute_run()
{
if (m_irq_state != CLEAR_LINE) {
if (!(GET_NP | GET_EP | GET_ID)) {
if (m_irq_line >=((PSW & 0xF0000) >> 16)) {
take_interrupt();
}
}
}
while(m_icount>0)
// TODO: move in execution body
// (breaks pcfx boot)
check_interrupts();
do
{
uint32_t op;
m_PPC=PC;
m_PPC = PC;
debugger_instruction_hook(PC);
op=R_OP(PC);
PC+=2;
int cnt;
cnt = (this->*s_OpCodeTable[op>>10])(op);
m_icount-= cnt;
}
uint32_t op = R_OP(PC);
PC += 2;
int cnt = (this->*s_OpCodeTable[op>>10])(op);
m_icount -= cnt;
} while(m_icount > 0);
}
void v810_device::execute_set_input( int irqline, int state)
// TODO: logically connects to /INTV0-/INTV3 pins
// v810 just exposes an INT and 4 V(ector?) lines.
// - on pcfx there's an unknown chip irq priority/mask dispatcher;
// - on vboy it's implied in the NVC specs with a laconic "Interrupt Encoder".
// It's also 5 possible lines, which wouldn't work bitwise;
void v810_device::execute_set_input( int irqline, int state )
{
m_irq_state = state;
m_irq_line = irqline;
if (state == HOLD_LINE)
throw emu_fatalerror("V810: using HOLD_LINE is unsupported by the core");
u16 mask = 1 << irqline;
if (state == ASSERT_LINE)
m_irq_state |= mask;
else
m_irq_state &= ~mask;
}

View file

@ -113,9 +113,8 @@ private:
address_space_config m_program_config;
address_space_config m_io_config;
uint32_t m_reg[65];
uint8_t m_irq_line;
uint8_t m_irq_state;
uint32_t m_reg[65]{};
uint16_t m_irq_state;
uint8_t m_nmi_line;
memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache m_cache;
memory_access<32, 2, 0, ENDIANNESS_LITTLE>::specific m_program;
@ -191,7 +190,7 @@ private:
uint32_t opXH(uint32_t op);
uint32_t opFpoint(uint32_t op);
uint32_t opBSU(uint32_t op);
void take_interrupt();
void check_interrupts();
};

View file

@ -293,14 +293,14 @@ void huc6261_device::write(offs_t offset, uint16_t data)
break;
case 0x01:
logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register );
//logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register );
switch( m_register )
{
/* Control register */
// -x-- ---- ---- ---- Enable HuC6271: 0 - disabled, 1 - enabled
// --x- ---- ---- ---- Enable HuC6272 BG3: 0 - disabled, 1 - enabled
// ---x ---- ---- ---- Enable HuC6272 BG2: 0 - disabled, 1 - enabled
// ---- x--- ---- ---- Enable Huc6272 BG1: 0 - disabled, 1 - enabled
// ---- x--- ---- ---- Enable HuC6272 BG1: 0 - disabled, 1 - enabled
// ---- -x-- ---- ---- Enable HuC6272 BG0: 0 - disabled, 1 - enabled
// ---- --x- ---- ---- Enable HuC6270 SPR: 0 - disabled, 1 - enabled
// ---- ---x ---- ---- Enable HuC6270 BG: 0 - disabled, 1 - enabled

View file

@ -6,12 +6,12 @@
TODO:
- Use NSCSI instead of legacy one;
\- SEL acknowledges with 0x84, bit 7 controller type is unknown at this time
(bit 2 should select the CD drive);
- Convert base mapping to address_map;
- Convert I/O to space address, and make it honor mem_mask;
- subclass "SCSICD" into SCSI-2 "CD-ROM DRIVE:FX"
\- Crashes if CD-ROM is in, on unhandled command 0x28 "Read(10)".
Tries to LBA with 0xffff'ff7a and 0 after failing a custom mode sense,
prior TOC tests makes even less sense, CPU core bug?
\- Fails detection of PC-FX discs, detects as normal audio CDs;
\- During POST it tries an unhandled 0x44 "Read Header";
\- Derivative design of PCE drive, which in turn is a derivative of PC-8801-30 (cd drive)
and PC-8801-31 (interface);
@ -70,7 +70,7 @@ void huc6272_device::amap(address_map &map)
void huc6272_device::io_map(address_map &map)
{
map(0x00, 0x00).rw(FUNC(huc6272_device::scsi_data_r), FUNC(huc6272_device::scsi_data_w));
map(0x01, 0x01).w(FUNC(huc6272_device::scsi_initiate_cmd_w));
map(0x01, 0x01).rw(FUNC(huc6272_device::scsi_cmd_status_r), FUNC(huc6272_device::scsi_initiate_cmd_w));
// map(0x02, 0x02) SCSI DMA mode
map(0x03, 0x03).w(FUNC(huc6272_device::scsi_target_cmd_w));
map(0x05, 0x05).rw(FUNC(huc6272_device::scsi_bus_r), FUNC(huc6272_device::scsi_bus_w));
@ -135,22 +135,23 @@ void huc6272_device::io_map(address_map &map)
//-------------------------------------------------
huc6272_device::huc6272_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, HUC6272, tag, owner, clock),
device_memory_interface(mconfig, *this),
m_huc6271(*this, finder_base::DUMMY_TAG),
m_cdda_l(*this, "cdda_l"),
m_cdda_r(*this, "cdda_r"),
m_program_space_config("microprg", ENDIANNESS_LITTLE, 16, 4, 0, address_map_constructor(FUNC(huc6272_device::microprg_map), this)),
m_data_space_config("kram", ENDIANNESS_LITTLE, 32, 21, 0, address_map_constructor(FUNC(huc6272_device::kram_map), this)),
m_io_space_config("io", ENDIANNESS_LITTLE, 32, 7, -2, address_map_constructor(FUNC(huc6272_device::io_map), this)),
m_microprg_ram(*this, "microprg_ram"),
m_kram_page0(*this, "kram_page0"),
m_kram_page1(*this, "kram_page1"),
m_scsibus(*this, "scsi"),
m_scsi_data_in(*this, "scsi_data_in"),
m_scsi_data_out(*this, "scsi_data_out"),
m_scsi_ctrl_in(*this, "scsi_ctrl_in"),
m_irq_changed_cb(*this)
: device_t(mconfig, HUC6272, tag, owner, clock)
, device_memory_interface(mconfig, *this)
, m_huc6271(*this, finder_base::DUMMY_TAG)
, m_cdda_l(*this, "cdda_l")
, m_cdda_r(*this, "cdda_r")
, m_program_space_config("microprg", ENDIANNESS_LITTLE, 16, 4, 0, address_map_constructor(FUNC(huc6272_device::microprg_map), this))
, m_data_space_config("kram", ENDIANNESS_LITTLE, 32, 21, 0, address_map_constructor(FUNC(huc6272_device::kram_map), this))
, m_io_space_config("io", ENDIANNESS_LITTLE, 32, 7, -2, address_map_constructor(FUNC(huc6272_device::io_map), this))
, m_microprg_ram(*this, "microprg_ram")
, m_kram_page0(*this, "kram_page0")
, m_kram_page1(*this, "kram_page1")
, m_scsibus(*this, "scsi")
, m_scsi_data_in(*this, "scsi_data_in")
, m_scsi_data_out(*this, "scsi_data_out")
, m_scsi_ctrl_in(*this, "scsi_ctrl_in")
, m_scsi_cmd_in(*this, "scsi_cmd_in")
, m_irq_changed_cb(*this)
{
}
@ -334,6 +335,11 @@ void huc6272_device::scsi_data_w(offs_t offset, u32 data, u32 mem_mask)
m_scsi_data_out->write(data & 0xff);
}
u32 huc6272_device::scsi_cmd_status_r(offs_t offset)
{
return m_scsi_cmd_in->read() & 0xff;
}
void huc6272_device::scsi_initiate_cmd_w(offs_t offset, u32 data, u32 mem_mask)
{
//m_scsibus->write_bsy(BIT(data, 0)); // bus?
@ -731,9 +737,16 @@ void huc6272_device::device_add_mconfig(machine_config &config)
scsibus.io_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit2));
scsibus.sel_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit1));
scsibus.rst_handler().append("scsi_cmd_in", FUNC(input_buffer_device::write_bit7));
scsibus.ack_handler().set("scsi_cmd_in", FUNC(input_buffer_device::write_bit4));
scsibus.sel_handler().append("scsi_cmd_in", FUNC(input_buffer_device::write_bit2));
scsibus.atn_handler().set("scsi_cmd_in", FUNC(input_buffer_device::write_bit1));
scsibus.bsy_handler().append("scsi_cmd_in", FUNC(input_buffer_device::write_bit0));
output_latch_device &scsiout(OUTPUT_LATCH(config, "scsi_data_out"));
scsibus.set_output_latch(scsiout);
INPUT_BUFFER(config, "scsi_cmd_in");
INPUT_BUFFER(config, "scsi_ctrl_in");
INPUT_BUFFER(config, "scsi_data_in");

View file

@ -114,6 +114,7 @@ private:
required_device<input_buffer_device> m_scsi_data_in;
required_device<output_latch_device> m_scsi_data_out;
required_device<input_buffer_device> m_scsi_ctrl_in;
required_device<input_buffer_device> m_scsi_cmd_in;
/* Callback for when the irq line may have changed (mandatory) */
devcb_write_line m_irq_changed_cb;
@ -142,6 +143,7 @@ private:
u32 scsi_data_r(offs_t offset);
void scsi_data_w(offs_t offset, u32 data, u32 mem_mask = ~0);
u32 scsi_cmd_status_r(offs_t offset);
void scsi_initiate_cmd_w(offs_t offset, u32 data, u32 mem_mask = ~0);
void scsi_target_cmd_w(offs_t offset, u32 data, u32 mem_mask = ~0);

View file

@ -172,11 +172,11 @@ private:
void put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal);
void fill_ovr_char(uint16_t code, uint8_t pal);
int8_t get_bg_map_pixel(int num, int xpos, int ypos);
int8_t get_bg_map_pixel(int num, int xpos, int ypos, u8 scx);
void draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my,int h, int w,
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num);
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx);
void draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int gx, int gp, int gy, int h, int w,
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num);
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx);
uint8_t display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt);
void set_brightness();
void vboy_palette(palette_device &palette) const;
@ -187,7 +187,6 @@ private:
TIMER_DEVICE_CALLBACK_MEMBER(vboy_scanlineL);
void vboy_map(address_map &map);
void vboy_io(address_map &map);
};
@ -206,33 +205,6 @@ void vboy_state::video_start()
m_bgmap = make_unique_clear<uint16_t[]>(0x20000 >> 1);
}
void vboy_state::put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal)
{
for (uint8_t yi = 0; yi < 8; yi++)
{
uint16_t const data = READ_FONT(code * 8 + yi);
for (uint8_t xi = 0; xi < 8; xi++)
{
uint8_t const dat = ((data >> (xi << 1)) & 0x03);
if (dat)
{
uint16_t const res_x = x + xi;
uint16_t const res_y = y + yi;
if (cliprect.contains(res_x, res_y))
{
uint8_t const col = (pal >> (dat * 2)) & 3;
bitmap.pix((res_y), (res_x)) = m_palette->pen(col);
}
}
}
}
}
void vboy_state::fill_ovr_char(uint16_t code, uint8_t pal)
{
@ -250,15 +222,19 @@ void vboy_state::fill_ovr_char(uint16_t code, uint8_t pal)
}
}
inline int8_t vboy_state::get_bg_map_pixel(int num, int xpos, int ypos)
inline int8_t vboy_state::get_bg_map_pixel(int num, int xpos, int ypos, u8 scx)
{
// auto profile1 = g_profiler.start(PROFILER_USER1);
int const y = ypos >>3;
int const x = xpos >>3;
int const y = ypos >> 3;
int const x = xpos >> 3;
// an individual tilemap is 64x64, the upper X/Y bits selects pages in 4096 units and joins with the global BGMAP_BASE.
// hyperfgt backgrounds in particular wants to multiply Y page by SCX factor,
// - it's 1 for E.Honda stage (the two 512x1024 tilemaps composing the hot bath)
// - and 2 elsewhere (1024x1024).
uint8_t const stepx = (x & 0x1c0) >> 6;
uint8_t const stepy = ((y & 0x1c0) >> 6) * (stepx+1);
uint8_t const stepy = ((y & 0x1c0) >> 6) * (scx + 1);
uint16_t const val = READ_BGMAP((x & 0x3f) + (64 * (y & 0x3f)) + ((num + stepx + stepy) * 0x1000));
int const pal = m_vip_io.GPLT[(val >> 14) & 3];
int const code = val & 0x3fff;
@ -271,11 +247,11 @@ inline int8_t vboy_state::get_bg_map_pixel(int num, int xpos, int ypos)
if(dat == 0)
return -1;
else
return (pal >> (dat*2)) & 3;
return (pal >> (dat * 2)) & 3;
}
void vboy_state::draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int mode, int gx, int gp, int gy, int mx, int mp, int my, int h, int w,
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num)
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx)
{
// auto profile2 = g_profiler.start(PROFILER_USER2);
@ -315,12 +291,12 @@ void vboy_state::draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, ui
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
}
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
}
if(pix != -1)
@ -330,17 +306,17 @@ void vboy_state::draw_bg_map(bitmap_ind16 &bitmap, const rectangle &cliprect, ui
}
void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t param_base, int gx, int gp, int gy, int h, int w,
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num)
uint16_t x_mask, uint16_t y_mask, uint8_t ovr, bool right, int bg_map_num, u8 scx)
{
// auto profile3 = g_profiler.start(PROFILER_USER3);
for(int y=0;y<=h;y++)
for(int y = 0; y <= h; y++)
{
float h_skw = (int16_t)READ_BGMAP(param_base + (y*8+0)) / 8.0;
float prlx = (int16_t)READ_BGMAP(param_base + (y*8+1)) / 8.0;
float v_skw = (int16_t)READ_BGMAP(param_base + (y*8+2)) / 8.0;
float h_scl = (int16_t)READ_BGMAP(param_base + (y*8+3)) / 512.0;
float v_scl = (int16_t)READ_BGMAP(param_base + (y*8+4)) / 512.0;
float h_skw = (int16_t)READ_BGMAP(param_base + (y * 8 + 0)) / 8.0;
float prlx = (int16_t)READ_BGMAP(param_base + (y * 8 + 1)) / 8.0;
float v_skw = (int16_t)READ_BGMAP(param_base + (y * 8 + 2)) / 8.0;
float h_scl = (int16_t)READ_BGMAP(param_base + (y * 8 + 3)) / 512.0;
float v_scl = (int16_t)READ_BGMAP(param_base + (y * 8 + 4)) / 512.0;
h_skw += right ? -prlx : prlx;
@ -351,7 +327,10 @@ void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect
int16_t x1 = (x+gx);
int pix = 0;
x1 += right ? -gp : gp;
x1 += (right ? -gp : gp);
// clamp for spaceinv gameplay shots
// (sets GPs with out of bounds GP values, cfr. $3da40/$3daa0 0xc*** world entries)
x1 &= 0x1fff;
src_x = (int32_t)((h_skw) + (h_scl * x));
src_y = (int32_t)((v_skw) + (v_scl * x));
@ -362,7 +341,7 @@ void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect
}
else
{
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask);
pix = get_bg_map_pixel(bg_map_num, src_x & x_mask, src_y & y_mask, scx);
}
if(pix != -1)
@ -372,40 +351,76 @@ void vboy_state::draw_affine_map(bitmap_ind16 &bitmap, const rectangle &cliprect
}
}
void vboy_state::put_obj(bitmap_ind16 &bitmap, const rectangle &cliprect, int x, int y, uint16_t code, uint8_t pal)
{
for (uint8_t yi = 0; yi < 8; yi++)
{
uint16_t const data = READ_FONT(code * 8 + yi);
for (uint8_t xi = 0; xi < 8; xi++)
{
uint8_t const dat = ((data >> (xi << 1)) & 0x03);
if (dat)
{
uint16_t const res_x = x + xi;
uint16_t const res_y = y + yi;
if (cliprect.contains(res_x, res_y))
{
uint8_t const col = (pal >> (dat * 2)) & 3;
bitmap.pix((res_y), (res_x)) = m_palette->pen(col);
}
}
}
}
}
/*
x--- ---- ---- ---- [0] LON
-x-- ---- ---- ---- RON
--xx ---- ---- ---- BGM type
---- xx-- ---- ---- SCX
---- --xx ---- ---- SCY
---- ---- x--- ---- OVR
---- ---- -x-- ---- END
---- ---- --00 ----
---- ---- ---- xxxx BGMAP_BASE
* $3d800 World list
*
* x--- ---- ---- ---- [0] LON enabled for left screen
* -x-- ---- ---- ---- RON enabled for right screen
* --xx ---- ---- ---- BGM type
* --00 ---- ---- ---- Normal
* --01 ---- ---- ---- Hi-Bias
* --10 ---- ---- ---- Affine
* --11 ---- ---- ---- OAM
* ---- xx-- ---- ---- SCX number of pages in the X axis
* ---- --xx ---- ---- SCY number of pages in the Y axis
* ---- ---- x--- ---- OVR enable overdraw char
* ---- ---- -x-- ---- END marker for end of list processing
* ---- ---- --00 ----
* ---- ---- ---- xxxx BGMAP_BASE
*/
uint8_t vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle &cliprect, bool right, int &cur_spt)
{
num <<= 4;
uint16_t def = READ_WORLD(num);
uint8_t lon = (def >> 15) & 1;
uint8_t ron = (def >> 14) & 1;
uint8_t mode = (def >> 12) & 3;
uint16_t scx = 64 << ((def >> 10) & 3);
uint16_t scy = 64 << ((def >> 8) & 3);
uint8_t ovr = (def >> 7) & 1;
uint8_t end = (def >> 6) & 1;
int16_t gx = READ_WORLD(num+1);
int16_t gp = READ_WORLD(num+2);
int16_t gy = READ_WORLD(num+3);
int16_t mx = READ_WORLD(num+4);
int16_t mp = READ_WORLD(num+5);
int16_t my = READ_WORLD(num+6);
uint16_t w = READ_WORLD(num+7);
uint16_t h = READ_WORLD(num+8);
uint16_t param_base = READ_WORLD(num+9) & 0xfff0;
uint16_t ovr_char = READ_BGMAP(READ_WORLD(num+10));
uint8_t bg_map_num = def & 0x0f;
const uint16_t def = READ_WORLD(num);
const uint8_t lon = (def >> 15) & 1;
const uint8_t ron = (def >> 14) & 1;
const uint8_t mode = (def >> 12) & 3;
const u8 raw_scx = ((def >> 10) & 3);
const u8 raw_scy = ((def >> 8) & 3);
const uint16_t scx = 64 << raw_scx;
const uint16_t scy = 64 << raw_scy;
const uint16_t scx_mask = scx * 8 - 1;
const uint16_t scy_mask = scy * 8 - 1;
const uint8_t ovr = (def >> 7) & 1;
const uint8_t end = (def >> 6) & 1;
const int16_t gx = READ_WORLD(num+1);
const int16_t gp = READ_WORLD(num+2);
const int16_t gy = READ_WORLD(num+3);
const int16_t mx = READ_WORLD(num+4);
const int16_t mp = READ_WORLD(num+5);
const int16_t my = READ_WORLD(num+6);
const uint16_t w = READ_WORLD(num+7);
const uint16_t h = READ_WORLD(num+8);
const uint16_t param_base = READ_WORLD(num+9) & 0xfff0;
const uint16_t ovr_char = READ_BGMAP(READ_WORLD(num+10));
const uint8_t bg_map_num = def & 0x0f;
if(end)
return 1;
@ -417,12 +432,12 @@ uint8_t vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle
if (lon && (!right))
{
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
}
if (ron && (right))
{
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
draw_bg_map(bitmap, cliprect, param_base, mode, gx, gp, gy, mx, mp, my, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
}
}
else if (mode==2) // Affine Mode
@ -432,12 +447,12 @@ uint8_t vboy_state::display_world(int num, bitmap_ind16 &bitmap, const rectangle
if (lon && (!right))
{
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
}
if (ron && (right))
{
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx*8-1, scy*8-1, ovr, right, bg_map_num);
draw_affine_map(bitmap, cliprect, param_base, gx, gp, gy, h,w, scx_mask, scy_mask, ovr, right, bg_map_num, raw_scx);
}
}
else if (mode==3) // OBJ Mode
@ -611,11 +626,18 @@ void vboy_state::timer_control_w(offs_t offset, u8 data)
{
m_maintimer->adjust(attotime::from_hz(10000));
}
}
}
else
{
m_maintimer->adjust(attotime::never);
// hyperfgt writes 0x18 -> 0x1c -> 0x19 in irq service,
// implying that a 1 -> 0 transition will ack as well
m_maincpu->set_input_line(1, CLEAR_LINE);
}
m_regs.tcr = (data & 0xfd) | (0xe4) | (m_regs.tcr & 2); // according to docs: bits 5, 6 & 7 are unused and set to 1, bit 1 is read only.
// according to docs: bits 5, 6 & 7 are unused and set to 1, bit 1 is read only.
m_regs.tcr = (data & 0xfd) | (0xe4) | (m_regs.tcr & 2);
if(data & 4)
m_regs.tcr &= 0xfd;
}
@ -675,8 +697,10 @@ void vboy_state::vip_map(address_map &map)
}
// TODO: verify against real HW
// - brightness presumably isn't a linear algorithm
// - REST needs to be taken into account (needs a working example)
// - LED brightness doesn't scale well with regular raster pen color.
// These BRTx values are the "time" where the LED stays on.
// - REST needs to be taken into account (nothing sets it up so far)
// - vfishing draws selection accents in main menu with BRTA signal (currently almost invisible);
void vboy_state::set_brightness()
{
int a,b,c;
@ -773,13 +797,13 @@ uint16_t vboy_state::vip_io_r(offs_t offset)
//printf("%d\n",row_num);
res = m_vip_io.XPSTTS & 0x00f3; // empty ^^'
res = m_vip_io.XPSTTS & 0x00f3;
res |= m_drawfb << 2;
if(m_row_num < 224/8)
{
res |= 0x8000;
res |= m_row_num<<8;
res |= m_row_num << 8;
}
return res;
@ -824,8 +848,9 @@ uint16_t vboy_state::vip_io_r(offs_t offset)
void vboy_state::vip_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// wariolnd end boss has these writes
if(mem_mask != 0xffff)
printf("%04x %02x\n",mem_mask,offset*2);
logerror("Warning: register %04x write with non-word access %02x & %04x\n",offset*2, data, mem_mask);
switch(offset << 1) {
/*
@ -1038,15 +1063,6 @@ void vboy_state::vboy_map(address_map &map)
//map(0x07000000, 0x07ffffff) cartslot ROM
}
void vboy_state::vboy_io(address_map &map)
{
map.global_mask(0x07ffffff);
map(0x00000000, 0x0007ffff).m(FUNC(vboy_state::vip_map));
map(0x01000000, 0x010005ff).rw("vbsnd", FUNC(vboysnd_device::read), FUNC(vboysnd_device::write));
map(0x02000000, 0x020000ff).mirror(0x0ffff00).m(FUNC(vboy_state::io_map)).umask32(0x000000ff);
// TODO: verify if ROM/RAM mirrors on I/O space (nesterfb)
}
/* Input ports */
static INPUT_PORTS_START( vboy )
PORT_START("INPUT")
@ -1218,7 +1234,8 @@ void vboy_state::vboy(machine_config &config)
/* basic machine hardware */
V810(config, m_maincpu, XTAL(20'000'000));
m_maincpu->set_addrmap(AS_PROGRAM, &vboy_state::vboy_map);
m_maincpu->set_addrmap(AS_IO, &vboy_state::vboy_io);
// no AS_IO, and some games relies on r/w the program map with INH/OUTH
// cfr. vforce, nesterfb, panicbom (sound)
TIMER(config, "scantimer_l").configure_scanline(FUNC(vboy_state::vboy_scanlineL), "3dleft", 0, 1);
//TIMER(config, "scantimer_r").configure_scanline(FUNC(vboy_state::vboy_scanlineR), "3dright", 0, 1);
@ -1234,13 +1251,13 @@ void vboy_state::vboy(machine_config &config)
PALETTE(config, m_palette, FUNC(vboy_state::vboy_palette), 4);
/* Left screen */
screen_device &lscreen(SCREEN(config, "3dleft", SCREEN_TYPE_RASTER));
screen_device &lscreen(SCREEN(config, "3dleft", SCREEN_TYPE_LCD));
lscreen.set_raw(XTAL(20'000'000)/2,757,0,384,264,0,224);
lscreen.set_screen_update(FUNC(vboy_state::screen_update_left));
lscreen.set_palette(m_palette);
/* Right screen */
screen_device &rscreen(SCREEN(config, "3dright", SCREEN_TYPE_RASTER));
screen_device &rscreen(SCREEN(config, "3dright", SCREEN_TYPE_LCD));
rscreen.set_raw(XTAL(20'000'000)/2,757,0,384,264,0,224);
rscreen.set_screen_update(FUNC(vboy_state::screen_update_right));
rscreen.set_palette(m_palette);