田舎の組み込みプログラマーがわざわざ趣味でも色々開発してみようとあがく様を綴るブログです。
DMM.makeのクリエイターズマーケットに出品しています。

ターミナルから指示してみる2018年05月15日

せっかくシリアルの送受信ができるようになったので、ターミナルからコマンドを入力して何かをやらせてみたいですね。

なので、ここまでの流れでLチカをターミナルから実行してみることにしました。
イメージとしてはこんな感じです。

ターミナルでLチカ

文字列を扱うのは面倒なのでコマンドは1文字とし、リターンキーで実行します。
"S"コマンドは数値を入力すると点滅時間の設定となり、コマンドのみでは現在の設定値を返すようにします。
そのため、LED_CONTROLには_GETアクションを追加しました。

そんなところで

いきなりソースです。

;==========================================================
PART$ TINY_TOKENIZER
;----------------------------------------------------------
ENUM$_SET 0
ENUM TYPE_CHAR
ENUM TYPE_NUM
ENUM TYPE_SPACE
ENUM TYPE_ENTER
ENUM TYPE_BS
ENUM TYPE_ESCAPE
C_BUFF_SIZE  .equ  10
c_type:      .blkb  1
c_index:     .blkb  1
c_buff:      .blkb  C_BUFF_SIZE
;==========================================================
  ; コンディション定義
  CONDITION$_DEFINE C_NANIKA  ; [何か入ってる]
  ; ステータス定義
  STATE$_DEFINE TKN_NULL      ; 何もない
  STATE$_DEFINE TKN_NUM       ; 数値<[何か入ってる]
  STATE$_DEFINE TKN_CHAR      ; 文字<[何か入ってる]

  ;-----------------------------------
  ; 何もない
  ;-----------------------------------
  STATE$ TKN_NULL, 0
    ; エントリー処理
    [ c_index ] = 0
  _PUT
    if [ c_type ] == TYPE_CHAR
      TRANS TKN_CHAR
    elif [ c_type ] == TYPE_NUM
      TRANS TKN_NUM
    endif
  _GO
    ; ヘラルドさんに実行を通達(命令が伝わっているかどうかは知らない)
    PROC$_CALL HERALD$_GO      
  _BACK
    ; ヘラルドさんに後退を伝える(後退できるかどうかは知らない)
    PROC$_CALL HERALD$_BACK    
  _ESCAPE
    ; ヘラルドさんに撤退を伝える(撤退できるかどうかは知らない)
    PROC$_CALL HERALD$_ESCAPE  
  _END

  ;=======================================
  ;[何か入ってる]
  ;=======================================
  CONDITION$ C_NANIKA
  _GO
    PROC$_CALL HERALD$_VALUE  ; ヘラルドさんにトークンを与える
    PROC$_CALL HERALD$_GO     ; ヘラルドさんに実行を通達
    TRANS TKN_NULL
  _BACK  ; BS 入力
    [ c_index ] = -- [ c_index ]
    if Z  ; 何もなくなった
      TRANS TKN_NULL
    endif
  _VALUE  ; トークン抽出
    PROC$_CALL HERALD$_VALUE  ; ヘラルドさんにトークンを与える
    TRANS TKN_NULL
  _ESCAPE
    TRANS TKN_NULL
  _END

  ;-----------------------------------
  ; 数値<[何か入ってる]
  ;-----------------------------------
  STATE$ TKN_NUM, C_NANIKA
  _PUT
    if [ c_type ] == TYPE_CHAR
      REDIRECT _ESCAPE  ; 俺はいらない
    endif
  _END

  ;-----------------------------------
  ; 文字<[何か入ってる]
  ;-----------------------------------
  STATE$ TKN_CHAR, C_NANIKA
    ; 俺は知らない
  _END

_PUT
  ; キャラクタータイプ判定
  [ c_type ] = TYPE_ESCAPE
  R0L = [ PARAMETER ].b
  if R0L <= 'z'
    ; コードの大きい方から判定
    if R0L >= 'a'  ; 小文字
      R0L = R0L - 20h    ; →大文字
      [ c_type ] = TYPE_CHAR
    elif R0L > 'Z'  ; Z より後ろ
      ; エスケープ扱い
    elif R0L >= 'A'  ; 大文字
      [ c_type ] = TYPE_CHAR
    elif R0L > '9'  ; 9 より後ろ
      ; エスケープ扱い
    elif R0L >= '0'  ; 数字
      [ c_type ] = TYPE_NUM
    elif R0L == ' ' || R0L == 0ah || R0L == 09h  ; SPC, LF, TAB
      [ c_type ] = TYPE_SPACE
    elif R0L == 0dh  ; CR
      [ c_type ] = TYPE_ENTER
    elif R0L == 08h  ; BS
      [ c_type ] = TYPE_BS
    endif
  endif
  ; キャラクタータイプに応じて処理を指示
  if [ c_type ] == TYPE_NUM || [ c_type ] == TYPE_CHAR
    if [ c_index ] < C_BUFF_SIZE
      mov.b c_index, A0
      [ c_buff[A0]] = R0L
      [ c_index ] = ++ [ c_index ]
      THROW  ; _PUT
    else ; バッファオーバー
      REDIRECT _ESCAPE
    endif
  elif [ c_type ] == TYPE_ENTER
    REDIRECT _GO
  elif [ c_type ] == TYPE_BS
    REDIRECT _BACK
  elif [ c_type ] == TYPE_SPACE
    REDIRECT _VALUE
  else
    REDIRECT _ESCAPE
  endif
;----------------------------------------------------------
_END
;==========================================================

;==========================================================
PART$ HERALD
;----------------------------------------------------------
;==========================================================
  ; コンディション定義
  CONDITION$_DEFINE C_WAIT_PARAM  ;[パラメータ待ち]
  CONDITION$_DEFINE C_READY       ;[準備完了]
  ; ステータス定義
  STATE$_DEFINE H_WAIT            ; 待機<[]
  STATE$_DEFINE H_B_CYCLE         ; 点滅周期<[パラメータ待ち]
  STATE$_DEFINE H_ON_READY        ; 点灯準備<[準備完了]
  STATE$_DEFINE H_OFF_READY       ; 消灯準備<[準備完了]
  STATE$_DEFINE H_BLINK_READY     ; 点滅準備<[準備完了]
  STATE$_DEFINE H_B_CYCLE_READY   ; 点滅周期<[準備完了]

  ;-----------------------------------
  ; 待機<[]
  ;-----------------------------------
  STATE$ H_WAIT, 0
  _VALUE  ; トークン
    R0L = [ c_buff ]
    if   R0L == 'O'    ; LED 点灯指令
      TRANS H_ON_READY     ; 点灯準備<[準備完了]
    elif R0L == 'F'    ; LED 消灯指令
      TRANS H_OFF_READY    ; 消灯準備<[準備完了]
    elif R0L == 'B'    ; LED 点滅指令
      TRANS H_BLINK_READY  ; 点滅準備<[準備完了]
    elif R0L == 'S'    ; LED 点滅時間 設定/返答
      TRANS H_B_CYCLE      ; 点滅周期<[パラメータ待ち]
    else
      RET$_FALSE
    endif
  _END

  ;=======================================
  ;[パラメータ待ち]
  ;=======================================
  CONDITION$ C_WAIT_PARAM
  _BACK  ; 後退
    TRANS H_WAIT
  _ESCAPE  ; 撤退
    TRANS H_WAIT
  _END

  ;-----------------------------------
  ; 点滅周期<[パラメータ待ち]]
  ;-----------------------------------
  STATE$ H_B_CYCLE, C_WAIT_PARAM
  _VALUE  ; パラメータ来た
    CHAR2VAL$_CALC c_buff, [c_index].b  ; 数値文字列→数値
    TRANS H_B_CYCLE_READY
  _GO    ; パラメータなし
    PROC$_CALL LED_CONTROL$_GET  ; 現在の設定値→R0
    VAL2CHAR$_UW R0       ; 符号なしでキャラクター変換
    VAL2CHAR$_GET R0L     ; 変換したキャラクターを順次受け取る
    for R0L != -1
      PROC$_CALL$_B S0T$_PUT, R0L   ; シリアルにキャラクターを送る
      VAL2CHAR$_GET R0L             ; 変換したキャラクターを順次受け取る
    next
    PROC$_CALL$_B S0T$_PUT, 0dh     ; [CR]
    TRANS H_WAIT
  _END

  ;=======================================
  ;[準備完了]
  ;=======================================
  CONDITION$ C_READY
  _VALUE  ; もうパラメーターはいらない
    TRANS H_WAIT
  _BACK  ; 後退
    TRANS H_WAIT
  _ESCAPE  ; 撤退
    TRANS H_WAIT
  _END

  ;-----------------------------------
  ; 点灯準備<[準備完了]
  ;-----------------------------------
  STATE$ H_ON_READY, C_READY
  _GO
    PROC$_CALL LED_CONTROL$_ON
    TRANS H_WAIT
  _END

  ;-----------------------------------
  ; 消灯準備<[準備完了]
  ;-----------------------------------
  STATE$ H_OFF_READY, C_READY
  _GO
    PROC$_CALL LED_CONTROL$_OFF
    TRANS H_WAIT
  _END

  ;-----------------------------------
  ; 点滅準備<[準備完了]
  ;-----------------------------------
  STATE$ H_BLINK_READY, C_READY
  _GO
    PROC$_CALL LED_CONTROL$_UNIQUE
    TRANS H_WAIT
  _END

  ;-----------------------------------
  ; 点滅周期<[準備完了]
  ;-----------------------------------
  STATE$ H_B_CYCLE_READY, C_READY
  _GO
    CHAR2VAL$_GET$_W R0      ; パラメータの数値取得
    PROC$_CALL$_W LED_CONTROL$_SET, R0
    TRANS H_WAIT
  _END
;----------------------------------------------------------
_END
;==========================================================

イメージでは解析器は1つにしていましたが、実際には2段階になっていて、この辺りは字句解析→構文解析のお約束ですね、多分。

呼び出し部分のソースは掲載していませんが、シリアルで受信した文字をTINY_TOKENIZERに_PUTすることで動作します。

TINY_TOKENIZERはパート部分でキャラクタータイプの判定だけを行い、文字か数字ならバッファに入れて、あとはアクティブステートに投げます。
その際、キャラクタータイプによって後に続く処理が限定されるので、処理をリダイレクトします。
例えば[CR]が来た場合は何かを実行することになりますので、アクティブステートに対して_GOを投げるといった具合です。

区切りとなる文字が入るとトークンが確定しますので、後処理であるHERALDに投げます。
変数渡しなのがちょっとアレですが、アセンブラですのでご容赦ください。

HERALDは受け取ったトークンが有効なコマンドなら対応する状態に遷移します。
で、受け取った指示とその時の状態によってやることが変わるわけで、状態遷移処理の真骨頂と言えると思います。

そしてお約束の

デバッガ画面です。

S コマンド動作結果

今回はソースコード量に比べて説明がアッサリ過ぎますが、この辺で。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
日本標準時子午線の都市名をかたかなで

コメント:

トラックバック

このエントリのトラックバックURL: http://trident.asablo.jp/blog/2018/05/15/8851364/tb