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