Old MC6800(Home) > 新しい基板 > MC6800 ソフトウェア > Flow Control
(MC6800 ソフトウェア)  フローコントロール (Old MC6800) 

Hardware Flow control の /RTS と RXD の波形 Flow control の リングバッファのイメージ

 通信速度を自由に選べますフローコントロールにより、CPU の処理が終わるまで相手の送信を一時停止させます
目  次内  容更新日
[0] 始めにフローコントロールとは 処理と受信を独立させる 相手の送信を止めるには2018/04/22
[1] 機能部分毎に実験 してみるリングバッファ  相手の送信を一時停止  割込による受信処理
[2] フローコントロール のテスト定量的に確認 (/RTS信号と RXD(又は IRQ)信号の波形観測) 
[3] 逆アセンブラ に組込んでみる劇的に速くなった  処理速度は 約4.6倍(9600bps)〜16倍(38400bps)
[4] BASIC に組込んでみる 
   MICRO BASIC V1.3Aいきなり暴走? フリーズ?      (Robert Uiterwyk作 SWTPC版)
   Tiny BASIC (6800電大版)処理の冒頭で割込禁止? 理由は ...  (bit誌(1978/08)に掲載されたもの) 



[0] 始めに
フォーマットの混乱を防ぐため、ここでは モトローラSフォーマットの拡張子は「.S19」、
インテルヘキサフォーマットの拡張子は「.HEX」としました (ノートパッドなどで開いて確認して下さい)

ここでは、オリジナルの MIKBUGを基にワークエリア($A000-$A07F)を記述しています
ワークエリアを $1F00-$1F7Fに変更している MIKBUG互換モニタでは $A0 を $1F と読み替えて下さい
一部では両方記述している場合も有り、統一性が無くて申し訳ありません

通信(受信)速度が、処理速度より遅ければ、フロー制御は全く必要有りません
オリジナルの MIKBUG のソフトウェアシリアル変換では、1200bpsまでしか通信出来ませんでした
処理の方が速いため、通信のデータ受信待ちに多くの時間を費やしていました

ACIA のハードウェアのシリアル変換では、115200bpsでも通信が出来てしまいますので
通信のデータ受信で、相手の送信を一時停止しておく制御が必要になって来ました

フローコントロール(フロー制御)とは
wikipediaによると「コンピュータネットワークにおいて、2つのノード間で 高速な送信側 低速な受信側
オーバーランさせてしまうことを防ぐようデータ転送のレートを管理するプロセスである」とあります

フロー制御 はデータ通信インタフェースの制御線を使って行う場合(RS-232制御線)と、
制御文字をフロー制御用に予約しておいて使う場合(例えば、ASCII制御文字のXON/XOFF)があります

今回は、RS-232の制御線の、RTS(送信要求)/CTS(送信可)を使用して ハードウェアフロー制御 を行います
今の所、MC6800 側が受信する場合だけが問題なので RTS 信号の制御だけを行います
(XON/XOFF を使った ソフトウェアフロー制御 は、別の機会に試してみたいと思います)

処理と受信を独立させる
1文字単位に相手の送信を一時停止させる事は出来ないので、(理由は後述)
データをある程度まとめて受信して一旦バッファに格納する処理を独立させます
メインの処理では、データ受信の代わりに、バッファから1文字づつ取り出します

[フォアグラウンド] データ受信以外の処理をフル操業で行います(ほとんどの時間を費やします)
[バックグラウンド] データが到着した時だけ(割込処理で)受信データをバッファに格納します

相手の送信を止めるには
ハードウェアフロー制御の場合は、/RTS 信号を Hiレベルにします
「送信要求:RTS(Request To Send)」 は負論理なので、 Lo:送信要求、Hi:送信一時停止 となります
/RTS 信号の操作は ACIAのコントロールレジスタの設定で行います

MIGBUG への組み込みに付いて
モニタ内に組込むのは止めて、独立したユーティリティとして ROM化しておく事とします
理由は、
(1)コンパクトでシンプルな MIKBUGの良さを損なう様な改造はしたくない
  512Byte以内に収まらない改造はしない様にしてきました
(2)割込処理を使ったプログラムのデバッグに支障が出る
  入力ルーチンを「割込み処理」と「割込み無し」の2種類作って切り替えるのは複雑になる


フロー制御サービスルーチン(FLCTSR.zip) と フロー制御実験ソフト(FLCTSREX.zip) は
ダウンロードページ に置いてあります 参考にして下さい





[1] 機能部分毎に実験 してみる 
リングバッファ
 リングバッファの処理で注意する点

  (1)バッファの先頭と後尾をつなげて
    リング状にする時の「つなぎ目の処理」

  (2)バッファが空の状態から満杯の状態
    までの「バッファ内文字数の管理」
テストプログラムは、以下の構成です
  (1)ポインタ類の初期化と、バッファへ読み書きして結果を表示するメイン処理
  (2)バッファへの書込みルーチン
    書込みを行った後、書込みポインタを進め、読出しポインタに追い付いてしまったらエラーを返します
  (3)バッファからの読出しルーチン
    読出しを行った後、読出しポインタを進め、書込みポインタに追い付いてしまったらエラーを返します


相手の送信を一時停止する
ACIA(MC6850)のコントロールレジスタに、約0.5秒毎に $15 と $55 を交互に書きこむだけです
確認はオシロスコープで、/RTS信号と RXD信号を確認します
ターミナルから、適当なファイルを(Send fileで)送信して、/RTS信号に従って送信が一時停止すれば OKです

 赤色:/RTS 信号
  Hiで送信一時停止、Loで送信要求

 黄色:RXD (受信データ)
  /RTS = Lo の時だけ送られてくることが分かる



割込による受信処理
データ受信を割込処理で行うための手順は
(1)バッファサイズやワークの配置を決める
  今回は、バッファの配置は $1FC0-$1FF(64Byte)とします
  ワークエリアは $1F00-$1F7F の内 MIKBUGの動作に支障のない番地に割り当てます
  割込処理と入力処理のコードは(実験では)メインプログラムの最後に付属させます
(2)割込処理ルーチンを作成する
  MC6800 では割込み時に全てのレジスタをスタックに自動的に退避してくれます
  割込要因解析 ---> ACIA受信処理 ---> 割込から戻る(RTI)
(3)割込処理の初期化ルーチンを作成する
  割込処理で使うワークエリアの初期化(ポインタ、カウンタ)
  割込処理の先頭番地を、$1F00-$1F01($A000-$A001)番地にセットする
    本来は割込ベクタ($FFF9-$FFF9)にセットしますが、MIKBUGモニタでは
    ROMが配置されているので RAM上に再配置されています
  割込みで使うワーク類を初期化してから最後に、割込みの許可をする
    ACIAの受信割込みの許可(コントロールレジスタの設定)
    CPUの 割込みの許可(CLI)

割込処理で注意する点 割込みの許可は初期化の最後で行います

 実行例
 赤色:/RTS 信号
 黄色:RXD (受信データ)

  メイン処理:約1秒毎に 64文字づつバッファから
  読みだして表示する
  バッファが空になったら相手に送信要求をする

  割込み受信処理:バッファが一杯になるまで受信する
  一杯になったら、相手の送信を一時停止させる




フロー制御実験ソフト(FLCTSREX.zip) は ダウンロードページ に置いてあります 参考にして下さい



[2] フローコントロール のテスト
全体の構成
処理の構成は、前項の「割込による受信処理」とほぼ同じです
違いは、より実際の状態に近付けるためにメインのシミュレーション部分を
約0.5mS毎にバッファから1文字読み出してターミナルに送信する様にしました

受信側は1文字受信する毎に割込みがかかり、読込とバッファへの格納をします
バッファフルになると送信一時停止のために /RTS信号を Hiにセットします

入力ルーチンはバッファから1文字読み出してリターンします
入力ルーチンの最初でバッファの文字数をチェックして、空の場合は
/RTS信号を Loにセットして文字入力を待ちます

 実行例
 赤色:/RTS 信号
 黄色:RXD (受信データ)

  メイン処理:約0.5mS毎に1文字バッファから
  読みだして送信する
  バッファが空になったら割込みによる受信を再開させる

  割込み受信処理:バッファが一杯になるまで受信する
  一杯になったら、相手の送信を一時停止させる



 実行例
 赤色:TXD (送信データ)
 黄色:RXD (受信データ)

  送信データは、約0.5mS毎に1文字送っていますが
  受信は、通信速度が速いのですぐにバッファフルになります
  バッファが空になると受信を再開している様子が分かります



赤色:/RTS 信号、黄色:割込信号
/RTS = Hi による送信一時停止
/RTS = Hi になった後も 2文字受信している
赤色:/RTS 信号、黄色:割込信号
/RTS = Lo による送信開始
/RTS = Lo になった後すぐに送信開始している

/RTS = Hi にしても、相手の送信は直ぐに止まらず2文字送られて来る事が分かりました
この2文字は、通信レートを変えて実験しても同じでした
この2文字送られて来る原因は、PC側の OSなのか、ターミナルソフトなのか
それとも、ハードウェアなのか、分かりません

フロー制御でバッファフルのチェックをする時、2文字以上前で
送信一時停止(/RTS = Hi)にする必要があります
今回は、少し余裕をみてバッファフルの4文字手前で /RTS = Hi としました


フロー制御実験ソフト(FLCTSREX.zip) は ダウンロードページ に置いてあります 参考にして下さい




[3] 逆アセンブラ に組込んでみる
逆アセンブラ本体にフロー制御サービスルーチンを付属させたバージョン
DIS68(逆アセンブラ)の末尾に「初期化・1文字読込・割込処理」の各ルーチンを追加して
MIKBUG のスタック PC($1F48-$1F49)に初期化ルーチンの開始アドレスをセットしています
初期化の後、DIS68のスタートアドレス($0100)にジャンプする様にしてあります
最後に「S9」ブロックを読み込むと、割込み禁止にして MIKBUGに戻ります

使い方
CPU:1MHz の場合のターミナルソフトの設定
Baud rate:38,400bps (これ以下に設定)
Flow control: hardware
Transmit delay 0 msec/char 1 msec/line  とします

フロー制御は「Transmit delay 0 msec/char 0 msec/line」としても問題なく動きますが
MIKBUGのローダは「Transmit delay 0 msec/char 1 msec/line」でないと追い付かないので
ターミナルソフトの設定をローダ側に合わせます(これでも十分速いです)

MIKBUGの「L」コマンドで、ロードファイルを読み込んで「G」コマンドで起動します
「DIS68」のプロンプトが表示されたら、逆アセンブルしたい(モトローラヘキサ)ファイルを
ターミナルソフトの窓にドラグ&ドロップします
(結果をファイルに落とすには、逆アセンブルの前にターミナルソフトのロギングの設定をしておきます)

MIKBUGから起動する フロー制御サービスルーチンを付属させた DIS68(逆アセンブラ)
  このソースファイルは、自分自身を逆アセンブルしたファイルを基に復元したものです
  ジャンプ先の絶対番地の前に機械的に "L_" を加えてラベル化しただけですが、
  再アセンブル可能なソースになっています (一部は読み易いラベルに変えてあります)
  ORG を変えればロードアドレスを変えられます

フロー制御を付属させた DIS68(逆アセンブラ) は ダウンロードページ に置いてあります 参考にして下さい


ROM化した「フロー制御サービスルーチン」をサブルーチンコールするバージョン
1文字入力ルーチンを MIKBUGの INEEE($E1AC)の代わりに、フロー制御の INFFF($E200)に置き換えてあります
フロー制御初期化ルーチンを実行した後、この DIS68(逆アセンブラ)を起動する必要があります
フロー制御初期化ルーチンを実行すると MIKBUGは動かなくなるので、初期化ルーチンにターゲットに
ジャンプする機能を付けました ($1F4A-$1F4B にセットしたアドレスにジャンプ)

名前開始番地処理内容仕様・備考  (サービスルーチンの配置:$E200-$E29B)
INFFF$E2001文字入力サブルーチンMIKBUG の INEEE と同じ仕様
  $1F16:エコー設定(0:Echo ON)
  $1F15:UPPER CASE 変換(0:ON)
INITSR$E283初期化処理サブルーチンバッファ初期化、割込ベクタセット、割込許可(ACIA, CPU)
INIJPT$E27C初期化処理後
ターゲットにジャンプする
バッファ初期化、割込ベクタセット、割込許可(ACIA, CPU)
$1F4A-$1F4B にターゲットの実行アドレスをセットしておく
CKEMPT$E242バッファチェックサブルーチンブレークチェック用 結果を C(キャリー)で引渡す
  C=0:文字数=0
  C=1:文字数>0
MIKBUGへの戻り方戻る前に
ACIAと CPUの割込み禁止する
(1)SEI, JMP $E0DE (ACIAを初期化する番地に戻る)
(2)SWI を実行する方法(割込禁止されレジスタ表示に戻る)
 (勿論、リセットしても割込禁止になります)
使用するワークエリア  ( )内はByte数説明・備考
IOV$1F00IO INTERRUPT POINTER(2)(MIKBUG)
BUFPTB$1FC0RING BUFFER(64)リングバッファ($1FC0-$1FFF)
XTEMP$1F12IX TEMP STORAGE(2)(MIKBUGと共用)
CHRCNT$1F14CHARACTER COUNT(1)リングバッファに書込まれた文字数
CVUPSW$1F15UPPER CASE FLAG(1)(MIKBUGと共用) 英小文字 --->大文字変換制御(0:変換 ON)
OUTSW$1F16ECHO FLAG(1)(MIKBUGと共用) エコー制御(0:エコー ON)
BPTW$1F17BUFFER POINTER WRITE(2)リングバッファへの書込み番地
BPTR$1F19BUFFER POINTER READ(2)リングバッファからの読出し番地
STARTV$1F48FLCTSR START VECTOR(2)(MIKBUG) (初期化処理のアドレスをセット)
TARGTV$1F4ATARGET START VECTOR(2)(FLCTSR) 初期化後ジャンプするターゲットアドレス
参照しているアドレス・サブルーチン説明・備考
ACIACS$8018ACIA CONTROL & STATUSACIA制御レジスタ
ACIADA$8019ACIA DATA REGISTERACIAデータレジスタ
OUTEEE$E1D1OUTPUT ONE CHAMIKBUGの1文字出力ルーチンを使用
(注記)(1) SEI により CPUの割込みを禁止する、MIKBUGの $E0DEに戻ることにより
       ACIAの制御レジスタに #$15がセットされ割込禁止となる
    (2) SWI により MIKBUGに戻ると CPUの割込が禁止され、ACIAは割込許可のままになるが
       割込みはかからないのでフロー制御は行われない
       他に割込みを使う場合は、ACIAを割込禁止にする必要がある


ROM化用の フロー制御サービスルーチン 及び これを使う DIS68(逆アセンブラ) は
  ダウンロードページ に置いてあります 参考にして下さい



使い方
  ターミナルソフトの設定は、上のバージョンとおなじです
  (a) 初期化処理ルーチン INITSR($E27C)を使う場合
    ターゲットプログラムに以下の変更をする
      ターゲットプログラムから最初に1回、初期化処理ルーチンを呼ぶ
      1文字入力ルーチンはフロー制御の INFFF($E200)を使う
      MIKBUGに戻る前に、ACIAと CPUの割込みを禁止する
    起動方法は、通常のプログラムと同じです

  (b) 初期化処理ターゲットにジャンプ INIJPT($E295)を使う場合
    ターゲットプログラムに以下の変更をする
      1文字入力ルーチンはフロー制御の INFFF($E200)を使う
      MIKBUGに戻る前に、ACIAと CPUの割込みを禁止する
    起動方法は
      初期化ルーチンのスタートアドレス($E295) を $1F48-$1F49 にセット
      ターゲットプログラムのスタートアドレスを $1F4A-$1F4B にセット
     「G」コマンドで起動します



実行結果
フロー制御を使わない場合
  ターミナルソフト(Tera Term)の設定は 「Transmit delay 1 msec/char 0 msec/line」とします
  DIS68(逆アセンブラ) は、これ以下の設定では正常に実行できませんでした
  この Transmit delay の設定単位は最小 1 msecなので、これ以下にはできません
  これは、文字送信した後 1mS 待って次の文字を送る設定なので、通信時間だけでも
  (1文字送信時間+1mS)× 文字数 の時間がかかります

  DIS68(逆アセンブラ) の処理時間の一番長い部分で、通信速度を決めなければならないので、
  短時間で処理できる部分ではデータ受信待ちの時間が多くなってしまいます


フロー制御を使った場合
  ターミナルソフト(Tera Term)の設定は 「Transmit delay 0 msec/char 0 msec/line」でも実行できます

  DIS68(逆アセンブラ) の処理時間の長短をフロー制御部が吸収します
    38,400bps 以下の通信速度では、処理時間は通信速度に比例しますが
    38,400bps 以上では、通信速度に依存せずに、CPUの処理速度(クロックの差)で決まります

DIS68(逆アセンブラ) 自身を、逆アセンブルした処理時間の比較
  (ターミナルソフトでタイムスタンプ付きのログを取り、その時刻から計算しました)
        
CPU:1MHz9,600 bps 38,400 bps 備考
フロー制御 無53.1 秒 51.9 秒 Transmit delay 1 msec/char に制約されている
フロー制御 有11.4 秒 3.2 秒 処理時間は通信速度に依存している

フロー制御 有 の場合の処理時間をさらに詳しく調べてみた

(通信速度・CPU速度 毎の)処理時間の測定結果
通信 \ CPU 1.0MHz 2.0MHz 2.5MHz 
9,600bps 11.4秒 11.4秒 11.4秒 
19,200bps 5.8秒 5.7秒 5.7秒 
38,400bps 3.2秒 2.9秒 2.9秒 
57,600bps *1(2.5秒)2.0秒 1.9秒 
115,200bps *2(ハング)1.3秒 1.2秒 
*1:この速度では MIKBUGのローダが追い付かない
  38,400bps でロード後、処理は 57,600bps で実行
*2:(推定)フロー制御が追い付いていないと思われる

CPUクロックが、1MHz の場合
  通信速度:38,400bpsまでが適している
  これ以上の通信速度では、MIKBUG(フロー制御無)のローダが動作しない

通信速度を、115,200bpsまで上げる場合
  CPUクロックを 2.0MHz以上にすると、MIKBUG(フロー制御無)のローダが動作する
  通信速度が十分に速いので、処理時間は CPUの処理速度(クロック)で決まる

通信・ターミナルソフト(Tera Term)のお勧めの設定(CPU Clock:1MHz)
Baud rate:38,400bpsPIC12F1822(32MHz)で作成できる限界
Flow control: hardwareMIKBUGでは常に /RTS=Lo なので問題ない
Transmit delay: 0 msec/char 1 msec/lineMIKBUGのローダが動く限界




[4] BASIC に組込んでみる 【一部書きかけの項目】
MICRO BASIC VERSION 1.3A
 (1)オリジナル紹介ページ:Robert Uiterwyk's Micro Basic  ソースコード掲載ページ
 (2)SBC6800データパック に含まれる MIKBUG互換(ACIA版)モニタへの 移植版「MICBAS13.ASM」

Tiny BASIC (6800 電大版)
オリジナルのアセンブラソースは、bit誌 1978年8月(Vol.10,No.8) P52-P57 に掲載されていたもの
MIKBUG互換(ACIA版)モニタへの 移植版


それぞれフロー制御の入力ルーチンを組み込んでテストしてみました
結果はどれも、全く動かなくなります(動かない理由を調べてみると)
  処理の中で SPレジスタを IXレジスタ代わりに使ったり、 MC6809のユーザSPの様な使い方をしています
  これは MC6800の IX と SP が1本づつしかない欠点をカバーして処理効率を上げているためと思われます

  SPがこの様に使われている場所が少なければその部分だけ割込禁止にすれば良いのでしょうが
  そのような場所が多数あるため、改造は諦めました

Tiny BASIC (6800 電大版) の場合は、処理の冒頭の初期化のところで、SEI(割込禁止)命令を実行しています
  最後まで、割込みを許可している所は有りません

MICRO BASIC VERSION 1.3A でも同様な処理をしていますが、割込みの考慮は、されていません
 (割込みがかかったら、即暴走します)

残念ながら、フロー制御を使いたかった BASIC には使えない事が分かりました




inserted by FC2 system