以前共立電子で入手したジャンクのZ80ボード(KBC-Z11)で、FIG-Forthを動かしたがそのままでは勿体ない。
(KBC-Z11でソフトウエアUART https://blog.goo.ne.jp/ishihara-h/e/7885842a5413c0d807481e0e3e8ba3c7)
このZ80ボードは先頭32KBがROMだし、RAMも32KBなのでCP/Mも難しい。一応Forthを動かしたと行っても、Forthのプログラムを保存することも出来ない。
Forthはトランジスタ技術別冊マイコンピュータNo.3「すぐ動くFORTH」を参考に移植した経験もあり、ディスクのセクタリード、ライトさえできれば
なんとかなる。そこでmicro SDカードをアクセス出来るようにしてみた。
SDカードへのアクセスはWeb上に記事が多数あるがやはり自分で動かすとなると難しい。
まずFIG-ForthレベルでSPIアクセスして動くかチェック。(とりあえず動かすことを考え速度などは考慮外)
TMPZ84C011のPA($50)を上位からCS,SCK,MOSI,MISO,xx,xx,RXD,TXDと割り振り以下のPGで動作。
(下2ビットは以前のソフトウエアUARTで使用しているところ)
注意点
・通信はPuttyを使って、以前作ったソフトウエアUARTでアクセスする。PGはPuttyの画面に貼り付けて送信と思っていたが、ソフトウエアUARTではフローコントロールしていないため、取りこぼしてしまう。
・screenを使用してと思ったが、ウエイトをかける方法がわからない。
・minicomを使用してと思ったが、調べてみるとminicom付属?の ascii-xfrがあることがわかり、
結局 コンソールの入出力はputtyを使用しつつ、必要な場合に ascii-xfr -s -l 400 -c 5 forth_sd.txt > /dev/ttyUSB0 として、line delay400msec, character delay 5ms としてプログラムを送り込むことにした。
(2023/6/21 追記)
実はascii-xftは改行を CR+LFで送り込むが、Forth側はキーボード入力を想定しているのでCRだけを期待している。
今回エラーにならなかったのは、CRを受信した段階でForthが実行を始め、次のLFを無視していたから。
ハードウエアでのシリアル入力を行うシステムでは、2行目以降(最初にLFがつくので)エラーになる。
ascii-xftでリダイレクトの入力が出来ればよかったのだが、ないのでいちいち
tr '\n' '\r' < orig.txt > nolf.txt としてCRだけにする必要があった。
------(2023/7/23 追記)---------
良く考えればrubyで簡単に出来た
---xfr.tb---
while l = gets
l.chomp.each_char do |x|
print x
sleep(0.005)
end
print "\r"
sleep(0.4)
end
------------
これで ruby xfr.rb < ファイル > /dev/ttyUSB0 とすれば良かった。
---------------------------------------
以下がプログラムで若干デバッグの痕跡が残ったままになっている。
最後の ' RW CFA DUP 956D ! 95B9 ! は、Forthの「R/W」を呼び出しているBUFFERとBLOCK 箇所を直接書き換えている。
VLISTやLOADが動くことが確認できたので、今後アセンブラに書き換えてROMに書き込む予定
===プログラム===
COLD
HEX
10 CONSTANT MISOBIT
20 CONSTANT MOSIBIT
40 CONSTANT SCKBIT
80 CONSTANT CSBIT
50 CONSTANT PA
54 CONSTANT PA_C
: PA@ PA P@ ;
: PA! PA P! ;
: CS_H PA@ CSBIT OR PA! ;
: CS_L PA@ [ CSBIT FF XOR ] LITERAL AND PA! ;
: SCK_H PA@ SCKBIT OR PA! ;
: SCK_L PA@ [ SCKBIT FF XOR ] LITERAL AND PA! ;
: MOSI_H PA@ MOSIBIT OR PA! ;
: MOSI_L PA@ [ MOSIBIT FF XOR ] LITERAL AND PA! ;
: SPI_H MOSI_H SCK_L SCK_H ;
: SPI_L MOSI_L SCK_L SCK_H ;
: SPI_8_W
8 0 DO DUP 80 AND IF SPI_H ELSE SPI_L THEN 2* LOOP DROP ;
: SPI_R SCK_L SCK_H PA@ MISOBIT AND ;
: SPI_8_R 0 8 0 DO 2* SPI_R IF 1 OR THEN LOOP ;
0 VARIABLE LCOUNT
: LOOP_RES 0 LCOUNT ! ;
: LOOP_CHECK LCOUNT @ 100 > IF ." TIMEOUT" ABORT THEN 1 LCOUNT +! ;
: SKIPRESULT LOOP_RES BEGIN LOOP_CHECK SPI_8_R DUP 7F > WHILE DROP REPEAT ;
: SKIPFF BEGIN SPI_8_R DUP FF = WHILE DROP REPEAT ;
: ERRMSG DUP 40 AND IF ." PARAMETER ERROR " THEN
DUP 20 AND IF ." ADDRESS ERROR " THEN
DUP 10 AND IF ." ERASE SEQUENSE ERROR " THEN
DUP 8 AND IF ." COMMAND CRC ERROR " THEN
DUP 4 AND IF ." ILLIGAL COMMAND " THEN
DUP 2 AND IF ." ERASE RESET " THEN
DUP 1 AND IF ." IN IDLE STATE " THEN
DROP CR ;
: SDERROR ." SDERROR " DUP . ." " ERRMSG CR ABORT ;
: ERRMSG2 1F AND DUP B = IF ." DATA REJECTED DUE TO A CRC ERROR "
ELSE DUP D = IF ." DATA REJECTED DUE TO A WRITE ERROR "
ELSE DUP ." ? " .
THEN THEN DROP CR ;
: SDERROR2 ." ERROR2 " DUP . ." " ERRMSG2 CR ABORT ;
: CHKRESULT SKIPRESULT
SWAP OVER <> IF SDERROR THEN DROP ;
: R1 CHKRESULT ;
: R3 CHKRESULT 4 0 DO SPI_8_R . LOOP ;
: R7 R3 ;
: CMD 6 0 DO SPI_8_W LOOP ;
: CMD0 CS_L 95 00 00 00 00 40 CMD 1 R1 CS_H ;
: CMD8 CS_L 87 AA 01 00 00 48 CMD 1 R7 CS_H ; ( R7 VOLTAGE )
: CMD58S FF 00 00 00 00 7A CMD ;
: CMD58_1R CS_L CMD58S 1 R3 CS_H ; ( R3 READ OCR )
: CMD58_0R CS_L CMD58S 0 R3 CS_H ; ( R3 READ OCR )
: CMD55 FF 00 00 00 00 77 CMD
SKIPRESULT 1 = IF ELSE SDERROR THEN ;
: ACMD41 FF 00 80 FF 40 69 CMD SKIPRESULT ;
: CMDACMD41 CS_L BEGIN CMD55 ACMD41 0= END CS_H ;
( APP_SEND_OP_COND )
0 VARIABLE BUF 200 ALLOT
: BUF_INIT BUF 2 + BUF ! ;
: BUF_NEXT BUF @ 1+ BUF ! ;
: WR_BUF BUF @ C! BUF_NEXT ;
: RD_BUF BUF @ C@ BUF_NEXT ;
: DUMP_16 10 0 DO RD_BUF 3 .R LOOP ;
: DUMP_BUF BUF_INIT CR 20 0 DO I 5 .R DUMP_16 CR LOOP ;
: CMD17 CS_L 51 CMD 0 R1
BEGIN SKIPFF DUP DUP 0 > SWAP 20 < AND
IF ." ERRORTOKEN " . ABORT THEN FE = END
200 0 DO SPI_8_R WR_BUF LOOP
SPI_8_R DROP SPI_8_R DROP CS_H ; ( SKIP CRC )
: CMD24 CS_L 58 CMD 0 R1 SPI_8_R DROP ( SKIP )
FE SPI_8_W ( DATA TOKEN )
200 0 DO RD_BUF SPI_8_W LOOP
FF SPI_8_W FF SPI_8_W ( CRC )
SPI_8_R DUP 1F AND 5 = IF DROP BEGIN SPI_8_R FF = END
ELSE ERRMSG2 THEN CS_H ; ( DATA RESPONSE )
( : PRPAR ." PARA:" DUP U. >R DUP U. >R DUP U. >R DUP U. R> R> R> ; )
: PRPAR ;
: INIT_SD E1 PA_C P! E1 PA P! A 0 DO FF SPI_8_W LOOP ;
: SECRD >R >R >R >R FF R> R> R> R> BUF_INIT PRPAR CMD17 ;
( : SECRD_TEST SECRD DUMP_BUF ; )
: SECWR >R >R >R >R FF R> R> R> R> BUF_INIT PRPAR CMD24 ;
INIT_SD
CMD0
CMD8
CMD58_1R
CMDACMD41
CMD58_0R
( 0 VARIABLE USE )
: RW ( addr blk f --- )
( ." RW:" DUP . >R 2DUP U. U. R> PRINT FLAG BLK ADDR )
USE @ >R SWAP 2 * ROT USE !
2 0 DO DUP DUP FF AND SWAP 100 / >R >R OVER
1 = IF R> R> 0 0 SECRD BUF 2+ USE @ 200 CMOVE
ELSE USE @ BUF 2+ 200 CMOVE R> 0 R> 0 SECWR
THEN
1+ 200 USE +! LOOP DROP DROP
R> USE ! ;
( : RW DROP DROP DROP ; )
' RW CFA DUP 956D ! 95B9 !
==================
===Forth 本体 以下のRSLWを書き換え===
9536 86 DB 86H ; BUFFER
9537 4255464645 DB 'BUFFE'
953C D2 DB 'R'+80H
953D 1495 DW DRONE-6
953F DF863594 BUFFE: DW DOCOL,USE
9543 6586E185 DW AT,DUP
9547 E384 DW TOR
9549 A794 BUFF1 DW PBUF ; WON'T WORK IF SINGLE BUFFER
954B 9481FCFF DW ZBRAN,BUFF1-$
954F 35949C86 DW USE,STORE
9553 0D856586 DW RR,AT
9557 3785 DW ZLESS
9559 94811400 DW ZBRAN,BUFF2-$
955D 0D85BE88 DW RR,TWOP
9561 0D856586 DW RR,AT
9565 5881FF7F DW LIT,7FFFH
9569 2B845E87 DW ANDD,ZERO
956D 6897 DW RSLW
956F 0D859C86 BUFF2 DW RR,STORE
9573 0D854094 DW RR,PREV
9577 9C86F984 DW STORE,FROMR
957B BE88AF84 DW TWOP,SEMIS
;
957F 85 DB 85H ; BLOCK
9580 424C4F43 DB 'BLOC'
9584 CB DB 'K'+80H
9585 3695 DW BUFFE-9
9587 DF864F88 BLOCK DW DOCOL,OFSET
958B 65864985 DW AT,PLUS
958F E3844094 DW TOR,PREV
9593 6586E185 DW AT,DUP
9597 65860D85 DW AT,RR
959B 5089 DW SUBB
959D E1854985 DW DUP,PLUS
95A1 94813400 DW ZBRAN,BLOC1-$
95A5 A7941B85 BLOC2 DW PBUF,ZEQU
95A9 94811400 DW ZBRAN,BLOC3-$
95AD C7850D85 DW DROP,RR
95B1 3F95E185 DW BUFFE,DUP
95B5 0D856687 DW RR,ONE
95B9 6897 DW RSLW
95BB 6E875089 DW TWO,SUBB
95BF E1856586 BLOC3 DW DUP,AT
95C3 0D855089 DW RR,SUBB
95C7 E1854985 DW DUP,PLUS
95CB 1B85 DW ZEQU
95CD 9481D6FF DW ZBRAN,BLOC2-$
95D1 E1854094 DW DUP,PREV
95D5 9C86 DW STORE
95D7 F984C785 BLOC1 DW FROMR,DROP
95DB BE88AF84 DW TWOP,SEMIS
============
特記事項
・導入していたFIG-Forthだがバグあり。?DUPで暴走する。(-DUPの別名として定義しているようだが間違っている。まあ -DUPが使えるから良いけど)
・みかんとひよどりさん「https://twitter.com/hiyodori5/status/1404048603262291970」の記事を参考にしたが、コネクタに一部間違いがあった。KBC-Z11コネクタCN2 Pin1はGNDではないので注意。パターンがコネクタの下なので追いかけられないが、導通チェッカで確認するとオープンに見える。
・SRS232CはLSBから送受信だが、SDカードのSPIはMSBから送受信。Web上に多数説明があるのに、当初完全な思い込みで間違った。
参考URL
ELMさん「MMC/SDCの使いかた」http://elm-chan.org/docs/mmc/mmc.html
中日電工さん「SDカードインターフェースの製作」https://userweb.alles.or.jp/chunichidenko/sdif1.html
(KBC-Z11でソフトウエアUART https://blog.goo.ne.jp/ishihara-h/e/7885842a5413c0d807481e0e3e8ba3c7)
このZ80ボードは先頭32KBがROMだし、RAMも32KBなのでCP/Mも難しい。一応Forthを動かしたと行っても、Forthのプログラムを保存することも出来ない。
Forthはトランジスタ技術別冊マイコンピュータNo.3「すぐ動くFORTH」を参考に移植した経験もあり、ディスクのセクタリード、ライトさえできれば
なんとかなる。そこでmicro SDカードをアクセス出来るようにしてみた。
SDカードへのアクセスはWeb上に記事が多数あるがやはり自分で動かすとなると難しい。
まずFIG-ForthレベルでSPIアクセスして動くかチェック。(とりあえず動かすことを考え速度などは考慮外)
TMPZ84C011のPA($50)を上位からCS,SCK,MOSI,MISO,xx,xx,RXD,TXDと割り振り以下のPGで動作。
(下2ビットは以前のソフトウエアUARTで使用しているところ)
注意点
・通信はPuttyを使って、以前作ったソフトウエアUARTでアクセスする。PGはPuttyの画面に貼り付けて送信と思っていたが、ソフトウエアUARTではフローコントロールしていないため、取りこぼしてしまう。
・screenを使用してと思ったが、ウエイトをかける方法がわからない。
・minicomを使用してと思ったが、調べてみるとminicom付属?の ascii-xfrがあることがわかり、
結局 コンソールの入出力はputtyを使用しつつ、必要な場合に ascii-xfr -s -l 400 -c 5 forth_sd.txt > /dev/ttyUSB0 として、line delay400msec, character delay 5ms としてプログラムを送り込むことにした。
(2023/6/21 追記)
実はascii-xftは改行を CR+LFで送り込むが、Forth側はキーボード入力を想定しているのでCRだけを期待している。
今回エラーにならなかったのは、CRを受信した段階でForthが実行を始め、次のLFを無視していたから。
ハードウエアでのシリアル入力を行うシステムでは、2行目以降(最初にLFがつくので)エラーになる。
ascii-xftでリダイレクトの入力が出来ればよかったのだが、ないのでいちいち
tr '\n' '\r' < orig.txt > nolf.txt としてCRだけにする必要があった。
------(2023/7/23 追記)---------
良く考えればrubyで簡単に出来た
---xfr.tb---
while l = gets
l.chomp.each_char do |x|
print x
sleep(0.005)
end
print "\r"
sleep(0.4)
end
------------
これで ruby xfr.rb < ファイル > /dev/ttyUSB0 とすれば良かった。
---------------------------------------
以下がプログラムで若干デバッグの痕跡が残ったままになっている。
最後の ' RW CFA DUP 956D ! 95B9 ! は、Forthの「R/W」を呼び出しているBUFFERとBLOCK 箇所を直接書き換えている。
VLISTやLOADが動くことが確認できたので、今後アセンブラに書き換えてROMに書き込む予定
===プログラム===
COLD
HEX
10 CONSTANT MISOBIT
20 CONSTANT MOSIBIT
40 CONSTANT SCKBIT
80 CONSTANT CSBIT
50 CONSTANT PA
54 CONSTANT PA_C
: PA@ PA P@ ;
: PA! PA P! ;
: CS_H PA@ CSBIT OR PA! ;
: CS_L PA@ [ CSBIT FF XOR ] LITERAL AND PA! ;
: SCK_H PA@ SCKBIT OR PA! ;
: SCK_L PA@ [ SCKBIT FF XOR ] LITERAL AND PA! ;
: MOSI_H PA@ MOSIBIT OR PA! ;
: MOSI_L PA@ [ MOSIBIT FF XOR ] LITERAL AND PA! ;
: SPI_H MOSI_H SCK_L SCK_H ;
: SPI_L MOSI_L SCK_L SCK_H ;
: SPI_8_W
8 0 DO DUP 80 AND IF SPI_H ELSE SPI_L THEN 2* LOOP DROP ;
: SPI_R SCK_L SCK_H PA@ MISOBIT AND ;
: SPI_8_R 0 8 0 DO 2* SPI_R IF 1 OR THEN LOOP ;
0 VARIABLE LCOUNT
: LOOP_RES 0 LCOUNT ! ;
: LOOP_CHECK LCOUNT @ 100 > IF ." TIMEOUT" ABORT THEN 1 LCOUNT +! ;
: SKIPRESULT LOOP_RES BEGIN LOOP_CHECK SPI_8_R DUP 7F > WHILE DROP REPEAT ;
: SKIPFF BEGIN SPI_8_R DUP FF = WHILE DROP REPEAT ;
: ERRMSG DUP 40 AND IF ." PARAMETER ERROR " THEN
DUP 20 AND IF ." ADDRESS ERROR " THEN
DUP 10 AND IF ." ERASE SEQUENSE ERROR " THEN
DUP 8 AND IF ." COMMAND CRC ERROR " THEN
DUP 4 AND IF ." ILLIGAL COMMAND " THEN
DUP 2 AND IF ." ERASE RESET " THEN
DUP 1 AND IF ." IN IDLE STATE " THEN
DROP CR ;
: SDERROR ." SDERROR " DUP . ." " ERRMSG CR ABORT ;
: ERRMSG2 1F AND DUP B = IF ." DATA REJECTED DUE TO A CRC ERROR "
ELSE DUP D = IF ." DATA REJECTED DUE TO A WRITE ERROR "
ELSE DUP ." ? " .
THEN THEN DROP CR ;
: SDERROR2 ." ERROR2 " DUP . ." " ERRMSG2 CR ABORT ;
: CHKRESULT SKIPRESULT
SWAP OVER <> IF SDERROR THEN DROP ;
: R1 CHKRESULT ;
: R3 CHKRESULT 4 0 DO SPI_8_R . LOOP ;
: R7 R3 ;
: CMD 6 0 DO SPI_8_W LOOP ;
: CMD0 CS_L 95 00 00 00 00 40 CMD 1 R1 CS_H ;
: CMD8 CS_L 87 AA 01 00 00 48 CMD 1 R7 CS_H ; ( R7 VOLTAGE )
: CMD58S FF 00 00 00 00 7A CMD ;
: CMD58_1R CS_L CMD58S 1 R3 CS_H ; ( R3 READ OCR )
: CMD58_0R CS_L CMD58S 0 R3 CS_H ; ( R3 READ OCR )
: CMD55 FF 00 00 00 00 77 CMD
SKIPRESULT 1 = IF ELSE SDERROR THEN ;
: ACMD41 FF 00 80 FF 40 69 CMD SKIPRESULT ;
: CMDACMD41 CS_L BEGIN CMD55 ACMD41 0= END CS_H ;
( APP_SEND_OP_COND )
0 VARIABLE BUF 200 ALLOT
: BUF_INIT BUF 2 + BUF ! ;
: BUF_NEXT BUF @ 1+ BUF ! ;
: WR_BUF BUF @ C! BUF_NEXT ;
: RD_BUF BUF @ C@ BUF_NEXT ;
: DUMP_16 10 0 DO RD_BUF 3 .R LOOP ;
: DUMP_BUF BUF_INIT CR 20 0 DO I 5 .R DUMP_16 CR LOOP ;
: CMD17 CS_L 51 CMD 0 R1
BEGIN SKIPFF DUP DUP 0 > SWAP 20 < AND
IF ." ERRORTOKEN " . ABORT THEN FE = END
200 0 DO SPI_8_R WR_BUF LOOP
SPI_8_R DROP SPI_8_R DROP CS_H ; ( SKIP CRC )
: CMD24 CS_L 58 CMD 0 R1 SPI_8_R DROP ( SKIP )
FE SPI_8_W ( DATA TOKEN )
200 0 DO RD_BUF SPI_8_W LOOP
FF SPI_8_W FF SPI_8_W ( CRC )
SPI_8_R DUP 1F AND 5 = IF DROP BEGIN SPI_8_R FF = END
ELSE ERRMSG2 THEN CS_H ; ( DATA RESPONSE )
( : PRPAR ." PARA:" DUP U. >R DUP U. >R DUP U. >R DUP U. R> R> R> ; )
: PRPAR ;
: INIT_SD E1 PA_C P! E1 PA P! A 0 DO FF SPI_8_W LOOP ;
: SECRD >R >R >R >R FF R> R> R> R> BUF_INIT PRPAR CMD17 ;
( : SECRD_TEST SECRD DUMP_BUF ; )
: SECWR >R >R >R >R FF R> R> R> R> BUF_INIT PRPAR CMD24 ;
INIT_SD
CMD0
CMD8
CMD58_1R
CMDACMD41
CMD58_0R
( 0 VARIABLE USE )
: RW ( addr blk f --- )
( ." RW:" DUP . >R 2DUP U. U. R> PRINT FLAG BLK ADDR )
USE @ >R SWAP 2 * ROT USE !
2 0 DO DUP DUP FF AND SWAP 100 / >R >R OVER
1 = IF R> R> 0 0 SECRD BUF 2+ USE @ 200 CMOVE
ELSE USE @ BUF 2+ 200 CMOVE R> 0 R> 0 SECWR
THEN
1+ 200 USE +! LOOP DROP DROP
R> USE ! ;
( : RW DROP DROP DROP ; )
' RW CFA DUP 956D ! 95B9 !
==================
===Forth 本体 以下のRSLWを書き換え===
9536 86 DB 86H ; BUFFER
9537 4255464645 DB 'BUFFE'
953C D2 DB 'R'+80H
953D 1495 DW DRONE-6
953F DF863594 BUFFE: DW DOCOL,USE
9543 6586E185 DW AT,DUP
9547 E384 DW TOR
9549 A794 BUFF1 DW PBUF ; WON'T WORK IF SINGLE BUFFER
954B 9481FCFF DW ZBRAN,BUFF1-$
954F 35949C86 DW USE,STORE
9553 0D856586 DW RR,AT
9557 3785 DW ZLESS
9559 94811400 DW ZBRAN,BUFF2-$
955D 0D85BE88 DW RR,TWOP
9561 0D856586 DW RR,AT
9565 5881FF7F DW LIT,7FFFH
9569 2B845E87 DW ANDD,ZERO
956D 6897 DW RSLW
956F 0D859C86 BUFF2 DW RR,STORE
9573 0D854094 DW RR,PREV
9577 9C86F984 DW STORE,FROMR
957B BE88AF84 DW TWOP,SEMIS
;
957F 85 DB 85H ; BLOCK
9580 424C4F43 DB 'BLOC'
9584 CB DB 'K'+80H
9585 3695 DW BUFFE-9
9587 DF864F88 BLOCK DW DOCOL,OFSET
958B 65864985 DW AT,PLUS
958F E3844094 DW TOR,PREV
9593 6586E185 DW AT,DUP
9597 65860D85 DW AT,RR
959B 5089 DW SUBB
959D E1854985 DW DUP,PLUS
95A1 94813400 DW ZBRAN,BLOC1-$
95A5 A7941B85 BLOC2 DW PBUF,ZEQU
95A9 94811400 DW ZBRAN,BLOC3-$
95AD C7850D85 DW DROP,RR
95B1 3F95E185 DW BUFFE,DUP
95B5 0D856687 DW RR,ONE
95B9 6897 DW RSLW
95BB 6E875089 DW TWO,SUBB
95BF E1856586 BLOC3 DW DUP,AT
95C3 0D855089 DW RR,SUBB
95C7 E1854985 DW DUP,PLUS
95CB 1B85 DW ZEQU
95CD 9481D6FF DW ZBRAN,BLOC2-$
95D1 E1854094 DW DUP,PREV
95D5 9C86 DW STORE
95D7 F984C785 BLOC1 DW FROMR,DROP
95DB BE88AF84 DW TWOP,SEMIS
============
特記事項
・導入していたFIG-Forthだがバグあり。?DUPで暴走する。(-DUPの別名として定義しているようだが間違っている。まあ -DUPが使えるから良いけど)
・みかんとひよどりさん「https://twitter.com/hiyodori5/status/1404048603262291970」の記事を参考にしたが、コネクタに一部間違いがあった。KBC-Z11コネクタCN2 Pin1はGNDではないので注意。パターンがコネクタの下なので追いかけられないが、導通チェッカで確認するとオープンに見える。
・SRS232CはLSBから送受信だが、SDカードのSPIはMSBから送受信。Web上に多数説明があるのに、当初完全な思い込みで間違った。
参考URL
ELMさん「MMC/SDCの使いかた」http://elm-chan.org/docs/mmc/mmc.html
中日電工さん「SDカードインターフェースの製作」https://userweb.alles.or.jp/chunichidenko/sdif1.html