summary refs log tree commit diff
path: root/src/drive.c
diff options
context:
space:
mode:
authorMaxwell Beck <max@rastertail.net>2024-12-24 10:54:02 -0600
committerMaxwell Beck <max@rastertail.net>2024-12-24 10:54:02 -0600
commitc684c194bccdb08df75423680ba334739a945fe5 (patch)
treeea052b7f413936629421fbb784aaea1dedb8133c /src/drive.c
Initial commit
Diffstat (limited to 'src/drive.c')
-rw-r--r--src/drive.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/drive.c b/src/drive.c
new file mode 100644
index 0000000..7afeaec
--- /dev/null
+++ b/src/drive.c
@@ -0,0 +1,222 @@
+#include "drive.h"
+
+#include <stdlib.h>
+
+const uint8_t SECTORS_PER_TRACK[40] = {
+	21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+	19, 19, 19, 19, 19, 19, 19,
+	18, 18, 18, 18, 18, 18,
+	17, 17, 17, 17, 17, 17, 17, 17, 17, 17
+};
+
+const uint8_t BITRATE_PER_TRACK[40] = {
+	80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+	73, 73, 73, 73, 73, 73, 73,
+	65, 65, 65, 65, 65, 65,
+	62 ,62, 62, 62, 62, 62, 62, 62, 62, 62
+};
+
+const uint8_t GCR_CONV[16] = {
+    0x0a, 0x0b, 0x12, 0x13,
+    0x0e, 0x0f, 0x16, 0x17,
+    0x09, 0x19, 0x1a, 0x1b,
+    0x0d, 0x1d, 0x1e, 0x15
+};
+
+void drive_init(drive_t *drive) {
+	drive->spinning = false;
+	drive->timer = 0;
+	drive->image = NULL;
+
+	drive->track = 17;
+	drive->halftrack = 0;
+	drive->image_ptr = 0x16500;
+	drive->sector = 0;
+	drive->byte = 0;
+
+	drive->gcr_word = 0;
+	drive->gcr_bits = 0;
+	drive->bit_counter = 0;
+
+	drive->gap_remain = 0;
+	drive->sync_remain = 4;
+	drive->header_remain = 8;
+	drive->data_remain = 0;
+	drive->data_checksum = 0;
+
+	drive->current_idx = 0;
+	drive->lookahead_idx = 0;
+
+	drive_cycle(drive, 0, false);
+}
+
+void drive_step_up(drive_t *drive) {
+	drive->halftrack = (drive->halftrack + 1) & 1;
+	if (drive->track < 39 && drive->halftrack == 0) {
+		drive->image_ptr += SECTORS_PER_TRACK[drive->track] << 8;
+		drive->track += 1;
+		drive->sector = drive->sector % SECTORS_PER_TRACK[drive->track];
+	}
+}
+
+void drive_step_down(drive_t *drive) {
+	drive->halftrack = (drive->halftrack + 1) & 1;
+	if (drive->track > 0 && drive->halftrack == 0) {
+		drive->image_ptr -= SECTORS_PER_TRACK[drive->track - 1] << 8;
+		drive->track -= 1;
+		drive->sector = drive->sector % SECTORS_PER_TRACK[drive->track];
+	}
+}
+
+bool drive_cycle(drive_t *drive, uint8_t cycles, bool advance) {
+	bool byte_ready = false;
+	if (advance) {
+		for (uint8_t i = 0; i < cycles; i++) {
+			uint8_t j = (i + drive->current_idx) & (DRIVE_LOOKAHEAD - 1);
+			byte_ready |= drive->byte_ready[j];
+		}
+		drive->current_idx = (drive->current_idx + cycles) & (DRIVE_LOOKAHEAD - 1);
+	}
+
+	while (drive->lookahead_idx != ((drive->current_idx - 1) & (DRIVE_LOOKAHEAD - 1))) {
+		uint8_t j = (drive->lookahead_idx + 1) & (DRIVE_LOOKAHEAD - 1);
+		uint8_t timer_next = drive->timer + BITRATE_PER_TRACK[drive->track];
+
+		uint16_t prev = drive->unlatched[drive->lookahead_idx];
+		if (timer_next < drive->timer) {
+			if (drive->gcr_bits == 0) {
+				/*
+				if (drive->spinning && drive->image != NULL) {
+					if (!drive->sync_flag && (drive->byte == 0 || drive->byte == 2)) {
+						drive->gcr_word = 0x3ff;
+						drive->sync_flag = true;
+					} else {
+						uint8_t b = drive->image[drive->image_ptr + (drive->sector << 8) + drive->byte];
+						drive->gcr_word = (GCR_CONV[(b & 0xf0) >> 4] << 5) | GCR_CONV[b & 0x0f];
+						drive->sync_flag = false;
+
+						uint8_t byte_next = drive->byte + 1;
+						uint8_t sector_next = drive->sector;
+						if (byte_next == 0) {
+							sector_next += 1;
+						}
+						if (sector_next >= SECTORS_PER_TRACK[drive->track]) {
+							sector_next = 0;
+						}
+						drive->byte = byte_next;
+						drive->sector = sector_next;
+					}
+				}
+
+				drive->gcr_bits = 10;
+				*/
+				if (drive->gap_remain > 0) {
+					drive->gap_remain--;
+					drive->gcr_word = 0x55;
+					drive->gcr_bits = 8;
+				} else if (drive->sync_remain > 0) {
+					drive->sync_remain--;
+					drive->gcr_word = 0x3ff;
+					drive->gcr_bits = 10;
+				} else if (drive->header_remain > 0) {
+					drive->header_remain--;
+					uint8_t track_plus_one = drive->track + 1;
+					uint8_t checksum = drive->sector ^ track_plus_one;
+					switch (drive->header_remain) {
+					case 7:
+						drive->gcr_word = (GCR_CONV[0x0] << 5) | GCR_CONV[0x8];
+						break;
+					case 6:
+						drive->gcr_word = (GCR_CONV[(checksum & 0xf0) >> 4] << 5) | GCR_CONV[checksum & 0x0f];
+						break;
+					case 5:
+						drive->gcr_word = (GCR_CONV[(drive->sector & 0xf0) >> 4] << 5) | GCR_CONV[drive->sector & 0x0f];
+						break;
+					case 4:
+						drive->gcr_word = (GCR_CONV[(track_plus_one & 0xf0) >> 4] << 5) | GCR_CONV[track_plus_one & 0x0f];
+						break;
+					case 3:
+					case 2:
+						drive->gcr_word = (GCR_CONV[0x0] << 5) | GCR_CONV[0x0];
+						break;
+					case 1:
+					case 0:
+						drive->gcr_word = (GCR_CONV[0x0] << 5) | GCR_CONV[0xf];
+						break;
+					}
+					if (drive->header_remain == 0) {
+						drive->gap_remain = 9;
+						drive->sync_remain = 4;
+						drive->data_remain = 260;
+						drive->data_checksum = 0;
+					}
+					drive->gcr_bits = 10;
+				} else if (drive->data_remain > 0) {
+					drive->data_remain--;
+					switch (drive->data_remain) {
+					case 259:
+						drive->gcr_word = (GCR_CONV[0x0] << 5) | GCR_CONV[0x7];
+						break;
+					case 2:
+						drive->gcr_word = (GCR_CONV[(drive->data_checksum & 0xf0) >> 4] << 5) | GCR_CONV[drive->data_checksum & 0x0f];
+						break;
+					case 1:
+					case 0:
+						drive->gcr_word = (GCR_CONV[0x0] << 5) | GCR_CONV[0xf];
+						break;
+					default:
+						{
+							uint8_t b = drive->image[drive->image_ptr + (drive->sector << 8) + drive->byte];
+							drive->gcr_word = (GCR_CONV[(b & 0xf0) >> 4] << 5) | GCR_CONV[b & 0x0f];
+							drive->data_checksum ^= b;
+
+							uint8_t byte_next = drive->byte + 1;
+							uint8_t sector_next = drive->sector;
+							if (byte_next == 0) {
+								sector_next += 1;
+							}
+							if (sector_next >= SECTORS_PER_TRACK[drive->track]) {
+								sector_next = 0;
+							}
+							drive->byte = byte_next;
+							drive->sector = sector_next;
+						}
+						break;
+					}
+					if (drive->data_remain == 0) {
+						drive->gap_remain = 10;
+						drive->sync_remain = 4;
+						drive->header_remain = 8;
+					}
+					drive->gcr_bits = 10;
+				}
+			}
+
+			drive->unlatched[j] = ((prev << 1) | ((drive->gcr_word & 0x200) >> 9)) & 0x3ff;
+			drive->gcr_word <<= 1;
+			drive->gcr_bits--;
+			drive->bit_counter += 1;
+		} else {
+			drive->unlatched[j] = prev;
+		}
+
+		drive->byte_ready[j] = false;
+		drive->latched[j] = drive->latched[drive->lookahead_idx];
+		if ((drive->unlatched[j] & 0x3ff) == 0x3ff) {
+			drive->bit_counter = 0;
+			drive->sync[j] = true;
+		} else {
+			if (drive->bit_counter == 8) {
+				drive->bit_counter = 0;
+				drive->byte_ready[j] = true;
+				drive->latched[j] = drive->unlatched[j] & 0xff;
+			}
+			drive->sync[j] = false;
+		}
+
+		drive->lookahead_idx = j;
+		drive->timer = timer_next;
+	}
+
+	return byte_ready;
+}