『 Intel 8008 』 と 『 Datapoint 2200 』 | Shimanetのブログ

『 Intel 8008 』 と 『 Datapoint 2200 』

i8008はx86命令セットの元になった8ビットCPUのi8080の原型でありZ80の祖先とも言える。
DP2200はプログラム可能なインテリジェント端末で最初期のパソコンの原型でもある。

そしてi8008はDP2200の命令セットをICのシングルチップCPUで実装するために
データポイント社からインテル社に製造を委託されたものが原型である。

しかしi8008の原型のICチップはCPUとしてDP2200には使われなかった・・・

NASAの2人の技術者フィル・レイとガス・ロシュが1968年に創業した
Computer Terminal Corporation は最初の製品であるビデオ表示端末の
Datapoint 3300 を1969年に出荷し生産能力を上回る程の注文を受けた。
後の1973年には社名を製品名と同じ Datapoint Corporation に変更する。

これはテレタイプ端末の ASR-33 などの代替品として紙にデータ印字するのではなく
テレビモニタ画面にデータ表示するタイプのコンピューターターミナルで
33の100倍よい製品として3300と命名されたようだ。

だが顧客からは高価な他の会社の異なる専用端末などに
エミュレートして使える汎用端末を求める声が多く寄せられ
各種端末のエミュレータープログラムを読み込ませて実行できる
小さなコンピューターを開発する事になり
データポイントの技術者のビクター・プアーズとハリー・パイルがDP2200命令セットを設計し
それを半導体メーカーのインテルとT.I.にICのシングルチップのCPUとして
製造を委託する事になった。

しかし先に完成したT.I.のTMC1795は電源仕様に問題があって安定動作が難しく
完成が遅れたインテルの1201もデータポイントが要求した性能に届かず
結局はデータポイントの技術者のゲイリー・アスベルがDP2200命令セットをTTL回路で設計し
ジャック・フラッサニートが筐体デザインをして Datapoint 2200 VersionⅠ として
1970年4月にプロトタイプが完成した。

1971年に製品の出荷が始まったDP2200は想定外の使われ方をされるようになった。
メインメモリが最大8Kバイトしかなく外部記録装置はカセットテープデッキと
オプションのプリンターだけのシステムに顧客はアセンブラモニタを購入し
給与計算や薬価計算などのプログラムを自前で組んで使用するようになった。

しばらくしてD-RAMチップが製造・供給されるようになり、
メモリ周りを改善した Datapoint 2200 VersionⅡ が1972年に開発された。
これによって明らかに性能が劣るインテルの1201はDP2200に使われる事が無くなり
インテルはこれを改良して単独CPUチップの8008として販売する事になった。


参照資料
Datapoint.Org http://www.datapoint.org/
Datapoint Corporation History
http://www.fundinguniverse.com/company-histories/Datapoint-Corporation-Company-History.html
Vic Poor Interview http://www.computerhistory.org/collections/accession/102658337


Datapoint 2200 の仕様 (DATAPOINT 2200 REFERENCE MANUAL VersionⅠ and VersionⅡ より)


システム構成(V1/V2共通)

CRT: 7x3.5インチ・グリーンディスプレイ、80x12行960文字、94キャラクターASCⅡセット
キーボード:41+11+5 フルキーボード
カセットテープデッキ: プロセッサーからコントロール可能な2台を内蔵
電源: AC115V、60Hz、180W

その他のオプション:
RS-232などのコミュニケーションアダプタ6種、プリンタ2種、
リールテープ、2MBリムーバルディスク、80columnカードリーダ


V1:プログラムレジスタ P - 13bit
(最大8,192ワード recirculating MOS shift register drum type memory ※シリアルメモリ)

V2:プログラムレジスタ P - 14bit
(最大16,384ワード random access dynamic MOS memory ※8ビットパラレルメモリ)

V1:プッシュダウンスタック S - ハードウェア15段(※Aug71のマニュアルだと7段)
V2:プッシュダウンスタック S - ハードウェア16段


8bitレジスタ(rシンボル - rs/rd - 機能)
/ V2はαとβのレジスタ・フラグのグループセットを切り替える事が可能

A - 0(000) - アキュームレーターレジスタ
B - 1(001) - 汎用レジスタ
C - 2(010) - 汎用レジスタ
D - 3(011) - 汎用レジスタ
E - 4(100) - 汎用レジスタ
H - 5(101) - メモリデータリファレンスレジスタ(MSP)
L - 6(110) - メモリデータリファレンスレジスタ(LSP)

M - 7(111) - HLレジスタペアで示されるメモリデータレジスタ


コントロールフリップフロップ(fcシンボル - c - 機能)
/ V2はαとβのレジスタ・フラグのグループセットを切り替える事が可能

C - 0(00) - Cf キャリー(ボロー) 演算結果の9桁目にキャリーまたはボローが発生した場合にセット
Z - 1(01) - Zf ゼロ 演算結果が0の場合にセット
S - 2(10) - Sf サイン 演算結果のbit-7(8桁目/MSB)がセット
P - 3(11) - Pf パリティ 演算結果の1になったビット数が偶数個の場合にセット


命令コード MNEMONIC TIMING(V1/V2/参考8008-0.5MHz μsec.)

00 ddd 110 L(rd) imm 16/3.2/32 LOAD IMMEDIATE rd←imm (d=0-6,d=7のMレジスタはX/未定義)
11 ddd sss L(rd)(rs) 16/3.2/20 LOAD rd←rs (s/d=0-6,s/d=0-6,s=d=0はNOPで他s=dは未定義)
11 111 sss LM(rs) 520/4.8/28 LOAD M←rs (s=0-6,s=7のMレジスタはX/HALT)
11 ddd 111 L(rd)M 520/4.8/32 LOAD rd←M (d=0-6,d=7のMレジスタはX/HALT)

00 000 100 AD imm 16/4.8/32 ADD IMMEDIATE A←A+imm
10 000 sss AD(rs) 16/3.2/20 ADD A←A+rs (s=0-6)
10 000 111 ADM 520/4.8/32 ADD A←A+M

00 001 100 AC imm 16/4.8/32 ADD W/CARRY IMM A←A+imm+Cf
10 001 sss AC(rs) 16/3.2/20 ADD W/CARRY A←A+rs+Cf (s=0-6)
10 001 111 ACM 520/4.8/32 ADD W/CARRY A←A+M+Cf

00 010 100 SU imm 16/4.8/32 SUB IMMEDIATE A←A-imm
10 010 sss SU(rs) 16/3.2/20 SUB A←A-rs (s=0-6)
10 010 111 SUM 520/4.8/32 SUB A←A-M

00 011 100 SB imm 16/4.8/32 SUB W/BORROW IMM A←A-imm-Cf
10 011 sss SB(rs) 16/3.2/20 SUB W/BORROW A←A-rs-Cf (s=0-6)
10 011 111 SBM 520/4.8/32 SUB W/BORROW A←A-M-Cf

00 100 100 ND imm 16/4.8/32 AND IMMEDIATE A←A∧imm
10 100 sss ND(rs) 16/3.2/20 AND A←A∧rs (s=0-6)
10 100 111 NDM 520/4.8/32 AND A←A∧M

00 101 100 XR imm 16/4.8/32 EX-OR IMMEDIATE A←A≠imm
10 101 sss XR(rs) 16/3.2/20 EX-OR A←A≠rs (s=0-6)
10 101 111 XRM 520/4.8/32 EX-OR A←A≠M

00 110 100 OR imm 16/4.8/32 OR IMMEDIATE A←A∨imm
10 110 sss OR(rs) 16/3.2/20 OR A←A∨rs (s=0-6)
10 110 111 ORM 520/4.8/32 OR A←A∨M

00 111 100 CP imm 16/4.8/32 COMPARE IMMEDIATE A-imm
10 111 sss CP(rs) 16/3.2/20 COMPARE A-rs (s=0-6)
10 111 111 CPM 520/4.8/32 COMPARE A-M

01 000 100 JMP lsp msp ※/6.4/44 JUMP P←(msp,lsp) ※Variable
01 0cc 000 JF(c) lsp msp ※/6.4/44 JUMP IF fc=FALSE P←(msp,lsp) ※Variable
24/4.8/36 Otherwise P←P+3
01 1cc 000 JT(c) lsp msp ※/6.4/44 JUMP IF fc=TRUE P←(msp,lsp) ※Variable
24/4.8/36 Otherwise P←P+3

01 000 110 CALL lsp msp ※/6.4/44 SUBROUTINE CALL S←P+3, P←(msp,lsp) ※Variable
01 0cc 010 CF(c) lsp msp ※/6.4/44 S-CALL IF fc=FALSE S←P+3,P←(msp,lsp) ※Variable
24/4.8/36 Otherwise P←P+3
01 1cc 010 CT(c) lsp msp ※/6.4/44 S-CALL IF fc=TRUE S←P+3,P←(msp,lsp) ※Variable
24/4.8/36 Otherwise P←P+3

00 000 111 RETURN / RET ※/3.2/20 SUBROUTINE RETURN P←S ※Variable
00 0cc 011 RF(c) ※/3.2/20 SUBROUTINE RETURN IF fc=FALSE P←S ※Variable
16/3.2/12 Otherwise P←P+1
00 1cc 011 RT(c) ※/3.2/20 SUBROUTINE RETURN IF fc=TRUE P←S ※Variable
16/3.2/12 Otherwise P←P+1

00 000 010 SLC 16/3.2/20 SHIFT LEFT CIRCULAR
Cf←A7,Am+1←Am,A0←A7 (m=6-0)
00 001 010 SRC 16/3.2/20 SHIFT RIGHT CIRCULAR
A0→A7,Am→Am-1,A0→Cf (m=7-1)

11 000 000 NOP 16/3.2/20 NO OPERATION

00 000 000 HALT ??/??/16 HALT (operation resumes at P+1)
00 000 001
11 111 111

01 000 001 INPUT / IN 16/9.6/32 INPUT A←(I/O Bus)
01 xxxxx 1 EX (exp) 16/9.6/24 EXTERNAL COMMAND
01 01000 1 EX ADR C-NUMBER 1 Address
01 01001 1 EX STATUS C-NUMBER 2 Sense Status
01 01010 1 EX DATA C-NUMBER 3 Sense Data
01 01011 1 EX WRITE C-NUMBER 4 Write Strobe
01 01100 1 EX COM1 C-NUMBER 5 Command 1
01 01101 1 EX COM2 C-NUMBER 6 Command 2
01 01110 1 EX COM3 C-NUMBER 7 Command 3
01 01111 1 EX COM4 C-NUMBER 8 Command 4
01 10000 1 (Unassigned) C-NUMBER 9
01 10001 1 (Unassigned) C-NUMBER 10
01 10010 1 (Unassigned) C-NUMBER 11
01 10011 1 (Unassigned) C-NUMBER 12
01 10100 1 EX BEEP C-NUMBER 13 Beep
01 10101 1 EX CLICK C-NUMBER 14 Click
01 10110 1 EX DECK1 C-NUMBER 15 Select Deck 1
01 10111 1 EX DECK2 C-NUMBER 16 Select Deck 2
01 11000 1 EX RBK C-NUMBER 17 Read Block
01 11001 1 EX WBK C-NUMBER 18 Write Block
01 11010 1 (Unassigned) C-NUMBER 19
01 11011 1 EX BSP C-NUMBER 20 Backspace One Block
01 11100 1 EX SF C-NUMBER 21 Slew Forward
01 11101 1 EX SB C-NUMBER 22 Slew Backward
01 11110 1 EX REWIND C-NUMBER 23 Rewind
01 11111 1 EX TSTOP C-NUMBER 24 Stop Tape

※※※ i8008で拡張されたもの
00 111 110 LMI imm --/--/36 LOAD DATA IMMEDIATE M←imm
00 ddd 000 IN(rd) --/--/20 INCREMENT INDEX REGISTER (rd)←(rd)+1 (d=1-6,A及びMレジスタは不可×,Cfは不変)
00 ddd 001 DC(rd) --/--/20 DECREMENT INDEX REGISTER (rd)←(rd)-1 (d=1-6,A及びMレジスタは不可×,Cfは不変)
00 010 010 RAL --/--/20 ROTATE left through carry
Cf←A7,Am+1←Am,A0←Cf (m=6-0)
00 011 010 RAR --/--/20 ROTATE right through carry
Cf→A7,Am→Am-1,A0→Cf (m=7-1)
00 AAA 101 RST --/--/20 RESTART S←P,P←(00000000 00AAA000)
01 00M MM1 INP --/--/32 INPUT A←(Input data line) (MMM=device number)
01 RRM MM1 OUT --/--/24 OUTPUT (output data line)←A (RRMMM=device number,RR=00は×/INP)

※※※ V2で拡張されたもの
00 010 000 BETA --/3.2/-- SELECT β MODE βグループのレジスタ・フラグに切り替え
00 011 000 ALPHA --/3.2/-- SELECT α MODE αグループのレジスタ・フラグに切り替え
00 100 000 DI --/3.2/-- DISABLE INTERRUPTS
00 101 000 EI --/3.2/-- ENABLE INTERRUPTS
00 110 000 POP --/4.8/-- POP H(msp),L(lsp)←S
00 111 000 PUSH --/3.2/-- PUSH S←H(msp),L(lsp)

-----------------------------------------

なんで、このような日記と言うか備忘録みたいな何かを書いたのかと言うと
2年ほど前にmixiのコミュで
「8080互換ワイヤードロジックのTTL組立ボードキットMYCPU80を販売」
の記事を見つけ、販売元の中日電工さん http://www.alles.or.jp/~thisida/index.htm
の連載製作記事を毎日のように読むようになりました。
今現在(2012/6/1)はCP/M-80を動作させてみたり、
なんとCP/M互換オリジナルDOSまで話しが進んでいます。

(ちなみにMYCPU80は 2MHz動作 MVI A, imm( LA imm ) 3μs なので、
V1の16μsの5倍以上、V2の3.2μsよりちょっと速いようです。)

それで連載記事の勉強のために 8080のwiki http://ja.wikipedia.org/wiki/Intel_8080
半導体コレクション展示会場さん http://www.st.rim.or.jp/~nkomatsu/ICcollection.html
のページなども読むようになり
その過程で 8008 Home Page さん http://www.mars.dti.ne.jp/~mark08/index.html のページを見つけ
「では 8080 の元祖の 8008 とは?」と
8008のwiki http://ja.wikipedia.org/wiki/Intel_8008 を調べると
Datapoint2200のwiki http://ja.wikipedia.org/wiki/Datapoint_2200 に辿り着きました。

最初は「へーそうなんだ」程度で終わってしまったのですが、
何度か8080や8008の事を調べている内に8008にはINA/DCAが無いのに気付き、
そのうち命令セット自体がなにか不自然なような気がしてきたのです。
それで「元もとのDP2200って、どうなのよ?」って事で、Datapoint2200のwikiから調べ直して
Datapoint documentation http://bitsavers.org/pdf/datapoint/ から
Datapoint2200のマニュアルをDLしてみたらINA/DCAが無いどころか、
そもそもインクリメント/デクリメントの命令すら無かったのでした。
さらに調べると割込みに便利なRST命令も無いし、
RAL/RAR命令やMレジスタに即値を入れるLMIも無い事が分かりました。

Z80から遡るとこんなにシンプルになってしまうのかと、
それと同時にDP2200のV1用に書かれたアセンブラなら
固有のハードウェアに強く依存するIN/OUT命令以外は
簡単な変換だけでZ80やx86でも動いてしまう事にも驚きました。

と言う事で、DP2200のマニュアル(凄く判りやすい!)のOSソースリストの一番最後の方にあった
データ転送プログラムをちょこっと変更してZ80アセンブラと併記してみました。
転送元アドレスHLから転送先アドレスDEにCレジスタの1バイト指定1~256文字分の
データを転送する簡単なプログラムです。

★ちゃんと動くかどうかは知りませんよ、適当に書いただけだから、あしからず、不悪。★


2200だ: LC 転送バイト数 ( LD C, 転送バイト数 )
8008も: LD 転送先MSP ( LD D, 転送先MSP )
LE 転送先LSP ( LD E, 転送先LSP )
LH 転送元MSP ( LD H, 転送元MSP )
LL 転送元LSP ( LD L, 転送元LSP )

MEMMOV: LBM ( LD B, (HL) ) ; Bに転送元データを保存
LAL ( LD A, L ) ;┐
AD 1 ( ADD A, 1 ) ;│転送元LSPのLをインクリメント(+1)して ──┐
LLE ( LD L, E ) ;│EとLを交換 │HLをインクリメント(+1)して
LEA ( LD E, A ) ;┘ │HL転送先/DE転送元に交換
LAH ( LD A, H ) ;┐転送元MSPのHを │転送元のアドレスが+1に
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)して┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘

LMB ( LD (HL), B ) ; 転送先アドレスにBの転送元データを入れる
LAL ( LD A, L ) ;┐
AD 1 ( ADD A, 1 ) ;│転送先LSPのLをインクリメント(+1)して ──┐
LLE ( LD L, E ) ;│EとLを交換 │HLをインクリメント(+1)して
LEA ( LD E, A ) ;┘ │HL転送元/DE転送先に交換
LAH ( LD A, H ) ;┐転送先MSPのHを │転送先のアドレスが+1に
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)して┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘

LAC ( LD A, C ) ;┐
SU 1 ( SUB 1 ) ;│転送バイト数のCをデクリメント(-1)
LCA ( LD C, A ) ;┘
JFZ MEMMOV ( JP NZ, MEMMOV ) ; 転送バイト数が0以外ならば繰り返す
終了: RET ( RET ) ; おしまい


ちなみに8080(Z80)で同じものを書くと2バイトのINX/DCR命令と
(HL)以外のメモリロード命令のおかげでかなり短くなります。
Z80以降のブロック転送命令ならば転送バイト数をBCの2バイト指定にして
MEMMOV: LDIR などのように1行にする事も出来ます。


8080だ: MVI C, 転送バイト数 ( LD C, 転送バイト数 )
LXI D, 転送先 ( LD DE, 転送先 )
LXI H, 転送元 ( LD HL, 転送元 )

MEMMOV: MOV A, M ( LD A, (HL) ) ; Aに転送元データを保存
STAX DE ( LD (DE), A ) ; 転送先アドレスにAの転送元データを入れる
INX H ( INC HL ) ; 転送元HLをインクリメント(+1)
INX D ( INC DE ) ; 転送先DEをインクリメント(+1)
DCR C ( DEC C ) ; 転送バイト数Cをデクリメント(-1)
JNZ MEMMOV ( JP NZ, MEMMOV ) ; 転送バイト数が0以外ならば繰り返す
終了: RET ( RET ) ; おしまい


参考までに2バイト指定(1~65536)のプログラムも作ってみましたが・・・
8008/DP2200はメモリロード命令が(HL)経由だけなので
レジスタ不足になって悲惨な事になってます(涙)。
・・・(DE)のメモリ転送命令が有る無しだけで、だいぶ利便性が異なりますね…orz.

実はこっちの方を先に作ったんですが、あまりに判りにくいので・・・なので、あくまで参考と言う事で(汗)
・・・あと、繰り返しますが、ちゃんと動くかどうかは知りませんよ、適当に書いただけだから、あしからず、不悪。


Z80だ: LD BC, 転送バイト数
LD DE, 転送先
LD HL, 転送元
MEMMOV: LDIR ; ブロック転送命令
終了: RET ; おしまい


それを8080(Z80)で書く


8080だ: LXI B, 転送バイト数 ( LD BC, 転送バイト数 )
LXI D, 転送先 ( LD DE, 転送先 )
LXI H, 転送元 ( LD HL, 転送元 )

MEMMOV: MOV A, M ( LD A, (HL) ) ; Aに転送元データを保存
STAX DE ( LD (DE), A ) ; 転送先アドレスにAの転送元データを入れる
INX H ( INC HL ) ; 転送元HLをインクリメント(+1)
INX D ( INC DE ) ; 転送先DEをインクリメント(+1)
DCX B ( DEC BC ) ; 転送バイト数BCをデクリメント(-1)/DCXではフラグが変化しないので
MOV A, B ( LD A, B ) ; AにBをいれ
ORA C ( OR C ) ; AとCの論理和をとる(両方とも0の時だけ結果が0になる)
JNZ MEMMOV ( JP NZ, MEMMOV ) ; 0でなければ繰り返す
終了: RET ( RET ) ; おしまい


それをDP2200(Z80)で書く

※Mレジスタ(HL)経由以外のメモリ転送が出来ないため、レジスタ不足で転送先のアドレスを一時的な作業領域にしています。

※そのため、このプログラムだけ転送先と転送元の初期保存レジスタを他のプログラムと逆にしています。


2200だ: LB 転送バイト数MSP ( LD B, 転送バイト数MSP )
8008も: LC 転送バイト数LSP ( LD C, 転送バイト数LSP )
LD 転送元MSP ( LD D, 転送元MSP )
LE 転送元LSP ( LD E, 転送元LSP )
LH 転送先MSP ( LD H, 転送先MSP )
LL 転送先LSP ( LD L, 転送先LSP )

MEMMOV: LMB ( LD (HL), B ) ; レジスタが足りないので転送先HLにBの転送バイト数MSPを保存
LAL ( LD A, L ) ;┐
LLE ( LD L, E ) ;│EとLを交換 ┐
LEA ( LD E, A ) ;┘ │HL転送元/DE転送先に交換
LAH ( LD A, H ) ;┐ ┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘

LBM ( LD B, (HL) ) ; レジスタが足りないのでBに転送元HLのデータを保存
LAL ( LD A, L ) ;┐
AD 1 ( ADD A, 1 ) ;│転送元LSPのLをインクリメント(+1)して ─┐
LLE ( LD L, E ) ;│EとLを交換 │HLをインクリメント(+1)して
LEA ( LD E, A ) ;┘ │HL転送先/DE転送元に交換
LAH ( LD A, H ) ;┐転送元MSPのHを │転送元のアドレスが+1に
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)して┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘

LAM ( LD A, (HL) ) ; レジスタが足りないのでAに転送先HLに保存してあった転送バイト数MSPを保存
LMB ( LD (HL), B ) ; 転送先HLにBに保存してあった転送元のデータを入れる
LBA ( LD B, A ) ; Aに保存してあった転送バイト数MSPをもとのBに戻す
LAL ( LD A, L ) ;┐ ┐
AD 1 ( ADD A, 1 ) ;│転送先LSPのLをインクリメント(+1) │転送先HLをインクリメント(+1)
LLA ( LD L, A ) ;┘ │ INR L ┐8008以降ならば
LAH ( LD A, H ) ;┐転送先MSPのHを │ JNZ NIRH │置換え可能
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)│ INR H │3バイトほど
LHA ( LD H, A ) ;┘ ┘NIRH: 次の処理 ┘節約出来ます

LAC ( LD A, C ) ;┐転送バイト数LSPのCを
SU 1 ( SUB 1 ) ;│デクリメント(-1) ┐
LCA ( LD C, A ) ;┘ │転送バイト数のBCをデクリメント(-1)
LAB ( LD A, B ) ;┐転送バイト数MSPのBを ┘
SB 0 ( SBC 0 ) ;│C-1した時のCfでデクリメント(0-Cf)
LBA ( LD B, A ) ;┘
ORC ( OR C ) ; AとCの論理和をとる(両方とも0の時だけ結果が0になる)
JFZ MEMMOV ( JP NZ, MEMMOV ) ; 0でなければ繰り返す
終了: RET ( RET ) ; おしまい


むー・・・これだとメモリへの読み書きの回数が2倍になるから、
速度も2倍遅くなるので…別パターンのプログラムを・・・

※レジスタ不足のため、BC>=0100Hの場合はCまた256バイト先の転送先アドレスを一時的な作業領域にしています。



2200だ: LB 転送バイト数MSP ( LD B, 転送バイト数MSP )
8008も: LC 転送バイト数LSP ( LD C, 転送バイト数LSP )
LD 転送先MSP ( LD D, 転送先MSP )
LE 転送先LSP ( LD E, 転送先LSP )
LH 転送元MSP ( LD H, 転送元MSP )
LL 転送元LSP ( LD L, 転送元LSP )

LAC ( LD A, C ) ; AにCを入れ ┐ほぼ自爆するので
ORB ( OR B ) ; AとBの論理和をとる(両方とも0の時だけ結果が0になる)│要らないと言えば
JTZ MSTART ( JP Z, MSTART ) ; 転送バイト数BCが0だったら最終判断をスキップする ┘要らないのですが

LAB ( LD A, B ) ; AにBを入れ
ORB ( OR B ) ; AとBの論理和をとる(両方とも0の時だけ結果が0になる)
JTZ MEMMOV ( JP Z, MEMMOV ) ; 転送バイト数MSPのBが0だったら最後の転送処理にジャンプ

MSTART: CALL SWDEHL ( CALL SWDEHL ) ; HL転送先/DE転送元に交換

LAC ( LD A, C ) ; AにCを入れ
ORC ( OR C ) ; AとCの論理和をとる(両方とも0の時だけ結果が0になる)
JTZ CALMMV ( JP Z, CALMMV ) ; 転送バイト数LSPのCが0だったら2バイト指定転送ループの最初にジャンプ

LAL ( LD A, L ) ;┐
ADC ( ADD A, C ) ;│転送先LSPのLに転送バイト数LSPのCを加算 ┐
LLA ( LD L, A ) ;┘ │転送先HLを+Cにする
LAH ( LD A, H ) ;┐ ┘
AC 0 ( ADC A, 0 ) ;│転送先MSPのHを0+Cfを加算
LHA ( LD H, A ) ;┘
LMB ( LD (HL), B ) ; レジスタが足りないのでCバイト先の転送先HLにBの転送バイト数MSPを保存
LAL ( LD A, L ) ;┐
SUC ( SUB C ) ;│転送先LSPのLから転送バイト数LSPのCを減算 ┐
LLA ( LD L, A ) ;┘ │転送先HLを-Cにする
LAH ( LD A, H ) ;┐ ┘
SB 0 ( SBC 0 ) ;│転送先MSPのHから0-Cfを減算
LHA ( LD H, A ) ;┘
JMP CA2MMV ( JP CA2MMV ) ; 2バイト指定転送ループの途中にジャンプ

CALMMV: LAH ( LD A, H ) ;┐
AD 1 ( ADD A, 1 ) ;│転送先MSPのHを+1して転送先HLを+256にする
LHA ( LD H, A ) ;┘
LMB ( LD (HL), B ) ; レジスタが足りないので256バイト先の転送先HLにBの転送バイト数MSPを保存
LAH ( LD A, H ) ;┐
SU 1 ( SUB 1 ) ;│転送先MSPのHから-1して転送先HLを-256にする
LHA ( LD H, A ) ;┘

CA2MMV: CALL SWDEHL ( CALL SWDEHL ) ; HL転送元/DE転送先に交換
CALL MEMMOV ( CALL MEMMOV ) ; 転送数1バイト指定のサブルーチンの呼び出し

CALL SWDEHL ( CALL SWDEHL ) ; HL転送先/DE転送元に交換
LAM ( LD A, M ) ; AにCまた256バイト先の転送先HLに保存してあった転送バイト数MSPを入れる
SU 1 ( SUB 1 ) ; Aの転送バイト数MSPをデクリメント(-1)
LBA ( LD B, A ) ; BにAの転送バイト数MSPを戻す
JFZ CALMMV ( JP NZ, CALMMV ) ; 転送バイト数MSPが0以外だったら2バイト指定数の転送を繰り返す
CALL SWDEHL ( CALL SWDEHL ) ; HL転送元/DE転送先に交換

MEMMOV: LBM ( LD B, (HL) ) ; Bに転送元データを保存
CALL SIDEHL ( CALL SIDEHL ) ; HL転送元アドレスをインクリメント(+1)してHL転送先/DE転送元に交換

LMB ( LD (HL), B ) ; 転送先アドレスに転送元データのBを入れる
CALL SIDEHL ( CALL SIDEHL ) ; HL転送先アドレスをインクリメント(+1)してHL転送元/DE転送先に交換

LAC ( LD A, C ) ;┐
SU 1 ( SUB 1 ) ;│転送バイト数LSPのCをデクリメント(-1)
LCA ( LD C, A ) ;┘
JFZ MEMMOV ( JP NZ, MEMMOV ) ; 転送バイト数LSPが0以外ならば1バイト指定数の転送を繰り返す
終了: RET ( RET ) ; おしまい

SIDEHL: LAL ( LD A, L ) ;┐
AD 1 ( ADD A, 1 ) ;│Lをインクリメント(+1)して ───────────┐
LLE ( LD L, E ) ;│EとLを交換 │HLをインクリメント(+1)して
LEA ( LD E, A ) ;┘ │DEとHLの内容を交換
LAH ( LD A, H ) ;┐Hを │
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)して┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘
終了: RET ( RET ) ; おしまい

SWDEHL: LAL ( LD A, L ) ;┐
LLE ( LD L, E ) ;│EとLを交換 ┐
LEA ( LD E, A ) ;┘ │DEとHLの内容を交換
LAH ( LD A, H ) ;┐ ┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘
終了: RET ( RET ) ; おしまい


・・・速度は改善されるけどプログラムが長くなって複雑に…orz.
あと特定のワークメモリにレジスタの値を退避する方法もあると思うけど、
たぶん同じくらい長くなるんじゃないかな・・・
下のV2の例のPUSH/POPの代わりに保存/退避用のプログラムを追加する感じかな…
最初の初期設定のPUSH部分相当が面倒そうだけど…
転送先アドレスにC、AにB、BCにHLを退避し、ワークメモリBにAを入れ、
HLにBCを戻し、Cに転送先の値を戻せば初期設定はOKかな?
後のループのPOP/PUSH部分の代わりは、AにC、BCにHLを退避し、
ワークメモリCにAを入れて転送バイト数LSPを保存して、
それからAにワークメモリBの値を入れ、転送バイト数MSPをデクリメント判定し、
ワークメモリBにその値を戻して、
Aに転送バイト数LSPを保存していたワークメモリCの値を入れて、
HLにBC、CにAの退避していた値を戻す・・・
みたいな感じでいいのかな?…いずれにしても凄く面倒ですね・・・


V2ならば転送バイト数MSPをスタックに退避させてデクリメント判定/再保存できるので、
だいぶ簡単に…


DP22V2: LB 転送バイト数MSP ( LD B, 転送バイト数MSP )
LC 転送バイト数LSP ( LD C, 転送バイト数LSP )
LD 転送先MSP ( LD D, 転送先MSP )
LE 転送先LSP ( LD E, 転送先LSP )
LH 転送元MSP ( LD H, 転送元MSP )
LL 転送元LSP ( LD L, 転送元LSP )

LAH ( LD A, H ) ;┐
LHB ( LD H, B ) ;│スタックのHにBの転送バイト数MSPを保存
CALMMV: PUSH ( PUSH HL ) ;│(スタックのLは無視)
LHA ( LD H, A ) ;┘

CALL MEMMOV ( CALL MEMMOV ) ; 転送数1バイト指定のサブルーチンの呼び出し

LBH ( LD B, H ) ; BにHの転送元MSPを退避
LAL ( LD A, L ) ; AにLの転送元LSPを退避
POP ( POP HL ) ; Hにスタックに保存していた転送バイト数MSPを入れる
LLA ( LD L, A ) ; LにAの転送元LSPを戻す
LAH ( LD A, H ) ; AにHの転送バイト数MSPを入れる
SU 1 ( SUB 1 ) ; Aの転送バイト数MSPをデクリメント(-1)
LHA ( LD H, A ) ; HにAの転送バイト数MSPを入れる
LAB ( LD A, B ) ; AにBの転送元MSPを入れる
JFZ CALMMV ( JP NZ, CALMMV ) ; 転送バイト数MSPが0以外だったら2バイト指定数の転送を繰り返す
LHA ( LD H, A ) ; HにAの転送元MSPを入れる

MEMMOV: LBM ( LD B, (HL) ) ; Bに転送元データを保存
CALL SIDEHL ( CALL SIDEHL ) ; HL転送元アドレスをインクリメント(+1)してHL転送先/DE転送元に交換

LMB ( LD (HL), B ) ; 転送先アドレスに転送元データのBを入れる
CALL SIDEHL ( CALL SIDEHL ) ; HL転送先アドレスをインクリメント(+1)してHL転送元/DE転送先に交換

LAC ( LD A, C ) ;┐
SU 1 ( SUB 1 ) ;│転送バイト数LSPのCをデクリメント(-1)
LCA ( LD C, A ) ;┘
JFZ MEMMOV ( JP NZ, MEMMOV ) ; 転送バイト数LSPが0以外ならば1バイト指定数の転送を繰り返す
終了: RET ( RET ) ; おしまい

SIDEHL: LAL ( LD A, L ) ;┐
AD 1 ( ADD A, 1 ) ;│Lをインクリメント(+1)して ───────────┐
LLE ( LD L, E ) ;│EとLを交換 │HLをインクリメント(+1)して
LEA ( LD E, A ) ;┘ │DEとHLの内容を交換
LAH ( LD A, H ) ;┐Hを │
AC 0 ( ADC A, 0 ) ;│L+1した時のCfでインクリメント(0+Cf)して┘
LHD ( LD H, D ) ;│DとHを交換
LDA ( LD D, A ) ;┘
終了: RET ( RET ) ; おしまい


・・・大事な事なので繰り返しますが、ちゃんと動くかどうかは知りませんよ、
適当に書いただけだから、あしからず、不悪。

☆元々の下書きのファイルからアメブロに書き込む時に、かなり余白とか見栄えを直してますので
間違っておかしくなってしまった所が、もしかしたらあるかもです。

-----------------------------------------

っえ!?もうブログのタイトル画像のリオちゃんや熊女将たちと全く関係ない話しだって?
そんな事はないですよ。
日本最初のパチスロ機にはZ80が使われていて、その販売に関わっていたのはネットさんですから、
遠いご先祖様のお話という事で。