Jump to content
Sign in to follow this  
insomnia

direct floppy access without using the DSR

Recommended Posts

So I'm trying to write a tiny operating system for the TI, and I'd like to avoid as much of TI's original design decisions as possible. This is mostly for the challenge of it and to see if whatever I can come up with is better then what the original engineers made. This would also let me make maximum use of the limited hardware resources.

My most basic problem is that I need to do my development on an emulator since my actual hardware is packed away at the moment. So my first question is if there are any Ti99/4a emulators which permit direct access to the FD1771 floppy controller. I'm pretty sure Classic99 does not do this, but I think MESS does.

I tried writing a really basic driver for the FD1771 and ran it in MESS. The problem I had there was that any attempt to read from the disk failed. I was able to get a lot of functions working properly, but when I tried to read a sector, I only got the first byte. After that I got "disk not ready" errors.

 

I've been using Thierry Nouspikel's code as a starting point, along with dumps of the floppy controller firmware and the FD1771 datasheet.

 

That's a bunch of text. Here's the short version:

1) Does anyone know of an emulator that emulates the floppy controller hardware?

2) Has anyone come across code which directly interfaces with the floppy controller, bypassing the DSR?

 

 

I've included the code I'm using under the spoiler tag.

 

 

 

// This is a driver for the FD1771 single-density floppy drive controller used
// in the TI floppy drive
// Double-density CorComp drives use a FD179x controller


#include "kernel/cru.h"
#include "kernel/printk.h"
#include "errno.h"


#define FDC_REG_R_STATUS *((volatile unsigned char*)0x5ff0)
#define FDC_REG_R_TRACK *((volatile unsigned char*)0x5ff2)
#define FDC_REG_R_SECTOR *((volatile unsigned char*)0x5ff4)
#define FDC_REG_R_DATA *((volatile unsigned char*)0x5ff6)
#define FDC_REG_W_COMMAND *((volatile unsigned char*)0x5ff8)
#define FDC_REG_W_TRACK *((volatile unsigned char*)0x5ffa)
#define FDC_REG_W_SECTOR *((volatile unsigned char*)0x5ffc)
#define FDC_REG_W_DATA *((volatile unsigned char*)0x5ffe)


#define FDC_CRU_R_LOAD_HEAD 0x1100
#define FDC_CRU_R_DRIVE_SEL 0x1102
#define FDC_CRU_R_DRIVE_SEL1 0x1102
#define FDC_CRU_R_DRIVE_SEL2 0x1104
#define FDC_CRU_R_DRIVE_SEL3 0x1106
#define FDC_CRU_R_MOTOR_STROBE 0x1108
// constant zero 0x110a
// constant one 0x110c
#define FDC_CRU_R_SIDE_SEL 0x110e


#define FDC_CRU_W_DRIVE_ENABLE 0x1100
#define FDC_CRU_W_MOTOR_STROBE 0x1102
#define FDC_CRU_W_WAIT_STATE 0x1104
#define FDC_CRU_W_LOAD_HEAD 0x1106
#define FDC_CRU_W_DRIVE_SEL 0x1108
#define FDC_CRU_W_DRIVE_SEL1 0x1108
#define FDC_CRU_W_DRIVE_SEL2 0x110a
#define FDC_CRU_W_DRIVE_SEL3 0x110c
#define FDC_CRU_W_SIDE_SEL 0x110e


#define FDC_CMD_FORCE_IRQ 0xd0
#define FDC_CMD_SEEK_ZERO 0x0a
#define FDC_CMD_SEEK_PREV 0x7a
#define FDC_CMD_SEEK_NEXT 0x5a
#define FDC_CMD_SEEK_TRACK 0x1e
#define FDC_CMD_READ_SECTOR 0x88
#define FDC_CMD_WRITE_SECTOR 0xa8
#define FDC_CMD_READ_ID  0xc0


#define FDC_STAT_BUSY 0x01
#define FDC_STAT_DRQ 0x02
#define FDC_STAT_DATA_LOST 0x04
#define FDC_STAT_TRACK_0 0x04
#define FDC_STAT_CRC_ERROR 0x08
#define FDC_STAT_NOT_FOUND 0x10
#define FDC_STAT_W_FAULT 0x20
#define FDC_STAT_W_PROTECT 0x40
#define FDC_STAT_NOT_READY 0x80


static int sector_size = 256;
static int curr_track = 0;
static int curr_drive = 0;


void command_wait(void);


void strobe_motor(void)
{
  SBZ(FDC_CRU_W_MOTOR_STROBE);
  SBO(FDC_CRU_W_MOTOR_STROBE);
}


void send_command(int command)
{
  char stat = ~FDC_REG_R_STATUS;
  if(stat & FDC_STAT_NOT_READY)
  {
    strobe_motor();
  }

  // Wait for drive to be ready
  while(stat & FDC_STAT_NOT_READY)
  {
    // Note: inverted data bus
    stat = ~FDC_REG_R_STATUS;
  }

  // Send command
  FDC_REG_W_COMMAND = ~command;

  // Signal HLT pin to allow read or write operations
  SBO(FDC_CRU_W_LOAD_HEAD);
}


int select_drive(int drive)
{
  volatile unsigned int select = 0x0080 << drive;
  volatile unsigned int val;

  // Deselect all drives, set SEL1, SEL2, SEL3 to zero
  LDCR(FDC_CRU_W_DRIVE_SEL, 0, 3);

  // Select our drive
  LDCR(FDC_CRU_W_DRIVE_SEL, select, 3);

  // Confirm drive selection
  STCR(FDC_CRU_R_DRIVE_SEL, val, 3);
  if(val != select)
  {
    return(ENXIO);
  }

  // Turn on controller
  SBO(FDC_CRU_W_DRIVE_ENABLE);
  send_command(FDC_CMD_FORCE_IRQ);
  command_wait();

  // Drive successfully selected
  curr_drive = drive;
  return(0);
}


void select_side(int side)
{
  LDCR(FDC_CRU_W_SIDE_SEL, side << 8, 1);
}


// Wait for command completion
void command_wait(void)
{
  unsigned char stat = FDC_STAT_BUSY;
  while((stat & (FDC_STAT_NOT_READY | FDC_STAT_BUSY)) == FDC_STAT_BUSY)
  {
    // Note: inverted data bus
    stat = ~(FDC_REG_R_STATUS);
  }
  if(stat & FDC_STAT_NOT_READY)
  {
    // Drive no longer ready, command failed
    send_command(FDC_CMD_FORCE_IRQ);
    command_wait();
  }
}


void initialize(void)
{
  // Turn on FDC
  SBO(FDC_CRU_W_DRIVE_ENABLE);

  // Clear SEL1, SEL2, SEL3 and SIDE lines
  LDCR(FDC_CRU_W_DRIVE_SEL, 0, 4);

  // Strobe motor
  strobe_motor();

  // Send "force interrupt" command
  send_command(FDC_CMD_FORCE_IRQ);

  command_wait();
}


void seek_track_0(void)
{
  send_command(FDC_CMD_SEEK_ZERO);
  command_wait();
  char stat = ~FDC_REG_R_STATUS;
  if((stat & FDC_STAT_TRACK_0) == 0)
  {
    printk("failed to seek\n");
    // Failed to seek
  }
  curr_track = 0;
}


void seek_track_prev(void)
{
  send_command(FDC_CMD_SEEK_PREV);
  command_wait();
  curr_track--;
}


void seek_track_next(void)
{
  send_command(FDC_CMD_SEEK_NEXT);
  command_wait();
  curr_track++;
}


void seek_track(char track)
{
  FDC_REG_W_DATA = ~track;
  // Only needed if we changed drives
  // FDC_REG_W_TRACK = ~curr_track;
  send_command(FDC_CMD_SEEK_TRACK);
  command_wait();
  curr_track = track;
}


// Hard coded to read sector 0 for testing
void read_sector(int sector, char *buffer)
{
  SBO(FDC_CRU_W_LOAD_HEAD);
  command_wait();

  FDC_REG_W_TRACK = ~0;
  FDC_REG_W_SECTOR = ~0;
  send_command(FDC_CMD_READ_SECTOR);

  SBO(FDC_CRU_W_WAIT_STATE); // Enable wait states

  int size = 256; //sector_size;
  do
  {
    // Unroll read loop
    *buffer++ = ~FDC_REG_R_DATA;
    *buffer++ = ~FDC_REG_R_DATA;
    size-=2;
  } while(size>0);

  SBZ(FDC_CRU_W_WAIT_STATE); // Disable wait states
  command_wait();
}


void write_sector(int sector, char *buffer)
{
  FDC_REG_W_TRACK = ~curr_track;
  FDC_REG_W_SECTOR = ~sector;
  send_command(FDC_CMD_WRITE_SECTOR);

  SBO(FDC_CRU_W_WAIT_STATE); // Enable wait states
  int size = sector_size;
  do
  {
    // Unroll write loop
    FDC_REG_W_DATA = ~*buffer++;
    FDC_REG_W_DATA = ~*buffer++;
    size -= 2;
  } while(size);

  SBZ(FDC_CRU_W_WAIT_STATE); // Disable wait states
  command_wait();
  // test "not found" "data lost" and "write protect" bits
}


int tifdc_init(void)
{
   // Turn on fdc rom
}


struct device_operations ti_fdc_ops =
{
  .read_block = &tifdc_read_block,
  .write_block = &tifdc_write_block,
  .init = &tifdc_init
};

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

If you're getting a Disk not ready, something in your code is not correct. The FD1771 emulation in MAME should be pretty reliable. You are using a sufficiently recent version, I assume?

 

Edit: This code (http://www.unige.ch/medecine/nouspikel/ti99/dc2.txt) is executed in MAME, accessing the FD1771. You should get it working in the same way. If not, mail me a copy of your program and I can do some debug logging to see where it fails.

Edited by mizapf

Share this post


Link to post
Share on other sites

I've done direct control of the floppy disk controller, when I made a program which could copy disk like Advanced diagnostics and Explorer from Miller's Graphics.

I've also done code which formats the disk with a different sector interlace, to make the p-system read the disks faster.

 

My code was written for the CorComp controller, in assembly language intended to be used as external procedures for Pascal.

Share this post


Link to post
Share on other sites

I was able to use MESS to directly read sectors as well, when I was testing ideas for the megademo loader. I actually copied the code from the TI DSR to do that test, so it probably works. ;)

 

I don't have any plans to add chip-level emulation of the disk controller to Classic99.

  • Like 1

Share this post


Link to post
Share on other sites

Some of the MG Explorer source code I just uploaded has direct track access (he makes 1K sectors and has a read routine for those as well).

 

There were different routines depending on which disk controller you were talking to (different chips).

 

For example, a regular 9 sector track and a two 1K sector track (pics attached). Kinda neat, talking directly to the controller, completely bypassing the DSR.

 

 

 

 

post-22866-0-30494000-1522797973_thumb.jpg

post-22866-0-96249200-1522798012_thumb.jpg

Share this post


Link to post
Share on other sites

Thanks for the replies everyone.

 

From what I'm hearing, what I'm trying to do is possible, I just need to look harder for my bugs.

 

Well, I was looking for a challenge...

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...