/*************************************************************************** machine/pc.c Functions to emulate general aspects of the machine (RAM, ROM, interrupts, I/O ports) The information herein is heavily based on 'Ralph Browns Interrupt List' Release 52, Last Change 20oct96 TODO: clean up (maybe split) the different pieces of hardware PIC, PIT, DMA... add support for LPT, COM (almost done) emulation of a serial mouse on a COM port (almost done) support for Game Controller port at 0x0201 support for XT harddisk (under the way, see machine/pc_hdc.c) whatever 'hardware' comes to mind, maybe SoundBlaster? EGA? VGA? ***************************************************************************/ #include "driver.h" #include "memconv.h" #include "machine/8255ppi.h" #include "machine/pic8259.h" #include "machine/pit8253.h" #include "machine/mc146818.h" #include "machine/pcshare.h" #include "machine/8237dma.h" #include "machine/pckeybrd.h" #ifdef MESS #include "machine/uart8250.h" #include "video/pc_vga.h" #include "video/pc_cga.h" #include "video/pc_mda.h" #include "video/pc_aga.h" #include "includes/pc_mouse.h" #include "machine/pc_fdc.h" #include "includes/pclpt.h" #include "includes/centroni.h" #include "machine/pc_hdc.h" #include "machine/nec765.h" #include "audio/pc.h" #include "mscommon.h" #endif /* MESS */ #define VERBOSE_DBG 0 /* general debug messages */ #if VERBOSE_DBG #define DBG_LOG(N,M,A) \ if(VERBOSE_DBG>=N){ if( M )logerror("%11.6f: %-24s",mame_time_to_double(mame_timer_get_time()),(char*)M ); logerror A; } #else #define DBG_LOG(n,m,a) #endif #define VERBOSE_JOY 0 /* JOY (joystick port) */ #if VERBOSE_JOY #define LOG(LEVEL,N,M,A) \ #define JOY_LOG(N,M,A) \ if(VERBOSE_JOY>=N){ if( M )logerror("%11.6f: %-24s",mame_time_to_double(mame_timer_get_time()),(char*)M ); logerror A; } #else #define JOY_LOG(n,m,a) #endif #define FDC_DMA 2 static mame_timer *pc_keyboard_timer; static TIMER_CALLBACK( pc_keyb_timer ); #define LOG_PORT80 0 /* ---------------------------------------------------------------------- */ #ifdef MESS /* called when a interrupt is set/cleared from com hardware */ static void pc_com_interrupt(int nr, int state) { static const int irq[4] = {4, 3, 4, 3}; /* issue COM1/3 IRQ4, COM2/4 IRQ3 */ pic8259_set_irq_line(0, irq[nr], state); } /* called when com registers read/written - used to update peripherals that are connected */ static void pc_com_refresh_connected(int n, int data) { /* mouse connected to this port? */ if (readinputport(3) & (0x80>>n)) pc_mouse_handshake_in(n,data); } /* PC interface to PC-com hardware. Done this way because PCW16 also uses PC-com hardware and doesn't have the same setup! */ static uart8250_interface com_interface[4]= { { TYPE8250, 1843200, pc_com_interrupt, NULL, pc_com_refresh_connected }, { TYPE8250, 1843200, pc_com_interrupt, NULL, pc_com_refresh_connected }, { TYPE8250, 1843200, pc_com_interrupt, NULL, pc_com_refresh_connected }, { TYPE8250, 1843200, pc_com_interrupt, NULL, pc_com_refresh_connected } }; #endif /* MESS */ static void pc_timer0_w(int state) { pic8259_set_irq_line(0, 0, state); } /* * timer0 heartbeat IRQ * timer1 DRAM refresh (ignored) * timer2 PIO port C pin 4 and speaker polling */ static const struct pit8253_config pc_pit8253_config = { TYPE8253, { { 4772720/4, /* heartbeat IRQ */ pc_timer0_w, NULL }, { 4772720/4, /* dram refresh */ NULL, NULL }, { 4772720/4, /* pio port c pin 4, and speaker polling enough */ NULL, #ifdef MESS pc_sh_speaker_change_clock #else NULL //pc_sh_speaker_change_clock #endif /* MESS */ } } }; static const struct pit8253_config pc_pit8254_config = { TYPE8254, { { 4772720/4, /* heartbeat IRQ */ pc_timer0_w, NULL }, { 4772720/4, /* dram refresh */ NULL, NULL }, { 4772720/4, /* pio port c pin 4, and speaker polling enough */ NULL, #ifdef MESS pc_sh_speaker_change_clock #else NULL //pc_sh_speaker_change_clock #endif /* MESS */ } } }; #ifdef MESS static PC_LPT_CONFIG lpt_config[3]={ { 1, LPT_UNIDIRECTIONAL, NULL }, { 1, LPT_UNIDIRECTIONAL, NULL }, { 1, LPT_UNIDIRECTIONAL, NULL } }; static CENTRONICS_CONFIG cent_config[3]={ { PRINTER_IBM, pc_lpt_handshake_in }, { PRINTER_IBM, pc_lpt_handshake_in }, { PRINTER_IBM, pc_lpt_handshake_in } }; #endif /************************************************************************* * * PC DMA stuff * *************************************************************************/ static UINT8 dma_offset[2][4]; static UINT8 at_pages[0x10]; static offs_t pc_page_offset_mask; READ8_HANDLER(pc_page_r) { return 0xFF; } WRITE8_HANDLER(pc_page_w) { switch(offset % 4) { case 1: dma_offset[0][2] = data; break; case 2: dma_offset[0][3] = data; break; case 3: dma_offset[0][0] = dma_offset[0][1] = data; break; } } READ16_HANDLER(pc_page16le_r) { return read16le_with_read8_handler(pc_page_r, offset, mem_mask); } WRITE16_HANDLER(pc_page16le_w) { write16le_with_write8_handler(pc_page_w, offset, data, mem_mask); } READ8_HANDLER(at_page8_r) { UINT8 data = at_pages[offset % 0x10]; switch(offset % 8) { case 1: data = dma_offset[(offset / 8) & 1][2]; break; case 2: data = dma_offset[(offset / 8) & 1][3]; break; case 3: data = dma_offset[(offset / 8) & 1][1]; break; case 7: data = dma_offset[(offset / 8) & 1][0]; break; } return data; } WRITE8_HANDLER(at_page8_w) { at_pages[offset % 0x10] = data; if (LOG_PORT80 && (offset == 0)) logerror(" at_page8_w(): Port 80h <== 0x%02x (PC=0x%08x)\n", data, (unsigned) activecpu_get_reg(REG_PC)); switch(offset % 8) { case 1: dma_offset[(offset / 8) & 1][2] = data; break; case 2: dma_offset[(offset / 8) & 1][3] = data; break; case 3: dma_offset[(offset / 8) & 1][1] = data; break; case 7: dma_offset[(offset / 8) & 1][0] = data; break; } } READ32_HANDLER(at_page32_r) { return read32le_with_read8_handler(at_page8_r, offset, mem_mask); } WRITE32_HANDLER(at_page32_w) { write32le_with_write8_handler(at_page8_w, offset, data, mem_mask); } static UINT8 pc_dma_read_byte(int channel, offs_t offset) { UINT8 result; offs_t page_offset = (((offs_t) dma_offset[0][channel]) << 16) & pc_page_offset_mask; cpuintrf_push_context(0); result = program_read_byte(page_offset + offset); cpuintrf_pop_context(); return result; } static void pc_dma_write_byte(int channel, offs_t offset, UINT8 data) { offs_t page_offset = (((offs_t) dma_offset[0][channel]) << 16) & pc_page_offset_mask; cpuintrf_push_context(0); program_write_byte(page_offset + offset, data); cpuintrf_pop_context(); } static struct dma8237_interface pc_dma = { 0, 1.0e-6, // 1us pc_dma_read_byte, pc_dma_write_byte, #ifdef MESS { 0, 0, pc_fdc_dack_r, pc_hdc_dack_r }, { 0, 0, pc_fdc_dack_w, pc_hdc_dack_w }, pc_fdc_set_tc_state #else { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0 #endif }; /* ----------------------------------------------------------------------- */ #ifdef MESS static void pc_fdc_interrupt(int state) { pic8259_set_irq_line(0, 6, state); } static void pc_fdc_dma_drq(int state, int read_) { dma8237_drq_write(0, FDC_DMA, state); } static const struct pc_fdc_interface fdc_interface = { NEC765A, pc_fdc_interrupt, pc_fdc_dma_drq, }; #endif /* MESS */ /* ----------------------------------------------------------------------- */ static void pc_pic_set_int_line(int which, int interrupt) { switch(which) { case 0: /* Master */ cpunum_set_input_line(0, 0, interrupt ? HOLD_LINE : CLEAR_LINE); break; case 1: /* Slave */ pic8259_set_irq_line(0, 2, interrupt); break; } } void init_pc_common(UINT32 flags) { #ifdef MESS /* MESS managed RAM */ if (mess_ram) memory_set_bankptr(10, mess_ram); #endif /* MESS */ /* PIT */ if (flags & PCCOMMON_TIMER_8254) pit8253_init(1, &pc_pit8254_config); else if (flags & PCCOMMON_TIMER_8253) pit8253_init(1, &pc_pit8253_config); #ifdef MESS /* FDC/HDC hardware */ pc_fdc_init(&fdc_interface); pc_hdc_setup(); /* com hardware */ uart8250_init(0, com_interface); uart8250_reset(0); uart8250_init(1, com_interface+1); uart8250_reset(1); uart8250_init(2, com_interface+2); uart8250_reset(2); uart8250_init(3, com_interface+3); uart8250_reset(3); pc_lpt_config(0, lpt_config); centronics_config(0, cent_config); pc_lpt_set_device(0, &CENTRONICS_PRINTER_DEVICE); pc_lpt_config(1, lpt_config+1); centronics_config(1, cent_config+1); pc_lpt_set_device(1, &CENTRONICS_PRINTER_DEVICE); pc_lpt_config(2, lpt_config+2); centronics_config(2, cent_config+2); pc_lpt_set_device(2, &CENTRONICS_PRINTER_DEVICE); /* serial mouse */ pc_mouse_set_serial_port(0); pc_mouse_initialise(); #endif /* MESS */ /* PC-XT keyboard */ if (flags & PCCOMMON_KEYBOARD_AT) at_keyboard_init(AT_KEYBOARD_TYPE_AT); else at_keyboard_init(AT_KEYBOARD_TYPE_PC); at_keyboard_set_scan_code_set(1); /* PIC */ pic8259_init(2, pc_pic_set_int_line); /* DMA */ if (flags & PCCOMMON_DMA8237_AT) { dma8237_init(2); dma8237_config(0, &pc_dma); pc_page_offset_mask = 0xFF0000; } else { dma8237_init(1); dma8237_config(0, &pc_dma); pc_page_offset_mask = 0x0F0000; } pc_keyboard_timer = mame_timer_alloc(pc_keyb_timer); } /* keyboard seams to permanently sent data clocked by the mainboard clock line low for longer means "resync", keyboard sends 0xaa as answer will become automatically 0x00 after a while */ static struct { UINT8 data; int on; } pc_keyb= { 0 }; UINT8 pc_keyb_read(void) { return pc_keyb.data; } static TIMER_CALLBACK( pc_keyb_timer ) { at_keyboard_reset(); pc_keyboard(); } void pc_keyb_set_clock(int on) { mame_time keyb_delay = { 0, MAX_SUBSECONDS/200 }; // 5ms on = on ? 1 : 0; if (pc_keyb.on != on) { if (on) mame_timer_adjust(pc_keyboard_timer, keyb_delay, 0, time_zero); else mame_timer_reset(pc_keyboard_timer, time_never); pc_keyb.on = on; } } void pc_keyb_clear(void) { pc_keyb.data = 0; } void pc_keyboard(void) { int data; at_keyboard_polling(); if (pc_keyb.on) { if ( (data=at_keyboard_read())!=-1) { pc_keyb.data = data; DBG_LOG(1,"KB_scancode",("$%02x\n", pc_keyb.data)); pic8259_set_irq_line(0, 1, 1); pic8259_set_irq_line(0, 1, 0); } } }