/***************************************************************************
Sega 16-bit common hardware
***************************************************************************/
#include "driver.h"
#include "segaic16.h"
#include "video/resnet.h"
extern void *fd1089_get_decrypted_base(void);
extern void fd1094_machine_init(void);
extern void *fd1094_get_decrypted_base(void);
/*************************************
*
* Debugging
*
*************************************/
#define LOG_MEMORY_MAP (1)
#define LOG_MULTIPLY (0)
#define LOG_DIVIDE (0)
#define LOG_COMPARE (0)
/*************************************
*
* Types
*
*************************************/
struct memory_mapper_chip
{
UINT8 regs[0x20];
int cpunum;
const struct segaic16_memory_map_entry *map;
void (*sound_w)(UINT8);
UINT8 (*sound_r)(void);
};
struct multiply_chip
{
UINT16 regs[4];
};
struct divide_chip
{
UINT16 regs[8];
};
struct compare_timer_chip
{
UINT16 regs[16];
UINT16 counter;
UINT8 bit;
void (*sound_w)(UINT8);
void (*timer_ack)(void);
};
/*************************************
*
* Statics
*
*************************************/
static struct memory_mapper_chip memory_mapper;
static struct multiply_chip multiply[3];
static struct divide_chip divide[3];
static struct compare_timer_chip compare_timer[2];
/*************************************
*
* Prototypes
*
*************************************/
static void update_memory_mapping(struct memory_mapper_chip *chip);
/*************************************
*
* Misc helpers
*
*************************************/
READ16_HANDLER( segaic16_open_bus_r )
{
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 = program_read_word_16be(activecpu_get_pc());
recurse = 0;
return result;
}
/*************************************
*
* Memory mapping chip
*
*************************************/
void segaic16_memory_mapper_init(int cpunum, const struct segaic16_memory_map_entry *entrylist, void (*sound_w_callback)(UINT8), UINT8 (*sound_r_callback)(void))
{
struct memory_mapper_chip *chip = &memory_mapper;
/* reset the chip structure */
memset(chip, 0, sizeof(*chip));
chip->cpunum = cpunum;
chip->map = entrylist;
chip->sound_w = sound_w_callback;
chip->sound_r = sound_r_callback;
/* create the initial regions */
segaic16_memory_mapper_reset();
}
void segaic16_memory_mapper_reset(void)
{
struct memory_mapper_chip *chip = &memory_mapper;
/* zap to 0 and remap everything */
memset(chip->regs, 0, sizeof(chip->regs));
update_memory_mapping(chip);
}
void segaic16_memory_mapper_config(const UINT8 *map_data)
{
struct memory_mapper_chip *chip = &memory_mapper;
/* zap to 0 and remap everything */
memcpy(&chip->regs[0x10], map_data, 0x10);
update_memory_mapping(chip);
}
void segaic16_memory_mapper_set_decrypted(UINT8 *decrypted)
{
struct memory_mapper_chip *chip = &memory_mapper;
offs_t romsize = memory_region_length(REGION_CPU1 + chip->cpunum);
int rgnum;
/* loop over the regions */
for (rgnum = 0; chip->map[rgnum].regbase != 0; rgnum++)
{
static const offs_t region_size_map[4] = { 0x00ffff, 0x01ffff, 0x07ffff, 0x1fffff };
const struct segaic16_memory_map_entry *rgn = &chip->map[rgnum];
offs_t region_size = region_size_map[chip->regs[rgn->regbase] & 3];
offs_t region_base = (chip->regs[rgn->regbase + 1] << 16) & ~region_size;
offs_t region_start = region_base + (rgn->regoffs & region_size);
read16_handler read = rgn->read;
int banknum = 0;
/* skip non-ROM regions */
if (read == NULL || rgn->romoffset == ~0)
continue;
/* check for mapping to banks */
if ((FPTR)read >= STATIC_BANK1 && (FPTR)read <= STATIC_BANKMAX)
banknum = ((FPTR)read - STATIC_BANK1) + 1;
/* skip any mappings beyond the ROM size */
if (region_start >= romsize)
continue;
memory_configure_bank_decrypted(banknum, 0, 1, decrypted + region_start, 0);
memory_set_bank(banknum, 0);
}
}
static void memory_mapper_w(struct memory_mapper_chip *chip, offs_t offset, UINT8 data)
{
UINT8 oldval;
/* wraps every 32 bytes */
offset &= 0x1f;
/* remember the previous value and swap in the new one */
oldval = chip->regs[offset];
chip->regs[offset] = data;
/* switch off the offset */
switch (offset)
{
case 0x02:
/* misc commands */
/* 00 - resume execution after 03 */
/* 03 - maybe controls halt and reset lines together? */
if ((oldval ^ chip->regs[offset]) & 3)
{
cpunum_set_input_line(chip->cpunum, INPUT_LINE_RESET, (chip->regs[offset] & 3) == 3 ? ASSERT_LINE : CLEAR_LINE);
if ((chip->regs[offset] & 3) == 3)
fd1094_machine_init();
}
break;
case 0x03:
if (chip->sound_w)
(*chip->sound_w)(data);
break;
case 0x04:
/* controls IRQ lines to 68000, negative logic -- write $B to signal IRQ4 */
if ((chip->regs[offset] & 7) != 7)
cpunum_set_input_line(chip->cpunum, (~chip->regs[offset] & 7), HOLD_LINE);
break;
case 0x05:
/* read/write control */
/* 01 - write data latched in 00,01 to 2 * (address in 0A,0B,0C) */
/* 02 - read data into latches 00,01 from 2 * (address in 07,08,09) */
if (data == 0x01)
{
offs_t addr = (chip->regs[0x0a] << 17) | (chip->regs[0x0b] << 9) | (chip->regs[0x0c] << 1);
cpuintrf_push_context(chip->cpunum);
program_write_word_16be(addr, (chip->regs[0x00] << 8) | chip->regs[0x01]);
cpuintrf_pop_context();
}
else if (data == 0x02)
{
offs_t addr = (chip->regs[0x07] << 17) | (chip->regs[0x08] << 9) | (chip->regs[0x09] << 1);
UINT16 result;
cpuintrf_push_context(chip->cpunum);
result = program_read_word_16be(addr);
cpuintrf_pop_context();
chip->regs[0x00] = result >> 8;
chip->regs[0x01] = result;
}
break;
case 0x07: case 0x08: case 0x09:
/* writes here latch a 68000 address for writing */
break;
case 0x0a: case 0x0b: case 0x0c:
/* writes here latch a 68000 address for reading */
break;
case 0x10: case 0x11:
case 0x12: case 0x13:
case 0x14: case 0x15:
case 0x16: case 0x17:
case 0x18: case 0x19:
case 0x1a: case 0x1b:
case 0x1c: case 0x1d:
case 0x1e: case 0x1f:
if (oldval != data)
update_memory_mapping(chip);
break;
default:
logerror("Unknown memory_mapper_w to address %02X = %02X\n", offset, data);
break;
}
}
static UINT16 memory_mapper_r(struct memory_mapper_chip *chip, offs_t offset, UINT16 unmapped_val)
{
/* wraps every 32 bytes */
offset &= 0x1f;
/* switch off the offset */
switch (offset)
{
case 0x00:
case 0x01:
/* data latches - return the values latched */
return chip->regs[offset];
case 0x02:
/* various input bits from the 68000 */
/* 01 - ???? */
/* 02 - ???? */
/* 04 - ???? */
/* 08 - ???? */
/* 40 - set if busy processing a read/write request */
/* Together, 01+02 == 00 if the 68000 is halted */
/* Together, 01+02+04+08 == 0F if the 68000 is executing */
return (chip->regs[0x02] & 3) == 3 ? 0x00 : 0x0f;
case 0x03:
/* this returns data that the sound CPU writes */
if (chip->sound_r)
return (*chip->sound_r)();
return 0xff;
default:
logerror("Unknown memory_mapper_r from address %02X\n", offset);
break;
}
return unmapped_val;
}
static void update_memory_mapping(struct memory_mapper_chip *chip)
{
int rgnum;
if (LOG_MEMORY_MAP) mame_printf_debug("----\nRemapping:\n");
/* first reset everything back to the beginning */
memory_install_read16_handler (chip->cpunum, ADDRESS_SPACE_PROGRAM, 0x000000, 0xffffff, 0, 0, segaic16_memory_mapper_lsb_r);
memory_install_write16_handler(chip->cpunum, ADDRESS_SPACE_PROGRAM, 0x000000, 0xffffff, 0, 0, segaic16_memory_mapper_lsb_w);
/* loop over the regions */
for (rgnum = 0; chip->map[rgnum].regbase != 0; rgnum++)
{
static const offs_t region_size_map[4] = { 0x00ffff, 0x01ffff, 0x07ffff, 0x1fffff };
const struct segaic16_memory_map_entry *rgn = &chip->map[rgnum];
offs_t region_size = region_size_map[chip->regs[rgn->regbase] & 3];
offs_t region_base = (chip->regs[rgn->regbase + 1] << 16) & ~region_size;
offs_t region_mirror = rgn->mirror & region_size;
offs_t region_start = region_base + (rgn->regoffs & region_size);
offs_t region_end = region_start + ((rgn->length - 1 < region_size) ? rgn->length - 1 : region_size);
write16_handler write = rgn->write;
read16_handler read = rgn->read;
int banknum = 0;
/* check for mapping to banks */
if ((FPTR)read >= STATIC_BANK1 && (FPTR)read <= STATIC_BANKMAX)
banknum = ((FPTR)read - STATIC_BANK1) + 1;
if ((FPTR)write >= STATIC_BANK1 && (FPTR)write <= STATIC_BANKMAX)
banknum = ((FPTR)write - STATIC_BANK1) + 1;
/* ROM areas need extra clamping */
if (rgn->romoffset != ~0)
{
offs_t romsize = memory_region_length(REGION_CPU1 + chip->cpunum);
if (region_start >= romsize)
read = NULL;
else if (region_start + rgn->length > romsize)
region_end = romsize - 1;
}
/* map it */
if (read)
memory_install_read16_handler(chip->cpunum, ADDRESS_SPACE_PROGRAM, region_start, region_end, 0, region_mirror, read);
if (write)
memory_install_write16_handler(chip->cpunum, ADDRESS_SPACE_PROGRAM, region_start, region_end, 0, region_mirror, write);
/* set the bank pointer */
if (banknum && read)
{
if (rgn->base)
{
memory_configure_bank(banknum, 0, 1, *rgn->base, 0);
memory_set_bank(banknum, 0);
}
else if (rgn->romoffset != ~0)
{
UINT8 *decrypted;
decrypted = fd1094_get_decrypted_base();
if (!decrypted)
decrypted = fd1089_get_decrypted_base();
memory_configure_bank(banknum, 0, 1, memory_region(REGION_CPU1 + chip->cpunum) + region_start, 0);
if (decrypted)
memory_configure_bank_decrypted(banknum, 0, 1, decrypted ? (decrypted + region_start) : 0, 0);
memory_set_bank(banknum, 0);
}
}
if (LOG_MEMORY_MAP) mame_printf_debug(" %06X-%06X (%06X) = %s\n", region_start, region_end, region_mirror, rgn->name);
}
}
READ8_HANDLER( segaic16_memory_mapper_r )
{
return memory_mapper_r(&memory_mapper, offset, 0xff);
}
WRITE8_HANDLER( segaic16_memory_mapper_w )
{
memory_mapper_w(&memory_mapper, offset, data);
}
READ16_HANDLER( segaic16_memory_mapper_lsb_r )
{
return memory_mapper_r(&memory_mapper, offset, segaic16_open_bus_r(0,0));
}
WRITE16_HANDLER( segaic16_memory_mapper_lsb_w )
{
if (ACCESSING_LSB)
memory_mapper_w(&memory_mapper, offset, data & 0xff);
}
/*************************************
*
* Multiply chip
*
*************************************/
static UINT16 multiply_r(int which, offs_t offset, UINT16 mem_mask)
{
offset &= 3;
switch (offset)
{
case 0: return multiply[which].regs[0];
case 1: return multiply[which].regs[1];
case 2: return ((INT16)multiply[which].regs[0] * (INT16)multiply[which].regs[1]) >> 16;
case 3: return ((INT16)multiply[which].regs[0] * (INT16)multiply[which].regs[1]) & 0xffff;
}
return 0xffff;
}
static void multiply_w(int which, offs_t offset, UINT16 data, UINT16 mem_mask)
{
offset &= 3;
switch (offset)
{
case 0: COMBINE_DATA(&multiply[which].regs[0]); break;
case 1: COMBINE_DATA(&multiply[which].regs[1]); break;
case 2: COMBINE_DATA(&multiply[which].regs[0]); break;
case 3: COMBINE_DATA(&multiply[which].regs[1]); break;
}
}
READ16_HANDLER( segaic16_multiply_0_r ) { return multiply_r(0, offset, mem_mask); }
READ16_HANDLER( segaic16_multiply_1_r ) { return multiply_r(1, offset, mem_mask); }
READ16_HANDLER( segaic16_multiply_2_r ) { return multiply_r(2, offset, mem_mask); }
WRITE16_HANDLER( segaic16_multiply_0_w ) { multiply_w(0, offset, data, mem_mask); }
WRITE16_HANDLER( segaic16_multiply_1_w ) { multiply_w(1, offset, data, mem_mask); }
WRITE16_HANDLER( segaic16_multiply_2_w ) { multiply_w(2, offset, data, mem_mask); }
/*************************************
*
* Divide chip
*
*************************************/
static void update_divide(int which, int mode)
{
/* clear the flags by default */
divide[which].regs[6] = 0;
/* if mode 0, store quotient/remainder */
if (mode == 0)
{
INT32 dividend = (INT32)((divide[which].regs[0] << 16) | divide[which].regs[1]);
INT32 divisor = (INT16)divide[which].regs[2];
INT32 quotient, remainder;
/* perform signed divide */
if (divisor == 0)
{
quotient = dividend;//((INT32)(dividend ^ divisor) < 0) ? 0x8000 : 0x7fff;
divide[which].regs[6] |= 0x4000;
}
else
quotient = dividend / divisor;
remainder = dividend - quotient * divisor;
/* clamp to 16-bit signed */
if (quotient < -32768)
{
quotient = -32768;
divide[which].regs[6] |= 0x8000;
}
else if (quotient > 32767)
{
quotient = 32767;
divide[which].regs[6] |= 0x8000;
}
/* store quotient and remainder */
divide[which].regs[4] = quotient;
divide[which].regs[5] = remainder;
}
/* if mode 1, store 32-bit quotient */
else
{
UINT32 dividend = (UINT32)((divide[which].regs[0] << 16) | divide[which].regs[1]);
UINT32 divisor = (UINT16)divide[which].regs[2];
UINT32 quotient;
/* perform unsigned divide */
if (divisor == 0)
{
quotient = dividend;//0x7fffffff;
divide[which].regs[6] |= 0x4000;
}
else
quotient = dividend / divisor;
/* store 32-bit quotient */
divide[which].regs[4] = quotient >> 16;
divide[which].regs[5] = quotient & 0xffff;
}
}
static UINT16 divide_r(int which, offs_t offset, UINT16 mem_mask)
{
/* 8 effective read registers */
offset &= 7;
switch (offset)
{
case 0: return divide[which].regs[0]; /* dividend high */
case 1: return divide[which].regs[1]; /* dividend low */
case 2: return divide[which].regs[2]; /* divisor */
case 4: return divide[which].regs[4]; /* quotient (mode 0) or quotient high (mode 1) */
case 5: return divide[which].regs[5]; /* remainder (mode 0) or quotient low (mode 1) */
case 6: return divide[which].regs[6]; /* flags */
}
return 0xffff;
}
static void divide_w(int which, offs_t offset, UINT16 data, UINT16 mem_mask)
{
int a4 = offset & 8;
int a3 = offset & 4;
if (LOG_DIVIDE) logerror("%06X:divide%d_w(%X) = %04X\n", activecpu_get_pc(), which, offset, data);
/* only 4 effective write registers */
offset &= 3;
switch (offset)
{
case 0: COMBINE_DATA(÷[which].regs[0]); break; /* dividend high */
case 1: COMBINE_DATA(÷[which].regs[1]); break; /* dividend low */
case 2: COMBINE_DATA(÷[which].regs[2]); break; /* divisor/trigger */
case 3: break;
}
/* if a4 line is high, divide, using a3 as the mode */
if (a4) update_divide(which, a3);
}
READ16_HANDLER( segaic16_divide_0_r ) { return divide_r(0, offset, mem_mask); }
READ16_HANDLER( segaic16_divide_1_r ) { return divide_r(1, offset, mem_mask); }
READ16_HANDLER( segaic16_divide_2_r ) { return divide_r(2, offset, mem_mask); }
WRITE16_HANDLER( segaic16_divide_0_w ) { divide_w(0, offset, data, mem_mask); }
WRITE16_HANDLER( segaic16_divide_1_w ) { divide_w(1, offset, data, mem_mask); }
WRITE16_HANDLER( segaic16_divide_2_w ) { divide_w(2, offset, data, mem_mask); }
/*************************************
*
* Compare/timer chip
*
*************************************/
void segaic16_compare_timer_init(int which, void (*sound_write_callback)(UINT8), void (*timer_ack_callback)(void))
{
compare_timer[which].sound_w = sound_write_callback;
compare_timer[which].timer_ack = timer_ack_callback;
compare_timer[which].counter = 0;
}
int segaic16_compare_timer_clock(int which)
{
int old_counter = compare_timer[which].counter;
int result = 0;
/* if we're enabled, clock the upcounter */
if (compare_timer[which].regs[10] & 1)
compare_timer[which].counter++;
/* regardless of the enable, a value of 0xfff will generate the IRQ */
if (old_counter == 0xfff)
{
result = 1;
compare_timer[which].counter = compare_timer[which].regs[8] & 0xfff;
}
return result;
}
static void update_compare(int which, int update_history)
{
int bound1 = (INT16)compare_timer[which].regs[0];
int bound2 = (INT16)compare_timer[which].regs[1];
int value = (INT16)compare_timer[which].regs[2];
int min = (bound1 < bound2) ? bound1 : bound2;
int max = (bound1 > bound2) ? bound1 : bound2;
if (value < min)
{
compare_timer[which].regs[7] = min;
compare_timer[which].regs[3] = 0x8000;
}
else if (value > max)
{
compare_timer[which].regs[7] = max;
compare_timer[which].regs[3] = 0x4000;
}
else
{
compare_timer[which].regs[7] = value;
compare_timer[which].regs[3] = 0x0000;
}
if (update_history)
compare_timer[which].regs[4] |= (compare_timer[which].regs[3] == 0) << compare_timer[which].bit++;
}
static void timer_interrupt_ack(int which)
{
if (compare_timer[which].timer_ack)
(*compare_timer[which].timer_ack)();
}
static UINT16 compare_timer_r(int which, offs_t offset, UINT16 mem_mask)
{
offset &= 0xf;
if (LOG_COMPARE) logerror("%06X:compare%d_r(%X) = %04X\n", activecpu_get_pc(), which, offset, compare_timer[which].regs[offset]);
switch (offset)
{
case 0x0: return compare_timer[which].regs[0];
case 0x1: return compare_timer[which].regs[1];
case 0x2: return compare_timer[which].regs[2];
case 0x3: return compare_timer[which].regs[3];
case 0x4: return compare_timer[which].regs[4];
case 0x5: return compare_timer[which].regs[1];
case 0x6: return compare_timer[which].regs[2];
case 0x7: return compare_timer[which].regs[7];
case 0x9:
case 0xd: timer_interrupt_ack(which); break;
}
return 0xffff;
}
static void compare_timer_w(int which, offs_t offset, UINT16 data, UINT16 mem_mask)
{
offset &= 0xf;
if (LOG_COMPARE) logerror("%06X:compare%d_w(%X) = %04X\n", activecpu_get_pc(), which, offset, data);
switch (offset)
{
case 0x0: COMBINE_DATA(&compare_timer[which].regs[0]); update_compare(which, 0); break;
case 0x1: COMBINE_DATA(&compare_timer[which].regs[1]); update_compare(which, 0); break;
case 0x2: COMBINE_DATA(&compare_timer[which].regs[2]); update_compare(which, 1); break;
case 0x4: compare_timer[which].regs[4] = 0; compare_timer[which].bit = 0; break;
case 0x6: COMBINE_DATA(&compare_timer[which].regs[2]); update_compare(which, 0); break;
case 0x8:
case 0xc: COMBINE_DATA(&compare_timer[which].regs[8]); break;
case 0x9:
case 0xd: timer_interrupt_ack(which); break;
case 0xa:
case 0xe: COMBINE_DATA(&compare_timer[which].regs[10]); break;
case 0xb:
case 0xf:
COMBINE_DATA(&compare_timer[which].regs[11]);
if (compare_timer[which].sound_w)
(*compare_timer[which].sound_w)(compare_timer[which].regs[11]);
break;
}
}
READ16_HANDLER( segaic16_compare_timer_0_r ) { return compare_timer_r(0, offset, mem_mask); }
READ16_HANDLER( segaic16_compare_timer_1_r ) { return compare_timer_r(1, offset, mem_mask); }
WRITE16_HANDLER( segaic16_compare_timer_0_w ) { compare_timer_w(0, offset, data, mem_mask); }
WRITE16_HANDLER( segaic16_compare_timer_1_w ) { compare_timer_w(1, offset, data, mem_mask); }