ウォンツテック

そでやまのーと

OS作成

Qemuでfloppyの1セクタのreadに成功。
しかし、石叩きはむずかしい、というか資料が少ない。必要な情報としては

  • 初期化
  • 読み込む手順

が欲しいんだけどデータシートとか「パソコンのレガシィI/O活用大全isbn:9784789834339」には石の中のレジスタの詳細しか載っていない。と言う事でいろんなkernelのsourceとデータシートを見ながら書くことになるけどLinuxやらのkernelは肥大化しすぎてやりたいことのみを探すのが大変。
後、ソフトだとnanosecくらいで終わる処理がMortorとかの可動部はmsec〜secくらいの時間が掛かるのでそれをどのように待ってアクセスするなどのノウハウはsourceから読み取るしかない。
後でちょろっとDMAとFDCの叩き方をまとめとこう。

※実機ではResult Phase辺りで失敗する。推測ではFDCからのデータ読み込みに時間が掛かって今のMSRステータスのチェック回数が少ないよって感じかな。

追記

recalibrate(track 0 へのシーク)コマンド後のwaitで失敗している。

追記

specifyコマンド投入後に何らかのコマンド(recalibrate)を流す前にFDCの状態がbusyになってて失敗している模様。コマンド投入後に読めるデータ全て吸い出せばFDCの状態はreadyにならないんだろうか。。わからない。。。

floppy.c

/*
 *  @File        floppy.c
 *  @Brief	 row floppy access
 *  
 *  @Author      Sodex
 *  @Revision	 0.1
 *  @License	 suspension
 *  @Date	 creae:	2007/05/02  update:	2007/05/03
 *		
 *	Copyright (C) 2007 Sodex
 */
#include "io.h"
#include "memory.h"
#include "vga.h"
#include "floppy.h"

static void delay(int n);
static void fdc_dma_start();
static void fdc_dma_stop();
static void init_dma_r();
static int  fdc_cmd(const u_int8_t *cmd, const u_int8_t length);
static int  fdc_wait_msrStatus(u_int8_t mask, u_int8_t expected);
static void fdc_motor_on();
static void fdc_motor_off();
static void fdc_recalibrate();
static void fdc_read_results();

static u_int8_t dma_databuf[DMA_DATABUF];

static struct _dma_trans {
  u_int32_t count;
  u_int32_t addr;
} dma_trans;

static struct FDC_RESULTS {
  u_int8_t	gets;
  u_int8_t	req_sense;
  u_int32_t status_count;
  u_int8_t	status[10];
} fdc_results;

static void delay(int n)
{
  int i;
  for (i = 0; i < n; ++i)
	in8(IO_DELAY);
}

static void fdc_dma_start()
{
  out8(DMA_SGL_MSK_SEC, 0x02);
}

static void fdc_dma_stop()
{
  out8(DMA_SGL_MSK_SEC, 0x06);
}

void init_dma()
{
  // DMAC reset
  out8(DMA_MSR_CLR_PRI, 0x00);
  out8(DMA_MSR_CLR_SEC, 0x00);
 
  // DMAC mode register setting
  out8(DMA_MOD_PRI, 0xc0);
  out8(DMA_MOD_SEC, 0x46);

  out8(DMA_SGL_MSK_PRI, 0x00);
}

static void init_dma_r()
{
  fdc_dma_stop();

  out8(DMA_MSR_CLR_SEC, 0x00);
  out8(DMA_CLR_FLP_SEC, 0);

  out8(DMA_MOD_SEC, 0x46);
  disableInterrupt();
  out8(DMA_ADD_SEC, dma_trans.addr >> 0);
  out8(DMA_ADD_SEC, dma_trans.addr >> 8);
  out8(DMA_TOP, dma_trans.addr >> 16);
  out8(DMA_CNT_SEC, dma_trans.count >> 0);
  out8(DMA_CNT_SEC, dma_trans.count >> 8);
  enableInterrupt();
  fdc_dma_start();
}

static int fdc_cmd(const u_int8_t *cmd, const u_int8_t length) {
  int i;
  for (i=0; i<length; i++) {
	fdc_wait_msrStatus(MSR_RQM|MSR_DIO, MSR_RQM);
	out8(FDC_DAT, cmd[i]);
  }
  return TRUE;
}

static int fdc_wait_msrStatus(u_int8_t mask, u_int8_t expected)
{
  u_int8_t status;
  int count = 0;

  do {
	status = in8(FDC_MSR);
	count++;
  } while (((status & mask) != expected) && (count < FDC_RESULT_MAXCOUNT));
  
  if (count == FDC_RESULT_MAXCOUNT)
	return FALSE;

  return TRUE;
}	

static void fdc_motor_on()
{
  out8(FDC_DOR, 0x1c);
}

static void fdc_motor_off()
{
  out8(FDC_DOR, 0x0c);
}

static void fdc_recalibrate() {
  u_int8_t cmd[] = {CMD_RECALIBRATE, CMD_SUB};
  u_int8_t cmd_ret[] = {CMD_SIS};
  u_int8_t result[FDC_RESULT_MAXCOUNT];
  int i, status;

  if (fdc_cmd(cmd, sizeof(cmd)) != TRUE) {
  }

  /* get result */
  fdc_cmd(cmd_ret, sizeof(cmd_ret));
  for (i=0; i<FDC_RESULT_MAXCOUNT; i++) {
    status = in8(FDC_MSR);
	sysPrints("Recalibrate MSR:");
	sysPrintBin(status);
	sysPrintc('\n');
    if ((status & MSR_DIO) == 0) break;
    result[i] = in8(FDC_DAT);
  }
  if (!fdc_wait_msrStatus(0x10, 0x00)) {
	sysPrints("recalibrate last wait fail\n");
  }
}

// FDC Read Result Phase
static void fdc_read_results()
{
  u_int8_t* msr = &(fdc_results.status[0]);
  u_int8_t status;
  int count;

  for (count = 0; count < FDC_RESULT_MAXCOUNT; count++) {
	status = in8(FDC_MSR);
	sysPrints("result phase FDC_MSR:");
	sysPrintBin(status);
	sysPrintc('\n');
	if ((status & (MSR_RQM|MSR_DIO)) == (MSR_RQM|MSR_DIO))
	  break;
  }
  if (count == FDC_RESULT_MAXCOUNT) {
	sysPrints("fdc result phase error 1\n");
	return;
  }

  do {
	*msr = in8(FDC_DAT);
	msr++;

	for (count = 0; count < FDC_RESULT_MAXCOUNT; count++) {
	  status = in8(FDC_MSR);
	  if (status & MSR_RQM)
		break;
	}
	if (count == FDC_RESULT_MAXCOUNT) {
	  sysPrints("fdc result phase error 2\n");
	  break;
	}
	sysPrints("FDC_DAT\n");
  } while (status & MSR_DIO);
}

static void fdc_specify()
{
  fdc_motor_on();

  u_int8_t specify_cmd[] = {0x03, 0xc1, 0x10};
  if (!fdc_cmd(specify_cmd, sizeof(specify_cmd))) {
	sysPrints("FDC sepcify cmd error\n");
	return;
  }
  sysPrints("specify end\n");

  fdc_motor_off();
}

void init_fdc()
{
  init_dma();
  dma_trans.addr = (u_int32_t)&dma_databuf[0];
  dma_trans.count = 512;
  init_dma_r();

  out8(FDC_DOR, 0x0);
  out8(FDC_CCR, 0x0);
  out8(FDC_DOR, 0xc);

  fdc_specify();
}

char *fdc_read(u_int8_t head, u_int8_t track, u_int8_t sector, u_int8_t length)
{
  fdc_motor_on();

  fdc_recalibrate();
  u_int8_t cmd[] = {
	0x6|0x40,		// CMD_READ|MODE_MFM
	head << 2,		// head
	track,			// track
	head,			// head
	sector,			// sector
	length,			// length
	sector,			// end of sector
	0x1b,			// dummy GSR
	0			// dummy STP
  };

  fdc_cmd(cmd, sizeof(cmd));
  fdc_dma_stop();
  fdc_read_results();

  // write the binary which we get from DMA
  int i;
  sysPrints("READ DATA:");
  for (i = 0; i < 16; i++) {
	sysPrintBin(dma_databuf[i]);
	sysPrintc(' ');
  }
  sysPrintc('\n');

  fdc_motor_off();

  return NULL;
}

floppy.h

#ifndef _FLOPPY_H
#define _FLOPPY_H


#define TRUE 1
#define FALSE 0

#define DMA_DATABUF	1024
#define FDC_RESULT_MAXCOUNT 0x10

#define DMA_ADD_SEC 0x04	//channel2 low address
#define DMA_CNT_SEC 0x05	//channel2 count address
#define DMA_TOP	0x81		//channel2 high address

#define DMA_CMD_PRI	0xD0
#define DMA_CMD_SEC 0x08
#define DMA_REQ_PRI	0xD2
#define DMA_REQ_SEC	0x09
#define DMA_SGL_MSK_PRI 0xD4
#define DMA_SGL_MSK_SEC 0x0A
#define DMA_MOD_PRI	0xD6
#define DMA_MOD_SEC	0x0B
#define DMA_CLR_FLP_PRI 0x0C
#define DMA_CLR_FLP_SEC 0xD8
#define DMA_MSR_CLR_PRI 0xDA
#define DMA_MSR_CLR_SEC 0x0D
#define DMA_CLR_MSK_PRI 0xDC
#define DMA_CLR_MSK_SEC 0x0E
#define DMA_ALL_MSK_PRI 0xDE
#define DMA_ALL_MSK_SEC 0x0F

#define FDC_SRA		0x3f0	// FDC status registerA (R)
#define FDC_SRB		0x3f1	// FDC status registerB (R) 
#define FDC_DOR		0x3f2	// FDC Control register (R/W)
#define FDC_MSR		0x3f4	// FDC Status register (R)
#define FDC_DSR		0x3f4	// FDC data rate select register (W)
#define FDC_DAT		0x3f5	// FDC Data	(R/W)
#define FDC_DIR		0x3f7	// FDC digital input register (R)
#define FDC_CCR		0x3f7	// FDC configuration control register (W)

#define MSR_RQM		0x80
#define MSR_DIO		0x40

/* FDC CMD */
#define CMD_RECALIBRATE 0x07
#define CMD_SEEK		0x0f
#define CMD_SIS			0x08 // SIS = SENSE INTERRUPT STATUS
#define CMD_READ		0x46 //MT=0,MF=1,SK=0

/*
  == FDC_CMD_SUB format ==
  x x x x x HD US1 US0
  x is anyone.
  HD is head number.
  US1 and US0 are drive number of FD.

  This cmd is used as the second byte of almost all command.
*/
#define CMD_SUB 0x00 //HD=0, US1 & US0 = 0


#define IO_DELAY 0x80


typedef struct _trans {
  u_int32_t count;
  u_int32_t addr;
} trans;

void init_dma();
void init_fdc();
char *fdc_read(u_int8_t head, u_int8_t track,
			   u_int8_t sector, u_int8_t length);

#endif /* _FLOPPY_H */