Sun, 05 Dec 2010 10:17:38 +0000
more floppy controller stuff
src/wd279x.c | file | annotate | diff | revisions | |
src/wd279x.h | file | annotate | diff | revisions |
1.1 --- a/src/wd279x.c Sun Dec 05 03:55:46 2010 +0000 1.2 +++ b/src/wd279x.c Sun Dec 05 10:17:38 2010 +0000 1.3 @@ -1,8 +1,8 @@ 1.4 #include <stdint.h> 1.5 #include <stdbool.h> 1.6 +#include <malloc.h> 1.7 #include "wd279x.h" 1.8 1.9 - 1.10 /// WD2797 command constants 1.11 enum { 1.12 CMD_MASK = 0xF0, ///< Bit mask to detect command bits 1.13 @@ -25,34 +25,69 @@ 1.14 }; 1.15 1.16 1.17 -/** 1.18 - * @brief Initialise a WD2797 context. 1.19 - */ 1.20 -void wd2797_init(WD279X_CTX *ctx) 1.21 +void wd2797_init(WD2797_CTX *ctx) 1.22 { 1.23 // track, head and sector unknown 1.24 - ctx->track = 0; 1.25 - ctx->head = 0; 1.26 - ctx->sector = 0; 1.27 + ctx->track = ctx->head = ctx->sector = 0; 1.28 1.29 - // no IRQ or DRQ, no IRQ callback 1.30 - ctx->irql = false; 1.31 - ctx->irqe = false; 1.32 + // no IRQ pending 1.33 + ctx->irql = ctx->irqe = false; 1.34 1.35 // no data available 1.36 - ctx->data_pos = 0; 1.37 - ctx->data_len = 0; 1.38 + ctx->data_pos = ctx->data_len = 0; 1.39 + ctx->data = NULL; 1.40 + 1.41 + // Status register clear, not busy 1.42 + ctx->status = 0; 1.43 + 1.44 + // Clear data register 1.45 + ctx->data_reg = 0; 1.46 + 1.47 + // Last step direction 1.48 + ctx->last_step_dir = -1; 1.49 1.50 // No disc image loaded 1.51 ctx->disc_image = NULL; 1.52 + ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0; 1.53 } 1.54 1.55 1.56 -/** 1.57 - * @brief Read IRQ Rising Edge status. Clears Rising Edge status if it is set. 1.58 - * @note No more IRQs will be sent until the Status Register is read, or a new command is written to the CR. 1.59 - */ 1.60 -bool wd279x_get_irq(WD279X_CTX *ctx) 1.61 +void wd2797_reset(WD2797_CTX *ctx) 1.62 +{ 1.63 + // track, head and sector unknown 1.64 + ctx->track = ctx->head = ctx->sector = 0; 1.65 + 1.66 + // no IRQ pending 1.67 + ctx->irql = ctx->irqe = false; 1.68 + 1.69 + // no data available 1.70 + ctx->data_pos = ctx->data_len = 0; 1.71 + 1.72 + // Status register clear, not busy 1.73 + ctx->status = 0; 1.74 + 1.75 + // Clear data register 1.76 + ctx->data_reg = 0; 1.77 + 1.78 + // Last step direction 1.79 + ctx->last_step_dir = -1; 1.80 +} 1.81 + 1.82 + 1.83 +void wd2797_done(WD2797_CTX *ctx) 1.84 +{ 1.85 + // Reset the WD2797 1.86 + wd2797_reset(ctx); 1.87 + 1.88 + // Free any allocated memory 1.89 + if (ctx->data) { 1.90 + free(ctx->data); 1.91 + ctx->data = NULL; 1.92 + } 1.93 +} 1.94 + 1.95 + 1.96 +bool wd2797_get_irq(WD2797_CTX *ctx) 1.97 { 1.98 // If an IRQ is pending, clear it and return true, otherwise return false 1.99 if (ctx->irqe) { 1.100 @@ -64,24 +99,69 @@ 1.101 } 1.102 1.103 1.104 -/** 1.105 - * @brief Read DRQ status. 1.106 - */ 1.107 -bool wd279x_get_drq(WD279X_CTX *ctx) 1.108 +bool wd2797_get_drq(WD2797_CTX *ctx) 1.109 { 1.110 return (ctx->data_pos < ctx->data_len); 1.111 } 1.112 1.113 1.114 -/** 1.115 - * @brief Read WD279x register. 1.116 - */ 1.117 -uint8_t wd279x_read_reg(WD279X_CTX *ctx, uint8_t addr) 1.118 +WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads) 1.119 +{ 1.120 + size_t filesize; 1.121 + 1.122 + // Start by finding out how big the image file is 1.123 + fseek(fp, 0, SEEK_END); 1.124 + filesize = ftell(fp); 1.125 + fseek(fp, 0, SEEK_SET); 1.126 + 1.127 + // Now figure out how many tracks it contains 1.128 + int tracks = filesize / secsz / spt / heads; 1.129 + // Confirm... 1.130 + if (tracks < 1) { 1.131 + return WD2797_ERR_BAD_GEOM; 1.132 + } 1.133 + 1.134 + // Allocate enough memory to store one disc track 1.135 + if (ctx->data) { 1.136 + free(ctx->data); 1.137 + } 1.138 + ctx->data = malloc(secsz * spt); 1.139 + if (!ctx->data) 1.140 + return WD2797_ERR_NO_MEMORY; 1.141 + 1.142 + // Load the image and the geometry data 1.143 + ctx->disc_image = fp; 1.144 + ctx->geom_tracks = tracks; 1.145 + ctx->geom_secsz = secsz; 1.146 + ctx->geom_heads = heads; 1.147 + ctx->geom_spt = spt; 1.148 + 1.149 + return WD2797_ERR_OK; 1.150 +} 1.151 + 1.152 + 1.153 +void wd2797_unload(WD2797_CTX *ctx) 1.154 +{ 1.155 + // Free memory buffer 1.156 + if (ctx->data) { 1.157 + free(ctx->data); 1.158 + ctx->data = NULL; 1.159 + } 1.160 + 1.161 + // Clear file pointer 1.162 + ctx->disc_image = NULL; 1.163 + 1.164 + // Clear the disc geometry 1.165 + ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0; 1.166 +} 1.167 + 1.168 + 1.169 +uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr) 1.170 { 1.171 uint8_t temp = 0; 1.172 1.173 switch (addr & 0x03) { 1.174 - case WD279X_REG_STATUS: // Status register 1.175 + case WD2797_REG_STATUS: // Status register 1.176 // Read from status register clears IRQ 1.177 ctx->irql = false; 1.178 1.179 @@ -92,16 +172,16 @@ 1.180 temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; 1.181 // FDC is busy if there is still data in the buffer 1.182 temp |= (ctx->data_pos < ctx->data_len) ? 0x01 : 0x00; // if data in buffer, then DMA hasn't copied it yet, and we're still busy! 1.183 - // TODO: also if seek delay / read delay hasn't passed (but that's for later) 1.184 + // TODO: also if seek delay / read delay hasn't passed (but that's for later) 1.185 return temp; 1.186 1.187 - case WD279X_REG_TRACK: // Track register 1.188 + case WD2797_REG_TRACK: // Track register 1.189 return ctx->track; 1.190 1.191 - case WD279X_REG_SECTOR: // Sector register 1.192 + case WD2797_REG_SECTOR: // Sector register 1.193 return ctx->sector; 1.194 1.195 - case WD279X_REG_DATA: // Data register 1.196 + case WD2797_REG_DATA: // Data register 1.197 // If there's data in the buffer, return it. Otherwise return 0xFF. 1.198 if (ctx->data_pos < ctx->data_len) { 1.199 // return data byte and increment pointer 1.200 @@ -118,17 +198,15 @@ 1.201 } 1.202 1.203 1.204 -/** 1.205 - * Write WD279X register 1.206 - */ 1.207 -void wd279x_write_reg(WD279X_CTX *ctx, uint8_t addr, uint8_t val) 1.208 +void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val) 1.209 { 1.210 uint8_t cmd = val & CMD_MASK; 1.211 size_t lba; 1.212 bool is_type1 = false; 1.213 + int temp; 1.214 1.215 switch (addr) { 1.216 - case WD279X_REG_COMMAND: // Command register 1.217 + case WD2797_REG_COMMAND: // Command register 1.218 // write to command register clears interrupt request 1.219 ctx->irql = false; 1.220 1.221 @@ -284,20 +362,24 @@ 1.222 case CMD_READ_SECTOR: 1.223 case CMD_READ_SECTOR_MULTI: 1.224 // Read Sector or Read Sector Multiple 1.225 - // TODO: multiple is not implemented! 1.226 - if (cmd == CMD_READ_SECTOR_MULTI) printf("WD279X: NOTIMP: read-multi\n"); 1.227 - 1.228 // reset data pointers 1.229 ctx->data_pos = ctx->data_len = 0; 1.230 1.231 - // Calculate the LBA address of the required sector 1.232 - lba = ((((ctx->track * ctx->geom_heads) + ctx->head) * ctx->geom_spt) + ctx->sector - 1) * ctx->geom_secsz; 1.233 + // Calculate number of sectors to read from disc 1.234 + if (cmd == CMD_READ_SECTOR_MULTI) 1.235 + temp = ctx->geom_spt; 1.236 + else 1.237 + temp = 1; 1.238 1.239 - // Read the sector from the file 1.240 - fseek(ctx->disc_image, lba, SEEK_SET); 1.241 - ctx->data_len = fread(ctx->data, 1, ctx->geom_secsz, ctx->disc_image); 1.242 - // TODO: if datalen < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr) 1.243 - // TODO: if we're asked to do a Read Multi, malloc for an entire track, then just read a full track... 1.244 + for (int i=0; i<temp; i++) { 1.245 + // Calculate the LBA address of the required sector 1.246 + lba = ((((ctx->track * ctx->geom_heads) + ctx->head) * ctx->geom_spt) + ctx->sector - 1) * ctx->geom_secsz; 1.247 + 1.248 + // Read the sector from the file 1.249 + fseek(ctx->disc_image, lba, SEEK_SET); 1.250 + ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image); 1.251 + // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr) 1.252 + } 1.253 1.254 // B6 = 0 1.255 // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks. 1.256 @@ -310,6 +392,7 @@ 1.257 1.258 case CMD_READ_TRACK: 1.259 // Read Track 1.260 + // TODO! implement this 1.261 // B6, B5, B4, B3 = 0 1.262 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated 1.263 // B1 = DRQ. Data request. 1.264 @@ -345,21 +428,25 @@ 1.265 1.266 case CMD_FORCE_INTERRUPT: 1.267 // Force Interrupt... 1.268 - // TODO: terminate current R/W op 1.269 - // TODO: variants for this command 1.270 + // Terminates current operation and sends an interrupt 1.271 + // TODO! 1.272 + ctx->data_pos = ctx->data_len = 0; 1.273 + // Set IRQ only if IRQL has been cleared (no pending IRQs) 1.274 + ctx->irqe = !ctx->irql; 1.275 + ctx->irql = true; 1.276 break; 1.277 } 1.278 break; 1.279 1.280 - case WD279X_REG_TRACK: // Track register 1.281 + case WD2797_REG_TRACK: // Track register 1.282 ctx->track = val; 1.283 break; 1.284 1.285 - case WD279X_REG_SECTOR: // Sector register 1.286 + case WD2797_REG_SECTOR: // Sector register 1.287 ctx->sector = val; 1.288 break; 1.289 1.290 - case WD279X_REG_DATA: // Data register 1.291 + case WD2797_REG_DATA: // Data register 1.292 // Save the value written into the data register 1.293 ctx->data_reg = val; 1.294
2.1 --- a/src/wd279x.h Sun Dec 05 03:55:46 2010 +0000 2.2 +++ b/src/wd279x.h Sun Dec 05 10:17:38 2010 +0000 2.3 @@ -6,13 +6,21 @@ 2.4 #include <stdint.h> 2.5 #include <stdio.h> 2.6 2.7 -enum { 2.8 - WD279X_REG_STATUS = 0, 2.9 - WD279X_REG_COMMAND = 0, 2.10 - WD279X_REG_TRACK = 1, 2.11 - WD279X_REG_SECTOR = 2, 2.12 - WD279X_REG_DATA = 3 2.13 -} WD279X_REG; 2.14 +/// WD279x registers 2.15 +typedef enum { 2.16 + WD2797_REG_STATUS = 0, ///< Status register 2.17 + WD2797_REG_COMMAND = 0, ///< Command register 2.18 + WD2797_REG_TRACK = 1, ///< Track register 2.19 + WD2797_REG_SECTOR = 2, ///< Sector register 2.20 + WD2797_REG_DATA = 3 ///< Data register 2.21 +} WD2797_REG; 2.22 + 2.23 +/// WD279x emulator error codes 2.24 +typedef enum { 2.25 + WD2797_ERR_OK = 0, ///< Operation succeeded 2.26 + WD2797_ERR_BAD_GEOM = -1, ///< Bad geometry, or image file too small 2.27 + WD2797_ERR_NO_MEMORY = -2 ///< Out of memory 2.28 +} WD2797_ERR; 2.29 2.30 typedef struct { 2.31 // Current track, head and sector 2.32 @@ -33,10 +41,78 @@ 2.33 // Last step direction. -1 for "towards zero", 1 for "away from zero" 2.34 int last_step_dir; 2.35 // Data buffer, current DRQ pointer and length 2.36 - uint8_t data[1024]; 2.37 + uint8_t *data; 2.38 size_t data_pos, data_len; 2.39 // Current disc image file 2.40 FILE *disc_image; 2.41 -} WD279X_CTX; 2.42 +} WD2797_CTX; 2.43 + 2.44 +/** 2.45 + * @brief Initialise a WD2797 context. 2.46 + * @param ctx WD2797 context. 2.47 + * 2.48 + * This must be run once when the context is created. 2.49 + */ 2.50 +void wd2797_init(WD2797_CTX *ctx); 2.51 + 2.52 +/** 2.53 + * @brief Reset a WD2797 context. 2.54 + * @param ctx WD2797 context. 2.55 + * 2.56 + * This should be run if the WD2797 needs to be reset (nRST line toggled). 2.57 + */ 2.58 +void wd2797_reset(WD2797_CTX *ctx); 2.59 + 2.60 +/** 2.61 + * Deinitialise a WD2797 context. 2.62 + * @param ctx WD2797 context. 2.63 + */ 2.64 +void wd2797_done(WD2797_CTX *ctx); 2.65 + 2.66 +/** 2.67 + * @brief Read IRQ Rising Edge status. Clears Rising Edge status if it is set. 2.68 + * @note No more IRQs will be sent until the Status Register is read, or a new command is written to the CR. 2.69 + * @param ctx WD2797 context. 2.70 + */ 2.71 +bool wd2797_get_irq(WD2797_CTX *ctx); 2.72 + 2.73 +/** 2.74 + * @brief Read DRQ status. 2.75 + * @param ctx WD2797 context. 2.76 + */ 2.77 +bool wd2797_get_drq(WD2797_CTX *ctx); 2.78 + 2.79 +/** 2.80 + * @brief Assign a disc image to the WD2797. 2.81 + * @param ctx WD2797 context. 2.82 + * @param fp Disc image file, already opened in "r+b" mode. 2.83 + * @param secsz Sector size: either 128, 256, 512 or 1024. 2.84 + * @param spt Sectors per track. 2.85 + * @param heads Number of heads (1 or 2). 2.86 + * @return Error code; WD279X_E_OK if everything worked OK. 2.87 + */ 2.88 +WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads); 2.89 + 2.90 +/** 2.91 + * @brief Deassign the current image file. 2.92 + * @param ctx WD2797 context. 2.93 + */ 2.94 +void wd2797_unload(WD2797_CTX *ctx); 2.95 + 2.96 +/** 2.97 + * @brief Read WD279x register. 2.98 + * @param ctx WD2797 context 2.99 + * @param addr Register address (0, 1, 2 or 3) 2.100 + */ 2.101 +uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr); 2.102 + 2.103 +/** 2.104 + * @brief Write WD279X register 2.105 + * @param ctx WD2797 context 2.106 + * @param addr Register address (0, 1, 2 or 3) 2.107 + * @param val Value to write 2.108 + */ 2.109 +void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val); 2.110 + 2.111 2.112 #endif