more floppy controller stuff

Sun, 05 Dec 2010 10:17:38 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Sun, 05 Dec 2010 10:17:38 +0000
changeset 49
545798274dad
parent 48
d52688dad7fd
child 50
c3ed7639e32d

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