Skip to content

Instantly share code, notes, and snippets.

@kkismd
Last active January 27, 2023 13:16
Show Gist options
  • Save kkismd/edf0ad69f84c948ad81e666998280a40 to your computer and use it in GitHub Desktop.
Save kkismd/edf0ad69f84c948ad81e666998280a40 to your computer and use it in GitHub Desktop.

VTL-2 for the65c02(VTLC02)

本文書は、Michael T. Barry作のソフトウェア「VTLC02」 ソースコード中のコメントを日本語に翻訳したものです。

また見出しは、Markdownに整形するにあたり翻訳者が便宜上つけたものです。

原文リポジトリのURL https://github.com/barrym95838/6502-Assembly/blob/main/VTLC02

著作権表示

Original Altair 680b version by Frank McCoy and Gary Shannon 1977
2012: Adapted to the 6502 by Michael T. Barry

Copyright (c) 2012, Michael T. Barry
Revision B (c) 2015, Michael T. Barry
Revision C (c) 2015, Michael T. Barry
Revision C02 (c) 2022, Michael T. Barry All rights reserved.

概要

VTLC02は、軽量の「自己完結型」 IDE であり、コマンドライン、プログラム・エディタ、言語インタープリタを備え、そのすべてが 970 バイトの高密度65c02マシン・コードで構成されています。「唯一」欠けているのは、プログラムを保存する方法ですが、このコワルスキー・バージョン(訳注)では、シミュレータのホストからコードを貼り付けることを想定しています。

訳注: "Kowalski 6502 Simulator" で動作する版のこと
https://sbc.rictor.org/kowalski.html

ライセンス表示

Redistribution and use in source and binary forms, with or without modification, are permitted, provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

互換性

後述の違いを除いて、VTLC02はFrankの680bバージョンの下記に述べられた「公式に文書化された」動作を再現するために作られました。
http://www.altair680kit.com/manuals/Altair_680-VTL-2%20Manual-05-Beta_1-Searchable.pdf

680b版と65c02版は、すべての構文エラーを無視して、VTL-2プログラムが「正しい」と仮定して、それぞれ独自の方法で実行を続けようとします。 したがって、VTL-2のコードが勇敢(あるいは愚か)にも道を踏み外した場合の互換性は主張しません。

680b版と65c02版の違い

  • 入力時に{&}{*}が初期化されます。 (訳注: 後述のシステム変数)
  • ゼロで割ると商は65535、余りは被除数を返す(オリジナルの6800版ではフリーズした)。
  • 65c02はPC以外の16ビットレジスタを持たず、6800よりもレジスタ領域が少ないため、インタプリタはVTLC02の不明瞭な変数 {@ $ ( ) 0 1 2 3 4 5 6 7 8 9 < > : ?} を内部用に確保しています (680b版も同様の戦術でしたが細部が異なっていました)。 9レベルより深くネストされた括弧は、意図しない副作用をもたらすことがあります。
  • システム変数{>}で機械語のサブルーチンを呼び出したい場合は、まずシステム変数{"}に適切なアドレスベクトルを設定する必要があります。 (例えば "=768
  • xレジスタは、単純なVTLC02の変数を指すのに使われます。(8bitしかないため、680bバージョンのように明示的に配列要素を指すことはできません。)コメント内で var[x] はレジスタxが指すゼロページ変数(アドレスxx+1に存在)の16ビットの内容を参照します。
  • yレジスタは、VTLC02ステートメント内のポインタオフセットとして使用します(最大ステートメント長約128バイトを容易に扱うことができます)。コメント中の @[y] は、{@}(訳注: 後述する内部変数)の値にレジスタyを加算した16ビットアドレスを指します。
  • このインタプリタの動作は680b版と似ていますが、より65c02に近い形式に再編されています (65c02には bsr 命令がないので、呼び出し元の 128 バイト以内にサブルーチンを「詰め込む」ことは相対分岐にしか有利に働かない)。
  • このバージョンは、かなりきつく巻かれたオリジナルの移植をベースにしています。オリジナルの768バイトにおさめることはできませんでしたが、そこに近づけるため多くの構造化プログラミングの原則を犠牲にしています。65c02は、16ビット量を操作するために 6800 よりも多くの命令を必要としますが、65c02の平均クロック/命令比が低いことと、オリジナルのバージョンにはなかった最適化により、全体的な性能は向上しています。
  • VTLC02は、私の世界への無償の贈り物(?)です。その使用から生じるいかなる責任もVTLC02の価格(無)に限定されるという条件付きで、そうすることに興味のある人は誰でも自由にコピー、共有、修正することが可能です。

リビジョンB

2015:リビジョンBには、いくつかの空間の最適化(dclxviによる提案)と機能強化(mkl0815とKlaus2m5による提案)が含まれました。

  • ビット演算子 & | ^ (and, or, xor)
    例:

    A=$|128) 文字を取得し、hi-bitを設定する。
    
  • 絶対番地指定でメモリから8ビット値を取得、格納するには、{< @}機能を用います。
    例:

    <=P)PのI/Oポートをポイントする
    @=@&254^128)ビット0をクリア&ビット7を反転
    
  • VTL02Bからは、スペース文字は有効なユーザ変数でもなく、「有効な」二項演算子でもなくなりました。現在は、数値定数の終端として、また文字列やプログラムリストのプレースホルダーとしてのみ重要であり、人間の可読性を高めるために使用することができます。(実行速度とメモリ消費に若干の犠牲を払うことになる)

               (VTL-2)
    1000 A=1)         Init loop index
    1010 ?=A)           Print index
    1020 ?="")          Newline
    1030 A=A+1)         Update index
    1040 #=A<10*1010) Loop until done
    
               (VTL02B and later)
    1000 A = 1             ) Init loop index
    1010     ? = A         )   Print index
    1020     ? = ""        )   Newline
    1030     A = A + 1     )   Update index
    1040 # = A < 10 * 1010 ) Loop until done
    

リビジョンC

2015:リビジョンCには、さらなる機能強化が含まれています(Klaus1m5氏による提案)。

  • "THEN" と "ELSE" 演算子 [ ]
    • A[B は、Aが0の場合は0を返し、それ以外の場合はBを返します。
    • A]B は、Aが0の場合はBを返し、それ以外の場合は0を返します。
  • 1KBという制約の中で、インタプリタコードの密度とインタプリタ性能のバランスを取るための工夫がなされました。構造化プログラミングの原則は低い優先度のままでした。

変数名と定数の定義

VTLC02の変数は RAM アドレス $0080 から $00ff を占め、65c02 の伝統に従ってリトルエンディアンとなります。 小文字や一部の制御文字は許可されますが推奨されません。大文字やシステム変数とのエイリアシングにより、混沌とした結果になるでしょう。 アスタリスクのついた変数はインタープリタが内部で使用するものであり、警告なしに変更されることがあります。 {@ 0..9 : > ?} は、(通常)インタプリタによって傍受されるので、VTLC02の内部で「安全に」使用されます。しかし{; < =}については同様ではありません。そのため要注意です!

  • {@}* 内部ポインタ / mem byte

VTLC02標準ユーザ変数空間

  • {A B C ... X Y Z [ \ ] ^ _}

VTLC02システム変数空間

  • { } VTL02Bからは、スペース文字はユーザ変数としてもバイナリ演算子としても使えなくなり、数値定数の終端文字と文字列やプログラムリストのプレースホルダとして降格しています。
  • {!} リターン行番号
  • {"} ユーザ機械語サブルーチンベクトル
  • {#} 現在行番号
  • {$} 文字入出力
  • {%} 最後の除算の余り
  • {&} 配列開始へのポインタ
  • {'} 擬似乱数
  • {(}*古い行番号 / 入れ子の式の開始
  • {)} 入れ子の式の終了 / コメント開始
  • {*} フリーメモリの終了位置へのポインタ
  • {+ , - . /} 有効なユーザ変数

インタプリタ引数スタック空間

  • {0 1 2 3 4 5 6 7 8 9 :}

まれにしか使わない変数と引数スタックのオーバーフロー

  • {;} 有効なユーザ変数

  • {<}* peek/poke用のバイトポインタ

  • {=}* 有効なユーザー変数

  • {>}* 一時変数 / 機械語サブルーチンを呼び出す

  • {?}* 一時変数 / 端末入出力

  • nulstk ページ1に存在するシステムスタック(の開始アドレス)

Kowalski 65c02シミュレータのための定義

  • 「現在の入力行の破棄」キー
  • 出力のための改行文字
  • 行送り文字
  • 「最後に押下した文字の削除」キー
  • ビット演算 OR 演算子
  • 入力行バッファ
  • VTLC02プログラムはここから増加する
  • ... ユーザーRAMの最上部まで
  • インタプリタのコールドスタート点 (ウォームスタート点はstartok)
  • シミュレーターの端末I/O設定
  • シリアル通信 データ出力レジスタ
  • シリアル通信 データ入力レジスタ

ラベル・サブルーチン

vtlc02

プログラム領域ポインタの初期化およびVTLC02の起動

17バイト

  • {&} -> プログラムを空にする

  • {*} -> ユーザーRAMの上限を設定する

  • "OK"メッセージをリクエストする

start

VTLC02のコマンドラインをプログラムそのままに起動/再起動

27 bytes

  • 賢明な予防策

  • スタックポインタ初期化

  • キャリーフラグがクリアなら "OK" 表示を省略

  • (-4) (訳注: 2の補数表現)

  • コンソールに "\nOK\n" を表示

    • 1文字表示
    • y を進める
  • y レジスタが0になるまで続ける

user

  • ユーザーから1行入力
  • cvbin ルーチンの対象を {#} に設定
  • 入力された行が番号で始まっている?
    • no: ダイレクトステートメント実行

(ラベルなし)

プログラム行の削除/挿入/置換、プログラムリスト

7 bytes

  • {#} = 0?
    • no: 行を削除/挿入/置換する。

list_

プログラムをリスト表示して OKプロンプトを再開する

  • entry: キャリーはクリアでなければならない
  • uses: findln:, outch:, prnum:, prstr:, {@ (}
  • exit: findln: 経由でコマンドラインへ

21 bytes

  • {#}以上の行を探す
  • prnum用の行番号
  • 行番号を印字
  • 行長の代わりにスペースを1バイト印字
  • デリミタのための 0
  • 行の残りを印字
  • 次の行を用意

訳注: 最後の行で使われているbra命令は65C02で追加された無条件相対分岐 BRanch Always

progr

メインプログラム実行ループ

  • entry: user: から "beq direct" で (cs)
  • exit: findln: でコマンドラインへ または"beq start"(ゼロフラグセットのときstartへ分岐)

45 bytes

訳注: cs = キャリーフラグセット、cc = キャリーフラグクリア

  • {#} = 0 ならば無視して続行

  • そうでない場合 {#} が変化した?

    • yes: 適切な方向に条件付けられたキャリーフラグで分岐実行
    • no: 次のラインを実行 (cs)
  • VTLC02の分岐命令を実行

  • {!} = {(}+ 1 (リターンポインタ)

  • キャリーフラグを補完

  • {#}以上の最初の行/次の行を探す

  • 行長バイトをスキップ

  • キャリーフラグを保存(cc: program, cs: direct)

  • VTLC02のステートメントを1つ実行

  • {#}のゼロフラグを更新

  • プログラムモードなら継続

  • ダイレクトモードなら{#}が変化したか?

    • no: "OK" プロンプトから再開
    • yes: {#}からプログラムを実行

edit

プログラム行を削除/挿入/置換して、コマンドプロンプトを再起動する("OK "がなければ成功)

  • entry: キャリーはクリアされている必要があります
  • uses: find, start:, linbuf, {@ > # & * (}

147 bytes

  • linbufオフセットポインタを保存する

  • {@}{#}以上の最初の行にポイント

  • 行が既に存在しない場合は、削除処理をスキップする

  • y = 削除する行の長さ

  • {&} = {&} - y

  • {>} = {@}

  • 行を削除する

  • x = Linbufオフセットポインタ

  • 新しい行番号をシステムスタックにプッシュする

  • 新しい行の長さを y で決定し、ステートメント文字列をシステムスタックにプッシュする。

  • 空行ならば挿入処理をスキップ

  • 新しい行の長さをxに保存する

  • 新しいプログラム終端を計算する

  • {>} = {&} + y

  • もし {>} >= {*} ならば、プログラムは利用可能な RAM に収まらないので、スタックを削除して "OK" プロンプトにアボートする

  • プログラム内に新しい行を保持するのに十分な大きさの隙間をスライドさせて空ける

  • ステートメント文字列と新しい行番号をスタックからプルし、プログラムのその隙間に格納する

  • 行番号の後に長さを格納する

  • {&} = {>}

  • スタックをドロップ、コマンドプロンプトを再開する

prstr0

{?="...}(プリント文)の処理。exec から呼び出される

1 byte

  • デリミタをセットしてそのまま後続の処理へ続く

prstr

@[y]の文字列を表示する

x にはデリミタ文字が入りますが、これはスキップされて表示されません ('\0' は常にデリミタです)

キーが押された場合、次のキー押下を待って復帰します。

これらのキーのいずれかが ctrl-C であった場合、スタックをドロップし、ユーザープログラムに損傷を与えず "OK" プロンプトを再開します。

  • entry: @[y] -> string, x = 区切り文字
  • uses: pause:, outch:, skpbyte:, execrts:
  • exit: (通常) @[y] -> '\0' or デリミタの後ろの1バイトを指す
    (ctrl-C) スタックをドロップして "OK" プロンプトを再開

34 bytes

  • 次の1バイト

  • デリミタまたは'\0'があるか?

    • yes:終了
    • no: 文字を端末に出力してループする
  • 閉じデリミタを保存する

  • 中断やアボートをチェックする

  • 閉じデリミタを回収する

  • '\0'デリミタの後は常に'\n'

  • デリミタを飛ばす

  • 続きの文字が';'の場合、改行'\n'の出力を抑制する

  • '\n' を端末へ出力する。

exec

@[y]からの(願わくば)有効なVTLC02ステートメントを実行する。

  • entry: @[y] -> ステートメントの左端
  • uses: ほぼ全て
  • exit: 機械語サブルーチン{>=...}の注記
    • ユーザーはシステムスタックポインタ、テキストベースポインタ {@}、元の行番号 {(} 以外にはレジスタや変数は保存することを要求されない。

代入演算子の直後にダブルクォーテーション '"' があれば、左辺の変数名に関わらず {?="...} として実行されます。

83 bytes

  • 左辺の変数名を取り出す

  • 空行ならなにもしない

  • 全行コメントも同じ

  • 引数スタックを初期化

  • arg[{0}] -> 左辺の変数

  • 代入演算子をスキップ

  • 右辺はリテラル文字列か?

    • yes:文字列を表示
  • ';' が後置されているかチェック&リターン

  • 評価ポイントを arg[{1}]

  • arg[{1}]の右辺を評価

  • 左辺が配列要素だった?

    • yes: デフォルトのアクションまでスキップする
  • {@=...} ステートメントの場合 arg[{1}]の下位1バイト から({<})へを書き込む

  • {$=...} ステートメントの場合 arg[{1}]をASCII文字として印字

  • {?=...} ステートメントの場合 arg[{1}]を符号なし十進数として印字

  • {>=...} ステートメントの場合 ユーザー定義の機械語ルーチンを呼び出す

  • arg[{1}]を左辺の変数に格納する

  • 擬似乱数{'}を生成する

  • ユーザ機械語ルーチンにジャンプする

    • a:x (MSB:LSB) を arg[{1}] に格納
  • {"} は有効な6502コードを指す必要があります

prnum0

{?=...} の処理ハンドラ; exec: から呼ばれる

2 bytes

  • x -> arg[{1}], フォールスルー

prnum

var[x] を符号なし10進数 (0..65535) で表示する。 Vフラグの巧妙なトリックはJohn Brooksの好意によるものです。

  • entry: var[x] = 表示する数値
  • uses: outch:
  • exit: var[x] = a = 0

36 bytes

  • yを保存する

  • スタックに置く終わりの目印

  • 繰り返し {

    • ASCII数値をスタックに置く

    • 剰余 = 0

    • (商が0より大きければセットされる)

    • 16-bitの 10で割る処理

    • 部分剰余が基数の半分以上?

      • yes: 剰余を書き換えてVをセット 商が非ゼロの場合VとCをセット
    • 新しい商は段階ごとにvar[x]を置き換えます。

    • 新しい剰余は段階ごとにaを置き換えます

    • 16ビット除算を継続

    • 余りをASCIIに変換

  • } 桁がなくなるまで繰り返し

  • 終わりの目印に出会うまでスタックの数字を降順で表示

  • yを復元

訳注: quotient = 商, remainder = 剰余

eval

@[y]にある(願わくば)有効なVTLC02の式を評価し、その計算された値をarg[x]に置く。

VTLC02の式とは、1つ以上の項を演算子で区切られた '\0' または ')' で終端する文字列として定義されます。

項とは、変数名、十進定数、または括弧で囲まれた入れ子の式を指し、左から右へ厳密に評価されます。

変数名とは、ユーザ変数、{: )}で囲まれた配列要素式、またはシステム変数(副作用を持つ場合がある)として定義されます。

  • entry: @[y] -> 式となるテキスト, x -> 引数
  • uses: getval:, oper:,{@}, 引数スタック領域
  • exit: arg[x] = 結果, @[y] -> 次のテキスト

27 bytes

  • 最初の項をarg[x]に取り出す
  • 式の終わり '\0' または ')'?
    • yes: 完了
    • no: スタック疑惑演算子
  • 引数スタックポインタを進める
  • arg[x+2] = 次項の値
  • 取得して演算子をarg[x], arg[x+2]に適用する
  • 式終了までループする。

getval

@[y] にある項の数値を var[x] に取り込む。

有効な項の例: 123, $, H, (15-:J)/?)

75 bytes

  • @[y]は10進数の定数か?

    • yes: var[x]に取得してリターン
  • ユーザーライン入力?

    • yes: @[y]を保存(現在の式ポインタ)
  • ユーザからの式を入力

  • 評価して結果を var[x]に格納

  • @[y]を復元する

  • "?"をスキップしてリターン

  • ユーザからの文字入力か?

    • yes:1文字入力
  • メモリアクセス?

    • yes: ({<})の指すメモリから1バイト読み出す
  • 入れ子の式か?

    • yes: 再帰的に評価
    • no: はじめにその名前の変数のアドレスを var[x] にセット
      次にそのアドレスを実際の値に置き換えてからリターンする
  • 項の値の上位バイトを格納

  • 項の値の下位バイトを格納

mul

var[x] = (var[x]*var[x+2])mod65536 (符号なし)

  • uses: plus:, {>}
  • exit: var[x+2]{>} は変化する

39 bytes

  • 被乗数を{>}にコピーする

  • 積をゼロにして開始する

  • 絶対アドレッシングのCMP命令

訳注: アセンブラの疑似命令.dbで埋め込んでいる。
次の2バイトをオペランドとして解釈するなら
次の命令が rol 3,x$36 $03 なので
つなげると $cd $36 $03 となり cmp $0336 となる?(自信なし)

  • 被乗数 = 0 の場合、早期に終了する

  • 被乗数を右シフト

  • シフトアウトされたビットをチェック

  • var[x] の中に積を構成する

  • 乗数を左シフト

  • 乗数 = 0 になるまでループ

訳注: このルーチンで使われている stz 0,x は65C02の拡張命令で指定したアドレスにゼロをセットする STore Zero

convp

var[x]aレジスタで指定された変数のアドレスにセット

  • entry: a が変数名, @[y] -> 配列インデックスの式を表す テキスト (a = ':'の場合)
  • uses: plus:, eval:, oper8d:, {@ &}
  • exit: (eq): var[x] -> 変数, @[y] 変更されない
    (ne): var[x] -> 配列の要素, @[y] -> 続きのテキスト

19 bytes

  • 配列の要素?

    • no: 単純変数
    • yes: @[y] を配列インデックスとして評価し y を進める.
  • var[x] -> 2*index+&のアドレスにある配列要素

plus

var[x] += var[x+2]

14 bytes

minus

var[x] -= var[x+2]

  • expects: (cs),事前に減算された x

10 bytes

simple

次のセクションは、名前付き単純変数をASCII値からそのゼロページアドレスに変換するためのものです。この場合、'A'$82に、'!'$c2に、などのように変換されます。 冒頭で定義された定数定義の並び順と対応がとれていないといけません。 さもないと、奇妙で楽しくないバグが移植の旅で疲れた旅行者に降りかかることになります。

5 bytes

  • 形式の単純な変数アドレス
  • マッピング関数は (a*2)|128 です。

oper

変数var[x]var[x+2]に二項演算子を適用します。 有効なVTLC02の演算子は {* + / [ ] - | ^ & < = >} {>} は大なりイコール ≧ と定義されます。

未定義の演算子は3つの比較演算子のうちの1つとして解釈されます

37 bytes

  • 加算演算子?

  • 乗算演算子?

  • 除算演算子?

  • "then" 演算子?

  • "else "演算子?

  • (続く演算子から考慮) 減算演算子?

  • ビット単位の or 演算子?

  • ビット単位の xor 演算子?

  • ビット単位の and 演算子?

oper8b

比較演算子を変数var[x]var[x+2]に適用し、 結果を var[x] に格納する (1: 真, 0: 偽)

  • expects: (cs), 事前にデクリメントされた x

28 bytes

  • 0: '<' 1: '=' 2: '>'

  • 他の値は定義されていないが、何らかの結果は得られるであろう

  • var[x] -= var[x+2]

  • 等値判定?

    • yes: 高位バイトと下位バイトのORをとる
      (cs) from minus if 0
      (cc) if not 0.
  • var[x] = 1 (真), 0 (偽)

and_

var[x] &= var[x+2]

  • expects: 事前にデクリメントされた x

10 bytes

or_

var[x] |= var[x+2]

  • expects: 前にデクリメントされた x

10 bytes

xor_

var[x] ^= var[x+2]

  • expects: 前にデクリメントされた x

10 bytes

then_

A[B Aが0の場合は0を返し、それ以外の場合はBを返す

14 bytes

else_

A]B Aが0の場合はBを返し、それ以外の場合は0を返す

10 bytes

div

var[x] = var[x]/var[x+2] (符号なし), {%} = 余り

var[x] を0で割った場合の結果は {%} = var[x], var[x] = 65535

39 bytes

  • ループカウンタ

  • {%} = 0

  • 被除数は徐々に商になる

  • {%} は徐々に余りになる

  • 部分余り >= 除数?

    • yes: 部分剰余を更新し、部分商の下位ビットを設定
    • "cmp #" 裸のオペコード
  • ループ 16回

cvbin

@[y]がテキストの符号なし10進数定数の場合、それを数値変換して var[x] (mod 65536) に格納し yを更新する

  • entry: @[y]の指すテキストが定数を含んでいる可能性がある場合;
    先頭の空白文字はスキップされますが、変換開始後に空白文字に遭遇した場合は変換が終了します。
  • used by: user:, getval:
  • uses: mul:, plus:, var[x], var[x+2], {@ > ?}
  • exit: (ne): var[x] = 定数, @[y] -> 次のテキスト
    (eq): var[x] = 0, @[y] は変更なし
    (cs): 本当におかしなケース以外ではすべて

41 bytes

  • var[x] = 0

  • 先頭の空白を飛ばす

  • ポインタを保存する

  • 小数点を保存する

  • var[x] *= 10

  • 十進数を取り出す

  • var[x] += digit

  • 次の文字を取得

  • @[y] の文字が10進数でなければ変換を停止

  • (ne) は有効、(eq) は無効

inln6

ユーザからの入力行を受け取り、それをゼロ終端の linbuf に格納する (非常に原始的な編集/キャンセルが可能)

  • entry: (inln6ではなくinlnまたはnewlnにサブルーチンコールする)
  • used by: user:, getval:
  • uses: inch:, outnl:, linbuf, {@}
  • exit: @[y] -> linbuf

42 bytes

  • escape?

    • yes: 行を全部捨てる
  • 行のリミットを超えた?

    • no: そのまま続ける
  • yes: 行を全部捨てる

  • entry point: 新たな行を始める

  • {@} -> 入力ラインバッファ

  • キー入力を1つ受け取る(そしてエコー)

  • backspace?

    • yes: ひとつ前の文字を削除
  • enter?

    • yes: '\0' で置き換える
  • キー入力を行バッファに格納する

  • '\0' でなければ続ける

  • y = 0

skpbyte

スペース文字を無視して @[y] からbytesを取得

10 bytes

  • 現在の文字を読み飛ばす

  • スペース文字をすべて読み飛ばす

find

行番号が{#}以上のプログラムの最初の/次の行を探す

  • entry: (cc): プログラムの先頭で検索開始
    (cs): 次の行で検索開始
    ({@} -> 現在の行の先頭)
  • used by: edit:, findln:
  • uses: prgm, {@ # & (}
  • exit: (cs): {@} = {&}, x:a{(} は無効, y = 2
    (cc): {@} -> 見つかった行の先頭、y = 2、
    x:a = {(} = 実際に見つかった行番号

52 bytes

  • (cc): 検索は最初の行から始まります。

  • {@}-> 次の行 (次の行がない場合は {&} ...)

  • {@} >= {&} (プログラムの終わり?)

  • yes: 行が見つかりません (cs)

  • {(} = 現在の行番号

  • {(} < {#} ならば次の行を試す

  • 行が見つかった (cc)

findln

@[y]が行番号が{#}以上の最初の/次の行を指すようにする

  • entry: (cc): プログラムの先頭で検索開始
    (cs): 次の行で検索開始
    ({@} -> 現在行の先頭)
  • used by: list_:, progr:
  • uses: find:, prgm, {@ # & (}
  • exit: 行が見つからないときは "OK" プロンプトに戻る
    それ以外は {@} -> 見つかった行
    x:a = {#} = {(} = 実際の行番号, y = 2, (cc)

13 bytes

  • {#}以上の最初の行/次の行を見つける

  • 終わりなら "OK"プロンプトを再開する

  • {#} = {(}

  • スタックをドロップ、"OK" プロンプトを再開

Kowalski I/O routines

Kowalski シミュレータの I/O サブルーチン、Klaus2m5 の努力に感謝します。

pause

ユーザーのキー入力をチェックし、何も入力されなければ戻る。そうでない場合は、別のキーが押されるまで一時停止してからリターンする。キーがctrl-Cの場合、スタックを削除し、"OK "プロンプトを再開する。

23 bytes

  • キーが押されないと戻る

  • キーが押された?

    • no:すぐに戻る
  • ctrl-C?

    • yes:「OK」プロンプトに戻る
  • LFを無視する (Kowalski)

inch

I/Oウィンドウからaレジスタへのキー入力待ちとエコー ctrl-Cならスタックを落として "OK "プロンプトに戻る

3 bytes

  • キー入力待ち、フォールスルー

outch

ASCII 文字を I/Oウィンドウに表示

16 bytes

  • LF を CR に追加(Kowalski)

  • LF を送信レジスタから出力

  • 文字を送信レジスタから出力

okay

"\nOK\n" プロンプト

4 bytes

end

  • 開始アドレスをセット
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment