first working Woz code (read-only, and only non-copyprotected disks)

This commit is contained in:
Jorj Bauer 2019-02-20 07:50:51 -05:00
parent 75342f5a49
commit 03b0a141bc
13 changed files with 1722 additions and 446 deletions

View File

@ -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

90
apple/crc32.c Normal file
View File

@ -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;
}

12
apple/crc32.h Normal file
View File

@ -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

View File

@ -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);
// ***
}

View File

@ -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;
};

12
apple/disktypes.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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

6
apple/version.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __VERSION_H
#define __VERSION_H
#define VERSION_STRING "Wozzle v1.0+dev"
#endif

14
apple/woz-serializer.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "woz-serializer.h"
bool WozSerializer::Serialize(int8_t fd)
{
// ***
return false;
}
bool WozSerializer::Deserialize(int8_t fd)
{
// ***
return false;
}

12
apple/woz-serializer.h Normal file
View File

@ -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

1088
apple/woz.cpp Normal file

File diff suppressed because it is too large Load Diff

89
apple/woz.h Normal file
View File

@ -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