前回でSDCARDでの仮想ディスクができたので、今回はBIOSでのディスク関連のルーチンを作成していきたいと思います。

主なディスク関連のルーチンは次のとおりです。順を追って作成してみます。
home: seldsk: settrk: setsec: setdma: read: write: sectran:

まずは、home: から
本来ならディスク装置のヘッドをホーム(トラック0)に移動することらしいです。ここでは、settrk:(トラック位置のセットルーチン)にトラック00を設定しています。

home:	
	ld	c,0x00		;track 00
	call	settrk
	ret

seldsk:は ディスクナンバーをセットします。
cレジスタにセットするディスクナンバー(a:>00 B:>01 ・・)が入っているのでセットナンバーにあったディスクパラメータのアドレスをHLレジスターにセットします。存在しない場合はHLレジスタに0x0000をセットします。
また、実際のread/writeにディスクナンバーが必要になりますのでdisk_numberに保存します。
DISKS_MAXには使用するディスク数をセットしておきます。
dpbaseを基準にディスクパラメータは1つのディスク16byte使用しているので、HLレジスタにディスクナンバーをいれて16倍したのち、dpbaseを足して該当のパラメータアドレスを取得しています。

seldsk:				;c reg set disk no
	ld	hl,0x0000	;err set hl=0000 return
	ld	a,c
	cp	DISKS_MAX
	ret	nc
	ld	(disk_number),a
	ld	l,a
	ld	h,0x00
	add	hl,hl
	add	hl,hl
	add	hl,hl
	add	hl,hl
	ld	de,dpbase
	add	hl,de
	ret

settrk:は呼び出すトラックナンバーがCレジスターに入ってきます。
read/writeで使用するので、track_numberにトラックナンバーを保存します。

settrk:				;set track address given by c
	ld	hl,track_number
	ld	(hl),c
	ret

setsec:は呼び出すセクタナンバーがCレジスターに入ってきます。
read/writeで使用するので、sector_numberにセクタナンバーを保存します。

setsec:				;set sector numbar given by c
	ld	hl,sector_number
	ld	(hl),c
	ret

setdma:はread/writeで読み書きするための128byteのバッファアドレスがBCレジスタに入ります。デフォルトは 0x0080になっています。
このアドレス(dma_buff_addr)も実際のread/writeで読み込み先アドレス/書き込み先アドレスとして使用します。

setdma:				;set dma address set bc reg
	ld	h,b
	ld	l,c
	ld	(dma_Buff_addr),hl
	ret

sectran:はディスクドライブの同心円状のセクタを早く読み書きするためのスキューという手法のためのルーチンですが、SDCARDではスキューを使う必要はありません。
ただ、今回は8インチ片面単密度のディスクパラメータをそのまま使用しているのでスキュー有りになっています。実際のルーチンはお手本のbiosをそのまま使用しています。
BCレジスタにスキューしたいセクタが、DEにスキュー変換テーブルのアドレスが、HLに変換されたセクタが格納されます。

sectran:			;sector translate
				;logical sector > BC
				;sector translate teble > de
				;return to translate ogical sector > hl
	ld	b,0
	ex	de,hl
	add	hl,bc
	ld	a,(hl)
	ld	(sector_number),a
	ld	l,a
	ret

read:は、ドライブナンバー、トラックナンバー、セクターナンバーから、該当のSDCARDのブロック位置の情報を読み込み、DMAアドレスの先頭から格納します。

write:でも使用する共通ルーチンとして、get_sectorを作成し、先程の情報から該当、ブロックナンバーを算出しています。

まずは、ディスクナンバーから、先頭のセクタ位置を算出します。
1ディスク 77トラック、1トラック26セクタあるので、1ディスクは77*26セクタになります。これをデスクナンバーにかければ 各ディスクの先頭セクタがわかります。
次に、トラックナンバーから 先頭セクタ+トラックナンバー*26にてトラックナンバー位置のセクタがわかります。
セクタは 1から26を取るので 1減算して、0から25にします。
そして、トラックナンバー位置のセクタと足すことにより、セクタ位置がわかるので、この位置をSDCARDのブロックナンバーとします。

get_sector:
	ld	hl,0x0000
	ld	de,DISK_TRACK * DISK_SECTOR	;1 disk sector >> DISK_TRACK * DISK_SECTOR
	ld	a,(disk_number)
	cp	0x00
	jr	z,read_track
	ld	b,a	
read_disk01:
	add	hl,de	
	djnz	read_disk01
read_track:
	ld	de,DISK_SECTOR		;1 track sector >> DISK_SECTOR
	ld	a,(track_number)
	cp	0x00
	jr	z,read_sector
	ld	b,a	
read_track01:
	add	hl,de	
	djnz	read_track01	
read_sector:
	ld	a,(sector_number)
	dec	a
	ld	e,a
	ld	d,0x00
	add 	hl,de
	ret

実際のread:はマルチブロックリードのcmd18を使用しました。
複数ブロックの読み込みが可能ですが、ここでは1ブロックだけのリードとなっています。
まずは、get_sectorで該当ブロックがHLレジスタに置かれます。
cmd18コマンド列の該当部分にこのHLレジスタの内容を格納します。
cmd18を発行してレスポンスを待って、1byte読み捨てしてからデータブロックの先頭の0xfeを探します。
続いて、データの先頭128byteだけdma_byff_addrの先頭から格納します。
残りの512-128byteは読み捨てします(128byte*3)
データブロックの最後のcrc 2byteも読み捨てします。
これで 1ブロック 512byte読み終えたので、1byte置いてからcmd12を発行してマルチブロックリードを終了します。
cmd12のレスポンスを確認してから、データ(DI)がLレベル(ビジー)からHレベルになるまで待ちます。
正常に終了したら、Aレジスターに0x00をエラーの場合には0x01をセットして戻ります。

read:				;read next disk record(assuming disk/trk/sec/dma set)

	call	get_sector		;read block   HL reg

			
        ld      a,(r7)
        bit     6,a
        jp      z,read_SD_ver2
        ld      a,h                    	;SDHC
        ld      (cmd18+03),a
        ld      a,l
        ld      (cmd18+04),a
        jr      spi_read2
read_SD_ver2:
        ld      a,h                    	;Ver2
        ld      (cmd18+02),a
        ld      a,l
        ld      (cmd18+03),a


	ld	hl,(dma_Buff_addr)
spi_read2:

        
        call    dummy_data              ;dummy data(0xff) out
        ld      hl,cmd18
        call    cmd_out                 ;cmd17 Issue
        call    r1_resp
        cp      0x00                    ;response is 0x00 read mode OK?
        jr      nz,cmd18_err
cmd18_st:
        ld      d,0xff
        call    spi_8bit


        call    r1_resp                 ;read data start response?
        cp      0xfe                    ;data start byte 0xfe?
        jr nz,  cmd18_st

        ld      b,0x80                  ;read data size 128byte
        ld	hl,(dma_Buff_addr)      ;read data buff address

cmd18_r:
        call    resp                    ;read data
        ld      (hl),a
        inc     hl
        djnz    cmd18_r

;;	dummy read 128byte 2st 3st 4st

        ld      b,0x80			;read data size 128byte
cmd18_r2st:
        call    resp
        djnz    cmd18_r2st
        ld      b,0x80
cmd18_r3st:
        call    resp
        djnz    cmd18_r3st

        ld      b,0x80
cmd18_r4st:
        call    resp
        djnz    cmd18_r4st

        call    resp                    ;crc Skipping read
        call    resp                    ;crc Skipping read
        ld      d,0xff                  ;dummy data 0xff
        call    spi_8bit


       	ld      hl,cmd12		;stop cmd
        call    cmd_out                 ;cmd12 Issue
	ld	d,0xff
	call	spi_8bit
        call    r1_resp
        cp      0x00                    ;response is 0x00 read mode OK?
        jr      nz,cmd18_err

cmd18_wch3:
	call	resp
	cp	0x00
	jr	z,cmd18_wch3

        call	set_cs		          ;CS="H"
        xor        a
        ret

cmd18_err:
	pop	hl
	call	set_cs		          ;CS="H"
	ld	a,0x01
	ret

write:の方ですがこちらも今回はマルチブロックライトのcmd25を使用しました。
複数ブロックの書き込みが可能ですが、ここでは1ブロックだけのライトとなっています。
まずは、get_sectorで該当ブロックがHLレジスタに置かれます。
cmd25コマンド列の該当部分にこのHLレジスタの内容を格納します。
cmd25を発行してレスポンスを待って、1byte読み捨てしてからデータブロックの先頭の0xfcを書き込みします。
続いて、dma_byff_addrの先頭から 128byte順次書き込みしていきます。
残りの512-128byteは0xe5で書き込み埋めていきます(128byte*3)
データブロックの最後のcrc 2byteは正式なcrc計算はせず0xffを書き込みします。(実際読み出しの際はcrcは無視しています^^;)
これで 1ブロック 512byte書き込が終了しました。
cmd25のデータブロックのレスポンスを確認してから、データ(DI)がLレベル(ビジー)からHレベルになるまで待ちます。
cmd25のストップトークン(oxfd)を発行して、1byte読み捨てしてからデータ(DI)がLレベル(ビジー)からHレベルになるまで待ちます。
これで、マルチブロックライトを終了します。
正常に終了したら、Aレジスターに0x00をエラーの場合には0x01をセットして戻ります。

write:
	call	get_sector
 
spi_write1:
        ld      a,(r7)
        bit     6,a
        jp      z,write_SD_ver2
        ld      a,h                   ;SDHC
        ld      (cmd25+03),a
        ld      a,l
        ld      (cmd25+04),a
        jr      spi_write2
write_SD_ver2:                        ;Ver2
        ld      a,h
        ld      (cmd25+02),a
        ld      a,l
        ld      (cmd25+03),a    


spi_write2:
        call    dummy_data
        ld      hl,cmd25
        call    cmd_out
        call    r1_resp
        cp      0x00
        jp      nz,cmd25_err

        ld      d,0xff
        call    spi_8bit
 
	ld      d,0xfc				;cmd25 data token
        call    spi_8bit

	ld	hl,(dma_Buff_addr)
        ld      b,0x80
cmd25_w:
        ld      d,(hl)
        inc     hl
        call    spi_8bit
        djnz    cmd25_w

;	2st dummy write(0xe5)
 
        ld      b,0x80
cmd25_w2st:
        ld      d,0xe5
        call    spi_8bit
        djnz    cmd25_w2st

;	3st dummy write(0xe5)  
        ld      b,0x80
cmd25_w3st:
        ld      d,0xe5
        call    spi_8bit
        djnz    cmd25_w3st

;	4st dummy write(0xe5)  
        ld      b,0x80
cmd25_w4st:
        ld      d,0xe5
        call    spi_8bit
        djnz    cmd25_w4st

        ld      d,0xff
        call    spi_8bit    ;crc
        ld      d,0xff
        call    spi_8bit    ;crc

        call    r1_resp
        and     0x0f
        cp      0x05
        jr      nz,cmd25_err2
cmd25_wch:
        call    r1_resp
        cp      0x00
        jr      z,cmd25_wch

	ld	d,0xfd			;cmd25 stop token
	call	spi_8bit 	
	ld	d,0xff
	call	spi_8bit
cmd25_wch3:
	call	resp
	cp	0x00
	jr	z,cmd25_wch3

	call	dummy_data
	call	set_cs			;set CS
        xor     a
        ret

cmd25_err:
cmd25_err2:
	call	set_cs			;set cs
	ld	a,0x01
	ret

これで ディスク リード/ライトのルーチンができましたので次回残りのwbootを作成して、cp/mを起動させてみたいと思います。



おすすめの記事