pcem/src/pc87306.c
2018-02-19 21:39:05 +00:00

205 lines
6.8 KiB
C

#include "ibm.h"
#include "fdc.h"
#include "io.h"
#include "lpt.h"
#include "serial.h"
#include "pc87306.h"
typedef struct pc87306_t
{
int first_read;
int cur_addr;
uint8_t regs[32];
uint8_t gpio_1, gpio_2;
} pc87306_t;
static pc87306_t pc87306_global;
#define REG_FER 0x00
#define REG_FAR 0x01
#define REG_GPBA 0x0f
#define REG_SCF0 0x12
#define REG_PNP0 0x1b
#define FER_LPT_ENA (1 << 0)
#define FER_COM1_ENA (1 << 1)
#define FER_COM2_ENA (1 << 2)
#define FER_FDC_ENA (1 << 3)
static uint16_t get_com_addr(pc87306_t *pc87306, int reg)
{
switch (reg)
{
case 0:
return 0x3f8;
case 1:
return 0x2f8;
case 2:
switch (pc87306->regs[REG_FAR] >> 6)
{
case 0:
return 0x3e8;
case 1:
return 0x338;
case 2:
return 0x2e8;
case 3:
return 0x220;
}
break;
case 3:
switch (pc87306->regs[REG_FAR] >> 6)
{
case 0:
return 0x2e8;
case 1:
return 0x238;
case 2:
return 0x2e0;
case 3:
return 0x228;
}
break;
}
fatal("get_com_addr invalid\n");
return 0xff;
}
static uint8_t pc87306_gpio_read(uint16_t port, void *p)
{
pc87306_t *pc87306 = (pc87306_t *)p;
uint8_t temp;
if (!(port & 1))
temp = pc87306->gpio_1;
else
temp = pc87306->gpio_2;
// pclog("Read GPIO %04x %02x\n", port, temp);
return temp;
}
static void pc87306_gpio_write(uint16_t port, uint8_t val, void *p)
{
pc87306_t *pc87306 = (pc87306_t *)p;
// pclog("Write GPIO %04x %02x\n", port, val);
if (!(port & 1))
pc87306->gpio_1 = val;
}
static uint8_t pc87306_read(uint16_t port, void *p)
{
pc87306_t *pc87306 = (pc87306_t *)p;
if (!(port & 1))
{
if (pc87306->first_read)
{
pc87306->first_read = 0;
return 0x88; /*ID*/
}
return pc87306->cur_addr;
}
else
{
pc87306->regs[8] = 0x70;
// pclog("PC87306 read %02x %02x %04x:%04x\n", pc87306->cur_addr, pc87306->regs[pc87306->cur_addr], CS,cpu_state.pc);
// if (pc87306->cur_addr == 0)
// output = 3;
return pc87306->regs[pc87306->cur_addr];
}
}
static void pc87306_write(uint16_t port, uint8_t val, void *p)
{
pc87306_t *pc87306 = (pc87306_t *)p;
//pclog("pc87306_write: port=%04x val=%02x %04x:%04x\n", port, val, CS,cpu_state.pc);
if (!(port & 1))
pc87306->cur_addr = val & 0x1f;
else
{
uint16_t old_gpio_addr = pc87306_global.regs[REG_GPBA] << 2;
pc87306->regs[pc87306->cur_addr] = val;
// pclog("PC87306 write %02x %02x\n", pc87306->cur_addr, val);
fdc_remove();
if (pc87306->regs[REG_FER] & FER_FDC_ENA)
fdc_add();
serial1_remove();
if (pc87306->regs[REG_FER] & FER_COM1_ENA)
{
uint16_t addr = get_com_addr(pc87306, (pc87306->regs[REG_FAR] >> 2) & 3);
switch ((pc87306->regs[REG_FAR] >> 2) & 3)
{
case 0: case 2:
serial1_set(addr, 4);
break;
case 1: case 3:
serial1_set(addr, 3);
break;
}
}
serial2_remove();
if (pc87306->regs[REG_FER] & FER_COM2_ENA)
{
uint16_t addr = get_com_addr(pc87306, (pc87306->regs[REG_FAR] >> 4) & 3);
switch ((pc87306->regs[REG_FAR] >> 4) & 3)
{
case 0: case 2:
serial2_set(addr, 4);
break;
case 1: case 3:
serial2_set(addr, 3);
break;
}
}
lpt1_remove();
lpt2_remove();
if (pc87306->regs[REG_FER] & FER_LPT_ENA)
{
int reg;
reg = pc87306->regs[REG_FAR] & 3;
reg |= (pc87306->regs[REG_PNP0] & 0x30) >> 2;
switch (reg)
{
case 0x0: case 0x8:
case 0x4: case 0x5: case 0x6: case 0x7:
lpt1_init(0x378);
break;
case 0x1: case 0x9:
lpt1_init(0x3bc);
break;
case 0x2: case 0xa:
case 0xc: case 0xd: case 0xe: case 0xf:
lpt1_init(0x278);
break;
}
}
io_removehandler(old_gpio_addr, 0x0002, pc87306_gpio_read, NULL, NULL, pc87306_gpio_write, NULL, NULL, &pc87306_global);
io_sethandler(pc87306_global.regs[REG_GPBA] << 2, 0x0002, pc87306_gpio_read, NULL, NULL, pc87306_gpio_write, NULL, NULL, &pc87306_global);
}
}
void pc87306_init(uint16_t port)
{
memset(&pc87306_global, 0, sizeof(pc87306_t));
pc87306_global.first_read = 1;
io_sethandler(port, 0x0002, pc87306_read, NULL, NULL, pc87306_write, NULL, NULL, &pc87306_global);
pc87306_global.regs[REG_FER] = 0;
pc87306_global.regs[REG_FAR] = 0x10;
pc87306_global.regs[REG_GPBA] = 0x78 >> 2;
pc87306_global.regs[REG_SCF0] = 0x30;
pc87306_global.gpio_2 = 0xfa;
io_sethandler(0x0078, 0x0002, pc87306_gpio_read, NULL, NULL, pc87306_gpio_write, NULL, NULL, &pc87306_global);
}