/* * All Input is assumed to be going to RAM * All Output is assumed to be coming from either RAM or ROM * */ #include #include #ifdef VMS #include #include #else #include #ifndef AMIGA #include #endif #endif #ifdef DJGPP #include "djgpp.h" #endif static char *rcsid = "$Id: sio.c,v 1.9 1998/02/21 14:55:21 david Exp $"; #define FALSE 0 #define TRUE 1 #include "atari.h" #include "cpu.h" #include "sio.h" #define MAGIC1 0x96 #define MAGIC2 0x02 #undef ORIGINAL_XFD_CODE /* new code should support DD XFD images - PS */ struct ATR_Header { unsigned char magic1; unsigned char magic2; unsigned char seccountlo; unsigned char seccounthi; unsigned char secsizelo; unsigned char secsizehi; unsigned char hiseccountlo; unsigned char hiseccounthi; unsigned char gash[8]; }; typedef enum Format { XFD, ATR } Format; static Format format[MAX_DRIVES]; static int disk[MAX_DRIVES] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int sectorcount[MAX_DRIVES]; static int sectorsize[MAX_DRIVES]; static enum DriveStatus { Off, NoDisk, ReadOnly, ReadWrite } drive_status[MAX_DRIVES]; char sio_status[256]; char sio_filename[MAX_DRIVES][FILENAME_LEN]; void SIO_Initialise(int *argc, char *argv[]) { int i; for (i = 0; i < MAX_DRIVES; i++) strcpy(sio_filename[i], "Empty"); } int SIO_Mount(int diskno, char *filename) { struct ATR_Header header; int fd; drive_status[diskno - 1] = ReadWrite; strcpy(sio_filename[diskno - 1], "Empty"); fd = open(filename, O_RDWR | O_BINARY); if (fd == -1) { fd = open(filename, O_RDONLY | O_BINARY); drive_status[diskno - 1] = ReadOnly; } if (fd >= 0) { int status; status = read(fd, &header, sizeof(struct ATR_Header)); if (status == -1) { close(fd); disk[diskno - 1] = -1; return FALSE; } strcpy(sio_filename[diskno - 1], filename); if ((header.magic1 == MAGIC1) && (header.magic2 == MAGIC2)) { sectorcount[diskno - 1] = header.hiseccounthi << 24 | header.hiseccountlo << 16 | header.seccounthi << 8 | header.seccountlo; sectorsize[diskno - 1] = header.secsizehi << 8 | header.secsizelo; sectorcount[diskno - 1] /= 8; if (sectorsize[diskno - 1] == 256) { sectorcount[diskno - 1] += 3; /* Compensate for first 3 sectors */ sectorcount[diskno - 1] /= 2; } #ifdef DEBUG printf("ATR: sectorcount = %d, sectorsize = %d\n", sectorcount[diskno - 1], sectorsize[diskno - 1]); #endif format[diskno - 1] = ATR; } else { ULONG file_length = lseek(fd, 0L, SEEK_END); format[diskno - 1] = XFD; #ifndef ORIGINAL_XFD_CODE /* XFD might be of double density ! (PS) */ sectorsize[diskno - 1] = (file_length > (1040 * 128)) ? 256 : 128; sectorcount[diskno - 1] = file_length / sectorsize[diskno - 1]; #endif } } else { drive_status[diskno - 1] = NoDisk; } disk[diskno - 1] = fd; return (disk[diskno - 1] != -1) ? TRUE : FALSE; } void SIO_Dismount(int diskno) { if (disk[diskno - 1] != -1) { close(disk[diskno - 1]); disk[diskno - 1] = -1; drive_status[diskno - 1] = NoDisk; strcpy(sio_filename[diskno - 1], "Empty"); } } void SIO_DisableDrive(int diskno) { drive_status[diskno - 1] = Off; strcpy(sio_filename[diskno - 1], "Off"); } void SeekSector(int dskno, int sector) { int offset; sprintf(sio_status, "%d: %d", dskno + 1, sector); switch (format[dskno]) { case XFD: #ifdef ORIGINAL_XFD_CODE /* does not support double density XFD disks */ offset = (sector - 1) * 128; #else offset = (sector - 1) * sectorsize[dskno] + 0; #endif break; case ATR: if (sector < 4) offset = (sector - 1) * 128 + 16; else offset = (sector - 1) * sectorsize[dskno] + 16; /* offset = 3*128 + (sector-4) * sectorsize[dskno] + 16; */ break; default: printf("Fatal Error in atari_sio.c\n"); Atari800_Exit(FALSE); exit(1); } if (offset > lseek(disk[dskno], 0L, SEEK_END)) { /* printf("Seek after end of file\n"); Atari800_Exit(FALSE); exit(1); */ } else lseek(disk[dskno], offset, SEEK_SET); } void SIO(void) { /* UBYTE DDEVIC = memory[0x0300]; */ UBYTE DUNIT = memory[0x0301]; UBYTE DCOMND = memory[0x0302]; /* UBYTE DSTATS = memory[0x0303]; */ UBYTE DBUFLO = memory[0x0304]; UBYTE DBUFHI = memory[0x0305]; /* UBYTE DTIMLO = memory[0x0306]; */ UBYTE DBYTLO = memory[0x0308]; UBYTE DBYTHI = memory[0x0309]; UBYTE DAUX1 = memory[0x030a]; UBYTE DAUX2 = memory[0x030b]; int sector; int buffer; int count; int i; if (drive_status[DUNIT - 1] != Off) { if (disk[DUNIT - 1] != -1) { int offset; sector = DAUX1 + DAUX2 * 256; buffer = DBUFLO + DBUFHI * 256; count = DBYTLO + DBYTHI * 256; switch (format[DUNIT - 1]) { case XFD: #ifdef ORIGINAL_XFD_CODE offset = (sector - 1) * 128 + 0; #else offset = (sector - 1) * sectorsize[DUNIT - 1] + 0; #endif break; case ATR: if (sector < 4) offset = (sector - 1) * 128 + 16; else offset = (sector - 1) * sectorsize[DUNIT - 1] + 16; /* offset = 3*128 + (sector-4) * sectorsize[DUNIT-1] + 16; */ break; default: printf("Fatal Error in atari_sio.c\n"); Atari800_Exit(FALSE); exit(1); } if (offset > lseek(disk[DUNIT - 1], 0L, SEEK_END)) { /* printf("Seek after end of file\n"); Atari800_Exit(FALSE); exit(1); */ } else lseek(disk[DUNIT - 1], offset, SEEK_SET); #ifdef DEBUG printf("SIO: DCOMND = %x, SECTOR = %d, BUFADR = %x, BUFLEN = %d\n", DCOMND, sector, buffer, count); #endif switch (DCOMND) { case 0x50: case 0x57: if (drive_status[DUNIT - 1] == ReadWrite) { write(disk[DUNIT - 1], &memory[buffer], count); regY = 1; ClrN; } else { regY = 146; SetN; } break; case 0x52: read(disk[DUNIT - 1], &memory[buffer], count); regY = 1; ClrN; break; case 0x21: /* Single Density Format */ case 0x22: /* Duel Density Format */ case 0x66: /* US Doubler Format - I think! */ regY = 1; ClrN; break; /* Status Request from Atari 400/800 Technical Reference Notes DVSTAT + 0 Command Status DVSTAT + 1 Hardware Status DVSTAT + 2 Timeout DVSTAT + 3 Unused Command Status Bits Bit 0 = 1 indicates an invalid command frame was received Bit 1 = 1 indicates an invalid data frame was received Bit 2 = 1 indicates that a PUT operation was unsuccessful Bit 3 = 1 indicates that the diskete is write protected Bit 4 = 1 indicates active/standby plus Bit 5 = 1 indicates double density Bit 7 = 1 indicates duel density disk (1050 format) */ case 0x53: /* Get Status */ for (i = 0; i < count; i++) { if (sectorsize[DUNIT - 1] == 256) memory[buffer + i] = 32 + 16; else memory[buffer + i] = 16; } regY = 1; ClrN; break; default: #ifdef DEBUG printf("SIO: DCOMND = %0x\n", DCOMND); #endif regY = 146; SetN; break; } } else { regY = 146; SetN; } } else { regY = 138; SetN; } memory[0x0303] = regY; } static unsigned char cmd_frame[5]; static int ncmd = 0; static int checksum = 0; static unsigned char data[256]; static int offst; static int buffer_offset; static int buffer_size; extern int DELAYED_SERIN_IRQ; extern int DELAYED_SEROUT_IRQ; extern int DELAYED_XMTDONE_IRQ; typedef enum { SIO_Normal, SIO_Put } SIO_State; static SIO_State sio_state = SIO_Normal; void Command_Frame(void) { sio_state = SIO_Normal; switch (cmd_frame[1]) { case 'R': /* Read */ #ifdef DEBUG printf("Read command\n"); #endif { int sector; int dskno; int i; dskno = cmd_frame[0] - 0x31; sector = cmd_frame[2] + cmd_frame[3] * 256; #ifdef DEBUG printf("Sector: %d(%x)\n", sector, sector); #endif SeekSector(dskno, sector); data[0] = 0x41; /* ACK */ data[1] = 0x43; /* OPERATION COMPLETE */ read(disk[dskno], &data[2], 128); checksum = 0; for (i = 2; i < 130; i++) { checksum += (unsigned char) data[i]; while (checksum > 255) checksum = checksum - 255; } data[130] = checksum; buffer_offset = 0; buffer_size = 131; DELAYED_SEROUT_IRQ = 1; DELAYED_XMTDONE_IRQ = 3; DELAYED_SERIN_IRQ = 150; /* BEFORE 7 */ } break; case 'S': /* Status */ #ifdef DEBUG printf("Status command\n"); #endif data[0] = 0x41; /* ACK */ data[1] = 0x43; /* OPERATION COMPLETE */ data[2] = 0x10; /* 2ea */ data[3] = 0x00; /* 2eb */ data[4] = 0x01; /* 2ec */ data[5] = 0x00; /* 2ed */ data[6] = 0x11; /* Checksum */ buffer_offset = 0; buffer_size = 7; DELAYED_SEROUT_IRQ = 1; DELAYED_XMTDONE_IRQ = 5; DELAYED_SERIN_IRQ = 150; break; case 'W': /* Write with verify */ case 'P': /* Put without verify */ #ifdef DEBUG printf("Put or Write command\n"); #endif data[0] = 0x41; /* ACK */ buffer_offset = 0; buffer_size = 1; DELAYED_SEROUT_IRQ = 1; DELAYED_XMTDONE_IRQ = 3; DELAYED_SERIN_IRQ = 150; /* BEFORE 7 */ sio_state = SIO_Put; break; case '!': /* Format */ #ifdef DEBUG printf("Format command\n"); #endif break; case 'T': /* Read Address */ #ifdef DEBUG printf("Read Address command\n"); #endif break; case 'Q': /* Read Spin */ #ifdef DEBUG printf("Read Spin command\n"); #endif break; case 'U': /* Motor On */ #ifdef DEBUG printf("Motor On command\n"); #endif break; case 'V': /* Verify Sector */ #ifdef DEBUG printf("Verify Sector\n"); #endif break; default: #ifdef DEBUG printf("Unknown command: %02x\n", cmd_frame[1]); printf("Command frame: %02x %02x %02x %02x %02x\n", cmd_frame[0], cmd_frame[1], cmd_frame[2], cmd_frame[3], cmd_frame[4]); #endif buffer_offset = 0; buffer_size = 0; DELAYED_XMTDONE_IRQ = 3; break; } } void SIO_SEROUT(unsigned char byte, int cmd) { checksum += (unsigned char) byte; while (checksum > 255) checksum = checksum - 255; #ifdef DEBUG printf("SIO_SEROUT: byte = %x, checksum = %x, cmd = %d\n", byte, checksum, cmd); #endif if (cmd) { cmd_frame[ncmd++] = byte; if (ncmd == 5) { Command_Frame(); offst = 0; checksum = 0; ncmd = 0; } else { DELAYED_SEROUT_IRQ = 1; } if (cmd_frame[0] == 0) ncmd = 0; } else if (sio_state == SIO_Put) { data[buffer_offset++] = byte; if (buffer_offset == 130) { int sector; int dskno; int i; checksum = 0; for (i = 1; i < 129; i++) { checksum += (unsigned char) data[i]; while (checksum > 255) checksum = checksum - 255; } if (checksum != data[129]) { printf("Direct SIO Write Error\n"); printf("Calculated Checksum = %x\n", checksum); printf("Actual Checksum = %x\n", data[129]); exit(1); } dskno = cmd_frame[0] - 0x31; sector = cmd_frame[2] + cmd_frame[3] * 256; #ifdef DEBUG printf("Sector: %d(%x)\n", sector, sector); #endif SeekSector(dskno, sector); write(disk[dskno], &data[1], 128); data[buffer_offset] = 0x41; /* ACK */ data[buffer_offset + 1] = 0x43; /* OPERATION COMPLETE */ buffer_size = buffer_offset + 2; DELAYED_SEROUT_IRQ = 1; DELAYED_XMTDONE_IRQ = 3; DELAYED_SERIN_IRQ = 7; DELAYED_XMTDONE_IRQ = 5; DELAYED_SERIN_IRQ = 150; } else { DELAYED_SEROUT_IRQ = 4; } } else { DELAYED_SEROUT_IRQ = 1; ncmd = 0; } } int SIO_SERIN(void) { int byte = 0; /* initialise the value, just for sure */ if (buffer_offset < buffer_size) { byte = (int) data[buffer_offset++]; #ifdef DEBUG printf("SERIN: byte = %x\n", byte); #endif if (buffer_offset < buffer_size) { #ifdef DEBUG printf("Setting SERIN Interrupt again\n"); #endif DELAYED_SERIN_IRQ = 3; DELAYED_SERIN_IRQ = 4; } } return byte; }