2017-05-05 01:49:42 +02:00
|
|
|
/*
|
|
|
|
|
* 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 several low-level support functions for
|
|
|
|
|
* the AHA-154x series of ISA Host Adapters made by Adaptec.
|
|
|
|
|
* These functions implement the support needed by the ROM BIOS
|
|
|
|
|
* of these cards.
|
|
|
|
|
*
|
|
|
|
|
* Version: @(#)aha154x.c 1.0.4 2017/04/21
|
|
|
|
|
*
|
|
|
|
|
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
|
|
|
|
* Copyright 2017 Fred N. van Kempen.
|
|
|
|
|
*/
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include "ibm.h"
|
|
|
|
|
#include "mem.h"
|
|
|
|
|
#include "rom.h"
|
|
|
|
|
#include "device.h"
|
|
|
|
|
#include "scsi_buslogic.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define AHA AHA154xCF /* set desired card type */
|
|
|
|
|
#define AHA154xB 1 /* AHA-154x Rev.B */
|
|
|
|
|
#define AHA154xC 2 /* AHA-154x Rev.C */
|
|
|
|
|
#define AHA154xCF 3 /* AHA-154x Rev.CF */
|
|
|
|
|
#define AHA154xCP 4 /* AHA-154x Rev.CP */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if AHA == AHA154xB
|
2017-05-06 04:02:03 +02:00
|
|
|
# define ROMFILE L"roms/adaptec/aha1540b310.bin"
|
2017-05-05 01:49:42 +02:00
|
|
|
# define AHA_BID 'A' /* AHA-154x B */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if AHA == AHA154xC
|
2017-05-06 04:02:03 +02:00
|
|
|
# define ROMFILE L"roms/adaptec/aha1542c101.bin"
|
2017-05-05 01:49:42 +02:00
|
|
|
# define AHA_BID 'D' /* AHA-154x C */
|
|
|
|
|
# define ROM_FWHIGH 0x0022 /* firmware version (hi/lo) */
|
|
|
|
|
# define ROM_SHRAM 0x3F80 /* shadow RAM address base */
|
|
|
|
|
# define ROM_SHRAMSZ 128 /* size of shadow RAM */
|
|
|
|
|
# define ROM_IOADDR 0x3F7E /* [2:0] idx into addr table */
|
|
|
|
|
# define EEP_SIZE 32 /* 32 bytes of storage */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if AHA == AHA154xCF
|
2017-05-06 04:02:03 +02:00
|
|
|
# define ROMFILE L"roms/adaptec/aha1542cf201.bin"
|
2017-05-05 01:49:42 +02:00
|
|
|
# define AHA_BID 'E' /* AHA-154x CF */
|
|
|
|
|
# define ROM_FWHIGH 0x0022 /* firmware version (hi/lo) */
|
|
|
|
|
# define ROM_SHRAM 0x3F80 /* shadow RAM address base */
|
|
|
|
|
# define ROM_SHRAMSZ 128 /* size of shadow RAM */
|
|
|
|
|
# define ROM_IOADDR 0x3F7E /* [2:0] idx into addr table */
|
|
|
|
|
# define EEP_SIZE 32 /* 32 bytes of storage */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if AHA == AHA154xCP
|
2017-05-06 04:02:03 +02:00
|
|
|
# define ROMFILE L"roms/adaptec/aha1542cp102.bin"
|
2017-05-05 01:49:42 +02:00
|
|
|
# define AHA_BID 'F' /* AHA-154x CP */
|
|
|
|
|
# define ROM_FWHIGH 0x0055 /* firmware version (hi/lo) */
|
|
|
|
|
# define ROM_SHRAM 0x3F80 /* shadow RAM address base */
|
|
|
|
|
# define ROM_SHRAMSZ 128 /* size of shadow RAM */
|
|
|
|
|
# define ROM_IOADDR 0x3F7E /* [2:0] idx into addr table */
|
|
|
|
|
# define EEP_SIZE 32 /* 32 bytes of storage */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define ROM_SIZE 16384 /* one ROM is 16K */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* EEPROM map and bit definitions. */
|
|
|
|
|
#define EE0_HOSTID 0x07 /* EE(0) [2:0] */
|
|
|
|
|
#define EE0_ALTFLOP 0x80 /* EE(0) [7] FDC at 370h */
|
|
|
|
|
#define EE1_IRQCH 0x07 /* EE(1) [3:0] */
|
|
|
|
|
#define EE1_DMACH 0x70 /* EE(1) [7:4] */
|
|
|
|
|
#define EE2_RMVOK 0x01 /* EE(2) [0] Support removable disks */
|
|
|
|
|
#define EE2_HABIOS 0x02 /* EE(2) [1] HA Bios Space Reserved */
|
|
|
|
|
#define EE2_INT19 0x04 /* EE(2) [2] HA Bios Controls INT19 */
|
|
|
|
|
#define EE2_DYNSCAN 0x08 /* EE(2) [3] Dynamically scan bus */
|
|
|
|
|
#define EE2_TWODRV 0x10 /* EE(2) [4] Allow more than 2 drives */
|
|
|
|
|
#define EE2_SEEKRET 0x20 /* EE(2) [5] Immediate return on seek */
|
|
|
|
|
#define EE2_EXT1G 0x80 /* EE(2) [7] Extended Translation >1GB */
|
|
|
|
|
#define EE3_SPEED 0x00 /* EE(3) [7:0] DMA Speed */
|
|
|
|
|
#define SPEED_33 0xFF
|
|
|
|
|
#define SPEED_50 0x00
|
|
|
|
|
#define SPEED_56 0x04
|
|
|
|
|
#define SPEED_67 0x01
|
|
|
|
|
#define SPEED_80 0x02
|
|
|
|
|
#define SPEED_10 0x03
|
|
|
|
|
#define EE4_FLOPTOK 0x80 /* EE(4) [7] Support Flopticals */
|
|
|
|
|
#define EE6_PARITY 0x01 /* EE(6) [0] parity check enable */
|
|
|
|
|
#define EE6_TERM 0x02 /* EE(6) [1] host term enable */
|
|
|
|
|
#define EE6_RSTBUS 0x04 /* EE(6) [2] reset SCSI bus on boot */
|
|
|
|
|
#define EEE_SYNC 0x01 /* EE(E) [0] Enable Sync Negotiation */
|
|
|
|
|
#define EEE_DISCON 0x02 /* EE(E) [1] Enable Disconnection */
|
|
|
|
|
#define EEE_FAST 0x04 /* EE(E) [2] Enable FAST SCSI */
|
|
|
|
|
#define EEE_START 0x08 /* EE(E) [3] Enable Start Unit */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static rom_t aha_bios; /* active ROM */
|
|
|
|
|
static uint8_t *aha_rom1; /* main BIOS */
|
|
|
|
|
static uint8_t *aha_rom2; /* SCSI-Select */
|
|
|
|
|
#ifdef EEP_SIZE
|
|
|
|
|
static uint8_t aha_eep[EEP_SIZE]; /* EEPROM storage */
|
|
|
|
|
#endif
|
|
|
|
|
static uint16_t aha_ports[] = {
|
|
|
|
|
0x0330, 0x0334, 0x0230, 0x0234,
|
|
|
|
|
0x0130, 0x0134, 0x0000, 0x0000
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write data to the BIOS space.
|
|
|
|
|
*
|
|
|
|
|
* AHA-1542C's and up have a feature where they map a 128-byte
|
|
|
|
|
* RAM space into the ROM BIOS' address space, and then use it
|
|
|
|
|
* as working memory. This function implements the writing to
|
|
|
|
|
* that memory.
|
|
|
|
|
*
|
|
|
|
|
* We enable/disable this memory through AHA command 0x24.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
aha_mem_write(uint32_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
rom_t *rom = (rom_t *)priv;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
pclog("AHA1542x: writing to BIOS space, %06lX, val %02x\n", addr, val);
|
|
|
|
|
pclog(" called from %04X:%04X\n", CS, cpu_state.pc);
|
|
|
|
|
#endif
|
|
|
|
|
if ((addr & rom->mask) >= 0x3F80)
|
|
|
|
|
rom->rom[addr & rom->mask] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
aha_mem_read(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
rom_t *rom = (rom_t *)priv;
|
|
|
|
|
|
|
|
|
|
return(rom->rom[addr & rom->mask]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
aha_mem_readw(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
rom_t *rom = (rom_t *)priv;
|
|
|
|
|
|
|
|
|
|
return(*(uint16_t *)&rom->rom[addr & rom->mask]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
aha_mem_readl(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
rom_t *rom = (rom_t *)priv;
|
|
|
|
|
|
|
|
|
|
return(*(uint32_t *)&rom->rom[addr & rom->mask]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ROM_IOADDR
|
|
|
|
|
/*
|
|
|
|
|
* Patch the ROM BIOS image for stuff Adaptec deliberately
|
|
|
|
|
* made hard to understand. Well, maybe not, maybe it was
|
|
|
|
|
* their way of handling issues like these at the time..
|
|
|
|
|
*
|
|
|
|
|
* Patch 1: emulate the I/O ADDR SW setting by patching a
|
|
|
|
|
* byte in the BIOS that indicates the I/O ADDR
|
|
|
|
|
* switch setting on the board.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
aha_patch(uint8_t *romptr, uint16_t ioaddr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Look up the I/O address in the table. */
|
|
|
|
|
for (i=0; i<8; i++)
|
|
|
|
|
if (aha_ports[i] == ioaddr) break;
|
|
|
|
|
if (i == 8) {
|
|
|
|
|
pclog("AHA154x: bad news, invalid I/O address %04x selected!\n",
|
|
|
|
|
ioaddr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
romptr[ROM_IOADDR] = (unsigned char)i;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialize AHA-154xNN-specific stuff. */
|
|
|
|
|
void
|
|
|
|
|
aha154x_init(uint16_t ioaddr, uint32_t memaddr, aha_info *aha)
|
|
|
|
|
{
|
|
|
|
|
uint32_t bios_size;
|
|
|
|
|
uint32_t bios_addr;
|
|
|
|
|
uint32_t bios_mask;
|
|
|
|
|
char *bios_path;
|
|
|
|
|
uint32_t temp;
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
/* Set BIOS load address. */
|
|
|
|
|
bios_addr = memaddr;
|
|
|
|
|
bios_path = ROMFILE;
|
|
|
|
|
pclog("AHA154x: loading BIOS from '%s'\n", bios_path);
|
|
|
|
|
|
|
|
|
|
/* Open the BIOS image file and make sure it exists. */
|
2017-05-06 04:02:03 +02:00
|
|
|
if ((f = romfopen(bios_path, L"rb")) == NULL) {
|
2017-05-05 01:49:42 +02:00
|
|
|
pclog("AHA154x: BIOS ROM not found!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Manually load and process the ROM image.
|
|
|
|
|
*
|
|
|
|
|
* We *could* use the system "rom_init" function here, but for
|
|
|
|
|
* this special case, we can't: we may need WRITE access to the
|
|
|
|
|
* memory later on.
|
|
|
|
|
*/
|
|
|
|
|
(void)fseek(f, 0L, SEEK_END);
|
|
|
|
|
temp = ftell(f);
|
|
|
|
|
(void)fseek(f, 0L, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
/* Load first chunk of BIOS (which is the main BIOS, aka ROM1.) */
|
|
|
|
|
aha_rom1 = malloc(ROM_SIZE);
|
|
|
|
|
(void)fread(aha_rom1, ROM_SIZE, 1, f);
|
|
|
|
|
temp -= ROM_SIZE;
|
|
|
|
|
if (temp > 0) {
|
|
|
|
|
aha_rom2 = malloc(ROM_SIZE);
|
|
|
|
|
(void)fread(aha_rom2, ROM_SIZE, 1, f);
|
|
|
|
|
temp -= ROM_SIZE;
|
|
|
|
|
} else {
|
|
|
|
|
aha_rom2 = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (temp != 0) {
|
|
|
|
|
pclog("AHA154x: BIOS ROM size invalid!\n");
|
|
|
|
|
free(aha_rom1);
|
|
|
|
|
if (aha_rom2 != NULL)
|
|
|
|
|
free(aha_rom2);
|
|
|
|
|
(void)fclose(f);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
temp = ftell(f);
|
|
|
|
|
if (temp > ROM_SIZE)
|
|
|
|
|
temp = ROM_SIZE;
|
|
|
|
|
(void)fclose(f);
|
|
|
|
|
|
|
|
|
|
/* Adjust BIOS size in chunks of 2K, as per BIOS spec. */
|
|
|
|
|
bios_size = 0x10000;
|
|
|
|
|
if (temp <= 0x8000)
|
|
|
|
|
bios_size = 0x8000;
|
|
|
|
|
if (temp <= 0x4000)
|
|
|
|
|
bios_size = 0x4000;
|
|
|
|
|
if (temp <= 0x2000)
|
|
|
|
|
bios_size = 0x2000;
|
|
|
|
|
bios_mask = (bios_size - 1);
|
|
|
|
|
pclog("AHA154x: BIOS at 0x%06lX, size %lu, mask %08lx\n",
|
|
|
|
|
bios_addr, bios_size, bios_mask);
|
|
|
|
|
|
|
|
|
|
/* Initialize the ROM entry for this BIOS. */
|
|
|
|
|
memset(&aha_bios, 0x00, sizeof(rom_t));
|
|
|
|
|
|
|
|
|
|
/* Enable ROM1 into the memory map. */
|
|
|
|
|
aha_bios.rom = aha_rom1;
|
|
|
|
|
|
|
|
|
|
/* Set up an address mask for this memory. */
|
|
|
|
|
aha_bios.mask = bios_mask;
|
|
|
|
|
|
|
|
|
|
/* Map this system into the memory map. */
|
|
|
|
|
mem_mapping_add(&aha_bios.mapping, bios_addr, bios_size,
|
|
|
|
|
aha_mem_read, aha_mem_readw, aha_mem_readl,
|
|
|
|
|
aha_mem_write, NULL, NULL,
|
|
|
|
|
aha_bios.rom, MEM_MAPPING_EXTERNAL, &aha_bios);
|
|
|
|
|
|
|
|
|
|
#ifdef ROM_IOADDR
|
|
|
|
|
/* Patch the ROM BIOS image to work with us. */
|
|
|
|
|
aha_patch(aha_bios.rom, ioaddr);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if ROM_FWHIGH
|
|
|
|
|
/* Read firmware version from the BIOS. */
|
|
|
|
|
aha->fwh = aha_bios.rom[ROM_FWHIGH];
|
|
|
|
|
aha->fwl = aha_bios.rom[ROM_FWHIGH+1];
|
|
|
|
|
#else
|
|
|
|
|
/* Fake BIOS firmware version. */
|
|
|
|
|
aha->fwh = '1';
|
|
|
|
|
aha->fwl = '0';
|
|
|
|
|
#endif
|
|
|
|
|
aha->bid = AHA_BID;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do a checksum on the ROM.
|
|
|
|
|
* The BIOS ROMs on the 154xC(F) boards will always fail
|
|
|
|
|
* the checksum, because they are incomplete: on the real
|
|
|
|
|
* boards, a shadow RAM and some other (config) registers
|
|
|
|
|
* are mapped into its space. It is assumed that boards
|
|
|
|
|
* have logic that automatically generate a "fixup" byte
|
|
|
|
|
* at the end of the data to 'make up' for this.
|
|
|
|
|
*
|
|
|
|
|
* We emulated some of those in the patch routine, so now
|
|
|
|
|
* it is time to "fix up" the BIOS image so that the main
|
|
|
|
|
* (system) BIOS considers it valid.
|
|
|
|
|
*/
|
|
|
|
|
again:
|
|
|
|
|
bios_mask = 0;
|
|
|
|
|
for (temp=0; temp<16384; temp++)
|
|
|
|
|
bios_mask += aha_bios.rom[temp];
|
|
|
|
|
bios_mask &= 0xff;
|
|
|
|
|
if (bios_mask != 0x00) {
|
|
|
|
|
pclog("AHA154x: fixing BIOS checksum (%02x) ..\n", bios_mask);
|
|
|
|
|
aha_bios.rom[temp-1] += (256 - bios_mask);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enable the memory. */
|
|
|
|
|
mem_mapping_enable(&aha_bios.mapping);
|
|
|
|
|
mem_mapping_set_addr(&aha_bios.mapping, bios_addr, bios_size);
|
|
|
|
|
|
|
|
|
|
#ifdef EEP_SIZE
|
|
|
|
|
/* Initialize the on-board EEPROM. */
|
|
|
|
|
memset(aha_eep, 0x00, EEP_SIZE);
|
|
|
|
|
aha_eep[0] = 7; /* SCSI ID 7 */
|
|
|
|
|
aha_eep[1] = 15-9; /* IRQ15 */
|
|
|
|
|
aha_eep[1] |= (6<<4); /* DMA6 */
|
|
|
|
|
aha_eep[2] = (EE2_HABIOS | /* BIOS Space Reserved */
|
|
|
|
|
EE2_SEEKRET); /* Immediate return on seek */
|
|
|
|
|
aha_eep[3] = SPEED_50; /* speed 5.0 MB/s */
|
|
|
|
|
aha_eep[6] = (EE6_TERM | /* host term enable */
|
|
|
|
|
EE6_RSTBUS); /* reset SCSI bus on boot */
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Mess with the AHA-154xCF's Shadow RAM. */
|
|
|
|
|
uint8_t
|
|
|
|
|
aha154x_shram(uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
#ifdef ROM_SHRAM
|
|
|
|
|
switch(cmd) {
|
|
|
|
|
case 0x00: /* disable, make it look like ROM */
|
|
|
|
|
memset(&aha_bios.rom[ROM_SHRAM], 0xFF, ROM_SHRAMSZ);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x02: /* clear it */
|
|
|
|
|
memset(&aha_bios.rom[ROM_SHRAM], 0x00, ROM_SHRAMSZ);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x03: /* enable, clear for use */
|
|
|
|
|
memset(&aha_bios.rom[ROM_SHRAM], 0x00, ROM_SHRAMSZ);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Firmware expects 04 status. */
|
|
|
|
|
return(0x04);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t
|
|
|
|
|
aha154x_eeprom(uint8_t cmd,uint8_t arg,uint8_t len,uint8_t off,uint8_t *bufp)
|
|
|
|
|
{
|
|
|
|
|
uint8_t r = 0xff;
|
|
|
|
|
|
|
|
|
|
pclog("AHA154x: EEPROM cmd=%02x, arg=%02x len=%d, off=%02x\n",
|
|
|
|
|
cmd, arg, len, off);
|
|
|
|
|
|
|
|
|
|
#ifdef EEP_SIZE
|
|
|
|
|
if ((off+len) > EEP_SIZE) return(r); /* no can do.. */
|
|
|
|
|
|
|
|
|
|
if (cmd == 0x22) {
|
|
|
|
|
/* Write data to the EEPROM. */
|
|
|
|
|
memcpy(&aha_eep[off], bufp, len);
|
|
|
|
|
r = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cmd == 0x23) {
|
|
|
|
|
/* Read data from the EEPROM. */
|
|
|
|
|
memcpy(bufp, &aha_eep[off], len);
|
|
|
|
|
r = len;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t
|
|
|
|
|
aha154x_memory(uint8_t cmd)
|
|
|
|
|
{
|
|
|
|
|
uint8_t r = 0xff;
|
|
|
|
|
|
|
|
|
|
pclog("AHA154x: MEMORY cmd=%02x\n", cmd);
|
|
|
|
|
|
|
|
|
|
if (cmd == 0x27) {
|
|
|
|
|
/* Enable the mapper, so, set ROM2 active. */
|
|
|
|
|
aha_bios.rom = aha_rom2;
|
|
|
|
|
}
|
|
|
|
|
if (cmd == 0x26) {
|
|
|
|
|
/* Disable the mapper, so, set ROM1 active. */
|
|
|
|
|
aha_bios.rom = aha_rom1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|