From d8f4411f3955d9c7f45e637ceffbb71462a8bcd2 Mon Sep 17 00:00:00 2001 From: Angelo Salese Date: Thu, 20 Apr 2023 16:24:11 +0200 Subject: [PATCH] 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; --- hash/vboy.xml | 34 +++--- src/devices/cpu/v810/v810.cpp | 165 +++++++++++++++----------- src/devices/cpu/v810/v810.h | 7 +- src/devices/video/huc6261.cpp | 4 +- src/devices/video/huc6272.cpp | 53 +++++---- src/devices/video/huc6272.h | 2 + src/mame/nintendo/vboy.cpp | 215 ++++++++++++++++++---------------- 7 files changed, 270 insertions(+), 210 deletions(-) diff --git a/hash/vboy.xml b/hash/vboy.xml index 279d5b72cd1..0b4f88aa23d 100644 --- a/hash/vboy.xml +++ b/hash/vboy.xml @@ -83,7 +83,8 @@ Ball goes out of bounds sometimes on gameplay (verify) 1995 Nintendo @@ -100,7 +101,9 @@ Missing intro and gameplay graphics, uses [VIP] framebuffer 2013? <unknown> @@ -181,7 +184,9 @@ Glitchy [VIP] backgrounds during gameplay 1995 Nintendo @@ -194,13 +199,12 @@ Title screen logo is misplaced if intro isn't skipped - + Nester's Funky Bowling (USA) 1996 Nintendo @@ -251,8 +255,6 @@ Gameplay [VIP] flickers too much, framebuffer? Nintendo @@ -270,8 +272,6 @@ Triggers [V810] TRAP opcode on gameplay (related to above?) T&E Soft @@ -308,8 +308,8 @@ Overdriven [sound] samples when moving outside the allocated space 1995 Taito @@ -357,7 +357,7 @@ I/O access (regression) 1995 Nintendo @@ -406,7 +406,7 @@ Verify player KO graphics 1995 Pack-In-Video @@ -459,7 +459,8 @@ Missing GFXs for gender select in name entry 1995 T&E Soft @@ -525,7 +526,7 @@ Unresponsive [pad] inputs on gameplay - + V-Tetris (Japan) 1995 Bullet-Proof Software @@ -550,6 +551,7 @@ Slightly overdriven [sound] samples during gameplay diff --git a/src/devices/cpu/v810/v810.cpp b/src/devices/cpu/v810/v810.cpp index 79b5b83ea68..3c20810e0a4 100644 --- a/src/devices/cpu/v810/v810.cpp +++ b/src/devices/cpu/v810/v810.cpp @@ -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 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; } diff --git a/src/devices/cpu/v810/v810.h b/src/devices/cpu/v810/v810.h index 9a392595eeb..4a56ecf9fb1 100644 --- a/src/devices/cpu/v810/v810.h +++ b/src/devices/cpu/v810/v810.h @@ -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(); }; diff --git a/src/devices/video/huc6261.cpp b/src/devices/video/huc6261.cpp index 4a117b73d08..13658b9a9cc 100644 --- a/src/devices/video/huc6261.cpp +++ b/src/devices/video/huc6261.cpp @@ -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 diff --git a/src/devices/video/huc6272.cpp b/src/devices/video/huc6272.cpp index 781bf0a2c91..bd046584f42 100644 --- a/src/devices/video/huc6272.cpp +++ b/src/devices/video/huc6272.cpp @@ -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"); diff --git a/src/devices/video/huc6272.h b/src/devices/video/huc6272.h index 67a629c1299..169eda8cee9 100644 --- a/src/devices/video/huc6272.h +++ b/src/devices/video/huc6272.h @@ -114,6 +114,7 @@ private: required_device m_scsi_data_in; required_device m_scsi_data_out; required_device m_scsi_ctrl_in; + required_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); diff --git a/src/mame/nintendo/vboy.cpp b/src/mame/nintendo/vboy.cpp index 93ef7f56474..7a2f00b329d 100644 --- a/src/mame/nintendo/vboy.cpp +++ b/src/mame/nintendo/vboy.cpp @@ -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(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);