// license:BSD-3-Clause // copyright-holders:Tim Schuerewegen /************************************************************************** * * gp32.c - Game Park GP32 * Driver by Tim Schuerewegen * * 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" #include "softlist.h" #define VERBOSE_LEVEL ( 0 ) static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, 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); device.logerror( "%s: %s", device.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(*this, 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(*this, 2, "LCD timer callback\n"); m_s3c240x_lcd.vpos = screen->vpos(); m_s3c240x_lcd.hpos = screen->hpos(); verboselog(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 3, "LCD - framerate %f\n", framerate); visarea.set(0, hozval, 0, lineval); verboselog(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 2, "DMA %d timer callback\n", ch); } // SMARTMEDIA void gp32_state::smc_reset() { verboselog(*this, 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(*this, 5, "smc_init\n"); smc_reset(); } UINT8 gp32_state::smc_read() { UINT8 data; data = m_smartmedia->data_r(); verboselog(*this, 5, "smc_read %08X\n", data); return data; } void gp32_state::smc_write(UINT8 data) { verboselog(*this, 5, "smc_write %08X\n", data); if ((m_smc.chip) && (!m_smc.read)) { if (m_smc.cmd_latch) { verboselog(*this, 5, "smartmedia_command_w %08X\n", data); m_smartmedia->command_w(data); } else if (m_smc.add_latch) { verboselog(*this, 5, "smartmedia_address_w %08X\n", data); m_smartmedia->address_w(data); } else { verboselog(*this, 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(*this, 5, "i2s_reset\n"); m_i2s.l3d = 0; m_i2s.l3m = 0; m_i2s.l3c = 0; } void gp32_state::i2s_init() { verboselog(*this, 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(*this, 5, "I2S L3C %d\n", data); m_i2s.l3c = data; } } break; case I2S_L3M : { if (data != m_i2s.l3m) { verboselog(*this, 5, "I2S L3M %d\n", data); m_i2s.l3m = data; } } break; case I2S_L3D : { if (data != m_i2s.l3d) { verboselog(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 5, "EEPROM %04X -> %02X\n", address, data); return data; } void gp32_state::eeprom_write(UINT16 address, UINT8 data) { verboselog(*this, 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(*this, 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(*this, 1, "IIC stop\n"); m_s3c240x_iic_timer->adjust( attotime::never); } void gp32_state::iic_resume() { verboselog(*this, 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(*this, 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(*this, 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(*this, 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(*this, 5, "IIC write %02X\n", data_shift); } else { UINT8 data_shift = eeprom_read(m_s3c240x_iic.address); verboselog(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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(*this, 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 = std::make_unique(0x2000); m_nvram->set_base(m_eeprom_data.get(), 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|MACHINE_NOT_WORKING|MACHINE_NO_SOUND)