Files
86Box/src/chipset/ali1409.c

344 lines
10 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the ALi M1409 chipset.
*
* Note: This chipset has no datasheet, everything were done via
* reverse engineering.
*
*
*
* Authors: Jose Phillips, <jose@latinol.com>
* Sarah Walker, <https://pcem-emulator.co.uk/>
*
* Copyright 2024 Jose Phillips.
* Copyright 2008-2018 Sarah Walker.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
#include <86box/timer.h>
#include <86box/io.h>
#include <86box/device.h>
#include <86box/apm.h>
#include <86box/mem.h>
#include <86box/fdd.h>
#include <86box/fdc.h>
#include <86box/smram.h>
#include <86box/chipset.h>
#include <86box/plat_unused.h>
#ifdef ENABLE_ALI1409_LOG
int ali1409_do_log = ENABLE_ALI1409_LOG;
static void
ali1409_log(const char *fmt, ...)
{
va_list ap;
if (ali1409_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define ali1409_log(fmt, ...)
#endif
typedef struct ali_1409_t {
uint8_t index;
uint8_t cfg_locked;
uint8_t regs[256];
uint8_t shadow[4];
uint8_t last_reg;
} ali1409_t;
/*
This here is because from the two BIOS'es I used to reverse engineer this,
it is unclear which of the two interpretations of the shadow RAM register
operation is correct.
The 16 kB interpretation appears to work fine right now but it may be wrong,
so I left the 32 kB interpretation in as well.
*/
#ifdef INTERPRETATION_32KB
#define SHADOW_SIZE 0x00008000
#else
#define SHADOW_SIZE 0x00004000
#endif
static void
ali1409_shadow_recalc(ali1409_t *dev)
{
uint32_t base = 0x000c0000;
for (uint8_t i = 0; i < 4; i++) {
uint8_t reg = 0x08 + i;
#ifdef INTERPRETATION_32KB
for (uint8_t j = 0; j < 4; j += 2) {
uint8_t mask = (0x03 << j);
#else
for (uint8_t j = 0; j < 4; j++) {
uint8_t mask = (0x01 << j);
#endif
uint8_t r_on = dev->regs[reg] & 0x10;
uint8_t w_on = dev->regs[reg] & 0x20;
uint8_t val = dev->regs[reg] & mask;
uint8_t xor = (dev->shadow[i] ^ dev->regs[reg]) & (mask | 0x30);
int read = r_on ? MEM_READ_INTERNAL : MEM_READ_EXTANY;
int write = w_on ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY;
if (xor) {
#ifdef INTERPRETATION_32KB
switch (val >> j) {
case 0x00:
mem_set_mem_state_both(base, SHADOW_SIZE, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
break;
case 0x01:
mem_set_mem_state_both(base, SHADOW_SIZE, MEM_READ_EXTANY | write);
break;
case 0x02:
mem_set_mem_state_both(base, SHADOW_SIZE, read | write);
break;
case 0x03:
mem_set_mem_state_both(base, SHADOW_SIZE, read | MEM_WRITE_EXTANY);
break;
}
#else
switch (val >> j) {
case 0x00:
mem_set_mem_state_both(base, SHADOW_SIZE, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
break;
case 0x01:
mem_set_mem_state_both(base, SHADOW_SIZE, read | write);
break;
}
#endif
}
base += SHADOW_SIZE;
}
dev->shadow[i] = dev->regs[reg];
}
flushmmucache_nopc();
}
static void
ali1409_write(uint16_t addr, uint8_t val, void *priv)
{
ali1409_t *dev = (ali1409_t *) priv;
ali1409_log("[%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, addr, val);
if (addr & 0x0001) {
if (dev->cfg_locked) {
if ((dev->last_reg == 0x14) && (val == 0x09))
dev->cfg_locked = 0;
dev->last_reg = val;
return;
}
/* It appears writing anything at all to register 0xFF locks it again. */
if (dev->index == 0xff)
dev->cfg_locked = 1;
else if (dev->index < 0x44) {
ali1409_log("[%04X:%08X] [W] Register %02X = %02X\n", CS, cpu_state.pc, dev->index, val);
if (dev->index < 0x10) {
dev->regs[dev->index] = val;
/*
There are still a lot of unknown here, but unfortunately, this is
as far as I have been able to come with two BIOS'es that are
available (the Acer 100T and an AMI Color dated 07/07/91).
*/
switch (dev->index) {
case 0x02:
/*
- Bit 7: The RAS address hold time:
- 0: 1/2 T;
- 1: 1 T.
- Bits 6-4: The RAS precharge time:
- 0, 0, 0: 1.5 T;
- 0, 0, 1: 2 T;
- 0, 1, 0: 2.5 T;
- 0, 1, 1: 3 T;
- 1, 0, 0: 3.5 T;
- 1, 0, 1: 4 T;
- 1, 1, 0: Reserved;
- 1, 1, 1: Reserved.
- Bit 3: Early miss cycle:
- 0: Disabled;
- 1: Enabled.
*/
break;
case 0x03:
/*
- Bit 6: CAS pulse for read cycle:
- 0: 1 T;
- 1: 1.5 T or 2 T.
I can not get the 2.5 T or 3 T setting to apply so
I have no idea what bit governs that.
- Bits 5, 4: CAS pulse for write cycle:
- 0, 0: 0.5 T or 1 T;
- 0, 1: 1.5 T or 2 T;
- 1, 0: 2.5 T or 3 T;
- 1, 1: Reserved.
- Bit 3: CAS active for read cycle:
- 0: Disabled;
- 1: Enabled.
- Bit 2: CAS active for write cycle:
- 0: Disabled;
- 1: Enabled.
*/
break;
case 0x06:
/*
- Bits 6-4: Clock divider:
- 0, 0, 0: / 2;
- 0, 0, 1: / 4;
- 0, 1, 0: / 8;
- 0, 1, 1: Reserved;
- 1, 0, 0: / 3;
- 1, 0, 1: / 6;
- 1, 1, 0: / 5;
- 1, 1, 1: / 10.
*/
switch ((val >> 4) & 7) {
default:
case 3: /* Reserved */
cpu_set_isa_speed(7159091);
break;
case 0:
cpu_set_isa_speed(cpu_busspeed / 2);
break;
case 1:
cpu_set_isa_speed(cpu_busspeed / 4);
break;
case 2:
cpu_set_isa_speed(cpu_busspeed / 8);
break;
case 4:
cpu_set_isa_speed(cpu_busspeed / 3);
break;
case 5:
cpu_set_isa_speed(cpu_busspeed / 6);
break;
case 6:
cpu_set_isa_speed(cpu_busspeed / 5);
break;
case 7:
cpu_set_isa_speed(cpu_busspeed / 10);
break;
}
break;
case 0x08 ... 0x0b:
ali1409_shadow_recalc(dev);
break;
case 0x0c:
/*
This appears to be turbo in bit 4 (1 = on, 0 = off),
and bus speed in the rest of the bits.
*/
break;
case 0x0d:
cpu_cache_ext_enabled = !!(val & 0x08);
cpu_update_waitstates();
break;
}
}
}
} else
dev->index = val;
}
static uint8_t
ali1409_read(uint16_t addr, void *priv)
{
const ali1409_t *dev = (ali1409_t *) priv;
uint8_t ret = 0xff;
if (dev->cfg_locked)
ret = 0xff;
else if (addr & 0x0001) {
if (dev->index < 0x44)
ret = dev->regs[dev->index];
} else
ret = dev->index;
ali1409_log("[%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret);
return ret;
}
static void
ali1409_close(void *priv)
{
ali1409_t *dev = (ali1409_t *) priv;
free(dev);
}
static void *
ali1409_init(UNUSED(const device_t *info))
{
ali1409_t *dev = (ali1409_t *) calloc(1, sizeof(ali1409_t));
dev->cfg_locked = 1;
ali1409_log("Bus speed: %i\n", cpu_busspeed);
io_sethandler(0x0022, 0x0002, ali1409_read, NULL, NULL, ali1409_write, NULL, NULL, dev);
dev->regs[0x0f] = 0x08;
cpu_set_isa_speed(7159091);
cpu_cache_ext_enabled = 0;
cpu_update_waitstates();
return dev;
}
const device_t ali1409_device = {
.name = "ALi M1409",
.internal_name = "ali1409",
.flags = 0,
.local = 0,
.init = ali1409_init,
.close = ali1409_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};