mirror of https://github.com/JorjBauer/aiie.git
first working Woz code (read-only, and only non-copyprotected disks)
This commit is contained in:
parent
75342f5a49
commit
03b0a141bc
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ CXXFLAGS=-Wall -I/usr/include/SDL2 -I .. -I . -I apple -I nix -I sdl -I/usr/loca
|
|||
|
||||
TSRC=cpu.cpp util/testharness.cpp
|
||||
|
||||
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o images.o apple/appleui.o vmram.o bios.o apple/noslotclock.o
|
||||
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o images.o apple/appleui.o vmram.o bios.o apple/noslotclock.o apple/woz.o apple/crc32.o apple/woz-serializer.o
|
||||
|
||||
FBOBJS=linuxfb/linux-speaker.o linuxfb/fb-display.o linuxfb/linux-keyboard.o linuxfb/fb-paddles.o nix/nix-filemanager.o linuxfb/aiie.o linuxfb/linux-printer.o nix/nix-clock.o nix/nix-prefs.o
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#include "crc32.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if 1
|
||||
// Prepopulated CRC table for the CRC32 polynomial
|
||||
|
||||
static uint32_t preload32[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
void preload_crc()
|
||||
{
|
||||
}
|
||||
#else
|
||||
// Dynamically rebuild the CRC table at runtime
|
||||
|
||||
#define poly32 (0xEDB88320L) // CRC32 polynomial
|
||||
static uint32_t preload32[256]; // Precomputed CRC table for the CRC32 polynomial
|
||||
|
||||
void preload_crc()
|
||||
{
|
||||
int i, j;
|
||||
uint32_t data;
|
||||
uint32_t temp1, temp2;
|
||||
|
||||
for (i=0; i<256; i++) {
|
||||
data=0;
|
||||
temp2=i;
|
||||
for (j=0; j<8; j++) {
|
||||
temp1=(data^temp2)&1;
|
||||
data = (data>>1) ^ (temp1*poly32);
|
||||
temp2>>=1;
|
||||
}
|
||||
preload32[i] = data;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t compute_crc_32(unsigned char *buffer, unsigned long length)
|
||||
{
|
||||
uint32_t ret = ~0U;
|
||||
|
||||
for (unsigned long i=0; i<length; i++)
|
||||
ret = (ret>>8) ^ preload32[(ret ^ buffer[i])&0xFF];
|
||||
|
||||
return ret ^ ~0U;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void preload_crc();
|
||||
uint32_t compute_crc_32(unsigned char *buffer, unsigned long length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
319
apple/diskii.cpp
319
apple/diskii.cpp
|
@ -21,34 +21,33 @@
|
|||
|
||||
DiskII::DiskII(AppleMMU *mmu)
|
||||
{
|
||||
this->trackBuffer = new LRingBuffer(NIBTRACKSIZE);
|
||||
|
||||
this->mmu = mmu;
|
||||
|
||||
curPhase[0] = curPhase[1] = 0;
|
||||
curHalfTrack[0] = curHalfTrack[1] = 0;
|
||||
|
||||
trackToFlush = -1;
|
||||
curWozTrack[0] = curWozTrack[1] = 0xFF;
|
||||
|
||||
writeMode = false;
|
||||
writeProt = false; // FIXME: expose an interface to this
|
||||
readWriteLatch = 0x00;
|
||||
sequencer = 0;
|
||||
dataRegister = 0;
|
||||
lastDiskRead[0] = lastDiskRead[1] = 0;
|
||||
|
||||
disk[0] = disk[1] = -1;
|
||||
disk[0] = disk[1] = NULL;
|
||||
indicatorIsOn[0] = indicatorIsOn[1] = 0;
|
||||
selectedDisk = 0;
|
||||
diskType[0] = diskType[1] = dosDisk;
|
||||
}
|
||||
|
||||
DiskII::~DiskII()
|
||||
{
|
||||
delete this->trackBuffer; this->trackBuffer = NULL;
|
||||
}
|
||||
|
||||
bool DiskII::Serialize(int8_t fd)
|
||||
{
|
||||
/* Make sure to flush anything to disk first */
|
||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
||||
return false;
|
||||
|
||||
// FIXME: all the new variables are missing
|
||||
|
||||
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
||||
|
||||
|
@ -62,19 +61,21 @@ bool DiskII::Serialize(int8_t fd)
|
|||
g_filemanager->writeByte(fd, writeMode);
|
||||
g_filemanager->writeByte(fd, writeProt);
|
||||
|
||||
// Don't save disk[0,1]; save their file names & cursors
|
||||
g_filemanager->SerializeFile(fd, disk[0]);
|
||||
g_filemanager->SerializeFile(fd, disk[1]);
|
||||
for (int i=0; i<2; i++) {
|
||||
if (disk[i]) {
|
||||
g_filemanager->writeByte(fd, 1);
|
||||
} else {
|
||||
g_filemanager->writeByte(fd, 0);
|
||||
}
|
||||
if (!disk[i]->Serialize(fd))
|
||||
return false;
|
||||
}
|
||||
|
||||
g_filemanager->writeByte(fd, indicatorIsOn[0]);
|
||||
g_filemanager->writeByte(fd, indicatorIsOn[1]);
|
||||
|
||||
g_filemanager->writeByte(fd, diskType[0]);
|
||||
g_filemanager->writeByte(fd, diskType[1]);
|
||||
g_filemanager->writeByte(fd, selectedDisk);
|
||||
|
||||
trackBuffer->Serialize(fd);
|
||||
|
||||
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
||||
|
||||
return true;
|
||||
|
@ -82,8 +83,8 @@ bool DiskII::Serialize(int8_t fd)
|
|||
|
||||
bool DiskII::Deserialize(int8_t fd)
|
||||
{
|
||||
/* Make sure to flush anything to disk first */
|
||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
||||
return false;
|
||||
// FIXME: all the new variables are missing
|
||||
|
||||
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
||||
return false;
|
||||
|
@ -99,22 +100,23 @@ bool DiskII::Deserialize(int8_t fd)
|
|||
writeMode = g_filemanager->readByte(fd);
|
||||
writeProt = g_filemanager->readByte(fd);
|
||||
|
||||
disk[0] = g_filemanager->DeserializeFile(fd);
|
||||
disk[1] = g_filemanager->DeserializeFile(fd);
|
||||
for (int i=0; i<2; i++) {
|
||||
if (disk[i])
|
||||
delete disk[i];
|
||||
if (g_filemanager->readByte(fd) == 1) {
|
||||
disk[i] = new WozSerializer();
|
||||
if (!disk[i]->Deserialize(fd))
|
||||
return false;
|
||||
} else {
|
||||
disk[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
indicatorIsOn[0] = g_filemanager->readByte(fd);
|
||||
indicatorIsOn[1] = g_filemanager->readByte(fd);
|
||||
|
||||
diskType[0] = g_filemanager->readByte(fd);
|
||||
diskType[1] = g_filemanager->readByte(fd);
|
||||
|
||||
selectedDisk = g_filemanager->readByte(fd);
|
||||
|
||||
trackBuffer->Deserialize(fd);
|
||||
|
||||
// Reset the dirty caches and whatnot
|
||||
trackToFlush = -1;
|
||||
|
||||
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
@ -127,8 +129,6 @@ void DiskII::Reset()
|
|||
curPhase[0] = curPhase[1] = 0;
|
||||
curHalfTrack[0] = curHalfTrack[1] = 0;
|
||||
|
||||
trackToFlush = -1;
|
||||
|
||||
writeMode = false;
|
||||
writeProt = false; // FIXME: expose an interface to this
|
||||
readWriteLatch = 0x00;
|
||||
|
@ -137,17 +137,12 @@ void DiskII::Reset()
|
|||
ejectDisk(1);
|
||||
}
|
||||
|
||||
// FIXME: why does this need an argument?
|
||||
void DiskII::checkFlush(int8_t track)
|
||||
{
|
||||
if (trackToFlush != -1) {
|
||||
flushTrack(trackToFlush, selectedDisk);
|
||||
trackToFlush = -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DiskII::readSwitches(uint8_t s)
|
||||
{
|
||||
#if 0
|
||||
g_cpu->realtime(); /* cause the CPU to stop processing its outer
|
||||
* loop b/c the disk might need attention immediately */
|
||||
#endif
|
||||
switch (s) {
|
||||
case 0x00: // change stepper motor phase
|
||||
break;
|
||||
|
@ -173,11 +168,13 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
|||
case 0x08: // drive off
|
||||
indicatorIsOn[selectedDisk] = 99;
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: delay a bit? Queue for later drawing?
|
||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
||||
break;
|
||||
case 0x09: // drive on
|
||||
indicatorIsOn[selectedDisk] = 100;
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing?
|
||||
|
||||
// Start the given disk drive spinning
|
||||
lastDiskRead[selectedDisk] = g_cpu->cycles;
|
||||
break;
|
||||
|
||||
case 0x0A: // select drive 1
|
||||
|
@ -189,6 +186,8 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
|||
|
||||
case 0x0C: // shift one read or write byte
|
||||
readWriteLatch = readOrWriteByte();
|
||||
if (readWriteLatch & 0x80)
|
||||
sequencer = 0;
|
||||
break;
|
||||
|
||||
case 0x0D: // load data register (latch)
|
||||
|
@ -213,7 +212,7 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
|||
// FIXME: improve the spin-down here. We need a CPU cycle callback
|
||||
// for some period of time instead of this silly decrement counter.
|
||||
if (!indicatorIsOn[selectedDisk]) {
|
||||
// printf("Unexpected read while disk isn't on?\n");
|
||||
printf("Unexpected read while disk isn't on?\n");
|
||||
indicatorIsOn[selectedDisk] = 100;
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: queue for later drawing?
|
||||
}
|
||||
|
@ -221,6 +220,9 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
|||
// slowly spin it down...
|
||||
if (--indicatorIsOn[selectedDisk] == 0) {
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
|
||||
|
||||
// Stop the given disk drive spinning
|
||||
lastDiskRead[selectedDisk] = 0; // FIXME: magic value. We need a tristate for this.
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -267,10 +269,12 @@ void DiskII::writeSwitches(uint8_t s, uint8_t v)
|
|||
break;
|
||||
|
||||
case 0x0C: // shift one read or write byte
|
||||
readOrWriteByte();
|
||||
if (readOrWriteByte() & 0x80)
|
||||
sequencer = 0;
|
||||
break;
|
||||
|
||||
case 0x0D: // drive write
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case 0x0E: // set read mode
|
||||
|
@ -331,7 +335,7 @@ void DiskII::setPhase(uint8_t phase)
|
|||
|
||||
curHalfTrack[selectedDisk] += _phase_delta[(prevPhase * 4) + phase];
|
||||
curPhase[selectedDisk] = phase;
|
||||
|
||||
|
||||
// Cap at 35 tracks (a normal disk size). Some drives let you go farther,
|
||||
// and we could support that by increasing this limit - but the images
|
||||
// would be different too, so there would be more work to abstract out...
|
||||
|
@ -345,20 +349,12 @@ void DiskII::setPhase(uint8_t phase)
|
|||
// recalibrate! This is where the fun noise goes DaDaDaDaDaDaDaDaDa
|
||||
}
|
||||
|
||||
/*
|
||||
printf("phase %d => %d; curHalfTrack %d => %d\n",
|
||||
prevPhase, curPhase[selectedDisk],
|
||||
prevHalfTrack, curHalfTrack[selectedDisk]);
|
||||
*/
|
||||
|
||||
if (curHalfTrack[selectedDisk]>>1 != prevHalfTrack>>1) {
|
||||
if (curHalfTrack[selectedDisk] != prevHalfTrack) {
|
||||
// We're changing track - flush the old track back to disk
|
||||
checkFlush(prevHalfTrack>>1);
|
||||
printf("track change\n");
|
||||
|
||||
// Prime the cache by reading the current track
|
||||
if (disk[selectedDisk] != -1) {
|
||||
readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
|
||||
}
|
||||
curWozTrack[selectedDisk] = disk[selectedDisk]->trackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2);
|
||||
printf("New half: %d; track: %d\n", curHalfTrack[selectedDisk]*2, curWozTrack[selectedDisk]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,36 +395,22 @@ static bool _endsWithI(const char *s1, const char *s2)
|
|||
void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt)
|
||||
{
|
||||
ejectDisk(driveNum);
|
||||
disk[driveNum] = g_filemanager->openFile(filename);
|
||||
|
||||
disk[driveNum] = new WozSerializer();
|
||||
disk[driveNum]->readFile(filename); // FIXME error checking
|
||||
|
||||
curWozTrack[driveNum] = disk[driveNum]->trackNumberForQuarterTrack(curHalfTrack[driveNum]*2);
|
||||
printf("Cur track: %d\n", curWozTrack[driveNum]);
|
||||
|
||||
if (drawIt)
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, false);
|
||||
|
||||
if (_endsWithI(filename, ".nib")) {
|
||||
diskType[driveNum] = nibDisk;
|
||||
} else if (_endsWithI(filename, ".po")) {
|
||||
diskType[driveNum] = prodosDisk;
|
||||
} else {
|
||||
diskType[driveNum] = dosDisk;
|
||||
#ifndef TEENSYDUINO
|
||||
// debugging: make a nib copy of the image to play with
|
||||
// convertDskToNib("/tmp/debug.nib");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (driveNum == selectedDisk) {
|
||||
readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
|
||||
}
|
||||
}
|
||||
|
||||
void DiskII::ejectDisk(int8_t driveNum)
|
||||
{
|
||||
if (disk[driveNum] != -1) {
|
||||
if (selectedDisk == driveNum) {
|
||||
checkFlush(0); // FIXME: bogus argument
|
||||
trackBuffer->clear();
|
||||
}
|
||||
g_filemanager->closeFile(disk[driveNum]);
|
||||
disk[driveNum] = -1;
|
||||
if (disk[driveNum]) {
|
||||
delete disk[driveNum];
|
||||
disk[driveNum] = NULL;
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, true);
|
||||
}
|
||||
}
|
||||
|
@ -438,125 +420,66 @@ void DiskII::select(int8_t which)
|
|||
if (which != 0 && which != 1)
|
||||
return;
|
||||
|
||||
if (which != selectedDisk) {
|
||||
indicatorIsOn[selectedDisk] = 0;
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
|
||||
printf("Select disk %d\n", which);
|
||||
|
||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
||||
if (which != selectedDisk) {
|
||||
indicatorIsOn[selectedDisk] = 100; // spindown time (fixme)
|
||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
|
||||
|
||||
// set the selected disk drive
|
||||
selectedDisk = which;
|
||||
|
||||
// Preread the current track
|
||||
if (disk[selectedDisk] != -1) {
|
||||
readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the current woz track for the given disk drive
|
||||
curWozTrack[selectedDisk] =
|
||||
disk[selectedDisk]->trackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2);
|
||||
printf("Cur Woz track is %d\n", curWozTrack[selectedDisk]);
|
||||
|
||||
}
|
||||
|
||||
uint8_t DiskII::readOrWriteByte()
|
||||
{
|
||||
if (disk[selectedDisk] == -1) {
|
||||
return GAP;
|
||||
if (!disk[selectedDisk]) {
|
||||
printf("reading from uninserted disk\n");
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// FIXME: not handling writes at all at the moment ***
|
||||
if (writeMode && !writeProt) {
|
||||
|
||||
if (!trackBuffer->hasData()) {
|
||||
// Error: writing to empty track buffer? That's a raw write w/o
|
||||
// knowing where we are on the disk. Dangerous, at very least;
|
||||
// I'm not sure what the best action would be here. For the
|
||||
// moment, just refuse to write it.
|
||||
|
||||
g_display->debugMsg("DII: unguarded write");
|
||||
return GAP;
|
||||
}
|
||||
|
||||
trackToFlush = curHalfTrack[selectedDisk]>>1;
|
||||
// It's possible that a badly behaving OS could try to write more
|
||||
// data than we have buffer to handle. Don't let it. We should
|
||||
// only need something like 500 bytes, at worst. In the typical
|
||||
// case, we're talking about something like
|
||||
//
|
||||
// ~5 bytes of GAP
|
||||
// 3 bytes of sector prolog
|
||||
// 2 bytes of volume
|
||||
// 2 bytes of track
|
||||
// 2 bytes of sector
|
||||
// 2 bytes of checksum
|
||||
// 2 bytes of epilog
|
||||
// ~5 bytes of GAP
|
||||
// 3 bytes of data prolog
|
||||
// 342 bytes of GRP-encoded (6:2) data
|
||||
// 1 byte of checksum
|
||||
// 3 bytes of epilog
|
||||
// 1 byte of GAP
|
||||
// == 373 bytes
|
||||
//
|
||||
// ... so if we get up to the full 1024 we've allocated, there's
|
||||
// something suspicious happening.
|
||||
|
||||
if (readWriteLatch < 0x96) {
|
||||
// Can't write a de-nibblized byte...
|
||||
g_display->debugMsg("DII: bad write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
trackBuffer->replaceByte(readWriteLatch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return 0x00 every other byte. Helps the logic sequencer stay in sync.
|
||||
// Otherwise we wind up waiting long periods of time for it to sync up,
|
||||
// presumably because we're overrunning it (returning data faster than
|
||||
// the actual drive would be able to)?
|
||||
static bool whitespace = false;
|
||||
if (whitespace) {
|
||||
whitespace = false;
|
||||
return 0x00;
|
||||
uint32_t curCycles = g_cpu->cycles;
|
||||
bool updateCycles = false;
|
||||
|
||||
if (lastDiskRead[selectedDisk] == 0) {
|
||||
// assume it's a first-read-after-spinup; return the first valid data
|
||||
sequencer = disk[selectedDisk]->nextDiskByte(curWozTrack[selectedDisk]);
|
||||
updateCycles = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
whitespace = !whitespace;
|
||||
// Otherwise we figure out how many cycles we missed since the last
|
||||
// disk read, and pop the right number of bits off the woz track
|
||||
uint32_t missedCycles;
|
||||
missedCycles = curCycles - lastDiskRead[selectedDisk];
|
||||
|
||||
uint8_t ret = trackBuffer->peekNext();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DiskII::readDiskTrack(int8_t diskWeAreUsing, int8_t trackWeAreReading)
|
||||
{
|
||||
checkFlush(trackWeAreReading); // FIXME: bogus argument
|
||||
|
||||
trackBuffer->clear();
|
||||
trackBuffer->setPeekCursor(0);
|
||||
|
||||
if (diskType[diskWeAreUsing] == nibDisk) {
|
||||
// Read one nibblized sector at a time and jam it in trackBuf
|
||||
// directly. We don't read the whole track at once only because
|
||||
// of RAM constraints on the Teensy. There's no reason we
|
||||
// couldn't, though, if RAM weren't at a premium.
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16 + i, true);
|
||||
if (!g_filemanager->readBlock(disk[diskWeAreUsing], rawTrackBuffer, true)) {
|
||||
// FIXME: error handling?
|
||||
g_display->debugMsg("DII: FM nib read failure");
|
||||
return;
|
||||
}
|
||||
trackBuffer->addBytes(rawTrackBuffer, 416);
|
||||
}
|
||||
} else {
|
||||
// It's a .dsk / .po disk image. Read the whole track in to
|
||||
// rawTrackBuffer and nibblize it.
|
||||
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16, false);
|
||||
if (!g_filemanager->readTrack(disk[diskWeAreUsing], rawTrackBuffer, false)) {
|
||||
// FIXME: error handling?
|
||||
g_display->debugMsg("DII: FM block read failure");
|
||||
return;
|
||||
}
|
||||
|
||||
nibblizeTrack(trackBuffer, rawTrackBuffer, diskType[diskWeAreUsing], curHalfTrack[selectedDisk]>>1);
|
||||
missedCycles >>= 2;
|
||||
if (missedCycles)
|
||||
updateCycles = true;
|
||||
while (missedCycles) {
|
||||
sequencer <<= 1;
|
||||
sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]);
|
||||
missedCycles--;
|
||||
}
|
||||
|
||||
done:
|
||||
if (updateCycles) {
|
||||
// We only update the lastDiskRead counter if the number of passed
|
||||
// cycles indicates that we did some sort of work...
|
||||
lastDiskRead[selectedDisk] = curCycles;
|
||||
}
|
||||
return sequencer;
|
||||
}
|
||||
|
||||
void DiskII::fillDiskBuffer()
|
||||
|
@ -565,8 +488,7 @@ void DiskII::fillDiskBuffer()
|
|||
|
||||
const char *DiskII::DiskName(int8_t num)
|
||||
{
|
||||
if (disk[num] != -1)
|
||||
return g_filemanager->fileName(disk[num]);
|
||||
// ***
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -592,38 +514,5 @@ void DiskII::flushTrack(int8_t track, int8_t sel)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!trackBuffer->hasData()) {
|
||||
// Dunno what happened - we're writing but haven't initialized the sector buffer?
|
||||
g_display->debugMsg("DII: uninit'd write");
|
||||
return;
|
||||
}
|
||||
|
||||
if (diskType[sel] == nibDisk) {
|
||||
// Write the whole track out exactly as we've got it. Hopefully
|
||||
// someone has re-calcuated appropriate checksums on it...
|
||||
g_display->debugMsg("DII: Not writing Nib image");
|
||||
return;
|
||||
}
|
||||
|
||||
nibErr e = denibblizeTrack(trackBuffer, rawTrackBuffer, diskType[sel], curHalfTrack[selectedDisk]>>1);
|
||||
switch (e) {
|
||||
case errorShortTrack:
|
||||
g_display->debugMsg("DII: short track");
|
||||
trackBuffer->clear();
|
||||
return;
|
||||
|
||||
case errorMissingSectors:
|
||||
// The nibblized track doesn't contain all possible sectors - so it's broken. Drop the write.
|
||||
g_display->debugMsg("DII: missing sectors");
|
||||
trackBuffer->clear();
|
||||
break;
|
||||
|
||||
case errorNone:
|
||||
break;
|
||||
}
|
||||
|
||||
// ok, write the track!
|
||||
g_filemanager->seekBlock(disk[sel], track * 16);
|
||||
g_filemanager->writeTrack(disk[sel], rawTrackBuffer);
|
||||
// ***
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "woz-serializer.h"
|
||||
|
||||
#include "filemanager.h"
|
||||
#include "applemmu.h"
|
||||
#include "slot.h"
|
||||
|
@ -44,28 +46,24 @@ class DiskII : public Slot {
|
|||
void select(int8_t which); // 0 or 1 for drives 1 and 2, respectively
|
||||
uint8_t readOrWriteByte();
|
||||
|
||||
void checkFlush(int8_t track);
|
||||
void readDiskTrack(int8_t diskWeAreUsing, int8_t trackWeAreReading);
|
||||
|
||||
#ifndef TEENSYDUINO
|
||||
void convertDskToNib(const char *outFN);
|
||||
#endif
|
||||
|
||||
private:
|
||||
volatile int8_t curHalfTrack[2];
|
||||
volatile uint8_t curWozTrack[2];
|
||||
volatile int8_t curPhase[2];
|
||||
uint8_t readWriteLatch;
|
||||
LRingBuffer *trackBuffer; // nibblized data
|
||||
uint8_t rawTrackBuffer[4096]; // not nibblized data
|
||||
uint8_t sequencer, dataRegister; // diskII logic state sequencer vars
|
||||
WozSerializer *disk[2];
|
||||
uint32_t lastDiskRead[2];
|
||||
|
||||
bool writeMode;
|
||||
bool writeProt;
|
||||
AppleMMU *mmu;
|
||||
|
||||
int8_t disk[2];
|
||||
volatile uint8_t indicatorIsOn[2];
|
||||
uint8_t diskType[2];
|
||||
volatile int8_t trackToFlush; // -1 when there's none
|
||||
|
||||
volatile int8_t selectedDisk;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __DISKTYPES_H
|
||||
#define __DISKTYPES_H
|
||||
|
||||
enum {
|
||||
T_AUTO = 0,
|
||||
T_WOZ = 1,
|
||||
T_NIB = 2,
|
||||
T_DSK = 3,
|
||||
T_PO = 4
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,143 +1,236 @@
|
|||
#include "nibutil.h"
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
// Long gaps are more "correct" in the sense that they're
|
||||
// nib-disk-like; but they mean the VM has to chew on a lot of disk
|
||||
// gaps to find the real data, which takes a noticeable amount of
|
||||
// time. With this off, we present a minimum number of gaps (that
|
||||
// hopefully aren't too short for the ROM to be able to write
|
||||
// correctly)
|
||||
// #define LONGGAPS
|
||||
#include <stdio.h>
|
||||
#include "disktypes.h"
|
||||
|
||||
// Default disk volume identifier
|
||||
#define DISK_VOLUME 254
|
||||
|
||||
// 4-and-4 encoding handlers
|
||||
#define nib1(a) (((a & 0xAA) >> 1) | 0xAA)
|
||||
#define nib2(b) (((b & 0x55) ) | 0xAA)
|
||||
#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA))
|
||||
|
||||
// In 6-and-2 encoding, there are 86 (0x56) 6-bit values
|
||||
#define SIXBIT_SPAN 0x56
|
||||
|
||||
typedef struct _bitPtr {
|
||||
uint16_t idx;
|
||||
uint8_t bitIdx;
|
||||
} bitPtr;
|
||||
|
||||
#define INCIDX(p) { p->bitIdx >>= 1; if (!p->bitIdx) {p->bitIdx = 0x80; p->idx++;} }
|
||||
|
||||
const static uint8_t _trans[64] = {0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
|
||||
|
||||
const static uint8_t _detrans[0x80] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
|
||||
0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
|
||||
0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
|
||||
0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
|
||||
0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
|
||||
0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
|
||||
0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
|
||||
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
|
||||
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
|
||||
|
||||
// dos 3.3 to physical sector conversion
|
||||
const static uint8_t dephys[16] = {
|
||||
0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04,
|
||||
const static uint8_t dephys[16] = {
|
||||
0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04,
|
||||
0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f };
|
||||
|
||||
// Prodos to physical sector conversion
|
||||
const uint8_t deProdosPhys[] = {
|
||||
const uint8_t deProdosPhys[] = {
|
||||
0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b,
|
||||
0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f };
|
||||
|
||||
const static uint8_t _trans[64] = {0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
|
||||
|
||||
const static uint8_t _detrans[0x80] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
|
||||
0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
|
||||
0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
|
||||
0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
|
||||
0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
|
||||
0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
|
||||
0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
|
||||
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
|
||||
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
|
||||
|
||||
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||
uint8_t diskType, int8_t track)
|
||||
uint8_t de44(uint8_t nibs[2])
|
||||
{
|
||||
int checksum;
|
||||
return denib(nibs[0], nibs[1]);
|
||||
}
|
||||
|
||||
for (uint8_t sector=0; sector<16; sector++) {
|
||||
static void _packBit(uint8_t *output, bitPtr *ptr, uint8_t isOn)
|
||||
{
|
||||
if (isOn)
|
||||
output[ptr->idx] |= ptr->bitIdx;
|
||||
INCIDX(ptr);
|
||||
}
|
||||
|
||||
for (uint8_t i=0;
|
||||
#ifdef LONGGAPS
|
||||
i < (sector==0 ? 0x63 : 0x13);
|
||||
#else
|
||||
i < 8;
|
||||
#endif
|
||||
i++) {
|
||||
trackBuffer->addByte(GAP);
|
||||
}
|
||||
static void _packGap(uint8_t *output, bitPtr *ptr)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
_packBit(output, ptr, 1);
|
||||
_packBit(output, ptr, 0);
|
||||
_packBit(output, ptr, 0);
|
||||
}
|
||||
|
||||
trackBuffer->addByte(0xD5); // prolog
|
||||
trackBuffer->addByte(0xAA);
|
||||
trackBuffer->addByte(0x96);
|
||||
|
||||
trackBuffer->addByte(nib1(DISK_VOLUME));
|
||||
trackBuffer->addByte(nib2(DISK_VOLUME));
|
||||
|
||||
trackBuffer->addByte(nib1(track));
|
||||
trackBuffer->addByte(nib2(track));
|
||||
|
||||
trackBuffer->addByte(nib1(sector));
|
||||
trackBuffer->addByte(nib2(sector));
|
||||
|
||||
checksum = DISK_VOLUME ^ track ^ sector;
|
||||
trackBuffer->addByte(nib1(checksum));
|
||||
trackBuffer->addByte(nib2(checksum));
|
||||
|
||||
trackBuffer->addByte(0xDE); // epilog
|
||||
trackBuffer->addByte(0xAA);
|
||||
trackBuffer->addByte(0xEB); // Not strictly necessary, but the DiskII controller does it, so we will too.
|
||||
|
||||
// The DiskII controller puts out 5 GAP bytes here.
|
||||
for (uint8_t i=0; i<5; i++) {
|
||||
trackBuffer->addByte(GAP);
|
||||
}
|
||||
|
||||
trackBuffer->addByte(0xD5); // data prolog
|
||||
trackBuffer->addByte(0xAA);
|
||||
trackBuffer->addByte(0xAD);
|
||||
|
||||
uint8_t physicalSector = (diskType == prodosDisk ? deProdosPhys[sector] : dephys[sector]);
|
||||
encodeData(trackBuffer, &rawTrackBuffer[physicalSector * 256]);
|
||||
|
||||
trackBuffer->addByte(0xDE); // data epilog
|
||||
trackBuffer->addByte(0xAA);
|
||||
trackBuffer->addByte(0xEB);
|
||||
|
||||
#ifdef LONGGAPS
|
||||
trackBuffer->addByte(GAP);
|
||||
#endif
|
||||
static void _packByte(uint8_t *output, bitPtr *ptr, uint8_t v)
|
||||
{
|
||||
for (int i=0; i<8; i++) {
|
||||
_packBit(output, ptr, v & (1 << (7-i)));
|
||||
}
|
||||
}
|
||||
|
||||
#define SIXBIT_SPAN 0x56 // 86 bytes
|
||||
// Take 256 bytes of input and turn it in to 343 bytes of nibblized output
|
||||
static void _encodeData(uint8_t *outputBuffer, bitPtr *ptr, const uint8_t input[256])
|
||||
{
|
||||
int ptr2 = 0;
|
||||
int ptr6 = 0x56;
|
||||
static int nibbles[0x156];
|
||||
|
||||
memset(nibbles, 0, sizeof(nibbles));
|
||||
|
||||
int idx2 = 0x55;
|
||||
for (int idx6 = 0x101; idx6 >= 0; idx6--) {
|
||||
int val6 = input[idx6 & 0xFF];
|
||||
int val2 = nibbles[ptr2 + idx2];
|
||||
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
|
||||
// There are 2 "extra" bytes of 2-bit data that we add in here.
|
||||
if (ptr6 + idx6 < 0x156) {
|
||||
nibbles[ptr6 + idx6] = val6;
|
||||
}
|
||||
if (ptr2 + idx2 < 0x156) {
|
||||
nibbles[ptr2 + idx2] = val2;
|
||||
}
|
||||
|
||||
if (--idx2 < 0) {
|
||||
idx2 = 0x55;
|
||||
}
|
||||
}
|
||||
// mask out the "extra" 2-bit data above. Note that the Apple decoders
|
||||
// don't care about the extra bits, so taking these back out isn't
|
||||
// operationally important.
|
||||
nibbles[0x54] &= 0x0F;
|
||||
nibbles[0x55] &= 0x0F;
|
||||
|
||||
int lastv = 0;
|
||||
for (int idx = 0; idx < 0x156; idx++) {
|
||||
int val = nibbles[idx];
|
||||
_packByte(outputBuffer, ptr, _trans[lastv ^ val]);
|
||||
lastv = val;
|
||||
}
|
||||
_packByte(outputBuffer, ptr, _trans[lastv]);
|
||||
}
|
||||
|
||||
static uint8_t _whichBit(uint8_t bitIdx)
|
||||
{
|
||||
switch (bitIdx) {
|
||||
case 0x80:
|
||||
return 0;
|
||||
case 0x40:
|
||||
return 1;
|
||||
case 0x20:
|
||||
return 2;
|
||||
case 0x10:
|
||||
return 3;
|
||||
case 0x08:
|
||||
return 4;
|
||||
case 0x04:
|
||||
return 5;
|
||||
case 0x02:
|
||||
return 6;
|
||||
case 0x01:
|
||||
return 7;
|
||||
default:
|
||||
return 0; // not used
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
// rawTrackBuffer is input (dsk/po format); outputBuffer is encoded
|
||||
// nibbles (416*16 bytes). Returns the number of bits actually
|
||||
// encoded.
|
||||
uint32_t nibblizeTrack(uint8_t outputBuffer[NIBTRACKSIZE], const uint8_t rawTrackBuffer[256*16],
|
||||
uint8_t diskType, int8_t track)
|
||||
{
|
||||
int checksum;
|
||||
bitPtr ptr = { 0, 0x80 };
|
||||
|
||||
for (uint8_t sector=0; sector<16; sector++) {
|
||||
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
_packGap(outputBuffer, &ptr);
|
||||
}
|
||||
|
||||
_packByte(outputBuffer, &ptr, 0xD5); // prolog
|
||||
_packByte(outputBuffer, &ptr, 0xAA);
|
||||
_packByte(outputBuffer, &ptr, 0x96);
|
||||
|
||||
_packByte(outputBuffer, &ptr, nib1(DISK_VOLUME));
|
||||
_packByte(outputBuffer, &ptr, nib2(DISK_VOLUME));
|
||||
|
||||
_packByte(outputBuffer, &ptr, nib1(track));
|
||||
_packByte(outputBuffer, &ptr, nib2(track));
|
||||
|
||||
_packByte(outputBuffer, &ptr, nib1(sector));
|
||||
_packByte(outputBuffer, &ptr, nib2(sector));
|
||||
|
||||
checksum = DISK_VOLUME ^ track ^ sector;
|
||||
_packByte(outputBuffer, &ptr, nib1(checksum));
|
||||
_packByte(outputBuffer, &ptr, nib2(checksum));
|
||||
|
||||
_packByte(outputBuffer, &ptr, 0xDE); // epilog
|
||||
_packByte(outputBuffer, &ptr, 0xAA);
|
||||
_packByte(outputBuffer, &ptr, 0xEB);
|
||||
|
||||
for (uint8_t i=0; i<5; i++) {
|
||||
_packGap(outputBuffer, &ptr);
|
||||
}
|
||||
|
||||
_packByte(outputBuffer, &ptr, 0xD5); // data prolog
|
||||
_packByte(outputBuffer, &ptr, 0xAA);
|
||||
_packByte(outputBuffer, &ptr, 0xAD);
|
||||
|
||||
uint8_t physicalSector = (diskType == T_PO ? deProdosPhys[sector] : dephys[sector]);
|
||||
_encodeData(outputBuffer, &ptr, &rawTrackBuffer[physicalSector * 256]);
|
||||
|
||||
_packByte(outputBuffer, &ptr, 0xDE); // data epilog
|
||||
_packByte(outputBuffer, &ptr, 0xAA);
|
||||
_packByte(outputBuffer, &ptr, 0xEB);
|
||||
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
_packGap(outputBuffer, &ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return (ptr.idx*8 + _whichBit(ptr.bitIdx));
|
||||
}
|
||||
|
||||
// Pop the next 343 bytes off of trackBuffer, which should be 342
|
||||
// 6:2-bit GCR encoded values, which we decode back in to 256 8-byte
|
||||
// output values; and one checksum byte.
|
||||
//
|
||||
// Return true if we've successfully consumed 343 bytes from
|
||||
// trackBuf. This reads from the circular buffer trackBuffer, so if
|
||||
// there's not enough data there, the results are somewhat
|
||||
// unpredictable.
|
||||
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
|
||||
// trackBuf.
|
||||
static bool _decodeData(const uint8_t trackBuffer[343], uint8_t output[256])
|
||||
{
|
||||
// Basic check that there's enough buffer data in trackBuffer. Note
|
||||
// that we're not checking it against startAt; we could be wrapping
|
||||
// around.
|
||||
if (trackBuffer->count() < 343)
|
||||
return false;
|
||||
|
||||
static uint8_t workbuf[342];
|
||||
|
||||
for (int i=0; i<342; i++) {
|
||||
uint8_t in = trackBuffer->peek(startAt++) & 0x7F; // strip high bit
|
||||
uint8_t in = *(trackBuffer++) & 0x7F; // strip high bit
|
||||
workbuf[i] = _detrans[in];
|
||||
}
|
||||
|
||||
|
@ -148,13 +241,12 @@ bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
|
|||
prev = workbuf[i];
|
||||
}
|
||||
|
||||
// Put the checksum on the track - only necessary if we're about to
|
||||
// write the nibblized version of the track back out
|
||||
/* uint16_t cursor = trackBuffer->Cursor();
|
||||
trackBuffer->setPeekCursor(startAt++);
|
||||
trackBuffer->replaceByte(prev); // 'prev' holds the checksum
|
||||
trackBuffer->setPeekCursor(cursor); // put it back where we found it
|
||||
*/
|
||||
#if 0
|
||||
if (prev != trackBuffer[342]) {
|
||||
printf("ERROR: checksum of sector is incorrect [0x%X v 0x%X]\n", prev, trackBuffer[342]);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Start with all of the bytes with 6 bits of data
|
||||
for (uint16_t i=0; i<256; i++) {
|
||||
|
@ -173,95 +265,46 @@ bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
|
|||
output[2*SIXBIT_SPAN + i] |= ((thisbyte & 0x80) >> 7) | ((thisbyte & 0x40) >> 5);
|
||||
}
|
||||
}
|
||||
// FIXME: check or update the checksum?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void encodeData(LRingBuffer *trackBuffer, uint8_t *data)
|
||||
{
|
||||
int16_t i;
|
||||
int ptr2 = 0;
|
||||
int ptr6 = 0x56;
|
||||
static int nibbles[0x156];
|
||||
|
||||
for (i=0; i<0x156; i++) {
|
||||
nibbles[i] = 0;
|
||||
}
|
||||
|
||||
int idx2 = 0x55;
|
||||
for (int idx6 = 0x101; idx6 >= 0; idx6--) {
|
||||
int val6 = data[idx6 & 0xFF];
|
||||
int val2 = nibbles[ptr2 + idx2];
|
||||
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
val2 = (val2 << 1) | (val6 & 1);
|
||||
val6 >>= 1;
|
||||
|
||||
// There are 2 "extra" bytes of 2-bit data that we ignore here.
|
||||
if (ptr6 + idx6 < 0x156) {
|
||||
nibbles[ptr6 + idx6] = val6;
|
||||
}
|
||||
if (ptr2 + idx2 < 0x156) {
|
||||
nibbles[ptr2 + idx2] = val2;
|
||||
}
|
||||
|
||||
if (--idx2 < 0) {
|
||||
idx2 = 0x55;
|
||||
}
|
||||
}
|
||||
|
||||
int lastv = 0;
|
||||
for (int idx = 0; idx < 0x156; idx++) {
|
||||
int val = nibbles[idx];
|
||||
trackBuffer->addByte(_trans[lastv ^ val]);
|
||||
lastv = val;
|
||||
}
|
||||
trackBuffer->addByte(_trans[lastv]);
|
||||
}
|
||||
|
||||
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||
// trackBuffer is input NIB data; rawTrackBuffer is output DSK/PO data
|
||||
nibErr denibblizeTrack(const uint8_t input[NIBTRACKSIZE], uint8_t rawTrackBuffer[256*16],
|
||||
uint8_t diskType, int8_t track)
|
||||
{
|
||||
// We can't tell exactly what the length should be, b/c there might
|
||||
// be varying numbers of GAP bytes. But we can tell, generally, that
|
||||
// this is the minimum acceptable length that might hold all the
|
||||
// track data.
|
||||
if (trackBuffer->count() < 16*MINNIBSECTORSIZE) {
|
||||
return errorShortTrack;
|
||||
}
|
||||
|
||||
// bitmask of the sectors that we've found while decoding. We should
|
||||
// find all 16.
|
||||
uint16_t sectorsUpdated = 0;
|
||||
|
||||
// loop through the data twice, so we make sure we read anything
|
||||
// that crosses the end/start boundary
|
||||
// FIXME: if this approach works, we probably want 1/16th extra, not 2*
|
||||
for (uint16_t i=0; i<2*trackBuffer->count(); ) {
|
||||
uint16_t startOfSector;
|
||||
for (uint16_t i=0; i<2*416*16; i++) {
|
||||
// Find the prolog
|
||||
while (trackBuffer->peek(i++) != 0xD5)
|
||||
;
|
||||
if (trackBuffer->peek(i++) != 0xAA) {
|
||||
if (input[i % NIBTRACKSIZE] != 0xD5)
|
||||
continue;
|
||||
}
|
||||
if (trackBuffer->peek(i++) != 0x96) {
|
||||
startOfSector = i;
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0x96)
|
||||
continue;
|
||||
i++;
|
||||
|
||||
// And now we should be in the header section
|
||||
uint8_t volumeID = denib(trackBuffer->peek(i),
|
||||
trackBuffer->peek(i+1));
|
||||
uint8_t volumeID = denib(input[i % NIBTRACKSIZE],
|
||||
input[(i+1) % NIBTRACKSIZE]);
|
||||
i += 2;
|
||||
uint8_t trackID = denib(trackBuffer->peek(i),
|
||||
trackBuffer->peek(i+1));
|
||||
uint8_t trackID = denib(input[i % NIBTRACKSIZE],
|
||||
input[(i+1) % NIBTRACKSIZE]);
|
||||
i += 2;
|
||||
uint8_t sectorNum = denib(trackBuffer->peek(i),
|
||||
trackBuffer->peek(i+1));
|
||||
uint8_t sectorNum = denib(input[i % NIBTRACKSIZE],
|
||||
input[(i+1) % NIBTRACKSIZE]);
|
||||
i += 2;
|
||||
uint8_t headerChecksum = denib(trackBuffer->peek(i),
|
||||
trackBuffer->peek(i+1));
|
||||
uint8_t headerChecksum = denib(input[i % NIBTRACKSIZE],
|
||||
input[(i+1) % NIBTRACKSIZE]);
|
||||
i += 2;
|
||||
|
||||
if (headerChecksum != (volumeID ^ trackID ^ sectorNum)) {
|
||||
|
@ -269,41 +312,51 @@ nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
|||
}
|
||||
|
||||
// check for the epilog
|
||||
if (trackBuffer->peek(i++) != 0xDE) {
|
||||
if (input[i % NIBTRACKSIZE] != 0xDE) {
|
||||
continue;
|
||||
}
|
||||
if (trackBuffer->peek(i++) != 0xAA) {
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xAA) {
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
|
||||
// Skip to the data prolog
|
||||
while (trackBuffer->peek(i++) != 0xD5)
|
||||
;
|
||||
if (trackBuffer->peek(i++) != 0xAA) {
|
||||
continue;
|
||||
while (input[i % NIBTRACKSIZE] != 0xD5) {
|
||||
i++;
|
||||
}
|
||||
if (trackBuffer->peek(i++) != 0xAD) {
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xAD)
|
||||
continue;
|
||||
i++;
|
||||
|
||||
|
||||
// Decode the data in to a temporary buffer: we don't want to overwrite
|
||||
// something valid with partial data
|
||||
uint8_t output[256];
|
||||
if (!decodeData(trackBuffer, i, output)) {
|
||||
// create a new nibData (in case it wraps around our track data)
|
||||
uint8_t nibData[343];
|
||||
for (int j=0; j<343; j++) {
|
||||
nibData[j] = input[(i+j)%NIBTRACKSIZE];
|
||||
}
|
||||
if (!_decodeData(nibData, output)) {
|
||||
continue;
|
||||
}
|
||||
i += 343;
|
||||
|
||||
// Check the data epilog
|
||||
if (trackBuffer->peek(i++) != 0xDE) {
|
||||
if (input[i % NIBTRACKSIZE] != 0xDE)
|
||||
continue;
|
||||
}
|
||||
if (trackBuffer->peek(i++) != 0xAA) {
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||
continue;
|
||||
}
|
||||
if (trackBuffer->peek(i++) != 0xEB) {
|
||||
i++;
|
||||
if (input[i % NIBTRACKSIZE] != 0xEB)
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
|
||||
// We've got a whole block! Put it in the rawTrackBuffer and mark
|
||||
// the bit for it in sectorsUpdated.
|
||||
|
@ -311,12 +364,15 @@ nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
|||
// FIXME: if trackID != curTrack, that's an error?
|
||||
|
||||
uint8_t targetSector;
|
||||
if (diskType == prodosDisk) {
|
||||
if (diskType == T_PO) {
|
||||
targetSector = deProdosPhys[sectorNum];
|
||||
} else {
|
||||
targetSector = dephys[sectorNum];
|
||||
}
|
||||
|
||||
if (targetSector > 16)
|
||||
return errorBadData;
|
||||
|
||||
memcpy(&rawTrackBuffer[targetSector * 256],
|
||||
output,
|
||||
256);
|
||||
|
|
|
@ -1,44 +1,54 @@
|
|||
#ifdef TEENSYDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#ifndef __NIBUTIL_H
|
||||
#define __NIBUTIL_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "LRingBuffer.h"
|
||||
|
||||
#define NIBTRACKSIZE 0x1A00
|
||||
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
|
||||
#define MINNIBSECTORSIZE (343 + 13 + 3)
|
||||
#define NIBSECTORSIZE 416
|
||||
|
||||
#define nib1(a) (((a & 0xAA) >> 1) | 0xAA)
|
||||
#define nib2(b) (((b & 0x55) ) | 0xAA)
|
||||
#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA))
|
||||
typedef struct _nibSector {
|
||||
uint8_t gap1[48];
|
||||
|
||||
#define GAP 0xFF
|
||||
uint8_t sectorProlog[3];
|
||||
uint8_t volume44[2];
|
||||
uint8_t track44[2];
|
||||
uint8_t sector44[2];
|
||||
uint8_t checksum44[2];
|
||||
uint8_t sectorEpilog[3];
|
||||
|
||||
enum {
|
||||
dosDisk = 0,
|
||||
prodosDisk = 1,
|
||||
nibDisk = 2
|
||||
};
|
||||
uint8_t gap2[5];
|
||||
|
||||
uint8_t dataProlog[3];
|
||||
uint8_t data62[342];
|
||||
uint8_t checksum;
|
||||
uint8_t dataEpilog[3];
|
||||
} nibSector;
|
||||
|
||||
enum nibErr {
|
||||
errorNone = 0,
|
||||
errorShortTrack = 1,
|
||||
errorMissingSectors = 2
|
||||
errorMissingSectors = 1,
|
||||
errorBadData = 2
|
||||
};
|
||||
|
||||
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||
uint8_t diskType, int8_t track);
|
||||
|
||||
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||
uint32_t nibblizeTrack(uint8_t outputBuffer[NIBTRACKSIZE], const uint8_t rawTrackBuffer[256*16],
|
||||
uint8_t diskType, int8_t track);
|
||||
|
||||
nibErr denibblizeTrack(const uint8_t input[NIBTRACKSIZE], uint8_t rawTrackBuffer[256*16],
|
||||
uint8_t diskType, int8_t track);
|
||||
|
||||
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
|
||||
void encodeData(LRingBuffer *trackBuffer, uint8_t *data);
|
||||
uint8_t de44(uint8_t nibs[2]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __VERSION_H
|
||||
#define __VERSION_H
|
||||
|
||||
#define VERSION_STRING "Wozzle v1.0+dev"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#include "woz-serializer.h"
|
||||
|
||||
bool WozSerializer::Serialize(int8_t fd)
|
||||
{
|
||||
// ***
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WozSerializer::Deserialize(int8_t fd)
|
||||
{
|
||||
// ***
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __WOZ_SERIALIZER_H
|
||||
#define __WOZ_SERIALIZER_H
|
||||
|
||||
#include "woz.h"
|
||||
class WozSerializer: public virtual Woz {
|
||||
public:
|
||||
bool Serialize(int8_t fd);
|
||||
bool Deserialize(int8_t fd);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
#ifndef __WOZ_H
|
||||
#define __WOZ_H
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include "nibutil.h"
|
||||
#include "disktypes.h"
|
||||
|
||||
typedef struct _diskInfo {
|
||||
uint8_t version; // Woz format version #
|
||||
uint8_t diskType; // 1 = 5.25"; 2 = 3.5"
|
||||
uint8_t writeProtected; // 1 = true
|
||||
uint8_t synchronized; // were tracks written with cross-track sync?
|
||||
uint8_t cleaned; // were MC3470 "fake bits" removed?
|
||||
char creator[33]; // 32 chars of creator string and one terminator
|
||||
uint8_t diskSides; // always 1 for a 5.25" disk
|
||||
uint8_t bootSectorFormat; // 0=Unknown; 1=16-sector; 2=13-sector; 3=both
|
||||
uint8_t optimalBitTiming; // 125-nS increments; standard == 32 (4uS)
|
||||
uint16_t compatHardware; // 0=unknown compatability
|
||||
uint16_t requiredRam; // value in K. 0 = unknown
|
||||
uint16_t largestTrack; // # of 512-byte blocks used for largest track
|
||||
} diskInfo;
|
||||
|
||||
typedef struct _trackInfo {
|
||||
uint16_t startingBlock; // v2
|
||||
uint32_t startingByte; // v1
|
||||
uint16_t blockCount;
|
||||
uint32_t bitCount;
|
||||
uint8_t *trackData;
|
||||
} trackInfo;
|
||||
|
||||
class Woz {
|
||||
public:
|
||||
Woz();
|
||||
~Woz();
|
||||
|
||||
bool readFile(const char *filename, uint8_t forceType = T_AUTO);
|
||||
bool writeFile(uint8_t version, const char *filename);
|
||||
|
||||
uint8_t getNextWozBit(uint8_t track);
|
||||
uint8_t nextDiskBit(uint8_t track);
|
||||
uint8_t nextDiskByte(uint8_t track);
|
||||
|
||||
bool decodeWozTrackToNib(uint8_t track, nibSector sectorData[16]);
|
||||
bool decodeWozTrackToDsk(uint8_t track, uint8_t subtype, uint8_t sectorData[256*16]);
|
||||
|
||||
void dumpInfo();
|
||||
|
||||
bool isSynchronized();
|
||||
|
||||
uint8_t trackNumberForQuarterTrack(uint16_t qt);
|
||||
|
||||
private:
|
||||
bool readWozFile(const char *filename);
|
||||
bool readDskFile(const char *filename, uint8_t subtype);
|
||||
bool readNibFile(const char *filename);
|
||||
|
||||
uint8_t fakeBit();
|
||||
|
||||
bool parseTRKSChunk(FILE *f, uint32_t chunkSize);
|
||||
bool parseTMAPChunk(FILE *f, uint32_t chunkSize);
|
||||
bool parseInfoChunk(FILE *f, uint32_t chunkSize);
|
||||
bool parseMetaChunk(FILE *f, uint32_t chunkSize);
|
||||
|
||||
bool writeInfoChunk(uint8_t version, FILE *f);
|
||||
bool writeTMAPChunk(uint8_t version, FILE *f);
|
||||
bool writeTRKSChunk(uint8_t version, FILE *f);
|
||||
|
||||
bool readQuarterTrackData(FILE *f, uint8_t quartertrack);
|
||||
bool readSectorData(uint8_t track, uint8_t sector, nibSector *sectorData);
|
||||
|
||||
void _initInfo();
|
||||
|
||||
protected:
|
||||
uint8_t quarterTrackMap[40*4];
|
||||
diskInfo di;
|
||||
trackInfo tracks[160];
|
||||
|
||||
// cursor for track enumeration
|
||||
uint32_t trackPointer;
|
||||
uint8_t trackByte;
|
||||
uint8_t trackBitIdx;
|
||||
uint8_t trackLoopCounter;
|
||||
char *metaData;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue