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 --- 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