/***************************************************************************
Amiga Computer / Arcadia Game System
Driver by:
Aaron Giles, Ernesto Corvi & Mariusz Wojcieszek
***************************************************************************/
#include "driver.h"
#include "includes/amiga.h"
#include "cpu/m68000/m68000.h"
#include "machine/6526cia.h"
/*************************************
*
* Debugging
*
*************************************/
#define LOG_CUSTOM 0
#define LOG_CIA 0
#define LOG_BLITS 0
/*************************************
*
* Constants
*
*************************************/
/* 715909 Hz for NTSC, 709379 for PAL */
#define O2_CLOCK (Machine->drv->cpu[0].clock / 10)
/* How many CPU cycles we delay until we fire a pending interrupt */
#define AMIGA_IRQ_DELAY_CYCLES 24
/* How many CPU cycles we wait until we process a blit when the blitter-nasty bit is set */
#define BLITTER_NASTY_DELAY 16
/*************************************
*
* Type definitions
*
*************************************/
typedef struct _autoconfig_device autoconfig_device;
struct _autoconfig_device
{
autoconfig_device * next;
amiga_autoconfig_device device;
offs_t base;
};
/*************************************
*
* Globals
*
*************************************/
UINT16 *amiga_chip_ram;
UINT32 *amiga_chip_ram32;
size_t amiga_chip_ram_size;
UINT16 *amiga_custom_regs;
UINT16 *amiga_expansion_ram;
UINT16 *amiga_autoconfig_mem;
static const amiga_machine_interface *amiga_intf;
static autoconfig_device *autoconfig_list;
static autoconfig_device *cur_autoconfig;
static emu_timer * amiga_irq_timer;
static emu_timer * amiga_blitter_timer;
const char *amiga_custom_names[0x100] =
{
/* 0x000 */
"BLTDDAT", "DMACONR", "VPOSR", "VHPOSR",
"DSKDATR", "JOY0DAT", "JOY1DAT", "CLXDAT",
"ADKCONR", "POT0DAT", "POT1DAT", "POTGOR",
"SERDATR", "DSKBYTR", "INTENAR", "INTREQR",
/* 0x020 */
"DSKPTH", "DSKPTL", "DSKLEN", "DSKDAT",
"REFPTR", "VPOSW", "VHPOSW", "COPCON",
"SERDAT", "SERPER", "POTGO", "JOYTEST",
"STREQU", "STRVBL", "STRHOR", "STRLONG",
/* 0x040 */
"BLTCON0", "BLTCON1", "BLTAFWM", "BLTALWM",
"BLTCPTH", "BLTCPTL", "BLTBPTH", "BLTBPTL",
"BLTAPTH", "BLTAPTL", "BLTDPTH", "BLTDPTL",
"BLTSIZE", "BLTCON0L", "BLTSIZV", "BLTSIZH",
/* 0x060 */
"BLTCMOD", "BLTBMOD", "BLTAMOD", "BLTDMOD",
"UNK068", "UNK06A", "UNK06C", "UNK06E",
"BLTCDAT", "BLTBDAT", "BLTADAT", "UNK076",
"UNK078", "UNK07A", "UNK07C", "DSRSYNC",
/* 0x080 */
"COP1LCH", "COP1LCL", "COP2LCH", "COP2LCL",
"COPJMP1", "COPJMP2", "COPINS", "DIWSTRT",
"DIWSTOP", "DDFSTRT", "DDFSTOP", "DMACON",
"CLXCON", "INTENA", "INTREQ", "ADKCON",
/* 0x0A0 */
"AUD0LCH", "AUD0LCL", "AUD0LEN", "AUD0PER",
"AUD0VOL", "AUD0DAT", "UNK0AC", "UNK0AE",
"AUD1LCH", "AUD1LCL", "AUD1LEN", "AUD1PER",
"AUD1VOL", "AUD1DAT", "UNK0BC", "UNK0BE",
/* 0x0C0 */
"AUD2LCH", "AUD2LCL", "AUD2LEN", "AUD2PER",
"AUD2VOL", "AUD2DAT", "UNK0CC", "UNK0CE",
"AUD3LCH", "AUD3LCL", "AUD3LEN", "AUD3PER",
"AUD3VOL", "AUD3DAT", "UNK0DC", "UNK0DE",
/* 0x0E0 */
"BPL1PTH", "BPL1PTL", "BPL2PTH", "BPL2PTL",
"BPL3PTH", "BPL3PTL", "BPL4PTH", "BPL4PTL",
"BPL5PTH", "BPL5PTL", "BPL6PTH", "BPL6PTL",
"UNK0F8", "UNK0FA", "UNK0FC", "UNK0FE",
/* 0x100 */
"BPLCON0", "BPLCON1", "BPLCON2", "UNK106",
"BPL1MOD", "BPL2MOD", "UNK10C", "UNK10E",
"BPL1DAT", "BPL2DAT", "BPL3DAT", "BPL4DAT",
"BPL5DAT", "BPL6DAT", "UNK11C", "UNK11E",
/* 0x120 */
"SPR0PTH", "SPR0PTL", "SPR1PTH", "SPR1PTL",
"SPR2PTH", "SPR2PTL", "SPR3PTH", "SPR3PTL",
"SPR4PTH", "SPR4PTL", "SPR5PTH", "SPR5PTL",
"SPR6PTH", "SPR6PTL", "SPR7PTH", "SPR7PTL",
/* 0x140 */
"SPR0POS", "SPR0CTL", "SPR0DATA", "SPR0DATB",
"SPR1POS", "SPR1CTL", "SPR1DATA", "SPR1DATB",
"SPR2POS", "SPR2CTL", "SPR2DATA", "SPR2DATB",
"SPR3POS", "SPR3CTL", "SPR3DATA", "SPR3DATB",
/* 0x160 */
"SPR4POS", "SPR4CTL", "SPR4DATA", "SPR4DATB",
"SPR5POS", "SPR5CTL", "SPR5DATA", "SPR5DATB",
"SPR6POS", "SPR6CTL", "SPR6DATA", "SPR6DATB",
"SPR7POS", "SPR7CTL", "SPR7DATA", "SPR7DATB",
/* 0x180 */
"COLOR00", "COLOR01", "COLOR02", "COLOR03",
"COLOR04", "COLOR05", "COLOR06", "COLOR07",
"COLOR08", "COLOR09", "COLOR10", "COLOR11",
"COLOR12", "COLOR13", "COLOR14", "COLOR15",
/* 0x1A0 */
"COLOR16", "COLOR17", "COLOR18", "COLOR19",
"COLOR20", "COLOR21", "COLOR22", "COLOR23",
"COLOR24", "COLOR25", "COLOR26", "COLOR27",
"COLOR28", "COLOR29", "COLOR30", "COLOR31",
/* 0x1C0 */
"UNK1C0", "UNK1C2", "UNK1C4", "UNK1C6",
"UNK1C8", "UNK1CA", "UNK1CC", "UNK1CE",
"UNK1D0", "UNK1D2", "UNK1D4", "UNK1D6",
"UNK1D8", "UNK1DA", "UNK1DC", "UNK1DE",
/* 0x1E0 */
"UNK1E0", "UNK1E2", "UNK1E4", "UNK1E6",
"UNK1E8", "UNK1EA", "UNK1EC", "UNK1EE",
"UNK1F0", "UNK1F2", "UNK1F4", "UNK1F6",
"UNK1F8", "UNK1FA", "UNK1FC", "UNK1FE"
};
/*************************************
*
* Prototypes
*
*************************************/
static void custom_reset(void);
static void autoconfig_reset(void);
static void amiga_cia_0_irq(int state);
static void amiga_cia_1_irq(int state);
static TIMER_CALLBACK( amiga_irq_proc );
static TIMER_CALLBACK( amiga_blitter_proc );
/*************************************
*
* Chipmem 16/32 bit access
*
*************************************/
UINT16 (*amiga_chip_ram_r)(offs_t offset);
void (*amiga_chip_ram_w)(offs_t offset, UINT16 data);
static UINT16 amiga_chip_ram16_r(offs_t offset)
{
extern const amiga_machine_interface *amiga_intf;
offset &= amiga_intf->chip_ram_mask;
return (offset < amiga_chip_ram_size) ? amiga_chip_ram[offset/2] : 0xffff;
}
static UINT16 amiga_chip_ram32_r(offs_t offset)
{
extern const amiga_machine_interface *amiga_intf;
offset &= amiga_intf->chip_ram_mask;
if ( offset < amiga_chip_ram_size )
{
UINT32 dat = amiga_chip_ram32[offset / 4];
if ( offset & 2 )
return (dat & 0xffff);
return (dat >> 16);
}
return 0xffff;
}
static void amiga_chip_ram16_w(offs_t offset, UINT16 data)
{
extern const amiga_machine_interface *amiga_intf;
offset &= amiga_intf->chip_ram_mask;
if (offset < amiga_chip_ram_size)
amiga_chip_ram[offset/2] = data;
}
static void amiga_chip_ram32_w(offs_t offset, UINT16 data)
{
extern const amiga_machine_interface *amiga_intf;
offset &= amiga_intf->chip_ram_mask;
if ( offset < amiga_chip_ram_size )
{
UINT32 dat = amiga_chip_ram32[offset / 4];
if ( offset & 2 )
{
dat &= 0xffff0000;
dat |= data;
}
else
{
dat &= 0x0000ffff;
dat |= ((UINT32)data) << 16;
}
amiga_chip_ram32[offset / 4] = dat;
}
}
/*************************************
*
* Machine config/reset
*
*************************************/
void amiga_machine_config(const amiga_machine_interface *intf)
{
cia6526_interface cia_intf[2];
amiga_intf = intf;
/* setup chipmem handlers */
if ( IS_AGA(intf) )
{
amiga_chip_ram_r = amiga_chip_ram32_r;
amiga_chip_ram_w = amiga_chip_ram32_w;
}
else
{
amiga_chip_ram_r = amiga_chip_ram16_r;
amiga_chip_ram_w = amiga_chip_ram16_w;
}
/* set up CIA interfaces */
memset(&cia_intf, 0, sizeof(cia_intf));
cia_intf[0].type = CIA8520;
cia_intf[0].clock = O2_CLOCK;
cia_intf[0].tod_clock = 0;
cia_intf[0].irq_func = amiga_cia_0_irq;
cia_intf[0].port[0].read = intf->cia_0_portA_r;
cia_intf[0].port[0].write = intf->cia_0_portA_w;
cia_intf[0].port[1].read = intf->cia_0_portB_r;
cia_intf[0].port[1].write = intf->cia_0_portB_w;
cia_config(0, &cia_intf[0]);
cia_intf[1].type = CIA8520;
cia_intf[1].clock = O2_CLOCK;
cia_intf[1].tod_clock = 0;
cia_intf[1].irq_func = amiga_cia_1_irq;
cia_intf[1].port[0].read = intf->cia_1_portA_r;
cia_intf[1].port[0].write = intf->cia_1_portA_w;
cia_intf[1].port[1].read = intf->cia_1_portB_r;
cia_intf[1].port[1].write = intf->cia_1_portB_w;
cia_config(1, &cia_intf[1]);
/* setup the timers */
amiga_irq_timer = timer_alloc(amiga_irq_proc);
amiga_blitter_timer = timer_alloc(amiga_blitter_proc);
}
static void amiga_m68k_reset(void)
{
logerror("Executed RESET at PC=%06x\n", activecpu_get_pc());
/* Initialize the various chips */
cia_reset();
custom_reset();
autoconfig_reset();
/* set the overlay bit */
if ( IS_AGA(amiga_intf) )
{
program_write_byte( 0xbfa001, 1 );
}
else
{
amiga_cia_w(0x1001/2, 1, 0);
}
if (activecpu_get_pc() < 0x80000)
memory_set_opbase(0);
}
MACHINE_RESET( amiga )
{
/* set m68k reset function */
cpunum_set_info_fct(0, CPUINFO_PTR_M68K_RESET_CALLBACK, (genf *)amiga_m68k_reset);
/* Initialize the various chips */
cia_reset();
custom_reset();
autoconfig_reset();
/* set the overlay bit */
if ( IS_AGA(amiga_intf) )
{
program_write_byte( 0xbfa001, 1 );
}
else
{
amiga_cia_w(0x1001/2, 1, 0);
}
/* call the system-specific callback */
if (amiga_intf->reset_callback)
(*amiga_intf->reset_callback)();
}
/*************************************
*
* Per scanline callback
*
*************************************/
INTERRUPT_GEN( amiga_scanline_callback )
{
int scanline = Machine->screen[0].height - 1 - cpu_getiloops();
/* on the first scanline, we do some extra bookkeeping */
if (scanline == 0)
{
/* signal VBLANK IRQ */
amiga_custom_w(REG_INTREQ, 0x8000 | INTENA_VERTB, 0);
/* clock the first CIA TOD */
cia_clock_tod(0);
/* call the system-specific callback */
if (amiga_intf->scanline0_callback != NULL)
(*amiga_intf->scanline0_callback)();
}
/* on every scanline, clock the second CIA TOD */
cia_clock_tod(1);
/* render this scanline */
amiga_render_scanline(scanline);
/* force a sound update */
amiga_audio_update();
}
/*************************************
*
* Interrupt management
*
*************************************/
static void update_irqs(void)
{
int ints = CUSTOM_REG(REG_INTENA) & CUSTOM_REG(REG_INTREQ);
int irq = -1;
/* Master interrupt switch */
if (CUSTOM_REG(REG_INTENA) & 0x4000)
{
/* Serial transmit buffer empty, disk block finished, software interrupts */
if (ints & 0x0007)
irq = 1;
/* I/O ports and timer interrupts */
if (ints & 0x0008)
irq = 2;
/* Copper, VBLANK, blitter interrupts */
if (ints & 0x0070)
irq = 3;
/* Audio interrupts */
if (ints & 0x0780)
irq = 4;
/* Serial receive buffer full, disk sync match */
if (ints & 0x1800)
irq = 5;
/* External interrupts */
if (ints & 0x2000)
irq = 6;
}
/* set the highest IRQ line */
if (irq >= 0)
cpunum_set_input_line(0, irq, ASSERT_LINE);
else
cpunum_set_input_line(0, 7, CLEAR_LINE);
}
static TIMER_CALLBACK( amiga_irq_proc )
{
update_irqs();
timer_reset( amiga_irq_timer, attotime_never);
}
/*************************************
*
* Standard joystick conversion
*
*************************************/
UINT32 amiga_joystick_convert(void *param)
{
UINT8 bits = readinputportbytag(param);
int up = (bits >> 0) & 1;
int down = (bits >> 1) & 1;
int left = (bits >> 2) & 1;
int right = (bits >> 3) & 1;
if (left) up ^= 1;
if (right) down ^= 1;
return down | (right << 1) | (up << 8) | (left << 9);
}
/*************************************
*
* Ascending blitter variant
*
*************************************/
static UINT32 blit_ascending(void)
{
UINT32 shifta = (CUSTOM_REG(REG_BLTCON0) >> 12) & 0xf;
UINT32 shiftb = (CUSTOM_REG(REG_BLTCON1) >> 12) & 0xf;
UINT32 height = CUSTOM_REG(REG_BLTSIZV);
UINT32 width = CUSTOM_REG(REG_BLTSIZH);
UINT32 acca = 0, accb = 0;
UINT32 blitsum = 0;
UINT32 x, y;
/* iterate over the height */
for (y = 0; y < height; y++)
{
/* iterate over the width */
for (x = 0; x < width; x++)
{
UINT16 abc0, abc1, abc2, abc3;
UINT32 tempa, tempd = 0;
UINT32 b;
/* fetch data for A */
if (CUSTOM_REG(REG_BLTCON0) & 0x0800)
{
CUSTOM_REG(REG_BLTADAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTAPTH));
CUSTOM_REG_LONG(REG_BLTAPTH) += 2;
}
/* fetch data for B */
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
{
CUSTOM_REG(REG_BLTBDAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTBPTH));
CUSTOM_REG_LONG(REG_BLTBPTH) += 2;
}
/* fetch data for C */
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
{
CUSTOM_REG(REG_BLTCDAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTCPTH));
CUSTOM_REG_LONG(REG_BLTCPTH) += 2;
}
/* apply start/end masks to the A data */
tempa = CUSTOM_REG(REG_BLTADAT);
if (x == 0)
tempa &= CUSTOM_REG(REG_BLTAFWM);
if (x == width - 1)
tempa &= CUSTOM_REG(REG_BLTALWM);
/* update the B accumulator applying shifts */
acca = (acca << 16) | (tempa << (16 - shifta));
accb = (accb << 16) | (CUSTOM_REG(REG_BLTBDAT) << (16 - shiftb));
/* build up 4 16-bit words containing 4 pixels each in 0ABC bit order */
abc0 = ((acca >> 17) & 0x4444) | ((accb >> 18) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 3) & 0x1111);
abc1 = ((acca >> 16) & 0x4444) | ((accb >> 17) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 2) & 0x1111);
abc2 = ((acca >> 15) & 0x4444) | ((accb >> 16) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 1) & 0x1111);
abc3 = ((acca >> 14) & 0x4444) | ((accb >> 15) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 0) & 0x1111);
/* now loop over bits and compute the destination value */
for (b = 0; b < 4; b++)
{
UINT32 bit;
/* shift previous data up 4 bits */
tempd <<= 4;
/* lookup first bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc0 >> 12)) & 1;
abc0 <<= 4;
tempd |= bit << 3;
/* lookup second bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc1 >> 12)) & 1;
abc1 <<= 4;
tempd |= bit << 2;
/* lookup third bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc2 >> 12)) & 1;
abc2 <<= 4;
tempd |= bit << 1;
/* lookup fourth bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc3 >> 12)) & 1;
abc3 <<= 4;
tempd |= bit << 0;
}
/* accumulate the sum */
blitsum |= tempd;
/* write to the destination */
if (CUSTOM_REG(REG_BLTCON0) & 0x0100)
{
amiga_chip_ram_w(CUSTOM_REG_LONG(REG_BLTDPTH), tempd);
CUSTOM_REG_LONG(REG_BLTDPTH) += 2;
}
}
/* apply end of line modulos */
if (CUSTOM_REG(REG_BLTCON0) & 0x0800)
CUSTOM_REG_LONG(REG_BLTAPTH) += CUSTOM_REG_SIGNED(REG_BLTAMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
CUSTOM_REG_LONG(REG_BLTBPTH) += CUSTOM_REG_SIGNED(REG_BLTBMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
CUSTOM_REG_LONG(REG_BLTCPTH) += CUSTOM_REG_SIGNED(REG_BLTCMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0100)
CUSTOM_REG_LONG(REG_BLTDPTH) += CUSTOM_REG_SIGNED(REG_BLTDMOD) & ~1;
}
/* return the blit sum */
return blitsum;
}
/*************************************
*
* Descending blitter variant
*
*************************************/
static UINT32 blit_descending(void)
{
UINT32 fill_exclusive = (CUSTOM_REG(REG_BLTCON1) >> 4);
UINT32 fill_inclusive = (CUSTOM_REG(REG_BLTCON1) >> 3);
UINT32 shifta = (CUSTOM_REG(REG_BLTCON0) >> 12) & 0xf;
UINT32 shiftb = (CUSTOM_REG(REG_BLTCON1) >> 12) & 0xf;
UINT32 height = CUSTOM_REG(REG_BLTSIZV);
UINT32 width = CUSTOM_REG(REG_BLTSIZH);
UINT32 acca = 0, accb = 0;
UINT32 blitsum = 0;
UINT32 x, y;
/* iterate over the height */
for (y = 0; y < height; y++)
{
UINT32 fill_state = (CUSTOM_REG(REG_BLTCON1) >> 2) & 1;
/* iterate over the width */
for (x = 0; x < width; x++)
{
UINT16 abc0, abc1, abc2, abc3;
UINT32 tempa, tempd = 0;
UINT32 b;
/* fetch data for A */
if (CUSTOM_REG(REG_BLTCON0) & 0x0800)
{
CUSTOM_REG(REG_BLTADAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTAPTH));
CUSTOM_REG_LONG(REG_BLTAPTH) -= 2;
}
/* fetch data for B */
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
{
CUSTOM_REG(REG_BLTBDAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTBPTH));
CUSTOM_REG_LONG(REG_BLTBPTH) -= 2;
}
/* fetch data for C */
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
{
CUSTOM_REG(REG_BLTCDAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTCPTH));
CUSTOM_REG_LONG(REG_BLTCPTH) -= 2;
}
/* apply start/end masks to the A data */
tempa = CUSTOM_REG(REG_BLTADAT);
if (x == 0)
tempa &= CUSTOM_REG(REG_BLTAFWM);
if (x == width - 1)
tempa &= CUSTOM_REG(REG_BLTALWM);
/* update the B accumulator applying shifts */
acca = (acca >> 16) | (tempa << shifta);
accb = (accb >> 16) | (CUSTOM_REG(REG_BLTBDAT) << shiftb);
/* build up 4 16-bit words containing 4 pixels each in 0ABC bit order */
abc0 = ((acca >> 1) & 0x4444) | ((accb >> 2) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 3) & 0x1111);
abc1 = ((acca >> 0) & 0x4444) | ((accb >> 1) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 2) & 0x1111);
abc2 = ((acca << 1) & 0x4444) | ((accb >> 0) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 1) & 0x1111);
abc3 = ((acca << 2) & 0x4444) | ((accb << 1) & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 0) & 0x1111);
/* now loop over bits and compute the destination value */
for (b = 0; b < 4; b++)
{
UINT32 prev_fill_state;
UINT32 bit;
/* shift previous data up 4 bits */
tempd >>= 4;
/* lookup fourth bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc3 & 0xf)) & 1;
abc3 >>= 4;
prev_fill_state = fill_state;
fill_state ^= bit;
bit ^= prev_fill_state & fill_exclusive;
bit |= prev_fill_state & fill_inclusive;
tempd |= bit << 12;
/* lookup third bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc2 & 0xf)) & 1;
abc2 >>= 4;
prev_fill_state = fill_state;
fill_state ^= bit;
bit ^= prev_fill_state & fill_exclusive;
bit |= prev_fill_state & fill_inclusive;
tempd |= bit << 13;
/* lookup second bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc1 & 0xf)) & 1;
abc1 >>= 4;
prev_fill_state = fill_state;
fill_state ^= bit;
bit ^= prev_fill_state & fill_exclusive;
bit |= prev_fill_state & fill_inclusive;
tempd |= bit << 14;
/* lookup first bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc0 & 0xf)) & 1;
abc0 >>= 4;
prev_fill_state = fill_state;
fill_state ^= bit;
bit ^= prev_fill_state & fill_exclusive;
bit |= prev_fill_state & fill_inclusive;
tempd |= bit << 15;
}
/* accumulate the sum */
blitsum |= tempd;
/* write to the destination */
if (CUSTOM_REG(REG_BLTCON0) & 0x0100)
{
amiga_chip_ram_w(CUSTOM_REG_LONG(REG_BLTDPTH), tempd);
CUSTOM_REG_LONG(REG_BLTDPTH) -= 2;
}
}
/* apply end of line modulos */
if (CUSTOM_REG(REG_BLTCON0) & 0x0800)
CUSTOM_REG_LONG(REG_BLTAPTH) -= CUSTOM_REG_SIGNED(REG_BLTAMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
CUSTOM_REG_LONG(REG_BLTBPTH) -= CUSTOM_REG_SIGNED(REG_BLTBMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
CUSTOM_REG_LONG(REG_BLTCPTH) -= CUSTOM_REG_SIGNED(REG_BLTCMOD) & ~1;
if (CUSTOM_REG(REG_BLTCON0) & 0x0100)
CUSTOM_REG_LONG(REG_BLTDPTH) -= CUSTOM_REG_SIGNED(REG_BLTDMOD) & ~1;
}
/* return the blit sum */
return blitsum;
}
/*************************************
*
* Line drawing blitter variant
*
*************************************/
/*
The exact line drawing algorithm is not known, but based on the cryptic
setup instructions, it is clear that it is a basic Bresenham line
algorithm. A standard Bresenham algorithm looks like this:
epsilon = 0;
while (length--)
{
plot(x, y);
x++;
epsilon += dy;
if ((2 * epsilon) >= dx)
{
y++;
epsilon -= dx;
}
}
If you multiply the epsilon term by 4 and shuffle the logic a bit, the
equivalent logic is:
epsilon = 4 * dy - 2 * dx;
while (length--)
{
plot(x, y);
x++;
if (epsilon >= 0)
{
y++;
epsilon += 4 * (dy - dx);
}
else
epsilon += 4 * dy;
}
With this refactoring, you can see that BLTAPT = epsilon,
BLTAMOD = 4 * (dy - dx) and BLTBMOD = 4 * dy.
*/
static UINT32 blit_line(void)
{
UINT32 singlemode = (CUSTOM_REG(REG_BLTCON1) & 0x0002) ? 0x0000 : 0xffff;
UINT32 singlemask = 0xffff;
UINT32 blitsum = 0;
UINT32 height;
/* see if folks are breaking the rules */
if (CUSTOM_REG(REG_BLTSIZH) != 0x0002)
logerror("Blitter: Blit width != 2 in line mode!\n");
if ((CUSTOM_REG(REG_BLTCON0) & 0x0a00) != 0x0a00)
logerror("Blitter: Channel selection incorrect in line mode!\n" );
/* extract the length of the line */
height = CUSTOM_REG(REG_BLTSIZV);
/* iterate over the line height */
while (height--)
{
UINT16 abc0, abc1, abc2, abc3;
UINT32 tempa, tempb, tempd = 0;
int b, dx, dy;
/* fetch data for C */
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
CUSTOM_REG(REG_BLTCDAT) = amiga_chip_ram_r(CUSTOM_REG_LONG(REG_BLTCPTH));
/* rotate the A data according to the shift */
tempa = CUSTOM_REG(REG_BLTADAT) >> (CUSTOM_REG(REG_BLTCON0) >> 12);
/* apply single bit mask */
tempa &= singlemask;
singlemask &= singlemode;
/* rotate the B data according to the shift and expand to 16 bits */
tempb = -((CUSTOM_REG(REG_BLTBDAT) >> (CUSTOM_REG(REG_BLTCON1) >> 12)) & 1);
/* build up 4 16-bit words containing 4 pixels each in 0ABC bit order */
abc0 = ((tempa >> 1) & 0x4444) | (tempb & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 3) & 0x1111);
abc1 = ((tempa >> 0) & 0x4444) | (tempb & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 2) & 0x1111);
abc2 = ((tempa << 1) & 0x4444) | (tempb & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 1) & 0x1111);
abc3 = ((tempa << 2) & 0x4444) | (tempb & 0x2222) | ((CUSTOM_REG(REG_BLTCDAT) >> 0) & 0x1111);
/* now loop over bits and compute the destination value */
for (b = 0; b < 4; b++)
{
UINT32 bit;
/* shift previous data up 4 bits */
tempd <<= 4;
/* lookup first bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc0 >> 12)) & 1;
abc0 <<= 4;
tempd |= bit << 3;
/* lookup second bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc1 >> 12)) & 1;
abc1 <<= 4;
tempd |= bit << 2;
/* lookup third bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc2 >> 12)) & 1;
abc2 <<= 4;
tempd |= bit << 1;
/* lookup fourth bit in series */
bit = (CUSTOM_REG(REG_BLTCON0) >> (abc3 >> 12)) & 1;
abc3 <<= 4;
tempd |= bit << 0;
}
/* accumulate the sum */
blitsum |= tempd;
/* write to the destination */
amiga_chip_ram_w(CUSTOM_REG_LONG(REG_BLTDPTH), tempd);
/* always increment along the major axis */
if (CUSTOM_REG(REG_BLTCON1) & 0x0010)
{
dx = (CUSTOM_REG(REG_BLTCON1) & 0x0004) ? -1 : 1;
dy = 0;
}
else
{
dx = 0;
dy = (CUSTOM_REG(REG_BLTCON1) & 0x0004) ? -1 : 1;
}
/* is the sign bit clear? */
if (!(CUSTOM_REG(REG_BLTCON1) & 0x0040))
{
/* add 4 * (dy-dx) */
CUSTOM_REG_LONG(REG_BLTAPTH) += CUSTOM_REG_SIGNED(REG_BLTAMOD) & ~1;
/* increment along the minor axis */
if (CUSTOM_REG(REG_BLTCON1) & 0x0010)
dy = (CUSTOM_REG(REG_BLTCON1) & 0x0008) ? -1 : 1;
else
dx = (CUSTOM_REG(REG_BLTCON1) & 0x0008) ? -1 : 1;
}
/* else add 4 * dy and don't increment along the minor axis */
else
CUSTOM_REG_LONG(REG_BLTAPTH) += CUSTOM_REG_SIGNED(REG_BLTBMOD) & ~1;
/* adjust X if necessary */
if (dx)
{
/* adjust the A shift value */
UINT32 temp = CUSTOM_REG(REG_BLTCON0) + (INT32)(dx << 12);
CUSTOM_REG(REG_BLTCON0) = temp;
/* if we went from 0xf to 0x0 or vice-versa, adjust the actual pointers */
if (temp & 0x10000)
{
CUSTOM_REG_LONG(REG_BLTCPTH) += 2 * dx;
CUSTOM_REG_LONG(REG_BLTDPTH) += 2 * dx;
}
}
/* adjust Y if necessary */
if (dy)
{
/* BLTCMOD seems to be used for both C and D pointers */
CUSTOM_REG_LONG(REG_BLTCPTH) += dy * (INT16)(CUSTOM_REG_SIGNED(REG_BLTCMOD) & ~1);
CUSTOM_REG_LONG(REG_BLTDPTH) += dy * (INT16)(CUSTOM_REG_SIGNED(REG_BLTCMOD) & ~1);
/* reset the single mask since we're on a new line */
singlemask = 0xffff;
}
/* set the new sign bit value */
CUSTOM_REG(REG_BLTCON1) = (CUSTOM_REG(REG_BLTCON1) & ~0x0040) | ((CUSTOM_REG(REG_BLTAPTL) >> 9) & 0x0040);
/* increment texture shift on every pixel */
CUSTOM_REG(REG_BLTCON1) += 0x1000;
}
return blitsum;
}
/*************************************
*
* Blitter deferred callback
*
*************************************/
static TIMER_CALLBACK( amiga_blitter_proc )
{
UINT32 blitsum = 0;
/* logging */
if (LOG_BLITS)
{
static const char *type[] = { "ASCENDING", "LINE", "DESCENDING", "LINE" };
logerror("BLIT %s: %dx%d %04x %04x\n", type[CUSTOM_REG(REG_BLTCON1) & 0x0003], CUSTOM_REG(REG_BLTSIZH), CUSTOM_REG(REG_BLTSIZV), CUSTOM_REG(REG_BLTCON0), CUSTOM_REG(REG_BLTCON1));
if (CUSTOM_REG(REG_BLTCON0) & 0x0800)
logerror(" A: addr=%06X mod=%3d shift=%2d maskl=%04x maskr=%04x\n", CUSTOM_REG_LONG(REG_BLTAPTH), CUSTOM_REG_SIGNED(REG_BLTAMOD), CUSTOM_REG(REG_BLTCON0) >> 12, CUSTOM_REG(REG_BLTAFWM), CUSTOM_REG(REG_BLTALWM));
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
logerror(" B: addr=%06X mod=%3d shift=%2d\n", CUSTOM_REG_LONG(REG_BLTBPTH), CUSTOM_REG_SIGNED(REG_BLTBMOD), CUSTOM_REG(REG_BLTCON1) >> 12);
if (CUSTOM_REG(REG_BLTCON0) & 0x0200)
logerror(" C: addr=%06X mod=%3d\n", CUSTOM_REG_LONG(REG_BLTCPTH), CUSTOM_REG_SIGNED(REG_BLTCMOD));
if (CUSTOM_REG(REG_BLTCON0) & 0x0100)
logerror(" D: addr=%06X mod=%3d\n", CUSTOM_REG_LONG(REG_BLTDPTH), CUSTOM_REG_SIGNED(REG_BLTDMOD));
}
/* set the zero flag */
CUSTOM_REG(REG_DMACON) |= 0x2000;
/* switch off the type of blit */
switch (CUSTOM_REG(REG_BLTCON1) & 0x0003)
{
case 0: /* ascending */
blitsum = blit_ascending();
break;
case 2: /* descending */
blitsum = blit_descending();
break;
case 1: /* line */
case 3:
blitsum = blit_line();
break;
}
/* clear the zero flag if we actually wrote data */
if (blitsum)
CUSTOM_REG(REG_DMACON) &= ~0x2000;
/* no longer busy */
CUSTOM_REG(REG_DMACON) &= ~0x4000;
/* signal an interrupt */
amiga_custom_w(REG_INTREQ, 0x8000 | INTENA_BLIT, 0);
/* reset the blitter timer */
timer_reset( amiga_blitter_timer, attotime_never);
}
/*************************************
*
* Blitter setup
*
*************************************/
static void blitter_setup(void)
{
int ticks, width, height, blittime;
/* is there another blitting in progress? */
if (CUSTOM_REG(REG_DMACON) & 0x4000)
{
logerror("PC: %08x - This program is playing tricks with the blitter\n", safe_activecpu_get_pc() );
return;
}
/* line mode is 8 ticks/pixel */
if (CUSTOM_REG(REG_BLTCON1) & 1)
ticks = 8;
/* standard mode is 4 ticks base */
else
{
ticks = 4;
/* plus 2 ticks if channel B is involved */
if (CUSTOM_REG(REG_BLTCON0) & 0x0400)
ticks += 2;
/* plus 2 ticks if both channel C and D are involved */
if ((CUSTOM_REG(REG_BLTCON0) & 0x0300) == 0x0300)
ticks += 2;
}
/* extract height/width */
width = CUSTOM_REG(REG_BLTSIZH);
height = CUSTOM_REG(REG_BLTSIZV);
/* compute the blit time */
blittime = ticks * height * width;
/* if 'blitter-nasty' is set, then the blitter takes over the bus. Make the blit semi-immediate */
if ( CUSTOM_REG(REG_DMACON) & 0x0400 )
{
/* simulate the 68k not running while the blit is going */
activecpu_adjust_icount( -(blittime/2) );
blittime = BLITTER_NASTY_DELAY;
}
/* AGA has twice the bus bandwidth, so blits take half the time */
if ( IS_AGA(amiga_intf) )
blittime /= 2;
/* signal blitter busy */
CUSTOM_REG(REG_DMACON) |= 0x4000;
/* set a timer */
timer_adjust( amiga_blitter_timer, ATTOTIME_IN_CYCLES( blittime, 0 ), 0, attotime_zero);
}
/*************************************
*
* 8520 CIA read handler
*
*************************************/
READ16_HANDLER( amiga_cia_r )
{
UINT8 data;
int shift, which;
/* offsets 0000-07ff reference CIA B, and are accessed via the MSB */
if ((offset & 0x0800) == 0)
{
which = 1;
shift = 8;
}
/* offsets 0800-0fff reference CIA A, and are accessed via the LSB */
else
{
which = 0;
shift = 0;
}
/* handle the reads */
data = cia_read(which, offset >> 7);
#if LOG_CIA
logerror("%06x:cia_%c_read(%03x) = %04x & %04x\n", safe_activecpu_get_pc(), 'A' + ((~offset & 0x0800) >> 11), offset * 2, data << shift, mem_mask ^ 0xffff);
#endif
return data << shift;
}
/*************************************
*
* 8520 CIA write handler
*
*************************************/
WRITE16_HANDLER( amiga_cia_w )
{
int which;
#if LOG_CIA
logerror("%06x:cia_%c_write(%03x) = %04x & %04x\n", safe_activecpu_get_pc(), 'A' + ((~offset & 0x0800) >> 11), offset * 2, data, mem_mask ^ 0xffff);
#endif
/* offsets 0000-07ff reference CIA B, and are accessed via the MSB */
if ((offset & 0x0800) == 0)
{
if (!ACCESSING_MSB)
return;
which = 1;
data >>= 8;
}
/* offsets 0800-0fff reference CIA A, and are accessed via the LSB */
else
{
if (!ACCESSING_LSB)
return;
which = 0;
data &= 0xff;
}
/* handle the writes */
cia_write(which, offset >> 7, (UINT8) data);
}
/*************************************
*
* CIA interrupt callbacks
*
*************************************/
static void amiga_cia_0_irq(int state)
{
amiga_custom_w(REG_INTREQ, (state ? 0x8000 : 0x0000) | INTENA_PORTS, 0);
}
static void amiga_cia_1_irq(int state)
{
amiga_custom_w(REG_INTREQ, (state ? 0x8000 : 0x0000) | INTENA_EXTER, 0);
}
/*************************************
*
* Custom chip reset
*
*************************************/
static void custom_reset(void)
{
int clock = Machine->drv->cpu[0].clock;
UINT16 vidmode = (clock == AMIGA_68000_NTSC_CLOCK || clock == AMIGA_68EC020_NTSC_CLOCK ) ? 0x1000 : 0x0000; /* NTSC or PAL? */
CUSTOM_REG(REG_DDFSTRT) = 0x18;
CUSTOM_REG(REG_DDFSTOP) = 0xd8;
CUSTOM_REG(REG_INTENA) = 0x0000;
CUSTOM_REG(REG_VPOSR) = vidmode;
CUSTOM_REG(REG_SERDATR) = 0x3000;
switch (amiga_intf->chip_ram_mask)
{
case ANGUS_CHIP_RAM_MASK:
case FAT_ANGUS_CHIP_RAM_MASK:
CUSTOM_REG(REG_DENISEID) = 0x00FF;
break;
case ECS_CHIP_RAM_MASK:
CUSTOM_REG(REG_VPOSR) |= 0x2000;
CUSTOM_REG(REG_DENISEID) = 0x00FC;
if (IS_AGA(amiga_intf))
{
CUSTOM_REG(REG_VPOSR) |= 0x0300;
CUSTOM_REG(REG_DENISEID) = 0x00F8;
}
break;
}
}
/*************************************
*
* Custom chip register read
*
*************************************/
READ16_HANDLER( amiga_custom_r )
{
UINT16 temp;
switch (offset & 0xff)
{
case REG_BLTDDAT:
return CUSTOM_REG(REG_BLTDDAT);
case REG_DMACONR:
return CUSTOM_REG(REG_DMACON);
case REG_VPOSR:
CUSTOM_REG(REG_VPOSR) &= 0xff00;
CUSTOM_REG(REG_VPOSR) |= amiga_gethvpos() >> 16;
return CUSTOM_REG(REG_VPOSR);
case REG_VHPOSR:
return amiga_gethvpos() & 0xffff;
case REG_SERDATR:
CUSTOM_REG(REG_SERDATR) &= ~0x4000;
CUSTOM_REG(REG_SERDATR) |= (CUSTOM_REG(REG_INTREQ) & INTENA_RBF) ? 0x4000 : 0x0000;
return CUSTOM_REG(REG_SERDATR);
case REG_JOY0DAT:
if (amiga_intf->joy0dat_r != NULL)
return (*amiga_intf->joy0dat_r)();
return readinputportbytag_safe("JOY0DAT", 0xffff);
case REG_JOY1DAT:
if (amiga_intf->joy1dat_r != NULL)
return (*amiga_intf->joy1dat_r)();
return readinputportbytag_safe("JOY1DAT", 0xffff);
case REG_ADKCONR:
return CUSTOM_REG(REG_ADKCON);
case REG_POTGOR:
return readinputportbytag_safe("POTGO", 0x5500);
case REG_POT0DAT:
return readinputportbytag_safe("POT0DAT", 0x0000);
case REG_POT1DAT:
return readinputportbytag_safe("POT1DAT", 0x0000);
case REG_DSKBYTR:
if (amiga_intf->dskbytr_r != NULL)
return (*amiga_intf->dskbytr_r)();
return 0x0000;
case REG_INTENAR:
return CUSTOM_REG(REG_INTENA);
case REG_INTREQR:
return CUSTOM_REG(REG_INTREQ);
case REG_COPJMP1:
copper_setpc(CUSTOM_REG_LONG(REG_COP1LCH));
break;
case REG_COPJMP2:
copper_setpc(CUSTOM_REG_LONG(REG_COP2LCH));
break;
case REG_CLXDAT:
temp = CUSTOM_REG(REG_CLXDAT);
CUSTOM_REG(REG_CLXDAT) = 0;
return temp;
case REG_DENISEID:
return CUSTOM_REG(REG_DENISEID);
break;
}
#if LOG_CUSTOM
logerror("%06X:read from custom %s\n", safe_activecpu_get_pc(), amiga_custom_names[offset & 0xff]);
#endif
return 0xffff;
}
/*************************************
*
* Custom chip register write
*
*************************************/
static TIMER_CALLBACK( finish_serial_write )
{
/* mark the transfer buffer empty */
CUSTOM_REG(REG_SERDATR) |= 0x3000;
/* signal an interrupt */
amiga_custom_w(REG_INTREQ, 0x8000 | INTENA_TBE, 0);
}
WRITE16_HANDLER( amiga_custom_w )
{
UINT16 temp;
offset &= 0xff;
#if LOG_CUSTOM
logerror("%06X:write to custom %s = %04X\n", safe_activecpu_get_pc(), amiga_custom_names[offset & 0xff], data);
#endif
switch (offset)
{
case REG_BLTDDAT: case REG_DMACONR: case REG_VPOSR: case REG_VHPOSR:
case REG_DSKDATR: case REG_JOY0DAT: case REG_JOY1DAT: case REG_CLXDAT:
case REG_ADKCONR: case REG_POT0DAT: case REG_POT1DAT: case REG_POTGOR:
case REG_SERDATR: case REG_DSKBYTR: case REG_INTENAR: case REG_INTREQR:
/* read-only registers */
break;
case REG_DSKLEN:
if (amiga_intf->dsklen_w != NULL)
(*amiga_intf->dsklen_w)(data);
break;
case REG_POTGO:
if (amiga_intf->potgo_w != NULL)
(*amiga_intf->potgo_w)(data);
break;
case REG_SERDAT:
if (amiga_intf->serdat_w != NULL)
(*amiga_intf->serdat_w)(data);
CUSTOM_REG(REG_SERDATR) &= ~0x3000;
timer_set(amiga_get_serial_char_period(), 0, finish_serial_write);
break;
case REG_BLTSIZE:
CUSTOM_REG(REG_BLTSIZE) = data;
CUSTOM_REG(REG_BLTSIZV) = (data >> 6) & 0x3ff;
CUSTOM_REG(REG_BLTSIZH) = data & 0x3f;
if ( CUSTOM_REG(REG_BLTSIZV) == 0 ) CUSTOM_REG(REG_BLTSIZV) = 0x400;
if ( CUSTOM_REG(REG_BLTSIZH) == 0 ) CUSTOM_REG(REG_BLTSIZH) = 0x40;
blitter_setup();
break;
case REG_BLTSIZV: /* ECS-AGA only */
if ( IS_ECS_OR_AGA(amiga_intf) )
{
CUSTOM_REG(REG_BLTSIZV) = data & 0x7fff;
if ( CUSTOM_REG(REG_BLTSIZV) == 0 ) CUSTOM_REG(REG_BLTSIZV) = 0x8000;
}
break;
case REG_BLTSIZH: /* ECS-AGA only */
if ( IS_ECS_OR_AGA(amiga_intf) )
{
CUSTOM_REG(REG_BLTSIZH) = data & 0x7ff;
if ( CUSTOM_REG(REG_BLTSIZH) == 0 ) CUSTOM_REG(REG_BLTSIZH) = 0x800;
blitter_setup();
}
break;
case REG_BLTCON0L: /* ECS-AGA only */
if ( IS_ECS_OR_AGA(amiga_intf) )
{
CUSTOM_REG(REG_BLTCON0) &= 0xff00;
CUSTOM_REG(REG_BLTCON0) |= data & 0xff;
}
break;
case REG_SPR0PTH: case REG_SPR1PTH: case REG_SPR2PTH: case REG_SPR3PTH:
case REG_SPR4PTH: case REG_SPR5PTH: case REG_SPR6PTH: case REG_SPR7PTH:
data &= ( amiga_intf->chip_ram_mask >> 16 );
break;
case REG_SPR0PTL: case REG_SPR1PTL: case REG_SPR2PTL: case REG_SPR3PTL:
case REG_SPR4PTL: case REG_SPR5PTL: case REG_SPR6PTL: case REG_SPR7PTL:
amiga_sprite_dma_reset((offset - REG_SPR0PTL) / 2);
break;
case REG_SPR0CTL: case REG_SPR1CTL: case REG_SPR2CTL: case REG_SPR3CTL:
case REG_SPR4CTL: case REG_SPR5CTL: case REG_SPR6CTL: case REG_SPR7CTL:
/* disable comparitor on writes here */
amiga_sprite_enable_comparitor((offset - REG_SPR0CTL) / 4, FALSE);
break;
case REG_SPR0DATA: case REG_SPR1DATA: case REG_SPR2DATA: case REG_SPR3DATA:
case REG_SPR4DATA: case REG_SPR5DATA: case REG_SPR6DATA: case REG_SPR7DATA:
/* enable comparitor on writes here */
amiga_sprite_enable_comparitor((offset - REG_SPR0DATA) / 4, TRUE);
break;
case REG_COP1LCH: case REG_COP2LCH:
data &= ( amiga_intf->chip_ram_mask >> 16 );
break;
case REG_COPJMP1:
copper_setpc(CUSTOM_REG_LONG(REG_COP1LCH));
break;
case REG_COPJMP2:
copper_setpc(CUSTOM_REG_LONG(REG_COP2LCH));
break;
case REG_DDFSTRT:
/* impose hardware limits ( HRM, page 75 ) */
data &= 0xfe;
if (data < 0x18)
data = 0x18;
break;
case REG_DDFSTOP:
/* impose hardware limits ( HRM, page 75 ) */
data &= 0xfe;
if (data > 0xd8)
data = 0xd8;
break;
case REG_DMACON:
amiga_audio_update();
/* bits BBUSY (14) and BZERO (13) are read-only */
data &= 0x9fff;
data = (data & 0x8000) ? (CUSTOM_REG(offset) | (data & 0x7fff)) : (CUSTOM_REG(offset) & ~(data & 0x7fff));
/* if 'blitter-nasty' has been turned on and we have a blit pending, reschedule it */
if ( ( data & 0x400 ) && ( CUSTOM_REG(REG_DMACON) & 0x4000 ) )
timer_adjust( amiga_blitter_timer, ATTOTIME_IN_CYCLES( BLITTER_NASTY_DELAY, 0 ), 0, attotime_zero);
break;
case REG_INTENA:
temp = data;
data = (data & 0x8000) ? (CUSTOM_REG(offset) | (data & 0x7fff)) : (CUSTOM_REG(offset) & ~(data & 0x7fff));
CUSTOM_REG(offset) = data;
if ( temp & 0x8000 ) /* if we're enabling irq's, delay a bit */
timer_adjust( amiga_irq_timer, ATTOTIME_IN_CYCLES( AMIGA_IRQ_DELAY_CYCLES, 0 ), 0, attotime_zero);
else /* if we're disabling irq's, process right away */
update_irqs();
break;
case REG_INTREQ:
temp = data;
/* Update serial data line status if appropiate */
if (!(data & 0x8000) && (data & INTENA_RBF))
CUSTOM_REG(REG_SERDATR) &= ~0x8000;
data = (data & 0x8000) ? (CUSTOM_REG(offset) | (data & 0x7fff)) : (CUSTOM_REG(offset) & ~(data & 0x7fff));
if ( cia_get_irq( 0 ) ) data |= INTENA_PORTS;
if ( cia_get_irq( 1 ) ) data |= INTENA_EXTER;
CUSTOM_REG(offset) = data;
if ( temp & 0x8000 ) /* if we're generating irq's, delay a bit */
timer_adjust( amiga_irq_timer, ATTOTIME_IN_CYCLES( AMIGA_IRQ_DELAY_CYCLES, 0 ), 0, attotime_zero);
else /* if we're clearing irq's, process right away */
update_irqs();
break;
case REG_ADKCON:
amiga_audio_update();
data = (data & 0x8000) ? (CUSTOM_REG(offset) | (data & 0x7fff)) : (CUSTOM_REG(offset) & ~(data & 0x7fff));
break;
case REG_AUD0LCL: case REG_AUD0LCH: case REG_AUD0LEN: case REG_AUD0PER: case REG_AUD0VOL:
case REG_AUD1LCL: case REG_AUD1LCH: case REG_AUD1LEN: case REG_AUD1PER: case REG_AUD1VOL:
case REG_AUD2LCL: case REG_AUD2LCH: case REG_AUD2LEN: case REG_AUD2PER: case REG_AUD2VOL:
case REG_AUD3LCL: case REG_AUD3LCH: case REG_AUD3LEN: case REG_AUD3PER: case REG_AUD3VOL:
amiga_audio_update();
break;
case REG_AUD0DAT: case REG_AUD1DAT: case REG_AUD2DAT: case REG_AUD3DAT:
amiga_audio_data_w((offset - REG_AUD0DAT) / 8, data);
break;
case REG_BPL1PTH: case REG_BPL2PTH: case REG_BPL3PTH: case REG_BPL4PTH:
case REG_BPL5PTH: case REG_BPL6PTH:
data &= ( amiga_intf->chip_ram_mask >> 16 );
break;
case REG_BPLCON0:
if ((data & (BPLCON0_BPU0 | BPLCON0_BPU1 | BPLCON0_BPU2)) == (BPLCON0_BPU0 | BPLCON0_BPU1 | BPLCON0_BPU2))
{
/* planes go from 0 to 6, inclusive */
logerror( "This game is doing funky planes stuff. (planes > 6)\n" );
data &= ~BPLCON0_BPU0;
}
break;
case REG_COLOR00: case REG_COLOR01: case REG_COLOR02: case REG_COLOR03:
case REG_COLOR04: case REG_COLOR05: case REG_COLOR06: case REG_COLOR07:
case REG_COLOR08: case REG_COLOR09: case REG_COLOR10: case REG_COLOR11:
case REG_COLOR12: case REG_COLOR13: case REG_COLOR14: case REG_COLOR15:
case REG_COLOR16: case REG_COLOR17: case REG_COLOR18: case REG_COLOR19:
case REG_COLOR20: case REG_COLOR21: case REG_COLOR22: case REG_COLOR23:
case REG_COLOR24: case REG_COLOR25: case REG_COLOR26: case REG_COLOR27:
case REG_COLOR28: case REG_COLOR29: case REG_COLOR30: case REG_COLOR31:
data &= 0xfff;
CUSTOM_REG(offset + 32) = (data >> 1) & 0x777;
break;
default:
break;
}
if (offset <= REG_COLOR31)
CUSTOM_REG(offset) = data;
}
/*************************************
*
* Serial writes
*
*************************************/
void amiga_serial_in_w(UINT16 data)
{
int mask = (CUSTOM_REG(REG_SERPER) & 0x8000) ? 0x1ff : 0xff;
/* copy the data to the low 8 bits of SERDATR and set RBF */
CUSTOM_REG(REG_SERDATR) &= ~0x3ff;
CUSTOM_REG(REG_SERDATR) |= (data & mask) | (mask + 1) | 0x4000;
/* set overrun if we weren't cleared */
if (CUSTOM_REG(REG_INTREQ) & INTENA_RBF)
{
mame_printf_debug("Serial data overflow\n");
CUSTOM_REG(REG_SERDATR) |= 0x8000;
}
/* signal an interrupt */
amiga_custom_w(REG_INTREQ, 0x8000 | INTENA_RBF, 0);
}
attotime amiga_get_serial_char_period(void)
{
UINT32 divisor = (CUSTOM_REG(REG_SERPER) & 0x7fff) + 1;
UINT32 baud = Machine->drv->cpu[0].clock / 2 / divisor;
UINT32 numbits = 2 + ((CUSTOM_REG(REG_SERPER) & 0x8000) ? 9 : 8);
return attotime_mul(ATTOTIME_IN_HZ(baud), numbits);
}
/*************************************
*
* Autoconfig registration
*
*************************************/
void amiga_add_autoconfig(amiga_autoconfig_device *device)
{
autoconfig_device *dev, **d;
/* validate the data */
assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call amiga_add_autoconfig at init time!");
assert_always((device->size & (device->size - 1)) == 0, "device->size must be power of 2!");
/* allocate memory and link it in at the end of the list */
dev = auto_malloc(sizeof(*dev));
dev->next = NULL;
for (d = &autoconfig_list; *d; d = &(*d)->next) ;
*d = dev;
/* fill in the data */
dev->device = *device;
dev->base = 0;
}
/*************************************
*
* Autoconfig reset
*
*************************************/
static void autoconfig_reset(void)
{
autoconfig_device *dev;
/* uninstall any installed devices */
for (dev = autoconfig_list; dev; dev = dev->next)
if (dev->base && dev->device.uninstall)
{
(*dev->device.uninstall)(dev->base);
dev->base = 0;
}
/* reset the current autoconfig */
cur_autoconfig = autoconfig_list;
}
/*************************************
*
* Autoconfig space read
*
*************************************/
READ16_HANDLER( amiga_autoconfig_r )
{
UINT8 byte;
int i;
/* if nothing present, just return */
if (!cur_autoconfig)
{
logerror("autoconfig_r(%02X) but no device selected\n", offset);
return 0;
}
/* switch off of the base offset */
switch (offset/2)
{
/*
00/02 1 1 x x x 0 0 0 = 8 Megabytes
^ ^ ^ 0 0 1 = 64 Kbytes
| | | 0 1 0 = 128 Kbytes
| | | 0 1 1 = 256 Kbytes
| | | 1 0 0 = 1 Megabyte
| | | 1 1 0 = 2 Megabytes
| | | 1 1 1 = 4 Megabytes
| | |
| | `-- 1 = multiple devices on this card
| `-------- 1 = ROM vector offset is valid
`----------- 1 = link into free memory list
*/
case 0x00/4:
byte = 0xc0;
if (cur_autoconfig->device.link_memory)
byte |= 0x20;
if (cur_autoconfig->device.rom_vector_valid)
byte |= 0x10;
if (cur_autoconfig->device.multi_device)
byte |= 0x08;
for (i = 0; i < 8; i++)
if (cur_autoconfig->device.size & (1 << i))
break;
byte |= (i + 1) & 7;
break;
/*
04/06 product number (all bits inverted)
*/
case 0x04/4:
byte = ~cur_autoconfig->device.product_number;
break;
/*
08/0a x x 1 1 1 1 1 1
^ ^
| |
| `-- 1 = this board can be shut up
`----- 0 = prefer 8 Meg address space
*/
case 0x08/4:
byte = 0x3f;
if (!cur_autoconfig->device.prefer_8meg)
byte |= 0x80;
if (cur_autoconfig->device.can_shutup)
byte |= 0x40;
break;
/*
10/12 manufacturers number (high byte, all inverted)
14/16 '' (low byte, all inverted)
*/
case 0x10/4:
byte = ~cur_autoconfig->device.mfr_number >> 8;
break;
case 0x14/4:
byte = ~cur_autoconfig->device.mfr_number >> 0;
break;
/*
18/1a optional serial number (all bits inverted) byte0
1c/1e '' byte1
20/22 '' byte2
24/26 '' byte3
*/
case 0x18/4:
byte = ~cur_autoconfig->device.serial_number >> 24;
break;
case 0x1c/4:
byte = ~cur_autoconfig->device.serial_number >> 16;
break;
case 0x20/4:
byte = ~cur_autoconfig->device.serial_number >> 8;
break;
case 0x24/4:
byte = ~cur_autoconfig->device.serial_number >> 0;
break;
/*
28/2a optional ROM vector offset (all bits inverted) high byte
2c/2e '' low byte
*/
case 0x28/4:
byte = ~cur_autoconfig->device.rom_vector >> 8;
break;
case 0x2c/4:
byte = ~cur_autoconfig->device.rom_vector >> 0;
break;
/*
40/42 optional interrupt control and status register
*/
case 0x40/4:
byte = 0x00;
if (cur_autoconfig->device.int_control_r)
byte = (*cur_autoconfig->device.int_control_r)();
break;
default:
byte = 0xff;
break;
}
/* return the appropriate nibble */
logerror("autoconfig_r(%02X) = %04X\n", offset, (offset & 1) ? ((byte << 12) | 0xfff) : ((byte << 8) | 0xfff));
return (offset & 1) ? ((byte << 12) | 0xfff) : ((byte << 8) | 0xfff);
}
/*************************************
*
* Autoconfig space write
*
*************************************/
WRITE16_HANDLER( amiga_autoconfig_w )
{
int move_to_next = FALSE;
logerror("autoconfig_w(%02X) = %04X & %04X\n", offset, data, mem_mask ^ 0xffff);
/* if no current device, bail */
if (!cur_autoconfig || !ACCESSING_MSB)
return;
/* switch off of the base offset */
switch (offset/2)
{
/*
48/4a write-only register for base address (A23-A16)
*/
case 0x48/4:
if ((offset & 1) == 0)
cur_autoconfig->base = (cur_autoconfig->base & ~0xf00000) | ((data & 0xf000) << 8);
else
cur_autoconfig->base = (cur_autoconfig->base & ~0x0f0000) | ((data & 0xf000) << 4);
move_to_next = TRUE;
break;
/*
4c/4e optional write-only 'shutup' trigger
*/
case 0x4c/4:
cur_autoconfig->base = 0;
move_to_next = TRUE;
break;
}
/* install and move to the next device if requested */
if (move_to_next && (offset & 1) == 0)
{
logerror("Install to %06X\n", cur_autoconfig->base);
if (cur_autoconfig->base && cur_autoconfig->device.install)
(*cur_autoconfig->device.install)(cur_autoconfig->base);
cur_autoconfig = cur_autoconfig->next;
}
}