/**************************************************************************
*
* gp32.c - Game Park GP32
* Skeleton by R. Belmont
*
* CPU: Samsung S3C2400X01 SoC
* S3C2400X01 consists of:
* ARM920T CPU core + MMU
* LCD controller
* DMA controller
* Interrupt controller
* USB controller
* and more.
*
**************************************************************************/
#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "cpu/arm7/arm7core.h"
#include "includes/gp32.h"
#include "rendlay.h"
#define VERBOSE_LEVEL ( 0 )
INLINE void ATTR_PRINTF(3,4) verboselog(running_machine &machine, int n_level, const char *s_fmt, ...)
{
if (VERBOSE_LEVEL >= n_level)
{
va_list v;
char buf[32768];
va_start( v, s_fmt);
vsprintf( buf, s_fmt, v);
va_end( v);
logerror( "%s: %s", machine.describe_context( ), buf);
}
}
#define CLOCK_MULTIPLIER 1
#define MPLLCON 1
#define UPLLCON 2
#define BIT(x,n) (((x)>>(n))&1)
#define BITS(x,m,n) (((x)>>(n))&((1<<((m)-(n)+1))-1))
// LCD CONTROLLER
#define BPPMODE_TFT_01 0x08
#define BPPMODE_TFT_02 0x09
#define BPPMODE_TFT_04 0x0A
#define BPPMODE_TFT_08 0x0B
#define BPPMODE_TFT_16 0x0C
inline rgb_t gp32_state::s3c240x_get_color_5551( UINT16 data)
{
UINT8 r, g, b, i;
r = BITS( data, 15, 11) << 3;
g = BITS( data, 10, 6) << 3;
b = BITS( data, 5, 1) << 3;
i = BIT( data, 1) << 2;
return rgb_t( r | i, g | i, b | i);
}
void gp32_state::s3c240x_lcd_dma_reload()
{
m_s3c240x_lcd.vramaddr_cur = m_s3c240x_lcd_regs[5] << 1;
m_s3c240x_lcd.vramaddr_max = ((m_s3c240x_lcd_regs[5] & 0xFFE00000) | m_s3c240x_lcd_regs[6]) << 1;
m_s3c240x_lcd.offsize = BITS( m_s3c240x_lcd_regs[7], 21, 11);
m_s3c240x_lcd.pagewidth_cur = 0;
m_s3c240x_lcd.pagewidth_max = BITS( m_s3c240x_lcd_regs[7], 10, 0);
verboselog( machine(), 3, "LCD - vramaddr %08X %08X offsize %08X pagewidth %08X\n", m_s3c240x_lcd.vramaddr_cur, m_s3c240x_lcd.vramaddr_max, m_s3c240x_lcd.offsize, m_s3c240x_lcd.pagewidth_max);
}
void gp32_state::s3c240x_lcd_dma_init()
{
s3c240x_lcd_dma_reload();
m_s3c240x_lcd.bppmode = BITS( m_s3c240x_lcd_regs[0], 4, 1);
m_s3c240x_lcd.bswp = BIT( m_s3c240x_lcd_regs[4], 1);
m_s3c240x_lcd.hwswp = BIT( m_s3c240x_lcd_regs[4], 0);
m_s3c240x_lcd.lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
m_s3c240x_lcd.hozval = BITS( m_s3c240x_lcd_regs[2], 18, 8);
}
UINT32 gp32_state::s3c240x_lcd_dma_read( )
{
UINT8 *vram, data[4];
int i;
for (i = 0; i < 2; i++)
{
vram = (UINT8 *)m_s3c240x_ram.target() + m_s3c240x_lcd.vramaddr_cur - 0x0C000000;
data[i*2+0] = vram[0];
data[i*2+1] = vram[1];
m_s3c240x_lcd.vramaddr_cur += 2;
m_s3c240x_lcd.pagewidth_cur++;
if (m_s3c240x_lcd.pagewidth_cur >= m_s3c240x_lcd.pagewidth_max)
{
m_s3c240x_lcd.vramaddr_cur += m_s3c240x_lcd.offsize << 1;
m_s3c240x_lcd.pagewidth_cur = 0;
}
}
if (m_s3c240x_lcd.hwswp == 0)
{
if (m_s3c240x_lcd.bswp == 0)
{
return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0] << 0);
}
else
{
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] << 0);
}
}
else
{
if (m_s3c240x_lcd.bswp == 0)
{
return (data[1] << 24) | (data[0] << 16) | (data[3] << 8) | (data[2] << 0);
}
else
{
return (data[2] << 24) | (data[3] << 16) | (data[0] << 8) | (data[1] << 0);
}
}
}
void gp32_state::s3c240x_lcd_render_01( )
{
bitmap_rgb32 &bitmap = m_bitmap;
UINT32 *scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
int i, j;
for (i = 0; i < 4; i++)
{
UINT32 data = s3c240x_lcd_dma_read();
for (j = 0; j < 32; j++)
{
*scanline++ = m_palette->pen_color((data >> 31) & 0x01);
data = data << 1;
m_s3c240x_lcd.hpos++;
if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 4))
{
m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
m_s3c240x_lcd.hpos = 0;
scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
}
}
}
}
void gp32_state::s3c240x_lcd_render_02( )
{
bitmap_rgb32 &bitmap = m_bitmap;
UINT32 *scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
int i, j;
for (i = 0; i < 4; i++)
{
UINT32 data = s3c240x_lcd_dma_read();
for (j = 0; j < 16; j++)
{
*scanline++ = m_palette->pen_color((data >> 30) & 0x03);
data = data << 2;
m_s3c240x_lcd.hpos++;
if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 3))
{
m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
m_s3c240x_lcd.hpos = 0;
scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
}
}
}
}
void gp32_state::s3c240x_lcd_render_04( )
{
bitmap_rgb32 &bitmap = m_bitmap;
UINT32 *scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
int i, j;
for (i = 0; i < 4; i++)
{
UINT32 data = s3c240x_lcd_dma_read( );
for (j = 0; j < 8; j++)
{
*scanline++ = m_palette->pen_color((data >> 28) & 0x0F);
data = data << 4;
m_s3c240x_lcd.hpos++;
if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 2))
{
m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
m_s3c240x_lcd.hpos = 0;
scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
}
}
}
}
void gp32_state::s3c240x_lcd_render_08( )
{
bitmap_rgb32 &bitmap = m_bitmap;
UINT32 *scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
int i, j;
for (i = 0; i < 4; i++)
{
UINT32 data = s3c240x_lcd_dma_read();
for (j = 0; j < 4; j++)
{
*scanline++ = m_palette->pen_color((data >> 24) & 0xFF);
data = data << 8;
m_s3c240x_lcd.hpos++;
if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 1))
{
m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
m_s3c240x_lcd.hpos = 0;
scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
}
}
}
}
void gp32_state::s3c240x_lcd_render_16( )
{
bitmap_rgb32 &bitmap = m_bitmap;
UINT32 *scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
int i, j;
for (i = 0; i < 4; i++)
{
UINT32 data = s3c240x_lcd_dma_read();
for (j = 0; j < 2; j++)
{
*scanline++ = s3c240x_get_color_5551( (data >> 16) & 0xFFFF);
data = data << 16;
m_s3c240x_lcd.hpos++;
if (m_s3c240x_lcd.hpos >= (m_s3c240x_lcd.pagewidth_max << 0))
{
m_s3c240x_lcd.vpos = (m_s3c240x_lcd.vpos + 1) % (m_s3c240x_lcd.lineval + 1);
m_s3c240x_lcd.hpos = 0;
scanline = &bitmap.pix32(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
}
}
}
}
TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_lcd_timer_exp)
{
screen_device *screen = machine().first_screen();
verboselog( machine(), 2, "LCD timer callback\n");
m_s3c240x_lcd.vpos = screen->vpos();
m_s3c240x_lcd.hpos = screen->hpos();
verboselog( machine(), 3, "LCD - vpos %d hpos %d\n", m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos);
if (m_s3c240x_lcd.vramaddr_cur >= m_s3c240x_lcd.vramaddr_max)
{
s3c240x_lcd_dma_reload();
}
verboselog( machine(), 3, "LCD - vramaddr %08X\n", m_s3c240x_lcd.vramaddr_cur);
while (m_s3c240x_lcd.vramaddr_cur < m_s3c240x_lcd.vramaddr_max)
{
switch (m_s3c240x_lcd.bppmode)
{
case BPPMODE_TFT_01 : s3c240x_lcd_render_01(); break;
case BPPMODE_TFT_02 : s3c240x_lcd_render_02(); break;
case BPPMODE_TFT_04 : s3c240x_lcd_render_04(); break;
case BPPMODE_TFT_08 : s3c240x_lcd_render_08(); break;
case BPPMODE_TFT_16 : s3c240x_lcd_render_16(); break;
default : verboselog( machine(), 0, "s3c240x_lcd_timer_exp: bppmode %d not supported\n", m_s3c240x_lcd.bppmode); break;
}
if ((m_s3c240x_lcd.vpos == 0) && (m_s3c240x_lcd.hpos == 0)) break;
}
m_s3c240x_lcd_timer->adjust( screen->time_until_pos(m_s3c240x_lcd.vpos, m_s3c240x_lcd.hpos));
}
void gp32_state::video_start()
{
machine().first_screen()->register_screen_bitmap(m_bitmap);
}
UINT32 gp32_state::screen_update_gp32(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
s3c240x_lcd_dma_init();
return 0;
}
READ32_MEMBER(gp32_state::s3c240x_lcd_r)
{
UINT32 data = m_s3c240x_lcd_regs[offset];
switch (offset)
{
// LCDCON1
case 0x00 / 4 :
{
// make sure line counter is going
UINT32 lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
data = (data & ~0xFFFC0000) | ((lineval - machine().first_screen()->vpos()) << 18);
}
break;
}
verboselog( machine(), 9, "(LCD) %08X -> %08X (PC %08X)\n", 0x14A00000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
void gp32_state::s3c240x_lcd_configure()
{
screen_device *screen = machine().first_screen();
UINT32 vspw, vbpd, lineval, vfpd, hspw, hbpd, hfpd, hozval, clkval, hclk;
double framerate, vclk;
rectangle visarea;
vspw = BITS( m_s3c240x_lcd_regs[1], 5, 0);
vbpd = BITS( m_s3c240x_lcd_regs[1], 31, 24);
lineval = BITS( m_s3c240x_lcd_regs[1], 23, 14);
vfpd = BITS( m_s3c240x_lcd_regs[1], 13, 6);
hspw = BITS( m_s3c240x_lcd_regs[3], 7, 0);
hbpd = BITS( m_s3c240x_lcd_regs[2], 25, 19);
hfpd = BITS( m_s3c240x_lcd_regs[2], 7, 0);
hozval = BITS( m_s3c240x_lcd_regs[2], 18, 8);
clkval = BITS( m_s3c240x_lcd_regs[0], 17, 8);
hclk = s3c240x_get_hclk(MPLLCON);
verboselog( machine(), 3, "LCD - vspw %d vbpd %d lineval %d vfpd %d hspw %d hbpd %d hfpd %d hozval %d clkval %d hclk %d\n", vspw, vbpd, lineval, vfpd, hspw, hbpd, hfpd, hozval, clkval, hclk);
vclk = (double)(hclk / ((clkval + 1) * 2));
verboselog( machine(), 3, "LCD - vclk %f\n", vclk);
framerate = vclk / (((vspw + 1) + (vbpd + 1) + (lineval + 1) + (vfpd + 1)) * ((hspw + 1) + (hbpd + 1) + (hfpd + 1) + (hozval + 1)));
verboselog( machine(), 3, "LCD - framerate %f\n", framerate);
visarea.set(0, hozval, 0, lineval);
verboselog( machine(), 3, "LCD - visarea min_x %d min_y %d max_x %d max_y %d\n", visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
screen->configure(hozval + 1, lineval + 1, visarea, HZ_TO_ATTOSECONDS( framerate));
}
void gp32_state::s3c240x_lcd_start()
{
screen_device *screen = machine().first_screen();
verboselog( machine(), 1, "LCD start\n");
s3c240x_lcd_configure();
s3c240x_lcd_dma_init();
m_s3c240x_lcd_timer->adjust( screen->time_until_pos(0, 0));
}
void gp32_state::s3c240x_lcd_stop()
{
verboselog( machine(), 1, "LCD stop\n");
m_s3c240x_lcd_timer->adjust( attotime::never);
}
void gp32_state::s3c240x_lcd_recalc()
{
if (m_s3c240x_lcd_regs[0] & 1)
{
s3c240x_lcd_start();
}
else
{
s3c240x_lcd_stop();
}
}
WRITE32_MEMBER(gp32_state::s3c240x_lcd_w)
{
UINT32 old_value = m_s3c240x_lcd_regs[offset];
verboselog( machine(), 9, "(LCD) %08X <- %08X (PC %08X)\n", 0x14A00000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_lcd_regs[offset]);
switch (offset)
{
// LCDCON1
case 0x00 / 4 :
{
if ((old_value & 1) != (data & 1))
{
s3c240x_lcd_recalc();
}
}
break;
}
}
// LCD PALETTE
READ32_MEMBER(gp32_state::s3c240x_lcd_palette_r)
{
UINT32 data = m_s3c240x_lcd_palette[offset];
verboselog( machine(), 9, "(LCD) %08X -> %08X (PC %08X)\n", 0x14A00400 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_lcd_palette_w)
{
verboselog( machine(), 9, "(LCD) %08X <- %08X (PC %08X)\n", 0x14A00400 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_lcd_palette[offset]);
if (mem_mask != 0xffffffff)
{
verboselog( machine(), 0, "s3c240x_lcd_palette_w: unknown mask %08x\n", mem_mask);
}
m_palette->set_pen_color( offset, s3c240x_get_color_5551( data & 0xFFFF));
}
// CLOCK & POWER MANAGEMENT
UINT32 gp32_state::s3c240x_get_fclk(int reg)
{
UINT32 data, mdiv, pdiv, sdiv;
data = m_s3c240x_clkpow_regs[reg]; // MPLLCON or UPLLCON
mdiv = BITS( data, 19, 12);
pdiv = BITS( data, 9, 4);
sdiv = BITS( data, 1, 0);
return (UINT32)((double)((mdiv + 8) * 12000000) / (double)((pdiv + 2) * (1 << sdiv)));
}
UINT32 gp32_state::s3c240x_get_hclk(int reg)
{
switch (m_s3c240x_clkpow_regs[5] & 0x3) // CLKDIVN
{
case 0 : return s3c240x_get_fclk(reg) / 1;
case 1 : return s3c240x_get_fclk(reg) / 1;
case 2 : return s3c240x_get_fclk(reg) / 2;
case 3 : return s3c240x_get_fclk(reg) / 2;
}
return 0;
}
UINT32 gp32_state::s3c240x_get_pclk(int reg)
{
switch (m_s3c240x_clkpow_regs[5] & 0x3) // CLKDIVN
{
case 0 : return s3c240x_get_fclk(reg) / 1;
case 1 : return s3c240x_get_fclk(reg) / 2;
case 2 : return s3c240x_get_fclk(reg) / 2;
case 3 : return s3c240x_get_fclk(reg) / 4;
}
return 0;
}
READ32_MEMBER(gp32_state::s3c240x_clkpow_r)
{
UINT32 data = m_s3c240x_clkpow_regs[offset];
verboselog( machine(), 9, "(CLKPOW) %08X -> %08X (PC %08X)\n", 0x14800000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_clkpow_w)
{
verboselog( machine(), 9, "(CLKPOW) %08X <- %08X (PC %08X)\n", 0x14800000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_clkpow_regs[offset]);
switch (offset)
{
// MPLLCON
case 0x04 / 4 :
{
m_maincpu->set_unscaled_clock(s3c240x_get_fclk(MPLLCON) * CLOCK_MULTIPLIER);
}
break;
}
}
// INTERRUPT CONTROLLER
void gp32_state::s3c240x_check_pending_irq()
{
if (m_s3c240x_irq_regs[0] != 0)
{
UINT32 int_type = 0, temp;
temp = m_s3c240x_irq_regs[0];
while (!(temp & 1))
{
int_type++;
temp = temp >> 1;
}
m_s3c240x_irq_regs[4] |= (1 << int_type); // INTPND
m_s3c240x_irq_regs[5] = int_type; // INTOFFSET
m_maincpu->set_input_line(ARM7_IRQ_LINE, ASSERT_LINE);
}
else
{
m_maincpu->set_input_line(ARM7_IRQ_LINE, CLEAR_LINE);
}
}
void gp32_state::s3c240x_request_irq(UINT32 int_type)
{
verboselog( machine(), 5, "request irq %d\n", int_type);
if (m_s3c240x_irq_regs[0] == 0)
{
m_s3c240x_irq_regs[0] |= (1 << int_type); // SRCPND
m_s3c240x_irq_regs[4] |= (1 << int_type); // INTPND
m_s3c240x_irq_regs[5] = int_type; // INTOFFSET
m_maincpu->set_input_line(ARM7_IRQ_LINE, ASSERT_LINE);
}
else
{
m_s3c240x_irq_regs[0] |= (1 << int_type); // SRCPND
s3c240x_check_pending_irq();
}
}
READ32_MEMBER(gp32_state::s3c240x_irq_r)
{
UINT32 data = m_s3c240x_irq_regs[offset];
verboselog( machine(), 9, "(IRQ) %08X -> %08X (PC %08X)\n", 0x14400000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_irq_w)
{
UINT32 old_value = m_s3c240x_irq_regs[offset];
verboselog( machine(), 9, "(IRQ) %08X <- %08X (PC %08X)\n", 0x14400000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_irq_regs[offset]);
switch (offset)
{
// SRCPND
case 0x00 / 4 :
{
m_s3c240x_irq_regs[0] = (old_value & ~data); // clear only the bit positions of SRCPND corresponding to those set to one in the data
s3c240x_check_pending_irq();
}
break;
// INTPND
case 0x10 / 4 :
{
m_s3c240x_irq_regs[4] = (old_value & ~data); // clear only the bit positions of INTPND corresponding to those set to one in the data
}
break;
}
}
// PWM TIMER
#if 0
static const char *const timer_reg_names[] =
{
"Timer config 0",
"Timer config 1",
"Timer control",
"Timer count buffer 0",
"Timer compare buffer 0",
"Timer count observation 0",
"Timer count buffer 1",
"Timer compare buffer 1",
"Timer count observation 1",
"Timer count buffer 2",
"Timer compare buffer 2",
"Timer count observation 2",
"Timer count buffer 3",
"Timer compare buffer 3",
"Timer count observation 3",
"Timer count buffer 4",
"Timer compare buffer 4",
"Timer count observation 4",
};
#endif
READ32_MEMBER(gp32_state::s3c240x_pwm_r)
{
UINT32 data = m_s3c240x_pwm_regs[offset];
verboselog( machine(), 9, "(PWM) %08X -> %08X (PC %08X)\n", 0x15100000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
void gp32_state::s3c240x_pwm_start(int timer)
{
static const int mux_table[] = { 2, 4, 8, 16 };
static const int prescaler_shift[] = { 0, 0, 8, 8, 8 };
static const int mux_shift[] = { 0, 4, 8, 12, 16 };
static const int tcon_shift[] = { 0, 8, 12, 16, 20 };
const UINT32 *regs = &m_s3c240x_pwm_regs[3+timer*3];
UINT32 prescaler, mux, cnt, cmp, auto_reload;
double freq, hz;
verboselog( machine(), 1, "PWM %d start\n", timer);
prescaler = (m_s3c240x_pwm_regs[0] >> prescaler_shift[timer]) & 0xFF;
mux = (m_s3c240x_pwm_regs[1] >> mux_shift[timer]) & 0x0F;
freq = s3c240x_get_pclk(MPLLCON) / (prescaler + 1) / mux_table[mux];
cnt = BITS( regs[0], 15, 0);
if (timer != 4)
{
cmp = BITS( regs[1], 15, 0);
auto_reload = BIT( m_s3c240x_pwm_regs[2], tcon_shift[timer] + 3);
}
else
{
cmp = 0;
auto_reload = BIT( m_s3c240x_pwm_regs[2], tcon_shift[timer] + 2);
}
hz = freq / (cnt - cmp + 1);
verboselog( machine(), 5, "PWM %d - FCLK=%d HCLK=%d PCLK=%d prescaler=%d div=%d freq=%f cnt=%d cmp=%d auto_reload=%d hz=%f\n", timer, s3c240x_get_fclk(MPLLCON), s3c240x_get_hclk(MPLLCON), s3c240x_get_pclk(MPLLCON), prescaler, mux_table[mux], freq, cnt, cmp, auto_reload, hz);
if (auto_reload)
{
m_s3c240x_pwm_timer[timer]->adjust( attotime::from_hz( hz), timer, attotime::from_hz( hz));
}
else
{
m_s3c240x_pwm_timer[timer]->adjust( attotime::from_hz( hz), timer);
}
}
void gp32_state::s3c240x_pwm_stop(int timer)
{
verboselog( machine(), 1, "PWM %d stop\n", timer);
m_s3c240x_pwm_timer[timer]->adjust( attotime::never);
}
void gp32_state::s3c240x_pwm_recalc(int timer)
{
static const int tcon_shift[] = { 0, 8, 12, 16, 20 };
if (m_s3c240x_pwm_regs[2] & (1 << tcon_shift[timer]))
{
s3c240x_pwm_start(timer);
}
else
{
s3c240x_pwm_stop(timer);
}
}
WRITE32_MEMBER(gp32_state::s3c240x_pwm_w)
{
UINT32 old_value = m_s3c240x_pwm_regs[offset];
verboselog( machine(), 9, "(PWM) %08X <- %08X (PC %08X)\n", 0x15100000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_pwm_regs[offset]);
switch (offset)
{
// TCON
case 0x08 / 4 :
{
if ((data & 1) != (old_value & 1))
{
s3c240x_pwm_recalc(0);
}
if ((data & 0x100) != (old_value & 0x100))
{
s3c240x_pwm_recalc(1);
}
if ((data & 0x1000) != (old_value & 0x1000))
{
s3c240x_pwm_recalc(2);
}
if ((data & 0x10000) != (old_value & 0x10000))
{
s3c240x_pwm_recalc(3);
}
if ((data & 0x100000) != (old_value & 0x100000))
{
s3c240x_pwm_recalc(4);
}
}
}
}
TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_pwm_timer_exp)
{
int ch = param;
static const int ch_int[] = { INT_TIMER0, INT_TIMER1, INT_TIMER2, INT_TIMER3, INT_TIMER4 };
verboselog( machine(), 2, "PWM %d timer callback\n", ch);
if (BITS( m_s3c240x_pwm_regs[1], 23, 20) == (ch + 1))
{
s3c240x_dma_request_pwm();
}
else
{
s3c240x_request_irq(ch_int[ch]);
}
}
// DMA
void gp32_state::s3c240x_dma_reload(int dma)
{
UINT32 *regs = &m_s3c240x_dma_regs[dma<<3];
regs[3] = (regs[3] & ~0x000FFFFF) | BITS( regs[2], 19, 0);
regs[4] = (regs[4] & ~0x1FFFFFFF) | BITS( regs[0], 28, 0);
regs[5] = (regs[5] & ~0x1FFFFFFF) | BITS( regs[1], 28, 0);
}
void gp32_state::s3c240x_dma_trigger(int dma)
{
UINT32 *regs = &m_s3c240x_dma_regs[dma<<3];
UINT32 curr_tc, curr_src, curr_dst;
address_space &space = m_maincpu->space( AS_PROGRAM);
int dsz, inc_src, inc_dst, servmode;
static const UINT32 ch_int[] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };
verboselog( machine(), 5, "DMA %d trigger\n", dma);
curr_tc = BITS( regs[3], 19, 0);
curr_src = BITS( regs[4], 28, 0);
curr_dst = BITS( regs[5], 28, 0);
dsz = BITS( regs[2], 21, 20);
servmode = BIT( regs[2], 26);
inc_src = BIT( regs[0], 29);
inc_dst = BIT( regs[1], 29);
verboselog( machine(), 5, "DMA %d - curr_src %08X curr_dst %08X curr_tc %d dsz %d\n", dma, curr_src, curr_dst, curr_tc, dsz);
while (curr_tc > 0)
{
curr_tc--;
switch (dsz)
{
case 0 : space.write_byte( curr_dst, space.read_byte( curr_src)); break;
case 1 : space.write_word( curr_dst, space.read_word( curr_src)); break;
case 2 : space.write_dword( curr_dst, space.read_dword( curr_src)); break;
}
if (inc_src == 0) curr_src += (1 << dsz);
if (inc_dst == 0) curr_dst += (1 << dsz);
if (servmode == 0) break;
}
// update curr_src
regs[4] = (regs[4] & ~0x1FFFFFFF) | curr_src;
// update curr_dst
regs[5] = (regs[5] & ~0x1FFFFFFF) | curr_dst;
// update curr_tc
regs[3] = (regs[3] & ~0x000FFFFF) | curr_tc;
// ...
if (curr_tc == 0)
{
int _int, reload;
reload = BIT( regs[2], 22);
if (!reload)
{
s3c240x_dma_reload(dma);
}
else
{
regs[6] &= ~(1 << 1); // clear on/off
}
_int = BIT( regs[2], 28);
if (_int)
{
s3c240x_request_irq(ch_int[dma]);
}
}
}
void gp32_state::s3c240x_dma_request_iis()
{
UINT32 *regs = &m_s3c240x_dma_regs[2<<3];
verboselog( machine(), 5, "s3c240x_dma_request_iis\n");
if ((BIT( regs[6], 1) != 0) && (BIT( regs[2], 23) != 0) && (BITS( regs[2], 25, 24) == 0))
{
s3c240x_dma_trigger(2);
}
}
void gp32_state::s3c240x_dma_request_pwm()
{
int i;
verboselog( machine(), 5, "s3c240x_dma_request_pwm\n");
for (i = 0; i < 4; i++)
{
if (i != 1)
{
UINT32 *regs = &m_s3c240x_dma_regs[i<<3];
if ((BIT( regs[6], 1) != 0) && (BIT( regs[2], 23) != 0) && (BITS( regs[2], 25, 24) == 3))
{
s3c240x_dma_trigger(i);
}
}
}
}
void gp32_state::s3c240x_dma_start(int dma)
{
UINT32 addr_src, addr_dst, tc;
UINT32 *regs = &m_s3c240x_dma_regs[dma<<3];
UINT32 dsz, tsz, reload;
int inc_src, inc_dst, _int, servmode, swhwsel, hwsrcsel;
verboselog( machine(), 1, "DMA %d start\n", dma);
addr_src = BITS( regs[0], 28, 0);
addr_dst = BITS( regs[1], 28, 0);
tc = BITS( regs[2], 19, 0);
inc_src = BIT( regs[0], 29);
inc_dst = BIT( regs[1], 29);
tsz = BIT( regs[2], 27);
_int = BIT( regs[2], 28);
servmode = BIT( regs[2], 26);
hwsrcsel = BITS( regs[2], 25, 24);
swhwsel = BIT( regs[2], 23);
reload = BIT( regs[2], 22);
dsz = BITS( regs[2], 21, 20);
verboselog( machine(), 5, "DMA %d - addr_src %08X inc_src %d addr_dst %08X inc_dst %d int %d tsz %d servmode %d hwsrcsel %d swhwsel %d reload %d dsz %d tc %d\n", dma, addr_src, inc_src, addr_dst, inc_dst, _int, tsz, servmode, hwsrcsel, swhwsel, reload, dsz, tc);
verboselog( machine(), 5, "DMA %d - copy %08X bytes from %08X (%s) to %08X (%s)\n", dma, tc << dsz, addr_src, inc_src ? "fix" : "inc", addr_dst, inc_dst ? "fix" : "inc");
s3c240x_dma_reload(dma);
if (swhwsel == 0)
{
s3c240x_dma_trigger(dma);
}
}
void gp32_state::s3c240x_dma_stop(int dma)
{
verboselog( machine(), 1, "DMA %d stop\n", dma);
}
void gp32_state::s3c240x_dma_recalc(int dma)
{
if (m_s3c240x_dma_regs[(dma<<3)+6] & 2)
{
s3c240x_dma_start(dma);
}
else
{
s3c240x_dma_stop(dma);
}
}
READ32_MEMBER(gp32_state::s3c240x_dma_r)
{
UINT32 data = m_s3c240x_dma_regs[offset];
verboselog( machine(), 9, "(DMA) %08X -> %08X (PC %08X)\n", 0x14600000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_dma_w)
{
UINT32 old_value = m_s3c240x_dma_regs[offset];
verboselog( machine(), 9, "(DMA) %08X <- %08X (PC %08X)\n", 0x14600000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_dma_regs[offset]);
switch (offset)
{
// DCON0
case 0x08 / 4 :
{
if (((data >> 22) & 1) != 0) // reload
{
m_s3c240x_dma_regs[0x18/4] &= ~(1 << 1); // clear on/off
}
}
break;
// DMASKTRIG0
case 0x18 / 4 :
{
if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(0);
}
break;
// DCON1
case 0x28 / 4 :
{
if (((data >> 22) & 1) != 0) // reload
{
m_s3c240x_dma_regs[0x38/4] &= ~(1 << 1); // clear on/off
}
}
break;
// DMASKTRIG1
case 0x38 / 4 :
{
if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(1);
}
break;
// DCON2
case 0x48 / 4 :
{
if (((data >> 22) & 1) != 0) // reload
{
m_s3c240x_dma_regs[0x58/4] &= ~(1 << 1); // clear on/off
}
}
break;
// DMASKTRIG2
case 0x58 / 4 :
{
if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(2);
}
break;
// DCON3
case 0x68 / 4 :
{
if (((data >> 22) & 1) != 0) // reload
{
m_s3c240x_dma_regs[0x78/4] &= ~(1 << 1); // clear on/off
}
}
break;
// DMASKTRIG3
case 0x78 / 4 :
{
if ((old_value & 2) != (data & 2)) s3c240x_dma_recalc(3);
}
break;
}
}
TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_dma_timer_exp)
{
int ch = param;
verboselog( machine(), 2, "DMA %d timer callback\n", ch);
}
// SMARTMEDIA
void gp32_state::smc_reset()
{
verboselog( machine(), 5, "smc_reset\n");
m_smc.add_latch = 0;
m_smc.chip = 0;
m_smc.cmd_latch = 0;
m_smc.do_read = 0;
m_smc.do_write = 0;
m_smc.read = 0;
m_smc.wp = 0;
m_smc.busy = 0;
}
void gp32_state::smc_init()
{
verboselog( machine(), 5, "smc_init\n");
smc_reset();
}
UINT8 gp32_state::smc_read()
{
UINT8 data;
data = m_smartmedia->data_r();
verboselog( machine(), 5, "smc_read %08X\n", data);
return data;
}
void gp32_state::smc_write(UINT8 data)
{
verboselog( machine(), 5, "smc_write %08X\n", data);
if ((m_smc.chip) && (!m_smc.read))
{
if (m_smc.cmd_latch)
{
verboselog( machine(), 5, "smartmedia_command_w %08X\n", data);
m_smartmedia->command_w(data);
}
else if (m_smc.add_latch)
{
verboselog( machine(), 5, "smartmedia_address_w %08X\n", data);
m_smartmedia->address_w(data);
}
else
{
verboselog( machine(), 5, "smartmedia_data_w %08X\n", data);
m_smartmedia->data_w(data);
}
}
}
void gp32_state::smc_update()
{
if (!m_smc.chip)
{
smc_reset();
}
else
{
if ((m_smc.do_write) && (!m_smc.read))
{
smc_write(m_smc.datatx);
}
else if ((!m_smc.do_write) && (m_smc.do_read) && (m_smc.read) && (!m_smc.cmd_latch) && (!m_smc.add_latch))
{
m_smc.datarx = smc_read();
}
}
}
// I2S
#define I2S_L3C ( 1 )
#define I2S_L3M ( 2 )
#define I2S_L3D ( 3 )
void gp32_state::i2s_reset()
{
verboselog( machine(), 5, "i2s_reset\n");
m_i2s.l3d = 0;
m_i2s.l3m = 0;
m_i2s.l3c = 0;
}
void gp32_state::i2s_init()
{
verboselog( machine(), 5, "i2s_init\n");
i2s_reset();
}
void gp32_state::i2s_write(int line, int data)
{
switch (line)
{
case I2S_L3C :
{
if (data != m_i2s.l3c)
{
verboselog( machine(), 5, "I2S L3C %d\n", data);
m_i2s.l3c = data;
}
}
break;
case I2S_L3M :
{
if (data != m_i2s.l3m)
{
verboselog( machine(), 5, "I2S L3M %d\n", data);
m_i2s.l3m = data;
}
}
break;
case I2S_L3D :
{
if (data != m_i2s.l3d)
{
verboselog( machine(), 5, "I2S L3D %d\n", data);
m_i2s.l3d = data;
}
}
break;
}
}
// I/O PORT
READ32_MEMBER(gp32_state::s3c240x_gpio_r)
{
UINT32 data = m_s3c240x_gpio[offset];
switch (offset)
{
// PBCON
case 0x08 / 4 :
{
// smartmedia
data = (data & ~0x00000001);
if (!m_smc.read) data = data | 0x00000001;
}
break;
// PBDAT
case 0x0C / 4 :
{
// smartmedia
data = (data & ~0x000000FF) | (m_smc.datarx & 0xFF);
// buttons
data = (data & ~0x0000FF00) | (m_io_in0->read() & 0x0000FF00);
}
break;
// PDDAT
case 0x24 / 4 :
{
// smartmedia
data = (data & ~0x000003C0);
if (!m_smc.busy) data = data | 0x00000200;
if (!m_smc.do_read) data = data | 0x00000100;
if (!m_smc.chip) data = data | 0x00000080;
if (!m_smartmedia->is_protected()) data = data | 0x00000040;
}
break;
// PEDAT
case 0x30 / 4 :
{
// smartmedia
data = (data & ~0x0000003C);
if (m_smc.cmd_latch) data = data | 0x00000020;
if (m_smc.add_latch) data = data | 0x00000010;
if (!m_smc.do_write) data = data | 0x00000008;
if (!m_smartmedia->is_present()) data = data | 0x00000004;
// buttons
data = (data & ~0x000000C0) | (m_io_in1->read() & 0x000000C0);
}
break;
}
verboselog( machine(), 9, "(GPIO) %08X -> %08X (PC %08X)\n", 0x15600000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_gpio_w)
{
COMBINE_DATA(&m_s3c240x_gpio[offset]);
verboselog( machine(), 9, "(GPIO) %08X <- %08X (PC %08X)\n", 0x15600000 + (offset << 2), data, space.device().safe_pc( ));
switch (offset)
{
// PBCON
case 0x08 / 4 :
{
// smartmedia
m_smc.read = ((data & 0x00000001) == 0);
smc_update();
}
break;
// PBDAT
case 0x0C / 4 :
{
// smartmedia
m_smc.datatx = data & 0xFF;
}
break;
// PDDAT
case 0x24 / 4 :
{
// smartmedia
m_smc.do_read = ((data & 0x00000100) == 0);
m_smc.chip = ((data & 0x00000080) == 0);
m_smc.wp = ((data & 0x00000040) == 0);
smc_update();
}
break;
// PEDAT
case 0x30 / 4 :
{
// smartmedia
m_smc.cmd_latch = ((data & 0x00000020) != 0);
m_smc.add_latch = ((data & 0x00000010) != 0);
m_smc.do_write = ((data & 0x00000008) == 0);
smc_update();
// sound
i2s_write(I2S_L3D, (data & 0x00000800) ? 1 : 0);
i2s_write(I2S_L3M, (data & 0x00000400) ? 1 : 0);
i2s_write(I2S_L3C, (data & 0x00000200) ? 1 : 0);
}
break;
#if 0
// PGDAT
case 0x48 / 4 :
{
int i2ssdo;
i2ssdo = BIT( data, 3);
}
break;
#endif
}
}
// MEMORY CONTROLLER
READ32_MEMBER(gp32_state::s3c240x_memcon_r)
{
UINT32 data = m_s3c240x_memcon_regs[offset];
verboselog( machine(), 9, "(MEMCON) %08X -> %08X (PC %08X)\n", 0x14000000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_memcon_w)
{
verboselog( machine(), 9, "(MEMCON) %08X <- %08X (PC %08X)\n", 0x14000000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_memcon_regs[offset]);
}
// USB HOST CONTROLLER
READ32_MEMBER(gp32_state::s3c240x_usb_host_r)
{
UINT32 data = m_s3c240x_usb_host_regs[offset];
verboselog( machine(), 9, "(USB H) %08X -> %08X (PC %08X)\n", 0x14200000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_usb_host_w)
{
verboselog( machine(), 9, "(USB H) %08X <- %08X (PC %08X)\n", 0x14200000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_usb_host_regs[offset]);
}
// UART 0
READ32_MEMBER(gp32_state::s3c240x_uart_0_r)
{
UINT32 data = m_s3c240x_uart_0_regs[offset];
switch (offset)
{
// UTRSTAT0
case 0x10 / 4 :
{
data = (data & ~0x00000006) | 0x00000004 | 0x00000002; // [bit 2] Transmitter empty / [bit 1] Transmit buffer empty
}
break;
}
verboselog( machine(), 9, "(UART 0) %08X -> %08X (PC %08X)\n", 0x15000000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_uart_0_w)
{
verboselog( machine(), 9, "(UART 0) %08X <- %08X (PC %08X)\n", 0x15000000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_uart_0_regs[offset]);
}
// UART 1
READ32_MEMBER(gp32_state::s3c240x_uart_1_r)
{
UINT32 data = m_s3c240x_uart_1_regs[offset];
switch (offset)
{
// UTRSTAT1
case 0x10 / 4 :
{
data = (data & ~0x00000006) | 0x00000004 | 0x00000002; // [bit 2] Transmitter empty / [bit 1] Transmit buffer empty
}
break;
}
verboselog( machine(), 9, "(UART 1) %08X -> %08X (PC %08X)\n", 0x15004000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_uart_1_w)
{
verboselog( machine(), 9, "(UART 1) %08X <- %08X (PC %08X)\n", 0x15004000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_uart_1_regs[offset]);
}
// USB DEVICE
READ32_MEMBER(gp32_state::s3c240x_usb_device_r)
{
UINT32 data = m_s3c240x_usb_device_regs[offset];
verboselog( machine(), 9, "(USB D) %08X -> %08X (PC %08X)\n", 0x15200140 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_usb_device_w)
{
verboselog( machine(), 9, "(USB D) %08X <- %08X (PC %08X)\n", 0x15200140 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_usb_device_regs[offset]);
}
// WATCHDOG TIMER
READ32_MEMBER(gp32_state::s3c240x_watchdog_r)
{
UINT32 data = m_s3c240x_watchdog_regs[offset];
verboselog( machine(), 9, "(WDOG) %08X -> %08X (PC %08X)\n", 0x15300000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_watchdog_w)
{
verboselog( machine(), 9, "(WDOG) %08X <- %08X (PC %08X)\n", 0x15300000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_watchdog_regs[offset]);
}
// EEPROM
UINT8 gp32_state::eeprom_read(UINT16 address)
{
UINT8 data;
data = m_eeprom_data[address];
verboselog( machine(), 5, "EEPROM %04X -> %02X\n", address, data);
return data;
}
void gp32_state::eeprom_write(UINT16 address, UINT8 data)
{
verboselog( machine(), 5, "EEPROM %04X <- %02X\n", address, data);
m_eeprom_data[address] = data;
}
// IIC
#if 0
UINT8 gp32_state::i2cmem_read_byte( int last)
{
UINT8 data = 0;
int i;
i2cmem_write( machine, 0, I2CMEM_SDA, 1);
for (i = 0; i < 8; i++)
{
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
data = (data << 1) + (i2cmem_read( machine, 0, I2CMEM_SDA) ? 1 : 0);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
i2cmem_write( machine, 0, I2CMEM_SDA, last);
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
return data;
}
#endif
#if 0
void gp32_state::i2cmem_write_byte( UINT8 data)
{
int i;
for (i = 0; i < 8; i++)
{
i2cmem_write( machine, 0, I2CMEM_SDA, (data & 0x80) ? 1 : 0);
data = data << 1;
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
i2cmem_write( machine, 0, I2CMEM_SDA, 1); // ack bit
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif
#if 0
void gp32_state::i2cmem_start( )
{
i2cmem_write( machine, 0, I2CMEM_SDA, 1);
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
i2cmem_write( machine, 0, I2CMEM_SDA, 0);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif
#if 0
void gp32_state::i2cmem_stop( )
{
i2cmem_write( machine, 0, I2CMEM_SDA, 0);
i2cmem_write( machine, 0, I2CMEM_SCL, 1);
i2cmem_write( machine, 0, I2CMEM_SDA, 1);
i2cmem_write( machine, 0, I2CMEM_SCL, 0);
}
#endif
void gp32_state::iic_start()
{
verboselog( machine(), 1, "IIC start\n");
m_s3c240x_iic.data_index = 0;
m_s3c240x_iic_timer->adjust( attotime::from_msec( 1));
}
void gp32_state::iic_stop()
{
verboselog( machine(), 1, "IIC stop\n");
m_s3c240x_iic_timer->adjust( attotime::never);
}
void gp32_state::iic_resume()
{
verboselog( machine(), 1, "IIC resume\n");
m_s3c240x_iic_timer->adjust( attotime::from_msec( 1));
}
READ32_MEMBER(gp32_state::s3c240x_iic_r)
{
UINT32 data = m_s3c240x_iic_regs[offset];
switch (offset)
{
// IICSTAT
case 0x04 / 4 :
{
data = data & ~0x0000000F;
}
break;
}
verboselog( machine(), 9, "(IIC) %08X -> %08X (PC %08X)\n", 0x15400000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_iic_w)
{
verboselog( machine(), 9, "(IIC) %08X <- %08X (PC %08X)\n", 0x15400000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_iic_regs[offset]);
switch (offset)
{
// ADDR_IICCON
case 0x00 / 4 :
{
int interrupt_pending_flag;
#if 0
static const int div_table[] = { 16, 512 };
int enable_interrupt, transmit_clock_value, tx_clock_source_selection
double clock;
transmit_clock_value = (data >> 0) & 0xF;
tx_clock_source_selection = (data >> 6) & 1;
enable_interrupt = (data >> 5) & 1;
clock = (double)(s3c240x_get_pclk(MPLLCON) / div_table[tx_clock_source_selection] / (transmit_clock_value + 1));
#endif
interrupt_pending_flag = BIT( data, 4);
if (interrupt_pending_flag == 0)
{
int start_stop_condition;
start_stop_condition = BIT( m_s3c240x_iic_regs[1], 5);
if (start_stop_condition != 0)
{
iic_resume();
}
}
}
break;
// IICSTAT
case 0x04 / 4 :
{
int start_stop_condition;
start_stop_condition = BIT( data, 5);
if (start_stop_condition != 0)
{
iic_start();
}
else
{
iic_stop();
}
}
break;
}
}
TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_iic_timer_exp)
{
int enable_interrupt, mode_selection;
verboselog( machine(), 2, "IIC timer callback\n");
mode_selection = BITS( m_s3c240x_iic_regs[1], 7, 6);
switch (mode_selection)
{
// master receive mode
case 2 :
{
if (m_s3c240x_iic.data_index == 0)
{
UINT8 data_shift = m_s3c240x_iic_regs[3] & 0xFF;
verboselog( machine(), 5, "IIC write %02X\n", data_shift);
}
else
{
UINT8 data_shift = eeprom_read(m_s3c240x_iic.address);
verboselog( machine(), 5, "IIC read %02X\n", data_shift);
m_s3c240x_iic_regs[3] = (m_s3c240x_iic_regs[3] & ~0xFF) | data_shift;
}
m_s3c240x_iic.data_index++;
}
break;
// master transmit mode
case 3 :
{
UINT8 data_shift = m_s3c240x_iic_regs[3] & 0xFF;
verboselog( machine(), 5, "IIC write %02X\n", data_shift);
m_s3c240x_iic.data[m_s3c240x_iic.data_index++] = data_shift;
if (m_s3c240x_iic.data_index == 3)
{
m_s3c240x_iic.address = (m_s3c240x_iic.data[1] << 8) | m_s3c240x_iic.data[2];
}
if ((m_s3c240x_iic.data_index == 4) && (m_s3c240x_iic.data[0] == 0xA0))
{
eeprom_write(m_s3c240x_iic.address, data_shift);
}
}
break;
}
enable_interrupt = BIT( m_s3c240x_iic_regs[0], 5);
if (enable_interrupt)
{
s3c240x_request_irq(INT_IIC);
}
}
// IIS
void gp32_state::s3c240x_iis_start()
{
static const UINT32 codeclk_table[] = { 256, 384 };
double freq;
int prescaler_enable, prescaler_control_a, prescaler_control_b, codeclk;
verboselog( machine(), 1, "IIS start\n");
prescaler_enable = BIT( m_s3c240x_iis_regs[0], 1);
prescaler_control_a = BITS( m_s3c240x_iis_regs[2], 9, 5);
prescaler_control_b = BITS( m_s3c240x_iis_regs[2], 4, 0);
codeclk = BIT( m_s3c240x_iis_regs[1], 2);
freq = (double)(s3c240x_get_pclk(MPLLCON) / (prescaler_control_a + 1) / codeclk_table[codeclk]) * 2; // why do I have to multiply by two?
verboselog( machine(), 5, "IIS - pclk %d psc_enable %d psc_a %d psc_b %d codeclk %d freq %f\n", s3c240x_get_pclk(MPLLCON), prescaler_enable, prescaler_control_a, prescaler_control_b, codeclk_table[codeclk], freq);
m_s3c240x_iis_timer->adjust( attotime::from_hz( freq), 0, attotime::from_hz( freq));
}
void gp32_state::s3c240x_iis_stop()
{
verboselog( machine(), 1, "IIS stop\n");
m_s3c240x_iis_timer->adjust( attotime::never);
}
void gp32_state::s3c240x_iis_recalc()
{
if (m_s3c240x_iis_regs[0] & 1)
{
s3c240x_iis_start();
}
else
{
s3c240x_iis_stop();
}
}
READ32_MEMBER(gp32_state::s3c240x_iis_r)
{
UINT32 data = m_s3c240x_iis_regs[offset];
#if 0
switch (offset)
{
// IISCON
case 0x00 / 4 :
{
data = data & ~1; // for mp3 player
}
break;
}
#endif
verboselog( machine(), 9, "(IIS) %08X -> %08X (PC %08X)\n", 0x15508000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_iis_w)
{
UINT32 old_value = m_s3c240x_iis_regs[offset];
verboselog( machine(), 9, "(IIS) %08X <- %08X (PC %08X)\n", 0x15508000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_iis_regs[offset]);
switch (offset)
{
// IISCON
case 0x00 / 4 :
{
if ((old_value & 1) != (data & 1)) s3c240x_iis_recalc();
}
break;
// IISFIF
case 0x10 / 4 :
{
if (ACCESSING_BITS_16_31)
{
m_s3c240x_iis.fifo[m_s3c240x_iis.fifo_index++] = BITS( data, 31, 16);
}
if (ACCESSING_BITS_0_15)
{
m_s3c240x_iis.fifo[m_s3c240x_iis.fifo_index++] = BITS( data, 15, 0);
}
if (m_s3c240x_iis.fifo_index == 2)
{
m_s3c240x_iis.fifo_index = 0;
m_dac1->write_signed16(m_s3c240x_iis.fifo[0] + 0x8000);
m_dac2->write_signed16(m_s3c240x_iis.fifo[1] + 0x8000);
}
}
break;
}
}
TIMER_CALLBACK_MEMBER(gp32_state::s3c240x_iis_timer_exp)
{
verboselog( machine(), 2, "IIS timer callback\n");
s3c240x_dma_request_iis();
}
// RTC
READ32_MEMBER(gp32_state::s3c240x_rtc_r)
{
UINT32 data = m_s3c240x_rtc_regs[offset];
verboselog( machine(), 9, "(RTC) %08X -> %08X (PC %08X)\n", 0x15700040 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_rtc_w)
{
verboselog( machine(), 9, "(RTC) %08X <- %08X (PC %08X)\n", 0x15700040 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_rtc_regs[offset]);
}
// A/D CONVERTER
READ32_MEMBER(gp32_state::s3c240x_adc_r)
{
UINT32 data = m_s3c240x_adc_regs[offset];
verboselog( machine(), 9, "(ADC) %08X -> %08X (PC %08X)\n", 0x15800000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_adc_w)
{
verboselog( machine(), 9, "(ADC) %08X <- %08X (PC %08X)\n", 0x15800000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_adc_regs[offset]);
}
// SPI
READ32_MEMBER(gp32_state::s3c240x_spi_r)
{
UINT32 data = m_s3c240x_spi_regs[offset];
verboselog( machine(), 9, "(SPI) %08X -> %08X (PC %08X)\n", 0x15900000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_spi_w)
{
verboselog( machine(), 9, "(SPI) %08X <- %08X (PC %08X)\n", 0x15900000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_spi_regs[offset]);
}
// MMC INTERFACE
READ32_MEMBER(gp32_state::s3c240x_mmc_r)
{
UINT32 data = m_s3c240x_mmc_regs[offset];
verboselog( machine(), 9, "(MMC) %08X -> %08X (PC %08X)\n", 0x15A00000 + (offset << 2), data, space.device().safe_pc( ));
return data;
}
WRITE32_MEMBER(gp32_state::s3c240x_mmc_w)
{
verboselog( machine(), 9, "(MMC) %08X <- %08X (PC %08X)\n", 0x15A00000 + (offset << 2), data, space.device().safe_pc( ));
COMBINE_DATA(&m_s3c240x_mmc_regs[offset]);
}
// ...
void gp32_state::s3c240x_machine_start()
{
m_s3c240x_pwm_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_pwm_timer_exp),this), (void *)(FPTR)0);
m_s3c240x_pwm_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_pwm_timer_exp),this), (void *)(FPTR)1);
m_s3c240x_pwm_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_pwm_timer_exp),this), (void *)(FPTR)2);
m_s3c240x_pwm_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_pwm_timer_exp),this), (void *)(FPTR)3);
m_s3c240x_pwm_timer[4] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_pwm_timer_exp),this), (void *)(FPTR)4);
m_s3c240x_dma_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_dma_timer_exp),this), (void *)(FPTR)0);
m_s3c240x_dma_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_dma_timer_exp),this), (void *)(FPTR)1);
m_s3c240x_dma_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_dma_timer_exp),this), (void *)(FPTR)2);
m_s3c240x_dma_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_dma_timer_exp),this), (void *)(FPTR)3);
m_s3c240x_iic_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_iic_timer_exp),this), (void *)(FPTR)0);
m_s3c240x_iis_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_iis_timer_exp),this), (void *)(FPTR)0);
m_s3c240x_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gp32_state::s3c240x_lcd_timer_exp),this), (void *)(FPTR)0);
m_eeprom_data = auto_alloc_array( machine(), UINT8, 0x2000);
m_nvram->set_base(m_eeprom_data, 0x2000);
smc_init();
i2s_init();
}
void gp32_state::s3c240x_machine_reset()
{
smc_reset();
i2s_reset();
m_s3c240x_iis.fifo_index = 0;
m_s3c240x_iic.data_index = 0;
}
static ADDRESS_MAP_START( gp32_map, AS_PROGRAM, 32, gp32_state )
AM_RANGE(0x00000000, 0x0007ffff) AM_ROM
AM_RANGE(0x0c000000, 0x0c7fffff) AM_RAM AM_SHARE("s3c240x_ram")
AM_RANGE(0x14000000, 0x1400003b) AM_READWRITE(s3c240x_memcon_r, s3c240x_memcon_w)
AM_RANGE(0x14200000, 0x1420005b) AM_READWRITE(s3c240x_usb_host_r, s3c240x_usb_host_w)
AM_RANGE(0x14400000, 0x14400017) AM_READWRITE(s3c240x_irq_r, s3c240x_irq_w)
AM_RANGE(0x14600000, 0x1460007b) AM_READWRITE(s3c240x_dma_r, s3c240x_dma_w)
AM_RANGE(0x14800000, 0x14800017) AM_READWRITE(s3c240x_clkpow_r, s3c240x_clkpow_w)
AM_RANGE(0x14a00000, 0x14a003ff) AM_READWRITE(s3c240x_lcd_r, s3c240x_lcd_w)
AM_RANGE(0x14a00400, 0x14a007ff) AM_READWRITE(s3c240x_lcd_palette_r, s3c240x_lcd_palette_w)
AM_RANGE(0x15000000, 0x1500002b) AM_READWRITE(s3c240x_uart_0_r, s3c240x_uart_0_w)
AM_RANGE(0x15004000, 0x1500402b) AM_READWRITE(s3c240x_uart_1_r, s3c240x_uart_1_w)
AM_RANGE(0x15100000, 0x15100043) AM_READWRITE(s3c240x_pwm_r, s3c240x_pwm_w)
AM_RANGE(0x15200140, 0x152001fb) AM_READWRITE(s3c240x_usb_device_r, s3c240x_usb_device_w)
AM_RANGE(0x15300000, 0x1530000b) AM_READWRITE(s3c240x_watchdog_r, s3c240x_watchdog_w)
AM_RANGE(0x15400000, 0x1540000f) AM_READWRITE(s3c240x_iic_r, s3c240x_iic_w)
AM_RANGE(0x15508000, 0x15508013) AM_READWRITE(s3c240x_iis_r, s3c240x_iis_w)
AM_RANGE(0x15600000, 0x1560005b) AM_READWRITE(s3c240x_gpio_r, s3c240x_gpio_w)
AM_RANGE(0x15700040, 0x1570008b) AM_READWRITE(s3c240x_rtc_r, s3c240x_rtc_w)
AM_RANGE(0x15800000, 0x15800007) AM_READWRITE(s3c240x_adc_r, s3c240x_adc_w)
AM_RANGE(0x15900000, 0x15900017) AM_READWRITE(s3c240x_spi_r, s3c240x_spi_w)
AM_RANGE(0x15a00000, 0x15a0003f) AM_READWRITE(s3c240x_mmc_r, s3c240x_mmc_w)
ADDRESS_MAP_END
static INPUT_PORTS_START( gp32 )
PORT_START("IN0")
PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("R") PORT_PLAYER(1)
PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("L") PORT_PLAYER(1)
PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B") PORT_PLAYER(1)
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A") PORT_PLAYER(1)
PORT_START("IN1")
PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_SELECT ) PORT_NAME("SELECT") PORT_PLAYER(1)
PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("START") PORT_PLAYER(1)
INPUT_PORTS_END
void gp32_state::machine_start()
{
s3c240x_machine_start();
}
void gp32_state::machine_reset()
{
s3c240x_machine_reset();
}
static MACHINE_CONFIG_START( gp32, gp32_state )
MCFG_CPU_ADD("maincpu", ARM9, 40000000)
MCFG_CPU_PROGRAM_MAP(gp32_map)
MCFG_PALETTE_ADD("palette", 32768)
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
MCFG_SCREEN_SIZE(240, 320)
MCFG_SCREEN_VISIBLE_AREA(0, 239, 0, 319)
MCFG_SCREEN_UPDATE_DRIVER(gp32_state, screen_update_gp32)
/* 320x240 is 4:3 but ROT270 causes an aspect ratio of 3:4 by default */
MCFG_DEFAULT_LAYOUT(layout_lcd_rot)
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("dac1", DAC, 0)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0)
MCFG_SOUND_ADD("dac2", DAC, 0)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0)
MCFG_NVRAM_ADD_1FILL("nvram")
MCFG_DEVICE_ADD("smartmedia", SMARTMEDIA, 0)
MCFG_SOFTWARE_LIST_ADD("memc_list","gp32")
MACHINE_CONFIG_END
ROM_START( gp32 )
ROM_REGION( 0x80000, "maincpu", 0 )
ROM_SYSTEM_BIOS( 0, "157e", "Firmware 1.5.7 (English)" )
ROMX_LOAD( "gp32157e.bin", 0x000000, 0x080000, CRC(b1e35643) SHA1(1566bc2a27980602e9eb501cf8b2d62939bfd1e5), ROM_BIOS(1) )
ROM_SYSTEM_BIOS( 1, "100k", "Firmware 1.0.0 (Korean)" )
ROMX_LOAD( "gp32100k.bin", 0x000000, 0x080000, CRC(d9925ac9) SHA1(3604d0d7210ed72eddd3e3e0c108f1102508423c), ROM_BIOS(2) )
ROM_SYSTEM_BIOS( 2, "156k", "Firmware 1.5.6 (Korean)" )
ROMX_LOAD( "gp32156k.bin", 0x000000, 0x080000, CRC(667fb1c8) SHA1(d179ab8e96411272b6a1d683e59da752067f9da8), ROM_BIOS(3) )
ROM_SYSTEM_BIOS( 3, "166m", "Firmware 1.6.6 (European)" )
ROMX_LOAD( "gp32166m.bin", 0x000000, 0x080000, CRC(4548a840) SHA1(1ad0cab0af28fb45c182e5e8c87ead2aaa4fffe1), ROM_BIOS(4) )
ROM_SYSTEM_BIOS( 4, "mfv2", "Mr. Spiv Multi Firmware V2" )
ROMX_LOAD( "gp32mfv2.bin", 0x000000, 0x080000, CRC(7ddaaaeb) SHA1(5a85278f721beb3b00125db5c912d1dc552c5897), ROM_BIOS(5) )
#if 0
ROM_SYSTEM_BIOS( 5, "test", "test" )
ROMX_LOAD( "test.bin", 0x000000, 0x080000, CRC(00000000) SHA1(0000000000000000000000000000000000000000), ROM_BIOS(6) )
#endif
ROM_END
CONS(2001, gp32, 0, 0, gp32, gp32, driver_device, 0, "Game Park Holdings", "GP32", ROT270|GAME_NOT_WORKING|GAME_NO_SOUND)