mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-09-10 10:54:27 +00:00
417 lines
9.2 KiB
C++
417 lines
9.2 KiB
C++
#include "hd32.h"
|
|
|
|
/* AppleWin 32-MB hard drive emulation.
|
|
*
|
|
* cf. https://github.com/AppleWin/AppleWin/tree/master/firmware/HDD
|
|
*
|
|
*
|
|
* General interface is outlined in
|
|
* https://github.com/AppleWin/AppleWin/blob/master/source/Harddisk.cpp
|
|
*/
|
|
|
|
#ifdef TEENSYDUINO
|
|
#include <Arduino.h>
|
|
#include "teensy-println.h"
|
|
#else
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include "applemmu.h" // for FLOATING
|
|
|
|
#include "globals.h"
|
|
|
|
#include "hd32-rom.h"
|
|
|
|
#define HD32_BLOCKSIZE 512
|
|
|
|
#define HD32MAGIC 0xF5
|
|
|
|
#define DEVICE_OK 0x00
|
|
#define DEVICE_UNKNOWN_ERROR 0x28
|
|
#define DEVICE_IO_ERROR 0x27
|
|
#define DEVICE_WRITE_PROTECTED 0x28
|
|
#define DEVICE_OFF_LINE 0x2F
|
|
|
|
// Switches...
|
|
#define HD32_EXEC_RETSTAT 0x0
|
|
#define HD32_STATUS 0x1
|
|
#define HD32_COMMAND 0x2
|
|
#define HD32_UNITNUM 0x3
|
|
#define HD32_LBBUF 0x4
|
|
#define HD32_HBBUF 0x5
|
|
#define HD32_LBBLOCKNUM 0x6
|
|
#define HD32_HBBLOCKNUM 0x7
|
|
#define HD32_NEXTBYTE 0x8
|
|
|
|
// Commands
|
|
#define CMD_STATUS 0x0
|
|
#define CMD_READ 0x1
|
|
#define CMD_WRITE 0x2
|
|
#define CMD_FORMAT 0x3
|
|
|
|
HD32::HD32(AppleMMU *mmu)
|
|
{
|
|
this->mmu = mmu;
|
|
Reset();
|
|
}
|
|
|
|
HD32::~HD32()
|
|
{
|
|
}
|
|
|
|
bool HD32::Serialize(int8_t fd)
|
|
{
|
|
uint8_t buf[19] = { HD32MAGIC,
|
|
driveSelected,
|
|
unitSelected,
|
|
command,
|
|
enabled,
|
|
errorState[0],
|
|
errorState[1],
|
|
(uint8_t)((memBlock[0] >> 8) & 0xFF),
|
|
(uint8_t)((memBlock[0] ) & 0xFF),
|
|
(uint8_t)((memBlock[1] >> 8) & 0xFF),
|
|
(uint8_t)((memBlock[1] ) & 0xFF),
|
|
(uint8_t)((cursor[0] >> 24) & 0xFF),
|
|
(uint8_t)((cursor[0] >> 16) & 0xFF),
|
|
(uint8_t)((cursor[0] >> 8) & 0xFF),
|
|
(uint8_t)((cursor[0] ) & 0xFF),
|
|
(uint8_t)((cursor[1] >> 24) & 0xFF),
|
|
(uint8_t)((cursor[1] >> 16) & 0xFF),
|
|
(uint8_t)((cursor[1] >> 8) & 0xFF),
|
|
(uint8_t)((cursor[1] ) & 0xFF)
|
|
};
|
|
if (g_filemanager->write(fd, buf, 19) != 10)
|
|
return false;
|
|
|
|
for (int i=0; i<2; i++) {
|
|
const char *fn = diskName(i);
|
|
if (g_filemanager->write(fd, fn, strlen(fn)+1) != strlen(fn)+1) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
buf[0] = HD32MAGIC;
|
|
return (g_filemanager->write(fd, buf, 1) == 1);
|
|
}
|
|
|
|
bool HD32::Deserialize(int8_t fd)
|
|
{
|
|
uint8_t buf[255];
|
|
if (g_filemanager->read(fd, buf, 19) != 19) {
|
|
return false;
|
|
}
|
|
if (buf[0] != HD32MAGIC)
|
|
return false;
|
|
|
|
driveSelected = buf[1];
|
|
unitSelected = buf[2];
|
|
command = buf[3];
|
|
enabled = buf[4];
|
|
errorState[0] = buf[5];
|
|
errorState[1] = buf[6];
|
|
memBlock[0] = buf[7];
|
|
memBlock[0] <<= 8; memBlock[0] |= buf[8];
|
|
memBlock[1] = buf[9];
|
|
memBlock[1] <<= 8; memBlock[1] |= buf[10];
|
|
cursor[0] = buf[11];
|
|
cursor[0] <<= 8; cursor[0] |= buf[12];
|
|
cursor[0] <<= 8; cursor[0] |= buf[13];
|
|
cursor[0] <<= 8; cursor[0] |= buf[14];
|
|
cursor[1] = buf[15];
|
|
cursor[1] <<= 8; cursor[1] |= buf[16];
|
|
cursor[1] <<= 8; cursor[1] |= buf[17];
|
|
cursor[1] <<= 8; cursor[1] |= buf[18];
|
|
|
|
cachedBlockNum = -1; // just invalidate the cache; it will reload...
|
|
|
|
for (int i=0; i<2; i++) {
|
|
uint32_t ptr = 0;
|
|
// FIXME: MAXPATH check!
|
|
while (1) {
|
|
if (g_filemanager->read(fd, &buf[ptr++], 1) != 1)
|
|
return false;
|
|
if (buf[ptr-1] == 0)
|
|
break;
|
|
}
|
|
if (strlen((char *)buf)) {
|
|
// FIXME: this tromps on error and some other vars ... that we just restored
|
|
insertDisk(i, (char *)buf);
|
|
}
|
|
}
|
|
|
|
if (g_filemanager->read(fd, buf, 1) != 1)
|
|
return false;
|
|
|
|
if (buf[0] != HD32MAGIC)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void HD32::Reset()
|
|
{
|
|
enabled = 1;
|
|
|
|
fd[0] = fd[1] = -1;
|
|
errorState[0] = errorState[1] = 0;
|
|
memBlock[0] = memBlock[1] = 0;
|
|
diskBlock[0] = diskBlock[1] = 0;
|
|
driveSelected = 0;
|
|
command = CMD_STATUS;
|
|
|
|
cachedBlockNum = -1;
|
|
}
|
|
|
|
uint8_t HD32::readSwitches(uint8_t s)
|
|
{
|
|
uint8_t ret = DEVICE_OK;
|
|
|
|
if (!enabled) {
|
|
return DEVICE_IO_ERROR;
|
|
}
|
|
|
|
switch (s) {
|
|
case HD32_EXEC_RETSTAT:
|
|
switch (command) {
|
|
case CMD_STATUS:
|
|
// set ret to DEVICE_IO_ERROR & set error state=true if no image loaded
|
|
if (fd[driveSelected] == -1) {
|
|
// Nothing inserted
|
|
ret = DEVICE_IO_ERROR;
|
|
errorState[driveSelected] = 1;
|
|
} else {
|
|
ret = DEVICE_OK;
|
|
errorState[driveSelected] = 0;
|
|
}
|
|
break;
|
|
|
|
case CMD_READ:
|
|
// FIXME: if diskblock[selectedDrive] >= disk image size, set/return io error
|
|
errorState[driveSelected] = 0;
|
|
ret = DEVICE_OK;
|
|
|
|
cursor[driveSelected] = diskBlock[driveSelected] * HD32_BLOCKSIZE;
|
|
if (!readBlockFromSelectedDrive()) {
|
|
ret = DEVICE_IO_ERROR;
|
|
errorState[driveSelected] = 1;
|
|
}
|
|
break;
|
|
|
|
case CMD_WRITE:
|
|
// FIXME: if diskblock[selectedDrive] >= disk image size, set/return io error
|
|
if (!writeBlockToSelectedDrive()){
|
|
ret = DEVICE_IO_ERROR;
|
|
errorState[driveSelected] = 1;
|
|
}
|
|
break;
|
|
|
|
case CMD_FORMAT:
|
|
// Currently ignored. FIXME: make this zero out a 32MB file?
|
|
break;
|
|
|
|
default:
|
|
errorState[driveSelected] = 1;
|
|
ret = DEVICE_UNKNOWN_ERROR;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HD32_STATUS:
|
|
ret = errorState[driveSelected];
|
|
break;
|
|
|
|
case HD32_COMMAND:
|
|
ret = command;
|
|
break;
|
|
|
|
case HD32_UNITNUM:
|
|
ret = unitSelected;
|
|
break;
|
|
|
|
case HD32_LBBUF:
|
|
ret = memBlock[driveSelected] & 0x00FF;
|
|
break;
|
|
case HD32_HBBUF:
|
|
ret = ((memBlock[driveSelected] & 0xFF00) >> 8);
|
|
break;
|
|
|
|
case HD32_LBBLOCKNUM:
|
|
ret = diskBlock[driveSelected] & 0x00FF;
|
|
break;
|
|
case HD32_HBBLOCKNUM:
|
|
ret = ((diskBlock[driveSelected] & 0xFF00) >> 8);
|
|
break;
|
|
|
|
case HD32_NEXTBYTE:
|
|
ret = readNextByteFromSelectedDrive();
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void HD32::writeSwitches(uint8_t s, uint8_t v)
|
|
{
|
|
if (!enabled)
|
|
return;
|
|
|
|
switch (s) {
|
|
case HD32_COMMAND:
|
|
command = v;
|
|
break;
|
|
case HD32_UNITNUM:
|
|
unitSelected = v;
|
|
// FIXME: verify slot#?
|
|
driveSelected = (v & 0x80) ? 1 : 0;
|
|
break;
|
|
case HD32_LBBUF:
|
|
memBlock[driveSelected] = (memBlock[driveSelected] & 0xFF00) | v;
|
|
break;
|
|
case HD32_HBBUF:
|
|
memBlock[driveSelected] = (memBlock[driveSelected] & 0x00FF) | (v << 8);
|
|
break;
|
|
case HD32_LBBLOCKNUM:
|
|
diskBlock[driveSelected] = (diskBlock[driveSelected] & 0xFF00) | v;
|
|
break;
|
|
case HD32_HBBLOCKNUM:
|
|
diskBlock[driveSelected] = (diskBlock[driveSelected] & 0x00FF) | (v << 8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HD32::loadROM(uint8_t *toWhere)
|
|
{
|
|
#ifdef TEENSYDUINO
|
|
println("loading HD32 rom");
|
|
for (uint16_t i=0; i<=0xFF; i++) {
|
|
toWhere[i] = pgm_read_byte(&romData[i]);
|
|
}
|
|
#else
|
|
printf("loading HD32 rom\n");
|
|
memcpy(toWhere, romData, 256);
|
|
#endif
|
|
}
|
|
|
|
uint8_t HD32::readNextByteFromSelectedDrive()
|
|
{
|
|
uint8_t ret = 0;
|
|
|
|
if (fd[driveSelected] == -1) {
|
|
return 0;
|
|
}
|
|
|
|
int32_t blockToRead = cursor[driveSelected] >> 9; // 512-byte block number
|
|
if (blockToRead != cachedBlockNum) {
|
|
if (g_filemanager->lseek(fd[driveSelected], blockToRead*512, SEEK_SET) != blockToRead*512) {
|
|
goto err;
|
|
}
|
|
ssize_t nread = g_filemanager->read(fd[driveSelected], cachedBlock, 512);
|
|
if (nread != 512) {
|
|
goto err;
|
|
}
|
|
cachedBlockNum = blockToRead;
|
|
|
|
}
|
|
|
|
ret = cachedBlock[cursor[driveSelected] & 0x1FF];
|
|
cursor[driveSelected]++;
|
|
return ret;
|
|
|
|
err:
|
|
// memset(cachedBlock, 0, sizeof(cachedBlock));
|
|
// cachedBlockNum = -1;
|
|
return false;
|
|
}
|
|
|
|
// Based on diskBlock[driveSelected]; updates cursor[driveSelected].
|
|
// Populates the local cache as well as the memory block pointed to.
|
|
bool HD32::readBlockFromSelectedDrive()
|
|
{
|
|
if (fd[driveSelected]==-1)
|
|
return false;
|
|
|
|
cursor[driveSelected] = diskBlock[driveSelected] * HD32_BLOCKSIZE;
|
|
int32_t blockToRead = cursor[driveSelected] >> 9; // 512-byte block number
|
|
if (blockToRead != cachedBlockNum) {
|
|
if (g_filemanager->lseek(fd[driveSelected], blockToRead*512, SEEK_SET) != blockToRead*512) {
|
|
goto err;
|
|
}
|
|
ssize_t nread = g_filemanager->read(fd[driveSelected], cachedBlock, 512);
|
|
if (nread != 512) {
|
|
goto err;
|
|
}
|
|
cachedBlockNum = blockToRead;
|
|
}
|
|
|
|
for (uint16_t i=0; i<HD32_BLOCKSIZE; i++) {
|
|
mmu->write(memBlock[driveSelected] + i, cachedBlock[i]);
|
|
}
|
|
|
|
return true;
|
|
|
|
err:
|
|
// memset(cachedBlock, 0, sizeof(cachedBlock));
|
|
// cachedBlockNum = -1;
|
|
return false;
|
|
}
|
|
|
|
bool HD32::writeBlockToSelectedDrive()
|
|
{
|
|
cachedBlockNum = -1; // just invalidate any cache we have
|
|
|
|
if (fd[driveSelected]==-1)
|
|
return false;
|
|
|
|
for (uint16_t i=0; i<HD32_BLOCKSIZE; i++) {
|
|
cachedBlock[i] = mmu->read(memBlock[driveSelected] + i);
|
|
}
|
|
if (g_filemanager->lseek(fd[driveSelected], diskBlock[driveSelected]*HD32_BLOCKSIZE, SEEK_SET) != diskBlock[driveSelected]*HD32_BLOCKSIZE ||
|
|
g_filemanager->write(fd[driveSelected], cachedBlock, HD32_BLOCKSIZE) != HD32_BLOCKSIZE) {
|
|
// FIXME
|
|
#ifndef TEENSYDUINO
|
|
printf("ERROR: failed to write to hd file? errno %d\n", errno);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void HD32::setEnabled(uint8_t e)
|
|
{
|
|
enabled = e;
|
|
}
|
|
|
|
const char *HD32::diskName(int8_t num)
|
|
{
|
|
if (fd[num] != -1)
|
|
return g_filemanager->fileName(fd[num]);
|
|
|
|
return "";
|
|
}
|
|
|
|
void HD32::insertDisk(int8_t driveNum, const char *filename)
|
|
{
|
|
ejectDisk(driveNum);
|
|
fd[driveNum] = g_filemanager->openFile(filename);
|
|
errorState[driveNum] = 0;
|
|
enabled = 1;
|
|
}
|
|
|
|
void HD32::ejectDisk(int8_t driveNum)
|
|
{
|
|
if (fd[driveNum] != -1) {
|
|
g_filemanager->closeFile(fd[driveNum]);
|
|
fd[driveNum] = -1;
|
|
}
|
|
}
|
|
|