mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-06-15 21:29:32 +00:00
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
|
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
|
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
90
apple/crc32.c
Normal 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
12
apple/crc32.h
Normal 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
|
313
apple/diskii.cpp
313
apple/diskii.cpp
|
@ -21,34 +21,33 @@
|
||||||
|
|
||||||
DiskII::DiskII(AppleMMU *mmu)
|
DiskII::DiskII(AppleMMU *mmu)
|
||||||
{
|
{
|
||||||
this->trackBuffer = new LRingBuffer(NIBTRACKSIZE);
|
|
||||||
|
|
||||||
this->mmu = mmu;
|
this->mmu = mmu;
|
||||||
|
|
||||||
curPhase[0] = curPhase[1] = 0;
|
curPhase[0] = curPhase[1] = 0;
|
||||||
curHalfTrack[0] = curHalfTrack[1] = 0;
|
curHalfTrack[0] = curHalfTrack[1] = 0;
|
||||||
|
curWozTrack[0] = curWozTrack[1] = 0xFF;
|
||||||
trackToFlush = -1;
|
|
||||||
|
|
||||||
writeMode = false;
|
writeMode = false;
|
||||||
writeProt = false; // FIXME: expose an interface to this
|
writeProt = false; // FIXME: expose an interface to this
|
||||||
readWriteLatch = 0x00;
|
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;
|
indicatorIsOn[0] = indicatorIsOn[1] = 0;
|
||||||
selectedDisk = 0;
|
selectedDisk = 0;
|
||||||
diskType[0] = diskType[1] = dosDisk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskII::~DiskII()
|
DiskII::~DiskII()
|
||||||
{
|
{
|
||||||
delete this->trackBuffer; this->trackBuffer = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskII::Serialize(int8_t fd)
|
bool DiskII::Serialize(int8_t fd)
|
||||||
{
|
{
|
||||||
/* Make sure to flush anything to disk first */
|
return false;
|
||||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
|
||||||
|
// FIXME: all the new variables are missing
|
||||||
|
|
||||||
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
||||||
|
|
||||||
|
@ -62,19 +61,21 @@ bool DiskII::Serialize(int8_t fd)
|
||||||
g_filemanager->writeByte(fd, writeMode);
|
g_filemanager->writeByte(fd, writeMode);
|
||||||
g_filemanager->writeByte(fd, writeProt);
|
g_filemanager->writeByte(fd, writeProt);
|
||||||
|
|
||||||
// Don't save disk[0,1]; save their file names & cursors
|
for (int i=0; i<2; i++) {
|
||||||
g_filemanager->SerializeFile(fd, disk[0]);
|
if (disk[i]) {
|
||||||
g_filemanager->SerializeFile(fd, disk[1]);
|
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[0]);
|
||||||
g_filemanager->writeByte(fd, indicatorIsOn[1]);
|
g_filemanager->writeByte(fd, indicatorIsOn[1]);
|
||||||
|
|
||||||
g_filemanager->writeByte(fd, diskType[0]);
|
|
||||||
g_filemanager->writeByte(fd, diskType[1]);
|
|
||||||
g_filemanager->writeByte(fd, selectedDisk);
|
g_filemanager->writeByte(fd, selectedDisk);
|
||||||
|
|
||||||
trackBuffer->Serialize(fd);
|
|
||||||
|
|
||||||
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
g_filemanager->writeByte(fd, DISKIIMAGIC);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -82,8 +83,8 @@ bool DiskII::Serialize(int8_t fd)
|
||||||
|
|
||||||
bool DiskII::Deserialize(int8_t fd)
|
bool DiskII::Deserialize(int8_t fd)
|
||||||
{
|
{
|
||||||
/* Make sure to flush anything to disk first */
|
return false;
|
||||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
// FIXME: all the new variables are missing
|
||||||
|
|
||||||
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -99,22 +100,23 @@ bool DiskII::Deserialize(int8_t fd)
|
||||||
writeMode = g_filemanager->readByte(fd);
|
writeMode = g_filemanager->readByte(fd);
|
||||||
writeProt = g_filemanager->readByte(fd);
|
writeProt = g_filemanager->readByte(fd);
|
||||||
|
|
||||||
disk[0] = g_filemanager->DeserializeFile(fd);
|
for (int i=0; i<2; i++) {
|
||||||
disk[1] = g_filemanager->DeserializeFile(fd);
|
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[0] = g_filemanager->readByte(fd);
|
||||||
indicatorIsOn[1] = 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);
|
selectedDisk = g_filemanager->readByte(fd);
|
||||||
|
|
||||||
trackBuffer->Deserialize(fd);
|
|
||||||
|
|
||||||
// Reset the dirty caches and whatnot
|
|
||||||
trackToFlush = -1;
|
|
||||||
|
|
||||||
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
if (g_filemanager->readByte(fd) != DISKIIMAGIC) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -127,8 +129,6 @@ void DiskII::Reset()
|
||||||
curPhase[0] = curPhase[1] = 0;
|
curPhase[0] = curPhase[1] = 0;
|
||||||
curHalfTrack[0] = curHalfTrack[1] = 0;
|
curHalfTrack[0] = curHalfTrack[1] = 0;
|
||||||
|
|
||||||
trackToFlush = -1;
|
|
||||||
|
|
||||||
writeMode = false;
|
writeMode = false;
|
||||||
writeProt = false; // FIXME: expose an interface to this
|
writeProt = false; // FIXME: expose an interface to this
|
||||||
readWriteLatch = 0x00;
|
readWriteLatch = 0x00;
|
||||||
|
@ -137,17 +137,12 @@ void DiskII::Reset()
|
||||||
ejectDisk(1);
|
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)
|
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) {
|
switch (s) {
|
||||||
case 0x00: // change stepper motor phase
|
case 0x00: // change stepper motor phase
|
||||||
break;
|
break;
|
||||||
|
@ -173,11 +168,13 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
||||||
case 0x08: // drive off
|
case 0x08: // drive off
|
||||||
indicatorIsOn[selectedDisk] = 99;
|
indicatorIsOn[selectedDisk] = 99;
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: delay a bit? Queue for later drawing?
|
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: delay a bit? Queue for later drawing?
|
||||||
checkFlush(curHalfTrack[selectedDisk]>>1);
|
|
||||||
break;
|
break;
|
||||||
case 0x09: // drive on
|
case 0x09: // drive on
|
||||||
indicatorIsOn[selectedDisk] = 100;
|
indicatorIsOn[selectedDisk] = 100;
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: delay a bit? Queue for later drawing?
|
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;
|
break;
|
||||||
|
|
||||||
case 0x0A: // select drive 1
|
case 0x0A: // select drive 1
|
||||||
|
@ -189,6 +186,8 @@ uint8_t DiskII::readSwitches(uint8_t s)
|
||||||
|
|
||||||
case 0x0C: // shift one read or write byte
|
case 0x0C: // shift one read or write byte
|
||||||
readWriteLatch = readOrWriteByte();
|
readWriteLatch = readOrWriteByte();
|
||||||
|
if (readWriteLatch & 0x80)
|
||||||
|
sequencer = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0D: // load data register (latch)
|
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
|
// FIXME: improve the spin-down here. We need a CPU cycle callback
|
||||||
// for some period of time instead of this silly decrement counter.
|
// for some period of time instead of this silly decrement counter.
|
||||||
if (!indicatorIsOn[selectedDisk]) {
|
if (!indicatorIsOn[selectedDisk]) {
|
||||||
// printf("Unexpected read while disk isn't on?\n");
|
printf("Unexpected read while disk isn't on?\n");
|
||||||
indicatorIsOn[selectedDisk] = 100;
|
indicatorIsOn[selectedDisk] = 100;
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, true); // FIXME: queue for later drawing?
|
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...
|
// slowly spin it down...
|
||||||
if (--indicatorIsOn[selectedDisk] == 0) {
|
if (--indicatorIsOn[selectedDisk] == 0) {
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
|
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;
|
break;
|
||||||
|
|
||||||
case 0x0C: // shift one read or write byte
|
case 0x0C: // shift one read or write byte
|
||||||
readOrWriteByte();
|
if (readOrWriteByte() & 0x80)
|
||||||
|
sequencer = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0D: // drive write
|
case 0x0D: // drive write
|
||||||
|
// FIXME
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0E: // set read mode
|
case 0x0E: // set read mode
|
||||||
|
@ -345,20 +349,12 @@ void DiskII::setPhase(uint8_t phase)
|
||||||
// recalibrate! This is where the fun noise goes DaDaDaDaDaDaDaDaDa
|
// recalibrate! This is where the fun noise goes DaDaDaDaDaDaDaDaDa
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (curHalfTrack[selectedDisk] != prevHalfTrack) {
|
||||||
printf("phase %d => %d; curHalfTrack %d => %d\n",
|
|
||||||
prevPhase, curPhase[selectedDisk],
|
|
||||||
prevHalfTrack, curHalfTrack[selectedDisk]);
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (curHalfTrack[selectedDisk]>>1 != prevHalfTrack>>1) {
|
|
||||||
// We're changing track - flush the old track back to disk
|
// 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
|
curWozTrack[selectedDisk] = disk[selectedDisk]->trackNumberForQuarterTrack(curHalfTrack[selectedDisk]*2);
|
||||||
if (disk[selectedDisk] != -1) {
|
printf("New half: %d; track: %d\n", curHalfTrack[selectedDisk]*2, curWozTrack[selectedDisk]);
|
||||||
readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,36 +395,22 @@ static bool _endsWithI(const char *s1, const char *s2)
|
||||||
void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt)
|
void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt)
|
||||||
{
|
{
|
||||||
ejectDisk(driveNum);
|
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)
|
if (drawIt)
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, false);
|
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)
|
void DiskII::ejectDisk(int8_t driveNum)
|
||||||
{
|
{
|
||||||
if (disk[driveNum] != -1) {
|
if (disk[driveNum]) {
|
||||||
if (selectedDisk == driveNum) {
|
delete disk[driveNum];
|
||||||
checkFlush(0); // FIXME: bogus argument
|
disk[driveNum] = NULL;
|
||||||
trackBuffer->clear();
|
|
||||||
}
|
|
||||||
g_filemanager->closeFile(disk[driveNum]);
|
|
||||||
disk[driveNum] = -1;
|
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, true);
|
g_ui->drawOnOffUIElement(UIeDisk1_state + driveNum, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,125 +420,66 @@ void DiskII::select(int8_t which)
|
||||||
if (which != 0 && which != 1)
|
if (which != 0 && which != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (which != selectedDisk) {
|
printf("Select disk %d\n", which);
|
||||||
indicatorIsOn[selectedDisk] = 0;
|
|
||||||
g_ui->drawOnOffUIElement(UIeDisk1_activity + selectedDisk, false); // FIXME: queue for later drawing?
|
|
||||||
|
|
||||||
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
|
// set the selected disk drive
|
||||||
selectedDisk = which;
|
selectedDisk = which;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
|
||||||
// Preread the current track
|
|
||||||
if (disk[selectedDisk] != -1) {
|
|
||||||
readDiskTrack(selectedDisk, curHalfTrack[selectedDisk]>>1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t DiskII::readOrWriteByte()
|
uint8_t DiskII::readOrWriteByte()
|
||||||
{
|
{
|
||||||
if (disk[selectedDisk] == -1) {
|
if (!disk[selectedDisk]) {
|
||||||
return GAP;
|
printf("reading from uninserted disk\n");
|
||||||
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: not handling writes at all at the moment ***
|
||||||
if (writeMode && !writeProt) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackBuffer->replaceByte(readWriteLatch);
|
uint32_t curCycles = g_cpu->cycles;
|
||||||
|
bool updateCycles = false;
|
||||||
|
|
||||||
return 0;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return 0x00 every other byte. Helps the logic sequencer stay in sync.
|
// Otherwise we figure out how many cycles we missed since the last
|
||||||
// Otherwise we wind up waiting long periods of time for it to sync up,
|
// disk read, and pop the right number of bits off the woz track
|
||||||
// presumably because we're overrunning it (returning data faster than
|
uint32_t missedCycles;
|
||||||
// the actual drive would be able to)?
|
missedCycles = curCycles - lastDiskRead[selectedDisk];
|
||||||
static bool whitespace = false;
|
|
||||||
if (whitespace) {
|
missedCycles >>= 2;
|
||||||
whitespace = false;
|
if (missedCycles)
|
||||||
return 0x00;
|
updateCycles = true;
|
||||||
|
while (missedCycles) {
|
||||||
|
sequencer <<= 1;
|
||||||
|
sequencer |= disk[selectedDisk]->nextDiskBit(curWozTrack[selectedDisk]);
|
||||||
|
missedCycles--;
|
||||||
}
|
}
|
||||||
|
|
||||||
whitespace = !whitespace;
|
done:
|
||||||
|
if (updateCycles) {
|
||||||
uint8_t ret = trackBuffer->peekNext();
|
// We only update the lastDiskRead counter if the number of passed
|
||||||
return ret;
|
// cycles indicates that we did some sort of work...
|
||||||
}
|
lastDiskRead[selectedDisk] = curCycles;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
return sequencer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskII::fillDiskBuffer()
|
void DiskII::fillDiskBuffer()
|
||||||
|
@ -565,8 +488,7 @@ void DiskII::fillDiskBuffer()
|
||||||
|
|
||||||
const char *DiskII::DiskName(int8_t num)
|
const char *DiskII::DiskName(int8_t num)
|
||||||
{
|
{
|
||||||
if (disk[num] != -1)
|
// ***
|
||||||
return g_filemanager->fileName(disk[num]);
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -592,38 +514,5 @@ void DiskII::flushTrack(int8_t track, int8_t sel)
|
||||||
return;
|
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>
|
#include <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "woz-serializer.h"
|
||||||
|
|
||||||
#include "filemanager.h"
|
#include "filemanager.h"
|
||||||
#include "applemmu.h"
|
#include "applemmu.h"
|
||||||
#include "slot.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
|
void select(int8_t which); // 0 or 1 for drives 1 and 2, respectively
|
||||||
uint8_t readOrWriteByte();
|
uint8_t readOrWriteByte();
|
||||||
|
|
||||||
void checkFlush(int8_t track);
|
|
||||||
void readDiskTrack(int8_t diskWeAreUsing, int8_t trackWeAreReading);
|
|
||||||
|
|
||||||
#ifndef TEENSYDUINO
|
#ifndef TEENSYDUINO
|
||||||
void convertDskToNib(const char *outFN);
|
void convertDskToNib(const char *outFN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
volatile int8_t curHalfTrack[2];
|
volatile int8_t curHalfTrack[2];
|
||||||
|
volatile uint8_t curWozTrack[2];
|
||||||
volatile int8_t curPhase[2];
|
volatile int8_t curPhase[2];
|
||||||
uint8_t readWriteLatch;
|
uint8_t readWriteLatch;
|
||||||
LRingBuffer *trackBuffer; // nibblized data
|
uint8_t sequencer, dataRegister; // diskII logic state sequencer vars
|
||||||
uint8_t rawTrackBuffer[4096]; // not nibblized data
|
WozSerializer *disk[2];
|
||||||
|
uint32_t lastDiskRead[2];
|
||||||
|
|
||||||
bool writeMode;
|
bool writeMode;
|
||||||
bool writeProt;
|
bool writeProt;
|
||||||
AppleMMU *mmu;
|
AppleMMU *mmu;
|
||||||
|
|
||||||
int8_t disk[2];
|
|
||||||
volatile uint8_t indicatorIsOn[2];
|
volatile uint8_t indicatorIsOn[2];
|
||||||
uint8_t diskType[2];
|
|
||||||
volatile int8_t trackToFlush; // -1 when there's none
|
|
||||||
|
|
||||||
volatile int8_t selectedDisk;
|
volatile int8_t selectedDisk;
|
||||||
};
|
};
|
||||||
|
|
12
apple/disktypes.h
Normal file
12
apple/disktypes.h
Normal 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
|
|
@ -1,34 +1,30 @@
|
||||||
#include "nibutil.h"
|
#include "nibutil.h"
|
||||||
|
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif
|
#include <stdio.h>
|
||||||
|
#include "disktypes.h"
|
||||||
// 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
|
|
||||||
|
|
||||||
|
// Default disk volume identifier
|
||||||
#define DISK_VOLUME 254
|
#define DISK_VOLUME 254
|
||||||
|
|
||||||
// dos 3.3 to physical sector conversion
|
// 4-and-4 encoding handlers
|
||||||
const static uint8_t dephys[16] = {
|
#define nib1(a) (((a & 0xAA) >> 1) | 0xAA)
|
||||||
0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04,
|
#define nib2(b) (((b & 0x55) ) | 0xAA)
|
||||||
0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f };
|
#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA))
|
||||||
|
|
||||||
// Prodos to physical sector conversion
|
// In 6-and-2 encoding, there are 86 (0x56) 6-bit values
|
||||||
const uint8_t deProdosPhys[] = {
|
#define SIXBIT_SPAN 0x56
|
||||||
0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b,
|
|
||||||
0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f };
|
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,
|
const static uint8_t _trans[64] = {0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||||
|
@ -56,88 +52,185 @@ const static uint8_t _detrans[0x80] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
|
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
|
||||||
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
|
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
|
||||||
|
|
||||||
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
// dos 3.3 to physical sector conversion
|
||||||
uint8_t diskType, int8_t track)
|
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[] = {
|
||||||
|
0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b,
|
||||||
|
0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f };
|
||||||
|
|
||||||
|
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;
|
static void _packGap(uint8_t *output, bitPtr *ptr)
|
||||||
#ifdef LONGGAPS
|
{
|
||||||
i < (sector==0 ? 0x63 : 0x13);
|
for (int i=0; i<8; i++)
|
||||||
#else
|
_packBit(output, ptr, 1);
|
||||||
i < 8;
|
_packBit(output, ptr, 0);
|
||||||
#endif
|
_packBit(output, ptr, 0);
|
||||||
i++) {
|
}
|
||||||
trackBuffer->addByte(GAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
trackBuffer->addByte(0xD5); // prolog
|
static void _packByte(uint8_t *output, bitPtr *ptr, uint8_t v)
|
||||||
trackBuffer->addByte(0xAA);
|
{
|
||||||
trackBuffer->addByte(0x96);
|
for (int i=0; i<8; i++) {
|
||||||
|
_packBit(output, ptr, v & (1 << (7-i)));
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#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
|
// 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
|
// 6:2-bit GCR encoded values, which we decode back in to 256 8-byte
|
||||||
// output values; and one checksum byte.
|
// output values; and one checksum byte.
|
||||||
//
|
//
|
||||||
// Return true if we've successfully consumed 343 bytes from
|
// Return true if we've successfully consumed 343 bytes from
|
||||||
// trackBuf. This reads from the circular buffer trackBuffer, so if
|
// trackBuf.
|
||||||
// there's not enough data there, the results are somewhat
|
static bool _decodeData(const uint8_t trackBuffer[343], uint8_t output[256])
|
||||||
// unpredictable.
|
|
||||||
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
|
|
||||||
{
|
{
|
||||||
// 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];
|
static uint8_t workbuf[342];
|
||||||
|
|
||||||
for (int i=0; i<342; i++) {
|
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];
|
workbuf[i] = _detrans[in];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,13 +241,12 @@ bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output)
|
||||||
prev = workbuf[i];
|
prev = workbuf[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the checksum on the track - only necessary if we're about to
|
#if 0
|
||||||
// write the nibblized version of the track back out
|
if (prev != trackBuffer[342]) {
|
||||||
/* uint16_t cursor = trackBuffer->Cursor();
|
printf("ERROR: checksum of sector is incorrect [0x%X v 0x%X]\n", prev, trackBuffer[342]);
|
||||||
trackBuffer->setPeekCursor(startAt++);
|
return false;
|
||||||
trackBuffer->replaceByte(prev); // 'prev' holds the checksum
|
}
|
||||||
trackBuffer->setPeekCursor(cursor); // put it back where we found it
|
#endif
|
||||||
*/
|
|
||||||
|
|
||||||
// Start with all of the bytes with 6 bits of data
|
// Start with all of the bytes with 6 bits of data
|
||||||
for (uint16_t i=0; i<256; i++) {
|
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);
|
output[2*SIXBIT_SPAN + i] |= ((thisbyte & 0x80) >> 7) | ((thisbyte & 0x40) >> 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME: check or update the checksum?
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encodeData(LRingBuffer *trackBuffer, uint8_t *data)
|
// trackBuffer is input NIB data; rawTrackBuffer is output DSK/PO data
|
||||||
{
|
nibErr denibblizeTrack(const uint8_t input[NIBTRACKSIZE], uint8_t rawTrackBuffer[256*16],
|
||||||
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,
|
|
||||||
uint8_t diskType, int8_t track)
|
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
|
// bitmask of the sectors that we've found while decoding. We should
|
||||||
// find all 16.
|
// find all 16.
|
||||||
uint16_t sectorsUpdated = 0;
|
uint16_t sectorsUpdated = 0;
|
||||||
|
|
||||||
// loop through the data twice, so we make sure we read anything
|
// loop through the data twice, so we make sure we read anything
|
||||||
// that crosses the end/start boundary
|
// that crosses the end/start boundary
|
||||||
// FIXME: if this approach works, we probably want 1/16th extra, not 2*
|
uint16_t startOfSector;
|
||||||
for (uint16_t i=0; i<2*trackBuffer->count(); ) {
|
for (uint16_t i=0; i<2*416*16; i++) {
|
||||||
// Find the prolog
|
// Find the prolog
|
||||||
while (trackBuffer->peek(i++) != 0xD5)
|
if (input[i % NIBTRACKSIZE] != 0xD5)
|
||||||
;
|
|
||||||
if (trackBuffer->peek(i++) != 0xAA) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
startOfSector = i;
|
||||||
if (trackBuffer->peek(i++) != 0x96) {
|
i++;
|
||||||
|
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||||
continue;
|
continue;
|
||||||
}
|
i++;
|
||||||
|
if (input[i % NIBTRACKSIZE] != 0x96)
|
||||||
|
continue;
|
||||||
|
i++;
|
||||||
|
|
||||||
// And now we should be in the header section
|
// And now we should be in the header section
|
||||||
uint8_t volumeID = denib(trackBuffer->peek(i),
|
uint8_t volumeID = denib(input[i % NIBTRACKSIZE],
|
||||||
trackBuffer->peek(i+1));
|
input[(i+1) % NIBTRACKSIZE]);
|
||||||
i += 2;
|
i += 2;
|
||||||
uint8_t trackID = denib(trackBuffer->peek(i),
|
uint8_t trackID = denib(input[i % NIBTRACKSIZE],
|
||||||
trackBuffer->peek(i+1));
|
input[(i+1) % NIBTRACKSIZE]);
|
||||||
i += 2;
|
i += 2;
|
||||||
uint8_t sectorNum = denib(trackBuffer->peek(i),
|
uint8_t sectorNum = denib(input[i % NIBTRACKSIZE],
|
||||||
trackBuffer->peek(i+1));
|
input[(i+1) % NIBTRACKSIZE]);
|
||||||
i += 2;
|
i += 2;
|
||||||
uint8_t headerChecksum = denib(trackBuffer->peek(i),
|
uint8_t headerChecksum = denib(input[i % NIBTRACKSIZE],
|
||||||
trackBuffer->peek(i+1));
|
input[(i+1) % NIBTRACKSIZE]);
|
||||||
i += 2;
|
i += 2;
|
||||||
|
|
||||||
if (headerChecksum != (volumeID ^ trackID ^ sectorNum)) {
|
if (headerChecksum != (volumeID ^ trackID ^ sectorNum)) {
|
||||||
|
@ -269,41 +312,51 @@ nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for the epilog
|
// check for the epilog
|
||||||
if (trackBuffer->peek(i++) != 0xDE) {
|
if (input[i % NIBTRACKSIZE] != 0xDE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trackBuffer->peek(i++) != 0xAA) {
|
i++;
|
||||||
|
if (input[i % NIBTRACKSIZE] != 0xAA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
// Skip to the data prolog
|
// Skip to the data prolog
|
||||||
while (trackBuffer->peek(i++) != 0xD5)
|
while (input[i % NIBTRACKSIZE] != 0xD5) {
|
||||||
;
|
i++;
|
||||||
if (trackBuffer->peek(i++) != 0xAA) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (trackBuffer->peek(i++) != 0xAD) {
|
i++;
|
||||||
|
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||||
continue;
|
continue;
|
||||||
}
|
i++;
|
||||||
|
if (input[i % NIBTRACKSIZE] != 0xAD)
|
||||||
|
continue;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
|
||||||
// Decode the data in to a temporary buffer: we don't want to overwrite
|
// Decode the data in to a temporary buffer: we don't want to overwrite
|
||||||
// something valid with partial data
|
// something valid with partial data
|
||||||
uint8_t output[256];
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
i += 343;
|
i += 343;
|
||||||
|
|
||||||
// Check the data epilog
|
// Check the data epilog
|
||||||
if (trackBuffer->peek(i++) != 0xDE) {
|
if (input[i % NIBTRACKSIZE] != 0xDE)
|
||||||
continue;
|
continue;
|
||||||
}
|
i++;
|
||||||
if (trackBuffer->peek(i++) != 0xAA) {
|
if (input[i % NIBTRACKSIZE] != 0xAA)
|
||||||
continue;
|
continue;
|
||||||
}
|
i++;
|
||||||
if (trackBuffer->peek(i++) != 0xEB) {
|
if (input[i % NIBTRACKSIZE] != 0xEB)
|
||||||
continue;
|
continue;
|
||||||
}
|
i++;
|
||||||
|
|
||||||
// We've got a whole block! Put it in the rawTrackBuffer and mark
|
// We've got a whole block! Put it in the rawTrackBuffer and mark
|
||||||
// the bit for it in sectorsUpdated.
|
// 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?
|
// FIXME: if trackID != curTrack, that's an error?
|
||||||
|
|
||||||
uint8_t targetSector;
|
uint8_t targetSector;
|
||||||
if (diskType == prodosDisk) {
|
if (diskType == T_PO) {
|
||||||
targetSector = deProdosPhys[sectorNum];
|
targetSector = deProdosPhys[sectorNum];
|
||||||
} else {
|
} else {
|
||||||
targetSector = dephys[sectorNum];
|
targetSector = dephys[sectorNum];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetSector > 16)
|
||||||
|
return errorBadData;
|
||||||
|
|
||||||
memcpy(&rawTrackBuffer[targetSector * 256],
|
memcpy(&rawTrackBuffer[targetSector * 256],
|
||||||
output,
|
output,
|
||||||
256);
|
256);
|
||||||
|
|
|
@ -1,44 +1,54 @@
|
||||||
#ifdef TEENSYDUINO
|
#ifndef __NIBUTIL_H
|
||||||
#include <Arduino.h>
|
#define __NIBUTIL_H
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "LRingBuffer.h"
|
|
||||||
|
|
||||||
#define NIBTRACKSIZE 0x1A00
|
#define NIBTRACKSIZE 0x1A00
|
||||||
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
|
#define NIBSECTORSIZE 416
|
||||||
#define MINNIBSECTORSIZE (343 + 13 + 3)
|
|
||||||
|
|
||||||
#define nib1(a) (((a & 0xAA) >> 1) | 0xAA)
|
typedef struct _nibSector {
|
||||||
#define nib2(b) (((b & 0x55) ) | 0xAA)
|
uint8_t gap1[48];
|
||||||
#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA))
|
|
||||||
|
|
||||||
#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 {
|
uint8_t gap2[5];
|
||||||
dosDisk = 0,
|
|
||||||
prodosDisk = 1,
|
uint8_t dataProlog[3];
|
||||||
nibDisk = 2
|
uint8_t data62[342];
|
||||||
};
|
uint8_t checksum;
|
||||||
|
uint8_t dataEpilog[3];
|
||||||
|
} nibSector;
|
||||||
|
|
||||||
enum nibErr {
|
enum nibErr {
|
||||||
errorNone = 0,
|
errorNone = 0,
|
||||||
errorShortTrack = 1,
|
errorMissingSectors = 1,
|
||||||
errorMissingSectors = 2
|
errorBadData = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
void nibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
uint32_t nibblizeTrack(uint8_t outputBuffer[NIBTRACKSIZE], const uint8_t rawTrackBuffer[256*16],
|
||||||
uint8_t diskType, int8_t track);
|
uint8_t diskType, int8_t track);
|
||||||
|
|
||||||
nibErr denibblizeTrack(LRingBuffer *trackBuffer, uint8_t *rawTrackBuffer,
|
nibErr denibblizeTrack(const uint8_t input[NIBTRACKSIZE], uint8_t rawTrackBuffer[256*16],
|
||||||
uint8_t diskType, int8_t track);
|
uint8_t diskType, int8_t track);
|
||||||
|
|
||||||
|
uint8_t de44(uint8_t nibs[2]);
|
||||||
|
|
||||||
bool decodeData(LRingBuffer *trackBuffer, uint16_t startAt, uint8_t *output);
|
#ifdef __cplusplus
|
||||||
void encodeData(LRingBuffer *trackBuffer, uint8_t *data);
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
6
apple/version.h
Normal file
6
apple/version.h
Normal 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
14
apple/woz-serializer.cpp
Normal 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
12
apple/woz-serializer.h
Normal 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
1088
apple/woz.cpp
Normal file
File diff suppressed because it is too large
Load Diff
89
apple/woz.h
Normal file
89
apple/woz.h
Normal 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
|
Loading…
Reference in New Issue
Block a user