SSブログ
English Version

SuperSimpleController(その15)逆ポーランド記法 [OriginalCPU]

 前回の記事で書いたようにモニタプログラムができ、パソコン側で開発したアプリケーションのロードも容易になったので2バイト整数の演算処理を作ってみました。

 今回開発した8bit CPU(Simple8Z)はハードウェアを最小化(13チップ構成)するためにハードリソースも必要最小限に近い構成なので2バイト演算をレジスタのみではできず、Z-80等に比べるとコード効率が2倍以上悪くなります。

 今回作成した2バイト整数の四則演算処理のアセンブラソースを貼っておきます。
 乗算と除算の処理内容としては10進数の場合に筆算で行う処理と同様なロジックで作成しています。0除算の場合はキャリーフラグを立てるようにしました。

2バイト整数の四則演算処理(Simple8Z用アセンブリ言語)
; Addition ; Para1 <- Para1 + Para2 AddW: RD_R0 Para2 LD_R1_R0 RD_R0 Para1 ADD_R0_R1 WR_R0 Para1 RD_R0 <Para2 + 1> LD_R1_R0 RD_R0 <Para1 + 1> ADC_R0_R1 WR_R0 <Para1 + 1> RET ; subtraction ; Para1 <- Para1 - Para2 SubW: RD_R0 Para2 LD_R1_R0 RD_R0 Para1 SUB_R0_R1 WR_R0 Para1 RD_R0 <Para2 + 1> LD_R1_R0 RD_R0 <Para1 + 1> SBC_R0_R1 WR_R0 <Para1 + 1> RET ; Absolute value ; Para1 <- abs(Para1) AbsW: RD_R0 <Para1 + 1> ; get high byte CKMSBR0 ; check MSB of R0 JNC AbsW90 ; if plus NegW: RD_R0 Para1 LD_R1_R0 CLR_R0 SUB_R0_R1 WR_R0 Para1 RD_R0 <Para1 + 1> LD_R1_R0 CLR_R0 SBC_R0_R1 WR_R0 <Para1 + 1> AbsW90: RET ; multiplication ; Para1 <- Para1 x Para2 MulW: RD_R0 Para1 WR_R0 CalTmp RD_R0 <Para1 + 1> WR_R0 <CalTmp + 1> CLR_R0 WR_R0 Para1 WR_R0 <Para1 + 1> LD_R0 16 WR_R0 CalCnt ; set loop counter MulWLp: RD_R0 <CalTmp + 1> SHR_R0 WR_R0 <CalTmp + 1> RD_R0 CalTmp SHRC_R0 WR_R0 CalTmp JNC MulW10 CALL AddW MulW10: RD_R0 Para2 ; Para2 <- Para2 * 2 SHL_R0 WR_R0 Para2 RD_R0 <Para2 + 1> SHLC_R0 WR_R0 <Para2 + 1> RD_R0 CalCnt DEC_R0 WR_R0 CalCnt JNZ MulWLp RET ; division ; Para1 <- Para1 / Para2 ; Para1 -> division ; CalMod -> modulo ; Cy -> 1:error DivW: RD_R0 Para2 LD_R1_R0 RD_R0 <Para2 + 1> OR_R0_R1 JZ DivErr CLR_R0 WR_R0 CalMod WR_R0 <CalMod + 1> LD_R0 16 WR_R0 CalCnt DivWLp: RD_R0 Para1 ; (CalMod : Para1) << 1 SHL_R0 WR_R0 Para1 RD_R0 <Para1 + 1> SHLC_R0 WR_R0 <Para1 + 1> RD_R0 CalMod SHLC_R0 WR_R0 CalMod RD_R0 <CalMod + 1> SHLC_R0 WR_R0 <CalMod + 1> RD_R0 <Para2 + 1> LD_R1_R0 RD_R0 <CalMod + 1> CP_R0_R1 JC DivW20 JNZ DivW10 RD_R0 Para2 LD_R1_R0 RD_R0 CalMod CP_R0_R1 JC DivW20 DivW10: RD_R0 Para2 ; CalMod <- CalMod - Para2 LD_R1_R0 RD_R0 CalMod SUB_R0_R1 WR_R0 CalMod RD_R0 <Para2 + 1> LD_R1_R0 RD_R0 <CalMod + 1> SBC_R0_R1 WR_R0 <CalMod + 1> RD_R0 Para1 ; Para1 <- Para1 + 1 INC_R0 WR_R0 Para1 ; always no Cy DivW20: RD_R0 CalCnt DEC_R0 WR_R0 CalCnt JNZ DivWLp RET DivErr: CKZR1 ; set Cy RET

★追記 2021/04/30
 AbsWとNegWを追加
★追記 2021/04/28
 乗算処理内で加算処理をコールしているので加算と減算処理のソースを追加
★変更 2021/04/27
 除算処理で商を直接Para1に設定するようにしてコード短縮&高速化

 乗算処理ができるようになったのでこれを使って10進数の文字列のバイナリ変換が作れます。また、除算処理を使ってバイナリをアスキー文字に変換する処理も作れます。

 作成したバイナリと10進アスキー文字列の相互変換と演算処理の動作確認をするために電卓のように自由に演算対象の数値を変更して演算できる環境が欲しくなります。

 演算子を使った一般的な数式を評価する処理を作成するのは少々面倒なので、割合簡単に作成できるスタックを使用した逆ポーランド記法(RPN)の数式を処理する環境を作ってみました。

 下図が作成した整数型RPN電卓の操作例です。下記の操作を行っています。最後に実行しているRPN式はウィキペディアに記載されているものです。
  1. EEPROM内のHexローダーを起動
  2. モニタプログラムのHexファイルをロードし、自動起動
  3. モニタのヘルプ表示
  4. r(Hexリード)コマンド実行
  5. RPN電卓のHexファイルをロード
  6. RPN電卓を起動
  7. RPN電卓のヘルプ表示(dupはスタックトップを複製するコマンド)
  8. 1から10までの合計値を計算
  9. 111の二乗を計算
  10. 1000/6を計算
  11. サンプルのRPN式の評価結果を10進と16進で表示

RPN電卓操作例


★変更 2021/05/05
 マイナス値の乗算例を追加
★変更 2021/04/28
 除算例を追加


[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

SuperSimpleController(その14)モニタの制作2 [OriginalCPU]

 前回の記事でプリント基板化した自作CPU(Simple8Z)にシリアルインターフェースを接続してモニタプログラムを作成中であることを書きました。
 今回はオレオレマシン語で記述したモニタプログラムが完成したのでモニタ機能の概要と対処した代表的な問題について書いてみます。

 下の写真はシリアルI/Fを接続した状態の様子です(3Dプリンタで出力したスペーサーの足を付けました)

シリアルI/Fを接続したSimple8Z



 今回作成したオリジナルのCPUはハードウェアの構成を最小化(結果として13チップ構成)するため、CPUリソースも必要最小限に近いため、レジスタは2個しかなく、ジャンプ命令等で特定のレジスタが破壊されるというよう環境であり、Z80のマシン語が高級言語に感じるほどですw

 今回自作したCPUである程度の規模のプログラムを作成するのは初めてなので特定の条件で発生するような想定外の問題が発生し、対処のためにマイクロコードの修正も並行して行いました。
 更にマイクロコードでは対応不可能な問題も発生し、GAL内のロジックも変更(詳細は後述)しています。

 その結果、モニタプログラム完成時には、動作が非常に安定しました(もう未発見の問題が残っていないことを祈りたい)

 モニタの操作ログ例を下記に貼りました。操作内容は
  • ROMに焼いたhexローダーを起動
  • モニタのhexファイルをロード後、自動起動
  • ヘルプ表示
  • IN/OUTコマンドを実行(シリアルIC(MC68B50)のステータス入力とデータポートへの41H出力)
  • メモリセットコマンド実行
  • メモリダンプコマンドの実行
  • モニタ試験プログラム(hello表示とエコーバック)のロード
  • メモリダンプコマンドの実行
  • モニタ試験プログラムの実行
です。

モニタ操作のサンプルログ
HexLoader Start 000000000000000000000000000000000000000000000001 exec SimonZ(monitor for Simple8Z) by skyriver V0.01 2021/04/15 ]h DUMP: D[aaaa[,aaaa]] FILL: Faaaa,aaaa,dd GO : Gaaaa SET : Saaaa IN : Iaa OUT : Oaa,dd READ: R HELP: H ]ic0 02 ]oc1 41 A] ? ]sa000 A000:C1 - 1 A001:82 - 2 A002:03 - 3 A003:04 - 4 A004:A2 - - A003:04 - - A002:03 - . ]d8008 801c 8000 : - 06 FE 10 60 50 0A FE 10 8010 : E0 50 09 FE 50 0D FE 10 - 00 B0 B0 84 B0 ]da000 A000 : 01 02 03 04 A2 E2 EA EA - 6C 55 72 75 04 45 15 5F A010 : 2E B9 AB 90 E3 AA B0 AA - 79 96 5F 4D 59 55 11 94 A020 : A8 BB 0F 22 0B BE 8B E1 - FA 1C 52 34 54 55 45 45 A030 : C8 EA 2A 0E 0A 8E 28 AF - D9 71 90 58 45 9B 23 74 A040 : AC B2 00 02 E4 EA C2 A9 - 55 3F 67 5C 67 45 01 41 A050 : AB 5F AA 2A 48 0D FE 9A - 75 FF 45 CF 74 DD 79 E7 A060 : A1 FB C8 7F 2B AA BE DB - 91 DE 51 7D 94 5E C7 85 A070 : 8E 0E C2 B6 F8 E8 AA 8B - 70 54 55 50 27 D1 54 55 ]r 0001 ]da000 A000 : 10 39 50 48 A0 60 48 A0 - 3D 50 48 A0 80 A0 4F 2F A010 : C2 18 A0 B0 2D A0 C0 04 - A0 98 C0 3A C5 18 A0 98 A020 : C1 18 0D 4E C2 2C A0 B0 - 2D A0 C0 18 A0 E0 40 98 A030 : C0 3A 3A 2F C5 2D A0 90 - C1 E0 48 65 6C 6C 6F 2C A040 : 77 6F 72 6C 64 0D 0A 00 - 00 3F 67 5C 67 45 01 41 A050 : AB 5F AA 2A 48 0D FE 9A - 75 FF 45 CF 74 DD 79 E7 A060 : A1 FB C8 7F 2B AA BE DB - 91 DE 51 7D 94 5E C7 85 A070 : 8E 0E C2 B6 F8 E8 AA 8B - 70 54 55 50 27 D1 54 55 ]ga000 Hello,world echo back test is OK!! :) ]


 次にGALの修正まで必要だった問題についてメモしておきます。
 GAL内のロジックとマイクロコードの内容を把握していないと判り辛いと思いますが自分へのメモという意味もあります。
  • 問題内容
     R0レジスタをメモリに保存するWR_R0 aaaaにおいて最後の3バイト目のメモリ上のアドレスの下位が0FFHの場合、PCL(74AS867)のTCO(上位へのキャリア信号)がアクティブになり、PCHがインクリメントされることでPCの値がズレてしまう。

  • 対処
     GAL内のPCLからPCHへのキャリ生成ロジックに下記の青色部分を追加し、上記問題時にはPHWが生成されないようにした(RCO:PCLのRCO、OPHW:PCHへのライト信号)。マイクロコードにもPWEN信号の制御を追加した。
     また、水平展開としてRD_R0 aaaaでも同様の問題が発生するはずなので同じ対処を行った。

     QRCO.d = RCO;
     append OPHW = !RCO & QRCO & PWEN;

 下図は対処後のロジアナ画面例で"A1"カーソルから"A2"までがWR_R0命令の実行区間です。
"A1"カーソルの右側での最初の06:QRCO/のアクティブパルスでPHW/が発生されず、"A2"カーソル部の06:QRCO/のアクティブパルス(PC値がxxFFの状態でPCLをインクリメント)に対してはPHW/が生成されていることが判ります。

GALで対処後のロジアナ画面例



★追記 2021/04/18
 上記ロジアナ画面のデータ採取時に実行したプログラムを追記します。 {

WR_R0命令問題再現プログラム
;++++++++++++++++++++++++++++++++++++++++ ; 100H prob test ; by skyriver ; V0.01 2021/04/15 ;++++++++++++++++++++++++++++++++++++++++ .list 0001 ROM EQU 1 ifdef ROM 01FB START EQU 01FBH else START EQU 90FBH endif 0000' ASEG ifdef ROM ORG 0000H JP START ; 12(9) 0000 C0 + DB 0C0H 0001 01FA + DW (START) - 1 ; because increment PC after set PC endif ORG START AND 0FF00H HALT 0100 FF + DB 0FFH ORG START LD_R0 23H ; 16(4) 01FB 10 + DB 10H 01FC 23 + DB 23H WR_R0 TMP ; 28(12) 01FD 50 + DB 50H 01FE 8010 + DW TMP LD_R0 34H ; 32(4) 0200 10 + DB 10H 0201 34 + DB 34H RD_R0 TMP ; 43(11) 0202 60 + DB 60H 0203 8010 + DW TMP HALT ; 45(2) 0205 FF + DB 0FFH ORG 8010H 8010 TMP: DS 1 END

}


 YouTubeにもSimple8Zの状況をアップしたので貼っておきます。
 冒頭部分の"consists of 16 ICs"の16は13のtypoです^^;

https://www.youtube.com/watch?v=LzoIbzHgR4k



[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

SuperSimpleController(その13)モニタの制作 [OriginalCPU]

 前回の記事で書いたように自作CPUのプリント基板(下の写真)ができたので今回はモニタの制作について少し書いてみます。

プリント基板化したSimple8Z



 始めにシリアル通信ができるように手持ちの調歩同期シリアル通信用IC(MC68B50)を載せたシリアルアダプタを汎用基板(秋月さんの両面スルホールCタイプ)を使って作成しました。
 Simple8Zの拡張用コネクタからはI/Oアクセス時にアクティブ(lowアクティブ)になるIOR/信号を出していますが、MC68C50のE信号(イネーブル信号)はhighアクティブなのでトランジスタを使ってIOR/を反転して接続しています。
 また、PIC12F683でシリアル通信用クロック(9600x16)を生成しています。
 具体的な回路図は「SuperSimpleController(その10)I/O命令追加」の記事を参照してください。

シリアルアダプタ(部品面)



シリアルアダプタ(半田面)



 下の写真はSimple8Z本体に今回作成したシリアルアダプタを接続した状態のものです。
 マシン語のディバッグもさることながら、今回の様にある程度まとまった処理を実行すると以前の1パス試験では出現しなかった問題が発生するのでマイクロコードも修正しつつの作業になります。
 ICソケットを痛めないようにuCODE用のROMにもゼロプレッシャーソケットを付けました。

Simple8Z with serial adapter



 まだ作成途中で部分的にしかコマンドを実装していませんが、Hexローダーでモニタをロードし、動かしているサンプル画面が下図になります。ディバッグのために1MHzの外部クロックで動かしています。

 もうどこから見てもCPUそのものではありませんか?

モニタロードと操作のサンプル画面



 モニタ内のHexロード機能('R'コマンド)、ダンプ及び実行コマンドの試験のために作成したHello表示とエコーバックを行う試験用プログラムのソースも貼っておきます。

モニタ試験用Hello&Echo back試験プログラム(Simple8Z用アセンブリ言語)
;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z Hello & echo back test ; by skyriver ; V0.01 2021/04/08 ;++++++++++++++++++++++++++++++++++++++++ .xlist include machine.inc .list START EQU 0A000H ; program start address SIO_CMD EQU 0C0H SIO_DAT EQU SIO_CMD + 1 ASEG ORG START LD_R0 <(LOW MSG) - 1> WR_R0 saveR0 LOOP: RD_R0 saveR0 INC_R0 WR_R0 saveR0 RD_R1_R0 <HIGH MSG> CKZR1 LD_R0_R1 JZ ECHO CALL PUTC JP LOOP ECHO: IN_R0 SIO_CMD SHR_R0 JNC ECHO IN_R0 SIO_DAT LD_R1 13 CP_R0_R1 JZ ECHOEN CALL PUTC JP ECHO ECHOEN: RET ; output character to SIO ; R0 <- data ; PUTC: LDF_R1_R0 IN_R0 SIO_CMD SHR_R0 SHR_R0 LD_R0_R1 JNC PUTC ; if tx not ready OUT_R0 SIO_DAT RET MSG: DB "Hello,world", 13,10,0 saveR0: DS 1 END


★追記 2021/04/09
 Twitterにポストした動画付きコメントを貼っておきます。
 EEPROMに書いたHexローダーでモニタをロード&起動後に上記の試験用プログラムのヘキサファイルをモニタの'R'コマンドでロードしてから'G'コマンドで実行しています。




★追記 2021/04/12
 最初はI2Cロガーの時のようにシリアルアダプタをCNCを使って片面基板で作成しようと考えていましたが、片面基板ではピンヘッダの固定力が弱く、抜き差しする際にピンが動きパターンが剥がれる恐れがあるので上述のように両面スルホールの汎用基板を使い手配線で作成することにしました。
 CNCでの作成用にパターン設計等はしていたので貼っておきます。
 トランジスタでIOR/信号を反転してMC68B50のE信号として入力していますが、立上りタイミングが2us程度遅れたので下記のパターン図にはないですが、ベース抵抗にパラでスピードアップコンデンサとして220pFを追加(遅延は0.3us程度に改善)しています。

片面基板パターン設計



 CNCでパターン切削から穴開け、外周切取りまで行いますが、パターン切削時のCNC制御画面が下図です。

CNCでのパターン切削画面




[TOP] [ 前へ ] 連載記事 [ 次へ ]


nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

SuperSimpleController(その12)プリント基板完成 [OriginalCPU]

 前回の記事の冒頭に書いたように「SuperSimpleController(その9)回路図とパターン設計」の記事で書いたプリント基板が製造依頼したElecrowさんから届き、部品実装してみたので記録しておきます。

 製造依頼は5枚ですが6枚届きました(Elecrowさんのいつものサービスですね)
 レジスト色は青にしています。下の写真がトップ面で特に問題ない仕上がりです。今回は10cm四方の基板にかなり詰め込んだので、トラック幅も部分的(SMD IC部品の周辺等)に最小値(0.15mm)に近い0.16mm幅のものを使いましたが問題なく製造されています。

 設計上の問題ですが、LEDのマイナス側を示す二重線が、シルク上ほとんど区別は付かない状態だったので次回はシルクを変更する必要があります。
 また中央上部にあるHALTのLEDの極性が逆でしたw。このLEDは当初RUN LEDとして付けたものをパターン設計中にHALT時に点灯するように変更したものです。

PCBのTOP面



 下の写真はボトム面で左上のSMDタイプの74HC574のICの下側のパスコンが半分程度ICに重なっていましたw。
 設計上のミスですがこのICはトップ面でもほぼ同じ位置に74HC574があるのでトップ面のICとの重なりだけをチェックしてしまっていたようです(部品の3Dデータが揃っていないので3D表示でのチェックはしていなかった^^;)。
 今回はこのパスコンを未実装にして組み立てています。

PCBのBottom面



 手書きのラベルが汚くて恐縮ですが部品実装後のトップ面が下の写真です。
 クロック上限が未評価で当面は外部からクロックを供給して動かすつもりなので水晶は未実装のままです。
 注文していた丸ピンソケットがまだ届いていないので、EEPROMは平ピンソケットを二重に重ねてゼロプレッシャーソケットを付けました。
 ALUのM27C322は42ピンなので32ピンの平ピンソケット+10ピン分削り加工したもので対応しています。

 青色LEDはVf:3V想定で電流制限抵抗を2Kにしましたが、眩しすぎるくらい明るいです(最近の青色LEDはVfが低い?)
 また、halt表示のLEDは通常動作時はDuty10%程度でhalt時にDuty100%になる設計(ハードリソース最小化のため専用の制御信号を待たない節約設計ですw)ですが、通常動作時でも十分明るいのでhalt表示機能としてはイマイチかもしれません。

Top面



 ボトム面が下図で追加で縦方向のトラックが引けないくらい混雑しています。上述のように一部のトラック幅を0.16mmにしていますが、見た目はそれ程細くないように見えますね。

Bottom面



 下の写真は動作中の状態で、前回の記事で書いたHexLoaderを動かしているところです(シリアルI/Fを付けていないのでロードはできない)。
 クロック50Hzで動かすとLE0の点灯状態が目まぐるしく変化して綺麗ですが、クロック1MHzでは変化が殆どありませんw

動作中の様子



★追記 2021/03/28
 Twitterにポストした動画付きコメントを貼っておきます。




[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

SuperSimpleController(その11)HexLoaderの制作 [OriginalCPU]

 前回の記事でシリアル通信ができたことを書きましたが、たまに文字化けが発生する状態でした。
 今回開発しているSimple8Z側の問題なのか調査したところ、PICで生成しているSIO(MC6850)用のクロック精度が悪いことが原因だということが判ったのでPIC内蔵クロックから外付けの8MHzクリスタルに変更したところシリアル通信が安定しました。

 PICのソースも変更したので貼っておきます。

8MHzクリスタルでの9.6KHz生成ソース(PIC12F683)
/* * SIO clock genelator for Simple8Z test008 * PIC12F683 * Ver 0.01 made by skyriver 2021/03/17 */ #include <INT16CXX.H> // define config bit assign(PIC12F683) #define conf_FCMEN (1<<11) // Fail-Safe Clock Monitor Enable #define conf_IESO (1<<10) // Internal External Switchover bit #define conf_BODEN1 (1<<9) // Brown-out Detect Selection bit #define conf_BODEN0 (1<<8) // Brown-out Detect Selection bit #define conf_CPD_ (1<<7) // Data code Protection(0:enable) #define conf_CP_ (1<<6) // Code Protection(0:enable) #define conf_MCLRE (1<<5) // GP3/MCLR_ pin function secet bit(1:MCLR) #define conf_PWRTEN_ (1<<4) // Power-up Timer Enablr bit(0:enable) #define conf_WDTEN (1<<3) // Watchdog Timer enable bit(1:enable) #define conf_FOSC_RC 0b111 #define conf_FOSC_RCIO 0b110 #define conf_FOSC_INTOSC 0b101 #define conf_FOSC_INTOSCIO 0b100 #define conf_FOSC_EC 0b011 #define conf_FOSC_HS 0b010 #define conf_FOSC_XT 0b001 #define conf_FOSC_LP 0b000 //#pragma config = conf_CPD_ | conf_CP_ | conf_FOSC_INTOSCIO #pragma config = conf_CPD_ | conf_CP_ | conf_FOSC_HS /* initialize H/W and workarea */ void init( void ) { GPIO = 0b00000000; // GP0:IntReq WPU = 0b00000000; // 1:Pull-up enable TRISIO = 0b00111011; // 0:output, 1:input CMCON0 = 0b00000111; // Comparator off CMCON1 = 0b00000011; // comp output is asynchronization VRCON = 0b00001000; // Vref is off ANSEL = 0b00000000; // 0:dig, 1:ana OSCCON = 0b01110001; // 8MHz internal clock OPTION = 0b00000000; // PortB_PullUp Fosc/4 T1PreScaler:1/2T WPU = 0b00110111; // PullUp enable // 8000000/(9600*4*1)=208.33 T2CON = 0b0000100; // Timer2 on,pre/post scaler 1:1 PR2 = 208; // (PR2+1)/2 = 104 : duty 50% CCP1CON = 0b00001100; // LSBS:00 PWM mode(active high) CCPR1L = 104; PCON = 0b00000000; } void main( void ) { init(); while ( 1 ) { } }


 今回の自作CPU(Simple8Z)には割込み機能は無いので連続した受信データ(9600bsp)の処理は難しいですが、TeraTermの設定でキャラクタ間に時間を設けてやれば処理できるはずです(TeraTermの設定は5ms/char、20ms/lineにしました)。Simple8Zのクロックも1MHzに上げてみました(上限はもっと高いと思う)。

★2021/04/03 追記 {
 不安定だった原因はJP関連の命令でクリティカルなタイミングがあったためでuCODEを見直したことで安定になりました。1MHzのクロックで動作時にTeraTermの設定が9600bpsで文字毎のウェイト無し(0ms/char)でも取りこぼしが発生しない状態になりました。また、シリアル通信用クロックもPIC内部発信で154.6KHz(=9600x16)付近のクロックを生成することで文字化けもなくなりました。
}

 今後の作業のことも考慮して、まずはヘキサファイルのローダーを作成してみました。前回の記事にマシン語コード表を書きましたが一通りのマシン語を実装したつもりなので、現状のマシン語セットである程度の規模の処理を記述できるかの確認の意味もあります。

 ジャンプ命令でR1レジスタが壊れる等、癖がありますが結果としては何とかできました。メモリ上のワーク領域にある2バイトのアドレスデータの示すメモリにレジスタの内容を書込む処理ができなかったので、ワーク領域の直前にWR_RO命令のオペコードを書いてそこをコールするという方法で対応しましたw

 作成したHexLoaderのリストは下記になります。処理内容としては「Z80GALの構想(その4)簡易モニタの製作」の記事に書いたHexファイルローダーとほぼ同様で、この時はZ80で約120バイトでしたが、今回は約290バイトなので2倍以上のサイズになってしまいました(但し、今回は自動実行の処理を追加している)。
 Simple8Zはレジスタ数が少ない(R0,R1の2個)のでメモリアクセスが多くなることがサイズが大きくなった主な原因だと思います。

作成したSimple8Z用HexLoaderのソース(アセンブリ言語)
;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z HexLoader program ; by skyriver ; V0.01 2021/03/25 ;++++++++++++++++++++++++++++++++++++++++ .xlist include machine.inc .list WORK EQU 0FE00H ; work area on RAM STACK EQU 0FFFFH ; stack low byte STACKP EQU 0FEH ; initial stack low byte value SIO_CMD EQU 0C0H SIO_DAT EQU SIO_CMD + 1 OPWRR0 EQU 050H ; WR_R0 ope code OPRET EQU 0E0H ; RET ope code ASEG ORG 0 START: LD_R0 STACKP ; set stack pointer WR_R0 STACK LD_R1 SIO_CMD ; initialize MC6850 LD_R0 00010111B ; reset cmd OUT_R1_R0 LD_R0 00010100B ; clock x1 OUT_R1_R0 LD_R0 OPWRR0 WR_R0 WRPNT LD_R0 OPRET WR_R0 WRPRET LD_R0 <LOW MSG> CALL PUTS ; display opening message LOAD: CALL GETC LD_R1 ':' CP_R0_R1 JNZ LOAD CALL GBYTE ; get data count WR_R0 COUNT CALL GWORD ; get adr RD_R0 WDATA WR_R0 JPAD RD_R0 <WDATA + 1> ; save first adr to jump adr WR_R0 <JPAD + 1> JP LOAD10 LOADLP: CALL GETC LD_R1 ':' CP_R0_R1 JNZ LOADLP CALL GBYTE ; get data count WR_R0 COUNT CALL GWORD ; get adr LOAD10: CALL GBYTE ; get record type WR_R0 TYPE JZ LOADRD ; if data record DEC_R0 JZ LOADEN ; if end of data LD_R0 <LOW MSGERR> ; type error CALL PUTS HALT LOADEN: CALL DSPTYP ; execute AP LD_R0 <LOW MSGEX> CALL PUTS RD_R0 <JPAD + 1> LDF_R1_R0 RD_R0 JPAD JP_RX LOADRD: CALL GBYTE ; get data byte CALL WRPNT ; write data to memory with adr RD_R0 WDATA INC_R0 WR_R0 WDATA JNC LOAD20 RD_R0 <WDATA + 1> INC_R0 WR_R0 <WDATA + 1> LOAD20: RD_R0 COUNT DEC_R0 WR_R0 COUNT JNZ LOADRD CALL DSPTYP JP LOADLP ; output character to SIO ; R0 <- data PUTC: LDF_R1_R0 ; save data IN_R0 SIO_CMD SHR_R0 SHR_R0 LD_R0_R1 JNC PUTC ; if tx is not ready OUT_R0 SIO_DAT RET ; get char from SIO ; R0 -> data GETC: IN_R0 SIO_CMD CKLSBR0 ; check R0's LSB JNC GETC IN_R0 SIO_DAT RET ; get nible from SIO ; R0 -> data GETNBL: CALL GETC LD_R1 '0' SUB_R0_R1 LD_R1 10 CP_R0_R1 JC GETNB9 LD_R1 <10 + '0' - 'A'> ADD_R0_R1 GETNB9: RET ; get byte data from SIO ; R0 -> data ; ZF -> 1:zero GBYTE: CALL GETNBL SHL_R0 SHL_R0 SHL_R0 SHL_R0 WR_R0 GBYTMP CALL GETNBL LDF_R1_R0 RD_R0 GBYTMP OR_R0_R1 RET ; get word data from SIO ; WDATA -> data GWORD: CALL GBYTE WR_R0 <WDATA + 1> ; save high byte CALL GBYTE WR_R0 WDATA ; save low byte RET ; put string to SIO ; R0 <- pointer ; caution:pointer's high adr is 01H PUTS: DEC_R0 WR_R0 PUTSP PUTSL: RD_R0 PUTSP INC_R0 WR_R0 PUTSP RD_R1_R0 01H LDF_R0_R1 ; if R1 is 0 then set ZF JNZ PUTS10 RET PUTS10: CALL PUTC JP PUTSL if 0 ; disp 1byte with Hex ; R0 <- data DspHxB: WR_R0 DspHx SHR_R0 SHR_R0 SHR_R0 SHR_R0 CALL DspH10 RD_R0 DspHx LD_R1 0FH AND_R0_R1 DspH10: LD_R1 '0' ADD_R0_R1 LD_R1 <'9' + 1> CP_R0_R1 JC PUTC LD_R1 <'A' - 10 - '0'> ADD_R0_R1 JP PUTC endif ; display hex line type ; TYPE <- type data DSPTYP: CALL GBYTE ; read check sum CALL GETC ; read delimitor RD_R0 TYPE LD_R1 '0' ADD_R0_R1 JP PUTC MSG: DB 'HexLoader Start',13,10,0 MSGERR: DB ' err', 13,10,0 MSGEX: DB ' exec',13,10,0 ; ORG WORK ;GBYTMP: DS 1 ; GBYTE's tmp value GBYTMP EQU WORK ;PUTSP: DS 1 ; PUTS's pointer PUTSP EQU GBYTMP + 1 ;TYPE: DS 1 ; Hex line type TYPE EQU PUTSP + 1 ;COUNT: DS 1 ; data count COUNT EQU TYPE + 1 ;JPAD: DS 2 ; jump addr JPAD EQU COUNT + 1 ;WRPNT: DS 1 ; WR_R0 ope code WRPNT EQU JPAD + 2 ;WDATA: DS 2 ; GWORD's return value WDATA EQU WRPNT + 1 ;WRPRET: DS 1 ; RET ope code WRPRET EQU WDATA + 2 ;DspHx DS 1 DspHx EQU WRPRET + 1 ;GETTMP: DS 1 GETRMP EQU DspHx + 1 END


 HexLoader試験のために作成したテストプログラムはHelloを表示する単純なものです。

ローダー試験用プログラム
;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z Loader test program ; by skyriver ; V0.01 2021/03/25 ;++++++++++++++++++++++++++++++++++++++++ .xlist include machine.inc .list START EQU 8000H ; RAM's top address SIO_CMD EQU 0C0H SIO_DAT EQU SIO_CMD + 1 ASEG ORG START LD_R0 <(LOW MSG) - 1> ; x64(4) R0:5F R1:C0 WR_R0 saveR0 ; x75(11) R0:5F R1:00 LOOP: RD_R0 saveR0 ; x86(11) R0:5F R1:00 INC_R0 ; x89(3) R0:60 R1:00 WR_R0 saveR0 ; x100(11) R0:60 R1:00 RD_R1_R0 <HIGH MSG> ; x110(10) R0:60 R1:48 CKZR1 ; x113(3) R0:60 R1:48 LD_R0_R1 ; x116(3) JNZ LOOP1 ; x124(8) R0:60 R1:48 HALT LOOP1: CALL PUTC ; x148(24) JP LOOP ; x214(8) ; output character to SIO ; R0 <- data PUTC: LDF_R1_R0 ; x151(3) IN_R0 SIO_CMD ; x159(8) SHR_R0 ; x162(3) SHR_R0 ; x165(3) LD_R0_R1 ; x168(3) JNC PUTC ; x176(8) if tx not ready OUT_R0 SIO_DAT ; x184(8) RET ; x206(22) MSG: DB "Hello,world", 13,10,0 saveR0: DS 1 END


 上記の試験用プログラムをアセンブル&リンクすることで下記のヘキサファイルが生成されます。

試験用プログラムのアセンブル&リンクで得られたヘキサファイル
:2080000010255034806034803D50348080804F2FC31380FFB01980C004804098C03A3A2F37
:15802000C5198090C1E048656C6C6F2C776F726C640D0A00005D
:00000001FF


 今回作成したHexLoaderはhexFileを1行読む度にレコードタイプを表示するようにしているのでデータ行数分'0'を表示後、終了レコードのレコードタイプである'1'を表示し、ロードしたアプリケーションを実行します。この時の実行アドレスは先頭のデータ行のアドレスにしています。
 HexLoaderを起動し、上記の試験用プログラムのHexFileをロードしている様子が下図の画面キャプチャーです。

HexLoader実行画面例



★追記 2021/03/25
 Twitterにポストした動画付きメッセージを貼っておきます。




[TOP] [ 前へ ] 連載記事 [ 次へ ]
nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー