205 lines
6.8 KiB
C
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);
|
|
}
|