前回でBIOSが完成したので、SDCARDのCP/Mシステム部分に書き込みして、SDCARDを読み込むことにより、システムが立ち上がるようにしたいと思います。
最終的には、モニタに組み込んで、モニタのコマンドで読み込むようにしますが、そのまえに、簡単な512byte読み書きとbootのソフトを作成して確認してみます。
512byteの読み込みのcmd18のマルチブロックリードはbiosで実装してるので、今回は、cmd25のマルチブロックライトについて簡単に確認してみたいと思います。
cmd25は複数ブロックの書き込みが可能ですが、指定ブロック数に応じて1ブロック512byteのブロック数をライトします。
まずは、rw_block_noで先頭ブロックNo.がHLレジスタに置かれます。
cmd25コマンド列の該当部分にこのHLレジスタの内容を格納します。
cmd25を発行してレスポンスを待って、1byte読み捨てしてからデータブロックの先頭の0xfcを書き込みします。
続いて、rw_addrの先頭から 256byte順次書き込みしていきます。
続いて、更に256byteは書き込みます。(256byte*2)
データブロックの最後のcrc 2byteは正式なcrc計算はせず0xffを書き込みします。(実際読み出しの際はcrcは無視しています^^;)
これで 1ブロック 512byte書き込が終了しました。
cmd25のデータブロックのレスポンスを確認してから、データ(DI)がLレベル(ビジー)からHレベルになるまで待ちます。1byte置いてから読み捨てしてから、rw_blockを減算します。結果が0でなかったら、次の512byteの書き込みを続けます。
結果が0の場合、cmd25のストップトークン(oxfd)を発行して、1byte読み捨てしてからデータ(DI)がLレベル(ビジー)からHレベルになるまで待ちます。
これで、マルチブロックライトを終了します。
正常に終了したら、Aレジスターに0x00をエラーの場合には0x01をセットして戻ります。
spi_write_go:
ld hl,(rw_block_no)
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_write4
write_SD_ver2: ;Ver2
ld a,h
ld (cmd25+02),a
ld a,l
ld (cmd25+03),a
spi_write4:
call dummy_data
ld hl,cmd25
call cmd_out
call r1_resp
cp 0x00
jp nz,cmd25_err
ld d,0xff
call spi_8bit
sector_write_loop:
ld hl,(rw_addr)
ld d,0xfc ;cmd25 data token
call spi_8bit
;; spi write 256byte 1st blok
ld b,0x00
cmd25_w: ;1st 256byte write
ld d,(hl)
inc hl
call spi_8bit ; D reg >> write data
djnz cmd25_w
; 2st write 256byte 1st blok
ld b,0x00
cmd25_w2st: ;2st 256byte write
ld d,(hl)
inc hl
call spi_8bit ; D reg >> write data
djnz cmd25_w2st
ld (rw_addr),hl
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 resp
cp 0x00
jr z,cmd25_wch
ld d,0xff
call spi_8bit
ld hl,(rw_block)
dec hl
ld (rw_block),hl
ld a,h ;HL = 0?
or l
jr nz,sector_write_loop
ld d,0xfd ;cmd25 stop token
call spi_8bit
ld d,0xff
call spi_8bit
cmd25_wch2:
call resp
cp 0x00
jr z,cmd25_wch2
call dummy_data
call set_cs ;set CS
ld hl,rw_ok_msg
call msgout
call cr
xor a
ret
md25_err:
cmd25_err2:
ld hl,cmd25_err_msg
call msgout
call set_cs ;set CS
ld a,0x01
ret
システムをbootするには、SDCARDのシステム(CCP+BDOS+BIOS)を読み込み、BIOSの先頭アドレスにジャンプする必要があります。
今回は8インチ片面単密度ディスクと同じディスクブロック構造になっているので、0,1トラックがシステム領域です。1トラック26セクタなので、先頭から52セクタがシステム領域になります。
本来ならば、1セクタ 128byteなので合計 0x1a00の大きさですがCCP+BDOSで 0x1600を占め、BIOSとして0x400しかなく、BIOSの入る余地がありません。そこで、このシステム領域のみ 1セクタ 512byteで読み書きすることで、BIOSの領域確保しています。
(BIOSのwboot:ではこの1ブロック512byteの読み込みに対応しています。)
SDCARDは1ブロック 512yteなので0ブロック(セクタ)から51ブロック(セクタ)までシステムとして使用できます。
CCP+BDOSは 0x1600の大きさなので、1ブロック512byteでは、0x000bブロックとなります。作成したBIOSは0xf200〜0xf875の大きさで0x0675あります。
ここでは、4ブロック 0x800確保しました。
実際のコードはシステム(CCP+BDOS)を読み込むため、まず、ブロック0x0000から0x000bブロック読み込み、0xdc00から格納しています。
次にBIOSをブロック0x000bから0x0004ブロック読み込み、0xf200から格納しています。格納が完了したら 0xf200へジャンプし CP/Mを起動します。
cpm_boot:
ld hl,0x0000 ;CP/M system block 0x0000 start
ld (rw_block_no),hl
ld hl,0x000b ;ccp+bdos >0x0b
ld (rw_block),hl
ld hl,0xdc00 ;62K cp/m ccp top addr >> 0xdc00
ld (rw_addr),hl
call spi_read_go
ld hl,0x000b ;CP/M bios block 0x000b start
ld (rw_block_no),hl
ld hl,0x0004 ;bios >0x04
ld (rw_block),hl
ld hl,0xf200 ;62K cp/m bios top addr >> 0xf200
ld (rw_addr),hl
call spi_read_go
jp 0xf200
実際にモニタで確認し、システムをSDCARDにセーブしたあと、ブートしたいと思います。
まずは、アセンブルします。(ソースファイル名はcpm_rw005.asm、最後の方でソースは展開しています)
$ asz80 -l -s -o cpm_rw005.asm
$ aslink -i cpm_rw005
モニタに入り、64Kモニタに切り替えた後、CPM.HEX、bios304.ihxのhexファイルをlコマンドで読み込みます。
続いて、今回作成したcpm_rw005.ihxを同じくlコマンドで読み込みます。
jコマンドで 0x8000へジャンプします。
j 8000 y:
:SDHC
read >> r :write >> w CP/M go >> g key in:
SDCARDにシステム(CCP+BDOS)書き込みします。
wキーでwriteを選択し、0000ブロックから000bブロック、アドレスdc00より書き込み指定し、yキーで書き込み実行します。
read/write start block No:0000
read/write block:000b
read/write addr:dc00
Yes: read/write ok!
続いてBIOSを書き込みます。
000bブロックから 0004ブロック、アドレスf200より書き込みしてします。
j 8000 y:
:SDHC
read >> r :write >> w CP/M go >> g key in:w
read/write start block No:000b
read/write block:0004
read/write addr:f200
Yes: read/write ok!
>
これで 書き込みされたので、一旦電源を切り、モニタを立ち上げ、tコマンドで64Kモニタに切り替えます 。
もう一度cpm_rw005.ihxをlコマンドで読込します。
j 8000で 8000番地にジャンプします。
今度は、bキーでCP/Mシステムをbootします。
j 8000 y:
:SDHC
read >> r :write >> w CP/M boot >> b key in:b
CP/M system boot !
:SDHC
62K CP/M VERS 2.2
A>
なんとかbootできました。
次回はモニタに組み込み、モニタからブートできるようにしたいと思います。
以下 アセンブラソースを展開しました。
;z80 (TMPZ84C015) cp/m V2.2 system read/write/boot by Pinecone 2020/04/01
; TMPZ84C015 cpu
; ram 0000h -- ffffh
; External clock 20MHz
; TMPZ84C015 serial communication (CH-B)
; extemal Clock 614.4KHz(4.9152MHz TC74HC4040 1/8 Frequency division)
; Band Rate:38400 8bits 1Stopbits none Parity (81N)
; assembler
; program start 0x8000
; main 0x8000
;
; assemblers ASxxxx and ASlink V5.10
; file name cpm_rw005.asm
; $ asz80 -l -s -o cpm_rw005.asm
; $ aslink -i cpm_rw005
; $ tmpz84_moni71 l command Hex load jump 0x8000
.z80
SIOBD .equ 0x1A ;SIO-B CH DATA REG
SIOBC .equ 0x1B ;SIO-B CH CONTL REG
PIOAD .equ 0x1c ;PIO-A CH DATA REG
PIOAC .equ 0x1d ;PIO-A CH CONTL REG
;OUTPUT-PORT
S_DI .equ 7 ;0b10000000 D7
S_CS .equ 6 ;0b01000000 D6
S_CLK .equ 5 ;0b00100000 D5
;INPUT-PORT
DO .equ 0 ;0b00000001 D0
.area FORMAT(ABS)
.org 0x8000
; spi read, write. boot
;
;
start:
call init_spi
ld hl,rwg_msg
call msgout
call getchar
call putchar
cp "r"
jr z,read
cp "w"
jr z,write
cp "b"
jr z,cpm_boot
ret
read:
call cr
call spi_read
ret
write:
call cr
call spi_write
ret
cpm_boot:
ld hl,0x0000 ;CP/M system block 0x0000 start
ld (rw_block_no),hl
ld hl,0x000b ;ccp+bdos >0x0b
ld (rw_block),hl
ld hl,0xdc00 ;62K cp/m ccp top addr >> 0xdc00
ld (rw_addr),hl
call spi_read_go
ld hl,0x000b ;CP/M bios block 0x000b start
ld (rw_block_no),hl
ld hl,0x0004 ;bios >0x04
ld (rw_block),hl
ld hl,0xf200 ;62K cp/m bios top addr >> 0xf200
ld (rw_addr),hl
call spi_read_go
ld hl,cpm_boot_msg
call msgout
jp 0xf200
init_spi:
push af
xor a
set S_CS,a ;CS="H"
set S_DI,a ;DI="H"
out (PIOAD),a
ld b,80 ;80 clok output
call loopspii
pop af
xor a
out (PIOAD),a ;CS="L" DI="L" CLK="L"
call cmd0_out
cp 0x01
jp nz,init_err
call cmd8_out
cp 0x01
jp nz,init_err
call cmd55_out
cp 0x01
jp nz,init_err
call acmd41_out
call cmd58_out
ld a,(r7)
bit 7,a
jp z,init_err
bit 6,a
jr z,SD_ver2
ld hl,sdhc_msg
init_msg:
call msgout
call set_cs
ret
SD_ver2:
ld hl,sd_ver2_msg
jr init_msg
init_err:
ld hl,spi_init_err_msg
call msgout
ld a,0x0d
call putchar
jp 0x0000
set_cs:
xor a
set S_CS,a ;CS="H"
set S_DI,a ;DI="H"
out (PIOAD),a
ret
cmd0_out:
ld hl,cmd0
call cmd_out
call r1_resp
ret
cmd8_out:
call dummy_data
ld hl,cmd8
call cmd_out
call r1_resp
cp 0x01
ret nz
ld b,4
ld hl,r7
cmd8_r:
call resp
ld (hl),a
inc hl
djnz cmd8_r
ld a,0x01
ret
cmd55_out:
call dummy_data
ld hl,cmd55
call cmd_out
call r1_resp
ret
acmd41_out:
call dummy_data
ld hl,acmd41 ;sd-card Initialize
call cmd_out
call r1_resp
cp 0x00
ret z
call cmd55_out
jr acmd41_out
cmd58_out:
call dummy_data
ld hl,cmd58
call cmd_out
call r1_resp
push af
ld b,4
ld hl,r7
cmd58_r:
call resp
ld (hl),a
inc hl
djnz cmd58_r
cmd58_r_d:
ld hl,r7
ld b,4
cmd58_r_d_loop:
inc hl
djnz cmd58_r_d_loop
ld a,0x0d
call putchar
pop af
ret
cr:
ld a,0x0d
call putchar
ret
spi_read:
call data_in
cp 0x00
ret z
ld hl,yes_msg
call msgout
call getchar
cp "y"
ret nz
call spi_read_go
call cr
ret
spi_write:
call data_in
cp 0x00
ret z
ld hl,yes_msg
call msgout
call getchar
cp "y"
ret nz
call spi_write_go
ret
data_in:
call cr
ld hl,rw_sblock_no_msg
call msgout
call input_hl
cp 0x00
jr z,spi_data_in01
call cr
xor a
ret
spi_data_in01: ;read/write block no
call cr
ld (rw_block_no),hl
ld hl,rw_block_msg
call msgout
call input_hl
cp 0x00
jr z,spi_data_in02
call cr
xor a
ret
spi_data_in02:
call cr
ld (rw_block),hl ;read/write block
ld hl,rw_addr_msg
call msgout
call input_hl
cp 0x00
jr z,spi_data_in03
call cr
xor a
ret
spi_data_in03:
call cr
ld (rw_addr),hl ;read/write addr
ld a,0xff
ret
spi_write_go:
ld hl,(rw_block_no)
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_write4
write_SD_ver2: ;Ver2
ld a,h
ld (cmd25+02),a
ld a,l
ld (cmd25+03),a
spi_write4:
call dummy_data
ld hl,cmd25
call cmd_out
call r1_resp
cp 0x00
jp nz,cmd25_err
ld d,0xff
call spi_8bit
sector_write_loop:
ld hl,(rw_addr)
ld d,0xfc ;cmd25 data token
call spi_8bit
;; spi write 256byte 1st blok
ld b,0x00
cmd25_w: ;1st 256byte write
ld d,(hl)
inc hl
call spi_8bit ; D reg >> write data
djnz cmd25_w
; 2st write 256byte 1st blok
ld b,0x00
cmd25_w2st: ;2st 256byte write
ld d,(hl)
inc hl
call spi_8bit ; D reg >> write data
djnz cmd25_w2st
ld (rw_addr),hl
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 resp
cp 0x00
jr z,cmd25_wch
ld d,0xff
call spi_8bit
ld hl,(rw_block)
dec hl
ld (rw_block),hl
ld a,h ;HL = 0?
or l
jr nz,sector_write_loop
ld d,0xfd ;cmd25 stop token
call spi_8bit
ld d,0xff
call spi_8bit
cmd25_wch2:
call resp
cp 0x00
jr z,cmd25_wch2
call dummy_data
call set_cs ;set CS
ld hl,rw_ok_msg
call msgout
call cr
xor a
ret
cmd25_err:
cmd25_err2:
ld hl,cmd25_err_msg
call msgout
call set_cs ;set CS
ld a,0x01
ret
spi_read_go:
ld hl,(rw_block_no)
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_read01
read_SD_ver2: ;Ver2
ld a,h
ld (cmd18+02),a
ld a,l
ld (cmd18+03),a
spi_read01:
call dummy_data
ld hl,cmd18
call cmd_out
call r1_resp
cp 0x00
jp nz,cmd18_err
sector_read_loop:
cmd18_st:
call r1_resp ;read data start response?
cp 0xfe ;data start byte 0xfe?
jr nz, cmd18_st
ld hl,(rw_addr)
ld b,0x00
cmd18_r:
call resp ;read data
ld (hl),a
inc hl
djnz cmd18_r
; 2st dummy read
ld b,0x00
cmd18_r2st: ;2st 256byte write
call resp
ld (hl),a
inc hl
djnz cmd18_r2st
ld (rw_addr),hl
call resp ;crc Skipping read
call resp ;crc Skipping read
ld hl,(rw_block)
dec hl
ld (rw_block),hl
ld a,h ;HL = 0?
or l
jr nz,sector_read_loop
ld d,0xff ;dummy data 0xff(cs low)
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_wch:
call resp
cp 0x00
jr z,cmd18_wch
call set_cs
xor a
ret
;; 1 sector write
cmd18_err:
cmd18_err2:
ld hl,cmd25_err_msg
call msgout
call set_cs
ld a,0x01
ret
;**************************************************
cmd_out: ;command out
push hl
push bc
ld b,0x06 ;command line word count
cmd_out1:
ld d,(hl) ;command line area
call spi_8bit
inc hl
djnz cmd_out1
pop bc
pop hl
ret
r1_resp:
call resp
cp 0xff
jr z,r1_resp
ret
resp:
ld d,0xff
call spi_8bit
ld a,e
ret
cmd0_msg:
.str "CMD0:"
.db 0x00
dummy_data:
xor a
set S_CS,a ;CS="H"
set S_DI,a ;DI="H"
out (PIOAD),a
ld b,8 ;1clok output
call loopspii
xor a
set S_CS,a ;CS="H"
set S_DI,a ;DI="H"
out (PIOAD),a
ret
loopspii:
res S_CLK,a ;CLK L
out (PIOAD),a
nop
nop
nop
nop
nop
nop
nop
nop
set S_CLK,a ;CLK H
out (PIOAD),a
nop
nop
nop
nop
nop
djnz loopspii
ret
spi_8bit: ;outdata Dreg,inputdata Ereg
push bc
ld c,0x00
ld b,0x08
loopspi:
xor a
rlc d
rra
res S_CLK,a ;CLK L
out (PIOAD),a
nop
nop
nop
nop
nop
nop
set S_CLK,a ;CLK H
out (PIOAD),a
in a,(PIOAD)
rrca
rl e
djnz loopspi
xor a
set S_DI,a ;DI H
out (PIOAD),a
pop bc
ret
msgout:
ld a,(hl) ;(HL)reg is output disp at 0x00
cp 0x00
ret z
call putchar
inc hl
jr msgout
hex_a_disp: ;Areg(HEX) >> putchar(areg)
push af
rrca ;7-4bit Right shift
rrca
rrca
rrca
call hex_ascii ;3-0bit Hex >> Hex ascii(0-F)
call putchar ;7-4bit putchar
pop af
call hex_ascii ;3-0bit Hex >> Hex ascii(0-F)
call putchar ;3-0bit putchar
ret
hex_ascii: ;Areg(HEX) >> Areg(ascii code)
and a,0x0f
cp 0x0a
jr c,ascii0_9
add 0x37
ret
ascii0_9:
add 0x30
ret
input_l: ;input Lreg hex (1byte)
push bc
ld b,0x02
jr input_loop
input_hl: ;input HLreg hex (2byte)
push bc
ld b,0x04
input_loop:
ld hl,0x0000 ;HL reg cler
jr input_hl2
shift_hl:
add hl,hl ;HL reg shift left 4bit
add hl,hl
add hl,hl
add hl,hl
input_hl2:
call getchar ;input ascii hex 1char
call putchar
call ascii_hex ;ascii hex 1char >> Areg
cp 0xff
jr z,input_hl_err
and 0x0f ;Areg low order 4bit >> Lreg
or l
ld l,a
djnz shift_hl
input_hl_end:
pop bc
xor a ;OK >> Areg 0x00
ret
input_hl_err:
ld a,0xff ;NG >> Areg 0xff
pop bc
ret
getchar:in a,(SIOBC)
bit 0,a
jr z,getchar
in a,(SIOBD)
ret
putchar:push af
putchar01:
in a,(SIOBC)
bit 2,a
jr z,putchar01
pop af
out (SIOBD),a
ret
ascii_hex:
sub 0x30
jr c,err_hex
cp 0x0A ;0-9
jr c,hex_1_9
sub 0x11
cp 0x06 ;A-F
jr c,hex_A_F
sub 0x20
cp 0x06 ;a-f
jr nc,err_hex
hex_A_F:
add 0x0a
hex_1_9:
ret
err_hex:ld a,0xff
ret
siobinit:
ld b,12
ld hl,SIO_init_data
sioainit01:
ld a,(HL)
out (SIOBC),a
inc hl
djnz sioainit01
ret
pioainit:
ld b,3
ld hl,PIO_init_data
pioainit01:
ld a,(HL)
out (PIOAC),a
inc hl
djnz pioainit01
ret
SIO_init_data:
.db 0x00,0b00110000 ;WR0=WR0:set ,WR0: err reset
.db 0x01,0b00000000 ;WR0=WR1:set ,WR1: all int reset
.db 0x02,0b00000000 ;WR0=WR2:set ,WR2: CH-B int set
.db 0x03,0b11000001 ;WR0=WR3:set ,WR3: RxBit=8bit Rx=ready
.db 0x04,0b01000100 ;WR0=WR4:set ,WR4: clck=exIN*1 stop=1bit parity=non
.db 0x05,0b11101010 ;WR0=WR5:set ,WR5: char=8bit hardware flow=non Tx=ready
PIO_init_data:
.db 0b11001111 ;PIOAC:set 0xcf: Mode3 (bit mode)
.db 0b00011111 ;PIOAC:set 0x1f: D4-D0=input D7-D5=output
.db 0b00000000 ;PIOAC:set 0x00: interrupt disables
sdhc_msg:
.str ":SDHC"
.db 0x0d
.db 0x00
sd_ver2_msg:
.str ":SD Ver2"
.db 0x0d
.db 0x00
spi_init_err_msg:
.str "Spi Init Err"
.db 0x0d
.db 0x00
cmd17_err_msg:
.str "cmd17 Err"
.db 0x0d
.db 0x00
cmd24_err_msg:
.str "cmd24 Err"
.db 0x0d
.db 0x00
cmd25_err_msg:
.str "cmd25 Err"
.db 0x0d
.db 0x00
rw_sblock_no_msg:
.str " read/write start block No:"
.db 0x00
rw_block_msg:
.str " read/write block:"
.db 0x00
rw_addr_msg:
.str " read/write addr:"
.db 0x00
yes_msg:
.str " Yes:"
.db 0x00
rw_ok_msg:
.str " read/write ok!"
.db 0x00
rwg_msg:
.str " read >> r :write >> w CP/M boot >> b key in:"
.db 0x00
cpm_boot_msg:
.db 0x0d
.str " CP/M system boot !"
.db 0x0d
.db 0x00
cmd0: ;;48bit(6byte) Fixed length
.db 0x40 ;;command index
.db 0x00 ;;argument
.db 0x00 ;;argument
.db 0x00 ;;argument
.db 0x00 ;;argument
.db 0x95 ;;CRC
cmd8:
.db 0x48
.db 0x00
.db 0x00
.db 0x01
.db 0xaa
.db 0x87
cmd55:
.db 0x77
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
acmd41:
.db 0x69
.db 0x40
.db 0xff
.db 0x80
.db 0x00
.db 0xff
cmd58:
.db 0x7a
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
cmd16:
.db 0x50
.db 0x00
.db 0x00
.db 0x02
.db 0x00
.db 0xff
cmd17:
.db 0x51
.db 0x00
.db 0x00
.db 0x02
.db 0x00
.db 0xff
cmd18:
.db 0x52
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
cmd12:
.db 0x4c
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
cmd23:
.db 0x57
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
cmd24:
.db 0x58
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
cmd25:
.db 0x59
.db 0x00
.db 0x00
.db 0x00
.db 0x00
.db 0xff
r7:
.ds 4
rw_block_no:
.ds 2
rw_block:
.ds 2
rw_addr:
.ds 2
.end