diff options
Diffstat (limited to 'src/mess/machine/nes.c')
-rw-r--r-- | src/mess/machine/nes.c | 619 |
1 files changed, 56 insertions, 563 deletions
diff --git a/src/mess/machine/nes.c b/src/mess/machine/nes.c index 14ca4c6aabe..b6cf0d1d086 100644 --- a/src/mess/machine/nes.c +++ b/src/mess/machine/nes.c @@ -7,21 +7,8 @@ ****************************************************************************/ #include "emu.h" -#include "crsshair.h" #include "cpu/m6502/m6502.h" #include "includes/nes.h" -#include "imagedev/flopdrv.h" - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -/* Set to dump info about the inputs to the errorlog */ -#define LOG_JOY 0 - -/* Set to generate prg & chr files when the cart is loaded */ -#define SPLIT_PRG 0 -#define SPLIT_CHR 0 /*************************************************************************** FUNCTIONS @@ -44,77 +31,12 @@ void nes_state::machine_reset() m_cartslot->pcb_reset(); m_maincpu->reset(); - - memset(m_pad_latch, 0, sizeof(m_pad_latch)); - memset(m_zapper_latch, 0, sizeof(m_zapper_latch)); - m_paddle_latch = 0; - m_paddle_btn_latch = 0; } //------------------------------------------------- // machine_start //------------------------------------------------- -void nes_state::state_register() -{ - save_item(NAME(m_last_frame_flip)); - - save_pointer(NAME(m_ciram), 0x800); - - save_item(NAME(m_pad_latch)); - save_item(NAME(m_zapper_latch)); - save_item(NAME(m_paddle_latch)); - save_item(NAME(m_paddle_btn_latch)); - save_item(NAME(m_mjpanel_latch)); - save_item(NAME(m_fck_scan)); - save_item(NAME(m_fck_mode)); - save_item(NAME(m_mic_obstruct)); - save_item(NAME(m_powerpad_latch)); - save_item(NAME(m_ftrainer_scan)); -} - -void nes_state::setup_ioports() -{ - for (int i = 0; i < 9; i++) - { - char str[7]; - sprintf(str, "FCKEY%i", i); - m_io_fckey[i] = ioport(str); - } - for (int i = 0; i < 13; i++) - { - char str[9]; - sprintf(str, "SUBKEY%i", i); - m_io_subkey[i] = ioport(str); - } - for (int i = 0; i < 4; i++) - { - char str[8]; - sprintf(str, "PAD%i", i + 1); - m_io_pad[i] = ioport(str); - sprintf(str, "MAH%i", i); - m_io_mahjong[i] = ioport(str); - sprintf(str, "FT_COL%i", i); - m_io_ftrainer[i] = ioport(str); - } - - m_io_ctrlsel = ioport("CTRLSEL"); - m_io_exp = ioport("EXP"); - m_io_paddle = ioport("PADDLE"); - m_io_paddle_btn = ioport("PADDLE_BUTTON"); - m_io_cc_left = ioport("CC_LEFT"); - m_io_cc_right = ioport("CC_RIGHT"); - m_io_zapper1_t = ioport("ZAPPER1_T"); - m_io_zapper1_x = ioport("ZAPPER1_X"); - m_io_zapper1_y = ioport("ZAPPER1_Y"); - m_io_zapper2_t = ioport("ZAPPER2_T"); - m_io_zapper2_x = ioport("ZAPPER2_X"); - m_io_zapper2_y = ioport("ZAPPER2_Y"); - m_io_powerpad[0] = ioport("POWERPAD1"); - m_io_powerpad[1] = ioport("POWERPAD2"); - m_io_disksel = ioport("FLIPDISK"); -} - void nes_state::machine_start() { address_space &space = m_maincpu->space(AS_PROGRAM); @@ -126,7 +48,7 @@ void nes_state::machine_start() m_ciram = auto_alloc_array(machine(), UINT8, 0x800); // other pointers got set in the loading routine, because they 'belong' to the cart itself - setup_ioports(); + m_io_disksel = ioport("FLIPDISK"); if (m_cartslot && m_cartslot->m_cart) { @@ -178,7 +100,9 @@ void nes_state::machine_start() m_cartslot->m_cart->pcb_reg_postload(machine()); } - state_register(); + // register saves + save_item(NAME(m_last_frame_flip)); + save_pointer(NAME(m_ciram), 0x800); } @@ -188,518 +112,71 @@ void nes_state::machine_start() READ8_MEMBER(nes_state::nes_in0_r) { - int cfg = m_io_ctrlsel->read(); - - // Some games expect bit 6 to be set because the last entry on the data bus shows up - // in the unused upper 3 bits, so typically a read from $4016 leaves 0x40 there. UINT8 ret = 0x40; - ret |= (m_pad_latch[0] & 0x01); - - // shift - m_pad_latch[0] >>= 1; - - // zapper - if ((cfg & 0x000f) == 0x0002) - { - int x = m_zapper_latch[0][1]; // x-position - int y = m_zapper_latch[0][2]; // y-position - UINT32 pix, color_base; - - // get the pixel at the gun position - pix = m_ppu->get_pixel(x, y); - - // get the color base from the ppu - color_base = m_ppu->get_colorbase(); - - // check if the cursor is over a bright pixel - if ((pix == color_base + 0x20) || (pix == color_base + 0x30) || - (pix == color_base + 0x33) || (pix == color_base + 0x34)) - ret &= ~0x08; // sprite hit - else - ret |= 0x08; // no sprite hit - - // light gun trigger - ret |= (m_zapper_latch[0][0] << 4); - } - - if (LOG_JOY) - logerror("joy 0 read, val: %02x, pc: %04x\n", ret, space.device().safe_pc()); - + ret |= m_ctrl1->read_bit0(); + ret |= m_ctrl1->read_bit34(); return ret; } READ8_MEMBER(nes_state::nes_in1_r) { - int cfg = m_io_ctrlsel->read(); - - // Some games expect bit 6 to be set because the last entry on the data bus shows up - // in the unused upper 3 bits, so typically a read from $4017 leaves 0x40 there. UINT8 ret = 0x40; - ret |= (m_pad_latch[1] & 0x01); - - // shift - m_pad_latch[1] >>= 1; - - // zapper - if ((cfg & 0x00f0) == 0x0020) - { - int x = m_zapper_latch[1][1]; // x-position - int y = m_zapper_latch[1][2]; // y-position - UINT32 pix, color_base; - - // get the pixel at the gun position - pix = m_ppu->get_pixel(x, y); - - // get the color base from the ppu - color_base = m_ppu->get_colorbase(); - - // check if the cursor is over a bright pixel - if ((pix == color_base + 0x20) || (pix == color_base + 0x30) || - (pix == color_base + 0x33) || (pix == color_base + 0x34)) - ret &= ~0x08; // sprite hit - else - ret |= 0x08; // no sprite hit - - // light gun trigger - ret |= (m_zapper_latch[1][0] << 4); - } - - // arkanoid paddle - if ((cfg & 0x00f0) == 0x0040) - { - ret |= (m_paddle_btn_latch << 3); // button - ret |= ((m_paddle_latch & 0x80) >> 3); // paddle data - m_paddle_latch <<= 1; - m_paddle_latch &= 0xff; - } - - // powerpad - if ((cfg & 0x00f0) == 0x0050 || (cfg & 0x00f0) == 0x0060) - { - ret |= ((m_powerpad_latch[0] & 0x01) << 3); - ret |= ((m_powerpad_latch[1] & 0x01) << 4); - m_powerpad_latch[0] >>= 1; - m_powerpad_latch[1] >>= 1; - } - - if (LOG_JOY) - logerror("joy 1 read, val: %02x, pc: %04x\n", ret, space.device().safe_pc()); - + ret |= m_ctrl2->read_bit0(); + ret |= m_ctrl2->read_bit34(); return ret; } WRITE8_MEMBER(nes_state::nes_in0_w) { - int cfg = m_io_ctrlsel->read(); - - if (LOG_JOY) - logerror("joy write, val: %02x, pc: %04x\n", data, space.device().safe_pc()); - - // Check if lightgun has been chosen as input: if so, enable crosshair - timer_set(attotime::zero, TIMER_ZAPPER_TICK); - - if (data & 0x01) - return; - - // Toggling bit 0 high then low resets controllers - m_pad_latch[0] = 0; - m_pad_latch[1] = 0; - m_zapper_latch[0][0] = 0; - m_zapper_latch[0][1] = 0; - m_zapper_latch[0][2] = 0; - m_zapper_latch[1][0] = 0; - m_zapper_latch[1][1] = 0; - m_zapper_latch[1][2] = 0; - m_paddle_btn_latch = 0; - m_paddle_latch = 0; - m_powerpad_latch[0] = 0; - m_powerpad_latch[1] = 0; - - // P1 inputs - switch (cfg & 0x000f) - { - case 0x01: // pad 1 - m_pad_latch[0] = m_io_pad[0]->read(); - break; - - case 0x02: // zapper (secondary) - m_zapper_latch[0][0] = m_io_zapper1_t->read(); - m_zapper_latch[0][1] = m_io_zapper1_x->read(); - m_zapper_latch[0][2] = m_io_zapper1_y->read(); - break; - } - - // P2 inputs - switch ((cfg & 0x00f0) >> 4) - { - case 0x01: // pad 2 - m_pad_latch[1] = m_io_pad[1]->read(); - break; - - case 0x02: // zapper (primary) - most games expect pad in port1 & zapper in port2 - m_zapper_latch[1][0] = m_io_zapper2_t->read(); - m_zapper_latch[1][1] = m_io_zapper2_x->read(); - m_zapper_latch[1][2] = m_io_zapper2_y->read(); - break; - - case 0x04: // arkanoid paddle - m_paddle_btn_latch = m_io_paddle_btn->read(); - m_paddle_latch = (UINT8) (m_io_paddle->read() ^ 0xff); - break; - - case 0x05: // power pad - case 0x06: // power pad - m_powerpad_latch[0] = m_io_powerpad[0]->read(); - m_powerpad_latch[1] = m_io_powerpad[1]->read() | 0xf0; - break; - } - - // P3 & P4 inputs in NES Four Score are read serially with P1 & P2 - // P3 inputs - if ((cfg & 0x0f00)) - m_pad_latch[0] |= ((m_io_pad[2]->read() << 8) | (0x08 << 16)); // pad 3 + signature - - // P4 inputs - if ((cfg & 0xf000)) - m_pad_latch[1] |= ((m_io_pad[3]->read() << 8) | (0x04 << 16)); // pad 4 + signature -} - - -WRITE8_MEMBER(nes_state::nes_in1_w) -{ + m_ctrl1->write(data); + m_ctrl2->write(data); } READ8_MEMBER(nes_state::fc_in0_r) { - int cfg = m_io_ctrlsel->read(); - int exp = m_io_exp->read(); - - // Some games expect bit 6 to be set because the last entry on the data bus shows up - // in the unused upper 3 bits, so typically a read from $4016 leaves 0x40 there. UINT8 ret = 0x40; - ret |= (m_pad_latch[0] & 0x01); - - // shift - m_pad_latch[0] >>= 1; - - // microphone bit - if ((cfg & 0x00f0) == 0x00f0) - ret |= m_mic_obstruct; //bit2! - - // EXP input - switch (exp & 0x0f) - { - case 0x02: // FC Keyboard: tape input - if ((m_cassette->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY) - { - double level = m_cassette->input(); - if (level < 0) - ret |= 0x00; - else - ret |= 0x02; - } - break; - - case 0x04: // Arkanoid paddle - ret |= (m_paddle_btn_latch << 1); // button - break; - - case 0x07: // Mahjong Panel - ret |= ((m_mjpanel_latch & 0x01) << 1); - m_mjpanel_latch >>= 1; - break; - - case 0x08: // 'multitap' p3 - ret |= ((m_pad_latch[2] & 0x01) << 1); - m_pad_latch[2] >>= 1; - break; - } - - if (LOG_JOY) - logerror("joy 0 read, val: %02x, pc: %04x\n", ret, space.device().safe_pc()); - + // bit 0 to controller port + ret |= m_ctrl1->read_bit0(); + + // expansion port bits (in the original FC, P2 controller was hooked to these lines + // too, so in principle some homebrew hardware modification could use the same + // connection with P1 controller too) + ret |= m_ctrl1->read_exp(0); + ret |= m_ctrl2->read_exp(0); + + // at the same time, we might have a standard joypad connected to the expansion port which + // shall be read as P3 (this is needed here to avoid implementing the expansion port as a + // different device compared to the standard control port... it might be changed later) + ret |= (m_exp->read_bit0() << 1); + // finally, read the expansion port as expected + ret |= m_exp->read_exp(0); return ret; } READ8_MEMBER(nes_state::fc_in1_r) { - int exp = m_io_exp->read(); - - // Some games expect bit 6 to be set because the last entry on the data bus shows up - // in the unused upper 3 bits, so typically a read from $4017 leaves 0x40 there. UINT8 ret = 0x40; - ret |= (m_pad_latch[1] & 0x01); - - // shift - m_pad_latch[1] >>= 1; - - - // EXP input - switch (exp & 0x0f) - { - case 0x01: // Lightgun - { - int x = m_zapper_latch[0][1]; // x-position - int y = m_zapper_latch[0][2]; // y-position - UINT32 pix, color_base; - - // get the pixel at the gun position - pix = m_ppu->get_pixel(x, y); - - // get the color base from the ppu - color_base = m_ppu->get_colorbase(); - - // check if the cursor is over a bright pixel - if ((pix == color_base + 0x20) || (pix == color_base + 0x30) || - (pix == color_base + 0x33) || (pix == color_base + 0x34)) - ret &= ~0x08; // sprite hit - else - ret |= 0x08; // no sprite hit - - // light gun trigger - ret |= (m_zapper_latch[0][0] << 4); - } - break; - - case 0x02: // FC Keyboard: rows of the keyboard matrix are read 4-bits at time and returned as bit1->bit4 - if (m_fck_scan < 9) - ret |= ~(((m_io_fckey[m_fck_scan]->read() >> (m_fck_mode * 4)) & 0x0f) << 1) & 0x1e; - else - ret |= 0x1e; - break; - - case 0x03: // Subor Keyboard: rows of the keyboard matrix are read 4-bits at time and returned as bit1->bit4 - if (m_fck_scan < 12) - ret |= ~(((m_io_subkey[m_fck_scan]->read() >> (m_fck_mode * 4)) & 0x0f) << 1) & 0x1e; - else - ret |= 0x1e; - break; - - case 0x04: // Arkanoid paddle - ret |= ((m_paddle_latch & 0x80) >> 6); // paddle data - m_paddle_latch <<= 1; - m_paddle_latch &= 0xff; - break; - - case 0x05: // family trainer - case 0x06: // family trainer - if (!BIT(m_ftrainer_scan, 0)) - { - // read low line: buttons 9,10,11,12 - for (int i = 0; i < 4; i++) - ret |= ((m_io_ftrainer[i]->read() & 0x01) << (1 + i)); - } - else if (!BIT(m_ftrainer_scan, 1)) - { - // read mid line: buttons 5,6,7,8 - for (int i = 0; i < 4; i++) - ret |= ((m_io_ftrainer[i]->read() & 0x02) << (1 + i)); - } - else if (!BIT(m_ftrainer_scan, 2)) - { - // read high line: buttons 1,2,3,4 - for (int i = 0; i < 4; i++) - ret |= ((m_io_ftrainer[i]->read() & 0x04) << (1 + i)); - } - break; - - case 0x07: // Mahjong Panel - ret |= ((m_mjpanel_latch & 0x01) << 1); - m_mjpanel_latch >>= 1; - break; - - case 0x08: // 'multitap' p4 - ret |= ((m_pad_latch[3] & 0x01) << 1); - m_pad_latch[3] >>= 1; - break; - } - - if (LOG_JOY) - logerror("joy 1 read, val: %02x, pc: %04x\n", ret, space.device().safe_pc()); - + // bit 0 to controller port + ret |= m_ctrl2->read_bit0(); + + // expansion port bits (in the original FC, P2 controller was hooked to these lines + // too, so in principle some homebrew hardware modification could use the same + // connection with P1 controller too) + ret |= m_ctrl1->read_exp(1); + ret |= m_ctrl2->read_exp(1); + + // finally, read the expansion port as expected (standard pad cannot be hooked as P4, so + // no read_bit0 here) + ret |= m_exp->read_exp(1); return ret; } WRITE8_MEMBER(nes_state::fc_in0_w) { - int cfg = m_io_ctrlsel->read(); - int exp = m_io_exp->read(); - - if (LOG_JOY) - logerror("joy write, val: %02x, pc: %04x\n", data, space.device().safe_pc()); - - // Check if lightgun has been chosen as input: if so, enable crosshair - timer_set(attotime::zero, TIMER_LIGHTGUN_TICK); - - // keyboards - if ((exp & 0x0f) == 0x02 || (exp & 0x0f) == 0x03) - { - // tape output (not fully tested) - if ((m_cassette->get_state() & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) - m_cassette->output(((data & 0x07) == 0x07) ? +1.0 : -1.0); - - if (BIT(data, 2)) // keyboard active - { - int lines = ((exp & 0x0f) == 0x02) ? 9 : 12; - UINT8 out = BIT(data, 1); // scan - - if (m_fck_mode && !out && ++m_fck_scan > lines) - m_fck_scan = 0; - - m_fck_mode = out; // access lower or upper 4 bits - - if (BIT(data, 0)) // reset - m_fck_scan = 0; - } - } - - // family trainer - if ((exp & 0x0f) == 0x05 || (exp & 0x0f) == 0x06) - { - // select raw to scan - m_ftrainer_scan = data & 0x07; - } - - if (data & 0x01) - return; - - // Toggling bit 0 high then low resets controllers - m_pad_latch[0] = 0; - m_pad_latch[1] = 0; - m_pad_latch[2] = 0; - m_pad_latch[3] = 0; - m_zapper_latch[0][0] = 0; - m_zapper_latch[0][1] = 0; - m_zapper_latch[0][2] = 0; - m_paddle_btn_latch = 0; - m_paddle_latch = 0; - m_mjpanel_latch = 0; - m_mic_obstruct = 0; - - // P1 inputs - switch (cfg & 0x000f) - { - case 0x01: // pad 1 - m_pad_latch[0] = m_io_pad[0]->read(); - break; - - case 0x02: // crazy climber (left stick) - m_pad_latch[0] = m_io_cc_left->read(); - break; - } - - // P2 inputs - switch ((cfg & 0x00f0) >> 4) - { - case 0x01: // pad 2 - m_pad_latch[1] = m_io_pad[1]->read(); - break; - - case 0x02: // crazy climber (right stick) - m_pad_latch[1] = m_io_cc_right->read(); - break; - - case 0x0f: // pad 2 old style with microphone instead of START/SELECT keys - // we only emulate obstruction of mic (when you blow or talk into it) - m_mic_obstruct = m_io_pad[1]->read() & 0x04; - m_pad_latch[1] = (m_io_pad[1]->read() & ~0x04); - break; - } - - // P3 & P4 inputs in Famicom (e.g. through Hori Twin Adapter or Hori 4 Players Adapter) - // are read in parallel with P1 & P2 (just using diff bits) - // P3 inputs - if ((exp & 0x0f) == 8 && (cfg & 0x0f00) == 0x0100) - m_pad_latch[2] = m_io_pad[2]->read(); // pad 3 - - // P4 inputs - if ((exp & 0x0f) == 8 && (cfg & 0xf000) == 0x1000) - m_pad_latch[3] = m_io_pad[3]->read(); // pad 4 - - - // EXP input - switch (exp & 0x0f) - { - case 0x01: // Lightgun - m_zapper_latch[0][0] = m_io_zapper2_t->read(); - m_zapper_latch[0][1] = m_io_zapper2_x->read(); - m_zapper_latch[0][2] = m_io_zapper2_y->read(); - break; - - case 0x02: // FC Keyboard - case 0x03: // Subor Keyboard - // these are scanned differently than other devices: - // writes to $4016 with bit2 set always update the - // line counter and writing bit0 set resets the counter - break; - - case 0x04: // Arkanoid paddle - m_paddle_btn_latch = m_io_paddle_btn->read(); - m_paddle_latch = (UINT8) (m_io_paddle->read() ^ 0xff); - break; - - case 0x05: // family trainer - case 0x06: // family trainer - // these are scanned differently than other devices: - // bit0-bit2 of writes to $4016 select the row to read - // from from the mat input "columns" - break; - - - case 0x07: // Mahjong Panel - if (data & 0xf8) - logerror("Error: Mahjong panel read with mux data %02x\n", (data & 0xfe)); - else - m_mjpanel_latch = m_io_mahjong[(data & 0xfe) >> 1]->read(); - break; - } - -} - - -void nes_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch (id) - { - case TIMER_ZAPPER_TICK: - if ((m_io_ctrlsel->read() & 0x000f) == 0x0002) - { - /* enable lightpen crosshair */ - crosshair_set_screen(machine(), 0, CROSSHAIR_SCREEN_ALL); - } - else - { - /* disable lightpen crosshair */ - crosshair_set_screen(machine(), 0, CROSSHAIR_SCREEN_NONE); - } - - if ((m_io_ctrlsel->read() & 0x00f0) == 0x0020) - { - /* enable lightpen crosshair */ - crosshair_set_screen(machine(), 1, CROSSHAIR_SCREEN_ALL); - } - else - { - /* disable lightpen crosshair */ - crosshair_set_screen(machine(), 1, CROSSHAIR_SCREEN_NONE); - } - break; - case TIMER_LIGHTGUN_TICK: - if ((m_io_exp->read() & 0x0f) == 0x01) - { - /* enable lightpen crosshair */ - crosshair_set_screen(machine(), 0, CROSSHAIR_SCREEN_ALL); - } - else - { - /* disable lightpen crosshair */ - crosshair_set_screen(machine(), 0, CROSSHAIR_SCREEN_NONE); - } - break; - default: - assert_always(FALSE, "Unknown id in nes_state::device_timer"); - } + m_ctrl1->write(data); + m_ctrl2->write(data); + m_exp->write(data); } @@ -711,3 +188,19 @@ DRIVER_INIT_MEMBER(nes_state,famicom) space.install_write_handler(0x4016, 0x4016, write8_delegate(FUNC(nes_state::fc_in0_w), this)); space.install_read_handler(0x4017, 0x4017, read8_delegate(FUNC(nes_state::fc_in1_r), this)); } + +NESCTRL_BRIGHTPIXEL_CB(nes_state::bright_pixel) +{ + // get the pixel at the gun position + UINT32 pix = m_ppu->get_pixel(x, y); + + // get the color base from the ppu + UINT32 color_base = m_ppu->get_colorbase(); + + // check if the cursor is over a bright pixel + if ((pix == color_base + 0x20) || (pix == color_base + 0x30) || + (pix == color_base + 0x33) || (pix == color_base + 0x34)) + return true; + else + return false; +} |