Created
January 22, 2020 04:25
-
-
Save tschak909/ba1d7a15f9ed56e840c9c1b32acf8a1a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Test #32 - Multilator Rev2 Rewrite | |
*/ | |
#define TEST_NAME "#FujiNet Multi-Diskulator" | |
#ifdef ESP8266 | |
#include <ESP8266WiFi.h> | |
#endif | |
#ifdef ESP32 | |
#include <SD.h> | |
#include <SPI.h> | |
#include <WiFi.h> | |
#include <SPIFFS.h> | |
#endif | |
#include <FS.h> | |
#include <WiFiUdp.h> | |
// Uncomment for Debug on 2nd UART (GPIO 2) | |
#define DEBUG_S | |
// Uncomment for Debug on TCP/6502 to DEBUG_HOST | |
// Run: `nc -vk -l 6502` on DEBUG_HOST | |
// #define DEBUG_N | |
// #define DEBUG_HOST "" | |
// #define DEBUG_SSID "" | |
// #define DEBUG_PASSWORD "" | |
// #define DEBUG_VERBOSE 1 | |
#ifdef ESP8266 | |
#define SIO_UART Serial | |
#define BUG_UART Serial1 | |
#define PIN_LED 2 | |
#define PIN_INT 5 | |
#define PIN_PROC 4 | |
#define PIN_MTR 16 | |
#define PIN_CMD 12 | |
#endif | |
#ifdef ESP32 | |
#define SIO_UART Serial2 | |
#define BUG_UART Serial | |
#define PIN_LED1 2 | |
#define PIN_LED2 4 | |
#define PIN_INT 26 | |
#define PIN_PROC 22 | |
#define PIN_MTR 33 | |
#define PIN_CMD 21 | |
#endif | |
#define DELAY_T0 750 | |
#define DELAY_T1 650 | |
#define DELAY_T2 0 | |
#define DELAY_T3 1000 | |
#define DELAY_T4 850 | |
#define DELAY_T5 250 | |
bool hispeed = false; | |
int command_frame_counter = 0; | |
#define COMMAND_FRAME_SPEED_CHANGE_THRESHOLD 2 | |
#define HISPEED_INDEX 0x0A | |
#define HISPEED_BAUDRATE 52640 | |
#define STANDARD_BAUDRATE 19200 | |
#define SERIAL_TIMEOUT 300 | |
/** | |
A Single command frame, both in structured and unstructured | |
form. | |
*/ | |
union | |
{ | |
struct | |
{ | |
unsigned char devic; | |
unsigned char comnd; | |
unsigned char aux1; | |
unsigned char aux2; | |
unsigned char cksum; | |
}; | |
byte cmdFrameData[5]; | |
} cmdFrame; | |
struct | |
{ | |
unsigned char num_tracks; | |
unsigned char step_rate; | |
unsigned char sectors_per_trackM; | |
unsigned char sectors_per_trackL; | |
unsigned char num_sides; | |
unsigned char density; | |
unsigned char sector_sizeM; | |
unsigned char sector_sizeL; | |
unsigned char drive_present; | |
unsigned char reserved1; | |
unsigned char reserved2; | |
unsigned char reserved3; | |
} percomBlock[8]; | |
/** | |
A single SSID entry | |
*/ | |
union | |
{ | |
struct | |
{ | |
char ssid[32]; | |
char rssi; | |
}; | |
unsigned char rawData[33]; | |
} ssidInfo; | |
/** | |
Network Configuration | |
*/ | |
union | |
{ | |
struct | |
{ | |
char ssid[32]; | |
char password[64]; | |
}; | |
unsigned char rawData[96]; | |
} netConfig; | |
/** | |
TNFS Packet | |
*/ | |
union | |
{ | |
struct | |
{ | |
byte session_idl; | |
byte session_idh; | |
byte retryCount; | |
byte command; | |
byte data[512]; | |
}; | |
byte rawData[516]; | |
} tnfsPacket; | |
union | |
{ | |
char host[8][32]; | |
unsigned char rawData[256]; | |
} hostSlots; | |
union | |
{ | |
struct | |
{ | |
unsigned char hostSlot; | |
unsigned char mode; | |
char file[36]; | |
} slot[8]; | |
unsigned char rawData[304]; | |
} deviceSlots; | |
struct | |
{ | |
unsigned char session_idl; | |
unsigned char session_idh; | |
} tnfsSessionIDs[8]; | |
union | |
{ | |
struct | |
{ | |
char ssid[32]; | |
char hostname[64]; | |
unsigned char localIP[4]; | |
unsigned char gateway[4]; | |
unsigned char netmask[4]; | |
unsigned char dnsIP[4]; | |
unsigned char macAddress[6]; | |
}; | |
unsigned char rawData[118]; | |
} adapterConfig; | |
union | |
{ | |
struct | |
{ | |
unsigned short numSectors; | |
unsigned short sectorSize; | |
unsigned char hostSlot; | |
unsigned char deviceSlot; | |
char filename[36]; | |
}; | |
unsigned char rawData[42]; | |
} newDisk; | |
union | |
{ | |
struct | |
{ | |
unsigned char magic1; | |
unsigned char magic2; | |
unsigned char filesizeH; | |
unsigned char filesizeL; | |
unsigned char secsizeH; | |
unsigned char secsizeL; | |
unsigned char filesizeHH; | |
unsigned char res0; | |
unsigned char res1; | |
unsigned char res2; | |
unsigned char res3; | |
unsigned char res4; | |
unsigned char res5; | |
unsigned char res6; | |
unsigned char res7; | |
unsigned char flags; | |
}; | |
unsigned char rawData[16]; | |
} atrHeader; | |
#ifdef DEBUG_N | |
WiFiClient wificlient; | |
#endif | |
WiFiUDP UDP; | |
File atrConfig; | |
byte sectorCache[8][2560]; | |
byte sector[256]; | |
char tnfsServer[256]; | |
char mountPath[256]; | |
char current_entry[256]; | |
char tnfs_fds[8]; | |
char tnfs_dir_fds[8]; | |
int firstCachedSector[8] = {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}; | |
unsigned short sectorSize[8] = {128, 128, 128, 128, 128, 128, 128, 128}; | |
unsigned char max_cached_sectors = 19; | |
bool load_config = true; | |
char statusSkip = 0; | |
void (*cmdPtr[256])(void); // command function pointers | |
char totalSSIDs; | |
// DEBUGGING MACROS ///////////////////////////////////////////////////////////////////////// | |
#ifdef DEBUG_S | |
#define Debug_print(...) BUG_UART.print( __VA_ARGS__ ) | |
#define Debug_printf(...) BUG_UART.printf( __VA_ARGS__ ) | |
#define Debug_println(...) BUG_UART.println( __VA_ARGS__ ) | |
#define DEBUG | |
#endif | |
#ifdef DEBUG_N | |
#define Debug_print(...) wificlient.print( __VA_ARGS__ ) | |
#define Debug_printf(...) wificlient.printf( __VA_ARGS__ ) | |
#define Debug_println(...) wificlient.println( __VA_ARGS__ ) | |
#define DEBUG | |
#endif | |
///////////////////////////////////////////////////////////////////////////////////////////// | |
/** | |
Set WiFi LED | |
*/ | |
void wifi_led(bool onOff) | |
{ | |
#ifdef ESP8266 | |
digitalWrite(PIN_LED, (onOff ? LOW : HIGH)); | |
#elif defined(ESP32) | |
digitalWrite(PIN_LED1, (onOff ? LOW : HIGH)); | |
#endif | |
} | |
/** | |
Set SIO LED | |
*/ | |
void sio_led(bool onOff) | |
{ | |
#ifdef ESP32 | |
digitalWrite(PIN_LED2, (onOff ? LOW : HIGH)); | |
#endif | |
} | |
/** | |
calculate 8-bit checksum. | |
*/ | |
byte sio_checksum(byte* chunk, int length) | |
{ | |
int chkSum = 0; | |
for (int i = 0; i < length; i++) { | |
chkSum = ((chkSum + chunk[i]) >> 8) + ((chkSum + chunk[i]) & 0xff); | |
} | |
return (byte)chkSum; | |
} | |
/** | |
Return true if valid device ID | |
*/ | |
bool sio_valid_device_id() | |
{ | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
if ((load_config == true) && (cmdFrame.devic == 0x31)) // Only respond to 0x31 if in config mode | |
return true; | |
else if (cmdFrame.devic == 0x70) // respond to FujiNet Network device commands | |
return true; | |
else if (cmdFrame.devic == 0x4F) // Do not respond to Type 3/4 polls | |
return false; | |
else if (cmdFrame.devic > 0x30 && cmdFrame.devic < 0x39) | |
{ | |
if (deviceSlots.slot[deviceSlot].hostSlot != 0xFF) | |
return true; | |
else | |
return false; | |
} | |
else | |
return false; | |
} | |
/** | |
sio NAK | |
*/ | |
void sio_nak() | |
{ | |
SIO_UART.write('N'); | |
#ifdef ESP32 | |
SIO_UART.flush(); | |
#endif | |
} | |
/** | |
sio ACK | |
*/ | |
void sio_ack() | |
{ | |
SIO_UART.write('A'); | |
#ifdef ESP32 | |
SIO_UART.flush(); | |
#endif | |
} | |
/** | |
sio COMPLETE | |
*/ | |
void sio_complete() | |
{ | |
delayMicroseconds(DELAY_T5); | |
SIO_UART.write('C'); | |
} | |
/** | |
sio ERROR | |
*/ | |
void sio_error() | |
{ | |
delayMicroseconds(DELAY_T5); | |
SIO_UART.write('E'); | |
} | |
/** | |
sio READ from PERIPHERAL to COMPUTER | |
b = buffer to send to Atari | |
len = length of buffer | |
err = did an error happen before this read? | |
*/ | |
void sio_to_computer(byte* b, unsigned short len, bool err) | |
{ | |
byte ck = sio_checksum(b, len); | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T5); | |
#endif | |
if (err == true) | |
sio_error(); | |
else | |
sio_complete(); | |
// Write data frame. | |
SIO_UART.write(b, len); | |
// Write checksum | |
SIO_UART.write(ck); | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("TO COMPUTER: "); | |
for (int i = 0; i < len; i++) | |
Debug_printf("%02x ", b[i]); | |
Debug_printf("\nCKSUM: %02x\n\n", ck); | |
#endif | |
} | |
/** | |
sio WRITE from COMPUTER to PERIPHERAL | |
b = buffer from atari to fujinet | |
len = length | |
returns checksum reported by atari | |
*/ | |
byte sio_to_peripheral(byte* b, unsigned short len) | |
{ | |
byte ck; | |
// Retrieve data frame from computer | |
size_t l = SIO_UART.readBytes(b, len); | |
// Wait for checksum | |
while (!SIO_UART.available()) | |
yield(); | |
// Receive Checksum | |
ck = SIO_UART.read(); | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("l: %d\n", l); | |
Debug_printf("TO PERIPHERAL: "); | |
for (int i = 0; i < len; i++) | |
Debug_printf("%02x ", sector[i]); | |
Debug_printf("\nCKSUM: %02x\n\n", ck); | |
#endif | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T4); | |
#endif | |
if (sio_checksum(b, len) != ck) | |
{ | |
sio_nak(); | |
return false; | |
} | |
else | |
{ | |
sio_ack(); | |
} | |
return ck; | |
} | |
/** | |
Make new disk and shove into device slot | |
*/ | |
void sio_new_disk() | |
{ | |
byte ck = sio_to_peripheral(newDisk.rawData, sizeof(newDisk)); | |
if (ck == sio_checksum(newDisk.rawData, sizeof(newDisk))) | |
{ | |
deviceSlots.slot[newDisk.deviceSlot].hostSlot = newDisk.hostSlot; | |
deviceSlots.slot[newDisk.deviceSlot].mode = 0x03; // R/W | |
strcpy(deviceSlots.slot[newDisk.deviceSlot].file, newDisk.filename); | |
if (tnfs_open(newDisk.deviceSlot, 0x03, true) == true) // create file | |
{ | |
#ifdef DEBUG | |
Debug_printf("XXX Created file %s\n", deviceSlots.slot[newDisk.deviceSlot].file); | |
#endif | |
if (tnfs_write_blank_atr(newDisk.deviceSlot, newDisk.sectorSize, newDisk.numSectors) == true) | |
{ | |
#ifdef DEBUG | |
Debug_printf("XXX Wrote ATR data\n"); | |
#endif | |
sectorSize[newDisk.deviceSlot] = newDisk.sectorSize; | |
derive_percom_block(newDisk.deviceSlot, newDisk.sectorSize, newDisk.numSectors); | |
sio_complete(); | |
return; | |
} | |
else | |
{ | |
#ifdef DEBUG | |
Debug_printf("XXX ATR data write failed.\n"); | |
#endif | |
sio_error(); | |
return; | |
} | |
} | |
else | |
{ | |
#ifdef DEBUG | |
Debug_printf("XXX Could not open file %s\n", deviceSlots.slot[newDisk.deviceSlot].file); | |
#endif | |
sio_error(); | |
return; | |
} | |
} | |
else | |
{ | |
#ifdef DEBUG | |
Debug_printf("XXX Bad Checksum.\n"); | |
#endif | |
sio_error(); | |
return; | |
} | |
} | |
/** | |
Get Adapter config. | |
*/ | |
void sio_get_adapter_config() | |
{ | |
strcpy(adapterConfig.ssid, netConfig.ssid); | |
#ifdef ESP8266 | |
strcpy(adapterConfig.hostname, WiFi.hostname().c_str()); | |
#else | |
strcpy(adapterConfig.hostname, WiFi.getHostname()); | |
#endif | |
adapterConfig.localIP[0] = WiFi.localIP()[0]; | |
adapterConfig.localIP[1] = WiFi.localIP()[1]; | |
adapterConfig.localIP[2] = WiFi.localIP()[2]; | |
adapterConfig.localIP[3] = WiFi.localIP()[3]; | |
adapterConfig.gateway[0] = WiFi.gatewayIP()[0]; | |
adapterConfig.gateway[1] = WiFi.gatewayIP()[1]; | |
adapterConfig.gateway[2] = WiFi.gatewayIP()[2]; | |
adapterConfig.gateway[3] = WiFi.gatewayIP()[3]; | |
adapterConfig.netmask[0] = WiFi.subnetMask()[0]; | |
adapterConfig.netmask[1] = WiFi.subnetMask()[1]; | |
adapterConfig.netmask[2] = WiFi.subnetMask()[2]; | |
adapterConfig.netmask[3] = WiFi.subnetMask()[3]; | |
adapterConfig.dnsIP[0] = WiFi.dnsIP()[0]; | |
adapterConfig.dnsIP[1] = WiFi.dnsIP()[1]; | |
adapterConfig.dnsIP[2] = WiFi.dnsIP()[2]; | |
adapterConfig.dnsIP[3] = WiFi.dnsIP()[3]; | |
adapterConfig.macAddress[0] = WiFi.macAddress()[0]; | |
adapterConfig.macAddress[1] = WiFi.macAddress()[1]; | |
adapterConfig.macAddress[2] = WiFi.macAddress()[2]; | |
adapterConfig.macAddress[3] = WiFi.macAddress()[3]; | |
adapterConfig.macAddress[4] = WiFi.macAddress()[4]; | |
adapterConfig.macAddress[5] = WiFi.macAddress()[5]; | |
sio_to_computer(adapterConfig.rawData, sizeof(adapterConfig.rawData), false); | |
} | |
/** | |
Scan for networks | |
*/ | |
void sio_net_scan_networks() | |
{ | |
char ret[4] = {0, 0, 0, 0}; | |
// Scan to computer | |
WiFi.mode(WIFI_STA); | |
totalSSIDs = WiFi.scanNetworks(); | |
ret[0] = totalSSIDs; | |
sio_to_computer((byte *)ret, 4, false); | |
} | |
/** | |
Return scanned network entry | |
*/ | |
void sio_net_scan_result() | |
{ | |
bool err = false; | |
if (cmdFrame.aux1 < totalSSIDs) | |
{ | |
strcpy(ssidInfo.ssid, WiFi.SSID(cmdFrame.aux1).c_str()); | |
ssidInfo.rssi = (char)WiFi.RSSI(cmdFrame.aux1); | |
} | |
else | |
{ | |
memset(ssidInfo.rawData, 0x00, sizeof(ssidInfo.rawData)); | |
err = true; | |
} | |
sio_to_computer(ssidInfo.rawData, sizeof(ssidInfo.rawData), false); | |
} | |
/** | |
Set SSID | |
*/ | |
void sio_net_set_ssid() | |
{ | |
byte ck = sio_to_peripheral((byte *)&netConfig.rawData, sizeof(netConfig.rawData)); | |
if (sio_checksum(netConfig.rawData, sizeof(netConfig.rawData)) != ck) | |
{ | |
sio_error(); | |
} | |
else | |
{ | |
#ifdef DEBUG | |
Debug_printf("Connecting to net: %s password: %s\n", netConfig.ssid, netConfig.password); | |
#endif | |
WiFi.begin(netConfig.ssid, netConfig.password); | |
UDP.begin(16384); | |
sio_complete(); | |
} | |
} | |
/** | |
SIO Status | |
*/ | |
void sio_status() | |
{ | |
byte status[4] = {0x10, 0xDF, 0xFE, 0x00}; | |
byte deviceSlot = cmdFrame.devic - 0x31; | |
if (sectorSize[deviceSlot] == 256) | |
{ | |
status[0] |= 0x20; | |
} | |
if (percomBlock[deviceSlot].sectors_per_trackL == 26) | |
{ | |
status[0] |= 0x80; | |
} | |
sio_to_computer(status, sizeof(status), false); // command always completes. | |
} | |
/** | |
SIO get WiFi Status | |
*/ | |
void sio_net_get_wifi_status() | |
{ | |
char wifiStatus = WiFi.status(); | |
// Update WiFi Status LED | |
if (wifiStatus == WL_CONNECTED) | |
wifi_led(true); | |
else | |
wifi_led(false); | |
sio_to_computer((byte *)&wifiStatus, 1, false); | |
} | |
/** | |
Format Disk (fake) | |
*/ | |
void sio_format() | |
{ | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
// Populate bad sector map (no bad sectors) | |
for (int i = 0; i < sectorSize[deviceSlot]; i++) | |
sector[i] = 0; | |
sector[0] = 0xFF; // no bad sectors. | |
sector[1] = 0xFF; | |
// Send to computer | |
sio_to_computer((byte *)sector, sectorSize[deviceSlot], false); | |
} | |
/** | |
SIO TNFS Server Mount | |
*/ | |
void sio_tnfs_mount_host() | |
{ | |
unsigned char hostSlot = cmdFrame.aux1; | |
bool err = tnfs_mount(hostSlot); | |
if (!err) | |
sio_error(); | |
else | |
sio_complete(); | |
} | |
/** | |
Dump PERCOM block | |
*/ | |
void dump_percom_block(unsigned char deviceSlot) | |
{ | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("Percom Block Dump\n"); | |
Debug_printf("-----------------\n"); | |
Debug_printf("Num Tracks: %d\n", percomBlock[deviceSlot].num_tracks); | |
Debug_printf("Step Rate: %d\n", percomBlock[deviceSlot].step_rate); | |
Debug_printf("Sectors per Track: %d\n", (percomBlock[deviceSlot].sectors_per_trackM * 256 + percomBlock[deviceSlot].sectors_per_trackL)); | |
Debug_printf("Num Sides: %d\n", percomBlock[deviceSlot].num_sides); | |
Debug_printf("Density: %d\n", percomBlock[deviceSlot].density); | |
Debug_printf("Sector Size: %d\n", (percomBlock[deviceSlot].sector_sizeM * 256 + percomBlock[deviceSlot].sector_sizeL)); | |
Debug_printf("Drive Present: %d\n", percomBlock[deviceSlot].drive_present); | |
Debug_printf("Reserved1: %d\n", percomBlock[deviceSlot].reserved1); | |
Debug_printf("Reserved2: %d\n", percomBlock[deviceSlot].reserved2); | |
Debug_printf("Reserved3: %d\n", percomBlock[deviceSlot].reserved3); | |
#endif | |
} | |
/** | |
Convert # of paragraphs to sectors | |
para = # of paragraphs returned from ATR header | |
ss = sector size returned from ATR header | |
*/ | |
unsigned short para_to_num_sectors(unsigned short para, unsigned char para_hi, unsigned short ss) | |
{ | |
unsigned long tmp = para_hi << 16; | |
tmp |= para; | |
unsigned short num_sectors = ((tmp << 4) / ss); | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("ATR Header\n"); | |
Debug_printf("----------\n"); | |
Debug_printf("num paragraphs: $%04x\n", para); | |
Debug_printf("Sector Size: %d\n", ss); | |
Debug_printf("num sectors: %d\n", num_sectors); | |
#endif | |
// Adjust sector size for the fact that the first three sectors are 128 bytes | |
if (ss == 256) | |
num_sectors += 2; | |
return num_sectors; | |
} | |
unsigned long num_sectors_to_para(unsigned short num_sectors, unsigned short sector_size) | |
{ | |
unsigned long file_size = (num_sectors * sector_size); | |
// Subtract bias for the first three sectors. | |
if (sector_size > 128) | |
file_size -= 384; | |
return file_size >> 4; | |
} | |
/** | |
Update PERCOM block from the total # of sectors. | |
*/ | |
void derive_percom_block(unsigned char deviceSlot, unsigned short sectorSize, unsigned short numSectors) | |
{ | |
// Start with 40T/1S 720 Sectors, sector size passed in | |
percomBlock[deviceSlot].num_tracks = 40; | |
percomBlock[deviceSlot].step_rate = 1; | |
percomBlock[deviceSlot].sectors_per_trackM = 0; | |
percomBlock[deviceSlot].sectors_per_trackL = 18; | |
percomBlock[deviceSlot].num_sides = 0; | |
percomBlock[deviceSlot].density = 0; // >128 bytes = MFM | |
percomBlock[deviceSlot].sector_sizeM = (sectorSize == 256 ? 0x01 : 0x00); | |
percomBlock[deviceSlot].sector_sizeL = (sectorSize == 256 ? 0x00 : 0x80); | |
percomBlock[deviceSlot].drive_present = 255; | |
percomBlock[deviceSlot].reserved1 = 0; | |
percomBlock[deviceSlot].reserved2 = 0; | |
percomBlock[deviceSlot].reserved3 = 0; | |
if (numSectors == 1040) // 5/25" 1050 density | |
{ | |
percomBlock[deviceSlot].sectors_per_trackM = 0; | |
percomBlock[deviceSlot].sectors_per_trackL = 26; | |
percomBlock[deviceSlot].density = 4; // 1050 density is MFM, override. | |
} | |
else if (numSectors == 720 && sectorSize == 256) // 5.25" SS/DD | |
{ | |
percomBlock[deviceSlot].density = 4; // 1050 density is MFM, override. | |
} | |
else if (numSectors == 1440) // 5.25" DS/DD | |
{ | |
percomBlock[deviceSlot].num_sides = 1; | |
percomBlock[deviceSlot].density = 4; // 1050 density is MFM, override. | |
} | |
else if (numSectors == 2880) // 5.25" DS/QD | |
{ | |
percomBlock[deviceSlot].num_sides = 1; | |
percomBlock[deviceSlot].num_tracks = 80; | |
percomBlock[deviceSlot].density = 4; // 1050 density is MFM, override. | |
} | |
else if (numSectors == 2002 && sectorSize == 128) // SS/SD 8" | |
{ | |
percomBlock[deviceSlot].num_tracks = 77; | |
percomBlock[deviceSlot].density = 0; // FM density | |
} | |
else if (numSectors == 2002 && sectorSize == 256) // SS/DD 8" | |
{ | |
percomBlock[deviceSlot].num_tracks = 77; | |
percomBlock[deviceSlot].density = 4; // MFM density | |
} | |
else if (numSectors == 4004 && sectorSize == 128) // DS/SD 8" | |
{ | |
percomBlock[deviceSlot].num_tracks = 77; | |
percomBlock[deviceSlot].density = 0; // FM density | |
} | |
else if (numSectors == 4004 && sectorSize == 256) // DS/DD 8" | |
{ | |
percomBlock[deviceSlot].num_sides = 1; | |
percomBlock[deviceSlot].num_tracks = 77; | |
percomBlock[deviceSlot].density = 4; // MFM density | |
} | |
else if (numSectors == 5760) // 1.44MB 3.5" High Density | |
{ | |
percomBlock[deviceSlot].num_sides = 1; | |
percomBlock[deviceSlot].num_tracks = 80; | |
percomBlock[deviceSlot].sectors_per_trackL = 36; | |
percomBlock[deviceSlot].density = 8; // I think this is right. | |
} | |
else | |
{ | |
// This is a custom size, one long track. | |
percomBlock[deviceSlot].num_tracks = 1; | |
percomBlock[deviceSlot].sectors_per_trackM = numSectors >> 8; | |
percomBlock[deviceSlot].sectors_per_trackL = numSectors & 0xFF; | |
} | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("Percom block dump for newly mounted device slot %d\n", deviceSlot); | |
dump_percom_block(deviceSlot); | |
#endif | |
} | |
/** | |
Read percom block | |
*/ | |
void sio_read_percom_block() | |
{ | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
#ifdef DEBUG_VERBOSE | |
dump_percom_block(deviceSlot); | |
#endif | |
sio_to_computer((byte *)&percomBlock[deviceSlot], 12, false); | |
SIO_UART.flush(); | |
} | |
/** | |
Write percom block | |
*/ | |
void sio_write_percom_block() | |
{ | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
sio_to_peripheral((byte *)&percomBlock[deviceSlot], 12); | |
#ifdef DEBUG_VERBOSE | |
dump_percom_block(deviceSlot); | |
#endif | |
sio_complete(); | |
} | |
/** | |
SIO TNFS Disk Image Mount | |
*/ | |
void sio_disk_image_mount() | |
{ | |
unsigned char deviceSlot = cmdFrame.aux1; | |
unsigned char options = cmdFrame.aux2; // 1=R | 2=R/W | 128=FETCH | |
unsigned short newss; | |
unsigned short num_para; | |
unsigned char num_para_hi; | |
unsigned short num_sectors; | |
bool opened = tnfs_open(deviceSlot, options, false); | |
if (!opened) | |
{ | |
sio_error(); | |
} | |
else | |
{ | |
// Get file and sector size from header | |
tnfs_seek(deviceSlot, 2); | |
tnfs_read(deviceSlot, 2); | |
num_para = (256 * tnfsPacket.data[4]) + tnfsPacket.data[3]; | |
tnfs_read(deviceSlot, 2); | |
newss = (256 * tnfsPacket.data[4]) + tnfsPacket.data[3]; | |
tnfs_read(deviceSlot, 1); | |
num_para_hi = tnfsPacket.data[3]; | |
sectorSize[deviceSlot] = newss; | |
num_sectors = para_to_num_sectors(num_para, num_para_hi, newss); | |
derive_percom_block(deviceSlot, newss, num_sectors); | |
sio_complete(); | |
} | |
} | |
/** | |
SIO TNFS Disk Image uMount | |
*/ | |
void sio_disk_image_umount() | |
{ | |
unsigned char deviceSlot = cmdFrame.aux1; | |
bool opened = tnfs_close(deviceSlot); | |
sio_complete(); // always completes. | |
} | |
/** | |
Open TNFS Directory | |
*/ | |
void sio_tnfs_open_directory() | |
{ | |
byte hostSlot = cmdFrame.aux1; | |
byte ck = sio_to_peripheral((byte *)¤t_entry, sizeof(current_entry)); | |
if (tnfs_opendir(hostSlot)) | |
sio_complete(); | |
else | |
sio_error(); | |
} | |
/** | |
Read next TNFS Directory entry | |
*/ | |
void sio_tnfs_read_directory_entry() | |
{ | |
byte hostSlot = cmdFrame.aux2; | |
byte len = cmdFrame.aux1; | |
byte ret = tnfs_readdir(hostSlot); | |
if (!ret) | |
current_entry[0] = 0x7F; // end of dir | |
sio_to_computer((byte *)¤t_entry, len, false); | |
} | |
/** | |
Close TNFS Directory | |
*/ | |
void sio_tnfs_close_directory() | |
{ | |
byte hostSlot = cmdFrame.aux1; | |
if (tnfs_closedir(hostSlot)) | |
sio_complete(); | |
else | |
sio_error(); | |
} | |
/** | |
(disk) High Speed | |
*/ | |
void sio_high_speed() | |
{ | |
byte hsd = HISPEED_INDEX; | |
sio_to_computer((byte *)&hsd, 1, false); | |
} | |
/** | |
Write hosts slots | |
*/ | |
void sio_write_hosts_slots() | |
{ | |
byte ck = sio_to_peripheral(hostSlots.rawData, sizeof(hostSlots.rawData)); | |
if (sio_checksum(hostSlots.rawData, sizeof(hostSlots.rawData)) == ck) | |
{ | |
atrConfig.seek(91792, SeekSet); | |
atrConfig.write(hostSlots.rawData, sizeof(hostSlots.rawData)); | |
atrConfig.flush(); | |
sio_complete(); | |
} | |
else | |
sio_error(); | |
} | |
/** | |
Write Device slots | |
*/ | |
void sio_write_device_slots() | |
{ | |
byte ck = sio_to_peripheral(deviceSlots.rawData, sizeof(deviceSlots.rawData)); | |
if (sio_checksum(deviceSlots.rawData, sizeof(deviceSlots.rawData)) == ck) | |
{ | |
atrConfig.seek(91408, SeekSet); | |
atrConfig.write(deviceSlots.rawData, sizeof(deviceSlots.rawData)); | |
atrConfig.flush(); | |
sio_complete(); | |
} | |
else | |
sio_error(); | |
} | |
/** | |
SIO Disk Write | |
*/ | |
void sio_write() | |
{ | |
byte ck; | |
int ss; // sector size | |
int offset = (256 * cmdFrame.aux2) + cmdFrame.aux1; | |
int sectorNum = offset; | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
if (sectorNum < 4) | |
{ | |
// First three sectors are always single density | |
offset *= 128; | |
offset -= 128; | |
offset += 16; // skip 16 byte ATR Header | |
ss = 128; | |
} | |
else | |
{ | |
// First three sectors are always single density | |
offset *= sectorSize[deviceSlot]; | |
offset -= sectorSize[deviceSlot]; | |
ss = sectorSize[deviceSlot]; | |
// Bias adjustment for 256 bytes | |
if (ss == 256) | |
offset -= 384; | |
offset += 16; // skip 16 byte ATR Header | |
} | |
memset(sector, 0, 256); // clear buffer | |
ck = sio_to_peripheral(sector, ss); | |
if (ck == sio_checksum(sector, ss)) | |
{ | |
if (load_config == true) | |
{ | |
atrConfig.seek(offset, SeekSet); | |
atrConfig.write(sector, ss); | |
atrConfig.flush(); | |
} | |
else | |
{ | |
tnfs_seek(deviceSlot, offset); | |
tnfs_write(deviceSlot, ss); | |
firstCachedSector[cmdFrame.devic - 0x31] = 65535; // invalidate cache | |
} | |
sio_complete(); | |
} | |
else | |
{ | |
sio_error(); | |
} | |
} | |
/** | |
Read hosts Slots | |
*/ | |
void sio_read_hosts_slots() | |
{ | |
sio_to_computer(hostSlots.rawData, sizeof(hostSlots.rawData), false); | |
} | |
/** | |
Read Device Slots | |
*/ | |
void sio_read_device_slots() | |
{ | |
load_config = false; | |
sio_to_computer(deviceSlots.rawData, sizeof(deviceSlots.rawData), false); | |
} | |
/** | |
SIO Disk read | |
*/ | |
void sio_read() | |
{ | |
int ss; | |
unsigned char deviceSlot = cmdFrame.devic - 0x31; | |
int sectorNum = (256 * cmdFrame.aux2) + cmdFrame.aux1; | |
int cacheOffset = 0; | |
int offset; | |
byte* s; | |
byte* d; | |
byte err = false; | |
#ifdef DEBUG | |
Debug_printf("Read sector #%u\n",sectorNum); | |
#endif | |
if (load_config == true) // no TNFS ATR mounted. | |
{ | |
ss = 128; | |
offset = sectorNum; | |
offset *= 128; | |
offset -= 128; | |
offset += 16; | |
atrConfig.seek(offset, SeekSet); | |
atrConfig.read(sector, 128); | |
} | |
else // TNFS ATR mounted and opened... | |
{ | |
if (sectorNum<4) | |
ss = 128; | |
else | |
ss = sectorSize[deviceSlot]; | |
offset = sectorNum; | |
offset *= ss; | |
offset -= ss; | |
// Bias adjustment for 256 bytes | |
if (ss==256) | |
offset -= 384; | |
#ifdef DEBUG | |
Debug_printf("Sector size: %d\n",ss); | |
#endif | |
offset += 16; // ATR header | |
tnfs_seek(deviceSlot,offset); | |
tnfs_read(deviceSlot,ss); | |
d = §or[0]; | |
s = &tnfsPacket.data[3]; | |
memcpy(d, s, ss); | |
} | |
sio_to_computer((byte *)§or, ss, err); | |
} | |
/** | |
Drain data out of SIO port | |
*/ | |
void sio_flush() | |
{ | |
while (SIO_UART.available()) | |
{ | |
SIO_UART.read(); // toss it. | |
#ifdef DEBUG | |
Debug_printf("."); | |
#endif | |
} | |
} | |
/** | |
Mount the TNFS server | |
*/ | |
bool tnfs_mount(unsigned char hostSlot) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
memset(tnfsPacket.rawData, 0, sizeof(tnfsPacket.rawData)); | |
// Do not mount, if we already have a session ID, just bail. | |
if (tnfsSessionIDs[hostSlot].session_idl != 0 && tnfsSessionIDs[hostSlot].session_idh != 0) | |
return true; | |
tnfsPacket.session_idl = 0; | |
tnfsPacket.session_idh = 0; | |
tnfsPacket.retryCount = 0; | |
tnfsPacket.command = 0; | |
tnfsPacket.data[0] = 0x01; // vers | |
tnfsPacket.data[1] = 0x00; // " " | |
tnfsPacket.data[2] = 0x2F; // / | |
tnfsPacket.data[3] = 0x00; // nul | |
tnfsPacket.data[4] = 0x00; // no username | |
tnfsPacket.data[5] = 0x00; // no password | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Mounting / from "); | |
Debug_println((char*)hostSlots.host[hostSlot]); | |
for (int i = 0; i < 32; i++) | |
Debug_printf("%02x ", hostSlots.host[hostSlot][i]); | |
Debug_printf("\n\n"); | |
Debug_print("Req Packet: "); | |
for (int i = 0; i < 10; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
UDP.beginPacket(String(hostSlots.host[hostSlot]).c_str(), 16384); | |
UDP.write(tnfsPacket.rawData, 10); | |
UDP.endPacket(); | |
#ifdef DEBUG_VERBOSE | |
Debug_println("Wrote the packet"); | |
#endif | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Resp Packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Successful, Session ID: "); | |
Debug_print(tnfsPacket.session_idl, HEX); | |
Debug_println(tnfsPacket.session_idh, HEX); | |
#endif /* DEBUG_S */ | |
// Persist the session ID. | |
tnfsSessionIDs[hostSlot].session_idl = tnfsPacket.session_idl; | |
tnfsSessionIDs[hostSlot].session_idh = tnfsPacket.session_idh; | |
return true; | |
} | |
else | |
{ | |
// Error | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Error #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S */ | |
return false; | |
} | |
} | |
} | |
// Otherwise we timed out. | |
#ifdef DEBUG_VERBOSE | |
Debug_println("Timeout after 5000ms"); | |
#endif /* DEBUG_S */ | |
retries++; | |
tnfsPacket.retryCount--; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
return false; | |
} | |
/** | |
Open 'autorun.atr' | |
*/ | |
bool tnfs_open(unsigned char deviceSlot, unsigned char options, bool create) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
int c = 0; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
strcpy(mountPath, deviceSlots.slot[deviceSlot].file); | |
tnfsPacket.session_idl = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idh; | |
tnfsPacket.retryCount++; // increase sequence # | |
tnfsPacket.command = 0x29; // OPEN | |
if (options == 0x01) | |
tnfsPacket.data[c++] = 0x01; | |
else if (options == 0x02) | |
tnfsPacket.data[c++] = 0x03; | |
else | |
tnfsPacket.data[c++] = 0x03; | |
tnfsPacket.data[c++] = (create == true ? 0x01 : 0x00); // Create flag | |
tnfsPacket.data[c++] = 0xFF; // mode | |
tnfsPacket.data[c++] = 0x01; // | |
tnfsPacket.data[c++] = '/'; // Filename start | |
for (int i = 0; i < strlen(mountPath); i++) | |
{ | |
tnfsPacket.data[i + 5] = mountPath[i]; | |
c++; | |
} | |
tnfsPacket.data[c++] = 0x00; | |
tnfsPacket.data[c++] = 0x00; | |
tnfsPacket.data[c++] = 0x00; | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("Opening /%s\n", mountPath); | |
Debug_println(""); | |
Debug_print("Req Packet: "); | |
for (int i = 0; i < c + 4; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
#endif /* DEBUG_S */ | |
UDP.beginPacket(hostSlots.host[deviceSlots.slot[deviceSlot].hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, c + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Resp packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif // DEBUG_S | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
tnfs_fds[deviceSlot] = tnfsPacket.data[1]; | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Successful, file descriptor: #"); | |
Debug_println(tnfs_fds[deviceSlot], HEX); | |
#endif /* DEBUG_S */ | |
return true; | |
} | |
else | |
{ | |
// unsuccessful | |
#ifdef DEBUG | |
Debug_print("Error code #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S*/ | |
return false; | |
} | |
} | |
} | |
// Otherwise, we timed out. | |
retries++; | |
tnfsPacket.retryCount--; | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
#endif /* DEBUG_S */ | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed\n"); | |
#endif | |
return false; | |
} | |
/** | |
Open 'autorun.atr' | |
*/ | |
bool tnfs_close(unsigned char deviceSlot) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
int c = 0; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
strcpy(mountPath, deviceSlots.slot[deviceSlot].file); | |
tnfsPacket.session_idl = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idh; | |
tnfsPacket.retryCount++; // increase sequence # | |
tnfsPacket.command = 0x23; // CLOSE | |
tnfsPacket.data[c++] = tnfs_fds[deviceSlot]; | |
for (int i = 0; i < strlen(mountPath); i++) | |
{ | |
tnfsPacket.data[i + 5] = mountPath[i]; | |
c++; | |
} | |
UDP.beginPacket(hostSlots.host[deviceSlots.slot[deviceSlot].hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, c + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Resp packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif // DEBUG_S | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
return true; | |
} | |
else | |
{ | |
// unsuccessful | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Error code #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S*/ | |
return false; | |
} | |
} | |
} | |
// Otherwise, we timed out. | |
retries++; | |
tnfsPacket.retryCount--; | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
#endif /* DEBUG_S */ | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed\n"); | |
#endif | |
return false; | |
} | |
/** | |
TNFS Open Directory | |
*/ | |
bool tnfs_opendir(unsigned char hostSlot) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
tnfsPacket.session_idl = tnfsSessionIDs[hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[hostSlot].session_idh; | |
tnfsPacket.retryCount++; // increase sequence # | |
tnfsPacket.command = 0x10; // OPENDIR | |
tnfsPacket.data[0] = '/'; // Open root dir | |
tnfsPacket.data[1] = 0x00; // nul terminated | |
#ifdef DEBUG | |
Debug_println("TNFS Open directory /"); | |
#endif | |
UDP.beginPacket(String(hostSlots.host[hostSlot]).c_str(), 16384); | |
UDP.write(tnfsPacket.rawData, 2 + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
tnfs_dir_fds[hostSlot] = tnfsPacket.data[1]; | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("Opened dir on slot #%d - fd = %02x\n", hostSlot, tnfs_dir_fds[hostSlot]); | |
#endif | |
return true; | |
} | |
else | |
{ | |
// Unsuccessful | |
return false; | |
} | |
} | |
} | |
// Otherwise, we timed out. | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
#endif | |
retries++; | |
tnfsPacket.retryCount--; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed."); | |
#endif | |
return false; | |
} | |
/** | |
TNFS Read Directory | |
Reads the next directory entry | |
*/ | |
bool tnfs_readdir(unsigned char hostSlot) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
tnfsPacket.session_idl = tnfsSessionIDs[hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[hostSlot].session_idh; | |
tnfsPacket.retryCount++; // increase sequence # | |
tnfsPacket.command = 0x11; // READDIR | |
tnfsPacket.data[0] = tnfs_dir_fds[hostSlot]; // Open root dir | |
#ifdef DEBUG_VERBOSE | |
Debug_printf("TNFS Read next dir entry, slot #%d - fd %02x\n\n", hostSlot, tnfs_dir_fds[hostSlot]); | |
#endif | |
UDP.beginPacket(String(hostSlots.host[hostSlot]).c_str(), 16384); | |
UDP.write(tnfsPacket.rawData, 1 + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
strcpy((char*)¤t_entry, (char *)&tnfsPacket.data[1]); | |
return true; | |
} | |
else | |
{ | |
// Unsuccessful | |
return false; | |
} | |
} | |
} | |
// Otherwise, we timed out. | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
#endif /* DEBUG_S */ | |
retries++; | |
tnfsPacket.retryCount--; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
} | |
/** | |
TNFS Close Directory | |
*/ | |
bool tnfs_closedir(unsigned char hostSlot) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
tnfsPacket.session_idl = tnfsSessionIDs[hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[hostSlot].session_idh; | |
tnfsPacket.retryCount++; // increase sequence # | |
tnfsPacket.command = 0x12; // CLOSEDIR | |
tnfsPacket.data[0] = tnfs_dir_fds[hostSlot]; // Open root dir | |
#ifdef DEBUG_VERBOSE | |
Debug_println("TNFS dir close"); | |
#endif | |
UDP.beginPacket(hostSlots.host[hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, 1 + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, 516); | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
return true; | |
} | |
else | |
{ | |
// Unsuccessful | |
return false; | |
} | |
} | |
} | |
// Otherwise, we timed out. | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
retries++; | |
tnfsPacket.retryCount--; | |
#endif /* DEBUG_S */ | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
return false; | |
} | |
/** | |
TNFS write | |
*/ | |
bool tnfs_write(unsigned char deviceSlot, unsigned short len) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
tnfsPacket.session_idl = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idh; | |
tnfsPacket.retryCount++; // Increase sequence | |
tnfsPacket.command = 0x22; // READ | |
tnfsPacket.data[0] = tnfs_fds[deviceSlot]; // returned file descriptor | |
tnfsPacket.data[1] = len & 0xFF; | |
tnfsPacket.data[2] = len >> 8; | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Writing to File descriptor: "); | |
Debug_println(tnfs_fds[deviceSlot]); | |
Debug_print("Req Packet: "); | |
for (int i = 0; i < 7; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
UDP.beginPacket(hostSlots.host[deviceSlots.slot[deviceSlot].hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, 4 + 3); | |
UDP.write(sector, len); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, sizeof(tnfsPacket.rawData)); | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Resp packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
#ifdef DEBUG_VERBOSE | |
Debug_println("Successful."); | |
#endif /* DEBUG_S */ | |
return true; | |
} | |
else | |
{ | |
// Error | |
#ifdef DEBUG | |
Debug_print("Error code #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S*/ | |
return false; | |
} | |
} | |
} | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
#endif /* DEBUG_S */ | |
retries++; | |
tnfsPacket.retryCount--; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
} | |
/** | |
TNFS Write blank ATR | |
*/ | |
bool tnfs_write_blank_atr(unsigned char deviceSlot, unsigned short sectorSize, unsigned short numSectors) | |
{ | |
unsigned long num_para = num_sectors_to_para(numSectors, sectorSize); | |
unsigned long offset; | |
// Write header | |
atrHeader.magic1 = 0x96; | |
atrHeader.magic2 = 0x02; | |
atrHeader.filesizeH = num_para & 0xFF; | |
atrHeader.filesizeL = (num_para & 0xFF00) >> 8; | |
atrHeader.filesizeHH = (num_para & 0xFF0000) >> 16; | |
atrHeader.secsizeH = sectorSize & 0xFF; | |
atrHeader.secsizeL = sectorSize >> 8; | |
#ifdef DEBUG | |
Debug_printf("TNFS: Write header\n"); | |
#endif | |
memcpy(sector, atrHeader.rawData, sizeof(atrHeader.rawData)); | |
tnfs_write(deviceSlot, sizeof(atrHeader.rawData)); | |
offset += sizeof(atrHeader.rawData); | |
// Write first three 128 byte sectors | |
memset(sector, 0x00, sizeof(sector)); | |
#ifdef DEBUG | |
Debug_printf("TNFS: Write first three sectors\n"); | |
#endif | |
for (unsigned char i = 0; i < 3; i++) | |
{ | |
tnfs_write(deviceSlot, 128); | |
offset += 128; | |
numSectors--; | |
} | |
#ifdef DEBUG | |
Debug_printf("TNFS: Sparse Write the rest.\n"); | |
#endif | |
// Write the rest of the sectors via sparse seek | |
offset += (numSectors * sectorSize) - sectorSize; | |
tnfs_seek(deviceSlot, offset); | |
tnfs_write(deviceSlot, sectorSize); | |
return true; //fixme | |
} | |
/** | |
TNFS read | |
*/ | |
bool tnfs_read(unsigned char deviceSlot, unsigned short len) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
tnfsPacket.session_idl = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idh; | |
tnfsPacket.retryCount++; // Increase sequence | |
tnfsPacket.command = 0x21; // READ | |
tnfsPacket.data[0] = tnfs_fds[deviceSlot]; // returned file descriptor | |
tnfsPacket.data[1] = len & 0xFF; // len bytes | |
tnfsPacket.data[2] = len >> 8; // | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Reading from File descriptor: "); | |
Debug_println(tnfs_fds[deviceSlot]); | |
Debug_print("Req Packet: "); | |
for (int i = 0; i < 7; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
UDP.beginPacket(hostSlots.host[deviceSlots.slot[deviceSlot].hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, 4 + 3); | |
UDP.endPacket(); | |
start = millis(); | |
dur = millis() - start; | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, sizeof(tnfsPacket.rawData)); | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Resp packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
if (tnfsPacket.data[0] == 0x00) | |
{ | |
// Successful | |
#ifdef DEBUG_VERBOSE | |
Debug_println("Successful."); | |
#endif /* DEBUG_S */ | |
return true; | |
} | |
else | |
{ | |
// Error | |
#ifdef DEBUG | |
Debug_print("Error code #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S*/ | |
return false; | |
} | |
} | |
} | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
if (retries < 5) | |
Debug_printf("Retrying...\n"); | |
#endif /* DEBUG_S */ | |
retries++; | |
tnfsPacket.retryCount--; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
return false; | |
} | |
/** | |
TNFS seek | |
*/ | |
bool tnfs_seek(unsigned char deviceSlot, long offset) | |
{ | |
int start = millis(); | |
int dur = millis() - start; | |
byte offsetVal[4]; | |
unsigned char retries = 0; | |
while (retries < 5) | |
{ | |
offsetVal[0] = (int)((offset & 0xFF000000) >> 24 ); | |
offsetVal[1] = (int)((offset & 0x00FF0000) >> 16 ); | |
offsetVal[2] = (int)((offset & 0x0000FF00) >> 8 ); | |
offsetVal[3] = (int)((offset & 0X000000FF)); | |
tnfsPacket.retryCount++; | |
tnfsPacket.session_idl = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idl; | |
tnfsPacket.session_idh = tnfsSessionIDs[deviceSlots.slot[deviceSlot].hostSlot].session_idh; | |
tnfsPacket.command = 0x25; // LSEEK | |
tnfsPacket.data[0] = tnfs_fds[deviceSlot]; | |
tnfsPacket.data[1] = 0x00; // SEEK_SET | |
tnfsPacket.data[2] = offsetVal[3]; | |
tnfsPacket.data[3] = offsetVal[2]; | |
tnfsPacket.data[4] = offsetVal[1]; | |
tnfsPacket.data[5] = offsetVal[0]; | |
#ifdef DEBUG | |
Debug_print("Seek requested to offset: "); | |
Debug_println(offset); | |
#endif /* DEBUG */ | |
#ifdef DEBUG_VERBOSE | |
Debug_print("Req packet: "); | |
for (int i = 0; i < 10; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S*/ | |
UDP.beginPacket(hostSlots.host[deviceSlots.slot[deviceSlot].hostSlot], 16384); | |
UDP.write(tnfsPacket.rawData, 6 + 4); | |
UDP.endPacket(); | |
while (dur < 5000) | |
{ | |
dur = millis() - start; | |
yield(); | |
if (UDP.parsePacket()) | |
{ | |
int l = UDP.read(tnfsPacket.rawData, sizeof(tnfsPacket.rawData)); | |
#ifdef DEBUG | |
Debug_print("Resp packet: "); | |
for (int i = 0; i < l; i++) | |
{ | |
Debug_print(tnfsPacket.rawData[i], HEX); | |
Debug_print(" "); | |
} | |
Debug_println(""); | |
#endif /* DEBUG_S */ | |
if (tnfsPacket.data[0] == 0) | |
{ | |
// Success. | |
#ifdef DEBUG_VERBOSE | |
Debug_println("Successful."); | |
#endif /* DEBUG_S */ | |
return true; | |
} | |
else | |
{ | |
// Error. | |
#ifdef DEBUG | |
Debug_print("Error code #"); | |
Debug_println(tnfsPacket.data[0], HEX); | |
#endif /* DEBUG_S*/ | |
return false; | |
} | |
} | |
} | |
#ifdef DEBUG | |
Debug_println("Timeout after 5000ms."); | |
if (retries < 5) | |
Debug_printf("Retrying...\n"); | |
#endif /* DEBUG_S */ | |
tnfsPacket.retryCount--; | |
retries++; | |
} | |
#ifdef DEBUG | |
Debug_printf("Failed.\n"); | |
#endif | |
return false; | |
} | |
void sio_wait() | |
{ | |
SIO_UART.read(); // Toss it for now | |
} | |
void setup() | |
{ | |
#ifdef DEBUG_S | |
BUG_UART.begin(115200); | |
Debug_println(); | |
Debug_println(TEST_NAME); | |
#endif | |
SPIFFS.begin(); | |
atrConfig = SPIFFS.open("/autorun.atr", "r+"); | |
// Go ahead and read the host slots from disk | |
atrConfig.seek(91792, SeekSet); | |
atrConfig.read(hostSlots.rawData, 256); | |
// And populate the device slots | |
atrConfig.seek(91408, SeekSet); | |
atrConfig.read(deviceSlots.rawData, 304); | |
// Go ahead and mark all device slots local | |
for (int i = 0; i < 8; i++) | |
{ | |
if (deviceSlots.slot[i].file[0] == 0x00) | |
{ | |
deviceSlots.slot[i].hostSlot = 0xFF; | |
} | |
} | |
// Set up pins | |
pinMode(PIN_INT, OUTPUT); // thanks AtariGeezer | |
digitalWrite(PIN_INT, HIGH); | |
pinMode(PIN_PROC, OUTPUT); // thanks AtariGeezer | |
digitalWrite(PIN_PROC, HIGH); | |
pinMode(PIN_MTR, INPUT); | |
pinMode(PIN_CMD, INPUT); | |
#ifdef ESP8266 | |
pinMode(PIN_LED, INPUT); | |
digitalWrite(PIN_LED, HIGH); // off | |
#elif defined(ESP32) | |
pinMode(PIN_LED1, OUTPUT); | |
pinMode(PIN_LED2, OUTPUT); | |
digitalWrite(PIN_LED1, HIGH); // off | |
digitalWrite(PIN_LED2, HIGH); // off | |
#endif | |
#ifdef DEBUG_N | |
/* Get WiFi started, but don't wait for it otherwise SIO | |
powered FujiNet fails to boot | |
*/ | |
WiFi.begin(DEBUG_SSID, DEBUG_PASSWORD); | |
#endif | |
// Set up serial | |
SIO_UART.begin(STANDARD_BAUDRATE); | |
SIO_UART.setTimeout(SERIAL_TIMEOUT); | |
#ifdef ESP8266 | |
SIO_UART.swap(); | |
#endif | |
// Set up SIO command function pointers | |
for (int i = 0; i < 256; i++) | |
cmdPtr[i] = sio_wait; | |
cmdPtr['P'] = sio_write; | |
cmdPtr['W'] = sio_write; | |
cmdPtr['R'] = sio_read; | |
cmdPtr['S'] = sio_status; | |
cmdPtr['!'] = sio_format; | |
cmdPtr['"'] = sio_format; | |
cmdPtr[0x3F] = sio_high_speed; | |
cmdPtr[0x4E] = sio_read_percom_block; | |
cmdPtr[0x4F] = sio_write_percom_block; | |
cmdPtr[0xFD] = sio_net_scan_networks; | |
cmdPtr[0xFC] = sio_net_scan_result; | |
cmdPtr[0xFB] = sio_net_set_ssid; | |
cmdPtr[0xFA] = sio_net_get_wifi_status; | |
cmdPtr[0xF9] = sio_tnfs_mount_host; | |
cmdPtr[0xF8] = sio_disk_image_mount; | |
cmdPtr[0xF7] = sio_tnfs_open_directory; | |
cmdPtr[0xF6] = sio_tnfs_read_directory_entry; | |
cmdPtr[0xF5] = sio_tnfs_close_directory; | |
cmdPtr[0xF4] = sio_read_hosts_slots; | |
cmdPtr[0xF3] = sio_write_hosts_slots; | |
cmdPtr[0xF2] = sio_read_device_slots; | |
cmdPtr[0xF1] = sio_write_device_slots; | |
cmdPtr[0xE9] = sio_disk_image_umount; | |
cmdPtr[0xE8] = sio_get_adapter_config; | |
cmdPtr[0xE7] = sio_new_disk; | |
// Go ahead and flush anything out of the serial port | |
sio_flush(); | |
} | |
void loop() | |
{ | |
int a; | |
if (digitalRead(PIN_CMD) == LOW) | |
{ | |
sio_led(true); | |
memset(cmdFrame.cmdFrameData, 0, 5); // clear cmd frame. | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T0); // computer is waiting for us to notice. | |
#endif | |
// read cmd frame | |
SIO_UART.readBytes(cmdFrame.cmdFrameData, 5); | |
#ifdef DEBUG | |
Debug_printf("CF: %02x %02x %02x %02x %02x\n", cmdFrame.devic, cmdFrame.comnd, cmdFrame.aux1, cmdFrame.aux2, cmdFrame.cksum); | |
#endif | |
byte ck = sio_checksum(cmdFrame.cmdFrameData, 4); | |
if (ck == cmdFrame.cksum) | |
{ | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T1); | |
#endif | |
// Wait for CMD line to raise again | |
while (digitalRead(PIN_CMD) == LOW) yield(); | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T2); | |
#endif | |
if (sio_valid_device_id()) | |
{ | |
if (cmdPtr[cmdFrame.comnd] == sio_wait) | |
{ | |
sio_nak(); | |
} | |
else | |
{ | |
sio_ack(); | |
#ifdef ESP8266 | |
delayMicroseconds(DELAY_T3); | |
#endif | |
cmdPtr[cmdFrame.comnd](); | |
} | |
} | |
} | |
else | |
{ | |
command_frame_counter++; | |
if (COMMAND_FRAME_SPEED_CHANGE_THRESHOLD == command_frame_counter) | |
{ | |
command_frame_counter = 0; | |
if (hispeed) | |
{ | |
#ifdef DEBUG | |
Debug_printf("Switching to %d baud...\n", STANDARD_BAUDRATE); | |
#endif | |
SIO_UART.updateBaudRate(STANDARD_BAUDRATE); | |
hispeed = false; | |
} | |
else | |
{ | |
#ifdef DEBUG | |
Debug_printf("Switching to %d baud...\n", HISPEED_BAUDRATE); | |
#endif | |
SIO_UART.updateBaudRate(HISPEED_BAUDRATE); | |
hispeed = true; | |
} | |
} | |
} | |
sio_led(false); | |
} | |
else | |
{ | |
sio_led(false); | |
a = SIO_UART.available(); | |
if (a) | |
while (SIO_UART.available()) | |
SIO_UART.read(); // dump it. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment