/*
|
/*
|
* disk.c -- disk simulation
|
* disk.c -- disk simulation
|
*/
|
*/
|
|
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
#include <setjmp.h>
|
#include <setjmp.h>
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "console.h"
|
#include "console.h"
|
#include "error.h"
|
#include "error.h"
|
#include "except.h"
|
#include "except.h"
|
#include "cpu.h"
|
#include "cpu.h"
|
#include "timer.h"
|
#include "timer.h"
|
#include "disk.h"
|
#include "disk.h"
|
|
|
|
|
static Bool debug = false;
|
static Bool debug = false;
|
|
|
static FILE *diskImage;
|
static FILE *diskImage;
|
static long totalSectors;
|
static long totalSectors;
|
|
|
static Word diskCtrl;
|
static Word diskCtrl;
|
static Word diskCnt;
|
static Word diskCnt;
|
static Word diskSct;
|
static Word diskSct;
|
static Word diskCap;
|
static Word diskCap;
|
|
|
static Byte diskBuffer[8 * SECTOR_SIZE];
|
static Byte diskBuffer[8 * SECTOR_SIZE];
|
|
|
static long lastSct;
|
static long lastSct;
|
|
|
|
|
static Word readWord(Byte *p) {
|
static Word readWord(Byte *p) {
|
Word data;
|
Word data;
|
|
|
data = ((Word) *(p + 0)) << 24 |
|
data = ((Word) *(p + 0)) << 24 |
|
((Word) *(p + 1)) << 16 |
|
((Word) *(p + 1)) << 16 |
|
((Word) *(p + 2)) << 8 |
|
((Word) *(p + 2)) << 8 |
|
((Word) *(p + 3)) << 0;
|
((Word) *(p + 3)) << 0;
|
return data;
|
return data;
|
}
|
}
|
|
|
|
|
static void writeWord(Byte *p, Word data) {
|
static void writeWord(Byte *p, Word data) {
|
*(p + 0) = (Byte) (data >> 24);
|
*(p + 0) = (Byte) (data >> 24);
|
*(p + 1) = (Byte) (data >> 16);
|
*(p + 1) = (Byte) (data >> 16);
|
*(p + 2) = (Byte) (data >> 8);
|
*(p + 2) = (Byte) (data >> 8);
|
*(p + 3) = (Byte) (data >> 0);
|
*(p + 3) = (Byte) (data >> 0);
|
}
|
}
|
|
|
|
|
static void diskCallback(int n) {
|
static void diskCallback(int n) {
|
int numScts;
|
int numScts;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** DISK CALLBACK, n = %d ****\n", n);
|
cPrintf("\n**** DISK CALLBACK, n = %d ****\n", n);
|
}
|
}
|
if (n == 0) {
|
if (n == 0) {
|
/* startup time expired */
|
/* startup time expired */
|
diskCap = totalSectors;
|
diskCap = totalSectors;
|
diskCtrl |= DISK_READY;
|
diskCtrl |= DISK_READY;
|
return;
|
return;
|
}
|
}
|
/* disk read or write */
|
/* disk read or write */
|
numScts = ((diskCnt - 1) & 0x07) + 1;
|
numScts = ((diskCnt - 1) & 0x07) + 1;
|
if (diskCap != 0 &&
|
if (diskCap != 0 &&
|
diskSct < diskCap &&
|
diskSct < diskCap &&
|
diskSct + numScts <= diskCap) {
|
diskSct + numScts <= diskCap) {
|
/* do the transfer */
|
/* do the transfer */
|
if (fseek(diskImage, diskSct * SECTOR_SIZE, SEEK_SET) != 0) {
|
if (fseek(diskImage, diskSct * SECTOR_SIZE, SEEK_SET) != 0) {
|
error("cannot position to sector in disk image");
|
error("cannot position to sector in disk image");
|
}
|
}
|
if (diskCtrl & DISK_WRT) {
|
if (diskCtrl & DISK_WRT) {
|
/* buffer --> disk */
|
/* buffer --> disk */
|
if (fwrite(diskBuffer, SECTOR_SIZE, numScts, diskImage) != numScts) {
|
if (fwrite(diskBuffer, SECTOR_SIZE, numScts, diskImage) != numScts) {
|
error("cannot write to disk image");
|
error("cannot write to disk image");
|
}
|
}
|
} else {
|
} else {
|
/* disk --> buffer */
|
/* disk --> buffer */
|
if (fread(diskBuffer, SECTOR_SIZE, numScts, diskImage) != numScts) {
|
if (fread(diskBuffer, SECTOR_SIZE, numScts, diskImage) != numScts) {
|
error("cannot read from disk image");
|
error("cannot read from disk image");
|
}
|
}
|
}
|
}
|
lastSct = (long) diskSct + (long) numScts - 1;
|
lastSct = (long) diskSct + (long) numScts - 1;
|
} else {
|
} else {
|
/* sectors requested exceed disk capacity */
|
/* sectors requested exceed disk capacity */
|
/* or we have no disk at all */
|
/* or we have no disk at all */
|
diskCtrl |= DISK_ERR;
|
diskCtrl |= DISK_ERR;
|
}
|
}
|
diskCtrl &= ~DISK_STRT;
|
diskCtrl &= ~DISK_STRT;
|
diskCtrl |= DISK_DONE;
|
diskCtrl |= DISK_DONE;
|
if (diskCtrl & DISK_IEN) {
|
if (diskCtrl & DISK_IEN) {
|
/* raise disk interrupt */
|
/* raise disk interrupt */
|
cpuSetInterrupt(IRQ_DISK);
|
cpuSetInterrupt(IRQ_DISK);
|
}
|
}
|
}
|
}
|
|
|
|
|
Word diskRead(Word addr) {
|
Word diskRead(Word addr) {
|
Word data;
|
Word data;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** DISK READ from 0x%08X", addr);
|
cPrintf("\n**** DISK READ from 0x%08X", addr);
|
}
|
}
|
if (addr == DISK_CTRL) {
|
if (addr == DISK_CTRL) {
|
data = diskCtrl;
|
data = diskCtrl;
|
} else
|
} else
|
if (addr == DISK_CNT) {
|
if (addr == DISK_CNT) {
|
data = diskCnt;
|
data = diskCnt;
|
} else
|
} else
|
if (addr == DISK_SCT) {
|
if (addr == DISK_SCT) {
|
data = diskSct;
|
data = diskSct;
|
} else
|
} else
|
if (addr == DISK_CAP) {
|
if (addr == DISK_CAP) {
|
data = diskCap;
|
data = diskCap;
|
} else
|
} else
|
if (addr & 0x80000) {
|
if (addr & 0x80000) {
|
/* buffer access */
|
/* buffer access */
|
data = readWord(diskBuffer + (addr & 0x0FFC));
|
data = readWord(diskBuffer + (addr & 0x0FFC));
|
} else {
|
} else {
|
/* illegal register */
|
/* illegal register */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
if (debug) {
|
if (debug) {
|
cPrintf(", data = 0x%08X ****\n", data);
|
cPrintf(", data = 0x%08X ****\n", data);
|
}
|
}
|
return data;
|
return data;
|
}
|
}
|
|
|
|
|
void diskWrite(Word addr, Word data) {
|
void diskWrite(Word addr, Word data) {
|
long delta;
|
long delta;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** DISK WRITE to 0x%08X, data = 0x%08X ****\n",
|
cPrintf("\n**** DISK WRITE to 0x%08X, data = 0x%08X ****\n",
|
addr, data);
|
addr, data);
|
}
|
}
|
if (addr == DISK_CTRL) {
|
if (addr == DISK_CTRL) {
|
if (data & DISK_WRT) {
|
if (data & DISK_WRT) {
|
diskCtrl |= DISK_WRT;
|
diskCtrl |= DISK_WRT;
|
} else {
|
} else {
|
diskCtrl &= ~DISK_WRT;
|
diskCtrl &= ~DISK_WRT;
|
}
|
}
|
if (data & DISK_IEN) {
|
if (data & DISK_IEN) {
|
diskCtrl |= DISK_IEN;
|
diskCtrl |= DISK_IEN;
|
} else {
|
} else {
|
diskCtrl &= ~DISK_IEN;
|
diskCtrl &= ~DISK_IEN;
|
}
|
}
|
if (data & DISK_STRT) {
|
if (data & DISK_STRT) {
|
diskCtrl |= DISK_STRT;
|
diskCtrl |= DISK_STRT;
|
diskCtrl &= ~DISK_ERR;
|
diskCtrl &= ~DISK_ERR;
|
diskCtrl &= ~DISK_DONE;
|
diskCtrl &= ~DISK_DONE;
|
/* only start a disk operation if disk is present */
|
/* only start a disk operation if disk is present */
|
if (diskCap != 0) {
|
if (diskCap != 0) {
|
delta = labs((long) diskSct - lastSct);
|
delta = labs((long) diskSct - lastSct);
|
if (delta > diskCap) {
|
if (delta > diskCap) {
|
delta = diskCap;
|
delta = diskCap;
|
}
|
}
|
timerStart(DISK_DELAY + (delta * DISK_SEEK) / diskCap,
|
timerStart(DISK_DELAY_USEC + (delta * DISK_SEEK_USEC) / diskCap,
|
diskCallback, 1);
|
diskCallback, 1);
|
}
|
}
|
} else {
|
} else {
|
diskCtrl &= ~DISK_STRT;
|
diskCtrl &= ~DISK_STRT;
|
if (data & DISK_ERR) {
|
if (data & DISK_ERR) {
|
diskCtrl |= DISK_ERR;
|
diskCtrl |= DISK_ERR;
|
} else {
|
} else {
|
diskCtrl &= ~DISK_ERR;
|
diskCtrl &= ~DISK_ERR;
|
}
|
}
|
if (data & DISK_DONE) {
|
if (data & DISK_DONE) {
|
diskCtrl |= DISK_DONE;
|
diskCtrl |= DISK_DONE;
|
} else {
|
} else {
|
diskCtrl &= ~DISK_DONE;
|
diskCtrl &= ~DISK_DONE;
|
}
|
}
|
}
|
}
|
if ((diskCtrl & DISK_IEN) != 0 &&
|
if ((diskCtrl & DISK_IEN) != 0 &&
|
(diskCtrl & DISK_DONE) != 0) {
|
(diskCtrl & DISK_DONE) != 0) {
|
/* raise disk interrupt */
|
/* raise disk interrupt */
|
cpuSetInterrupt(IRQ_DISK);
|
cpuSetInterrupt(IRQ_DISK);
|
} else {
|
} else {
|
/* lower disk interrupt */
|
/* lower disk interrupt */
|
cpuResetInterrupt(IRQ_DISK);
|
cpuResetInterrupt(IRQ_DISK);
|
}
|
}
|
} else
|
} else
|
if (addr == DISK_CNT) {
|
if (addr == DISK_CNT) {
|
diskCnt = data;
|
diskCnt = data;
|
} else
|
} else
|
if (addr == DISK_SCT) {
|
if (addr == DISK_SCT) {
|
diskSct = data;
|
diskSct = data;
|
} else
|
} else
|
if (addr == DISK_CAP) {
|
if (addr == DISK_CAP) {
|
/* this register is read-only */
|
/* this register is read-only */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
} else
|
} else
|
if (addr & 0x80000) {
|
if (addr & 0x80000) {
|
/* buffer access */
|
/* buffer access */
|
writeWord(diskBuffer + (addr & 0x0FFC), data);
|
writeWord(diskBuffer + (addr & 0x0FFC), data);
|
} else {
|
} else {
|
/* illegal register */
|
/* illegal register */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
}
|
}
|
|
|
|
|
void diskReset(void) {
|
void diskReset(void) {
|
cPrintf("Resetting Disk...\n");
|
cPrintf("Resetting Disk...\n");
|
diskCtrl = 0;
|
diskCtrl = 0;
|
diskCnt = 0;
|
diskCnt = 0;
|
diskSct = 0;
|
diskSct = 0;
|
diskCap = 0;
|
diskCap = 0;
|
lastSct = 0;
|
lastSct = 0;
|
if (totalSectors != 0) {
|
if (totalSectors != 0) {
|
cPrintf("Disk of size %ld sectors (%ld bytes) installed.\n",
|
cPrintf("Disk of size %ld sectors (%ld bytes) installed.\n",
|
totalSectors, totalSectors * SECTOR_SIZE);
|
totalSectors, totalSectors * SECTOR_SIZE);
|
timerStart(DISK_STARTUP, diskCallback, 0);
|
timerStart(DISK_START_USEC, diskCallback, 0);
|
}
|
}
|
}
|
}
|
|
|
|
|
void diskInit(char *diskImageName) {
|
void diskInit(char *diskImageName) {
|
long numBytes;
|
long numBytes;
|
|
|
if (diskImageName == NULL) {
|
if (diskImageName == NULL) {
|
/* do not install disk */
|
/* do not install disk */
|
diskImage = NULL;
|
diskImage = NULL;
|
totalSectors = 0;
|
totalSectors = 0;
|
} else {
|
} else {
|
/* try to install disk */
|
/* try to install disk */
|
diskImage = fopen(diskImageName, "r+b");
|
diskImage = fopen(diskImageName, "r+b");
|
if (diskImage == NULL) {
|
if (diskImage == NULL) {
|
error("cannot open disk image '%s'", diskImageName);
|
error("cannot open disk image '%s'", diskImageName);
|
}
|
}
|
fseek(diskImage, 0, SEEK_END);
|
fseek(diskImage, 0, SEEK_END);
|
numBytes = ftell(diskImage);
|
numBytes = ftell(diskImage);
|
fseek(diskImage, 0, SEEK_SET);
|
fseek(diskImage, 0, SEEK_SET);
|
if (numBytes % SECTOR_SIZE != 0) {
|
if (numBytes % SECTOR_SIZE != 0) {
|
error("disk image '%s' does not contain an integral number of sectors",
|
error("disk image '%s' does not contain an integral number of sectors",
|
diskImageName);
|
diskImageName);
|
}
|
}
|
totalSectors = numBytes / SECTOR_SIZE;
|
totalSectors = numBytes / SECTOR_SIZE;
|
}
|
}
|
diskReset();
|
diskReset();
|
}
|
}
|
|
|
|
|
void diskExit(void) {
|
void diskExit(void) {
|
if (diskImage == NULL) {
|
if (diskImage == NULL) {
|
/* disk not installed */
|
/* disk not installed */
|
return;
|
return;
|
}
|
}
|
fclose(diskImage);
|
fclose(diskImage);
|
}
|
}
|
|
|