/* Megadrive / Genesis support PRELIMINARY The Megadrive / Genesis form the basis of a number of official Sega Arcade PCBs as well as a selection of bootlegs. Current Issues Timing is wrong -- DMA timing not emulated -- Sprite render timing incorrect -- Interrupt Timing Problems Known Problems -- g_lem / Lemmings (JU) (REV01) [!] Rasters are off -- g_drac / Bram Stoker's Dracula (U) [!] Doesn't work, Timing Sensisitve -- g_sscc / Sesame Street Counting Cafe (U) [!] Doesn't work -- g_fatr / Fatal Rewind (UE) [!] (and clones) Doesn't work. Timing Sensitive -- various Rasters off by 1 line, bottom line corrupt? bad frame timing? + more ToDo: Fix bugs - comprehensive testing! Add SegaCD Fix 32X support (not used by any arcade systems?) - this seems to require far greater sync and timing accuracy on rom / ram access than MAME can provide - split NTSC / PAL drivers - 36greatju: missing backup ram, has issues with golfer select due of that - bcracers: write to undefined PWM register? - fifa96 / nbajamte: dies on the gameplay, waiting for a comm change that never occurs; - marsch1: doesn't boot, Master / Slave communicates through SCI - nbajamte: missing I2C hookup, startup fails due of that (same I2C type as plain MD version); - nflquart: black screen, missing h irq? - sangoku4: black screen after the Sega logo - soulstar: OSD and player sprite isn't drawn; - tempo: intro is too fast, mostly noticeable with the PWM sound that cuts off too early when it gets to the title screen; - tmek: gameplay is clearly too fast - vrdxu: has 3d geometry bugs, caused by a SH-2 DRC bug; - vrdxu: crashes if you attempt to enter into main menu; - wwfraw: writes fb data to the cart area and expects it to be read back, kludging the cart area to be writeable makes the 32x gfxs to appear, why? - wwfwre: no 32x gfxs - xmen: black screen after that you choose the level, needs bare minimum SH-2 SCI support Add PicoDrive support (not arcade) Change SegaC2 to use the VDP emulation here Change System18 to use the VDP emulation here Known Non-Issues (confirmed on Real Genesis) Castlevania - Bloodlines (U) [!] - Pause text is missing on upside down level Blood Shot (E) (M4) [!] - corrupt texture in level 1 is correct... Cleanup / Rewrite notes: On SegaC2 the VDP never turns on the IRQ6 enable register This is because on the real PCB that line of the VDP isn't connected. Instead the IRQ6 interrupt is triggered by the line that is used to generate the Z80 interrupt on a standard genesis. (Once, every frame, on screenline 224) I should provide interrupt callback functions for each vdp line state change, which can be configured in the init rather than hardcoding them. 32x Marsch tests documentation (keep start pressed at start-up for individual tests): MD side check: #1 Communication Check #2 FM Bit #3 Irq Register #4 Bank Control Register #5 DREQ Control FULL bit #6 DREQ SRC Address #7 DREQ DST Address #8 DREQ SIZE Address #9 SEGA TV Register #10 H IRQ Vector #11 PWM Control Register #12 PWM Frequency Register #13 PWM Lch Pulse Width Register #14 PWM Rch Pulse Width Register #15 PWM MONO Pulse Width Register 32x side check: #16 SH-2 Master Communication Check #17 SH-2 Slave Communication Check #18 SH-2 Master FM Bit #19 SH-2 Slave FM Bit #20 SH-2 Master IRQ Mask Register #21 SH-2 Slave IRQ Mask Register #22 SH-2 Master H Counter Register #23 SH-2 Slave H Counter Register #24 SH-2 Master PWM Timer Register #25 SH-2 Slave PWM Timer Register #26 SH-2 Master PWM Cont. Register #27 SH-2 Slave PWM Cont. Register #28 SH-2 Master PWM Freq. Register #29 SH-2 Slave PWM Freq. Register #30 SH-2 Master PWM Lch Register #31 SH-2 Slave PWM Lch Register #32 SH-2 Master PWM Rch Register #33 SH-2 Slave PWM Rch Register #34 SH-2 Master PWM Mono Register #35 SH-2 Slave PWM Mono Register #36 SH-2 Master ROM Read Check #37 SH-2 Slave ROM Read Check #38 SH-2 Serial Communication (ERROR - returns a Timeout Error) MD & 32x check: #39 MD&SH-2 Master Communication #40 MD&SH-2 Slave Communication #41 MD&SH-2 Master FM Bit R/W #42 MD&SH-2 Slave FM Bit R/W #43 MD&SH-2 Master DREQ CTL #44 MD&SH-2 Slave DREQ CTL #45 MD&SH-2 Master DREQ SRC address #46 MD&SH-2 Slave DREQ SRC address #47 MD&SH-2 Master DREQ DST address #48 MD&SH-2 Slave DREQ DST address #49 MD&SH-2 Master DREQ SIZE address #50 MD&SH-2 Slave DREQ SIZE address #51 SH-2 Master V IRQ #52 SH-2 Slave V IRQ #53 SH2 Master H IRQ (MD 0) #54 SH2 Slave H IRQ (MD 0) #55 SH2 Master H IRQ (MD 1) #56 SH2 Slave H IRQ (MD 1) #57 SH2 Master H IRQ (MD 2) #58 SH2 Slave H IRQ (MD 2) MD VDP check: #59 Bitmap Mode Register #60 Shift Register #61 Auto Fill Length Register #62 Auto Fill Start Address Register #63 V Blank BIT #64 H Blank BIT #65 Palette Enable BIT SH-2 VDP check: #66 Frame Swap BIT #67 SH-2 Master Bitmap MD #68 SH-2 Slave Bitmap MD #69 SH-2 Master Shift #70 SH-2 Slave Shift #71 SH-2 Master Fill SIZE #72 SH-2 Slave Fill SIZE #73 SH-2 Master Fill START #74 SH-2 Slave Fill START #75 SH-2 Master V Blank Bit #76 SH-2 Slave V Blank Bit #77 SH-2 Master H Blank Bit #78 SH-2 Slave H Blank Bit #79 SH-2 Master Palette Enable Bit #80 SH-2 Slave Palette Enable Bit #81 SH-2 Master Frame Swap Bit #82 SH-2 Slave Frame Swap Bit Framebuffer Check: #83 MD Frame Buffer 0 #84 MD Frame Buffer 1 #85 SH-2 Master Frame Buffer 0 #86 SH-2 Slave Frame Buffer 0 #87 SH-2 Master Frame Buffer 1 #88 SH-2 Slave Frame Buffer 1 #89 MD Frame Buffer 0 Overwrite #90 MD Frame Buffer 1 Overwrite #91 MD Frame Buffer 0 Byte Write #92 MD Frame Buffer 1 Byte Write #93 SH-2 Master Frame Buffer 0 Overwrite #94 SH-2 Slave Frame Buffer 0 Overwrite #95 SH-2 Master Frame Buffer 1 Overwrite #96 SH-2 Slave Frame Buffer 1 Overwrite #97 SH-2 Master Frame Buffer 0 Byte Write #98 SH-2 Slave Frame Buffer 0 Byte Write #99 SH-2 Master Frame Buffer 1 Byte Write #100 SH-2 Slave Frame Buffer 1 Byte Write #101 MD Frame Buffer 0 Fill Data #102 MD Frame Buffer 1 Fill Data #103 MD Frame Buffer 0 Fill Length & Address #104 MD Frame Buffer 1 Fill Length & Address #105 SH-2 Master Frame Buffer 0 Fill Data #106 SH-2 Slave Frame Buffer 0 Fill Data #107 SH-2 Master Frame Buffer 1 Fill Data #108 SH-2 Slave Frame Buffer 1 Fill Data #109 SH-2 Master Frame Buffer 0 Fill Address #110 SH-2 Slave Frame Buffer 0 Fill Address #111 SH-2 Master Frame Buffer 1 Fill Address #112 SH-2 Slave Frame Buffer 1 Fill Address #113 MD Palette R/W (Blank Mode) #114 MD Palette R/W (Display Mode) #115 MD Palette R/W (Fill Mode) #116 SH-2 Master Palette R/W (Blank Mode) #117 SH-2 Slave Palette R/W (Blank Mode) #118 SH-2 Master Palette R/W (Display Mode) #119 SH-2 Slave Palette R/W (Display Mode) #120 SH-2 Master Palette R/W (Fill Mode) #121 SH-2 Slave Palette R/W (Fill Mode) MD or SH-2 DMA check: #122 SH-2 Master CPU Write DMA (68S) (ERROR) #123 SH-2 Slave CPU Write DMA (68S) (ERROR) #124 MD ROM to VRAM DMA (asserts after this) ----- #127 SH-2 Master ROM to SDRAM DMA #128 SH-2 Slave ROM to SDRAM DMA #129 SH-2 Master ROM to Frame DMA #130 SH-2 Slave ROM to Frame DMA #131 SH-2 Master SDRAM to Frame DMA #132 SH-2 Slave SDRAM to Frame DMA #133 SH-2 Master Frame to SDRAM DMA #134 SH-2 Slave Frame to SDRAM DMA Sound Test (these don't explicitly fails): #135 MD 68k Monaural Sound #136 MD 68k L Sound #137 MD 68k R Sound #138 MD 68k L -> R Sound #139 MD 68k R -> L Sound #140 SH-2 Master Monaural Sound #141 SH-2 Master L Sound #142 SH-2 Master R Sound #143 SH-2 Master L -> R Pan #144 SH-2 Master R -> L Pan #145 SH-2 Slave Monaural Sound #146 SH-2 Slave L Sound #147 SH-2 Slave R Sound #148 SH-2 Slave L -> R Pan #149 SH-2 Slave R -> L Pan #150 SH-2 Master PWM Interrupt #151 SH-2 Slave PWM Interrupt #152 SH-2 Master PWM DMA Write (!) #153 SH-2 Slave PWM DMA Write (!) #154 Z80 PWM Monaural Sound (!) #155 Z80 PWM L Sound (!) #156 Z80 PWM R Sound (!) GFX check (these don't explicitly fails): #157 Direct Color Mode #158 Packed Pixel Mode #159 Runlength Mode #160 Runlength Mode #161 Runlength Mode ---------------------------- SegaCD notes ---------------------------- the use of the MAME tilemap system for the SegaCD 'Roz tilemap' isn't intended as a long-term solution. (In reality it's not a displayable tilemap anyway, just a source buffer which has a tilemap-like structure, from which data is copied) */ #include "emu.h" #include "coreutil.h" #include "cpu/z80/z80.h" #include "sound/sn76496.h" #include "sound/2612intf.h" #include "sound/upd7759.h" #include "sound/fm.h" #include "sound/dac.h" #include "cpu/m68000/m68000.h" #include "includes/megadriv.h" #include "cpu/sh2/sh2.h" #include "cpu/sh2/sh2comn.h" #include "sound/cdda.h" #ifdef MESS #include "devices/chd_cd.h" #endif #include "sound/rf5c68.h" #define MEGADRIV_VDP_VRAM(address) megadrive_vdp_vram[(address)&0x7fff] #define HAZE_MD 0 // to make appear / disappear the Region DipSwitch /* timing details */ static int megadriv_framerate; static int megadrive_total_scanlines; static int megadrive_visible_scanlines; static int megadrive_irq6_scanline; //int megadrive_irq6_hpos = 320; static int megadrive_z80irq_scanline; //int megadrive_z80irq_hpos = 320; static int megadrive_imode = 0; static int megadrive_imode_odd_frame = 0; static int megadrive_vblank_flag = 0; static int megadrive_irq6_pending = 0; static int megadrive_irq4_pending = 0; /* 32x! */ static cpu_device *_32x_master_cpu; static cpu_device *_32x_slave_cpu; static int _32x_is_connected; static int sh2_are_running; static int _32x_adapter_enabled; static int _32x_access_auth; static int _32x_screenshift; static int _32x_videopriority; static int _32x_displaymode; static int _32x_240mode; static UINT16 _32x_68k_a15104_reg; static int sh2_master_vint_enable, sh2_slave_vint_enable; static int sh2_master_hint_enable, sh2_slave_hint_enable; static int sh2_master_cmdint_enable, sh2_slave_cmdint_enable; static int sh2_master_pwmint_enable, sh2_slave_pwmint_enable; static int sh2_hint_in_vbl; static int sh2_master_vint_pending; static int sh2_slave_vint_pending; static int _32x_fb_swap; static int _32x_hcount_reg,_32x_hcount_compare_val; void _32x_check_irqs(running_machine* machine); #define SH2_VRES_IRQ_LEVEL 14 #define SH2_VINT_IRQ_LEVEL 12 #define SH2_HINT_IRQ_LEVEL 10 #define SH2_CINT_IRQ_LEVEL 8 #define SH2_PINT_IRQ_LEVEL 6 // Fifa96 needs the CPUs swapped for the gameplay to enter due to some race conditions // when using the DRC core. Needs further investigation, the non-DRC core works either // way #define _32X_SWAP_MASTER_SLAVE_HACK static UINT16* _32x_dram0; static UINT16* _32x_dram1; static UINT16 *_32x_display_dram, *_32x_access_dram; static UINT16* _32x_palette; static UINT16* _32x_palette_lookup; /* SegaCD! */ static cpu_device *_segacd_68k_cpu; static emu_timer *segacd_gfx_conversion_timer; static emu_timer *segacd_dmna_ret_timer; static emu_timer *segacd_irq3_timer; static emu_timer *segacd_hock_timer; static UINT8 hock_cmd; static int segacd_wordram_mapped = 0; static TIMER_CALLBACK( segacd_irq3_timer_callback ); /* SVP (virtua racing) */ static cpu_device *_svp_cpu; static cpu_device *_genesis_snd_z80_cpu; int segac2_bg_pal_lookup[4]; int segac2_sp_pal_lookup[4]; // hacks for C2 int genvdp_use_cram = 0; // c2 uses it's own palette ram int genesis_always_irq6 = 0; // c2 never enables the irq6, different source?? int genesis_other_hacks = 0; // misc hacks INLINE UINT16 get_hposition(void); static UINT8* sprite_renderline; static UINT8* highpri_renderline; static UINT32* video_renderline; UINT16* megadrive_vdp_palette_lookup; UINT16* megadrive_vdp_palette_lookup_sprite; // for C2 UINT16* megadrive_vdp_palette_lookup_shadow; UINT16* megadrive_vdp_palette_lookup_highlight; UINT16* megadrive_ram; static UINT8 megadrive_vram_fill_pending = 0; static UINT16 megadrive_vram_fill_length = 0; static int genesis_scanline_counter = 0; static int megadrive_sprite_collision = 0; static int megadrive_region_export; static int megadrive_region_pal; static int megadrive_max_hposition; static timer_device* frame_timer; static timer_device* scanline_timer; static timer_device* irq6_on_timer; static timer_device* irq4_on_timer; static bitmap_t* render_bitmap; //emu_timer* vblankirq_off_timer; /* Sega CD stuff */ static int sega_cd_connected = 0x00; static UINT16 segacd_irq_mask; static UINT8 segacd_cdd_rx[10]; static UINT8 segacd_cdd_tx[10]; static struct { UINT8 buffer[5]; UINT8 ctrl; UINT8 scd_status; // SCSI status? }segacd_cdd; #ifdef MESS static struct { UINT32 current_frame; UINT32 end_frame; UINT32 last_frame; UINT8 first_track; UINT8 last_track; cdrom_file *cd; const cdrom_toc* toc; }segacd; #endif static void segacd_mark_tiles_dirty(running_machine* machine, int offset); #ifdef UNUSED_FUNCTION /* taken from segaic16.c */ /* doesn't seem to meet my needs, not used */ static UINT16 read_next_instruction(address_space *space) { static UINT8 recurse = 0; UINT16 result; /* Unmapped memory returns the last word on the data bus, which is almost always the opcode */ /* of the next instruction due to prefetch; however, since we may be encrypted, we actually */ /* need to return the encrypted opcode, not the last decrypted data. */ /* Believe it or not, this is actually important for Cotton, which has the following evil */ /* code: btst #0,$7038f7, which tests the low bit of an unmapped address, which thus should */ /* return the prefetched value. */ /* prevent recursion */ if (recurse) return 0xffff; /* read original encrypted memory at that address */ recurse = 1; result = space->read_word(cpu_get_pc(space->cpu)); recurse = 0; return result; } #endif static struct genesis_z80_vars { int z80_is_reset; int z80_has_bus; UINT32 z80_bank_addr; UINT8* z80_prgram; } genz80; static void megadriv_z80_bank_w(UINT16 data) { genz80.z80_bank_addr = ( ( genz80.z80_bank_addr >> 1 ) | ( data << 23 ) ) & 0xff8000; } static WRITE16_HANDLER( megadriv_68k_z80_bank_write ) { //logerror("%06x: 68k writing bit to bank register %01x\n", cpu_get_pc(space->cpu),data&0x01); megadriv_z80_bank_w(data&0x01); } static WRITE8_HANDLER(megadriv_z80_z80_bank_w) { //logerror("%04x: z80 writing bit to bank register %01x\n", cpu_get_pc(space->cpu),data&0x01); megadriv_z80_bank_w(data&0x01); } static READ16_HANDLER( megadriv_68k_check_z80_bus ); static WRITE16_HANDLER(megadriv_68k_req_z80_bus); static READ16_HANDLER( megadriv_68k_read_z80_ram ); static WRITE16_HANDLER( megadriv_68k_write_z80_ram ); static WRITE16_HANDLER( megadriv_68k_req_z80_reset ); /* The VDP occupies addresses C00000h to C0001Fh. C00000h - Data port (8=r/w, 16=r/w) C00002h - Data port (mirror) C00004h - Control port (8=r/w, 16=r/w) C00006h - Control port (mirror) C00008h - HV counter (8/16=r/o) C0000Ah - HV counter (mirror) C0000Ch - HV counter (mirror) C0000Eh - HV counter (mirror) C00011h - SN76489 PSG (8=w/o) C00013h - SN76489 PSG (mirror) C00015h - SN76489 PSG (mirror) C00017h - SN76489 PSG (mirror) */ static int megadrive_vdp_command_pending; // 2nd half of command pending.. static UINT16 megadrive_vdp_command_part1; static UINT16 megadrive_vdp_command_part2; static UINT8 megadrive_vdp_code; static UINT16 megadrive_vdp_address; static UINT16 megadrive_vdp_register[0x20]; static UINT16* megadrive_vdp_vram; static UINT16* megadrive_vdp_cram; static UINT16* megadrive_vdp_vsram; /* The VDP keeps a 0x400 byte on-chip cache of the Sprite Attribute Table to speed up processing */ static UINT16* megadrive_vdp_internal_sprite_attribute_table; /* $00 - Mode Set Register No. 1 ----------------------------- d7 - No effect d6 - No effect d5 - No effect d4 - IE1 (Horizontal interrupt enable) d3 - 1= Invalid display setting d2 - Palette select d1 - M3 (HV counter latch enable) d0 - Display disable */ #define MEGADRIVE_REG0_UNUSED ((megadrive_vdp_register[0x00]&0xc0)>>6) #define MEGADRIVE_REG0_BLANK_LEFT ((megadrive_vdp_register[0x00]&0x20)>>5) // like SMS, not used by any commercial games? #define MEGADRIVE_REG0_IRQ4_ENABLE ((megadrive_vdp_register[0x00]&0x10)>>4) #define MEGADRIVE_REG0_INVALID_MODE ((megadrive_vdp_register[0x00]&0x08)>>3) // invalid display mode, unhandled #define MEGADRIVE_REG0_SPECIAL_PAL ((megadrive_vdp_register[0x00]&0x04)>>2) // strange palette mode, unhandled #define MEGADRIVE_REG0_HVLATCH_ENABLE ((megadrive_vdp_register[0x00]&0x02)>>1) // HV Latch, used by lightgun games #define MEGADRIVE_REG0_DISPLAY_DISABLE ((megadrive_vdp_register[0x00]&0x01)>>0) /* $01 - Mode Set Register No. 2 ----------------------------- d7 - TMS9918 / Genesis display select d6 - DISP (Display Enable) d5 - IE0 (Vertical Interrupt Enable) d4 - M1 (DMA Enable) d3 - M2 (PAL / NTSC) d2 - SMS / Genesis display select d1 - 0 (No effect) d0 - 0 (See notes) */ #define MEGADRIVE_REG01_TMS9918_SELECT ((megadrive_vdp_register[0x01]&0x80)>>7) #define MEGADRIVE_REG01_DISP_ENABLE ((megadrive_vdp_register[0x01]&0x40)>>6) #define MEGADRIVE_REG01_IRQ6_ENABLE ((megadrive_vdp_register[0x01]&0x20)>>5) #define MEGADRIVE_REG01_DMA_ENABLE ((megadrive_vdp_register[0x01]&0x10)>>4) #define MEGADRIVE_REG01_240_LINE ((megadrive_vdp_register[0x01]&0x08)>>3) #define MEGADRIVE_REG01_SMS_SELECT ((megadrive_vdp_register[0x01]&0x04)>>2) #define MEGADRIVE_REG01_UNUSED ((megadrive_vdp_register[0x01]&0x02)>>1) #define MEGADRIVE_REG01_STRANGE_VIDEO ((megadrive_vdp_register[0x01]&0x01)>>0) // unhandled, does strange things to the display #define MEGADRIVE_REG02_UNUSED1 ((megadrive_vdp_register[0x02]&0xc0)>>6) #define MEGADRIVE_REG02_PATTERN_ADDR_A ((megadrive_vdp_register[0x02]&0x38)>>3) #define MEGADRIVE_REG02_UNUSED2 ((megadrive_vdp_register[0x02]&0x07)>>0) #define MEGADRIVE_REG03_UNUSED1 ((megadrive_vdp_register[0x03]&0xc0)>>6) #define MEGADRIVE_REG03_PATTERN_ADDR_W ((megadrive_vdp_register[0x03]&0x3e)>>1) #define MEGADRIVE_REG03_UNUSED2 ((megadrive_vdp_register[0x03]&0x01)>>0) #define MEGADRIVE_REG04_UNUSED ((megadrive_vdp_register[0x04]&0xf8)>>3) #define MEGADRIVE_REG04_PATTERN_ADDR_B ((megadrive_vdp_register[0x04]&0x07)>>0) #define MEGADRIVE_REG05_UNUSED ((megadrive_vdp_register[0x05]&0x80)>>7) #define MEGADRIVE_REG05_SPRITE_ADDR ((megadrive_vdp_register[0x05]&0x7f)>>0) /* 6? */ #define MEGADRIVE_REG07_UNUSED ((megadrive_vdp_register[0x07]&0xc0)>>6) #define MEGADRIVE_REG07_BGCOLOUR ((megadrive_vdp_register[0x07]&0x3f)>>0) /* 8? */ /* 9? */ #define MEGADRIVE_REG0A_HINT_VALUE ((megadrive_vdp_register[0x0a]&0xff)>>0) #define MEGADRIVE_REG0B_UNUSED ((megadrive_vdp_register[0x0b]&0xf0)>>4) #define MEGADRIVE_REG0B_IRQ2_ENABLE ((megadrive_vdp_register[0x0b]&0x08)>>3) #define MEGADRIVE_REG0B_VSCROLL_MODE ((megadrive_vdp_register[0x0b]&0x04)>>2) #define MEGADRIVE_REG0B_HSCROLL_MODE ((megadrive_vdp_register[0x0b]&0x03)>>0) #define MEGADRIVE_REG0C_RS0 ((megadrive_vdp_register[0x0c]&0x80)>>7) #define MEGADRIVE_REG0C_UNUSED1 ((megadrive_vdp_register[0x0c]&0x40)>>6) #define MEGADRIVE_REG0C_SPECIAL ((megadrive_vdp_register[0x0c]&0x20)>>5) #define MEGADRIVE_REG0C_UNUSED2 ((megadrive_vdp_register[0x0c]&0x10)>>4) #define MEGADRIVE_REG0C_SHADOW_HIGLIGHT ((megadrive_vdp_register[0x0c]&0x08)>>3) #define MEGADRIVE_REG0C_INTERLEAVE ((megadrive_vdp_register[0x0c]&0x06)>>1) #define MEGADRIVE_REG0C_RS1 ((megadrive_vdp_register[0x0c]&0x01)>>0) #define MEGADRIVE_REG0D_UNUSED ((megadrive_vdp_register[0x0d]&0xc0)>>6) #define MEGADRIVE_REG0D_HSCROLL_ADDR ((megadrive_vdp_register[0x0d]&0x3f)>>0) /* e? */ #define MEGADRIVE_REG0F_AUTO_INC ((megadrive_vdp_register[0x0f]&0xff)>>0) #define MEGADRIVE_REG10_UNUSED1 ((megadrive_vdp_register[0x10]&0xc0)>>6) #define MEGADRIVE_REG10_VSCROLL_SIZE ((megadrive_vdp_register[0x10]&0x30)>>4) #define MEGADRIVE_REG10_UNUSED2 ((megadrive_vdp_register[0x10]&0x0c)>>2) #define MEGADRIVE_REG10_HSCROLL_SIZE ((megadrive_vdp_register[0x10]&0x03)>>0) #define MEGADRIVE_REG11_WINDOW_RIGHT ((megadrive_vdp_register[0x11]&0x80)>>7) #define MEGADRIVE_REG11_UNUSED ((megadrive_vdp_register[0x11]&0x60)>>5) #define MEGADRIVE_REG11_WINDOW_HPOS ((megadrive_vdp_register[0x11]&0x1f)>>0) #define MEGADRIVE_REG12_WINDOW_DOWN ((megadrive_vdp_register[0x12]&0x80)>>7) #define MEGADRIVE_REG12_UNUSED ((megadrive_vdp_register[0x12]&0x60)>>5) #define MEGADRIVE_REG12_WINDOW_VPOS ((megadrive_vdp_register[0x12]&0x1f)>>0) #define MEGADRIVE_REG13_DMALENGTH1 ((megadrive_vdp_register[0x13]&0xff)>>0) #define MEGADRIVE_REG14_DMALENGTH2 ((megadrive_vdp_register[0x14]&0xff)>>0) #define MEGADRIVE_REG15_DMASOURCE1 ((megadrive_vdp_register[0x15]&0xff)>>0) #define MEGADRIVE_REG16_DMASOURCE2 ((megadrive_vdp_register[0x16]&0xff)>>0) #define MEGADRIVE_REG17_DMASOURCE3 ((megadrive_vdp_register[0x17]&0xff)>>0) #define MEGADRIVE_REG17_DMATYPE ((megadrive_vdp_register[0x17]&0xc0)>>6) #define MEGADRIVE_REG17_UNUSED ((megadrive_vdp_register[0x17]&0x3f)>>0) static void vdp_vram_write(UINT16 data) { UINT16 sprite_base_address = MEGADRIVE_REG0C_RS1?((MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9):((MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9); int spritetable_size = MEGADRIVE_REG0C_RS1?0x400:0x200; int lowlimit = sprite_base_address; int highlimit = sprite_base_address+spritetable_size; if (megadrive_vdp_address&1) { data = ((data&0x00ff)<<8)|((data&0xff00)>>8); } MEGADRIV_VDP_VRAM(megadrive_vdp_address>>1) = data; /* The VDP stores an Internal copy of any data written to the Sprite Attribute Table. This data is _NOT_ invalidated when the Sprite Base Address changes, thus allowing for some funky effects, as used by Castlevania Bloodlines Stage 6-3 */ if (megadrive_vdp_address>=lowlimit && megadrive_vdp_address>1] = data; } megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address &= 0xffff; } static void vdp_vsram_write(UINT16 data) { megadrive_vdp_vsram[(megadrive_vdp_address&0x7e)>>1] = data; //logerror("Wrote to VSRAM addr %04x data %04x\n",megadrive_vdp_address&0xfffe,megadrive_vdp_vsram[megadrive_vdp_address>>1]); megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address &=0xffff; } static void write_cram_value(running_machine *machine, int offset, int data) { megadrive_vdp_cram[offset] = data; //logerror("Wrote to CRAM addr %04x data %04x\n",megadrive_vdp_address&0xfffe,megadrive_vdp_cram[megadrive_vdp_address>>1]); if (genvdp_use_cram) { int r,g,b; r = ((data >> 1)&0x07); g = ((data >> 5)&0x07); b = ((data >> 9)&0x07); palette_set_color_rgb(machine,offset,pal3bit(r),pal3bit(g),pal3bit(b)); megadrive_vdp_palette_lookup[offset] = (b<<2) | (g<<7) | (r<<12); megadrive_vdp_palette_lookup_sprite[offset] = (b<<2) | (g<<7) | (r<<12); megadrive_vdp_palette_lookup_shadow[offset] = (b<<1) | (g<<6) | (r<<11); megadrive_vdp_palette_lookup_highlight[offset] = ((b|0x08)<<1) | ((g|0x08)<<6) | ((r|0x08)<<11); } } static void vdp_cram_write(running_machine *machine, UINT16 data) { int offset; offset = (megadrive_vdp_address&0x7e)>>1; write_cram_value(machine, offset,data); megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address &=0xffff; } static void megadriv_vdp_data_port_w(running_machine *machine, int data) { megadrive_vdp_command_pending = 0; /* 0000b : VRAM read 0001b : VRAM write 0011b : CRAM write 0100b : VSRAM read 0101b : VSRAM write 1000b : CRAM read */ // logerror("write to vdp data port %04x with code %04x, write address %04x\n",data, megadrive_vdp_code, megadrive_vdp_address ); if (megadrive_vram_fill_pending) { int count; megadrive_vdp_address&=0xffff; if (megadrive_vdp_address&1) { MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1))&0xff00) | (data&0x00ff); } else { MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1))&0x00ff) | ((data&0x00ff)<<8); } for (count=0;count<=megadrive_vram_fill_length;count++) // <= for james pond 3 { if (megadrive_vdp_address&1) { MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1))&0x00ff) | (data&0xff00); } else { MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1)) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address>>1))&0xff00) | ((data&0xff00)>>8); } megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; } megadrive_vdp_register[0x13] = 0; megadrive_vdp_register[0x14] = 0; // megadrive_vdp_register[0x15] = (source>>1) & 0xff; // megadrive_vdp_register[0x16] = (source>>9) & 0xff; // megadrive_vdp_register[0x17] = (source>>17) & 0xff; } else { switch (megadrive_vdp_code & 0x000f) { case 0x0000: logerror("Attempting to WRITE to DATA PORT in VRAM READ MODE\n"); break; case 0x0001: vdp_vram_write(data); break; case 0x0003: vdp_cram_write(machine, data); break; case 0x0004: logerror("Attempting to WRITE to DATA PORT in VSRAM READ MODE\n"); break; case 0x0005: vdp_vsram_write(data); break; case 0x0008: logerror("Attempting to WRITE to DATA PORT in CRAM READ MODE\n"); break; default: logerror("Attempting to WRITE to DATA PORT in #UNDEFINED# MODE %1x %04x\n",megadrive_vdp_code&0xf, data); break; } } } static void megadrive_vdp_set_register(running_machine *machine, int regnum, UINT8 value) { megadrive_vdp_register[regnum] = value; /* We need special handling for the IRQ enable registers, some games turn off the irqs before they are taken, delaying them until the IRQ is turned back on */ if (regnum == 0x00) { //mame_printf_debug("setting reg 0, irq enable is now %d\n",MEGADRIVE_REG0_IRQ4_ENABLE); if (megadrive_irq4_pending) { if (MEGADRIVE_REG0_IRQ4_ENABLE) cputag_set_input_line(machine, "maincpu", 4, HOLD_LINE); else cputag_set_input_line(machine, "maincpu", 4, CLEAR_LINE); } /* ??? Fatal Rewind needs this but I'm not sure it's accurate behavior it causes flickering in roadrash */ // megadrive_irq6_pending = 0; // megadrive_irq4_pending = 0; } if (regnum == 0x01) { if (megadrive_irq6_pending) { if (MEGADRIVE_REG01_IRQ6_ENABLE ) cputag_set_input_line(machine, "maincpu", 6, HOLD_LINE); else cputag_set_input_line(machine, "maincpu", 6, CLEAR_LINE); } /* ??? */ // megadrive_irq6_pending = 0; // megadrive_irq4_pending = 0; } // if (regnum == 0x0a) // mame_printf_debug("Set HINT Reload Register to %d on scanline %d\n",value, genesis_scanline_counter); // mame_printf_debug("%s: Setting VDP Register #%02x to %02x\n",cpuexec_describe_context(machine), regnum,value); } static void update_megadrive_vdp_code_and_address(void) { megadrive_vdp_code = ((megadrive_vdp_command_part1 & 0xc000) >> 14) | ((megadrive_vdp_command_part2 & 0x00f0) >> 2); megadrive_vdp_address = ((megadrive_vdp_command_part1 & 0x3fff) >> 0) | ((megadrive_vdp_command_part2 & 0x0003) << 14); } static UINT16 (*vdp_get_word_from_68k_mem)(running_machine *machine, UINT32 source); static UINT16 vdp_get_word_from_68k_mem_default(running_machine *machine, UINT32 source) { // should we limit the valid areas here? // how does this behave with the segacd etc? // note, the RV bit on 32x is important for this to work, because it causes a normal cart mapping - see tempo address_space *space68k = machine->device("maincpu")->space(); //printf("vdp_get_word_from_68k_mem_default %08x\n", source); if (( source >= 0x000000 ) && ( source <= 0x3fffff )) { if (_svp_cpu != NULL) { source -= 2; // the SVP introduces some kind of DMA 'lag', which we have to compensate for, this is obvious even on gfx DMAd from ROM (the Speedometer) } // likewise segaCD, at least when reading wordram? // we might need to check what mode we're in here.. if (segacd_wordram_mapped) { source -= 2; } return space68k->read_word(source); } else if (( source >= 0xe00000 ) && ( source <= 0xffffff )) { return space68k->read_word(source); } else { printf("DMA Read unmapped %06x\n",source); return mame_rand(machine); } } /* Table from Charles Macdonald DMA Mode Width Display Transfer Count ----------------------------------------------------- 68K > VDP 32-cell Active 16 Blanking 167 40-cell Active 18 Blanking 205 VRAM Fill 32-cell Active 15 Blanking 166 40-cell Active 17 Blanking 204 VRAM Copy 32-cell Active 8 Blanking 83 40-cell Active 9 Blanking 102 */ /* Note, In reality this transfer is NOT instant, the 68k isn't paused as the 68k address bus isn't accessed */ /* Wani Wani World, James Pond 3, Pirates Gold! */ static void megadrive_do_insta_vram_copy(UINT32 source, UINT16 length) { int x; for (x=0;x>1)&0x00ff; else source_byte = (MEGADRIV_VDP_VRAM((source&0xffff)>>1)&0xff00)>>8; if (megadrive_vdp_address&1) { MEGADRIV_VDP_VRAM((megadrive_vdp_address&0xffff)>>1) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address&0xffff)>>1)&0xff00) | source_byte; } else { MEGADRIV_VDP_VRAM((megadrive_vdp_address&0xffff)>>1) = (MEGADRIV_VDP_VRAM((megadrive_vdp_address&0xffff)>>1)&0x00ff) | (source_byte<<8); } source++; megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; } } /* Instant, but we pause the 68k a bit */ static void megadrive_do_insta_68k_to_vram_dma(running_machine *machine, UINT32 source,int length) { int count; if (length==0x00) length = 0xffff; /* This is a hack until real DMA timings are implemented */ cpu_spinuntil_time(machine->device("maincpu"), ATTOTIME_IN_NSEC(length * 1000 / 3500)); for (count = 0;count<(length>>1);count++) { vdp_vram_write(vdp_get_word_from_68k_mem(machine, source)); source+=2; if (source>0xffffff) source = 0xe00000; } megadrive_vdp_address&=0xffff; megadrive_vdp_register[0x13] = 0; megadrive_vdp_register[0x14] = 0; megadrive_vdp_register[0x15] = (source>>1) & 0xff; megadrive_vdp_register[0x16] = (source>>9) & 0xff; megadrive_vdp_register[0x17] = (source>>17) & 0xff; } static void megadrive_do_insta_68k_to_cram_dma(running_machine *machine,UINT32 source,UINT16 length) { int count; if (length==0x00) length = 0xffff; for (count = 0;count<(length>>1);count++) { //if (megadrive_vdp_address>=0x80) return; // abandon write_cram_value(machine, (megadrive_vdp_address&0x7e)>>1, vdp_get_word_from_68k_mem(machine, source)); source+=2; if (source>0xffffff) source = 0xfe0000; megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; } megadrive_vdp_register[0x13] = 0; megadrive_vdp_register[0x14] = 0; megadrive_vdp_register[0x15] = (source>>1) & 0xff; megadrive_vdp_register[0x16] = (source>>9) & 0xff; megadrive_vdp_register[0x17] = (source>>17) & 0xff; } static void megadrive_do_insta_68k_to_vsram_dma(running_machine *machine,UINT32 source,UINT16 length) { int count; if (length==0x00) length = 0xffff; for (count = 0;count<(length>>1);count++) { if (megadrive_vdp_address>=0x80) return; // abandon megadrive_vdp_vsram[(megadrive_vdp_address&0x7e)>>1] = vdp_get_word_from_68k_mem(machine, source); source+=2; if (source>0xffffff) source = 0xfe0000; megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; } megadrive_vdp_register[0x13] = 0; megadrive_vdp_register[0x14] = 0; megadrive_vdp_register[0x15] = (source>>1) & 0xff; megadrive_vdp_register[0x16] = (source>>9) & 0xff; megadrive_vdp_register[0x17] = (source>>17) & 0xff; } /* This can be simplified quite a lot.. */ static void handle_dma_bits(running_machine *machine) { if (megadrive_vdp_code&0x20) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0xff)<<16))<<1; length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1; // mame_printf_debug("%s 68k DMAtran set source %06x length %04x dest %04x enabled %01x code %02x %02x\n", cpuexec_describe_context(machine), source, length, megadrive_vdp_address,MEGADRIVE_REG01_DMA_ENABLE, megadrive_vdp_code,MEGADRIVE_REG0F_AUTO_INC); } if (megadrive_vdp_code==0x20) { mame_printf_debug("DMA bit set 0x20 but invalid??\n"); } else if (megadrive_vdp_code==0x21 || megadrive_vdp_code==0x31) /* 0x31 used by tecmo cup */ { if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1; length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1; /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */ //mame_printf_debug("68k->VRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, megadrive_vdp_address,MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) megadrive_do_insta_68k_to_vram_dma(machine,source,length); } else if (MEGADRIVE_REG17_DMATYPE==0x2) { //mame_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) { megadrive_vram_fill_pending = 1; megadrive_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); } } else if (MEGADRIVE_REG17_DMATYPE==0x3) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8)); // source (byte offset) length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); // length in bytes //mame_printf_debug("setting vram copy mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) megadrive_do_insta_vram_copy(source, length); } } else if (megadrive_vdp_code==0x23) { if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1; length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1; /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */ //mame_printf_debug("68k->CRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, megadrive_vdp_address,MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) megadrive_do_insta_68k_to_cram_dma(machine,source,length); } else if (MEGADRIVE_REG17_DMATYPE==0x2) { //mame_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) { megadrive_vram_fill_pending = 1; megadrive_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); } } else if (MEGADRIVE_REG17_DMATYPE==0x3) { mame_printf_debug("setting vram copy (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); } } else if (megadrive_vdp_code==0x25) { if (MEGADRIVE_REG17_DMATYPE==0x0 || MEGADRIVE_REG17_DMATYPE==0x1) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8) | ((MEGADRIVE_REG17_DMASOURCE3&0x7f)<<16))<<1; length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8))<<1; /* The 68k is frozen during this transfer, it should be safe to throw a few cycles away and do 'instant' DMA because the 68k can't detect it being in progress (can the z80?) */ //mame_printf_debug("68k->VSRAM DMA transfer source %06x length %04x dest %04x enabled %01x\n", source, length, megadrive_vdp_address,MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) megadrive_do_insta_68k_to_vsram_dma(machine,source,length); } else if (MEGADRIVE_REG17_DMATYPE==0x2) { //mame_printf_debug("vram fill length %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) { megadrive_vram_fill_pending = 1; megadrive_vram_fill_length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); } } else if (MEGADRIVE_REG17_DMATYPE==0x3) { mame_printf_debug("setting vram copy (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); } } else if (megadrive_vdp_code==0x30) { if (MEGADRIVE_REG17_DMATYPE==0x0) { mame_printf_debug("setting vram 68k->vram (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); } else if (MEGADRIVE_REG17_DMATYPE==0x1) { mame_printf_debug("setting vram 68k->vram (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); } else if (MEGADRIVE_REG17_DMATYPE==0x2) { mame_printf_debug("setting vram fill (INVALID?) mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); } else if (MEGADRIVE_REG17_DMATYPE==0x3) { UINT32 source; UINT16 length; source = (MEGADRIVE_REG15_DMASOURCE1 | (MEGADRIVE_REG16_DMASOURCE2<<8)); // source (byte offset) length = (MEGADRIVE_REG13_DMALENGTH1 | (MEGADRIVE_REG14_DMALENGTH2<<8)); // length in bytes //mame_printf_debug("setting vram copy mode length registers are %02x %02x other regs! %02x %02x %02x(Mode Bits %02x) Enable %02x\n", MEGADRIVE_REG13_DMALENGTH1, MEGADRIVE_REG14_DMALENGTH2, MEGADRIVE_REG15_DMASOURCE1, MEGADRIVE_REG16_DMASOURCE2, MEGADRIVE_REG17_DMASOURCE3, MEGADRIVE_REG17_DMATYPE, MEGADRIVE_REG01_DMA_ENABLE); if (MEGADRIVE_REG01_DMA_ENABLE) megadrive_do_insta_vram_copy(source, length); } } } static void megadriv_vdp_ctrl_port_w(running_machine *machine, int data) { // logerror("write to vdp control port %04x\n",data); megadrive_vram_fill_pending = 0; // ?? if (megadrive_vdp_command_pending) { /* 2nd part of 32-bit command */ megadrive_vdp_command_pending = 0; megadrive_vdp_command_part2 = data; update_megadrive_vdp_code_and_address(); handle_dma_bits(machine); //logerror("VDP Write Part 2 setting Code %02x Address %04x\n",megadrive_vdp_code, megadrive_vdp_address); } else { if ((data & 0xc000) == 0x8000) { /* Register Setting Command */ int regnum = (data & 0x3f00) >> 8; int value = (data & 0x00ff); if (regnum &0x20) mame_printf_debug("reg error\n"); megadrive_vdp_set_register(machine, regnum&0x1f,value); megadrive_vdp_code = 0; megadrive_vdp_address = 0; } else { megadrive_vdp_command_pending = 1; megadrive_vdp_command_part1 = data; update_megadrive_vdp_code_and_address(); //logerror("VDP Write Part 1 setting Code %02x Address %04x\n",megadrive_vdp_code, megadrive_vdp_address); } } } WRITE16_HANDLER( megadriv_vdp_w ) { switch (offset<<1) { case 0x00: case 0x02: if (!ACCESSING_BITS_8_15) { data = (data&0x00ff) | data<<8; // mame_printf_debug("8-bit write VDP data port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask); } else if (!ACCESSING_BITS_0_7) { data = (data&0xff00) | data>>8; // mame_printf_debug("8-bit write VDP data port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask); } megadriv_vdp_data_port_w(space->machine, data); break; case 0x04: case 0x06: if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) mame_printf_debug("8-bit write VDP control port access, offset %04x data %04x mem_mask %04x\n",offset,data,mem_mask); megadriv_vdp_ctrl_port_w(space->machine, data); break; case 0x08: case 0x0a: case 0x0c: case 0x0e: logerror("Attempt to Write to HV counters!!\n"); break; case 0x10: case 0x12: case 0x14: case 0x16: if (ACCESSING_BITS_0_7) sn76496_w(space->machine->device("snsnd"), 0, data & 0xff); //if (ACCESSING_BITS_8_15) sn76496_w(space->machine->device("snsnd"), 0, (data >>8) & 0xff); break; default: mame_printf_debug("write to unmapped vdp port\n"); } } static UINT16 vdp_vram_r(void) { return MEGADRIV_VDP_VRAM((megadrive_vdp_address&0xfffe)>>1); } static UINT16 vdp_vsram_r(void) { return megadrive_vdp_vsram[(megadrive_vdp_address&0x7e)>>1]; } static UINT16 vdp_cram_r(void) { return megadrive_vdp_cram[(megadrive_vdp_address&0x7e)>>1]; } static UINT16 megadriv_vdp_data_port_r(running_machine *machine) { UINT16 retdata=0; //return mame_rand(machine); megadrive_vdp_command_pending = 0; switch (megadrive_vdp_code & 0x000f) { case 0x0000: retdata = vdp_vram_r(); megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; break; case 0x0001: logerror("Attempting to READ from DATA PORT in VRAM WRITE MODE\n"); retdata = mame_rand(machine); break; case 0x0003: logerror("Attempting to READ from DATA PORT in CRAM WRITE MODE\n"); retdata = mame_rand(machine); break; case 0x0004: retdata = vdp_vsram_r(); megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; break; case 0x0005: logerror("Attempting to READ from DATA PORT in VSRAM WRITE MODE\n"); break; case 0x0008: retdata = vdp_cram_r(); megadrive_vdp_address+=MEGADRIVE_REG0F_AUTO_INC; megadrive_vdp_address&=0xffff; break; default: logerror("Attempting to READ from DATA PORT in #UNDEFINED# MODE\n"); retdata = mame_rand(machine); break; } // mame_printf_debug("vdp_data_port_r %04x %04x %04x\n",megadrive_vdp_code, megadrive_vdp_address, retdata); // logerror("Read VDP Data Port\n"); return retdata; } /* NTSC, 256x224 ------------- Lines Description 224 Active display 8 Bottom border 3 Bottom blanking 3 Vertical blanking 13 Top blanking 11 Top border V counter values 00-EA, E5-FF PAL, 256x224 ------------ Lines Description 224 Active display 32 Bottom border 3 Bottom blanking 3 Vertical blanking 13 Top blanking 38 Top border V counter values 00-FF, 00-02, CA-FF PAL, 256x240 ------------ Lines Description 240 Active display 24 Bottom border 3 Bottom blanking 3 Vertical blanking 13 Top blanking 30 Top border V counter values 00-FF, 00-0A, D2-FF Pixels H.Cnt Description 256 : 00-7F : Active display 15 : 80-87 : Right border 8 : 87-8B : Right blanking 26 : 8B-ED : Horizontal sync 2 : ED-EE : Left blanking 14 : EE-F5 : Color burst 8 : F5-F9 : Left blanking 13 : F9-FF : Left border */ static UINT16 megadriv_vdp_ctrl_port_r(void) { /* Battletoads is very fussy about the vblank flag it wants it to be 1. in scanline 224 */ /* Double Dragon 2 is very sensitive to hblank timing */ /* xperts is very fussy too */ /* Game no Kanzume Otokuyou (J) [!] is also fussy - it cares about the bits labeled always 0, always 1.. (!) */ /* Megalo Mania also fussy - cares about pending flag*/ int megadrive_sprite_overflow = 0; int megadrive_odd_frame = megadrive_imode_odd_frame^1; int megadrive_hblank_flag = 0; int megadrive_dma_active = 0; int vblank; int fifo_empty = 1; int fifo_full = 0; UINT16 hpos = get_hposition(); if (hpos>400) megadrive_hblank_flag = 1; if (hpos>460) megadrive_hblank_flag = 0; vblank = megadrive_vblank_flag; /* extra case */ if (MEGADRIVE_REG01_DISP_ENABLE==0) vblank = 1; /* // these aren't *always* 0/1 some of them are open bus return d15 - Always 0 d14 - Always 0 d13 - Always 1 d12 - Always 1 d11 - Always 0 d10 - Always 1 d9 - FIFO Empty d8 - FIFO Full d7 - Vertical interrupt pending d6 - Sprite overflow on current scan line d5 - Sprite collision d4 - Odd frame d3 - Vertical blanking d2 - Horizontal blanking d1 - DMA in progress d0 - PAL mode flag */ return (0<<15) | // ALWAYS 0 (0<<14) | // ALWAYS 0 (1<<13) | // ALWAYS 1 (1<<12) | // ALWAYS 1 (0<<11) | // ALWAYS 0 (1<<10) | // ALWAYS 1 (fifo_empty<<9 ) | // FIFO EMPTY (fifo_full<<8 ) | // FIFO FULL (megadrive_irq6_pending << 7) | // exmutants has a tight loop checking this .. (megadrive_sprite_overflow << 6) | (megadrive_sprite_collision << 5) | (megadrive_odd_frame << 4) | (vblank << 3) | (megadrive_hblank_flag << 2) | (megadrive_dma_active << 1 ) | (megadrive_region_pal<<0); // PAL MODE FLAG checked by striker for region prot.. } static const UINT8 vc_ntsc_224[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,/**/0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; static const UINT8 vc_ntsc_240[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; static const UINT8 vc_pal_224[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02,/**/0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; static const UINT8 vc_pal_240[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,/**/0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; static UINT16 megadriv_read_hv_counters(void) { /* Bubble and Squeek wants vcount=0xe0 */ /* Dracula is very sensitive to this */ /* Marvel Land is sensitive to this */ int vpos = genesis_scanline_counter; UINT16 hpos = get_hposition(); // if (hpos>424) vpos++; // fixes dracula, breaks road rash if (hpos>460) vpos++; // when does vpos increase.. also on sms, check game gear manual.. /* shouldn't happen.. */ if (vpos<0) { vpos = megadrive_total_scanlines; mame_printf_debug("negative vpos?!\n"); } if (MEGADRIVE_REG01_240_LINE) { if (!megadrive_region_pal) { vpos = vc_ntsc_240[vpos%megadrive_total_scanlines]; } else { vpos = vc_pal_240[vpos%megadrive_total_scanlines]; } } else { if (!megadrive_region_pal) { vpos = vc_ntsc_224[vpos%megadrive_total_scanlines]; } else { vpos = vc_pal_224[vpos%megadrive_total_scanlines]; } } if (hpos>0xf7) hpos -=0x49; return ((vpos&0xff)<<8)|(hpos&0xff); } READ16_HANDLER( megadriv_vdp_r ) { UINT16 retvalue = 0; switch (offset<<1) { case 0x00: case 0x02: if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) mame_printf_debug("8-bit VDP read data port access, offset %04x mem_mask %04x\n",offset,mem_mask); retvalue = megadriv_vdp_data_port_r(space->machine); break; case 0x04: case 0x06: // if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) mame_printf_debug("8-bit VDP read control port access, offset %04x mem_mask %04x\n",offset,mem_mask); retvalue = megadriv_vdp_ctrl_port_r(); // retvalue = mame_rand(space->machine); // mame_printf_debug("%06x: Read Control Port at scanline %d hpos %d (return %04x)\n",cpu_get_pc(space->cpu),genesis_scanline_counter, get_hposition(),retvalue); break; case 0x08: case 0x0a: case 0x0c: case 0x0e: // if ((!ACCESSING_BITS_8_15) || (!ACCESSING_BITS_0_7)) mame_printf_debug("8-bit VDP read HV counter port access, offset %04x mem_mask %04x\n",offset,mem_mask); retvalue = megadriv_read_hv_counters(); // retvalue = mame_rand(space->machine); // mame_printf_debug("%06x: Read HV counters at scanline %d hpos %d (return %04x)\n",cpu_get_pc(space->cpu),genesis_scanline_counter, get_hposition(),retvalue); break; case 0x10: case 0x12: case 0x14: case 0x16: logerror("Attempting to read PSG!\n"); retvalue = 0; break; } return retvalue; } READ8_DEVICE_HANDLER( megadriv_68k_YM2612_read) { //mame_printf_debug("megadriv_68k_YM2612_read %02x %04x\n",offset,mem_mask); if ( (genz80.z80_has_bus==0) && (genz80.z80_is_reset==0) ) { return ym2612_r(device, offset); } else { logerror("%s: 68000 attempting to access YM2612 (read) without bus\n", cpuexec_describe_context(device->machine)); return 0; } return -1; } WRITE8_DEVICE_HANDLER( megadriv_68k_YM2612_write) { //mame_printf_debug("megadriv_68k_YM2612_write %02x %04x %04x\n",offset,data,mem_mask); if ( (genz80.z80_has_bus==0) && (genz80.z80_is_reset==0) ) { ym2612_w(device, offset, data); } else { logerror("%s: 68000 attempting to access YM2612 (write) without bus\n", cpuexec_describe_context(device->machine)); } } /* Megadrive / Genesis has 3 I/O ports */ static emu_timer *io_timeout[3]; static int io_stage[3]; static TIMER_CALLBACK( io_timeout_timer_callback ) { io_stage[(int)(FPTR)ptr] = -1; } static void init_megadri6_io(running_machine *machine) { int i; for (i=0; i<3; i++) { io_timeout[i] = timer_alloc(machine, io_timeout_timer_callback, (void*)(FPTR)i); } } /* pointers to our io data read/write functions */ UINT8 (*megadrive_io_read_data_port_ptr)(running_machine *machine, int offset); void (*megadrive_io_write_data_port_ptr)(running_machine *machine, int offset, UINT16 data); /* A10001h = A0 Version register A10003h = 7F Data register for port A A10005h = 7F Data register for port B A10007h = 7F Data register for port C A10009h = 00 Ctrl register for port A A1000Bh = 00 Ctrl register for port B A1000Dh = 00 Ctrl register for port C A1000Fh = FF TxData register for port A A10011h = 00 RxData register for port A A10013h = 00 S-Ctrl register for port A A10015h = FF TxData register for port B A10017h = 00 RxData register for port B A10019h = 00 S-Ctrl register for port B A1001Bh = FF TxData register for port C A1001Dh = 00 RxData register for port C A1001Fh = 00 S-Ctrl register for port C Bit 7 - (Not connected) Bit 6 - TH Bit 5 - TL Bit 4 - TR Bit 3 - RIGHT Bit 2 - LEFT Bit 1 - DOWN Bit 0 - UP */ INPUT_PORTS_START( md_common ) PORT_START("PAD1") /* Joypad 1 (3 button + start) NOT READ DIRECTLY */ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 B") // b PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 C") // c PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 A") // a PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 START") // start PORT_START("PAD2") /* Joypad 2 (3 button + start) NOT READ DIRECTLY */ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 B") // b PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 C") // c PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 A") // a PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 START") // start INPUT_PORTS_END INPUT_PORTS_START( megadriv ) PORT_INCLUDE( md_common ) PORT_START("RESET") /* Buttons on Genesis Console */ PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_NAME("Reset Button") PORT_IMPULSE(1) // reset, resets 68k (and..?) #if HAZE_MD PORT_START("REGION") /* Region setting for Console */ /* Region setting for Console */ PORT_DIPNAME( 0x000f, 0x0000, DEF_STR( Region ) ) PORT_DIPSETTING( 0x0000, "Use HazeMD Default Choice" ) PORT_DIPSETTING( 0x0001, "US (NTSC, 60fps)" ) PORT_DIPSETTING( 0x0002, "JAPAN (NTSC, 60fps)" ) PORT_DIPSETTING( 0x0003, "EUROPE (PAL, 50fps)" ) #endif INPUT_PORTS_END INPUT_PORTS_START( megadri6 ) PORT_INCLUDE( megadriv ) PORT_START("EXTRA1") /* Extra buttons for Joypad 1 (6 button + start + mode) NOT READ DIRECTLY */ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("P1 Z") // z PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Y") // y PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 X") // x PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("P1 MODE") // mode PORT_START("EXTRA2") /* Extra buttons for Joypad 2 (6 button + start + mode) NOT READ DIRECTLY */ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_PLAYER(2) PORT_NAME("P2 Z") // z PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Y") // y PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 X") // x PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_PLAYER(2) PORT_NAME("P2 MODE") // mode INPUT_PORTS_END UINT8 megadrive_io_data_regs[3]; UINT8 megadrive_io_ctrl_regs[3]; static UINT8 megadrive_io_tx_regs[3]; static void megadrive_init_io(running_machine *machine) { const input_port_token *ipt = machine->gamedrv->ipt; if (ipt == INPUT_PORTS_NAME(megadri6)) init_megadri6_io(machine); if (ipt == INPUT_PORTS_NAME(ssf2mdb)) init_megadri6_io(machine); if (ipt == INPUT_PORTS_NAME(mk3mdb)) init_megadri6_io(machine); } static void megadrive_reset_io(running_machine *machine) { int i; megadrive_io_data_regs[0] = 0x7f; megadrive_io_data_regs[1] = 0x7f; megadrive_io_data_regs[2] = 0x7f; megadrive_io_ctrl_regs[0] = 0x00; megadrive_io_ctrl_regs[1] = 0x00; megadrive_io_ctrl_regs[2] = 0x00; megadrive_io_tx_regs[0] = 0xff; megadrive_io_tx_regs[1] = 0xff; megadrive_io_tx_regs[2] = 0xff; for (i=0; i<3; i++) { io_stage[i] = -1; } } /************* 6 buttons version **************************/ static UINT8 megadrive_io_read_data_port_6button(running_machine *machine, int portnum) { UINT8 retdata, helper = (megadrive_io_ctrl_regs[portnum] & 0x3f) | 0xc0; // bits 6 & 7 always come from megadrive_io_data_regs static const char *const pad3names[] = { "PAD1", "PAD2", "IN0", "UNK" }; static const char *const pad6names[] = { "EXTRA1", "EXTRA2", "IN0", "UNK" }; if (megadrive_io_data_regs[portnum] & 0x40) { if (io_stage[portnum] == 2) { /* here we read B, C & the additional buttons */ retdata = (megadrive_io_data_regs[portnum] & helper) | (((input_port_read_safe(machine, pad3names[portnum], 0) & 0x30) | (input_port_read_safe(machine, pad6names[portnum], 0) & 0x0f)) & ~helper); } else { /* here we read B, C & the directional buttons */ retdata = (megadrive_io_data_regs[portnum] & helper) | ((input_port_read_safe(machine, pad3names[portnum], 0) & 0x3f) & ~helper); } } else { if (io_stage[portnum] == 1) { /* here we read ((Start & A) >> 2) | 0x00 */ retdata = (megadrive_io_data_regs[portnum] & helper) | (((input_port_read_safe(machine, pad3names[portnum], 0) & 0xc0) >> 2) & ~helper); } else if (io_stage[portnum]==2) { /* here we read ((Start & A) >> 2) | 0x0f */ retdata = (megadrive_io_data_regs[portnum] & helper) | ((((input_port_read_safe(machine, pad3names[portnum], 0) & 0xc0) >> 2) | 0x0f) & ~helper); } else { /* here we read ((Start & A) >> 2) | Up and Down */ retdata = (megadrive_io_data_regs[portnum] & helper) | ((((input_port_read_safe(machine, pad3names[portnum], 0) & 0xc0) >> 2) | (input_port_read_safe(machine, pad3names[portnum], 0) & 0x03)) & ~helper); } } // mame_printf_debug("read io data port stage %d port %d %02x\n",io_stage[portnum],portnum,retdata); return retdata | (retdata << 8); } /************* 3 buttons version **************************/ static UINT8 megadrive_io_read_data_port_3button(running_machine *machine, int portnum) { UINT8 retdata, helper = (megadrive_io_ctrl_regs[portnum] & 0x7f) | 0x80; // bit 7 always comes from megadrive_io_data_regs static const char *const pad3names[] = { "PAD1", "PAD2", "IN0", "UNK" }; if (megadrive_io_data_regs[portnum] & 0x40) { /* here we read B, C & the directional buttons */ retdata = (megadrive_io_data_regs[portnum] & helper) | (((input_port_read_safe(machine, pad3names[portnum], 0) & 0x3f) | 0x40) & ~helper); } else { /* here we read ((Start & A) >> 2) | Up and Down */ retdata = (megadrive_io_data_regs[portnum] & helper) | ((((input_port_read_safe(machine, pad3names[portnum], 0) & 0xc0) >> 2) | (input_port_read_safe(machine, pad3names[portnum], 0) & 0x03) | 0x40) & ~helper); } return retdata; } /* used by megatech bios, the test mode accesses the joypad/stick inputs like this */ UINT8 megatech_bios_port_cc_dc_r(running_machine *machine, int offset, int ctrl) { UINT8 retdata; if (ctrl == 0x55) { /* A keys */ retdata = ((input_port_read(machine, "PAD1") & 0x40) >> 2) | ((input_port_read(machine, "PAD2") & 0x40) >> 4) | 0xeb; } else { if (offset == 0) { retdata = (input_port_read(machine, "PAD1") & 0x3f) | ((input_port_read(machine, "PAD2") & 0x03) << 6); } else { retdata = ((input_port_read(machine, "PAD2") & 0x3c) >> 2) | 0xf0; } } return retdata; } static UINT8 megadrive_io_read_ctrl_port(int portnum) { UINT8 retdata; retdata = megadrive_io_ctrl_regs[portnum]; //mame_printf_debug("read io ctrl port %d %02x\n",portnum,retdata); return retdata | (retdata << 8); } static UINT8 megadrive_io_read_tx_port(int portnum) { UINT8 retdata; retdata = megadrive_io_tx_regs[portnum]; return retdata | (retdata << 8); } static UINT8 megadrive_io_read_rx_port(int portnum) { return 0x00; } static UINT8 megadrive_io_read_sctrl_port(int portnum) { return 0x00; } READ16_HANDLER( megadriv_68k_io_read ) { UINT8 retdata; retdata = 0; /* Charles MacDonald ( http://cgfm2.emuviews.com/ ) D7 : Console is 1= Export (USA, Europe, etc.) 0= Domestic (Japan) D6 : Video type is 1= PAL, 0= NTSC D5 : Sega CD unit is 1= not present, 0= connected. D4 : Unused (always returns zero) D3 : Bit 3 of version number D2 : Bit 2 of version number D1 : Bit 1 of version number D0 : Bit 0 of version number */ //return (mame_rand(space->machine)&0x0f0f)|0xf0f0;//0x0000; switch (offset) { case 0: logerror("%06x read version register\n", cpu_get_pc(space->cpu)); retdata = megadrive_region_export<<7 | // Export megadrive_region_pal<<6 | // NTSC (sega_cd_connected?0x00:0x20) | // 0x20 = no sega cd 0x00 | // Unused (Always 0) 0x00 | // Bit 3 of Version Number 0x00 | // Bit 2 of Version Number 0x00 | // Bit 1 of Version Number 0x01 ; // Bit 0 of Version Number break; /* Joystick Port Registers */ case 0x1: case 0x2: case 0x3: // retdata = megadrive_io_read_data_port(offset-1); retdata = megadrive_io_read_data_port_ptr(space->machine, offset-1); break; case 0x4: case 0x5: case 0x6: retdata = megadrive_io_read_ctrl_port(offset-4); break; /* Serial I/O Registers */ case 0x7: retdata = megadrive_io_read_tx_port(0); break; case 0x8: retdata = megadrive_io_read_rx_port(0); break; case 0x9: retdata = megadrive_io_read_sctrl_port(0); break; case 0xa: retdata = megadrive_io_read_tx_port(1); break; case 0xb: retdata = megadrive_io_read_rx_port(1); break; case 0xc: retdata = megadrive_io_read_sctrl_port(1); break; case 0xd: retdata = megadrive_io_read_tx_port(2); break; case 0xe: retdata = megadrive_io_read_rx_port(2); break; case 0xf: retdata = megadrive_io_read_sctrl_port(2); break; } return retdata | (retdata << 8); } static void megadrive_io_write_data_port_3button(running_machine *machine, int portnum, UINT16 data) { megadrive_io_data_regs[portnum] = data; //mame_printf_debug("Writing IO Data Register #%d data %04x\n",portnum,data); } /****************************** 6 buttons version*****************************/ static void megadrive_io_write_data_port_6button(running_machine *machine, int portnum, UINT16 data) { if (megadrive_io_ctrl_regs[portnum]&0x40) { if (((megadrive_io_data_regs[portnum]&0x40)==0x00) && ((data&0x40) == 0x40)) { io_stage[portnum]++; timer_adjust_oneshot(io_timeout[portnum], machine->device("maincpu")->cycles_to_attotime(8192), 0); } } megadrive_io_data_regs[portnum] = data; //mame_printf_debug("Writing IO Data Register #%d data %04x\n",portnum,data); } /*************************** 3 buttons version ****************************/ static void megadrive_io_write_ctrl_port(running_machine *machine, int portnum, UINT16 data) { megadrive_io_ctrl_regs[portnum] = data; // mame_printf_debug("Setting IO Control Register #%d data %04x\n",portnum,data); } static void megadrive_io_write_tx_port(running_machine *machine, int portnum, UINT16 data) { megadrive_io_tx_regs[portnum] = data; } static void megadrive_io_write_rx_port(running_machine *machine, int portnum, UINT16 data) { } static void megadrive_io_write_sctrl_port(running_machine *machine, int portnum, UINT16 data) { } WRITE16_HANDLER( megadriv_68k_io_write ) { // mame_printf_debug("IO Write #%02x data %04x mem_mask %04x\n",offset,data,mem_mask); switch (offset) { case 0x0: mame_printf_debug("Write to Version Register?!\n"); break; /* Joypad Port Registers */ case 0x1: case 0x2: case 0x3: // megadrive_io_write_data_port(offset-1,data); megadrive_io_write_data_port_ptr(space->machine, offset-1,data); break; case 0x4: case 0x5: case 0x6: megadrive_io_write_ctrl_port(space->machine,offset-4,data); break; /* Serial I/O Registers */ case 0x7: megadrive_io_write_tx_port(space->machine,0,data); break; case 0x8: megadrive_io_write_rx_port(space->machine,0,data); break; case 0x9: megadrive_io_write_sctrl_port(space->machine,0,data); break; case 0xa: megadrive_io_write_tx_port(space->machine,1,data); break; case 0xb: megadrive_io_write_rx_port(space->machine,1,data); break; case 0xc: megadrive_io_write_sctrl_port(space->machine,1,data); break; break; case 0xd: megadrive_io_write_tx_port(space->machine,2,data); break; case 0xe: megadrive_io_write_rx_port(space->machine,2,data); break; case 0xf: megadrive_io_write_sctrl_port(space->machine,2,data); break; break; } } static ADDRESS_MAP_START( megadriv_map, ADDRESS_SPACE_PROGRAM, 16 ) AM_RANGE(0x000000, 0x3fffff) AM_ROM /* (0x000000 - 0x3fffff) == GAME ROM (4Meg Max, Some games have special banking too) */ AM_RANGE(0xa00000, 0xa01fff) AM_READWRITE(megadriv_68k_read_z80_ram,megadriv_68k_write_z80_ram) AM_RANGE(0xa02000, 0xa03fff) AM_WRITE(megadriv_68k_write_z80_ram) AM_RANGE(0xa04000, 0xa04003) AM_DEVREADWRITE8("ymsnd", megadriv_68k_YM2612_read,megadriv_68k_YM2612_write, 0xffff) AM_RANGE(0xa06000, 0xa06001) AM_WRITE(megadriv_68k_z80_bank_write) AM_RANGE(0xa10000, 0xa1001f) AM_READWRITE(megadriv_68k_io_read,megadriv_68k_io_write) AM_RANGE(0xa11100, 0xa11101) AM_READWRITE(megadriv_68k_check_z80_bus,megadriv_68k_req_z80_bus) AM_RANGE(0xa11200, 0xa11201) AM_WRITE(megadriv_68k_req_z80_reset) /* these are fake - remove allocs in VIDEO_START to use these to view ram instead */ // AM_RANGE(0xb00000, 0xb0ffff) AM_RAM AM_BASE(&megadrive_vdp_vram) // AM_RANGE(0xb10000, 0xb1007f) AM_RAM AM_BASE(&megadrive_vdp_vsram) // AM_RANGE(0xb10100, 0xb1017f) AM_RAM AM_BASE(&megadrive_vdp_cram) AM_RANGE(0xc00000, 0xc0001f) AM_READWRITE(megadriv_vdp_r,megadriv_vdp_w) AM_RANGE(0xd00000, 0xd0001f) AM_READWRITE(megadriv_vdp_r,megadriv_vdp_w) // the earth defend AM_RANGE(0xe00000, 0xe0ffff) AM_RAM AM_MIRROR(0x1f0000) AM_BASE(&megadrive_ram) // AM_RANGE(0xff0000, 0xffffff) AM_READONLY /* 0xe00000 - 0xffffff) == MAIN RAM (64kb, Mirrored, most games use ff0000 - ffffff) */ ADDRESS_MAP_END /* z80 sounds/sub CPU */ static READ16_HANDLER( megadriv_68k_read_z80_ram ) { //mame_printf_debug("read z80 ram %04x\n",mem_mask); if ( (genz80.z80_has_bus==0) && (genz80.z80_is_reset==0) ) { return genz80.z80_prgram[(offset<<1)^1] | (genz80.z80_prgram[(offset<<1)]<<8); } else { logerror("%06x: 68000 attempting to access Z80 (read) address space without bus\n", cpu_get_pc(space->cpu)); return mame_rand(space->machine); } } static WRITE16_HANDLER( megadriv_68k_write_z80_ram ) { //logerror("write z80 ram\n"); if ((genz80.z80_has_bus==0) && (genz80.z80_is_reset==0)) { if (!ACCESSING_BITS_0_7) // byte (MSB) access { genz80.z80_prgram[(offset<<1)] = (data & 0xff00) >> 8; } else if (!ACCESSING_BITS_8_15) { genz80.z80_prgram[(offset<<1)^1] = (data & 0x00ff); } else // for WORD access only the MSB is used, LSB is ignored { genz80.z80_prgram[(offset<<1)] = (data & 0xff00) >> 8; } } else { logerror("%06x: 68000 attempting to access Z80 (write) address space without bus\n", cpu_get_pc(space->cpu)); } } static READ16_HANDLER( megadriv_68k_check_z80_bus ) { UINT16 retvalue; /* Double Dragon, Shadow of the Beast, Super Off Road, and Time Killers have buggy sound programs. They request the bus, then have a loop which waits for the bus to be unavailable, checking for a 0 value due to bad coding. The real hardware appears to return bits of the next instruction in the unused bits, thus meaning the value is never zero. Time Killers is the most fussy, and doesn't like the read_next_instruction function from system16, so I just return a random value in the unused bits */ UINT16 nextvalue = mame_rand(space->machine);//read_next_instruction(space)&0xff00; /* Check if the 68k has the z80 bus */ if (!ACCESSING_BITS_0_7) // byte (MSB) access { if (genz80.z80_has_bus || genz80.z80_is_reset) retvalue = nextvalue | 0x0100; else retvalue = (nextvalue & 0xfeff); //logerror("%06x: 68000 check z80 Bus (byte MSB access) returning %04x mask %04x\n", cpu_get_pc(space->cpu),retvalue, mem_mask); return retvalue; } else if (!ACCESSING_BITS_8_15) // is this valid? { //logerror("%06x: 68000 check z80 Bus (byte LSB access) %04x\n", cpu_get_pc(space->cpu),mem_mask); if (genz80.z80_has_bus || genz80.z80_is_reset) retvalue = 0x0001; else retvalue = 0x0000; return retvalue; } else { //logerror("%06x: 68000 check z80 Bus (word access) %04x\n", cpu_get_pc(space->cpu),mem_mask); if (genz80.z80_has_bus || genz80.z80_is_reset) retvalue = nextvalue | 0x0100; else retvalue = (nextvalue & 0xfeff); // mame_printf_debug("%06x: 68000 check z80 Bus (word access) %04x %04x\n", cpu_get_pc(space->cpu),mem_mask, retvalue); return retvalue; } } static TIMER_CALLBACK( megadriv_z80_run_state ) { /* Is the z80 RESET line pulled? */ if ( genz80.z80_is_reset ) { devtag_reset( machine, "genesis_snd_z80" ); machine->device( "genesis_snd_z80" )->suspend(SUSPEND_REASON_HALT, 1 ); devtag_reset( machine, "ymsnd" ); } else { /* Check if z80 has the bus */ if ( genz80.z80_has_bus ) { machine->device( "genesis_snd_z80" )->resume(SUSPEND_REASON_HALT ); } else { machine->device( "genesis_snd_z80" )->suspend(SUSPEND_REASON_HALT, 1 ); } } } static WRITE16_HANDLER( megadriv_68k_req_z80_bus ) { /* Request the Z80 bus, allows 68k to read/write Z80 address space */ if (!ACCESSING_BITS_0_7) // byte access { if (data & 0x0100) { //logerror("%06x: 68000 request z80 Bus (byte MSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 0; } else { //logerror("%06x: 68000 return z80 Bus (byte MSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 1; } } else if (!ACCESSING_BITS_8_15) // is this valid? { if (data & 0x0001) { //logerror("%06x: 68000 request z80 Bus (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 0; } else { //logerror("%06x: 68000 return z80 Bus (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 1; } } else // word access { if (data & 0x0100) { //logerror("%06x: 68000 request z80 Bus (word access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 0; } else { //logerror("%06x: 68000 return z80 Bus (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_has_bus = 1; } } /* If the z80 is running, sync the z80 execution state */ if ( ! genz80.z80_is_reset ) timer_set( space->machine, attotime_zero, NULL, 0, megadriv_z80_run_state ); } static WRITE16_HANDLER ( megadriv_68k_req_z80_reset ) { if (!ACCESSING_BITS_0_7) // byte access { if (data & 0x0100) { //logerror("%06x: 68000 clear z80 reset (byte MSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 0; } else { //logerror("%06x: 68000 start z80 reset (byte MSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 1; } } else if (!ACCESSING_BITS_8_15) // is this valid? { if (data & 0x0001) { //logerror("%06x: 68000 clear z80 reset (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 0; } else { //logerror("%06x: 68000 start z80 reset (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 1; } } else // word access { if (data & 0x0100) { //logerror("%06x: 68000 clear z80 reset (word access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 0; } else { //logerror("%06x: 68000 start z80 reset (byte LSB access) %04x %04x\n", cpu_get_pc(space->cpu),data,mem_mask); genz80.z80_is_reset = 1; } } timer_set( space->machine, attotime_zero, NULL, 0, megadriv_z80_run_state ); } // just directly access the 68k space, this makes it easier to deal with // add-on hardware which changes the cpu mapping like the 32x and SegaCD. // - we might need to add exceptions for example, z80 reading / writing the // z80 area of the 68k if games misbehave static READ8_HANDLER( z80_read_68k_banked_data ) { address_space *space68k = space->machine->device("maincpu")->space(); UINT8 ret = space68k->read_byte(genz80.z80_bank_addr+offset); return ret; } static WRITE8_HANDLER( z80_write_68k_banked_data ) { address_space *space68k = space->machine->device("maincpu")->space(); space68k->write_byte(genz80.z80_bank_addr+offset,data); } static WRITE8_HANDLER( megadriv_z80_vdp_write ) { switch (offset) { case 0x11: case 0x13: case 0x15: case 0x17: sn76496_w(space->machine->device("snsnd"), 0, data); break; default: mame_printf_debug("unhandled z80 vdp write %02x %02x\n",offset,data); } } static READ8_HANDLER( megadriv_z80_vdp_read ) { mame_printf_debug("megadriv_z80_vdp_read %02x\n",offset); return mame_rand(space->machine); } static READ8_HANDLER( megadriv_z80_unmapped_read ) { return 0xff; } static ADDRESS_MAP_START( megadriv_z80_map, ADDRESS_SPACE_PROGRAM, 8 ) AM_RANGE(0x0000, 0x1fff) AM_RAMBANK("bank1") AM_MIRROR(0x2000) // RAM can be accessed by the 68k AM_RANGE(0x4000, 0x4003) AM_DEVREADWRITE("ymsnd", ym2612_r,ym2612_w) AM_RANGE(0x6000, 0x6000) AM_WRITE(megadriv_z80_z80_bank_w) AM_RANGE(0x6001, 0x6001) AM_WRITE(megadriv_z80_z80_bank_w) // wacky races uses this address AM_RANGE(0x6100, 0x7eff) AM_READ(megadriv_z80_unmapped_read) AM_RANGE(0x7f00, 0x7fff) AM_READWRITE(megadriv_z80_vdp_read,megadriv_z80_vdp_write) AM_RANGE(0x8000, 0xffff) AM_READWRITE(z80_read_68k_banked_data,z80_write_68k_banked_data) // The Z80 can read the 68k address space this way ADDRESS_MAP_END static ADDRESS_MAP_START( megadriv_z80_io_map, ADDRESS_SPACE_IO, 8 ) ADDRESS_MAP_GLOBAL_MASK(0xff) AM_RANGE(0x0000, 0xff) AM_NOP ADDRESS_MAP_END /************************************ Megadrive Bootlegs *************************************/ // smaller ROM region because some bootlegs check for RAM there static ADDRESS_MAP_START( md_bootleg_map, ADDRESS_SPACE_PROGRAM, 16 ) AM_RANGE(0x000000, 0x0fffff) AM_ROM /* Cartridge Program Rom */ AM_RANGE(0x200000, 0x2023ff) AM_RAM // tested AM_RANGE(0xa00000, 0xa01fff) AM_READWRITE(megadriv_68k_read_z80_ram, megadriv_68k_write_z80_ram) AM_RANGE(0xa02000, 0xa03fff) AM_WRITE(megadriv_68k_write_z80_ram) AM_RANGE(0xa04000, 0xa04003) AM_DEVREADWRITE8("ymsnd", megadriv_68k_YM2612_read, megadriv_68k_YM2612_write, 0xffff) AM_RANGE(0xa06000, 0xa06001) AM_WRITE(megadriv_68k_z80_bank_write) AM_RANGE(0xa10000, 0xa1001f) AM_READWRITE(megadriv_68k_io_read, megadriv_68k_io_write) AM_RANGE(0xa11100, 0xa11101) AM_READWRITE(megadriv_68k_check_z80_bus, megadriv_68k_req_z80_bus) AM_RANGE(0xa11200, 0xa11201) AM_WRITE(megadriv_68k_req_z80_reset) AM_RANGE(0xc00000, 0xc0001f) AM_READWRITE(megadriv_vdp_r, megadriv_vdp_w) AM_RANGE(0xd00000, 0xd0001f) AM_READWRITE(megadriv_vdp_r, megadriv_vdp_w) // the earth defend AM_RANGE(0xe00000, 0xe0ffff) AM_RAM AM_MIRROR(0x1f0000) AM_BASE(&megadrive_ram) ADDRESS_MAP_END MACHINE_CONFIG_DERIVED( md_bootleg, megadriv ) MDRV_CPU_MODIFY("maincpu") MDRV_CPU_PROGRAM_MAP(md_bootleg_map) MACHINE_CONFIG_END /****************************************** 32X related ******************************************/ /**********************************************************************************************/ // Function Prototypes /**********************************************************************************************/ static READ16_HANDLER( _32x_common_vdp_regs_r ); static WRITE16_HANDLER( _32x_common_vdp_regs_w ); static UINT16 _32x_autofill_length; static UINT16 _32x_autofill_address; static UINT16 _32x_autofill_data; static READ16_HANDLER( _32x_68k_palette_r ) { return _32x_palette[offset]; } static WRITE16_HANDLER( _32x_68k_palette_w ) { int r,g,b, p; COMBINE_DATA(&_32x_palette[offset]); data = _32x_palette[offset]; r = ((data >> 0) & 0x1f); g = ((data >> 5) & 0x1f); b = ((data >> 10) & 0x1f); p = ((data >> 15) & 0x01); // priority 'through' bit _32x_palette_lookup[offset] = (r << 10) | (g << 5) | (b << 0) | (p << 15); palette_set_color_rgb(space->machine,offset+0x40,pal5bit(r),pal5bit(g),pal5bit(b)); } static READ16_HANDLER( _32x_68k_dram_r ) { return _32x_access_dram[offset]; } static WRITE16_HANDLER( _32x_68k_dram_w ) { if ((mem_mask&0xffff) == 0xffff) { // 16-bit accesses are normal COMBINE_DATA(&_32x_access_dram[offset]); } else { // 8-bit writes act as if they were going to the overwrite region! // bc-racers and world series baseball rely on this! // (tested on real hw) if ((mem_mask & 0xffff) == 0xff00) { if ((data & 0xff00) != 0x0000) { _32x_access_dram[offset] = (data & 0xff00) | (_32x_access_dram[offset] & 0x00ff); } } else if ((mem_mask & 0xffff) == 0x00ff) { if ((data & 0x00ff) != 0x0000) { _32x_access_dram[offset] = (data & 0x00ff) | (_32x_access_dram[offset] & 0xff00); } } } } static READ16_HANDLER( _32x_68k_dram_overwrite_r ) { return _32x_access_dram[offset]; } static WRITE16_HANDLER( _32x_68k_dram_overwrite_w ) { //COMBINE_DATA(&_32x_access_dram[offset+0x10000]); if (ACCESSING_BITS_8_15) { if (data & 0xff00) { _32x_access_dram[offset] = (_32x_access_dram[offset]&0x00ff) | (data & 0xff00); } } if (ACCESSING_BITS_0_7) { if (data & 0x00ff) { _32x_access_dram[offset] = (_32x_access_dram[offset]&0xff00) | (data & 0x00ff); } } } /**********************************************************************************************/ // 68k side a15112 // FIFO /**********************************************************************************************/ static UINT16 fifo_block_a[4]; static UINT16 fifo_block_b[4]; static UINT16* current_fifo_block; static UINT16* current_fifo_readblock; int current_fifo_write_pos; int current_fifo_read_pos; int fifo_block_a_full; int fifo_block_b_full; /* 15106 DREQ ---- ---- F--- -K0R F = Fifo FULL K = 68k CPU Write mode (0 = no, 1 = CPU write) 0 = always 0? no, marsch test wants it to be latched or 1 R = RV (0 = no operation, 1 = DMA Start allowed) <-- RV bit actually affects memory mapping, this is misleading.. it just sets the memory up in a suitable way to use the genesis VDP DMA */ static UINT16 a15106_reg; static READ16_HANDLER( _32x_68k_a15106_r ) { UINT16 retval; retval = a15106_reg; if (fifo_block_a_full && fifo_block_b_full) retval |= 0x8080; return retval; } static WRITE16_HANDLER( _32x_68k_a15106_w ) { if (ACCESSING_BITS_0_7) { a15106_reg = data & 0x7; if (a15106_reg & 0x1) /* NBA Jam TE relies on this */ { // install the game rom in the normal 0x000000-0x03fffff space used by the genesis - this allows VDP DMA operations to work as they have to be from this area or RAM // it should also UNMAP the banked rom area... memory_install_rom(space, 0x0000100, 0x03fffff, 0, 0, memory_region(space->machine, "gamecart") + 0x100); } else { // we should be careful and map back any rom overlay (hint) and backup ram too I think... // this is actually blank / nop area // we should also map the banked area back (we don't currently unmap it tho) memory_install_rom(space, 0x0000100, 0x03fffff, 0, 0, memory_region(space->machine, "maincpu")+0x100); } if((a15106_reg & 4) == 0) // clears the FIFO state { current_fifo_block = fifo_block_a; current_fifo_readblock = fifo_block_b; current_fifo_write_pos = 0; current_fifo_read_pos = 0; fifo_block_a_full = 0; fifo_block_b_full = 0; } //printf("_32x_68k_a15106_w %04x\n", data); /* if (a15106_reg & 0x4) printf(" --- 68k Write Mode enabled\n"); else printf(" --- 68k Write Mode disabled\n"); if (a15106_reg & 0x1) printf(" --- DMA Start Allowed \n"); else printf(" --- DMA Start No Operation\n"); */ } } static UINT16 dreq_src_addr[2],dreq_dst_addr[2],dreq_size; static READ16_HANDLER( _32x_dreq_common_r ) { address_space* _68kspace = cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM); switch (offset) { case 0x00/2: // a15108 / 4008 case 0x02/2: // a1510a / 400a return dreq_src_addr[offset&1]; case 0x04/2: // a1510c / 400c case 0x06/2: // a1510e / 400e return dreq_dst_addr[offset&1]; case 0x08/2: // a15110 / 4010 return dreq_size; case 0x0a/2: // a15112 / 4012 if (space == _68kspace) { printf("attempting to READ FIFO with 68k!\n"); return 0xffff; } UINT16 retdat = current_fifo_readblock[current_fifo_read_pos]; current_fifo_read_pos++; // printf("reading FIFO!\n"); if (current_fifo_readblock == fifo_block_a && !fifo_block_a_full) printf("Fifo block a isn't filled!\n"); if (current_fifo_readblock == fifo_block_b && !fifo_block_b_full) printf("%08x Fifo block b isn't filled!\n",cpu_get_pc(space->cpu)); if (current_fifo_read_pos==4) { if (current_fifo_readblock == fifo_block_a) { fifo_block_a_full = 0; if (fifo_block_b_full) { current_fifo_readblock = fifo_block_b; current_fifo_block = fifo_block_a; } current_fifo_read_pos = 0; } else if (current_fifo_readblock == fifo_block_b) { fifo_block_b_full = 0; if (fifo_block_a_full) { current_fifo_readblock = fifo_block_a; current_fifo_block = fifo_block_b; } current_fifo_read_pos = 0; } } return retdat; } return 0x0000; } static WRITE16_HANDLER( _32x_dreq_common_w ) { address_space* _68kspace = cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM); switch (offset) { case 0x00/2: // a15108 / 4008 case 0x02/2: // a1510a / 400a if (space != _68kspace) { printf("attempting to WRITE DREQ SRC with SH2!\n"); return; } dreq_src_addr[offset&1] = ((offset&1) == 0) ? (data & 0xff) : (data & 0xfffe); //if((dreq_src_addr[0]<<16)|dreq_src_addr[1]) // printf("DREQ set SRC = %08x\n",(dreq_src_addr[0]<<16)|dreq_src_addr[1]); break; case 0x04/2: // a1510c / 400c case 0x06/2: // a1510e / 400e if (space != _68kspace) { printf("attempting to WRITE DREQ DST with SH2!\n"); return; } dreq_dst_addr[offset&1] = ((offset&1) == 0) ? (data & 0xff) : (data & 0xffff); //if((dreq_dst_addr[0]<<16)|dreq_dst_addr[1]) // printf("DREQ set DST = %08x\n",(dreq_dst_addr[0]<<16)|dreq_dst_addr[1]); break; case 0x08/2: // a15110 / 4010 if (space != _68kspace) { printf("attempting to WRITE DREQ SIZE with SH2!\n"); return; } dreq_size = data & 0xfffc; // if(dreq_size) // printf("DREQ set SIZE = %04x\n",dreq_size); break; case 0x0a/2: // a15112 / 4012 - FIFO Write (68k only!) if (space != _68kspace) { printf("attempting to WRITE FIFO with SH2!\n"); return; } if (current_fifo_block==fifo_block_a && fifo_block_a_full) { printf("attempt to write to Full Fifo block a!\n"); return; } if (current_fifo_block==fifo_block_b && fifo_block_b_full) { printf("attempt to write to Full Fifo block b!\n"); return; } if((a15106_reg & 4) == 0) { printf("attempting to WRITE FIFO with 68S cleared!"); return; } current_fifo_block[current_fifo_write_pos] = data; current_fifo_write_pos++; if (current_fifo_write_pos==4) { if (current_fifo_block==fifo_block_a) { fifo_block_a_full = 1; if (!fifo_block_b_full) { current_fifo_block = fifo_block_b; current_fifo_readblock = fifo_block_a; // incase we have a stalled DMA in progress, let the SH2 know there is data available sh2_notify_dma_data_available(space->machine->device("32x_master_sh2")); sh2_notify_dma_data_available(space->machine->device("32x_slave_sh2")); } current_fifo_write_pos = 0; } else { fifo_block_b_full = 1; if (!fifo_block_a_full) { current_fifo_block = fifo_block_a; current_fifo_readblock = fifo_block_b; // incase we have a stalled DMA in progress, let the SH2 know there is data available sh2_notify_dma_data_available(space->machine->device("32x_master_sh2")); sh2_notify_dma_data_available(space->machine->device("32x_slave_sh2")); } current_fifo_write_pos = 0; } } break; } } static UINT8 sega_tv; static READ16_HANDLER( _32x_68k_a1511a_r ) { return sega_tv; } static WRITE16_HANDLER( _32x_68k_a1511a_w ) { sega_tv = data & 1; printf("SEGA TV register set = %04x\n",data); } /* 000070 H interrupt vector can be overwritten apparently */ static UINT16 hint_vector[2]; static READ16_HANDLER( _32x_68k_hint_vector_r ) { return hint_vector[offset]; } static WRITE16_HANDLER( _32x_68k_hint_vector_w ) { hint_vector[offset] = data; } // returns MARS, the system ID of the 32x static READ16_HANDLER( _32x_68k_MARS_r ) { switch (offset) { case 0: return 0x4d41; case 1: return 0x5253; } return 0x0000; } /**********************************************************************************************/ // 68k side a15100 // control register - used to enable 32x etc. /**********************************************************************************************/ static UINT16 a15100_reg; static READ16_HANDLER( _32x_68k_a15100_r ) { return (_32x_access_auth<<15) | 0x0080; } static WRITE16_HANDLER( _32x_68k_a15100_w ) { if (ACCESSING_BITS_0_7) { a15100_reg = (a15100_reg & 0xff00) | (data & 0x00ff); if (data & 0x02) { cpu_set_input_line(_32x_master_cpu, INPUT_LINE_RESET, CLEAR_LINE); cpu_set_input_line(_32x_slave_cpu, INPUT_LINE_RESET, CLEAR_LINE); } if (data & 0x01) { _32x_adapter_enabled = 1; memory_install_rom(space, 0x0880000, 0x08fffff, 0, 0, memory_region(space->machine, "gamecart")); // 'fixed' 512kb rom bank memory_install_read_bank(space, 0x0900000, 0x09fffff, 0, 0, "bank12"); // 'bankable' 1024kb rom bank memory_set_bankptr(space->machine, "bank12", memory_region(space->machine, "gamecart")+((_32x_68k_a15104_reg&0x3)*0x100000) ); memory_install_rom(space, 0x0000000, 0x03fffff, 0, 0, memory_region(space->machine, "32x_68k_bios")); /* VDP area */ memory_install_readwrite16_handler(space, 0x0a15180, 0x0a1518b, 0, 0, _32x_common_vdp_regs_r, _32x_common_vdp_regs_w); // common / shared VDP regs memory_install_readwrite16_handler(space, 0x0a15200, 0x0a153ff, 0, 0, _32x_68k_palette_r, _32x_68k_palette_w); // access to 'palette' xRRRRRGGGGGBBBBB memory_install_readwrite16_handler(space, 0x0840000, 0x085ffff, 0, 0, _32x_68k_dram_r, _32x_68k_dram_w); // access to 'display ram' (framebuffer) memory_install_readwrite16_handler(space, 0x0860000, 0x087ffff, 0, 0, _32x_68k_dram_overwrite_r, _32x_68k_dram_overwrite_w); // access to 'display ram' (framebuffer) memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x000070, 0x000073, 0, 0, _32x_68k_hint_vector_r, _32x_68k_hint_vector_w); // h interrupt vector } else { _32x_adapter_enabled = 0; memory_install_rom(space, 0x0000000, 0x03fffff, 0, 0, memory_region(space->machine, "gamecart")); memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x000070, 0x000073, 0, 0, _32x_68k_hint_vector_r, _32x_68k_hint_vector_w); // h interrupt vector } } if (ACCESSING_BITS_8_15) { a15100_reg = (a15100_reg & 0x00ff) | (data & 0xff00); _32x_access_auth = (data & 0x8000)>>15; } } /**********************************************************************************************/ // 68k side a15102 // command interrupt to SH2 /**********************************************************************************************/ static int _32x_68k_a15102_reg; static READ16_HANDLER( _32x_68k_a15102_r ) { //printf("_32x_68k_a15102_r\n"); return _32x_68k_a15102_reg; } static WRITE16_HANDLER( _32x_68k_a15102_w ) { if (ACCESSING_BITS_0_7) { _32x_68k_a15102_reg = data & 3; if (data&0x1) { if (sh2_master_cmdint_enable) cpu_set_input_line(_32x_master_cpu,SH2_CINT_IRQ_LEVEL,ASSERT_LINE); else printf("master cmdint when masked!\n"); } if (data&0x2) { if (sh2_slave_cmdint_enable) cpu_set_input_line(_32x_slave_cpu,SH2_CINT_IRQ_LEVEL,ASSERT_LINE); else printf("slave cmdint when masked!\n"); } } } /**********************************************************************************************/ // 68k side a15104 // ROM banking for 68k rom /**********************************************************************************************/ static READ16_HANDLER( _32x_68k_a15104_r ) { return _32x_68k_a15104_reg; } static WRITE16_HANDLER( _32x_68k_a15104_w ) { if (ACCESSING_BITS_0_7) { _32x_68k_a15104_reg = (_32x_68k_a15104_reg & 0xff00) | (data & 0x00ff); } if (ACCESSING_BITS_8_15) { _32x_68k_a15104_reg = (_32x_68k_a15104_reg & 0x00ff) | (data & 0xff00); } memory_set_bankptr(space->machine, "bank12", memory_region(space->machine, "gamecart")+((_32x_68k_a15104_reg&0x3)*0x100000) ); } /**********************************************************************************************/ // 68k side a15120 - a1512f // Communication Port 0 // access from the SH2 via 4020 - 402f /**********************************************************************************************/ #define _32X_COMMS_PORT_SYNC 0 static UINT16 commsram[8]; /**********************************************************************************************/ // reads static READ16_HANDLER( _32x_68k_commsram_r ) { if (_32X_COMMS_PORT_SYNC) timer_call_after_resynch(space->machine, NULL, 0, NULL); return commsram[offset]; } // writes static WRITE16_HANDLER( _32x_68k_commsram_w ) { COMBINE_DATA(&commsram[offset]); if (_32X_COMMS_PORT_SYNC) timer_call_after_resynch(space->machine, NULL, 0, NULL); } /**********************************************************************************************/ // 68k side a15130 - a1513f // PWM registers // access from the SH2 via 4030 - 403f /**********************************************************************************************/ /* TODO: - noticeable static noise on Virtua Fighter Sega logo at start-up - Understand if Speaker OFF makes the FIFO to advance or not */ #define PWM_FIFO_SIZE pwm_tm_reg // guess, Marsch calls this register as FIFO width #define PWM_CLOCK megadrive_region_pal ? ((MASTER_CLOCK_PAL*3) / 7) : ((MASTER_CLOCK_NTSC*3) / 7) static UINT16 pwm_ctrl,pwm_cycle,pwm_tm_reg; static UINT16 cur_lch[0x10],cur_rch[0x10]; static UINT16 pwm_cycle_reg; //used for latching static UINT8 pwm_timer_tick; static UINT8 lch_index_r,rch_index_r,lch_index_w,rch_index_w; static UINT16 lch_fifo_state,rch_fifo_state; static emu_timer *_32x_pwm_timer; static void calculate_pwm_timer(void) { if(pwm_tm_reg == 0) { pwm_tm_reg = 16; } // zero gives max range if(pwm_cycle == 0) { pwm_cycle = 4095; } // zero gives max range /* if both RMD and LMD are set to OFF or pwm cycle register is one, then PWM timer ticks doesn't occur */ if(pwm_cycle == 1 || ((pwm_ctrl & 0xf) == 0)) timer_adjust_oneshot(_32x_pwm_timer, attotime_never, 0); else { pwm_timer_tick = 0; lch_fifo_state = rch_fifo_state = 0x4000; lch_index_r = rch_index_r = 0; lch_index_w = rch_index_w = 0; timer_adjust_oneshot(_32x_pwm_timer, ATTOTIME_IN_HZ((PWM_CLOCK) / (pwm_cycle - 1)), 0); } } static TIMER_CALLBACK( _32x_pwm_callback ) { if(lch_index_r < PWM_FIFO_SIZE) { switch(pwm_ctrl & 3) { case 0: lch_index_r++; /*Speaker OFF*/ break; case 1: dac_signed_data_16_w(machine->device("lch_pwm"), cur_lch[lch_index_r++]); break; case 2: dac_signed_data_16_w(machine->device("rch_pwm"), cur_lch[lch_index_r++]); break; case 3: popmessage("Undefined PWM Lch value 3, contact MESSdev"); break; } lch_index_w = 0; } lch_fifo_state = (lch_index_r == PWM_FIFO_SIZE) ? 0x4000 : 0x0000; if(rch_index_r < PWM_FIFO_SIZE) { switch((pwm_ctrl & 0xc) >> 2) { case 0: rch_index_r++; /*Speaker OFF*/ break; case 1: dac_signed_data_16_w(machine->device("rch_pwm"), cur_rch[rch_index_r++]); break; case 2: dac_signed_data_16_w(machine->device("lch_pwm"), cur_rch[rch_index_r++]); break; case 3: popmessage("Undefined PWM Rch value 3, contact MESSdev"); break; } rch_index_w = 0; } rch_fifo_state = (rch_index_r == PWM_FIFO_SIZE) ? 0x4000 : 0x0000; pwm_timer_tick++; if(pwm_timer_tick == pwm_tm_reg) { pwm_timer_tick = 0; if(sh2_master_pwmint_enable) { cpu_set_input_line(_32x_master_cpu, SH2_PINT_IRQ_LEVEL,ASSERT_LINE); } if(sh2_slave_pwmint_enable) { cpu_set_input_line(_32x_slave_cpu, SH2_PINT_IRQ_LEVEL,ASSERT_LINE); } } timer_adjust_oneshot(_32x_pwm_timer, ATTOTIME_IN_HZ((PWM_CLOCK) / (pwm_cycle - 1)), 0); } static READ16_HANDLER( _32x_pwm_r ) { switch(offset) { case 0x00/2: return pwm_ctrl; //control register case 0x02/2: return pwm_cycle_reg; // cycle register case 0x04/2: return lch_fifo_state; // l ch case 0x06/2: return rch_fifo_state; // r ch case 0x08/2: return lch_fifo_state & rch_fifo_state; // mono ch } printf("Read at undefined PWM register %02x\n",offset); return 0xffff; } static WRITE16_HANDLER( _32x_pwm_w ) { switch(offset) { case 0x00/2: pwm_ctrl = data & 0xffff; pwm_tm_reg = (pwm_ctrl & 0xf00) >> 8; calculate_pwm_timer(); break; case 0x02/2: pwm_cycle = pwm_cycle_reg = data & 0xfff; calculate_pwm_timer(); break; case 0x04/2: if(lch_index_w < PWM_FIFO_SIZE) { cur_lch[lch_index_w++] = ((data & 0xfff) << 4) | (data & 0xf); lch_index_r = 0; } lch_fifo_state = (lch_index_w == PWM_FIFO_SIZE) ? 0x8000 : 0x0000; break; case 0x06/2: if(rch_index_w < PWM_FIFO_SIZE) { cur_rch[rch_index_w++] = ((data & 0xfff) << 4) | (data & 0xf); rch_index_r = 0; } rch_fifo_state = (rch_index_w == PWM_FIFO_SIZE) ? 0x8000 : 0x0000; break; case 0x08/2: if(lch_index_w < PWM_FIFO_SIZE) { cur_lch[lch_index_w++] = ((data & 0xfff) << 4) | (data & 0xf); lch_index_r = 0; } if(rch_index_w < PWM_FIFO_SIZE) { cur_rch[rch_index_w++] = ((data & 0xfff) << 4) | (data & 0xf); rch_index_r = 0; } lch_fifo_state = (lch_index_w == PWM_FIFO_SIZE) ? 0x8000 : 0x0000; rch_fifo_state = (rch_index_w == PWM_FIFO_SIZE) ? 0x8000 : 0x0000; break; default: printf("Write at undefined PWM register %02x %04x\n",offset,data); break; } } static WRITE16_HANDLER( _32x_68k_pwm_w ) { if(offset == 0/2) _32x_pwm_w(space,offset,(data & 0x7f) | (pwm_ctrl & 0xff80),mem_mask); else _32x_pwm_w(space,offset,data,mem_mask); } /**********************************************************************************************/ // 68k side a15180 // framebuffer control // also accessed from the SH2 @ 4100 /**********************************************************************************************/ static UINT16 _32x_a1518a_reg; static READ16_HANDLER( _32x_common_vdp_regs_r ) { // what happens if the z80 accesses it, what authorization do we use? // printf("_32x_68k_a15180_r (a15180) %04x\n",mem_mask); // read needs authorization too I think, undefined behavior otherwise switch (offset) { case 0x00: // the flag is inverted compared to the megadrive int ntsc; if (megadrive_region_pal) ntsc = 0; else ntsc = 1; return (ntsc << 15) | (_32x_videopriority << 7 ) | ( _32x_240mode << 6 ) | ( _32x_displaymode << 0 ); case 0x02/2: return _32x_screenshift; case 0x04/2: return _32x_autofill_length; case 0x06/2: return _32x_autofill_address; case 0x08/2: return _32x_autofill_data; case 0x0a/2: UINT16 retdata = _32x_a1518a_reg; UINT16 hpos = get_hposition(); int megadrive_hblank_flag = 0; if (megadrive_vblank_flag) retdata |= 0x8000; if (hpos>400) megadrive_hblank_flag = 1; if (hpos>460) megadrive_hblank_flag = 0; if (megadrive_hblank_flag) retdata |= 0x4000; if (megadrive_vblank_flag) { retdata |= 2; } // framebuffer approval (TODO: condition is unknown at current time) if (megadrive_hblank_flag && megadrive_vblank_flag) { retdata |= 0x2000; } // palette approval (TODO: active high or low?) return retdata; } return 0x0000; } void _32x_check_framebuffer_swap(void) { if(_32x_is_connected) { // this logic should be correct, but makes things worse? //if (genesis_scanline_counter >= megadrive_irq6_scanline) { _32x_a1518a_reg = _32x_fb_swap & 1; if (_32x_fb_swap & 1) { _32x_access_dram = _32x_dram0; _32x_display_dram = _32x_dram1; } else { _32x_display_dram = _32x_dram0; _32x_access_dram = _32x_dram1; } } } } static WRITE16_HANDLER( _32x_common_vdp_regs_w ) { // what happens if the z80 accesses it, what authorization do we use? which address space do we get?? the z80 *can* write here and to the framebuffer via the window address_space* _68kspace = cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM); if (space!= _68kspace) { if (_32x_access_auth!=1) return; } if (space== _68kspace) { if (_32x_access_auth!=0) return; } switch (offset) { case 0x00: //printf("_32x_68k_a15180_w (a15180) %04x %04x source _32x_access_auth %04x\n",data,mem_mask, _32x_access_auth); if (ACCESSING_BITS_0_7) { _32x_videopriority = (data & 0x80) >> 7; _32x_240mode = (data & 0x40) >> 6; _32x_displaymode = (data & 0x03) >> 0; } break; case 0x02/2: if (ACCESSING_BITS_0_7) { _32x_screenshift = data & 1; // allows 1 pixel shifting } if (ACCESSING_BITS_8_15) { } break; case 0x04/2: if (ACCESSING_BITS_0_7) { _32x_autofill_length = data & 0xff; } if (ACCESSING_BITS_8_15) { } break; case 0x06/2: if (ACCESSING_BITS_0_7) { _32x_autofill_address = (_32x_autofill_address & 0xff00) | (data & 0x00ff); } if (ACCESSING_BITS_8_15) { _32x_autofill_address = (_32x_autofill_address & 0x00ff) | (data & 0xff00); } break; case 0x08/2: if (ACCESSING_BITS_0_7) { _32x_autofill_data = (_32x_autofill_data & 0xff00) | (data & 0x00ff); } if (ACCESSING_BITS_8_15) { _32x_autofill_data = (_32x_autofill_data & 0x00ff) | (data & 0xff00); } // do the fill - shouldn't be instant.. { int i; for (i=0; i<_32x_autofill_length+1;i++) { _32x_access_dram[_32x_autofill_address] = _32x_autofill_data; _32x_autofill_address = (_32x_autofill_address & 0xff00) | ((_32x_autofill_address+1) & 0x00ff); } } break; case 0x0a/2: // bit 0 is the framebuffer select, change is delayed until vblank; // _32x_a1518a_reg = (_32x_a1518a_reg & 0xfffe); if (ACCESSING_BITS_0_7) { _32x_fb_swap = data & 1; _32x_check_framebuffer_swap(); } break; } } /**********************************************************************************************/ // SH2 side 4000 // IRQ Control // Different for each SH2 /* f--- --ec h--- VHCP f = framebuffer permission (0 md, 1 sh2) e = Adapter enabled (0 no, 1 yes) c = Cart Inserted (0 yes, 1 no) h = H Interrupt allowed within Vblank (0 no, 1 yes) *** these are independent for each SH2 *** V = V Interrupt Mask (0 masked, 1 allowed) H = H Interrupt Mask (0 masked, 1 allowed) C = Command Interrupt Mask (0 masked, 1 allowed) P = PWM Interrupt Mask (0 masked, 1 allowed) */ /**********************************************************************************************/ /* MASTER */ static READ16_HANDLER( _32x_sh2_master_4000_r ) { UINT16 retvalue = 0x0200; retvalue |= _32x_access_auth << 15; retvalue |= sh2_hint_in_vbl; retvalue |= sh2_master_vint_enable; retvalue |= sh2_master_hint_enable; retvalue |= sh2_master_cmdint_enable; retvalue |= sh2_master_pwmint_enable; return retvalue; } static WRITE16_HANDLER( _32x_sh2_master_4000_w ) { if (ACCESSING_BITS_8_15) { _32x_access_auth = (data &0x8000) >> 15; } if (ACCESSING_BITS_0_7) { sh2_hint_in_vbl = data & 0x80; sh2_master_vint_enable = data & 0x8; sh2_master_hint_enable = data & 0x4; sh2_master_cmdint_enable = data & 0x2; sh2_master_pwmint_enable = data & 0x1; //if (sh2_master_hint_enable) printf("sh2_master_hint_enable enable!\n"); //if (sh2_master_pwmint_enable) printf("sh2_master_pwn_enable enable!\n"); _32x_check_irqs(space->machine); } } /* SLAVE */ static READ16_HANDLER( _32x_sh2_slave_4000_r ) { UINT16 retvalue = 0x0200; retvalue |= _32x_access_auth << 15; retvalue |= sh2_hint_in_vbl; retvalue |= sh2_slave_vint_enable; retvalue |= sh2_slave_hint_enable; retvalue |= sh2_slave_cmdint_enable; retvalue |= sh2_slave_pwmint_enable; return retvalue; } static WRITE16_HANDLER( _32x_sh2_slave_4000_w ) { if (ACCESSING_BITS_8_15) { _32x_access_auth = (data &0x8000) >> 15; } if (ACCESSING_BITS_0_7) { sh2_hint_in_vbl = data & 0x80; sh2_slave_vint_enable = data & 0x8; sh2_slave_hint_enable = data & 0x4; sh2_slave_cmdint_enable = data & 0x2; sh2_slave_pwmint_enable = data & 0x1; //if (sh2_slave_hint_enable) printf("sh2_slave_hint_enable enable!\n"); //if (sh2_slave_pwmint_enable) printf("sh2_slave_pwm_enable enable!\n"); _32x_check_irqs(space->machine); } } /**********************************************************************************************/ // SH2 side 4002 // Reserved ( Stand By Change Register ) // Shouldn't be used /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_common_4002_r ) { printf("reading 4002!\n"); return 0x0000; } static WRITE16_HANDLER( _32x_sh2_common_4002_w ) { printf("write 4002!\n"); } /**********************************************************************************************/ // SH2 side 4004 // H Count Register (H Interrupt) // 0 = every line /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_common_4004_r ) { return _32x_hcount_reg; } static WRITE16_HANDLER( _32x_sh2_common_4004_w ) { _32x_hcount_reg = data & 0xff; } /**********************************************************************************************/ // SH2 side 4006 // DReq Control Register /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_common_4006_r ) { //printf("DREQ read!\n"); // tempo reads it, shut up for now return _32x_68k_a15106_r(space,offset,mem_mask); } static WRITE16_HANDLER( _32x_sh2_common_4006_w ) { printf("DREQ write!\n"); //register is read only on SH-2 side } /**********************************************************************************************/ // SH2 side 4014 // VRES (md reset button interrupt) clear /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_4014_w ){ cpu_set_input_line(_32x_master_cpu,SH2_VRES_IRQ_LEVEL,CLEAR_LINE);} static WRITE16_HANDLER( _32x_sh2_slave_4014_w ) { cpu_set_input_line(_32x_slave_cpu, SH2_VRES_IRQ_LEVEL,CLEAR_LINE);} /**********************************************************************************************/ // SH2 side 4016 // VINT (vertical interrupt) clear /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_4016_w ){ sh2_master_vint_pending = 0; _32x_check_irqs(space->machine); } static WRITE16_HANDLER( _32x_sh2_slave_4016_w ) { sh2_slave_vint_pending = 0; _32x_check_irqs(space->machine); } /**********************************************************************************************/ // SH2 side 4018 // HINT (horizontal interrupt) clear /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_4018_w ){ cpu_set_input_line(_32x_master_cpu,SH2_HINT_IRQ_LEVEL,CLEAR_LINE);} static WRITE16_HANDLER( _32x_sh2_slave_4018_w ) { cpu_set_input_line(_32x_slave_cpu, SH2_HINT_IRQ_LEVEL,CLEAR_LINE);} /**********************************************************************************************/ // SH2 side 401A // HINT (control register interrupt) clear // Note: flag cleared here is a guess, according to After Burner behaviour /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_401a_w ){ _32x_68k_a15102_reg &= ~1; cpu_set_input_line(_32x_master_cpu,SH2_CINT_IRQ_LEVEL,CLEAR_LINE);} static WRITE16_HANDLER( _32x_sh2_slave_401a_w ) { _32x_68k_a15102_reg &= ~2; cpu_set_input_line(_32x_slave_cpu, SH2_CINT_IRQ_LEVEL,CLEAR_LINE);} /**********************************************************************************************/ // SH2 side 401C // PINT (PWM timer interrupt) clear /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_401c_w ){ cpu_set_input_line(_32x_master_cpu,SH2_PINT_IRQ_LEVEL,CLEAR_LINE);} static WRITE16_HANDLER( _32x_sh2_slave_401c_w ) { cpu_set_input_line(_32x_slave_cpu, SH2_PINT_IRQ_LEVEL,CLEAR_LINE);} /**********************************************************************************************/ // SH2 side 401E // ?? unknown / unused /**********************************************************************************************/ static WRITE16_HANDLER( _32x_sh2_master_401e_w ) { printf("_32x_sh2_master_401e_w\n"); } static WRITE16_HANDLER( _32x_sh2_slave_401e_w ) { printf("_32x_sh2_slave_401e_w\n"); } /**********************************************************************************************/ // SH2 side 4020 - 402f // SH2 -> 68k Comms ports, // access at a15120 - a1512f on 68k // these just map through to the 68k functions /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_commsram16_r ) { return _32x_68k_commsram_r(space, offset, mem_mask); } static WRITE16_HANDLER( _32x_sh2_commsram16_w ) { _32x_68k_commsram_w(space, offset, data, mem_mask); } /**********************************************************************************************/ // SH2 side 4030 // PWM Control Register /**********************************************************************************************/ /**********************************************************************************************/ // SH2 side 4032 // Cycle Register /**********************************************************************************************/ /**********************************************************************************************/ // SH2 side 4034 // LCH Pulse Width Register /**********************************************************************************************/ /**********************************************************************************************/ // SH2 side 4036 // RCH Pulse Width Register /**********************************************************************************************/ /**********************************************************************************************/ // SH2 side 4038 // Mono Pulse Width Register /**********************************************************************************************/ /* 4100 - 43ff are VDP registers, you need permission to access them - ensure this is true for all! */ /**********************************************************************************************/ // SH2 side 4200 - 43ff // palette // maps through to 68k at a15200 - a153ff /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_paletteram16_r ) { return _32x_68k_palette_r(space,offset,mem_mask); } static WRITE16_HANDLER( _32x_sh2_paletteram16_w ) { _32x_68k_palette_w(space,offset,data,mem_mask); } /**********************************************************************************************/ // SH2 side 4000000 - 401ffff // framebuffer // maps through to 68k at 840000 - 85ffff /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_framebuffer_dram16_r ) { return _32x_68k_dram_r(space,offset,mem_mask); } static WRITE16_HANDLER( _32x_sh2_framebuffer_dram16_w ) { _32x_68k_dram_w(space,offset,data,mem_mask); } /**********************************************************************************************/ // SH2 side 4020000 - 403ffff // framebuffer overwrite // maps through to 68k at 860000 - 87ffff /**********************************************************************************************/ static READ16_HANDLER( _32x_sh2_framebuffer_overwrite_dram16_r ) { return _32x_68k_dram_overwrite_r(space,offset,mem_mask); } static WRITE16_HANDLER( _32x_sh2_framebuffer_overwrite_dram16_w ) { _32x_68k_dram_overwrite_w(space,offset,data,mem_mask); } /**********************************************************************************************/ // SH2 access Macros /**********************************************************************************************/ /* the 32x treats everything as 16-bit registers, so we remap the 32-bit read & writes to 2x 16-bit handlers here (TODO: nuke this eventually) */ #define _32X_MAP_READHANDLERS(NAMEA,NAMEB) \ static READ32_HANDLER( _32x_sh2_##NAMEA##_##NAMEB##_r ) \ { \ UINT32 retvalue = 0x00000000; \ if (ACCESSING_BITS_16_31) \ { \ UINT16 ret = _32x_sh2_##NAMEA##_r(space,0,(mem_mask>>16)&0xffff); \ retvalue |= ret << 16; \ } \ if (ACCESSING_BITS_0_15) \ { \ UINT16 ret = _32x_sh2_##NAMEB##_r(space,0,(mem_mask>>0)&0xffff); \ retvalue |= ret << 0; \ } \ \ return retvalue; \ } \ #define _32X_MAP_WRITEHANDLERS(NAMEA,NAMEB) \ static WRITE32_HANDLER( _32x_sh2_##NAMEA##_##NAMEB##_w) \ { \ if (ACCESSING_BITS_16_31) \ { \ _32x_sh2_##NAMEA##_w(space,0,(data>>16)&0xffff,(mem_mask>>16)&0xffff); \ } \ if (ACCESSING_BITS_0_15) \ { \ _32x_sh2_##NAMEB##_w(space,0,(data>>0)&0xffff,(mem_mask>>0)&0xffff); \ } \ } \ /* for RAM ranges, eg. Framebuffer, Comms RAM etc. */ #define _32X_MAP_RAM_READHANDLERS(NAMEA) \ static READ32_HANDLER( _32x_sh2_##NAMEA##_r ) \ { \ UINT32 retvalue = 0x00000000; \ if (ACCESSING_BITS_16_31) \ { \ UINT16 ret = _32x_sh2_##NAMEA##16_r(space,offset*2,(mem_mask>>16)&0xffff); \ retvalue |= ret << 16; \ } \ if (ACCESSING_BITS_0_15) \ { \ UINT16 ret = _32x_sh2_##NAMEA##16_r(space,offset*2+1,(mem_mask>>0)&0xffff); \ retvalue |= ret << 0; \ } \ \ return retvalue; \ } \ #define _32X_MAP_RAM_WRITEHANDLERS(NAMEA) \ static WRITE32_HANDLER( _32x_sh2_##NAMEA##_w) \ { \ if (ACCESSING_BITS_16_31) \ { \ _32x_sh2_##NAMEA##16_w(space,offset*2,(data>>16)&0xffff,(mem_mask>>16)&0xffff); \ } \ if (ACCESSING_BITS_0_15) \ { \ _32x_sh2_##NAMEA##16_w(space,offset*2+1,(data>>0)&0xffff,(mem_mask>>0)&0xffff); \ } \ } \ /**********************************************************************************************/ // SH2 access for Memory Map /**********************************************************************************************/ _32X_MAP_READHANDLERS(master_4000,common_4002) // _32x_sh2_master_4000_common_4002_r _32X_MAP_WRITEHANDLERS(master_4000,common_4002) // _32x_sh2_master_4000_common_4002_w _32X_MAP_READHANDLERS(slave_4000,common_4002) // _32x_sh2_slave_4000_common_4002_r _32X_MAP_WRITEHANDLERS(slave_4000,common_4002) // _32x_sh2_slave_4000_common_4002_w _32X_MAP_READHANDLERS(common_4004,common_4006) _32X_MAP_WRITEHANDLERS(common_4004,common_4006) _32X_MAP_WRITEHANDLERS(master_4014,master_4016) // _32x_sh2_master_4014_master_4016_w _32X_MAP_WRITEHANDLERS(master_4018,master_401a) // _32x_sh2_master_4018_master_401a_w _32X_MAP_WRITEHANDLERS(master_401c,master_401e) // _32x_sh2_master_401c_master_401e_w _32X_MAP_WRITEHANDLERS(slave_4014,slave_4016) // _32x_sh2_slave_4014_slave_4016_w _32X_MAP_WRITEHANDLERS(slave_4018,slave_401a) // _32x_sh2_slave_4018_slave_401a_w _32X_MAP_WRITEHANDLERS(slave_401c,slave_401e) // _32x_sh2_slave_401c_slave_401e_w _32X_MAP_RAM_READHANDLERS(commsram) // _32x_sh2_commsram_r _32X_MAP_RAM_WRITEHANDLERS(commsram) // _32x_sh2_commsram_w _32X_MAP_RAM_READHANDLERS(framebuffer_dram) // _32x_sh2_framebuffer_dram_r _32X_MAP_RAM_WRITEHANDLERS(framebuffer_dram) // _32x_sh2_framebuffer_dram_w _32X_MAP_RAM_READHANDLERS(framebuffer_overwrite_dram) // _32x_sh2_framebuffer_overwrite_dram_r _32X_MAP_RAM_WRITEHANDLERS(framebuffer_overwrite_dram) // _32x_sh2_framebuffer_overwrite_dram_w _32X_MAP_RAM_READHANDLERS(paletteram) // _32x_sh2_paletteram_r _32X_MAP_RAM_WRITEHANDLERS(paletteram) // _32x_sh2_paletteram_w /**********************************************************************************************/ // SH2 memory maps /**********************************************************************************************/ static ADDRESS_MAP_START( sh2_main_map, ADDRESS_SPACE_PROGRAM, 32 ) AM_RANGE(0x00000000, 0x00003fff) AM_ROM AM_RANGE(0x00004000, 0x00004003) AM_READWRITE( _32x_sh2_master_4000_common_4002_r, _32x_sh2_master_4000_common_4002_w ) AM_RANGE(0x00004004, 0x00004007) AM_READWRITE( _32x_sh2_common_4004_common_4006_r, _32x_sh2_common_4004_common_4006_w) AM_RANGE(0x00004008, 0x00004013) AM_READWRITE16( _32x_dreq_common_r, _32x_dreq_common_w, 0xffffffff ) AM_RANGE(0x00004014, 0x00004017) AM_READNOP AM_WRITE( _32x_sh2_master_4014_master_4016_w ) // IRQ clear AM_RANGE(0x00004018, 0x0000401b) AM_READNOP AM_WRITE( _32x_sh2_master_4018_master_401a_w ) // IRQ clear AM_RANGE(0x0000401c, 0x0000401f) AM_READNOP AM_WRITE( _32x_sh2_master_401c_master_401e_w ) // IRQ clear AM_RANGE(0x00004020, 0x0000402f) AM_READWRITE( _32x_sh2_commsram_r, _32x_sh2_commsram_w ) AM_RANGE(0x00004030, 0x0000403f) AM_READWRITE16( _32x_pwm_r, _32x_pwm_w, 0xffffffff ) AM_RANGE(0x00004100, 0x0000410b) AM_READWRITE16( _32x_common_vdp_regs_r, _32x_common_vdp_regs_w , 0xffffffff) AM_RANGE(0x00004200, 0x000043ff) AM_READWRITE( _32x_sh2_paletteram_r, _32x_sh2_paletteram_w) AM_RANGE(0x04000000, 0x0401ffff) AM_READWRITE(_32x_sh2_framebuffer_dram_r, _32x_sh2_framebuffer_dram_w) AM_RANGE(0x04020000, 0x0403ffff) AM_READWRITE(_32x_sh2_framebuffer_overwrite_dram_r, _32x_sh2_framebuffer_overwrite_dram_w) AM_RANGE(0x06000000, 0x0603ffff) AM_RAM AM_SHARE("share10") AM_RANGE(0x02000000, 0x023fffff) AM_ROM AM_REGION("gamecart_sh2", 0) // program is writeable (wwfraw) AM_RANGE(0x22000000, 0x223fffff) AM_ROM AM_REGION("gamecart_sh2", 0) // cart mirror (fifa96) AM_RANGE(0xc0000000, 0xc0000fff) AM_RAM ADDRESS_MAP_END static ADDRESS_MAP_START( sh2_slave_map, ADDRESS_SPACE_PROGRAM, 32 ) AM_RANGE(0x00000000, 0x00003fff) AM_ROM AM_RANGE(0x00004000, 0x00004003) AM_READWRITE( _32x_sh2_slave_4000_common_4002_r, _32x_sh2_slave_4000_common_4002_w ) AM_RANGE(0x00004004, 0x00004007) AM_READWRITE( _32x_sh2_common_4004_common_4006_r, _32x_sh2_common_4004_common_4006_w) AM_RANGE(0x00004008, 0x00004013) AM_READWRITE16( _32x_dreq_common_r, _32x_dreq_common_w, 0xffffffff ) AM_RANGE(0x00004014, 0x00004017) AM_READNOP AM_WRITE( _32x_sh2_slave_4014_slave_4016_w ) // IRQ clear AM_RANGE(0x00004018, 0x0000401b) AM_READNOP AM_WRITE( _32x_sh2_slave_4018_slave_401a_w ) // IRQ clear AM_RANGE(0x0000401c, 0x0000401f) AM_READNOP AM_WRITE( _32x_sh2_slave_401c_slave_401e_w ) // IRQ clear AM_RANGE(0x00004020, 0x0000402f) AM_READWRITE( _32x_sh2_commsram_r, _32x_sh2_commsram_w ) AM_RANGE(0x00004030, 0x0000403f) AM_READWRITE16( _32x_pwm_r, _32x_pwm_w, 0xffffffff ) AM_RANGE(0x00004100, 0x0000410b) AM_READWRITE16( _32x_common_vdp_regs_r, _32x_common_vdp_regs_w , 0xffffffff) AM_RANGE(0x00004200, 0x000043ff) AM_READWRITE(_32x_sh2_paletteram_r, _32x_sh2_paletteram_w) AM_RANGE(0x04000000, 0x0401ffff) AM_READWRITE(_32x_sh2_framebuffer_dram_r, _32x_sh2_framebuffer_dram_w) AM_RANGE(0x04020000, 0x0403ffff) AM_READWRITE(_32x_sh2_framebuffer_overwrite_dram_r, _32x_sh2_framebuffer_overwrite_dram_w) AM_RANGE(0x06000000, 0x0603ffff) AM_RAM AM_SHARE("share10") AM_RANGE(0x02000000, 0x023fffff) AM_ROM AM_REGION("gamecart_sh2", 0) // program is writeable (wwfraw) AM_RANGE(0x22000000, 0x223fffff) AM_ROM AM_REGION("gamecart_sh2", 0) // cart mirror (fifa96) AM_RANGE(0xc0000000, 0xc0000fff) AM_RAM ADDRESS_MAP_END /****************************************** END 32X related *************************************/ /************************************************************************************************* Sega CD related *************************************************************************************************/ static UINT16* segacd_4meg_prgram; // pointer to SubCPU PrgRAM static UINT16 segacd_hint_register; static UINT16 segacd_imagebuffer_vdot_size; static UINT16 segacd_imagebuffer_vcell_size; static UINT16 segacd_imagebuffer_hdot_size; static UINT16 a12000_halt_reset_reg = 0x0000; int segacd_conversion_active = 0; static UINT16 segacd_stampmap_base_address; static UINT16 segacd_imagebuffer_start_address; static UINT16 segacd_imagebuffer_offset; static tilemap_t *segacd_stampmap[4]; //static void segacd_mark_stampmaps_dirty(void); static WRITE16_HANDLER( scd_a12000_halt_reset_w ) { COMBINE_DATA(&a12000_halt_reset_reg); if (ACCESSING_BITS_0_7) { // reset line if (a12000_halt_reset_reg&0x0001) cputag_set_input_line(space->machine, "segacd_68k", INPUT_LINE_RESET, CLEAR_LINE); else cputag_set_input_line(space->machine, "segacd_68k", INPUT_LINE_RESET, ASSERT_LINE); // request BUS if (a12000_halt_reset_reg&0x0002) cputag_set_input_line(space->machine, "segacd_68k", INPUT_LINE_HALT, ASSERT_LINE); else cputag_set_input_line(space->machine, "segacd_68k", INPUT_LINE_HALT, CLEAR_LINE); } if (ACCESSING_BITS_8_15) { if (a12000_halt_reset_reg&0x0100) { // check if it's masked! (irq check function?) cputag_set_input_line(space->machine, "segacd_68k", 2, ASSERT_LINE); } if (a12000_halt_reset_reg&0x8000) { printf("a12000_halt_reset_reg & 0x8000 set\n"); // irq2 mask? } } } static READ16_HANDLER( scd_a12000_halt_reset_r ) { return a12000_halt_reset_reg; } /******************************************************************************** MEMORY MODE CONTROL - main / sub sides differ! ********************************************************************************/ // // we might need a delay on the segacd_maincpu_has_ram_access registers, as they actually indicate requests being made // so probably don't change instantly... // int segacd_dmna = 0; int segacd_ret = 0; static TIMER_CALLBACK( segacd_dmna_ret_timer_callback ) { // this is the initial state, and the state after changing modes? if ((segacd_dmna==0) && (segacd_ret ==0)) { //printf("aaa %d %d\n", segacd_dmna, segacd_ret); segacd_dmna = 0; segacd_ret = 1; //printf("bbb %d %d\n", segacd_dmna, segacd_ret); } else if ((segacd_dmna==1) && (segacd_ret == 1)) { //printf("aaa %d %d\n", segacd_dmna, segacd_ret); segacd_dmna = 1; segacd_ret = 0; //printf("bbb %d %d\n", segacd_dmna, segacd_ret); } else if ((segacd_dmna==1) && (segacd_ret == 0)) { //printf("aaa %d %d\n", segacd_dmna, segacd_ret); segacd_dmna = 0; segacd_ret = 1; //printf("bbb %d %d\n", segacd_dmna, segacd_ret); } else { printf("huh? %d %d\n", segacd_dmna, segacd_ret); } } static UINT8 segacd_ram_writeprotect_bits; static int segacd_ram_mode; static int segacd_ram_mode_old; //static int segacd_maincpu_has_ram_access = 0; static int segacd_4meg_prgbank = 0; // which bank the MainCPU can see of the SubCPU PrgRAM static int segacd_memory_priority_mode = 0; static int segacd_stampsize; static READ16_HANDLER( scd_a12002_memory_mode_r ) { return (segacd_ram_writeprotect_bits << 8) | (segacd_4meg_prgbank << 6) | (segacd_ram_mode << 2) | ((segacd_dmna) << 1) | ((segacd_ret) << 0); } /* I'm still not 100% clear how this works, the sources I have are a bit vague, it might still be incorrect in both modes for a simple way to swap blocks of ram between cpus this is stupidly convoluted */ // DMNA = Decleration Mainram No Access (bit 0) // RET = Return access (bit 1) /* In Mode 0 */ /* In Mode 1 RET 0 = Main CPU Accesses WordRam0 (1st Half of WordRAM) Sub CPU Accesses WordRam1 (2nd Half of WordRAM) 1 = Main CPU Accesses WordRam1 (2nd Half of WordRAM) Sub CPU Accesses WordRam0 (1st Half of WordRAM) DMNA Setting this bis sends a Swap request to the SUB-CPU (Main CPU access only?) 1 = Swap Reqested / In Progress 0 = Swap Complete (personal note, is this just a software flag? in this mode? sub p11/p12) */ static WRITE16_HANDLER( scd_a12002_memory_mode_w ) { //printf("scd_a12002_memory_mode_w %04x %04x\n", data, mem_mask); if (ACCESSING_BITS_0_7) { //if (data&0x0001) printf("ret bit set (invalid? can't set from main68k?)\n"); if (data&0x0002) { //printf("dmna set (swap requested)\n"); // give ram to sub? // this should take some time? //segacd_ret = 1; //printf("main cpu dmna set dmna: %d ret: %d\n", segacd_dmna, segacd_ret); if (segacd_ram_mode==0) { if (!timer_enabled(segacd_dmna_ret_timer)) { if (!segacd_dmna) { //printf("main dmna\n"); segacd_dmna = 1; timer_adjust_oneshot(segacd_dmna_ret_timer, ATTOTIME_IN_USEC(100), 0); } } } else { printf("dmna bit in mode 1\n"); } } //if (data&0x0004) printf("mode set (invalid? can't set from main68k?)\n"); //if (data&0x0038) printf("unknown bits set\n"); //if (data&0x00c0) { //printf("bank set to %02x\n", (data&0x00c0)>>6); segacd_4meg_prgbank = (data&0x00c0)>>6; } } if (ACCESSING_BITS_8_15) { if (data & 0xff00) { printf("write protect bits set %02x\n", data >> 8); } segacd_ram_writeprotect_bits = data >> 8; } } // can't read the bank? static READ16_HANDLER( segacd_sub_memory_mode_r ) { return (segacd_ram_writeprotect_bits << 8) | /*(segacd_4meg_prgbank << 6) | */ (segacd_memory_priority_mode << 3) | (segacd_ram_mode << 2) | ((segacd_dmna) << 1) | ((segacd_ret) << 0); } static WRITE16_HANDLER( segacd_sub_memory_mode_w ) { //printf("segacd_sub_memory_mode_w %04x %04x\n", data, mem_mask); if (ACCESSING_BITS_0_7) { if (data&0x0001) { //printf("ret bit set\n"); //segacd_dmna = 0; //printf("sub cpu ret set dmna: %d ret: %d\n", segacd_dmna, segacd_ret); if (segacd_ram_mode==0) { if (!timer_enabled(segacd_dmna_ret_timer)) { if (segacd_dmna) { // printf("sub ret\n"); // segacd_ret = 1; // segacd_dmna = 0; timer_adjust_oneshot(segacd_dmna_ret_timer, ATTOTIME_IN_USEC(100), 0); } } } else { // in mode 1 this changes the word ram 1 to main cpu and word ram 0 to sub cpu? // but should be proceeded by a dmna request? is this only valid if dmna has been // set to 1 by the main CPU first? printf("ret bit in mode 1\n"); segacd_ret = 1; } } else { // in mode 1 this changes the word ram 0 to main cpu and word ram 1 to sub cpu? // but should be proceeded by a dmna request? is this only valid if dmna has been // set to 1 by the main CPU first? if (segacd_ram_mode==1) { segacd_ret = 0; } } //if (data&0x0002) printf("dmna set (swap requested) (invalid, can't be set from sub68k?\n"); //if (data&0x0004) { segacd_ram_mode = (data&0x0004)>>2; if (segacd_ram_mode!=segacd_ram_mode_old) { printf("mode set %d\n", segacd_ram_mode); segacd_ram_mode_old = segacd_ram_mode; if (segacd_ram_mode==0) { // reset it flags etc.? segacd_ret = 0; segacd_dmna = 0; timer_adjust_oneshot(segacd_dmna_ret_timer, ATTOTIME_IN_USEC(100), 0); } else { segacd_ret = 0; segacd_dmna = 0; } } } //if (data&0x0018) { segacd_memory_priority_mode = (data&0x0018)>>3; //printf("priority mode bits set to %d\n", segacd_memory_priority_mode); } //if (data&0x00e0) printf("unknown bits set\n"); } if (ACCESSING_BITS_8_15) { if (data & 0xff00) { printf("write protect bits set %02x (invalid, can only be set by main68k)\n", data >> 8); } } } /******************************************************************************** END MEMORY MODE CONTROL ********************************************************************************/ /******************************************************************************** COMMUNICATION FLAGS - main / sub sides differ in which bits are write only ********************************************************************************/ static UINT16 segacd_comms_flags = 0x0000; static READ16_HANDLER( segacd_comms_flags_r ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); return segacd_comms_flags; } static WRITE16_HANDLER( segacd_comms_flags_subcpu_w ) { if (ACCESSING_BITS_0_7) { segacd_comms_flags = (segacd_comms_flags & 0xff00) | (data & 0x00ff); timer_call_after_resynch(space->machine, NULL, 0, NULL); } if (ACCESSING_BITS_8_15) { if (data & 0xff00) { printf("sub cpu attempting to write non-zero data to read-only comms flags!\n"); } } } static WRITE16_HANDLER( segacd_comms_flags_maincpu_w ) { if (ACCESSING_BITS_0_7) { if (data & 0x00ff) { printf("main cpu attempting to write non-zero data to read-only comms flags!\n"); } } if (ACCESSING_BITS_8_15) { segacd_comms_flags = (segacd_comms_flags & 0x00ff) | (data & 0xff00); timer_call_after_resynch(space->machine, NULL, 0, NULL); } } static WRITE16_HANDLER( scd_4m_prgbank_ram_w ) { UINT16 realoffset = ((segacd_4meg_prgbank * 0x20000)/2) + offset; // todo: // check for write protection? (or does that only apply to writes on the SubCPU side? COMBINE_DATA(&segacd_4meg_prgram[realoffset]); } /* Callback when the genesis enters interrupt code */ static IRQ_CALLBACK(segacd_sub_int_callback) { if (irqline==2) { // clear this bit a12000_halt_reset_reg &= ~0x0100; cputag_set_input_line(device->machine, "segacd_68k", 2, CLEAR_LINE); } return (0x60+irqline*4)/4; // vector address } UINT16 segacd_comms_part1[0x8]; UINT16 segacd_comms_part2[0x8]; static READ16_HANDLER( segacd_comms_main_part1_r ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); return segacd_comms_part1[offset]; } static WRITE16_HANDLER( segacd_comms_main_part1_w ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); COMBINE_DATA(&segacd_comms_part1[offset]); } static READ16_HANDLER( segacd_comms_main_part2_r ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); return segacd_comms_part2[offset]; } static WRITE16_HANDLER( segacd_comms_main_part2_w ) { printf("Sega CD main CPU attempting to write to read only comms regs\n"); } static READ16_HANDLER( segacd_comms_sub_part1_r ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); return segacd_comms_part1[offset]; } static WRITE16_HANDLER( segacd_comms_sub_part1_w ) { printf("Sega CD sub CPU attempting to write to read only comms regs\n"); } static READ16_HANDLER( segacd_comms_sub_part2_r ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); return segacd_comms_part2[offset]; } static WRITE16_HANDLER( segacd_comms_sub_part2_w ) { timer_call_after_resynch(space->machine, NULL, 0, NULL); COMBINE_DATA(&segacd_comms_part2[offset]); } /************************************************************** CDC Stuff ******** **************************************************************/ static int segacd_cdc_regaddress; static int segacd_cdc_destination_device; static UINT8 segacd_cdc_registers[0x10]; static WRITE16_HANDLER( segacd_cdc_mode_address_w ) { if (ACCESSING_BITS_0_7) { segacd_cdc_regaddress = data & 0x0f; if (data & 0xf0) printf("unknown bits set in register address write %04x\n", data); printf("CDC register address set to %02x\n" , segacd_cdc_regaddress); } if (ACCESSING_BITS_8_15) { segacd_cdc_destination_device = (data & 0x0700)>>8; if (data & 0xf800) printf("unknown bits set in destination address write %04x\n",data); printf("destination set to: "); switch (segacd_cdc_destination_device&0x7) { case 0x00: printf("Illegal\n"); break; case 0x01: printf("Illegal\n"); break; case 0x02: printf("Main Cpu Read\n"); break; case 0x03: printf("Sub Cpu Read\n"); break; case 0x04: printf("RFC5164 PCM\n"); break; case 0x05: printf("PRG RAM\n"); break; case 0x06: printf("Illegal\n"); break; case 0x07: printf("2M AM (2M Mode) / SUB CPU 1H RAM\n"); break; } } } static READ16_HANDLER( segacd_cdc_mode_address_r ) { UINT16 retdata = 0x0000; if (ACCESSING_BITS_0_7) { retdata |= segacd_cdc_regaddress; } if (ACCESSING_BITS_8_15) { retdata |= segacd_cdc_destination_device << 8; // todo: hook them up int data_set_ready = 0; int end_of_data_transfer = 0; if (data_set_ready) retdata |= 0x4000; if (end_of_data_transfer) retdata |= 0x8000; } return retdata; } static WRITE16_HANDLER( segacd_cdc_data_w ) { if (ACCESSING_BITS_0_7) { segacd_cdc_registers[segacd_cdc_regaddress] = data; printf("CDC Register %02x set to %02x\n", segacd_cdc_regaddress, data & 0xff); } if (ACCESSING_BITS_8_15) { // nothing here? } } static READ16_HANDLER( segacd_cdc_data_r ) { UINT16 retdata = 0x0000; if (ACCESSING_BITS_0_7) { retdata |= segacd_cdc_registers[segacd_cdc_regaddress]; printf("CDC Register %02x Read\n", segacd_cdc_regaddress); } if (ACCESSING_BITS_8_15) { // nothing here? } return retdata; } static UINT16* segacd_dataram; static UINT16* segacd_dataram2; static READ16_HANDLER( segacd_main_dataram_part1_r ) { if (segacd_ram_mode==0) { // is this correct? if (!segacd_dmna) { //printf("segacd_main_dataram_part1_r in mode 0 %08x %04x\n", offset*2, segacd_dataram[offset]); return segacd_dataram[offset]; } else { printf("Illegal: segacd_main_dataram_part1_r in mode 0 without permission\n"); return 0xffff; } } else if (segacd_ram_mode==1) { if (offset<0x20000/2) { // wordram accees //printf("Unspported: segacd_main_dataram_part1_r (word RAM) in mode 1\n"); // ret bit set by sub cpu determines which half of WorkRAM we have access to? if (segacd_ret) { return segacd_dataram[offset+0x20000/2]; } else { return segacd_dataram[offset+0x00000/2]; } } else { printf("Unspported: segacd_main_dataram_part1_r (Cell rearranged RAM) in mode 1\n"); return 0x0000; } } return 0x0000; } static WRITE16_HANDLER( segacd_main_dataram_part1_w ) { if (segacd_ram_mode==0) { // is this correct? if (!segacd_dmna) { COMBINE_DATA(&segacd_dataram[offset]); segacd_mark_tiles_dirty(space->machine, offset); } else { printf("Illegal: segacd_main_dataram_part1_w in mode 0 without permission\n"); } } else if (segacd_ram_mode==1) { if (offset<0x20000/2) { //printf("Unspported: segacd_main_dataram_part1_w (word RAM) in mode 1\n"); // wordram accees // ret bit set by sub cpu determines which half of WorkRAM we have access to? if (segacd_ret) { COMBINE_DATA(&segacd_dataram[offset+0x20000/2]); } else { COMBINE_DATA(&segacd_dataram[offset+0x00000/2]); } } else { printf("Unspported: segacd_main_dataram_part1_w (Cell rearranged RAM) in mode 1 (illega?)\n"); // is this legal?? } } } static READ16_HANDLER( scd_hint_vector_r ) { // printf("read HINT offset %d\n", offset); switch (offset&1) { case 0x00: //return 0x00ff; // doesn't make much sense.. return 0xffff; case 0x01: return segacd_hint_register; } return 0; } static READ16_HANDLER( scd_a12006_hint_register_r ) { return segacd_hint_register; } static WRITE16_HANDLER( scd_a12006_hint_register_w ) { COMBINE_DATA(&segacd_hint_register); } static TIMER_CALLBACK( segacd_gfx_conversion_timer_callback ) { // todo irqmask //printf("segacd_gfx_conversion_timer_callback\n"); if (segacd_irq_mask & 0x02) cputag_set_input_line(machine, "segacd_68k", 1, HOLD_LINE); segacd_conversion_active = 0; } // the tiles in RAM are 8x8 tiles // they are referenced in the cell look-up map as either 16x16 or 32x32 tiles (made of 4 / 16 8x8 tiles) #define SEGACD_BYTES_PER_TILE16 (128) #define SEGACD_BYTES_PER_TILE32 (512) #define SEGACD_NUM_TILES16 (0x40000/SEGACD_BYTES_PER_TILE16) #define SEGACD_NUM_TILES32 (0x40000/SEGACD_BYTES_PER_TILE32) /* static const gfx_layout sega_8x8_layout = { 8,8, SEGACD_NUM_TILES16, 4, { 0,1,2,3 }, { 8,12,0,4,24,28,16,20 }, { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 }, 8*32 }; */ /* also create pre-rotated versions.. - it might still be possible to use these decodes with our own copying routines */ #define _16x16_SEQUENCE_1 { 8,12,0,4,24,28,16,20, 512+8, 512+12, 512+0, 512+4, 512+24, 512+28, 512+16, 512+20 }, #define _16x16_SEQUENCE_1_FLIP { 512+20,512+16,512+28,512+24,512+4,512+0, 512+12,512+8, 20,16,28,24,4,0,12,8 }, #define _16x16_SEQUENCE_2 { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32, 8*32, 9*32,10*32,11*32,12*32,13*32,14*32,15*32 }, #define _16x16_SEQUENCE_2_FLIP { 15*32, 14*32, 13*32, 12*32, 11*32, 10*32, 9*32, 8*32, 7*32, 6*32, 5*32, 4*32, 3*32, 2*32, 1*32, 0*32 }, #define _16x16_START \ { \ 16,16, \ SEGACD_NUM_TILES16, \ 4, \ { 0,1,2,3 }, \ #define _16x16_END \ 8*128 \ }; \ #define _32x32_START \ { \ 32,32, \ SEGACD_NUM_TILES32, \ 4, \ { 0,1,2,3 }, \ #define _32x32_END \ 8*512 \ }; \ #define _32x32_SEQUENCE_1 \ { 8,12,0,4,24,28,16,20, \ 1024+8, 1024+12, 1024+0, 1024+4, 1024+24, 1024+28, 1024+16, 1024+20, \ 2048+8, 2048+12, 2048+0, 2048+4, 2048+24, 2048+28, 2048+16, 2048+20, \ 3072+8, 3072+12, 3072+0, 3072+4, 3072+24, 3072+28, 3072+16, 3072+20 \ }, \ #define _32x32_SEQUENCE_1_FLIP \ { 3072+20, 3072+16, 3072+28, 3072+24, 3072+4, 3072+0, 3072+12, 3072+8, \ 2048+20, 2048+16, 2048+28, 2048+24, 2048+4, 2048+0, 2048+12, 2048+8, \ 1024+20, 1024+16, 1024+28, 1024+24, 1024+4, 1024+0, 1024+12, 1024+8, \ 20, 16, 28, 24, 4, 0, 12, 8}, \ #define _32x32_SEQUENCE_2 \ { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32, \ 8*32, 9*32, 10*32, 11*32, 12*32, 13*32, 14*32, 15*32, \ 16*32,17*32,18*32,19*32,20*32,21*32,22*32,23*32, \ 24*32,25*32, 26*32, 27*32, 28*32, 29*32, 30*32, 31*32}, \ #define _32x32_SEQUENCE_2_FLIP \ { 31*32, 30*32, 29*32, 28*32, 27*32, 26*32, 25*32, 24*32, \ 23*32, 22*32, 21*32, 20*32, 19*32, 18*32, 17*32, 16*32, \ 15*32, 14*32, 13*32, 12*32, 11*32, 10*32, 9*32 , 8*32 , \ 7*32 , 6*32 , 5*32 , 4*32 , 3*32 , 2*32 , 1*32 , 0*32}, \ /* 16x16 decodes */ static const gfx_layout sega_16x16_r00_f0_layout = _16x16_START _16x16_SEQUENCE_1 _16x16_SEQUENCE_2 _16x16_END static const gfx_layout sega_16x16_r01_f0_layout = _16x16_START _16x16_SEQUENCE_2 _16x16_SEQUENCE_1_FLIP _16x16_END static const gfx_layout sega_16x16_r10_f0_layout = _16x16_START _16x16_SEQUENCE_1_FLIP _16x16_SEQUENCE_2_FLIP _16x16_END static const gfx_layout sega_16x16_r11_f0_layout = _16x16_START _16x16_SEQUENCE_2_FLIP _16x16_SEQUENCE_1 _16x16_END static const gfx_layout sega_16x16_r00_f1_layout = _16x16_START _16x16_SEQUENCE_1_FLIP _16x16_SEQUENCE_2 _16x16_END static const gfx_layout sega_16x16_r01_f1_layout = _16x16_START _16x16_SEQUENCE_2 _16x16_SEQUENCE_1 _16x16_END static const gfx_layout sega_16x16_r10_f1_layout = _16x16_START _16x16_SEQUENCE_1 _16x16_SEQUENCE_2_FLIP _16x16_END static const gfx_layout sega_16x16_r11_f1_layout = _16x16_START _16x16_SEQUENCE_2_FLIP _16x16_SEQUENCE_1_FLIP _16x16_END /* 32x32 decodes */ static const gfx_layout sega_32x32_r00_f0_layout = _32x32_START _32x32_SEQUENCE_1 _32x32_SEQUENCE_2 _32x32_END static const gfx_layout sega_32x32_r01_f0_layout = _32x32_START _32x32_SEQUENCE_2 _32x32_SEQUENCE_1_FLIP _32x32_END static const gfx_layout sega_32x32_r10_f0_layout = _32x32_START _32x32_SEQUENCE_1_FLIP _32x32_SEQUENCE_2_FLIP _32x32_END static const gfx_layout sega_32x32_r11_f0_layout = _32x32_START _32x32_SEQUENCE_2_FLIP _32x32_SEQUENCE_1 _32x32_END static const gfx_layout sega_32x32_r00_f1_layout = _32x32_START _32x32_SEQUENCE_1_FLIP _32x32_SEQUENCE_2 _32x32_END static const gfx_layout sega_32x32_r01_f1_layout = _32x32_START _32x32_SEQUENCE_2 _32x32_SEQUENCE_1 _32x32_END static const gfx_layout sega_32x32_r10_f1_layout = _32x32_START _32x32_SEQUENCE_1 _32x32_SEQUENCE_2_FLIP _32x32_END static const gfx_layout sega_32x32_r11_f1_layout = _32x32_START _32x32_SEQUENCE_2_FLIP _32x32_SEQUENCE_1_FLIP _32x32_END static void segacd_mark_tiles_dirty(running_machine* machine, int offset) { gfx_element_mark_dirty(machine->gfx[0], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[1], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[2], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[3], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[4], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[5], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[6], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[7], (offset*2)/(SEGACD_BYTES_PER_TILE16)); gfx_element_mark_dirty(machine->gfx[8], (offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[9], (offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[10],(offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[11],(offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[12],(offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[13],(offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[14],(offset*2)/(SEGACD_BYTES_PER_TILE32)); gfx_element_mark_dirty(machine->gfx[15],(offset*2)/(SEGACD_BYTES_PER_TILE32)); } // mame specific.. map registers to which tilemap cache we use static int segacd_get_active_stampmap_tilemap(void) { return (segacd_stampsize & 0x6)>>1; } #if 0 static void segacd_mark_stampmaps_dirty(void) { tilemap_mark_all_tiles_dirty(segacd_stampmap[segacd_get_active_stampmap_tilemap()]); //tilemap_mark_all_tiles_dirty(segacd_stampmap[0]); //tilemap_mark_all_tiles_dirty(segacd_stampmap[1]); //tilemap_mark_all_tiles_dirty(segacd_stampmap[2]); //tilemap_mark_all_tiles_dirty(segacd_stampmap[3]); } #endif void SCD_GET_TILE_INFO_16x16_1x1( int& tile_region, int& tileno, int tile_index ) { tile_region = 0; // 16x16 tiles int tile_base = (segacd_stampmap_base_address & 0xff80) * 4; int tiledat = segacd_dataram[((tile_base>>1)+tile_index) & 0x1ffff]; tileno = tiledat & 0x07ff; int xflip = tiledat & 0x8000; int roll = (tiledat & 0x6000)>>13; if (xflip) tile_region += 4; tile_region+=roll; } void SCD_GET_TILE_INFO_32x32_1x1( int& tile_region, int& tileno, int tile_index ) { tile_region = 8; // 32x32 tiles int tile_base = (segacd_stampmap_base_address & 0xffe0) * 4; int tiledat = segacd_dataram[((tile_base>>1)+tile_index) & 0x1ffff]; tileno = (tiledat & 0x07fc)>>2; int xflip = tiledat & 0x8000; int roll = (tiledat & 0x6000)>>13; if (xflip) tile_region += 4; tile_region+=roll; } void SCD_GET_TILE_INFO_16x16_16x16( int& tile_region, int& tileno, int tile_index ) { tile_region = 0; // 16x16 tiles int tile_base = (0x8000) * 4; // fixed address in this mode int tiledat = segacd_dataram[((tile_base>>1)+tile_index) & 0x1ffff]; tileno = tiledat & 0x07ff; int xflip = tiledat & 0x8000; int roll = (tiledat & 0x6000)>>13; if (xflip) tile_region += 4; tile_region+=roll; } void SCD_GET_TILE_INFO_32x32_16x16( int& tile_region, int& tileno, int tile_index ) { tile_region = 8; // 32x32 tiles int tile_base = (segacd_stampmap_base_address & 0xe000) * 4; int tiledat = segacd_dataram[((tile_base>>1)+tile_index) & 0x1ffff]; tileno = (tiledat & 0x07fc)>>2; int xflip = tiledat & 0x8000; int roll = (tiledat & 0x6000)>>13; if (xflip) tile_region += 4; tile_region+=roll; } /* Tilemap callbacks (we don't actually use the tilemaps due to the excessive overhead */ static TILE_GET_INFO( get_stampmap_16x16_1x1_tile_info ) { int tile_region, tileno; SCD_GET_TILE_INFO_16x16_1x1(tile_region,tileno,(int)tile_index); SET_TILE_INFO(tile_region, tileno, 0, 0); } static TILE_GET_INFO( get_stampmap_32x32_1x1_tile_info ) { int tile_region, tileno; SCD_GET_TILE_INFO_32x32_1x1(tile_region,tileno,(int)tile_index); SET_TILE_INFO(tile_region, tileno, 0, 0); } static TILE_GET_INFO( get_stampmap_16x16_16x16_tile_info ) { int tile_region, tileno; SCD_GET_TILE_INFO_16x16_16x16(tile_region,tileno,(int)tile_index); SET_TILE_INFO(tile_region, tileno, 0, 0); } static TILE_GET_INFO( get_stampmap_32x32_16x16_tile_info ) { int tile_region, tileno; SCD_GET_TILE_INFO_32x32_16x16(tile_region,tileno,(int)tile_index); SET_TILE_INFO(tile_region, tileno, 0, 0); } // non-tilemap functions to get a pixel from a 'tilemap' based on the above, but looking up each pixel, as to avoid the heavy cache bitmap INLINE UINT8 get_stampmap_16x16_1x1_tile_info_pixel(running_machine* machine, int xpos, int ypos) { const int tilesize = 4; // 0xf pixels const int tilemapsize = 0x0f; int wraparound = segacd_stampsize&1; int xtile = xpos / (1<tilemapsize) return 0; if (xtile<0) return 0; if (ytile>tilemapsize) return 0; if (ytile<0) return 0; } int tile_index = (ytile * (tilemapsize+1)) + xtile; int tile_region, tileno; SCD_GET_TILE_INFO_16x16_1x1(tile_region,tileno,(int)tile_index); const gfx_element *gfx = machine->gfx[tile_region]; tileno %= gfx->total_elements; const UINT8* srcdata = gfx_element_get_data(gfx, tileno); return srcdata[((ypos&((1<tilemapsize) return 0; if (xtile<0) return 0; if (ytile>tilemapsize) return 0; if (ytile<0) return 0; } int tile_index = (ytile * (tilemapsize+1)) + xtile; int tile_region, tileno; SCD_GET_TILE_INFO_32x32_1x1(tile_region,tileno,(int)tile_index); const gfx_element *gfx = machine->gfx[tile_region]; tileno %= gfx->total_elements; const UINT8* srcdata = gfx_element_get_data(gfx, tileno); return srcdata[((ypos&((1<tilemapsize) return 0; if (xtile<0) return 0; if (ytile>tilemapsize) return 0; if (ytile<0) return 0; } int tile_index = (ytile * (tilemapsize+1)) + xtile; int tile_region, tileno; SCD_GET_TILE_INFO_16x16_16x16(tile_region,tileno,(int)tile_index); const gfx_element *gfx = machine->gfx[tile_region]; tileno %= gfx->total_elements; const UINT8* srcdata = gfx_element_get_data(gfx, tileno); return srcdata[((ypos&((1<tilemapsize) return 0; if (xtile<0) return 0; if (ytile>tilemapsize) return 0; if (ytile<0) return 0; } int tile_index = (ytile * (tilemapsize+1)) + xtile; int tile_region, tileno; SCD_GET_TILE_INFO_32x32_16x16(tile_region,tileno,(int)tile_index); const gfx_element *gfx = machine->gfx[tile_region]; tileno %= gfx->total_elements; const UINT8* srcdata = gfx_element_get_data(gfx, tileno); return srcdata[((ypos&((1<> 4; segacd_cdd_rx[1] = (segacd_cdd.buffer[0] & 0x0f) >> 0; segacd_cdd_rx[2] = (segacd_cdd.buffer[1] & 0xf0) >> 4; segacd_cdd_rx[3] = (segacd_cdd.buffer[1] & 0x0f) >> 0; segacd_cdd_rx[4] = (segacd_cdd.buffer[2] & 0xf0) >> 4; segacd_cdd_rx[5] = (segacd_cdd.buffer[2] & 0x0f) >> 0; segacd_cdd_rx[6] = (segacd_cdd.buffer[3] & 0xf0) >> 4; segacd_cdd_rx[7] = (segacd_cdd.buffer[3] & 0x0f) >> 0; segacd_cdd_rx[8] = (segacd_cdd.buffer[4] & 0x0f) >> 0; /* Do checksum calculation of the above registers */ cdd_crc = 0; for(i=0;i<9;i++) cdd_crc += segacd_cdd_rx[i]; segacd_cdd_rx[9] = ((cdd_crc & 0xf) ^ 0xf); if(segacd_irq_mask & 0x10) cputag_set_input_line(machine, "segacd_68k", 4, HOLD_LINE); hock_cmd = 0; } timer_adjust_oneshot(segacd_hock_timer, ATTOTIME_IN_HZ(75), 0); } /* main CPU map set up in INIT */ void segacd_init_main_cpu( running_machine* machine ) { address_space* space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); segacd_4meg_prgbank = 0; memory_unmap_readwrite(space, 0x20000,0x3fffff,0,0); memory_install_read_bank(space, 0x0020000, 0x003ffff, 0, 0, "scd_4m_prgbank"); memory_set_bankptr(space->machine, "scd_4m_prgbank", segacd_4meg_prgram + segacd_4meg_prgbank * 0x20000 ); memory_install_write16_handler (space, 0x0020000, 0x003ffff, 0, 0, scd_4m_prgbank_ram_w ); segacd_wordram_mapped = 1; memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x200000, 0x23ffff, 0, 0, segacd_main_dataram_part1_r, segacd_main_dataram_part1_w); // RAM shared with sub memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa12000, 0xa12001, 0, 0, scd_a12000_halt_reset_r, scd_a12000_halt_reset_w); // sub-cpu control memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa12002, 0xa12003, 0, 0, scd_a12002_memory_mode_r, scd_a12002_memory_mode_w); // memory mode / write protect memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa12006, 0xa12007, 0, 0, scd_a12006_hint_register_r, scd_a12006_hint_register_w); // where HINT points on main CPU memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa1200e, 0xa1200f, 0, 0, segacd_comms_flags_r, segacd_comms_flags_maincpu_w); // communication flags block memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa12010, 0xa1201f, 0, 0, segacd_comms_main_part1_r, segacd_comms_main_part1_w); memory_install_readwrite16_handler(cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa12020, 0xa1202f, 0, 0, segacd_comms_main_part2_r, segacd_comms_main_part2_w); cpu_set_irq_callback(machine->device("segacd_68k"), segacd_sub_int_callback); memory_install_read16_handler (space, 0x0000070, 0x0000073, 0, 0, scd_hint_vector_r ); segacd_gfx_conversion_timer = timer_alloc(machine, segacd_gfx_conversion_timer_callback, 0); timer_adjust_oneshot(segacd_gfx_conversion_timer, attotime_never, 0); segacd_dmna_ret_timer = timer_alloc(machine, segacd_dmna_ret_timer_callback, 0); timer_adjust_oneshot(segacd_gfx_conversion_timer, attotime_never, 0); segacd_hock_timer = timer_alloc(machine, segacd_hock_callback, 0); timer_adjust_oneshot(segacd_hock_timer, attotime_never, 0); segacd_irq3_timer = timer_alloc(machine, segacd_irq3_timer_callback, 0); timer_adjust_oneshot(segacd_irq3_timer, attotime_never, 0); /* create the char set (gfx will then be updated dynamically from RAM) */ machine->gfx[0] = gfx_element_alloc(machine, &sega_16x16_r00_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[1] = gfx_element_alloc(machine, &sega_16x16_r01_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[2] = gfx_element_alloc(machine, &sega_16x16_r10_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[3] = gfx_element_alloc(machine, &sega_16x16_r11_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[4] = gfx_element_alloc(machine, &sega_16x16_r00_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[5] = gfx_element_alloc(machine, &sega_16x16_r01_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[6] = gfx_element_alloc(machine, &sega_16x16_r10_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[7] = gfx_element_alloc(machine, &sega_16x16_r11_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[8] = gfx_element_alloc(machine, &sega_32x32_r00_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[9] = gfx_element_alloc(machine, &sega_32x32_r01_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[10]= gfx_element_alloc(machine, &sega_32x32_r10_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[11]= gfx_element_alloc(machine, &sega_32x32_r11_f0_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[12]= gfx_element_alloc(machine, &sega_32x32_r00_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[13]= gfx_element_alloc(machine, &sega_32x32_r01_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[14]= gfx_element_alloc(machine, &sega_32x32_r10_f1_layout, (UINT8 *)segacd_dataram, 0, 0); machine->gfx[15]= gfx_element_alloc(machine, &sega_32x32_r11_f1_layout, (UINT8 *)segacd_dataram, 0, 0); /* as a temporary measure we use the MAME tilemaps, this is hideously inefficient as we have to mark the active one as dirty before each operation due to the RAM based tiles. For the larger tilemaps this means we have to re-render a 4096x4096 bitmap to the tilemap cache on each blit operation just to copy the few needed tiles out of it, needless to say, this is SLOW. Eventually the tilemaps will be replaced with a get_pixel function which will perform all the needed lookups on a per-pixel basis instead of re-rendering the whole thing */ segacd_stampmap[0] = tilemap_create(machine, get_stampmap_16x16_1x1_tile_info, tilemap_scan_rows, 16, 16, 16, 16); segacd_stampmap[1] = tilemap_create(machine, get_stampmap_32x32_1x1_tile_info, tilemap_scan_rows, 32, 32, 8, 8); segacd_stampmap[2] = tilemap_create(machine, get_stampmap_16x16_16x16_tile_info, tilemap_scan_rows, 16, 16, 256, 256); // 128kb! segacd_stampmap[3] = tilemap_create(machine, get_stampmap_32x32_16x16_tile_info, tilemap_scan_rows, 32, 32, 128, 128); // 32kb! } static MACHINE_RESET( segacd ) { int i; cpu_set_input_line(_segacd_68k_cpu, INPUT_LINE_RESET, ASSERT_LINE); cpu_set_input_line(_segacd_68k_cpu, INPUT_LINE_HALT, ASSERT_LINE); segacd_hint_register = 0xffff; // -1 for(i=0;i<10;i++) { segacd_cdd_rx[i] = 0; } segacd_cdd_rx[9] = 0xf; // default checksum /* init cd-rom device */ #ifdef MESS { running_device *device; device = machine->device("cdrom"); if ( device ) { segacd.cd = mess_cd_get_cdrom_file(device); if ( segacd.cd ) { segacd.toc = cdrom_get_toc( segacd.cd ); cdda_set_cdrom( machine->device("cdda"), segacd.cd ); segacd.last_frame = cdrom_get_track_start( segacd.cd, cdrom_get_last_track( segacd.cd ) - 1 ); segacd.last_frame += segacd.toc->tracks[ cdrom_get_last_track( segacd.cd ) - 1 ].frames; segacd.end_frame = segacd.last_frame; } } segacd_cdd.scd_status = 0x00; // initial state } #endif segacd_dmna = 0; segacd_ret = 0; segacd_ram_mode = 0; segacd_ram_mode_old = 0; timer_adjust_oneshot(segacd_dmna_ret_timer, attotime_zero, 0); timer_adjust_oneshot(segacd_hock_timer, attotime_zero, 0); hock_cmd = 0; } static int segacd_redled = 0; static int segacd_greenled = 0; static int segacd_ready = 1; // actually set 100ms after startup? static READ16_HANDLER( segacd_sub_led_ready_r ) { UINT16 retdata = 0x0000; if (ACCESSING_BITS_0_7) { retdata |= segacd_ready; } if (ACCESSING_BITS_8_15) { retdata |= segacd_redled << 8; retdata |= segacd_greenled << 9; } return retdata; } static WRITE16_HANDLER( segacd_sub_led_ready_w ) { if (ACCESSING_BITS_0_7) { if ((data&0x01) == 0x00) { // reset CD unit } } if (ACCESSING_BITS_8_15) { segacd_redled = (data >> 8)&1; segacd_greenled = (data >> 9)&1; //popmessage("%02x %02x",segacd_greenled,segacd_redled); } } static READ16_HANDLER( segacd_sub_dataram_part1_r ) { if (segacd_ram_mode==0) { // is this correct? if (segacd_dmna) return segacd_dataram[offset]; else { printf("Illegal: segacd_sub_dataram_part1_r in mode 0 without permission\n"); return 0x0000; } } else if (segacd_ram_mode==1) { printf("Unspported: segacd_sub_dataram_part1_r in mode 1 (Word RAM Expander - 1 Byte Per Pixel)\n"); return 0x0000; } return 0x0000; } static WRITE16_HANDLER( segacd_sub_dataram_part1_w ) { if (segacd_ram_mode==0) { // is this correct? if (segacd_dmna) { COMBINE_DATA(&segacd_dataram[offset]); segacd_mark_tiles_dirty(space->machine, offset); } else { printf("Illegal: segacd_sub_dataram_part1_w in mode 0 without permission\n"); } } else if (segacd_ram_mode==1) { printf("Unspported: segacd_sub_dataram_part1_w in mode 1 (Word RAM Expander - 1 Byte Per Pixel)\n"); } } static READ16_HANDLER( segacd_sub_dataram_part2_r ) { if (segacd_ram_mode==0) { printf("ILLEGAL segacd_sub_dataram_part2_r in mode 0\n"); // not mapepd to anything in mode 0 return 0x0000; } else if (segacd_ram_mode==1) { //printf("Unsupported: segacd_sub_dataram_part2_r in mode 1 (Word RAM)\n"); // ret bit set by sub cpu determines which half of WorkRAM we have access to? if (!segacd_ret) { return segacd_dataram[offset+0x20000/2]; } else { return segacd_dataram[offset+0x00000/2]; } } return 0x0000; } static WRITE16_HANDLER( segacd_sub_dataram_part2_w ) { if (segacd_ram_mode==0) { printf("ILLEGAL segacd_sub_dataram_part2_w in mode 0\n"); // not mapepd to anything in mode 0 } else if (segacd_ram_mode==1) { //printf("Unsupported: segacd_sub_dataram_part2_w in mode 1 (Word RAM)\n"); // ret bit set by sub cpu determines which half of WorkRAM we have access to? if (!segacd_ret) { COMBINE_DATA(&segacd_dataram[offset+0x20000/2]); } else { COMBINE_DATA(&segacd_dataram[offset+0x00000/2]); } } } #if 0 static void cdd_hock_irq(running_machine *machine,UINT8 dir) { if((segacd_cdd.ctrl & 4 || dir) && segacd_irq_mask & 0x10) // export status, check if bit 2 (HOst ClocK) and irq is enabled { segacd_cdd.ctrl |= 4; // enable Hock bit if it isn't already active timer_set(machine, ATTOTIME_IN_HZ(75), NULL,0, execute_hock_irq); // 1 / 75th of a second } } #endif static READ16_HANDLER( segacd_irq_mask_r ) { return segacd_irq_mask; } static WRITE16_HANDLER( segacd_irq_mask_w ) { segacd_irq_mask = data & 0x7e; // TODO: check here pending IRQs } static READ16_HANDLER( segacd_cdd_ctrl_r ) { return segacd_cdd.ctrl; } static WRITE16_HANDLER( segacd_cdd_ctrl_w ) { segacd_cdd.ctrl = data; hock_cmd = (data & 4) >> 2; if(data & 4) // enable Hock timer timer_adjust_oneshot(segacd_hock_timer, ATTOTIME_IN_HZ(75), 0); else timer_adjust_oneshot(segacd_hock_timer, attotime_never, 0); } /* 68k <- CDD communication comms are 4-bit wide */ static READ8_HANDLER( segacd_cdd_rx_r ) { return (segacd_cdd_rx[offset] & 0xf); } static const char *const segacd_cdd_cmd[] = { "Status", "Stop All", "Get TOC Info", "Play", "Seek", "Pause/Stop", "Resume", "Fast Forward", "Fast Rewind", "Recover Initial state", "Close Tray", "Open Tray", "Unknown 0xE", "Unknown 0xF" }; static const char *const segacd_cdd_get_toc_cmd[] = { "Get Current Position", "Get Elapsed Time of Current Track", "Get Current Track", "Get Total Length", "Get First and Last Track Number", "Get Track Addresses", "Invalid 0x6", "Invalid 0x7", "Invalid 0x8", "Invalid 0x9", "Invalid 0xA", "Invalid 0xB", "Invalid 0xC", "Invalid 0xD", "Invalid 0xE", "Invalid 0xF", }; #ifdef MESS #define LOG_CDD 1 static void segacd_cdd_get_status(running_machine *machine) { //segacd_cdd.buffer[0] = 0; //segacd_cdd.buffer[1] = 0; //segacd_cdd.buffer[2] = 0; //segacd_cdd.buffer[3] = 0; //segacd_cdd.buffer[4] = 0; hock_cmd = 1; } static void segacd_cdd_stop_all(running_machine *machine) { segacd_cdd.ctrl |= 0x100; segacd_cdd.buffer[0] = 0; segacd_cdd.buffer[1] = 0; segacd_cdd.buffer[2] = 0; segacd_cdd.buffer[3] = 0; segacd_cdd.buffer[4] = 0; hock_cmd = 1; } static void segacd_cdd_get_toc_info(running_machine *machine) { segacd_cdd.buffer[0] = (segacd_cdd_tx[3] & 0xf) | (segacd_cdd.scd_status & 0xf0); // TODO: remove me #if LOG_CDD if(segacd_cdd_tx[3] >= 6) printf("CDD: TOC command %s issued\n",segacd_cdd_get_toc_cmd[segacd_cdd_tx[3] & 0xf]); #endif // TODO: check if tray is open if ( ! segacd.cd ) // no cd is present { segacd_cdd.scd_status = 0x00; segacd_cdd.buffer[0] = (0) | (segacd_cdd.scd_status & 0xf0); segacd_cdd.buffer[1] = 0; segacd_cdd.buffer[2] = 0; segacd_cdd.buffer[3] = 0; segacd_cdd.buffer[4] = 0; hock_cmd = 1; return; } else { switch(segacd_cdd_tx[3] & 0xf) { case 0x0: //Get Current Position { UINT32 msf; segacd_cdd.scd_status = 0x40; //TODO: check this msf = lba_to_msf( segacd.current_frame ); segacd_cdd.buffer[0] = (0x0 & 0xf) | (segacd_cdd.scd_status & 0xf0); segacd_cdd.buffer[1] = dec_2_bcd( msf >> 16 ) & 0xff; segacd_cdd.buffer[2] = dec_2_bcd( msf >> 8 ) & 0xff; segacd_cdd.buffer[3] = dec_2_bcd( msf >> 0 ) & 0xff; segacd_cdd.buffer[4] = 0; hock_cmd = 1; return; } case 0x1: //Get Elapsed Time of Current Track { UINT32 msf; UINT32 end_frame_track; segacd_cdd.scd_status = 0x40; //TODO: check this end_frame_track = segacd.toc->tracks[ cdrom_get_track(segacd.cd, segacd.current_frame) + 1 ].physframeofs; // correct? msf = lba_to_msf( end_frame_track - segacd.current_frame ); segacd_cdd.buffer[0] = (0x1 & 0xf) | (segacd_cdd.scd_status & 0xf0); segacd_cdd.buffer[1] = dec_2_bcd( msf >> 16 ) & 0xff; segacd_cdd.buffer[2] = dec_2_bcd( msf >> 8 ) & 0xff; segacd_cdd.buffer[3] = dec_2_bcd( msf >> 0 ) & 0xff; segacd_cdd.buffer[4] = 0; hock_cmd = 1; return; } case 0x2: //Get Current Track { segacd_cdd.buffer[0] = (0x2 & 0xf) | (segacd_cdd.buffer[0] & 0xf0); segacd_cdd.buffer[1] = cdrom_get_track(segacd.cd, segacd.current_frame); segacd_cdd.buffer[2] = 0; segacd_cdd.buffer[3] = 0; segacd_cdd.buffer[4] = 0; hock_cmd = 1; return; } case 0x3: //Get Total Length (in MSF) { UINT32 frame,msf; frame = segacd.toc->tracks[segacd.toc->numtrks-1].physframeofs; frame += segacd.toc->tracks[segacd.toc->numtrks-1].frames; frame += 150; // 2 seconds of pre-gap msf = lba_to_msf( frame ); segacd_cdd.buffer[0] = (0x3 & 0xf) | (segacd_cdd.scd_status & 0xf0); segacd_cdd.buffer[1] = ((msf >> 16) & 0xff); segacd_cdd.buffer[2] = ((msf >> 8) & 0xff); segacd_cdd.buffer[3] = ((msf >> 0) & 0xff); segacd_cdd.buffer[4] = 0; printf("%02x %02x %02x %02x %02x\n",segacd_cdd.buffer[0],segacd_cdd.buffer[1],segacd_cdd.buffer[2],segacd_cdd.buffer[3],segacd_cdd.buffer[4]); hock_cmd = 1; return; } case 0x4: //Get First and Last Track Number { segacd_cdd.scd_status = 0x40; segacd_cdd.buffer[0] = (0x4 & 0xf) | (segacd_cdd.scd_status & 0xf0); segacd.first_track = dec_2_bcd(1); segacd.last_track = dec_2_bcd(segacd.toc->numtrks - 1); segacd_cdd.buffer[1] = segacd.first_track; segacd_cdd.buffer[2] = segacd.last_track; segacd_cdd.buffer[3] = 0; segacd_cdd.buffer[4] = 0; printf("%02x %02x %02x %02x %02x\n",segacd_cdd.buffer[0],segacd_cdd.buffer[1],segacd_cdd.buffer[2],segacd_cdd.buffer[3],segacd_cdd.buffer[4]); hock_cmd = 1; return; } case 0x5: //Get Track Addresses { UINT8 track_num; UINT32 start_frame,frame,msf; track_num = ((segacd_cdd_tx[4] & 0xf) * 10) | (segacd_cdd_tx[5] & 0x0f); if(track_num > segacd.last_track) track_num = segacd.last_track; if(track_num < segacd.first_track) track_num = segacd.first_track; start_frame = segacd.toc->tracks[ bcd_2_dec( track_num - 1 ) ].physframeofs; //end_frame = segacd.toc->tracks[ bcd_2_dec( track_num ) ].physframeofs; frame = start_frame; msf = lba_to_msf( frame ); segacd_cdd.buffer[0] = (0x5 & 0xf) | (segacd_cdd.scd_status & 0xf0); segacd_cdd.buffer[1] = ((msf >> 16) & 0xff); segacd_cdd.buffer[2] = ((msf >> 8) & 0xff); segacd_cdd.buffer[3] = ((msf >> 0) & 0xff); segacd_cdd.buffer[4] = ((track_num) % 10) | (( segacd.toc->tracks[track_num-1].trktype == CD_TRACK_AUDIO ) ? 0x80 : 0x00); printf("%02x %02x %02x %02x %02x\n",segacd_cdd.buffer[0],segacd_cdd.buffer[1],segacd_cdd.buffer[2],segacd_cdd.buffer[3],segacd_cdd.buffer[4]); hock_cmd = 1; return; } //default: logerror("CDD: unhandled TOC command %s issued\n",segacd_cdd_get_toc_cmd[segacd_cdd_tx[3] & 0xf]); } } hock_cmd = 1; } static void segacd_cdd_seek(running_machine *machine) { UINT8 m,s,f; UINT8 track_num; UINT32 frame; // TODO: check if tray is open or CD isn't there and throw an error if so segacd_cdd.scd_status = 0x20; //READY flag m = ((segacd_cdd_tx[2] & 0xf) * 10) | (segacd_cdd_tx[3] & 0x0f); s = ((segacd_cdd_tx[4] & 0xf) * 10) | (segacd_cdd_tx[5] & 0x0f); f = ((segacd_cdd_tx[6] & 0xf) * 10) | (segacd_cdd_tx[7] & 0x0f); frame = (m << 16) | (s << 8) | (f); track_num = cdrom_get_track(segacd.cd, frame); segacd.current_frame = frame; // printf("%02x %02x %02x %02x\n",m,s,f,track_num); segacd_cdd.ctrl &= 0xfeff; segacd_cdd.ctrl |= (( segacd.toc->tracks[track_num-1].trktype == CD_TRACK_AUDIO ) ? 0x000 : 0x100); segacd_cdd.buffer[0] = segacd_cdd.scd_status & 0x20; // set READY segacd_cdd.buffer[1] = 0; segacd_cdd.buffer[2] = 0; segacd_cdd.buffer[3] = 0; segacd_cdd.buffer[4] = 0; hock_cmd = 1; } #endif static WRITE8_HANDLER( segacd_cdd_tx_w ) { //printf("CDD Communication write %02x -> [%02x]\n",data,offset); segacd_cdd_tx[offset] = data & 0xf; #ifdef MESS if(offset == 9) //execute the command when crc is sent (TODO: I wonder if we need to check if crc is valid. Plus obviously this shouldn't be instant) { #if LOG_CDD if(segacd_cdd_tx[0] != 0 && segacd_cdd_tx[0] != 2) printf("CDD: command %s issued\n",segacd_cdd_cmd[segacd_cdd_tx[0] & 0xf]); #endif switch(segacd_cdd_tx[0] & 0xf) { case 0x0: segacd_cdd_get_status(space->machine); break; case 0x1: segacd_cdd_stop_all(space->machine); break; case 0x2: segacd_cdd_get_toc_info(space->machine); break; //case 0x3: segacd_cdd_play(space->machine); break; case 0x4: segacd_cdd_seek(space->machine); break; //default: logerror("CDD: unhandled command %s issued\n",segacd_cdd_cmd[segacd_cdd_tx[0] & 0xf]); } } #endif } static READ16_HANDLER( segacd_stampsize_r ) { UINT16 retdata = 0x0000; retdata |= segacd_conversion_active<<15; retdata |= segacd_stampsize & 0x7; return retdata; } static WRITE16_HANDLER( segacd_stampsize_w ) { printf("segacd_stampsize_w %04x %04x\n",data, mem_mask); if (ACCESSING_BITS_0_7) { segacd_stampsize = data & 0x07; if (data & 0xf8) printf(" unused bits (LSB) set in stampsize!\n"); if (data&1) printf(" Repeat On\n"); else printf(" Repeat Off\n"); if (data&2) printf(" 32x32 dots\n"); else printf(" 16x16 dots\n"); if (data&4) printf(" 16x16 screens\n"); else printf(" 1x1 screen\n"); } if (ACCESSING_BITS_8_15) { if (data&0xff00) printf(" unused bits (MSB) set in stampsize!\n"); } } // these functions won't cope if // // the lower 3 bits of segacd_imagebuffer_hdot_size are set // this really needs to be doing it's own lookups rather than depending on the inefficient MAME cache.. INLINE UINT8 read_pixel_from_stampmap( running_machine* machine, bitmap_t* srcbitmap, int x, int y) { /* if (!srcbitmap) { return mame_rand(machine); } if (x >= srcbitmap->width) return 0; if (y >= srcbitmap->height) return 0; UINT16* cacheptr = BITMAP_ADDR16( srcbitmap, y, x); return cacheptr[0] & 0xf; */ switch (segacd_get_active_stampmap_tilemap()&3) { case 0x00: return get_stampmap_16x16_1x1_tile_info_pixel( machine, x, y ); case 0x01: return get_stampmap_32x32_1x1_tile_info_pixel( machine, x, y ); case 0x02: return get_stampmap_16x16_16x16_tile_info_pixel( machine, x, y ); case 0x03: return get_stampmap_32x32_16x16_tile_info_pixel( machine, x, y ); } return 0; } INLINE void write_pixel_to_imagebuffer( running_machine* machine, UINT32 pix, int line, int xpos ) { UINT32 bufferstart = (segacd_imagebuffer_start_address&0xfff8)*2; UINT32 bufferend = bufferstart + (((segacd_imagebuffer_vcell_size+1) * (segacd_imagebuffer_hdot_size>>3)*0x20)/2); UINT32 offset; offset = bufferstart+(((segacd_imagebuffer_vcell_size+1)*0x10)*xpos); // lines of each output cell offset+= (line*2); while (offset>=bufferend) offset-= bufferend; switch (segacd_memory_priority_mode) { case 0x00: // normal write, just write the data segacd_dataram[offset] = pix >> 16; segacd_dataram[offset+1] = pix & 0xffff; break; case 0x01: // underwrite, only write if the existing data is 0 if ((segacd_dataram[offset]&0xf000) == 0x0000) segacd_dataram[offset] |= (pix>>16)&0xf000; if ((segacd_dataram[offset]&0x0f00) == 0x0000) segacd_dataram[offset] |= (pix>>16)&0x0f00; if ((segacd_dataram[offset]&0x00f0) == 0x0000) segacd_dataram[offset] |= (pix>>16)&0x00f0; if ((segacd_dataram[offset]&0x000f) == 0x0000) segacd_dataram[offset] |= (pix>>16)&0x000f; if ((segacd_dataram[offset+1]&0xf000) == 0x0000) segacd_dataram[offset+1] |= (pix)&0xf000; if ((segacd_dataram[offset+1]&0x0f00) == 0x0000) segacd_dataram[offset+1] |= (pix)&0x0f00; if ((segacd_dataram[offset+1]&0x00f0) == 0x0000) segacd_dataram[offset+1] |= (pix)&0x00f0; if ((segacd_dataram[offset+1]&0x000f) == 0x0000) segacd_dataram[offset+1] |= (pix)&0x000f; break; case 0x02: // overwrite, only write non-zero data if ((pix>>16)&0xf000) segacd_dataram[offset] = (segacd_dataram[offset] & 0x0fff) | ((pix>>16)&0xf000); if ((pix>>16)&0x0f00) segacd_dataram[offset] = (segacd_dataram[offset] & 0xf0ff) | ((pix>>16)&0x0f00); if ((pix>>16)&0x00f0) segacd_dataram[offset] = (segacd_dataram[offset] & 0xff0f) | ((pix>>16)&0x00f0); if ((pix>>16)&0x000f) segacd_dataram[offset] = (segacd_dataram[offset] & 0xfff0) | ((pix>>16)&0x000f); if ((pix)&0xf000) segacd_dataram[offset+1] = (segacd_dataram[offset+1] & 0x0fff) | ((pix)&0xf000); if ((pix)&0x0f00) segacd_dataram[offset+1] = (segacd_dataram[offset+1] & 0xf0ff) | ((pix)&0x0f00); if ((pix)&0x00f0) segacd_dataram[offset+1] = (segacd_dataram[offset+1] & 0xff0f) | ((pix)&0x00f0); if ((pix)&0x000f) segacd_dataram[offset+1] = (segacd_dataram[offset+1] & 0xfff0) | ((pix)&0x000f); break; default: case 0x03: // invalid? segacd_dataram[offset] = mame_rand(machine); segacd_dataram[offset+1] = mame_rand(machine); break; } segacd_mark_tiles_dirty(machine, offset); segacd_mark_tiles_dirty(machine, offset+1); } // this triggers the conversion operation, which will cause an IRQ1 when finished WRITE16_HANDLER( segacd_trace_vector_base_address_w ) { if (segacd_ram_mode==1) { printf("ILLEGAL: segacd_trace_vector_base_address_w %04x %04x in mode 1!\n",data,mem_mask); } //printf("segacd_trace_vector_base_address_w %04x %04x\n",data,mem_mask); { int base = (data & 0xfffe) * 4; //printf("actual base = %06x\n", base + 0x80000); // nasty nasty nasty //segacd_mark_stampmaps_dirty(); segacd_conversion_active = 1; // todo: proper time calculation timer_adjust_oneshot(segacd_gfx_conversion_timer, ATTOTIME_IN_HZ(500), 0); int line; //bitmap_t *srcbitmap = tilemap_get_pixmap(segacd_stampmap[segacd_get_active_stampmap_tilemap()]); bitmap_t *srcbitmap = 0; for (line=0;line>1]; tilemapyoffs = segacd_dataram[(currbase+0x2)>>1]; deltax = segacd_dataram[(currbase+0x4)>>1]; // x-zoom deltay = segacd_dataram[(currbase+0x6)>>1]; // rotation //printf("%06x: %04x (%d) %04x (%d) %04x %04x\n", currbase, tilemapxoffs, tilemapxoffs>>3, tilemapyoffs, tilemapyoffs>>3, deltax, deltay); int xbase = tilemapxoffs * 256; int ybase = tilemapyoffs * 256; int count; for (count=0;count<(segacd_imagebuffer_hdot_size>>3);count++) { int i; UINT32 pixblock = 0x00000000; for (i=7*4;i>=0;i-=4) { pixblock |= read_pixel_from_stampmap(space->machine, srcbitmap, xbase>>(3+8), ybase>>(3+8)) << (i); xbase += deltax; ybase += deltay; // clamp to 24-bits, seems to be required for all the intro effects to work xbase &= 0xffffff; ybase &= 0xffffff; } write_pixel_to_imagebuffer(space->machine, pixblock, line, count); } } } } // actually just the low 8 bits? READ16_HANDLER( segacd_imagebuffer_vdot_size_r ) { return segacd_imagebuffer_vdot_size; } WRITE16_HANDLER( segacd_imagebuffer_vdot_size_w ) { //printf("segacd_imagebuffer_vdot_size_w %04x %04x\n",data,mem_mask); COMBINE_DATA(&segacd_imagebuffer_vdot_size); } // basically the 'tilemap' base address, for the 16x16 / 32x32 source tiles static READ16_HANDLER( segacd_stampmap_base_address_r ) { // different bits are valid in different modes, but I'm guessing the register // always returns all the bits set, even if they're not used? return segacd_stampmap_base_address; } static WRITE16_HANDLER( segacd_stampmap_base_address_w ) { // WORD ACCESS // low 3 bitsa aren't used, are they stored? COMBINE_DATA(&segacd_stampmap_base_address); } // destination for 'rendering' the section of the tilemap(stampmap) requested static READ16_HANDLER( segacd_imagebuffer_start_address_r ) { return segacd_imagebuffer_start_address; } static WRITE16_HANDLER( segacd_imagebuffer_start_address_w ) { COMBINE_DATA(&segacd_imagebuffer_start_address); //int base = (segacd_imagebuffer_start_address & 0xfffe) * 4; //printf("segacd_imagebuffer_start_address_w %04x %04x (actual base = %06x)\n", data, segacd_imagebuffer_start_address, base); } static READ16_HANDLER( segacd_imagebuffer_offset_r ) { return segacd_imagebuffer_offset; } static WRITE16_HANDLER( segacd_imagebuffer_offset_w ) { COMBINE_DATA(&segacd_imagebuffer_offset); printf("segacd_imagebuffer_offset_w %04x\n", segacd_imagebuffer_offset); } static READ16_HANDLER( segacd_imagebuffer_vcell_size_r ) { return segacd_imagebuffer_vcell_size; } static WRITE16_HANDLER( segacd_imagebuffer_vcell_size_w ) { COMBINE_DATA(&segacd_imagebuffer_vcell_size); } static READ16_HANDLER( segacd_imagebuffer_hdot_size_r ) { return segacd_imagebuffer_hdot_size; } static WRITE16_HANDLER( segacd_imagebuffer_hdot_size_w ) { COMBINE_DATA(&segacd_imagebuffer_hdot_size); } static UINT16 segacd_irq3_timer_reg; static READ16_HANDLER( segacd_irq3timer_r ) { return segacd_irq3_timer_reg; // always returns value written, not current counter! } #define SEGACD_IRQ3_TIMER_SPEED (ATTOTIME_IN_NSEC(segacd_irq3_timer_reg*30720)) static WRITE16_HANDLER( segacd_irq3timer_w ) { if (ACCESSING_BITS_0_7) { segacd_irq3_timer_reg = data & 0xff; // time = reg * 30.72 us if (segacd_irq3_timer_reg) timer_adjust_oneshot(segacd_irq3_timer, SEGACD_IRQ3_TIMER_SPEED, 0); else timer_adjust_oneshot(segacd_irq3_timer, attotime_never, 0); printf("segacd_irq3timer_w %02x\n", segacd_irq3_timer_reg); } } static TIMER_CALLBACK( segacd_irq3_timer_callback ) { if (segacd_irq_mask & 0x08) cputag_set_input_line(machine, "segacd_68k", 3, HOLD_LINE); timer_adjust_oneshot(segacd_irq3_timer, SEGACD_IRQ3_TIMER_SPEED, 0); } static ADDRESS_MAP_START( segacd_map, ADDRESS_SPACE_PROGRAM, 16 ) AM_RANGE(0x000000, 0x07ffff) AM_RAM AM_BASE(&segacd_4meg_prgram) AM_RANGE(0x080000, 0x0bffff) AM_READWRITE(segacd_sub_dataram_part1_r, segacd_sub_dataram_part1_w) AM_BASE(&segacd_dataram) AM_RANGE(0x0c0000, 0x0dffff) AM_READWRITE(segacd_sub_dataram_part2_r, segacd_sub_dataram_part2_w) AM_BASE(&segacd_dataram2) AM_RANGE(0xfe0000, 0xfe3fff) AM_RAM // backup RAM, odd bytes only! AM_RANGE(0xff0000, 0xff001f) AM_DEVWRITE8("rfsnd", rf5c68_w, 0x00ff) // PCM, RF5C164 AM_RANGE(0xff2000, 0xff3fff) AM_DEVREADWRITE8("rfsnd", rf5c68_mem_r, rf5c68_mem_w,0x00ff) // PCM, RF5C164 AM_RANGE(0xff8000 ,0xff8001) AM_READWRITE(segacd_sub_led_ready_r, segacd_sub_led_ready_w) AM_RANGE(0xff8002 ,0xff8003) AM_READWRITE(segacd_sub_memory_mode_r, segacd_sub_memory_mode_w) AM_RANGE(0xff8004 ,0xff8005) AM_READWRITE(segacd_cdc_mode_address_r, segacd_cdc_mode_address_w) AM_RANGE(0xff8006 ,0xff8007) AM_READWRITE(segacd_cdc_data_r, segacd_cdc_data_w) // AM_RANGE(0xff8008, 0xff8009) // CDC Host Data // AM_RANGE(0xff800a, 0xff800b) // CDC DMA Address // AM_RANGE(0xff800c, 0xff800d) // Stopwatch timer AM_RANGE(0xff800e ,0xff800f) AM_READWRITE(segacd_comms_flags_r, segacd_comms_flags_subcpu_w) AM_RANGE(0xff8010 ,0xff801f) AM_READWRITE(segacd_comms_sub_part1_r, segacd_comms_sub_part1_w) AM_RANGE(0xff8020 ,0xff802f) AM_READWRITE(segacd_comms_sub_part2_r, segacd_comms_sub_part2_w) AM_RANGE(0xff8030, 0xff8031) AM_READWRITE(segacd_irq3timer_r, segacd_irq3timer_w) // Timer W/INT3 AM_RANGE(0xff8032, 0xff8033) AM_READWRITE(segacd_irq_mask_r,segacd_irq_mask_w) AM_RANGE(0xff8034, 0xff8035) AM_NOP // CD Fader AM_RANGE(0xff8036, 0xff8037) AM_READWRITE(segacd_cdd_ctrl_r,segacd_cdd_ctrl_w) AM_RANGE(0xff8038, 0xff8041) AM_READ8(segacd_cdd_rx_r,0xffff) AM_RANGE(0xff8042, 0xff804b) AM_WRITE8(segacd_cdd_tx_w,0xffff) // AM_RANGE(0xff804c, 0xff804d) // Font Color // AM_RANGE(0xff804e, 0xff804f) // Font bit // AM_RANGE(0xff8050, 0xff8057) // Font data (read only) AM_RANGE(0xff8058, 0xff8059) AM_READWRITE(segacd_stampsize_r, segacd_stampsize_w) // Stamp size AM_RANGE(0xff805a, 0xff805b) AM_READWRITE(segacd_stampmap_base_address_r, segacd_stampmap_base_address_w) // Stamp map base address AM_RANGE(0xff805c, 0xff805d) AM_READWRITE(segacd_imagebuffer_vcell_size_r, segacd_imagebuffer_vcell_size_w)// Image buffer V cell size AM_RANGE(0xff805e, 0xff805f) AM_READWRITE(segacd_imagebuffer_start_address_r, segacd_imagebuffer_start_address_w) // Image buffer start address AM_RANGE(0xff8060, 0xff8061) AM_READWRITE(segacd_imagebuffer_offset_r, segacd_imagebuffer_offset_w) AM_RANGE(0xff8062, 0xff8063) AM_READWRITE(segacd_imagebuffer_hdot_size_r, segacd_imagebuffer_hdot_size_w) // Image buffer H dot size AM_RANGE(0xff8064, 0xff8065) AM_READWRITE(segacd_imagebuffer_vdot_size_r, segacd_imagebuffer_vdot_size_w ) // Image buffer V dot size AM_RANGE(0xff8066, 0xff8067) AM_WRITE(segacd_trace_vector_base_address_w)// Trace vector base address // AM_RANGE(0xff8068, 0xff8069) // Subcode address // AM_RANGE(0xff8100, 0xff817f) // Subcode buffer area // AM_RANGE(0xff8180, 0xff81ff) // mirror of subcode buffer area ADDRESS_MAP_END /****************************************** SVP related *****************************************/ /* * Emulator of memory controller in SVP chip * * Copyright 2008, Grazvydas Ignotas * based on RE work by Tasco Deluxe * * SSP1601 EXT registers are mapped as I/O ports due to their function * (they are interfaced through external bus), and are named as follows * (these are unofficial names, official ones are unknown): * EXT0: PM0 - programmable register 0 * EXT1: PM1 - ... 1 * EXT2: PM2 - ... 2 * EXT3: XST - external status. Can also act as PM. * EXT4: PM4 - ... 4 * EXT5: (unused) * EXT6: PMC - programmable memory register control (PMAC). * EXT7: AL - although internal to SSP1601, it still causes bus access * * Depending on GPO bits in status register, PM0, PM1, PM2 and XST can act as * external status registers, os as programmable memory registers. PM4 always * acts as PM register (independend on GPO bits). */ #include "cpu/ssp1601/ssp1601.h" static struct svp_vars { UINT8 *iram; // IRAM (0-0x7ff) UINT8 *dram; // [0x20000]; UINT32 pmac_read[6]; // read modes/addrs for PM0-PM5 UINT32 pmac_write[6]; // write ... PAIR pmc; #define SSP_PMC_HAVE_ADDR 1 // address written to PMAC, waiting for mode #define SSP_PMC_SET 2 // PMAC is set, PMx can be programmed UINT32 emu_status; UINT16 XST; // external status, mapped at a15000 and a15002 on 68k side. UINT16 XST2; // status of XST (bit1 set when 68k writes to XST) } svp; static int get_inc(int mode) { int inc = (mode >> 11) & 7; if (inc != 0) { if (inc != 7) inc--; inc = 1 << inc; // 0 1 2 4 8 16 32 128 if (mode & 0x8000) inc = -inc; // decrement mode } return inc; } INLINE void overwrite_write(UINT16 *dst, UINT16 d) { if (d & 0xf000) { *dst &= ~0xf000; *dst |= d & 0xf000; } if (d & 0x0f00) { *dst &= ~0x0f00; *dst |= d & 0x0f00; } if (d & 0x00f0) { *dst &= ~0x00f0; *dst |= d & 0x00f0; } if (d & 0x000f) { *dst &= ~0x000f; *dst |= d & 0x000f; } } static UINT32 pm_io(address_space *space, int reg, int write, UINT32 d) { if (svp.emu_status & SSP_PMC_SET) { svp.pmac_read[write ? reg + 6 : reg] = svp.pmc.d; svp.emu_status &= ~SSP_PMC_SET; return 0; } // just in case if (svp.emu_status & SSP_PMC_HAVE_ADDR) { svp.emu_status &= ~SSP_PMC_HAVE_ADDR; } if (reg == 4 || (cpu_get_reg(space->cpu, SSP_ST) & 0x60)) { #define CADDR ((((mode<<16)&0x7f0000)|addr)<<1) UINT16 *dram = (UINT16 *)svp.dram; if (write) { int mode = svp.pmac_write[reg]>>16; int addr = svp.pmac_write[reg]&0xffff; if ((mode & 0x43ff) == 0x0018) // DRAM { int inc = get_inc(mode); if (mode & 0x0400) { overwrite_write(&dram[addr], d); } else dram[addr] = d; svp.pmac_write[reg] += inc; } else if ((mode & 0xfbff) == 0x4018) // DRAM, cell inc { if (mode & 0x0400) { overwrite_write(&dram[addr], d); } else dram[addr] = d; svp.pmac_write[reg] += (addr&1) ? 31 : 1; } else if ((mode & 0x47ff) == 0x001c) // IRAM { int inc = get_inc(mode); ((UINT16 *)svp.iram)[addr&0x3ff] = d; svp.pmac_write[reg] += inc; } else { logerror("ssp FIXME: PM%i unhandled write mode %04x, [%06x] %04x\n", reg, mode, CADDR, d); } } else { int mode = svp.pmac_read[reg]>>16; int addr = svp.pmac_read[reg]&0xffff; if ((mode & 0xfff0) == 0x0800) // ROM, inc 1, verified to be correct { UINT16 *ROM = (UINT16 *) memory_region(space->machine, "maincpu"); svp.pmac_read[reg] += 1; d = ROM[addr|((mode&0xf)<<16)]; } else if ((mode & 0x47ff) == 0x0018) // DRAM { int inc = get_inc(mode); d = dram[addr]; svp.pmac_read[reg] += inc; } else { logerror("ssp FIXME: PM%i unhandled read mode %04x, [%06x]\n", reg, mode, CADDR); d = 0; } } // PMC value corresponds to last PMR accessed (not sure). svp.pmc.d = svp.pmac_read[write ? reg + 6 : reg]; return d; } return (UINT32)-1; } static READ16_HANDLER( read_PM0 ) { UINT32 d = pm_io(space, 0, 0, 0); if (d != (UINT32)-1) return d; d = svp.XST2; svp.XST2 &= ~2; // ? return d; } static WRITE16_HANDLER( write_PM0 ) { UINT32 r = pm_io(space, 0, 1, data); if (r != (UINT32)-1) return; svp.XST2 = data; // ? } static READ16_HANDLER( read_PM1 ) { UINT32 r = pm_io(space, 1, 0, 0); if (r != (UINT32)-1) return r; logerror("svp: PM1 acces in non PM mode?\n"); return 0; } static WRITE16_HANDLER( write_PM1 ) { UINT32 r = pm_io(space, 1, 1, data); if (r != (UINT32)-1) return; logerror("svp: PM1 acces in non PM mode?\n"); } static READ16_HANDLER( read_PM2 ) { UINT32 r = pm_io(space, 2, 0, 0); if (r != (UINT32)-1) return r; logerror("svp: PM2 acces in non PM mode?\n"); return 0; } static WRITE16_HANDLER( write_PM2 ) { UINT32 r = pm_io(space, 2, 1, data); if (r != (UINT32)-1) return; logerror("svp: PM2 acces in non PM mode?\n"); } static READ16_HANDLER( read_XST ) { UINT32 d = pm_io(space, 3, 0, 0); if (d != (UINT32)-1) return d; return svp.XST; } static WRITE16_HANDLER( write_XST ) { UINT32 r = pm_io(space, 3, 1, data); if (r != (UINT32)-1) return; svp.XST2 |= 1; svp.XST = data; } static READ16_HANDLER( read_PM4 ) { return pm_io(space, 4, 0, 0); } static WRITE16_HANDLER( write_PM4 ) { pm_io(space, 4, 1, data); } static READ16_HANDLER( read_PMC ) { if (svp.emu_status & SSP_PMC_HAVE_ADDR) { svp.emu_status |= SSP_PMC_SET; svp.emu_status &= ~SSP_PMC_HAVE_ADDR; return ((svp.pmc.w.l << 4) & 0xfff0) | ((svp.pmc.w.l >> 4) & 0xf); } else { svp.emu_status |= SSP_PMC_HAVE_ADDR; return svp.pmc.w.l; } } static WRITE16_HANDLER( write_PMC ) { if (svp.emu_status & SSP_PMC_HAVE_ADDR) { svp.emu_status |= SSP_PMC_SET; svp.emu_status &= ~SSP_PMC_HAVE_ADDR; svp.pmc.w.h = data; } else { svp.emu_status |= SSP_PMC_HAVE_ADDR; svp.pmc.w.l = data; } } static READ16_HANDLER( read_AL ) { svp.emu_status &= ~(SSP_PMC_SET|SSP_PMC_HAVE_ADDR); return 0; } static WRITE16_HANDLER( write_AL ) { } static READ16_HANDLER( svp_68k_io_r ) { UINT32 d; switch (offset) { // 0xa15000, 0xa15002 case 0: case 1: return svp.XST; // 0xa15004 case 2: d = svp.XST2; svp.XST2 &= ~1; return d; default: logerror("unhandled SVP reg read @ %x\n", offset<<1); } return 0; } static WRITE16_HANDLER( svp_68k_io_w ) { switch (offset) { // 0xa15000, 0xa15002 case 0: case 1: svp.XST = data; svp.XST2 |= 2; break; // 0xa15006 case 3: break; // possibly halts SSP1601 default: logerror("unhandled SVP reg write %04x @ %x\n", data, offset<<1); } } static READ16_HANDLER( svp_68k_cell1_r ) { // this is rewritten 68k test code UINT32 a1 = offset; a1 = (a1 & 0x7001) | ((a1 & 0x3e) << 6) | ((a1 & 0xfc0) >> 5); return ((UINT16 *)svp.dram)[a1]; } static READ16_HANDLER( svp_68k_cell2_r ) { // this is rewritten 68k test code UINT32 a1 = offset; a1 = (a1 & 0x7801) | ((a1 & 0x1e) << 6) | ((a1 & 0x7e0) >> 4); return ((UINT16 *)svp.dram)[a1]; } static ADDRESS_MAP_START( svp_ssp_map, ADDRESS_SPACE_PROGRAM, 16 ) AM_RANGE(0x0000, 0x03ff) AM_ROMBANK("bank3") AM_RANGE(0x0400, 0xffff) AM_ROMBANK("bank4") ADDRESS_MAP_END static ADDRESS_MAP_START( svp_ext_map, ADDRESS_SPACE_IO, 16 ) ADDRESS_MAP_GLOBAL_MASK(0xf) AM_RANGE(0*2, 0*2+1) AM_READWRITE(read_PM0, write_PM0) AM_RANGE(1*2, 1*2+1) AM_READWRITE(read_PM1, write_PM1) AM_RANGE(2*2, 2*2+1) AM_READWRITE(read_PM2, write_PM2) AM_RANGE(3*2, 3*2+1) AM_READWRITE(read_XST, write_XST) AM_RANGE(4*2, 4*2+1) AM_READWRITE(read_PM4, write_PM4) AM_RANGE(6*2, 6*2+1) AM_READWRITE(read_PMC, write_PMC) AM_RANGE(7*2, 7*2+1) AM_READWRITE(read_AL, write_AL) ADDRESS_MAP_END /* emulate testmode plug */ static UINT8 megadrive_io_read_data_port_svp(running_machine *machine, int portnum) { if (portnum == 0 && input_port_read_safe(machine, "MEMORY_TEST", 0x00)) { return (megadrive_io_data_regs[0] & 0xc0); } return megadrive_io_read_data_port_3button(machine, portnum); } static READ16_HANDLER( svp_speedup_r ) { cpu_spinuntil_time(space->cpu, ATTOTIME_IN_USEC(100)); return 0x0425; } static void svp_init(running_machine *machine) { UINT8 *ROM; memset(&svp, 0, sizeof(svp)); /* SVP stuff */ svp.dram = auto_alloc_array(machine, UINT8, 0x20000); memory_install_ram(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x300000, 0x31ffff, 0, 0, svp.dram); memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15000, 0xa150ff, 0, 0, svp_68k_io_r, svp_68k_io_w); // "cell arrange" 1 and 2 memory_install_read16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x390000, 0x39ffff, 0, 0, svp_68k_cell1_r); memory_install_read16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x3a0000, 0x3affff, 0, 0, svp_68k_cell2_r); memory_install_read16_handler(cputag_get_address_space(machine, "svp", ADDRESS_SPACE_PROGRAM), 0x438, 0x438, 0, 0, svp_speedup_r); svp.iram = auto_alloc_array(machine, UINT8, 0x800); memory_set_bankptr(machine, "bank3", svp.iram ); /* SVP ROM just shares m68k region.. */ ROM = memory_region(machine, "maincpu"); memory_set_bankptr(machine, "bank4", ROM + 0x800 ); megadrive_io_read_data_port_ptr = megadrive_io_read_data_port_svp; } INPUT_PORTS_START( megdsvp ) PORT_INCLUDE( megadriv ) PORT_START("MEMORY_TEST") /* special memtest mode */ /* Region setting for Console */ PORT_DIPNAME( 0x01, 0x00, DEF_STR( Test ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) INPUT_PORTS_END MACHINE_CONFIG_DERIVED( megdsvp, megadriv ) MDRV_CPU_ADD("svp", SSP1601, MASTER_CLOCK_NTSC / 7 * 3) /* ~23 MHz (guessed) */ MDRV_CPU_PROGRAM_MAP(svp_ssp_map) MDRV_CPU_IO_MAP(svp_ext_map) /* IRQs are not used by this CPU */ MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( megdsvppal, megadpal ) MDRV_CPU_ADD("svp", SSP1601, MASTER_CLOCK_PAL / 7 * 3) /* ~23 MHz (guessed) */ MDRV_CPU_PROGRAM_MAP(svp_ssp_map) MDRV_CPU_IO_MAP(svp_ext_map) /* IRQs are not used by this CPU */ MACHINE_CONFIG_END /****************************************** END SVP related *************************************/ //static attotime time_elapsed_since_crap; VIDEO_START(megadriv) { int x; render_bitmap = machine->primary_screen->alloc_compatible_bitmap(); megadrive_vdp_vram = auto_alloc_array(machine, UINT16, 0x10000/2); megadrive_vdp_cram = auto_alloc_array(machine, UINT16, 0x80/2); megadrive_vdp_vsram = auto_alloc_array(machine, UINT16, 0x80/2); megadrive_vdp_internal_sprite_attribute_table = auto_alloc_array(machine, UINT16, 0x400/2); for (x=0;x<0x20;x++) megadrive_vdp_register[x]=0; // memset(megadrive_vdp_vram, 0xff, 0x10000); // memset(megadrive_vdp_cram, 0xff, 0x80); // memset(megadrive_vdp_vsram, 0xff, 0x80); memset(megadrive_vdp_vram, 0x00, 0x10000); memset(megadrive_vdp_cram, 0x00, 0x80); memset(megadrive_vdp_vsram, 0x00, 0x80); memset(megadrive_vdp_internal_sprite_attribute_table, 0x00, 0x400); megadrive_max_hposition = 480; sprite_renderline = auto_alloc_array(machine, UINT8, 1024); highpri_renderline = auto_alloc_array(machine, UINT8, 320); video_renderline = auto_alloc_array(machine, UINT32, 320); megadrive_vdp_palette_lookup = auto_alloc_array(machine, UINT16, 0x40); megadrive_vdp_palette_lookup_sprite = auto_alloc_array(machine, UINT16, 0x40); megadrive_vdp_palette_lookup_shadow = auto_alloc_array(machine, UINT16, 0x40); megadrive_vdp_palette_lookup_highlight = auto_alloc_array(machine, UINT16, 0x40); memset(megadrive_vdp_palette_lookup,0x00,0x40*2); memset(megadrive_vdp_palette_lookup_sprite,0x00,0x40*2); memset(megadrive_vdp_palette_lookup_shadow,0x00,0x40*2); memset(megadrive_vdp_palette_lookup_highlight,0x00,0x40*2); /* no special lookups */ segac2_bg_pal_lookup[0] = 0x00; segac2_bg_pal_lookup[1] = 0x10; segac2_bg_pal_lookup[2] = 0x20; segac2_bg_pal_lookup[3] = 0x30; segac2_sp_pal_lookup[0] = 0x00; segac2_sp_pal_lookup[1] = 0x10; segac2_sp_pal_lookup[2] = 0x20; segac2_sp_pal_lookup[3] = 0x30; } VIDEO_UPDATE(megadriv) { /* Copy our screen buffer here */ copybitmap(bitmap, render_bitmap, 0, 0, 0, 0, cliprect); // int xxx; /* reference */ // time_elapsed_since_crap = frame_timer->time_elapsed(); // xxx = screen->machine->device("maincpu")->attotime_to_cycles(time_elapsed_since_crap); // mame_printf_debug("update cycles %d, %08x %08x\n",xxx, (UINT32)(time_elapsed_since_crap.attoseconds>>32),(UINT32)(time_elapsed_since_crap.attoseconds&0xffffffff)); return 0; } static TIMER_DEVICE_CALLBACK( frame_timer_callback ) { /* callback */ } // line length = 342 /* The V counter counts up from 00h to EAh, then it jumps back to E5h and continues counting up to FFh. This allows it to cover the entire 262 line display. The H counter counts up from 00h to E9h, then it jumps back to 93h and continues counting up to FFh. This allows it to cover an entire 342 pixel line. */ /* - The 80th sprite has been drawn in 40-cell mode. - The 64th sprite has been drawn in 32-cell mode. - Twenty sprites on the same scanline have been drawn in 40 cell mode. - Sixteen sprites on the same scanline have been drawn in 32 cell mode. - 320 pixels worth of sprite data has been drawn on the same scanline in 40 cell mode. - 256 pixels worth of sprite data has been drawn on the same scanline in 32 cell mode. - The currently drawn sprite has a link field of zero. */ /* $05 - Sprite Attribute Table Base Address ----------------------------------------- Bits 6-0 of this register correspond to bits A15-A09 of the sprite attribute table. In 40-cell mode, A09 is always forced to zero. */ static void genesis_render_spriteline_to_spritebuffer(int scanline) { int screenwidth; int maxsprites=0; int maxpixels=0; UINT16 base_address=0; screenwidth = MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1); switch (screenwidth&3) { case 0: maxsprites = 64; maxpixels = 256; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9; break; case 1: maxsprites = 64; maxpixels = 256; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7f)<<9; break; case 2: maxsprites = 80; maxpixels = 320; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9; break; case 3: maxsprites = 80; maxpixels = 320; base_address = (MEGADRIVE_REG05_SPRITE_ADDR&0x7e)<<9; break; } /* Clear our Render Buffer */ memset(sprite_renderline, 0, 1024); { int spritenum; int ypos,xpos,addr; int drawypos; int /*drawwidth,*/ drawheight; int spritemask = 0; UINT8 height,width=0,link=0,xflip,yflip,colour,pri; /* Get Sprite Attribs */ spritenum = 0; //if (scanline==40) mame_printf_debug("spritelist start base %04x\n",base_address); do { //UINT16 value1,value2,value3,value4; //value1 = megadrive_vdp_vram[((base_address>>1)+spritenum*4)+0x0]; //value2 = megadrive_vdp_vram[((base_address>>1)+spritenum*4)+0x1]; //value3 = megadrive_vdp_vram[((base_address>>1)+spritenum*4)+0x2]; //value4 = megadrive_vdp_vram[((base_address>>1)+spritenum*4)+0x3]; ypos = (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x01ff)>>0; /* 0x03ff? */ // puyo puyo requires 0x1ff mask, not 0x3ff, see speech bubble corners height= (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x0300)>>8; width = (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x0c00)>>10; link = (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x1] & 0x007f)>>0; xpos = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x3) & 0x01ff)>>0; /* 0x03ff? */ // pirates gold has a sprite with co-ord 0x200... if(megadrive_imode==3) { ypos = (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x03ff)>>0; /* 0x3ff requried in interlace mode (sonic 2 2 player) */ drawypos = ypos - 256; drawheight = (height+1)*16; } else { ypos = (megadrive_vdp_internal_sprite_attribute_table[(spritenum*4)+0x0] & 0x01ff)>>0; /* 0x03ff? */ // puyo puyo requires 0x1ff mask, not 0x3ff, see speech bubble corners drawypos = ypos - 128; drawheight = (height+1)*8; } //if (scanline==40) mame_printf_debug("xpos %04x ypos %04x\n",xpos,ypos); if ((drawypos<=scanline) && ((drawypos+drawheight)>scanline)) { addr = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x07ff)>>0; xflip = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x0800)>>11; yflip = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x1000)>>12; colour= (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x6000)>>13; pri = (MEGADRIV_VDP_VRAM(((base_address>>1)+spritenum*4)+0x2) & 0x8000)>>15; if(megadrive_imode==3) { addr<<=1; addr &=0x7ff; } //drawwidth = (width+1)*8; if (pri==1) pri = 0x80; else pri = 0x40; /* todo: fix me, I'm sure this isn't right but sprite 0 + other sprite seem to do something.. maybe spritemask|=2 should be set for anything < 0x40 ?*/ if (xpos==0x00) spritemask|=1; //if (xpos==0x01) spritemask|=2; //if (xpos==0x04) spritemask|=2; // sonic 2 title screen //if (xpos==0x08) spritemask|=2; // rocket night adventures //if (xpos==0x10) spritemask|=2; // mercs l1 boss //if (xpos==0x0a) spritemask|=2; // legend of galahad //if (xpos==0x21) spritemask|=2; // shadow of the beast? if ((xpos>0) && (xpos<0x40)) spritemask|=2; if (spritemask==0x3) return; /* end todo: */ { //int xdraw; int xtile; int yline = scanline - drawypos; for (xtile=0;xtile>28; gfxdata <<=4; if (dat) { if (!sprite_renderline[xxx]) { sprite_renderline[xxx] = dat | (colour<<4)| pri; } else { megadrive_sprite_collision = 1; } } xxx++;xxx&=0x1ff; if (--maxpixels == 0x00) return; } } else { UINT16 base_addr; int xxx; UINT32 gfxdata; int loopcount; if(megadrive_imode==3) { if (!yflip) base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*16)))+(yline*2); else base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*16)))+((((height+1)*16)-yline-1)*2); } else { if (!yflip) base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*8)))+(yline*2); else base_addr = (addr<<4)+(((width-xtile))*((height+1)*(2*8)))+((((height+1)*8)-yline-1)*2); } xxx = (xpos+xtile*8)&0x1ff; gfxdata = MEGADRIV_VDP_VRAM((base_addr+1)&0x7fff) | (MEGADRIV_VDP_VRAM((base_addr+0)&0x7fff)<<16); for(loopcount=0;loopcount<8;loopcount++) { dat = (gfxdata & 0x0000000f)>>0; gfxdata >>=4; if (dat) { if (!sprite_renderline[xxx]) { sprite_renderline[xxx] = dat | (colour<<4)| pri; } else { megadrive_sprite_collision = 1; } } xxx++;xxx&=0x1ff; if (--maxpixels == 0x00) return; } } } } } spritenum = link; maxsprites--; } while ((maxsprites>=0) && (link!=0)); } } /* Clean up this function (!) */ static void genesis_render_videoline_to_videobuffer(int scanline) { UINT16 base_a; UINT16 base_w=0; UINT16 base_b; UINT16 size; UINT16 hsize = 64; UINT16 vsize = 64; UINT16 window_right; UINT16 window_hpos; UINT16 window_down; UINT16 window_vpos; UINT16 hscroll_base; UINT8 vscroll_mode; UINT8 hscroll_mode; int window_firstline; int window_lastline; int window_firstcol; int window_lastcol; int screenwidth; int numcolumns = 0; int hscroll_a = 0; int hscroll_b = 0; int x; int window_hsize=0; int window_vsize=0; int window_is_bugged = 0; int non_window_firstcol; int non_window_lastcol; int screenheight = MEGADRIVE_REG01_240_LINE?240:224; /* Clear our Render Buffer */ for (x=0;x<320;x++) { video_renderline[x]=MEGADRIVE_REG07_BGCOLOUR | 0x20000; // mark as BG } memset(highpri_renderline, 0, 320); /* is this line enabled? */ if (!MEGADRIVE_REG01_DISP_ENABLE) { //mame_printf_debug("line disabled %d\n",scanline); return; } /* looks different? */ if (MEGADRIVE_REG0_DISPLAY_DISABLE) { return; } base_a = MEGADRIVE_REG02_PATTERN_ADDR_A << 13; base_b = MEGADRIVE_REG04_PATTERN_ADDR_B << 13; size = MEGADRIVE_REG10_HSCROLL_SIZE | (MEGADRIVE_REG10_VSCROLL_SIZE<<4); window_right = MEGADRIVE_REG11_WINDOW_RIGHT; window_hpos = MEGADRIVE_REG11_WINDOW_HPOS; window_down = MEGADRIVE_REG12_WINDOW_DOWN; window_vpos = MEGADRIVE_REG12_WINDOW_VPOS; screenwidth = MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1); switch (screenwidth) { case 0: numcolumns = 32; window_hsize = 32; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1f) << 11; break; case 1: numcolumns = 32; window_hsize = 32; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1f) << 11; break; case 2: numcolumns = 40; window_hsize = 64; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1e) << 11; break; case 3: numcolumns = 40; window_hsize = 64; window_vsize = 32; base_w = (MEGADRIVE_REG03_PATTERN_ADDR_W&0x1e) << 11; break; // talespin cares about base mask, used for status bar } //mame_printf_debug("screenwidth %d\n",screenwidth); //base_w = mame_rand(Machine)&0xff; /* Calculate Exactly where we're going to draw the Window, and if the Window Bug applies */ window_is_bugged = 0; if (window_right) { window_firstcol = MEGADRIVE_REG11_WINDOW_HPOS*16; window_lastcol = numcolumns*8; if (window_firstcol>window_lastcol) window_firstcol = window_lastcol; non_window_firstcol = 0; non_window_lastcol = window_firstcol; } else { window_firstcol = 0; window_lastcol = MEGADRIVE_REG11_WINDOW_HPOS*16; if (window_lastcol>numcolumns*8) window_lastcol = numcolumns*8; non_window_firstcol = window_lastcol; non_window_lastcol = numcolumns*8; if (window_lastcol!=0) window_is_bugged=1; } if (window_down) { window_firstline = MEGADRIVE_REG12_WINDOW_VPOS*8; window_lastline = screenheight; // 240 in PAL? if (window_firstline>screenheight) window_firstline = screenheight; } else { window_firstline = 0; window_lastline = MEGADRIVE_REG12_WINDOW_VPOS*8; if (window_lastline>screenheight) window_lastline = screenheight; } /* if we're on a window scanline between window_firstline and window_lastline the window is the full width of the screen */ if (scanline>=window_firstline && scanline < window_lastline) { window_firstcol = 0; window_lastcol = numcolumns*8; // window is full-width of the screen non_window_firstcol = 0; non_window_lastcol=0; // disable non-window } vscroll_mode = MEGADRIVE_REG0B_VSCROLL_MODE; hscroll_mode = MEGADRIVE_REG0B_HSCROLL_MODE; hscroll_base = MEGADRIVE_REG0D_HSCROLL_ADDR<<10; switch (size) { case 0x00: hsize = 32; vsize = 32; break; case 0x01: hsize = 64; vsize = 32; break; case 0x02: hsize = 64; vsize = 1; /* mame_printf_debug("Invalid HSize! %02x\n",size);*/ break; case 0x03: hsize = 128;vsize = 32; break; case 0x10: hsize = 32; vsize = 64; break; case 0x11: hsize = 64; vsize = 64; break; case 0x12: hsize = 64; vsize = 1; /*mame_printf_debug("Invalid HSize! %02x\n",size);*/ break; case 0x13: hsize = 128;vsize = 32;/*mame_printf_debug("Invalid Total Size! %02x\n",size);*/break; case 0x20: hsize = 32; vsize = 64; mame_printf_debug("Invalid VSize!\n"); break; case 0x21: hsize = 64; vsize = 64; mame_printf_debug("Invalid VSize!\n"); break; case 0x22: hsize = 64; vsize = 1; /*mame_printf_debug("Invalid HSize & Invalid VSize!\n");*/ break; case 0x23: hsize = 128;vsize = 64; mame_printf_debug("Invalid VSize!\n"); break; case 0x30: hsize = 32; vsize = 128; break; case 0x31: hsize = 64; vsize = 64; /*mame_printf_debug("Invalid Total Size! %02x\n",size);*/break; // super skidmarks attempts this.. case 0x32: hsize = 64; vsize = 1; /*mame_printf_debug("Invalid HSize & Invalid Total Size!\n");*/ break; case 0x33: hsize = 128;vsize = 128; mame_printf_debug("Invalid Total Size! %02x\n",size);break; } switch (MEGADRIVE_REG0B_HSCROLL_MODE) { case 0x00: // Full Screen Scroll hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1); break; case 0x01: // 'Broken' Line Scroll if(megadrive_imode==3) { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+((scanline>>1)&7)*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+((scanline>>1)&7)*2); } else { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline&7)*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline&7)*2); } break; case 0x02: // Cell Scroll if(megadrive_imode==3) { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+((scanline>>1)&~7)*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+((scanline>>1)&~7)*2); } else { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline&~7)*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline&~7)*2); } break; case 0x03: // Full Line Scroll if(megadrive_imode==3) { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+(scanline>>1)*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+(scanline>>1)*2); } else { hscroll_a = MEGADRIV_VDP_VRAM((hscroll_base>>1)+0+scanline*2); hscroll_b = MEGADRIV_VDP_VRAM((hscroll_base>>1)+1+scanline*2); } break; } /* Low Priority B Tiles */ { int column; int vscroll; for (column=0;column>3))&(hsize-1); if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=hscroll_part;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=hscroll_part;shift<8;shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } if (MEGADRIVE_REG0B_VSCROLL_MODE) { if (hscroll_b&0xf) vscroll = megadrive_vdp_vsram[((column-1)*2+1)&0x3f]; else vscroll = megadrive_vdp_vsram[((column)*2+1)&0x3f]; } else { vscroll = megadrive_vdp_vsram[1]; } hcolumn = ((column*2)-(hscroll_b>>3))&(hsize-1); if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(shift*4))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } if (MEGADRIVE_REG0B_VSCROLL_MODE) { vscroll = megadrive_vdp_vsram[((column)*2+1)&0x3f]; } else { vscroll = megadrive_vdp_vsram[1]; } hcolumn = ((column*2+1)-(hscroll_b>>3))&(hsize-1); if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); tile_base = (base_b>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); tile_base = (base_b>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<(hscroll_part);shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<(hscroll_part);shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; dpos++; } } } } /* END */ } /* Low Priority A Tiles + Window(!) */ { int column; int vscroll; for (column=window_firstcol/16;column>1)+((vcolumn>>4)*window_hsize)+hcolumn; } else { tile_base = (base_w>>1)+((vcolumn>>3)*window_hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; } if(megadrive_imode==3) { if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } hcolumn = (column*2+1)&(window_hsize-1); if(megadrive_imode==3) { tile_base = (base_w>>1)+((vcolumn>>4)*window_hsize)+hcolumn; } else { tile_base = (base_w>>1)+((vcolumn>>3)*window_hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; } if(megadrive_imode==3) { if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } } /* Non Window Part */ for (column=non_window_firstcol/16;columnnon_window_firstcol/16)) hcolumn = ((column*2-1)-(hscroll_a>>3))&(hsize-1); else hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1); if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); } if(megadrive_imode==3) { tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=hscroll_part;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=hscroll_part;shift<8;shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } if (MEGADRIVE_REG0B_VSCROLL_MODE) { if (hscroll_a&0xf) vscroll = megadrive_vdp_vsram[((column-1)*2+0)&0x3f]; else vscroll = megadrive_vdp_vsram[((column)*2+0)&0x3f]; } else { vscroll = megadrive_vdp_vsram[0]; } if ((!window_is_bugged) || ((hscroll_a&0xf)==0) || (column>non_window_firstcol/16)) hcolumn = ((column*2)-(hscroll_a>>3))&(hsize-1); // not affected by bug? else { if ((hscroll_a&0xf)<8) hcolumn = ((column*2)-(hscroll_a>>3))&(hsize-1); else hcolumn = ((column*2+2)-(hscroll_a>>3))&(hsize-1); } if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<8;shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } if (MEGADRIVE_REG0B_VSCROLL_MODE) { vscroll = megadrive_vdp_vsram[((column)*2+0)&0x3f]; } else { vscroll = megadrive_vdp_vsram[0]; } if ((!window_is_bugged) || ((hscroll_a&0xf)==0) || (column>non_window_firstcol/16)) hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1); else hcolumn = ((column*2+1)-(hscroll_a>>3))&(hsize-1); if(megadrive_imode==3) { vcolumn = (vscroll + scanline)&((vsize*16)-1); tile_base = (base_a>>1)+((vcolumn>>4)*hsize)+hcolumn; } else { vcolumn = (vscroll + scanline)&((vsize*8)-1); tile_base = (base_a>>1)+((vcolumn>>3)*hsize)+hcolumn; } tile_base &=0x7fff; tile_dat = MEGADRIV_VDP_VRAM(tile_base); tile_xflip = (tile_dat&0x0800); tile_yflip = (tile_dat&0x1000); tile_colour =(tile_dat&0x6000)>>13; tile_pri = (tile_dat&0x8000)>>15; tile_addr = ((tile_dat&0x07ff)<<4); if(megadrive_imode==3) { tile_addr <<=1; tile_addr &=0x7fff; } if(megadrive_imode==3) { if (!tile_yflip) tile_addr+=(vcolumn&0xf)*2; else tile_addr+=((0xf-vcolumn)&0xf)*2; } else { if (!tile_yflip) tile_addr+=(vcolumn&7)*2; else tile_addr+=((7-vcolumn)&7)*2; } if (!tile_xflip) { /* 8 pixels */ UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<(hscroll_part);shift++) { dat = (gfxdata>>(28-(shift*4)))&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } else { UINT32 gfxdata = (MEGADRIV_VDP_VRAM(tile_addr+0)<<16)|MEGADRIV_VDP_VRAM(tile_addr+1); int shift; for (shift=0;shift<(hscroll_part);shift++) { dat = (gfxdata>>(shift*4) )&0x000f; if (!tile_pri) { if(dat) video_renderline[dpos] = dat | (tile_colour<<4); } else { if (dat) highpri_renderline[dpos] = dat | (tile_colour<<4) | 0x80; else highpri_renderline[dpos] = highpri_renderline[dpos]|0x80; } dpos++; } } } } } /* END */ /* MEGADRIVE_REG0C_SHADOW_HIGLIGHT */ /* Low Priority Sprites */ for (x=0;x<320;x++) { if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT) { if (sprite_renderline[x+128] & 0x40) { video_renderline[x] = sprite_renderline[x+128]&0x3f; video_renderline[x] |= 0x10000; // mark as sprite pixel } } else { /* Special Shadow / Highlight processing */ if (sprite_renderline[x+128] & 0x40) { UINT8 spritedata; spritedata = sprite_renderline[x+128]&0x3f; if ((spritedata==0x0e) || (spritedata==0x1e) || (spritedata==0x2e)) { /* BUG in sprite chip, these colours are always normal intensity */ video_renderline[x] = spritedata | 0x4000; video_renderline[x] |= 0x10000; // mark as sprite pixel } else if (spritedata==0x3e) { /* Everything below this is half colour, mark with 0x8000 to mark highlight' */ video_renderline[x] = video_renderline[x]|0x8000; // spiderwebs.. } else if (spritedata==0x3f) { /* This is a Shadow operator, but everything below is already low pri, no effect */ video_renderline[x] = video_renderline[x]|0x2000; } else { video_renderline[x] = spritedata; video_renderline[x] |= 0x10000; // mark as sprite pixel } } } } /* High Priority A+B Tiles */ for (x=0;x<320;x++) { if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT) { /* Normal Processing */ int dat; dat = highpri_renderline[x]; if (dat&0x80) { if (dat&0x0f) video_renderline[x] = highpri_renderline[x]&0x3f; } } else { /* Shadow / Highlight Mode */ int dat; dat = highpri_renderline[x]; if (dat&0x80) { if (dat&0x0f) video_renderline[x] = (highpri_renderline[x]&0x3f) | 0x4000; else video_renderline[x] = video_renderline[x] | 0x4000; // set 'normal' } } } /* High Priority Sprites */ for (x=0;x<320;x++) { if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT) { /* Normal */ if (sprite_renderline[x+128] & 0x80) { video_renderline[x] = sprite_renderline[x+128]&0x3f; video_renderline[x] |= 0x10000; // mark as sprite pixel } } else { if (sprite_renderline[x+128] & 0x80) { UINT8 spritedata; spritedata = sprite_renderline[x+128]&0x3f; if (spritedata==0x3e) { /* set flag 0x8000 to indicate highlight */ video_renderline[x] = video_renderline[x]|0x8000; } else if (spritedata==0x3f) { /* This is a Shadow operator set shadow bit */ video_renderline[x] = video_renderline[x]|0x2000; } else { video_renderline[x] = spritedata | 0x4000; video_renderline[x] |= 0x10000; // mark as sprite pixel } } } } } static UINT32 _32x_linerender[320+258]; // tmp buffer (bigger than it needs to be to simplify RLE decode) /* This converts our render buffer to real screen colours */ static void genesis_render_videobuffer_to_screenbuffer(running_machine *machine, int scanline) { UINT16*lineptr; int x; lineptr = BITMAP_ADDR16(render_bitmap, scanline, 0); /* render 32x output to a buffer */ if (_32x_is_connected && (_32x_displaymode != 0)) { if (_32x_displaymode==1) { UINT32 lineoffs; int start; lineoffs = _32x_display_dram[scanline]; if (_32x_screenshift == 0) start=0; else start = -1; for (x=start;x<320;x++) { UINT16 coldata; coldata = _32x_display_dram[lineoffs]; { if (x>=0) { _32x_linerender[x] = _32x_palette_lookup[(coldata & 0xff00)>>8]; } x++; if (x>=0) { _32x_linerender[x] = _32x_palette_lookup[(coldata & 0x00ff)]; } } lineoffs++; } } else if (_32x_displaymode==3) // mode 3 = RLE (used by BRUTAL intro) { UINT32 lineoffs; int start; lineoffs = _32x_display_dram[scanline]; if (_32x_screenshift == 0) start=0; else start = -1; x = start; while (x<320) { UINT16 coldata, length, l; coldata = _32x_display_dram[lineoffs]; length = ((coldata & 0xff00)>>8)+1; coldata = (coldata & 0x00ff)>>0; for (l=0;l=0) { _32x_linerender[x] = _32x_palette_lookup[(coldata)]; } x++; } lineoffs++; } } else // MODE 2 - 15bpp mode, not used by any commercial games? { UINT32 lineoffs; int start; lineoffs = _32x_display_dram[scanline]; if (_32x_screenshift == 0) start=0; else start = -1; x = start; while (x<320) { UINT16 coldata; coldata = _32x_display_dram[lineoffs&0xffff]; // need to swap red and blue around for MAME { int r = ((coldata >> 0) & 0x1f); int g = ((coldata >> 5) & 0x1f); int b = ((coldata >> 10) & 0x1f); int p = ((coldata >> 15) & 0x01); // priority 'through' bit coldata = (r << 10) | (g << 5) | (b << 0) | (p << 15); } if (x>=0) _32x_linerender[x] = coldata; x++; lineoffs++; } } } int _32x_priority = _32x_videopriority; if (!MEGADRIVE_REG0C_SHADOW_HIGLIGHT) { for (x=0;x<320;x++) { UINT32 dat; dat = video_renderline[x]; int drawn = 0; // low priority 32x - if it's the bg pen, we have a 32x, and it's display is enabled... if ((dat&0x20000) && (_32x_is_connected) && (_32x_displaymode != 0)) { if (!_32x_priority) { if (!(_32x_linerender[x]&0x8000)) { lineptr[x] = _32x_linerender[x]&0x7fff; drawn = 1; } } else { if ((_32x_linerender[x]&0x8000)) { lineptr[x] = _32x_linerender[x]&0x7fff; drawn = 1; } } } if (drawn==0) { if (dat&0x10000) lineptr[x] = megadrive_vdp_palette_lookup_sprite[(dat&0x0f) | segac2_sp_pal_lookup[(dat&0x30)>>4]]; else lineptr[x] = megadrive_vdp_palette_lookup[(dat&0x0f) | segac2_bg_pal_lookup[(dat&0x30)>>4]]; } } } else { for (x=0;x<320;x++) { UINT32 dat; dat = video_renderline[x]; int drawn = 0; // low priority 32x - if it's the bg pen, we have a 32x, and it's display is enabled... if ((dat&0x20000) && (_32x_is_connected) && (_32x_displaymode != 0)) { if (!_32x_priority) { if (!(_32x_linerender[x]&0x8000)) { lineptr[x] = _32x_linerender[x]&0x7fff; drawn = 1; } } else { if ((_32x_linerender[x]&0x8000)) { lineptr[x] = _32x_linerender[x]&0x7fff; drawn = 1; } } } if (drawn==0) { /* Verify my handling.. I'm not sure all cases are correct */ switch (dat&0x1e000) { case 0x00000: // low priority, no shadow sprite, no highlight = shadow case 0x02000: // low priority, shadow sprite, no highlight = shadow case 0x06000: // normal pri, shadow sprite, no highlight = shadow? case 0x10000: // (sprite) low priority, no shadow sprite, no highlight = shadow case 0x12000: // (sprite) low priority, shadow sprite, no highlight = shadow case 0x16000: // (sprite) normal pri, shadow sprite, no highlight = shadow? lineptr[x] = megadrive_vdp_palette_lookup_shadow[(dat&0x0f) | segac2_bg_pal_lookup[(dat&0x30)>>4]]; break; case 0x4000: // normal pri, no shadow sprite, no highlight = normal; case 0x8000: // low pri, highlight sprite = normal; lineptr[x] = megadrive_vdp_palette_lookup[(dat&0x0f) | segac2_bg_pal_lookup[(dat&0x30)>>4]]; break; case 0x14000: // (sprite) normal pri, no shadow sprite, no highlight = normal; case 0x18000: // (sprite) low pri, highlight sprite = normal; lineptr[x] = megadrive_vdp_palette_lookup_sprite[(dat&0x0f) | segac2_sp_pal_lookup[(dat&0x30)>>4]]; break; case 0x0c000: // normal pri, highlight set = highlight? case 0x1c000: // (sprite) normal pri, highlight set = highlight? lineptr[x] = megadrive_vdp_palette_lookup_highlight[(dat&0x0f) | segac2_bg_pal_lookup[(dat&0x30)>>4]]; break; case 0x0a000: // shadow set, highlight set - not possible case 0x0e000: // shadow set, highlight set, normal set, not possible case 0x1a000: // (sprite)shadow set, highlight set - not possible case 0x1e000: // (sprite)shadow set, highlight set, normal set, not possible default: lineptr[x] = mame_rand(machine)&0x3f; break; } } } } // high priority 32x if ((_32x_is_connected) && (_32x_displaymode != 0)) { for (x=0;x<320;x++) { if (!_32x_priority) { if ((_32x_linerender[x]&0x8000)) lineptr[x] = _32x_linerender[x]&0x7fff; } else { if (!(_32x_linerender[x]&0x8000)) lineptr[x] = _32x_linerender[x]&0x7fff; } } } } static void genesis_render_scanline(running_machine *machine, int scanline) { //if (MEGADRIVE_REG01_DMA_ENABLE==0) mame_printf_debug("off\n"); genesis_render_spriteline_to_spritebuffer(genesis_scanline_counter); genesis_render_videoline_to_videobuffer(scanline); genesis_render_videobuffer_to_screenbuffer(machine, scanline); } INLINE UINT16 get_hposition(void) { // static int lowest = 99999; // static int highest = -99999; attotime time_elapsed_since_scanline_timer; UINT16 value4; time_elapsed_since_scanline_timer = scanline_timer->time_elapsed(); if (time_elapsed_since_scanline_timer.attoseconds<(ATTOSECONDS_PER_SECOND/megadriv_framerate /megadrive_total_scanlines)) { value4 = (UINT16)(megadrive_max_hposition*((double)(time_elapsed_since_scanline_timer.attoseconds) / (double)(ATTOSECONDS_PER_SECOND/megadriv_framerate /megadrive_total_scanlines))); } else /* in some cases (probably due to rounding errors) we get some stupid results (the odd huge value where the time elapsed is much higher than the scanline time??!).. hopefully by clamping the result to the maximum we limit errors */ { value4 = megadrive_max_hposition; } // if (value4>highest) highest = value4; // if (value4=0 && genesis_scanline_counteradjust(attotime_div(ATTOTIME_IN_HZ(megadriv_framerate), megadrive_total_scanlines)); render_timer->adjust(ATTOTIME_IN_USEC(1)); if (genesis_scanline_counter==megadrive_irq6_scanline ) { // mame_printf_debug("x %d",genesis_scanline_counter); irq6_on_timer->adjust(ATTOTIME_IN_USEC(6)); megadrive_irq6_pending = 1; megadrive_vblank_flag = 1; // 32x interrupt! if (_32x_is_connected) { sh2_master_vint_pending = 1; sh2_slave_vint_pending = 1; _32x_check_irqs(timer.machine); } } _32x_check_framebuffer_swap(); // if (genesis_scanline_counter==0) irq4counter = MEGADRIVE_REG0A_HINT_VALUE; // irq4counter = MEGADRIVE_REG0A_HINT_VALUE; if (genesis_scanline_counter<=224) { irq4counter--; if (irq4counter==-1) { if (megadrive_imode==3) irq4counter = MEGADRIVE_REG0A_HINT_VALUE*2; else irq4counter=MEGADRIVE_REG0A_HINT_VALUE; megadrive_irq4_pending = 1; if (MEGADRIVE_REG0_IRQ4_ENABLE) { irq4_on_timer->adjust(ATTOTIME_IN_USEC(1)); //mame_printf_debug("irq4 on scanline %d reload %d\n",genesis_scanline_counter,MEGADRIVE_REG0A_HINT_VALUE); } } } else { if (megadrive_imode==3) irq4counter = MEGADRIVE_REG0A_HINT_VALUE*2; else irq4counter=MEGADRIVE_REG0A_HINT_VALUE; } //if (genesis_scanline_counter==0) irq4_on_timer->adjust(ATTOTIME_IN_USEC(2)); if(_32x_is_connected) { _32x_hcount_compare_val++; if(_32x_hcount_compare_val >= _32x_hcount_reg) { _32x_hcount_compare_val = -1; if(genesis_scanline_counter < 224 || sh2_hint_in_vbl) { if(sh2_master_hint_enable) { cpu_set_input_line(_32x_master_cpu,SH2_HINT_IRQ_LEVEL,ASSERT_LINE); } if(sh2_slave_hint_enable) { cpu_set_input_line(_32x_slave_cpu,SH2_HINT_IRQ_LEVEL,ASSERT_LINE); } } } } if (timer.machine->device("genesis_snd_z80") != NULL) { if (genesis_scanline_counter == megadrive_z80irq_scanline) { if ((genz80.z80_has_bus == 1) && (genz80.z80_is_reset == 0)) cputag_set_input_line(timer.machine, "genesis_snd_z80", 0, HOLD_LINE); } if (genesis_scanline_counter == megadrive_z80irq_scanline + 1) { cputag_set_input_line(timer.machine, "genesis_snd_z80", 0, CLEAR_LINE); } } } else /* pretend we're still on the same scanline to compensate for rounding errors */ { genesis_scanline_counter = megadrive_total_scanlines - 1; } } static TIMER_DEVICE_CALLBACK( irq6_on_callback ) { //mame_printf_debug("irq6 active on %d\n",genesis_scanline_counter); { // megadrive_irq6_pending = 1; if (MEGADRIVE_REG01_IRQ6_ENABLE || genesis_always_irq6) cputag_set_input_line(timer.machine, "maincpu", 6, HOLD_LINE); } } static TIMER_DEVICE_CALLBACK( irq4_on_callback ) { //mame_printf_debug("irq4 active on %d\n",genesis_scanline_counter); cputag_set_input_line(timer.machine, "maincpu", 4, HOLD_LINE); } /*****************************************************************************************/ static int hazemdchoice_megadrive_region_export; static int hazemdchoice_megadrive_region_pal; static int hazemdchoice_megadriv_framerate; MACHINE_START( megadriv ) { megadrive_init_io(machine); } MACHINE_RESET( megadriv ) { /* default state of z80 = reset, with bus */ mame_printf_debug("Resetting Megadrive / Genesis\n"); switch (input_port_read_safe(machine, "REGION", 0x00)) { case 1: // US megadrive_region_export = 1; megadrive_region_pal = 0; megadriv_framerate = 60; mame_printf_debug("Using Region = US\n"); break; case 2: // JAPAN megadrive_region_export = 0; megadrive_region_pal = 0; megadriv_framerate = 60; mame_printf_debug("Using Region = JAPAN\n"); break; case 3: // EUROPE megadrive_region_export = 1; megadrive_region_pal = 1; megadriv_framerate = 50; mame_printf_debug("Using Region = EUROPE\n"); break; default: // as chosen by driver megadrive_region_export = hazemdchoice_megadrive_region_export; megadrive_region_pal = hazemdchoice_megadrive_region_pal; megadriv_framerate = hazemdchoice_megadriv_framerate; mame_printf_debug("Using Region = DEFAULT\n"); break; } if (machine->device("genesis_snd_z80") != NULL) { genz80.z80_is_reset = 1; genz80.z80_has_bus = 1; genz80.z80_bank_addr = 0; genesis_scanline_counter = -1; timer_set( machine, attotime_zero, NULL, 0, megadriv_z80_run_state ); } megadrive_imode = 0; megadrive_reset_io(machine); frame_timer = machine->device("frame_timer"); scanline_timer = machine->device("scanline_timer"); render_timer = machine->device("render_timer"); irq6_on_timer = machine->device("irq6_timer"); irq4_on_timer = machine->device("irq4_timer"); frame_timer->adjust(attotime_zero); scanline_timer->adjust(attotime_zero); if (genesis_other_hacks) { // set_refresh_rate(megadriv_framerate); machine->device("maincpu")->set_clock_scale(0.9950f); /* Fatal Rewind is very fussy... */ // machine->device("maincpu")->set_clock_scale(0.3800f); /* Fatal Rewind is very fussy... */ memset(megadrive_ram,0x00,0x10000); } irq4counter = -1; megadrive_total_scanlines = 262; megadrive_visible_scanlines = 224; megadrive_irq6_scanline = 224; megadrive_z80irq_scanline = 226; /* if any of these extra CPUs exist, pause them until we actually turn them on */ if (_32x_master_cpu != NULL) { cpu_set_input_line(_32x_master_cpu, INPUT_LINE_RESET, ASSERT_LINE); } if (_32x_slave_cpu != NULL) { cpu_set_input_line(_32x_slave_cpu, INPUT_LINE_RESET, ASSERT_LINE); } if (_segacd_68k_cpu != NULL ) { MACHINE_RESET_CALL( segacd ); } if(_32x_is_connected) { current_fifo_block = fifo_block_a; current_fifo_readblock = fifo_block_b; current_fifo_write_pos = 0; current_fifo_read_pos = 0; fifo_block_a_full = 0; fifo_block_b_full = 0; _32x_hcount_compare_val = -1; } } void megadriv_stop_scanline_timer(void) { scanline_timer->reset(); } /* 999999999999999960 1000000000000000000 subseconds = 1 second / 60 */ /* VIDEO_EOF is used to resync the scanline counters */ VIDEO_EOF(megadriv) { rectangle visarea; int scr_width = 320; megadrive_vblank_flag = 0; //megadrive_irq6_pending = 0; /* NO! (breaks warlock) */ /* Set it to -1 here, so it becomes 0 when the first timer kicks in */ genesis_scanline_counter = -1; megadrive_sprite_collision=0;//? when to reset this .. megadrive_imode = MEGADRIVE_REG0C_INTERLEAVE; // can't change mid-frame.. megadrive_imode_odd_frame^=1; // cputag_set_input_line(machine, "genesis_snd_z80", 0, CLEAR_LINE); // if the z80 interrupt hasn't happened by now, clear it.. if (input_port_read_safe(machine, "RESET", 0x00) & 0x01) cputag_set_input_line(machine, "maincpu", INPUT_LINE_RESET, PULSE_LINE); /* int megadrive_total_scanlines = 262; int megadrive_visible_scanlines = 224; int megadrive_irq6_scanline = 224; int megadrive_irq6_hpos = 320; int megadrive_z80irq_scanline = 224; int megadrive_z80irq_hpos = 320; */ if (MEGADRIVE_REG01_240_LINE) { if (!megadrive_region_pal) { /* this is invalid! */ megadrive_visible_scanlines = 240; megadrive_total_scanlines = 262; megadrive_irq6_scanline = 240; megadrive_z80irq_scanline = 240; } else { megadrive_visible_scanlines = 240; megadrive_total_scanlines = 313; megadrive_irq6_scanline = 240; megadrive_z80irq_scanline = 240; } } else { if (!megadrive_region_pal) { megadrive_visible_scanlines = 224; megadrive_total_scanlines=262; megadrive_irq6_scanline = 224; megadrive_z80irq_scanline = 224; } else { megadrive_visible_scanlines = 224; megadrive_total_scanlines=313; megadrive_irq6_scanline = 224; megadrive_z80irq_scanline = 224; } } if (megadrive_imode==3) { megadrive_visible_scanlines<<=1; megadrive_total_scanlines<<=1; megadrive_irq6_scanline <<=1; megadrive_z80irq_scanline <<=1; } //get_hposition(); switch (MEGADRIVE_REG0C_RS0 | (MEGADRIVE_REG0C_RS1 << 1)) { /* note, add 240 mode + init new timings! */ case 0:scr_width = 256;break;// configure_screen(0, 256-1, megadrive_visible_scanlines-1,(double)megadriv_framerate); break; case 1:scr_width = 256;break;// configure_screen(0, 256-1, megadrive_visible_scanlines-1,(double)megadriv_framerate); mame_printf_debug("invalid screenmode!\n"); break; case 2:scr_width = 320;break;// configure_screen(0, 320-1, megadrive_visible_scanlines-1,(double)megadriv_framerate); break; /* technically invalid, but used in rare cases */ case 3:scr_width = 320;break;// configure_screen(0, 320-1, megadrive_visible_scanlines-1,(double)megadriv_framerate); break; } // mame_printf_debug("my mode %02x", megadrive_vdp_register[0x0c]); visarea.min_x = 0; visarea.max_x = scr_width-1; visarea.min_y = 0; visarea.max_y = megadrive_visible_scanlines-1; machine->primary_screen->configure(scr_width, megadrive_visible_scanlines, visarea, HZ_TO_ATTOSECONDS(megadriv_framerate)); if (0) { //int xxx; UINT64 frametime; // /* reference */ frametime = ATTOSECONDS_PER_SECOND/megadriv_framerate; //time_elapsed_since_crap = frame_timer->time_elapsed(); //xxx = machine->device("maincpu")->attotime_to_cycles(time_elapsed_since_crap); //mame_printf_debug("---------- cycles %d, %08x %08x\n",xxx, (UINT32)(time_elapsed_since_crap.attoseconds>>32),(UINT32)(time_elapsed_since_crap.attoseconds&0xffffffff)); //mame_printf_debug("---------- framet %d, %08x %08x\n",xxx, (UINT32)(frametime>>32),(UINT32)(frametime&0xffffffff)); frame_timer->adjust(attotime_zero); } scanline_timer->adjust(attotime_zero); if(_32x_is_connected) _32x_hcount_compare_val = -1; } UINT16* megadriv_backupram; int megadriv_backupram_length; #ifndef MESS static NVRAM_HANDLER( megadriv ) { if (megadriv_backupram!=NULL) { if (read_or_write) mame_fwrite(file, megadriv_backupram, megadriv_backupram_length); else { if (file) { mame_fread(file, megadriv_backupram, megadriv_backupram_length); } else { int x; for (x=0;x 0.40? (sound is corrupted even if DAC is slient?!) MDRV_DEVICE_REMOVE("ymsnd") MDRV_DEVICE_REMOVE("snsnd") MDRV_SOUND_ADD("ymsnd", YM2612, MASTER_CLOCK_NTSC/7) MDRV_SOUND_ROUTE(0, "lspeaker", (0.50)/2) MDRV_SOUND_ROUTE(1, "rspeaker", (0.50)/2) /* sound hardware */ MDRV_SOUND_ADD("snsnd", SMSIII, MASTER_CLOCK_NTSC/15) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", (0.25)/2) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", (0.25)/2) MDRV_SOUND_ADD("lch_pwm", DAC, 0) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.40) MDRV_SOUND_ADD("rch_pwm", DAC, 0) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.40) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( genesis_32x_pal, megadpal ) #ifndef _32X_SWAP_MASTER_SLAVE_HACK MDRV_CPU_ADD("32x_master_sh2", SH2, (MASTER_CLOCK_PAL*3)/7 ) MDRV_CPU_PROGRAM_MAP(sh2_main_map) MDRV_CPU_CONFIG(sh2_conf_master) #endif MDRV_CPU_ADD("32x_slave_sh2", SH2, (MASTER_CLOCK_PAL*3)/7 ) MDRV_CPU_PROGRAM_MAP(sh2_slave_map) MDRV_CPU_CONFIG(sh2_conf_slave) #ifdef _32X_SWAP_MASTER_SLAVE_HACK MDRV_CPU_ADD("32x_master_sh2", SH2, (MASTER_CLOCK_PAL*3)/7 ) MDRV_CPU_PROGRAM_MAP(sh2_main_map) MDRV_CPU_CONFIG(sh2_conf_master) #endif // brutal needs at least 30000 or the backgrounds don't animate properly / lock up, and the game // freezes. Some stage seem to need as high as 80000 ? this *KILLS* performance // // boosting the interleave here actually makes Kolibri run incorrectly however, that // one works best just boosting the interleave on communications?! MDRV_QUANTUM_TIME(HZ(1800000)) // we need to remove and re-add the sound system because the balance is different // due to MAME / MESS having severe issues if the dac output is > 0.40? (sound is corrupted even if DAC is slient?!) MDRV_DEVICE_REMOVE("ymsnd") MDRV_DEVICE_REMOVE("snsnd") MDRV_SOUND_ADD("ymsnd", YM2612, MASTER_CLOCK_PAL/7) MDRV_SOUND_ROUTE(0, "lspeaker", (0.50)/2) MDRV_SOUND_ROUTE(1, "rspeaker", (0.50)/2) /* sound hardware */ MDRV_SOUND_ADD("snsnd", SMSIII, MASTER_CLOCK_PAL/15) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", (0.25)/2) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", (0.25)/2) MDRV_SOUND_ADD("lch_pwm", DAC, 0) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.40) MDRV_SOUND_ADD("rch_pwm", DAC, 0) MDRV_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.40) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( genesis_scd, megadriv ) MDRV_CPU_ADD("segacd_68k", M68000, SEGACD_CLOCK ) /* 12.5 MHz */ MDRV_CPU_PROGRAM_MAP(segacd_map) MDRV_SOUND_ADD( "cdda", CDDA, 0 ) MDRV_SOUND_ROUTE( 0, "lspeaker", 1.00 ) MDRV_SOUND_ROUTE( 1, "rspeaker", 1.00 ) MDRV_SOUND_ADD("rfsnd", RF5C68, SEGACD_CLOCK) // RF5C164! MDRV_SOUND_ROUTE( 0, "lspeaker", 0.25 ) MDRV_SOUND_ROUTE( 1, "rspeaker", 0.25 ) #ifdef MESS MDRV_CDROM_ADD( "cdrom" ) #endif MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( genesis_32x_scd, genesis_32x ) MDRV_CPU_ADD("segacd_68k", M68000, SEGACD_CLOCK ) /* 12.5 MHz */ MDRV_CPU_PROGRAM_MAP(segacd_map) MDRV_SOUND_ADD( "cdda", CDDA, 0 ) MDRV_SOUND_ROUTE( 0, "lspeaker", 1.00 ) MDRV_SOUND_ROUTE( 1, "rspeaker", 1.00 ) MDRV_SOUND_ADD("rfsnd", RF5C68, SEGACD_CLOCK) // RF5C164 MDRV_SOUND_ROUTE( 0, "lspeaker", 0.25 ) MDRV_SOUND_ROUTE( 1, "rspeaker", 0.25 ) #ifdef MESS MDRV_CDROM_ADD( "cdrom" ) #endif MACHINE_CONFIG_END /* Callback when the genesis enters interrupt code */ static IRQ_CALLBACK(genesis_int_callback) { if (irqline==4) { megadrive_irq4_pending = 0; } if (irqline==6) { megadrive_irq6_pending = 0; // mame_printf_debug("clear pending!\n"); } return (0x60+irqline*4)/4; // vector address } static int megadriv_tas_callback(running_device *device) { return 0; // writeback not allowed } static void megadriv_init_common(running_machine *machine) { const input_port_token *ipt = machine->gamedrv->ipt; /* Look to see if this system has the standard Sound Z80 */ _genesis_snd_z80_cpu = machine->device("genesis_snd_z80"); if (_genesis_snd_z80_cpu != NULL) { //printf("GENESIS Sound Z80 cpu found '%s'\n", _genesis_snd_z80_cpu->tag() ); genz80.z80_prgram = auto_alloc_array(machine, UINT8, 0x2000); memory_set_bankptr(machine, "bank1", genz80.z80_prgram ); } /* Look to see if this system has the 32x Master SH2 */ _32x_master_cpu = machine->device("32x_master_sh2"); if (_32x_master_cpu != NULL) { printf("32x MASTER SH2 cpu found '%s'\n", _32x_master_cpu->tag() ); } /* Look to see if this system has the 32x Slave SH2 */ _32x_slave_cpu = machine->device("32x_slave_sh2"); if (_32x_slave_cpu != NULL) { printf("32x SLAVE SH2 cpu found '%s'\n", _32x_slave_cpu->tag() ); } if ((_32x_master_cpu != NULL) && (_32x_slave_cpu != NULL)) { _32x_is_connected = 1; } else { _32x_is_connected = 0; } if(_32x_is_connected) { _32x_pwm_timer = timer_alloc(machine, _32x_pwm_callback, 0); timer_adjust_oneshot(_32x_pwm_timer, attotime_never, 0); } sega_cd_connected = 0; segacd_wordram_mapped = 0; _segacd_68k_cpu = machine->device("segacd_68k"); if (_segacd_68k_cpu != NULL) { printf("Sega CD secondary 68k cpu found '%s'\n", _segacd_68k_cpu->tag() ); sega_cd_connected = 1; segacd_init_main_cpu(machine); } _svp_cpu = machine->device("svp"); if (_svp_cpu != NULL) { printf("SVP (cpu) found '%s'\n", _svp_cpu->tag() ); } cpu_set_irq_callback(machine->device("maincpu"), genesis_int_callback); megadriv_backupram = NULL; megadriv_backupram_length = 0; vdp_get_word_from_68k_mem = vdp_get_word_from_68k_mem_default; m68k_set_tas_callback(machine->device("maincpu"), megadriv_tas_callback); if ((ipt == INPUT_PORTS_NAME(megadri6)) || (ipt == INPUT_PORTS_NAME(ssf2mdb)) || (ipt == INPUT_PORTS_NAME(mk3mdb))) { megadrive_io_read_data_port_ptr = megadrive_io_read_data_port_6button; megadrive_io_write_data_port_ptr = megadrive_io_write_data_port_6button; mame_printf_debug("6 button game\n"); } else { megadrive_io_read_data_port_ptr = megadrive_io_read_data_port_3button; megadrive_io_write_data_port_ptr = megadrive_io_write_data_port_3button; mame_printf_debug("3 button game\n"); } { /* only really useful on official games, ea games etc. don't bother some games specify a single address, (start 200001, end 200001) this usually means there is serial eeprom instead */ int i; UINT16 *rom = (UINT16*)memory_region(machine, "maincpu"); mame_printf_debug("DEBUG:: Header: Backup RAM string (ignore for games without)\n"); for (i=0;i<12;i++) { if (i==2) mame_printf_debug("\nstart: "); if (i==4) mame_printf_debug("\nend : "); if (i==6) mame_printf_debug("\n"); mame_printf_debug("%04x ",rom[(0x1b0/2)+i]); } mame_printf_debug("\n"); } /* if we have an SVP cpu then do some extra initilization for it */ if (_svp_cpu != NULL) { svp_init(machine); } } DRIVER_INIT( megadriv_c2 ) { genvdp_use_cram = 0; genesis_always_irq6 = 1; genesis_other_hacks = 0; megadriv_init_common(machine); hazemdchoice_megadrive_region_export = 1; hazemdchoice_megadrive_region_pal = 0; hazemdchoice_megadriv_framerate = 60; } DRIVER_INIT( megadriv ) { genvdp_use_cram = 1; genesis_always_irq6 = 0; genesis_other_hacks = 1; megadriv_init_common(machine); hazemdchoice_megadrive_region_export = 1; hazemdchoice_megadrive_region_pal = 0; hazemdchoice_megadriv_framerate = 60; } DRIVER_INIT( megadrij ) { genvdp_use_cram = 1; genesis_always_irq6 = 0; genesis_other_hacks = 1; megadriv_init_common(machine); hazemdchoice_megadrive_region_export = 0; hazemdchoice_megadrive_region_pal = 0; hazemdchoice_megadriv_framerate = 60; } DRIVER_INIT( megadrie ) { genvdp_use_cram = 1; genesis_always_irq6 = 0; genesis_other_hacks = 1; megadriv_init_common(machine); hazemdchoice_megadrive_region_export = 1; hazemdchoice_megadrive_region_pal = 1; hazemdchoice_megadriv_framerate = 50; } DRIVER_INIT( mpnew ) { DRIVER_INIT_CALL(megadrij); megadrive_io_read_data_port_ptr = megadrive_io_read_data_port_3button; megadrive_io_write_data_port_ptr = megadrive_io_write_data_port_3button; } /* used by megatech */ static READ8_HANDLER( z80_unmapped_port_r ) { // printf("unmapped z80 port read %04x\n",offset); return 0; } static WRITE8_HANDLER( z80_unmapped_port_w ) { // printf("unmapped z80 port write %04x\n",offset); } static READ8_HANDLER( z80_unmapped_r ) { printf("unmapped z80 read %04x\n",offset); return 0; } static WRITE8_HANDLER( z80_unmapped_w ) { printf("unmapped z80 write %04x\n",offset); } /* sets the megadrive z80 to it's normal ports / map */ void megatech_set_megadrive_z80_as_megadrive_z80(running_machine *machine, const char* tag) { running_device *ym = machine->device("ymsnd"); /* INIT THE PORTS *********************************************************************************************/ memory_install_readwrite8_handler(cputag_get_address_space(machine, tag, ADDRESS_SPACE_IO), 0x0000, 0xffff, 0, 0, z80_unmapped_port_r, z80_unmapped_port_w); /* catch any addresses that don't get mapped */ memory_install_readwrite8_handler(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x0000, 0xffff, 0, 0, z80_unmapped_r, z80_unmapped_w); memory_install_readwrite_bank(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x0000, 0x1fff, 0, 0, "bank1"); memory_set_bankptr(machine, "bank1", genz80.z80_prgram ); memory_install_ram(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x0000, 0x1fff, 0, 0, genz80.z80_prgram); // not allowed?? // memory_install_readwrite_bank(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x2000, 0x3fff, 0, 0, "bank1"); memory_install_readwrite8_device_handler(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), ym, 0x4000, 0x4003, 0, 0, ym2612_r, ym2612_w); memory_install_write8_handler (cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x6000, 0x6000, 0, 0, megadriv_z80_z80_bank_w); memory_install_write8_handler (cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x6001, 0x6001, 0, 0, megadriv_z80_z80_bank_w); memory_install_read8_handler (cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x6100, 0x7eff, 0, 0, megadriv_z80_unmapped_read); memory_install_readwrite8_handler(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x7f00, 0x7fff, 0, 0, megadriv_z80_vdp_read, megadriv_z80_vdp_write); memory_install_readwrite8_handler(cputag_get_address_space(machine, tag, ADDRESS_SPACE_PROGRAM), 0x8000, 0xffff, 0, 0, z80_read_68k_banked_data, z80_write_68k_banked_data); } // these are tests for 'special case' hardware to make sure I don't break anything while rearranging things // DRIVER_INIT( _32x ) { _32x_dram0 = auto_alloc_array(machine, UINT16, 0x40000/2); _32x_dram1 = auto_alloc_array(machine, UINT16, 0x40000/2); memset(_32x_dram0, 0x00, 0x40000); memset(_32x_dram1, 0x00, 0x40000); _32x_palette_lookup = auto_alloc_array(machine, UINT16, 0x200/2); _32x_palette = auto_alloc_array(machine, UINT16, 0x200/2); memset(_32x_palette_lookup, 0x00, 0x200); memset(_32x_palette, 0x00, 0x200); _32x_display_dram = _32x_dram0; _32x_access_dram = _32x_dram1; _32x_adapter_enabled = 0; if (_32x_adapter_enabled == 0) { memory_install_rom(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x0000000, 0x03fffff, 0, 0, memory_region(machine, "gamecart")); memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x000070, 0x000073, 0, 0, _32x_68k_hint_vector_r, _32x_68k_hint_vector_w); // h interrupt vector }; a15100_reg = 0x0000; memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15100, 0xa15101, 0, 0, _32x_68k_a15100_r, _32x_68k_a15100_w); // framebuffer control regs memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15102, 0xa15103, 0, 0, _32x_68k_a15102_r, _32x_68k_a15102_w); // send irq to sh2 memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15104, 0xa15105, 0, 0, _32x_68k_a15104_r, _32x_68k_a15104_w); // 68k BANK rom set memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15106, 0xa15107, 0, 0, _32x_68k_a15106_r, _32x_68k_a15106_w); // dreq stuff memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15108, 0xa15113, 0, 0, _32x_dreq_common_r, _32x_dreq_common_w); // dreq src / dst / length /fifo memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa1511a, 0xa1511b, 0, 0, _32x_68k_a1511a_r, _32x_68k_a1511a_w); // SEGA TV memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15120, 0xa1512f, 0, 0, _32x_68k_commsram_r, _32x_68k_commsram_w); // comms reg 0-7 memory_install_readwrite16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0xa15130, 0xa1513f, 0, 0, _32x_pwm_r, _32x_68k_pwm_w); memory_install_read16_handler(cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM), 0x0a130ec, 0x0a130ef, 0, 0, _32x_68k_MARS_r); // system ID /* Interrupts are masked / disabled at first */ sh2_master_vint_enable = sh2_slave_vint_enable = 0; sh2_master_hint_enable = sh2_slave_hint_enable = 0; sh2_master_cmdint_enable = sh2_slave_cmdint_enable = 0; sh2_master_pwmint_enable = sh2_slave_pwmint_enable = 0; sh2_master_vint_pending = sh2_slave_vint_pending = 0; // start in a reset state sh2_are_running = 0; _32x_a1518a_reg = 0x00; // inital value _32x_68k_a15104_reg = 0x00; _32x_autofill_length = 0; _32x_autofill_address = 0; _32x_autofill_data = 0; _32x_screenshift = 0; _32x_videopriority = 0; // MD priority _32x_displaymode = 0; _32x_240mode = 0; // checking if these help brutal, they don't. sh2drc_set_options(machine->device("32x_master_sh2"), SH2DRC_COMPATIBLE_OPTIONS); sh2drc_set_options(machine->device("32x_slave_sh2"), SH2DRC_COMPATIBLE_OPTIONS); DRIVER_INIT_CALL(megadriv); } #if 1 ROM_START( 32x_bios ) ROM_REGION16_BE( 0x400000, "gamecart", ROMREGION_ERASEFF ) /* 68000 Code */ ROM_REGION32_BE( 0x400000, "gamecart_sh2", 0 ) /* Copy for the SH2 */ ROM_COPY( "gamecart", 0x0, 0x0, 0x400000) ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */ // ROM_COPY( "gamecart", 0x0, 0x0, 0x400000) ROM_LOAD( "32x_g_bios.bin", 0x000000, 0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) ) ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 ) // temp, rom should only be visible here when one of the regs is set, tempo needs it ROM_COPY( "32x_68k_bios", 0x0, 0x0, 0x100) ROM_REGION( 0x400000, "32x_master_sh2", 0 ) /* SH2 Code */ ROM_SYSTEM_BIOS( 0, "retail", "Mars Version 1.0 (retail)" ) ROMX_LOAD( "32x_m_bios.bin", 0x000000, 0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e), ROM_BIOS(1) ) ROM_SYSTEM_BIOS( 1, "sdk", "Mars Version 1.0 (early sdk)" ) ROMX_LOAD( "32x_m_bios_sdk.bin", 0x000000, 0x000800, BAD_DUMP CRC(c7102c53) SHA1(ed73a47f186b373b8eff765f84ef26c3d9ef6cb0), ROM_BIOS(2) ) ROM_REGION( 0x400000, "32x_slave_sh2", 0 ) /* SH2 Code */ ROM_LOAD( "32x_s_bios.bin", 0x000000, 0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) ) ROM_END ROM_START( segacd ) ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 ) ROM_LOAD( "segacd_model2_bios_2_11_u.bin", 0x000000, 0x020000, CRC(2e49d72c) SHA1(328a3228c29fba244b9db2055adc1ec4f7a87e6b) ) ROM_END /* some games use the 32x and SegaCD together to give better quality FMV */ ROM_START( 32x_scd ) ROM_REGION16_BE( 0x400000, "maincpu", ROMREGION_ERASE00 ) ROM_REGION16_BE( 0x400000, "gamecart", 0 ) /* 68000 Code */ ROM_LOAD( "segacd_model2_bios_2_11_u.bin", 0x000000, 0x020000, CRC(2e49d72c) SHA1(328a3228c29fba244b9db2055adc1ec4f7a87e6b) ) ROM_REGION16_BE( 0x400000, "32x_68k_bios", 0 ) /* 68000 Code */ ROM_LOAD( "32x_g_bios.bin", 0x000000, 0x000100, CRC(5c12eae8) SHA1(dbebd76a448447cb6e524ac3cb0fd19fc065d944) ) ROM_REGION( 0x400000, "32x_master_sh2", 0 ) /* SH2 Code */ ROM_LOAD( "32x_m_bios.bin", 0x000000, 0x000800, CRC(dd9c46b8) SHA1(1e5b0b2441a4979b6966d942b20cc76c413b8c5e) ) ROM_REGION( 0x400000, "32x_slave_sh2", 0 ) /* SH2 Code */ ROM_LOAD( "32x_s_bios.bin", 0x000000, 0x000400, CRC(bfda1fe5) SHA1(4103668c1bbd66c5e24558e73d4f3f92061a109a) ) ROM_END ROM_START( g_virr ) ROM_REGION( 0x400000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD( "g_virr.bin", 0x000000, 0x200000, CRC(7e1a324a) SHA1(ff969ae53120cc4e7cb1a8a7e47458f2eb8a2165) ) ROM_END ROM_START( g_virrj ) ROM_REGION( 0x400000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD( "g_virrj.bin", 0x000000, 0x200000, CRC(53a293b5) SHA1(0ad38a3ab1cc99edac72184f8ae420e13df5cac6) ) ROM_END ROM_START( g_virre ) ROM_REGION( 0x400000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD( "g_virre.bin", 0x000000, 0x200000, CRC(9624d4ef) SHA1(2c3812f8a010571e51269a33a989598787d27c2d) ) ROM_END ROM_START( g_virrea ) ROM_REGION( 0x400000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD( "g_virrea.bin", 0x000000, 0x200000, CRC(5a943df9) SHA1(2c08ea556c79d48e88ff5202944c161ae1b41c63) ) ROM_END #ifndef MESS GAME( 1994, 32x_bios, 0, genesis_32x, megadriv, _32x, ROT0, "Sega", "32X Bios", GAME_NOT_WORKING ) GAME( 1994, segacd, 0, genesis_scd, megadriv, megadriv,ROT0, "Sega", "Sega-CD Model 2 BIOS V2.11 (U)", GAME_NOT_WORKING ) GAME( 1994, 32x_scd, 0, genesis_32x_scd, megadriv, _32x, ROT0, "Sega", "Sega-CD Model 2 BIOS V2.11 (U) (with 32X)", GAME_NOT_WORKING ) GAME( 1994, g_virr, 0, megdsvp, megadriv, megadriv, ROT0, "Sega", "Virtua Racing (U) [!]", 0 ) GAME( 1994, g_virrj, g_virr, megdsvp, megadriv, megadrij, ROT0, "Sega", "Virtua Racing (J) [!]", 0 ) GAME( 1994, g_virre, g_virr, megdsvppal, megadriv, megadrie, ROT0, "Sega", "Virtua Racing (E) [!]", 0 ) GAME( 1994, g_virrea, g_virr, megdsvppal, megadriv, megadrie, ROT0, "Sega", "Virtua Racing (E) [a1]", 0 ) #endif #endif