aiie/apple/hd32.cpp

417 lines
9.2 KiB
C++
Raw Normal View History

#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>
2020-07-04 11:41:32 +00:00
#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
2020-07-04 18:46:31 +00:00
#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()
{
}
2017-12-30 20:20:34 +00:00
bool HD32::Serialize(int8_t fd)
{
2020-07-04 18:46:31 +00:00
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);
2017-12-30 20:20:34 +00:00
}
bool HD32::Deserialize(int8_t fd)
{
2020-07-04 18:46:31 +00:00
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...
2020-07-04 18:46:31 +00:00
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;
2017-12-30 20:20:34 +00:00
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
2020-07-04 11:41:32 +00:00
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);
2020-06-28 19:24:49 +00:00
}
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) {
2020-06-28 19:24:49 +00:00
// FIXME
#ifndef TEENSYDUINO
2020-06-28 19:24:49 +00:00
printf("ERROR: failed to write to hd file? errno %d\n", errno);
#endif
2020-06-28 19:24:49 +00:00
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;
}
}