PDP-11の磁気ディスク装置RK11の勉強メモ

## ディスクドライブの構成
構成物は３つで下記の通り：

* ディスクコントローラ: RK11
* ディスクドライブ: RK05
* ディスクカードリッジ: RK03-KA

RK11ディスクコントローラには最大8台のドライブを接続できる。
また、RK11-DとRK11-Eと2種類ある。
前者は1word=16bit、後者は18bit単位でデータをアクセスする。

RK05ディスクドライブには交換可能なディスクカードリッジが１枚挿さる。ディスクのサイズは14インチ！
ネットの写真を見ると大人が抱えて持つくらいな大きさ。

ディスクの論理的な構成は、現代のハードディスクとまったく一緒だ。
ディスク表面だけでなく裏面にもデータ記録に使う。１ディスク２面持っているということだ。

スペック:

* 1ディスクカードリッジ/ディスクドライブ
* 2面/ディスク
* 203シリンダ/ディスク
* 203トラック/面 (406トラック/ディスク)
* 12セクタ/トラック
* 256ワード/セクタ


RK11-Dの場合は1word=16bitなので、512バイト/セクタになる。
一枚のディスクの容量は、512バイト x 12セクタ x 406トラック = 2494464バイト。（2.5MB)である。

## ディスクの読み書き
ディスクに対してデータを読み書きするためには、RK11の制御レジスタにアクセスして行う。
PDP-11はMemory-mapped I/O なので、ペリフェラルのレジスタは特定のメモリ番地の読み書きをすればよい。

以下はマニュアルからの抜粋。

####RK11の制御レジスタ一覧

|Name                        |Abbreviation|Address|意味|
|----------------------------|:----------:|:-----:|:--:|
|RK11 Drive Status Register  |RKDS        |777400  |ドライブの状態を表す|
|RK11 Error Register         |RKER        |777402  |エラーの状態を表す|
|RK11 Control Status Register|RKCS        |777404  |ディスク制御に使用される|
|RK11 Word Count Register    |RKWC        |777406  |転送データサイズを表す|
|RK11 Bus Address Register   |RKBA        |777410  |メモリ中の転送データのアドレス|
|RK11 Disk Address Register  |RKDA        |777412  |ドライブ中の転送データのアドレス|
|RK11 Data Buffer Register   |RKDB        |777416  ||

※アドレスは8進数表記

別の資料を探したら、RK11のコントロール用のレジスタ一覧は以下が見やすい。(http://www.psych.usyd.edu.au/pdp-11/rk05_internals.html)


まず知りたいのは、ディスク領域の場所指定の仕方だ。
ディスクのアドレス指定は、RKDAレジスタを使う。


```
777412  Disk address  15-13  Drive select      : 3bit (0〜7)
                      12-5   Cylinder address  : 8bit (0〜202)
                        4    Surface (head)    : 1bit (0,1)
                       3-0   Sector            : 4bit (0〜11)
```

ドライブ番号は0〜7番。シリンダは外周側から0番号が振られ、202まで。
ディスク面は表と裏の0,1指定。あとはセクタ番号のの指定(0〜11)。


#### UNIX V6のディスクブロック
でも、これデータを保存するときに、いちいち全パラメータを指定するのは面倒だろう。
UNIX V6の場合はどうしているのかというと、ドライブ番号はデバイスファイルのマイナー番号として使っているが、あとは論理的にはブロック番号というもので統一して扱っている。

あるディスク上のブロック番号は、シリンダ0, 面0, セクタ0 をブロック0として、リニアに増えて行く。（これはハードディスクのLBA方式と同じ考え方ですね。UNIXがベースになってるのかしら）

#####ブロック番号とセクタ、面、シリンダの関係
|Block |Cylinder |Surface |Sector |
|:----:|:-------:|:------:|:-----:|
| 0    |   0     |  0     |  0    |
| 1    |   0     |  0     |  1    |
| 2    |   0     |  0     |  2    |
| ...|||
| 11   |   0     |  0     |  11   |
| 12   |   0     |  1     |  0    |
| 13   |   0     |  1     |  1    |
| ...|||
| 23   |   0     |  1     |  11   |
| 24   |   1     |  0     |  0    |
| 25   |   1     |  0     |  1    |
| ...|||


#####計算式 (C言語)

* シリンダ番号 = (ブロック番号／１２)／２
* 面番号 = (ブロック番号／１２)＆１
* セクタ番号 = ブロック番号％１２ 

#####UNIX V6の実装
コードそのままではないが、以下のような実装をしている。
```
return (d_minor<<13 | (blkno/12)<<4 | blkno%12);
```

d_minor:デバイスのマイナー番号、blkno:ブロック番号。
またシリンダ番号と面番号はビット的には隣どおしなので、(blkno/12)<<4だけで済んでいる。


## PDP-11(エミュ）上でRK11を使ってみる

PDP-11エミュレータsimhを使って、rk11のディスクを使ってみる。
具体的にはディスクの１ブロック分(512バイト分）のデータをメモリに読み込んでみる。

簡単なアセンブラプログラムを、UNIX V6のディスクブートローダのソース（mdec/rk.s)を参考にしながら作ってみた。

下記のプログラムは、rk11ディスクコントローラを使ってブロックNo 1のディスク領域からメモリ番地02000番地に512バイト転送する。
(なお、以下も含めアドレスは全て8進表記)

```start.S
	.global start

	STACK = 01000
	rkda = 0177412

	.text
start:
	mov	$STACK,sp
	mov	$1,r1			/ r1 = block no = 1
rblk:
	clr	r0				/ r0 = 0
	div	$12,r0			/ r0 = r1/12, r1 = r1%12
	ash	$4,r0			/ r0 = r0 << 4
	bis	r1,r0			/ r0 = r0
	mov	$rkda,r1
	mov	r0,(r1)			/ *RKDA = r0
	mov	$02000,-(r1)	/ *RKBA = 02000
	mov	$-256,-(r1)		/ *RKWC = -256
	mov	$5,-(r1)		/ *RKCS = 5 (READ & GO)
1:
	tstb	(r1)		/ check *RKCS is ready
	bpl	1b

loop:
	br loop

	.end
```
```ld.script
ENTRY(start)
SECTIONS
{
	. = 01000;
	.text : {
		*(.text)
		*(.rodata)
		. = ALIGN(0100);
	}
	.data : {
		*(.data)
		. = ALIGN(0100);
	}
	.bss : {
		*(.bss)
		. = ALIGN(0100);
	}
	end = .;
}
```
```mf:Makefile
TARGET = start
OBJS = start.o
CC = pdp11-aout-gcc
AS = pdp11-aout-as

CFLAGS = -ffreestanding

all: $(TARGET).lda

run: rc-pdp11
	pdp11 $<

$(TARGET).lda: $(TARGET).out
	bin2load -a -f $< -o $@

$(TARGET).out: $(OBJS) ld.script
	pdp11-aout-ld -T ld.script $(OBJS) -o $@

.c.o:
	$(CC) $(CFLAGS) -c $< -o $@

.c.s:
	$(CC) -S $< -o $@

.S.o:
	$(AS) $< -o $@

clean:
	rm -f *.lda
	rm -f *.out
	rm -f *.o
```

#####実行はこんな感じ

    $ pdp11 rc-pdp11

``rc-pdp11``はsimhのPDP-11の設定ファイルで、起動時に引数として与えて読み込ませている。内容は以下の通り。

```:rc-pdp11
set cpu 11/40
set cpu u18
att rk0 unix0_v6_rk.dsk
l start.lda
```

``attr rk0 unix0_v6_rk.dsk``ではUNIXのディスクイメージのファイルをディスクrk0にアタッチしている。そして、ここで作ったプログラムのバイナリの``start.lda``は最後に読み込んでいる。

unix0_v6_rk.dskはodで中身を確認する。01000番地（512バイト）以降、つまりブロック番号１では、以下のようになっている。

```
*
0001000    000144  007640  000052  002412  002371  002442  002141  002130
0001020    002157  002376  002411  002214  002417  002307  002440  002437
```

で、エミュレータ起動後は、以下のようにして``g``でプログラムを実行する。

    PDP-11 simulator V3.9-0
    Disabling XQ
    sim> g
    # <C-e>を押す
    Simulation stopped, PC: 001052 (BR 1052)
    sim> e 2000:2030
    2000:	000144
    2002:	007640
    2004:	000052
    ....


最後の方は、コマンド``e``でメモリ番地の02000の内容をexamin(ダンプ)している。
02000はプログラムで読み込み先として指定した番地。
中身がunix0_v6_rk.dskをodした内容と同じことが確認できたかと。


##参考資料
* [シリンダ、トラック、セクタの用語の意味](http://park12.wakwak.com/~eslab/pcmemo/hdisk/)
* [RK11 disk driveマニュアル](http://bitsavers.trailing-edge.com/pdf/dec/unibus/EK-RK11D-MM-002.pdf)
* [RK05 Maintenance Manual](http://chmhdd.wikifoundry.com/page/DEC+RK05)
* 「はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)」
 
## 必要なツール環境
(1) PDP-11のgccクロス環境構築は以下記事を参照
http://qiita.com/hifistar/items/187fd7ad780c6aa26141

(2) bin2loadは以下から
[The Ancient Bits adventure: Writing PDP11 assembly code from Linux (and running it on bare -simulated- metal!)](http://ancientbits.blogspot.jp/2012/07/programming-barebones-pdp11.html)　

(3) simh
http://simh.trailing-edge.com/