ウォンツテック

そでやまのーと

OS作成

FDCの実機での1セクタ読み込みに成功。
苦労した点としては

  • どのくらいwaitすればFDCがbusyからready状態になるのか
  • どのコマンド操作をした時にFDCからCPUに割り込みが入るのか
  • FDCの割り込みマスク解除を忘れていた
  • FDCの割り込みhandlerで次の割り込み待ち処理を忘れていた
  • 3つのフェーズ(コマンドフェーズ、エクスキュージョンフェーズ、リザルトフェーズ)を正確に理解するのに時間が掛かった

などです。


今後のために正確なFDC読み込み手順をメモ

0. 前提知識
 FDCは以下の3つのフェーズがある
 ・コマンドフェーズ
 ・エクスキュージョンフェーズ
 ・リザルトフェーズ
 最初にコマンドフェーズでFDCに対してコマンドを送り、FDCは
 エクスキュージョンフェーズとなる。処理が終了するとFDCはCPUに
 対して割り込み処理を掛けてくるので、この時FDCはエクスキュージョン
 フェーズからリザルトフェーズへと移行する。このフェーズでは
 きっちり全ての結果を受け取らないとFDCが次のコマンドを受け付ける
 事が出来なくなる。
 また例外的にコマンドとしては以下のようなものがある。
 ・Specifyコマンド
  コマンドフェーズしかなく、結果を受け取る必要がない。
 ※割り込みも発生しない
 ・sense interrupt statusコマンド
 このコマンドはrecalibrate, seekコマンド実行後にそのリザルトを
 受け取るためのコマンドで、このコマンド自体は直接リザルト
 フェーズへと以降する
 ※割り込みも発生しない
 ・Recalibrateコマンド、Seekコマンド
 これらのコマンドは3つのフェーズが存在するが、エクスキュージョン
 フェーズ後に割り込みが発生したらsense interrupt statusコマンド
 を発行してリザルトを受け取らないといけない。


1. DMAの初期設定
 FDCとメモリ間の転送はDMAといわれるコントローラを使い直接行う。
(CPUを経由しないので転送中CPUは違う処理が出来る)
 この処理がinit_dma()でDMACのリセットと二つあるDMACのmodeを設定している。
 ※Slave(DMAC_XXX_SEC)の方しか使わない。

2. 読み取り方向(FDC⇒メモリ)のDMAの設定
 init_dma_r()で設定し、主に転送先のバッファメモリのアドレスの
 指定やバッファのサイズの指定をする。

3. FDCの初期化
  init_fdc()で設定。ここでinit_dma(), init_dma_r()の設定を行う
  またFDCのレジスタの初期化を行い、motorをonにする。
 ※今回はテストなのでfdc_readが完了するまでmotorはonのままにしておく。

4. FDC割り込みhandlerの設定
 i26h_fdchandler()を割り込み関数としてidt.cで登録。
 またPICのマスク処理
  // mask outport at not key & timer & fdc
  out8(0x21, 0xBC);
  の所で、6bit目がFDCの割り込み番号なのでそこを0にしておく。

  FDCはコマンドフェーズが完了するとCPUに対して割り込みを発生
 させ、その終了を伝える。したがって割り込みの設定をする必要
 がある。そのhandlerを設定しIDT(idt.c)のところで登録をして
 おく。今回の割り込み処理は割り込みが発生した事を知らせる
 ために単純にファイルスコープな変数fdc_interruptをインクリ
 メントして伝えている。fdc_chk_interrupt()とfdc_wait_interrup()
  で発生を捕らえている。
 ※fdc_chk_interrupt()とfdc_wait_interrupt()をまとめて書いて
 しまうとコンパイルした段階で「fdc_interrupt」の値が「0」で
 確定してしまい無限ループとなるので分ける必要がある。

5. specifyコマンド
 初期設定が終わったらspecifyコマンドを送る。
 これはFDCのDMAの転送に関する事や信号のパルス周期の設定など
 を行う。
 ※specifyコマンドは「コマンドフェーズ」しかなくResultを
 受け取る必要がない。

6. recalibrateコマンド
 FDCのヘッド(各セクタを読み込む機械部)を0track位置に初期化
 する

7. readコマンド
 recalibrateが終了したらreadをするが、終了の割り込みを待って
 からコマンドを送る。またこのreadコマンドを送ったらまた割り込み
 を待ち、fdc_read_results()で割り込み結果を全て受け取る。
 そうするとDMAで設定したバッファに1セクタ分のデータが格納
 されているのでそれを読み出す。

8. 終了処理。
 fdc_dma_stop(), fdc_motor_off()で読み込み終了処理をする。

floppy.c

#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_specify();
static void fdc_recalibrate();
static void fdc_sense_interrupt();
static void fdc_read_results();
static u_int32_t fdc_wait_interrupt();
static void fdc_clear_interrupt();

static u_int8_t dma_databuf[DMA_DATABUF];
static u_int32_t fdc_interrupt = 0; // for 0x26 FDC interrupt

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;

void init_dma()
{
  // DMAC reset
  out8(DMA_MSR_CLR_PRI, 0x00);
  out8(DMA_MSR_CLR_SEC, 0x00);

  out8(DMA_CMD_PRI, 0x00);
  out8(DMA_CMD_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();
}

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_motor_on();

  fdc_specify();
}

void i26h_fdchandler()
{
  fdc_interrupt++;
  sysPrints("[FDC] Interrupt occur!\n");

  out8(0x20, 0x66);
  asm("mov %ebp,%esp");
  asm("pop %ebp");
  asm("iret");
}

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);
}

static int fdc_chk_interrupt()
{
  return fdc_interrupt;
}

static u_int32_t fdc_wait_interrupt()
{
  while (!fdc_chk_interrupt());

  return TRUE;
}

static void fdc_clear_interrupt()
{
  fdc_interrupt = 0;
}

static int fdc_cmd(const u_int8_t *cmd, const u_int8_t length)
{
  /*
  volatile u_int8_t status;
  u_int8_t cmdcount;
  u_int32_t count;

  sysPrints("fdc cmd busy check\n");
  for (count = 0; count < FDC_RESULT_MAXCOUNT; count++) {
    if (!((status = in8(FDC_MSR)) & MSR_BUSY))
      break;
  }
  if (count == FDC_RESULT_MAXCOUNT) {
    sysPrints("fdc cmd busy check error\n");
    return FALSE;
  }
  sysPrints("fdc cmd busy check OK\n");
  */


  sysPrints("[FDC] cmd busy check.\n");
  if (!fdc_wait_msrStatus(MSR_BUSY, MSR_READY)) {
    sysPrints("[FDC] cmd busy check error.\n");
    return FALSE;
  }
  sysPrints("[FDC] cmd busy check [OK]\n");


  /*
  cmdcount = 0;
  count = 0;
  sysPrints("fdc cmd msr check:");
  do {

    if ((status & (MSR_RQM|MSR_DIO)) == MSR_RQM) {
      out8(FDC_DAT, cmd[cmdcount]);
      cmdcount++;
      count = 0;
      sysPrintBin(status);
      sysPrintc(' ');
    } else {
      count++;
    }
    status = in8(FDC_MSR);
  } while ((cmdcount < length) && (count < FDC_RESULT_MAXCOUNT));
  sysPrintBin(status);
  sysPrintc('\n');
  if (count == FDC_RESULT_MAXCOUNT) {
    sysPrints("fdc cmd error\n");
    return FALSE;
  }
  */

  sysPrints("[FDC] cmd out and msr check.\n");
  int i;
  for (i=0; i < length; i++) {
    if (!fdc_wait_msrStatus(MSR_RQM|MSR_DIO, MSR_RQM)) {
      sysPrints("[FDC] msr RQM|DIO error\n");
      return FALSE;
    }
    out8(FDC_DAT, cmd[i]);
  }
  sysPrints("[FDC] cmd out and msr check [OK]\n");

  return TRUE;
}

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

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

  if (count == FDC_RESULT_MAXCOUNT) {
    sysPrints("[FDC] msr wait error. ");
    sysPrints("status:");
    sysPrintBin(status);
    sysPrintc('\n');
    return FALSE;
  }

  return TRUE;
}

static void fdc_sense_interrupt()
{
  u_int8_t cmd[] = {CMD_SENSE_INT_STS};

  fdc_clear_interrupt();
  if (fdc_cmd(cmd, sizeof(cmd)) != TRUE) {
    sysPrints("[FDC] sense interrupt status cmd error\n");
    return;
  }
  fdc_read_results();
}

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

  fdc_results.status_count = 0;

  /*
  for (count = 0; count < FDC_RESULT_MAXCOUNT; count++) {
    status = in8(FDC_MSR);
    if ((status & (MSR_RQM|MSR_DIO)) == (MSR_RQM|MSR_DIO))
      break;
  }
  if (count == FDC_RESULT_MAXCOUNT) {
    sysPrints("result phase status:");
    sysPrintBin(status);
    sysPrintc('\n');
    sysPrints("fdc result phase error 1\n");
    return;
  }
  */
  sysPrints("[FDC] read result RQM|DIO check.\n");
  if (!fdc_wait_msrStatus(MSR_RQM|MSR_DIO, MSR_RQM|MSR_DIO)) {
    sysPrints("[FDC] read result MSR_RQM|MSR_DIO error.\n");
    return FALSE;
  }
  sysPrints("[FDC] read result RQM|DIO check [OK]\n");


  sysPrints("[FDC] read result check.\n");
  do {
    *msr = in8(FDC_DAT);
    msr++;
    fdc_results.status_count++;

    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 FDC_MSR:");
      sysPrintBin(status);
      sysPrintc('\n');
      sysPrints("[FDC] result phase RQM wait error\n");
      break;
    }
  } while (status & MSR_DIO);
  sysPrints("[FDC] read result check [OK]\n");

  sysPrints("[FDC] Results:");
  int i;
  for (i = 0; i < fdc_results.status_count; i++) {
    sysPrintBin(fdc_results.status[i]);
    sysPrintc(' ');
  }
  sysPrintc('\n');
}

static void fdc_specify()
{
  u_int8_t specify_cmd[] = {0x03, 0xc1, 0x10};

  fdc_clear_interrupt();

  sysPrints("[FDC] Specify Cmd.\n");
  if (!fdc_cmd(specify_cmd, sizeof(specify_cmd))) {
    sysPrints("[FDC] Sepcify Cmd error\n");
    return;
  }
  sysPrints("[FDC] Specify Cmd [OK]\n");
}

static void fdc_recalibrate()
{
  u_int8_t cmd[] = {CMD_RECALIBRATE, CMD_SUB};

  fdc_clear_interrupt();

  sysPrints("[FDC] Recalibrate Cmd.\n");
  if (!fdc_cmd(cmd, sizeof(cmd))) {
    sysPrints("[FDC] Recalibrate Cmd error\n");
    return;
  }
  sysPrints("[FDC] Recalibrate Cmd [OK]\n");

  if (!fdc_wait_interrupt())
    sysPrints("[FDC] wait interrupt error\n");

  /* get result */
  fdc_sense_interrupt();

  if (!fdc_wait_msrStatus(MSR_BUSY, MSR_READY)) {
    sysPrints("[FDC] Recalibrate  wait fail\n");
  }
}

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

  u_int8_t cmd[] = {
    CMD_READ,
    head << 2,      // head
    track,          // track
    head,           // head
    sector,         // sector
    0x2,            // sector length (0x2 = 512byte)
    0x12,           // end of track (EOT)
    0x1b,           // dummy GSR
    0               // dummy STP
  };

  fdc_clear_interrupt();

  fdc_cmd(cmd, sizeof(cmd));

  if (!fdc_wait_interrupt())
    sysPrints("[FDC] wait interrupt error\n");

  fdc_read_results();

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

  fdc_dma_stop();
  fdc_motor_off();

  return NULL;
}

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

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