Files
Main/support/st/st_tos.cpp
2020-04-17 23:44:05 +08:00

603 lines
13 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../../hardware.h"
#include "../../menu.h"
#include "../../file_io.h"
#include "../../debug.h"
#include "../../user_io.h"
#include "../../fpga_io.h"
#include "st_tos.h"
#define CONFIG_FILENAME "ATARIST0.CFG"
typedef struct {
unsigned long system_ctrl; // system control word
char tos_img[1024];
char cart_img[1024];
char acsi_img[2][1024];
char video_adjust[2];
char cdc_control_redirect;
} tos_config_t;
static tos_config_t config;
fileTYPE hdd_image[2] = {};
static unsigned char dma_buffer[512];
static const char *acsi_cmd_name(int cmd) {
static const char *cmdname[] = {
"Test Drive Ready", "Restore to Zero", "Cmd $2", "Request Sense",
"Format Drive", "Read Block limits", "Reassign Blocks", "Cmd $7",
"Read Sector", "Cmd $9", "Write Sector", "Seek Block",
"Cmd $C", "Cmd $D", "Cmd $E", "Cmd $F",
"Cmd $10", "Cmd $11", "Inquiry", "Verify",
"Cmd $14", "Mode Select", "Cmd $16", "Cmd $17",
"Cmd $18", "Cmd $19", "Mode Sense", "Start/Stop Unit",
"Cmd $1C", "Cmd $1D", "Cmd $1E", "Cmd $1F",
// extended commands supported by ICD feature:
"Cmd $20", "Cmd $21", "Cmd $22",
"Read Format Capacities", "Cmd $24", "Read Capacity (10)",
"Cmd $26", "Cmd $27", "Read (10)", "Read Generation",
"Write (10)", "Seek (10)"
};
if (cmd > 0x2b) return NULL;
return cmdname[cmd];
}
/*
int tos_get_cdc_control_redirect(void)
{
return config.cdc_control_redirect;
}
void tos_set_cdc_control_redirect(char mode)
{
if (mode <= CDC_REDIRECT_MIDI)
{
config.cdc_control_redirect = mode;
// core is only informed about redirections of rs232/par/midi
if (mode < CDC_REDIRECT_RS232)
mode = 0;
else
mode -= CDC_REDIRECT_RS232 - 1;
tos_update_sysctrl((tos_system_ctrl() & ~0x0c000000) |
(((unsigned long)mode) << 26));
}
}
*/
static void mist_set_control(uint32_t ctrl)
{
spi_uio_cmd_cont(UIO_SET_STATUS2);
spi32w(ctrl);
DisableIO();
}
static void mist_memory_read(unsigned char *data, unsigned long words)
{
EnableFpga();
spi8(ST_READ_MEMORY);
// transmitted bytes must be multiple of 2 (-> words)
uint16_t *buf = (uint16_t*)data;
while (words--) *buf++ = spi_w(0);
DisableFpga();
}
static void mist_memory_write(unsigned char *data, unsigned long words)
{
EnableFpga();
spi8(ST_WRITE_MEMORY);
uint16_t *buf = (uint16_t*)data;
while (words--) spi_w(*buf++);
DisableFpga();
}
static void dma_ack(unsigned char status)
{
EnableFpga();
spi8(ST_ACK_DMA);
spi8(status);
DisableFpga();
}
static void dma_nak(void)
{
EnableFpga();
spi8(ST_NAK_DMA);
DisableFpga();
}
static void handle_acsi(unsigned char *buffer)
{
static uint8_t buf[65536];
static unsigned char asc[2] = { 0,0 };
unsigned char target = buffer[10] >> 5;
unsigned char device = buffer[1] >> 5;
unsigned char cmd = buffer[0];
unsigned long lba = 256 * 256 * (buffer[1] & 0x1f) +
256 * buffer[2] + buffer[3];
unsigned short length = buffer[4];
if (length == 0) length = 256;
if (0)
{
tos_debugf("ACSI: target %d.%d, \"%s\" (%02x)", target, device, acsi_cmd_name(cmd), cmd);
tos_debugf("ACSI: lba %lu (%lx), length %u", lba, lba, length);
}
// only a harddisk on ACSI 0/1 is supported
// ACSI 0/1 is only supported if a image is loaded
if (((target < 2) && (hdd_image[target].size != 0)))
{
unsigned long blocks = hdd_image[target].size / 512;
// only lun0 is fully supported
switch (cmd) {
case 0x25:
if (device == 0) {
bzero(dma_buffer, 512);
dma_buffer[0] = (blocks - 1) >> 24;
dma_buffer[1] = (blocks - 1) >> 16;
dma_buffer[2] = (blocks - 1) >> 8;
dma_buffer[3] = (blocks - 1) >> 0;
dma_buffer[6] = 2; // 512 bytes per block
mist_memory_write(dma_buffer, 4);
dma_ack(0x00);
asc[target] = 0x00;
}
else {
dma_ack(0x02);
asc[target] = 0x25;
}
break;
case 0x00: // test drive ready
case 0x04: // format
if (device == 0) {
asc[target] = 0x00;
dma_ack(0x00);
}
else {
asc[target] = 0x25;
dma_ack(0x02);
}
break;
case 0x03: // request sense
if (device != 0)
asc[target] = 0x25;
bzero(dma_buffer, 512);
dma_buffer[7] = 0x0b;
if (asc[target] != 0) {
dma_buffer[2] = 0x05;
dma_buffer[12] = asc[target];
}
mist_memory_write(dma_buffer, 9); // 18 bytes
dma_ack(0x00);
asc[target] = 0x00;
break;
case 0x08: // read sector
case 0x28: // read (10)
if (device == 0)
{
if (cmd == 0x28)
{
lba =
256 * 256 * 256 * buffer[2] +
256 * 256 * buffer[3] +
256 * buffer[4] +
buffer[5];
length = 256 * buffer[7] + buffer[8];
// iprintf("READ(10) %d, %d\n", lba, length);
}
if (lba + length <= blocks)
{
DISKLED_ON;
FileSeekLBA(&hdd_image[target], lba);
while (length)
{
uint32_t len = length;
if (len > 128) len = 128;
length -= len;
len *= 512;
FileReadAdv(&hdd_image[target], buf, len);
mist_memory_write(buf, len / 2);
}
DISKLED_OFF;
dma_ack(0x00);
asc[target] = 0x00;
}
else
{
tos_debugf("ACSI: read (%lu+%d) exceeds device limits (%lu)", lba, length, blocks);
dma_ack(0x02);
asc[target] = 0x21;
}
}
else
{
dma_ack(0x02);
asc[target] = 0x25;
}
break;
case 0x0a: // write sector
case 0x2a: // write (10)
if (device == 0)
{
if (cmd == 0x2a)
{
lba =
256 * 256 * 256 * buffer[2] +
256 * 256 * buffer[3] +
256 * buffer[4] +
buffer[5];
length = 256 * buffer[7] + buffer[8];
// iprintf("WRITE(10) %d, %d\n", lba, length);
}
if (lba + length <= blocks)
{
DISKLED_ON;
FileSeekLBA(&hdd_image[target], lba);
while (length)
{
uint32_t len = length;
if (len > 128) len = 128;
length -= len;
len *= 512;
mist_memory_read(buf, len / 2);
FileWriteAdv(&hdd_image[target], buf, len);
}
DISKLED_OFF;
dma_ack(0x00);
asc[target] = 0x00;
}
else {
tos_debugf("ACSI: write (%lu+%d) exceeds device limits (%lu)",
lba, length, blocks);
dma_ack(0x02);
asc[target] = 0x21;
}
}
else {
dma_ack(0x02);
asc[target] = 0x25;
}
break;
case 0x12: // inquiry
tos_debugf("ACSI: Inquiry %.11s", hdd_image[target].name);
bzero(dma_buffer, 512);
dma_buffer[2] = 2; // SCSI-2
dma_buffer[4] = length - 5; // len
memcpy(dma_buffer + 8, "MISTer ", 8); // Vendor
memcpy(dma_buffer + 16, " ", 16); // Clear device entry
memcpy(dma_buffer + 16, hdd_image[target].name, 11); // Device
memcpy(dma_buffer + 32, "ATH ", 4); // Product revision
memcpy(dma_buffer + 36, VDATE " ", 8); // Serial number
if (device != 0) dma_buffer[0] = 0x7f;
mist_memory_write(dma_buffer, length / 2);
dma_ack(0x00);
asc[target] = 0x00;
break;
case 0x1a: // mode sense
if (device == 0) {
tos_debugf("ACSI: mode sense, blocks = %lu", blocks);
bzero(dma_buffer, 512);
dma_buffer[3] = 8; // size of extent descriptor list
dma_buffer[5] = blocks >> 16;
dma_buffer[6] = blocks >> 8;
dma_buffer[7] = blocks;
dma_buffer[10] = 2; // byte 1 of block size in bytes (512)
mist_memory_write(dma_buffer, length / 2);
dma_ack(0x00);
asc[target] = 0x00;
}
else {
asc[target] = 0x25;
dma_ack(0x02);
}
break;
#if 0
case 0x1f: // ICD command?
tos_debugf("ACSI: ICD command %s ($%02x)",
acsi_cmd_name(buffer[1] & 0x1f), buffer[1] & 0x1f);
asc[target] = 0x05;
dma_ack(0x02);
break;
#endif
default:
tos_debugf("ACSI: >>>>>>>>>>>> Unsupported command <<<<<<<<<<<<<<<<");
asc[target] = 0x20;
dma_ack(0x02);
break;
}
}
else {
tos_debugf("ACSI: Request for unsupported target");
// tell acsi state machine that io controller is done
// but don't generate a acsi irq
dma_nak();
}
}
static void mist_get_dmastate()
{
unsigned char buffer[16];
EnableFpga();
spi8(ST_GET_DMASTATE);
spi_read(buffer, 16, 0);
DisableFpga();
if (buffer[10] & 0x01) handle_acsi(buffer);
}
static void fill_tx(unsigned char fill, unsigned int len, unsigned char index)
{
user_io_set_index(index);
user_io_set_download(1);
uint16_t wfill = (fill << 8) | fill;
len /= 2;
EnableFpga();
spi8(UIO_FILE_TX_DAT);
while(len--) spi_w(wfill);
DisableFpga();
user_io_set_download(0);
}
void tos_load_cartridge(const char *name)
{
if (name) strncpy(config.cart_img, name, 11);
// upload cartridge
if (config.cart_img[0] && FileExists(config.cart_img))
{
user_io_file_tx(config.cart_img, 0x02);
tos_debugf("%s uploaded", config.cart_img);
return;
}
// erase that ram area to remove any previously uploaded
// image
tos_debugf("Erasing cart memory");
fill_tx(0xff, 128 * 1024, 0x02);
}
char tos_cartridge_is_inserted()
{
return config.cart_img[0];
}
void tos_poll()
{
static unsigned long timer = 0;
mist_get_dmastate();
// check the user button
if (!user_io_osd_is_visible() && user_io_user_button())
{
if (!timer) timer = GetTimer(1000);
else if (timer != 1)
{
if (CheckTimer(timer))
{
tos_reset(1);
timer = 1;
}
}
}
else
{
timer = 0;
}
}
void tos_update_sysctrl(unsigned long n)
{
config.system_ctrl = n;
mist_set_control(config.system_ctrl);
}
const char *tos_get_disk_name(int index)
{
const char *name = 0;
if(index <= 1) name = get_image_name(index);
else
{
if (!hdd_image[index & 1].size) name = 0;
else
{
name = strrchr(hdd_image[index & 1].name, '/');
if (!name) name = hdd_image[index & 1].name; else name++;
}
}
return name ? name : "* no disk *";
}
const char *tos_get_image_name()
{
char *p = strrchr(config.tos_img, '/');
return p ? p+1 : config.tos_img;
}
const char *tos_get_cartridge_name()
{
if (!config.cart_img[0]) return "* no cartridge *";
char *p = strrchr(config.cart_img, '/');
return p ? p + 1 : config.cart_img;
}
char tos_disk_is_inserted(int index)
{
if (index <= 1) return (get_image_name(index) != NULL);
return hdd_image[index & 1].size != 0;
}
static void tos_select_hdd_image(int i, const char *name)
{
tos_debugf("Select ACSI%c image %s", '0' + i, name);
strcpy(config.acsi_img[i], name);
if (!strlen(name))
{
FileClose(&hdd_image[i]);
hdd_image[i].size = 0;
config.system_ctrl &= ~(TOS_ACSI0_ENABLE << i);
}
else
{
if (FileOpen(&hdd_image[i], name))
{
config.system_ctrl |= (TOS_ACSI0_ENABLE << i);
}
}
// update system control
mist_set_control(config.system_ctrl);
}
void tos_insert_disk(int index, const char *name)
{
if (index <= 1) user_io_file_mount(name, index);
else tos_select_hdd_image(index & 1, name);
}
// force ejection of all disks (SD card has been removed)
void tos_eject_all()
{
for (int i = 0; i < 4; i++) tos_insert_disk(i, "");
}
unsigned long tos_system_ctrl(void)
{
return config.system_ctrl;
}
static void tos_upload_mist2()
{
// clear first 16k
tos_debugf("Clear first 16k");
fill_tx(0, 16 * 1024, 0x03);
// upload and verify tos image
int len = FileLoad(config.tos_img, 0, 0);
if (len)
{
tos_debugf("TOS.IMG:\n size = %d", len);
if (len >= 256 * 1024) user_io_file_tx(config.tos_img, 0);
else if (len == 192 * 1024) user_io_file_tx(config.tos_img, 0x01);
else tos_debugf("WARNING: Unexpected TOS size!");
}
else
{
tos_debugf("Unable to find tos.img");
return;
}
tos_load_cartridge(NULL);
for (int i = 0; i < 2; i++)
{
if (FileExists(config.acsi_img[i]))
{
tos_select_hdd_image(i, config.acsi_img[i]);
}
}
}
void tos_reset(char cold)
{
tos_update_sysctrl(config.system_ctrl | TOS_CONTROL_CPU_RESET); // set reset
if (cold) tos_upload_mist2();
tos_update_sysctrl(config.system_ctrl & ~TOS_CONTROL_CPU_RESET); // release reset
}
void tos_upload(const char *name)
{
if(name) strcpy(config.tos_img, name);
tos_reset(1);
}
// load/init configuration
void tos_config_load(int slot)
{
char name[64] = { CONFIG_FILENAME };
static char last_slot = 0;
char new_slot;
tos_eject_all();
new_slot = (slot == -1) ? last_slot : slot;
// set default values
config.system_ctrl = TOS_MEMCONFIG_4M | TOS_CONTROL_BLITTER | TOS_CONTROL_VIDEO_COLOR;
strcpy(config.tos_img, user_io_get_core_path());
strcat(config.tos_img, "/TOS.IMG");
config.cart_img[0] = 0;
strcpy(config.acsi_img[0], "HARDDISK.VHD");
config.acsi_img[1][0] = 0;
config.video_adjust[0] = config.video_adjust[1] = 0;
config.cdc_control_redirect = 0;
// try to load config
name[7] = '0' + new_slot;
int len = FileLoadConfig(name, 0, 0);
tos_debugf("Configuration file size: %d (should be %d)", len, sizeof(tos_config_t));
if (len == sizeof(tos_config_t)) FileLoadConfig(name, &config, sizeof(tos_config_t));
// ethernet is auto detected later
config.system_ctrl &= ~TOS_CONTROL_ETHERNET;
}
// save configuration
void tos_config_save(int slot)
{
char name[64] = { CONFIG_FILENAME };
name[7] = '0' + slot;
FileSaveConfig(name, &config, sizeof(config));
}
// configuration file check
int tos_config_exists(int slot)
{
char name[64] = { CONFIG_FILENAME };
name[7] = '0' + slot;
return FileLoadConfig(name, 0, 0);
}