はじめに
『PASTA/80』という 8080 / Z80 用 Turbo Pascal 3.0 互換クロスコンパイラがあります。
『PASTA/80』は『Free Pascal』で書かれています。ソースコードの形で提供されているので、コンパイルして実行形式ファイルを作る必要があります。
これを見て、「...ひょっとして、PC-Gxxx 系用の Turbo Pascal コンパイラ作れるんじゃないか?🤔」 と思ったのが始まりでした。
See also:
PASTA/80
『PASTA/80』は 『sjASMPlus』 用のアセンブラソース (*.z80) を吐くコンパイラです。--ide オプションを付けて実行すると Turbo Pascal 3.0 のような IDE が起動します。
標準で CP/M-80 用のコードを吐きますが、ターゲットを変更する事もできます。
ターゲットが CP/M-80 の場合、IDE での実行 (Run) やコマンドラインパラメータに --run が指定された場合、エミュレータ『tnylpo』でバイナリが実行されます。
| 環境 | エミュレータ |
|---|---|
| CP/M-80 | tnylpo |
| ZX Spectrum 48K | Fuse |
| ZX Spectrum 128K | Fuse |
| ZX Spectrum Next | CSpect |
| Agon Light/Console8 | Fab Agon Emulator |
エミュレータの実行ファイルがパスに存在しない場合にはもちろん実行されず、ターゲット用の *.bin ファイルだけがコンパイル時に出力されます。ターゲットは IDE の Option やコマンドラインパラメータで任意に切り替えることが出来ます。
| 環境 | オプション |
|---|---|
| CP/M-80 | (なし) |
| ZX Spectrum 48K | --zx48 |
| ZX Spectrum 128K | --zx128 |
| ZX Spectrum Next | --zxnext |
| Agon Light/Console8 | --agon |
エディタは標準で『nano』が使われます。
Turbo Pascal 3.0 との相違点
PASTA/80 が Turbo Pascal 3.0 と 100% 互換のクローンかといえばそうではありません。後のコンパイラで取り入れられた機能の幾つかが PASTA/80 でも利用可能なため、とてもコードが書きやすくなっています。
| 相違点 | 実装 |
|---|---|
| 条件コンパイル指令 ({$IF}) | TP4.0 |
| Inc() および Dec() | TP4.0 |
| inline 指令 (inline 文のみで構成されたルーチン) | TP4.0 |
| (結果を破棄する) 関数呼び出し | TP6.0 |
| break および continue | BP/TP 7.0 |
| Include() および Exclude() | BP/TP 7.0 |
| 単一行コメント (//) | Delphi 2.0 |
| パラメータのないルーチンの空パラメータによる呼び出し | Delphi 2.0 |
| 結果を返す Exit() | Delphi XE |
| 2 進数リテラル (%) | Delphi 11 Alexandria |
KeyPressed() や ReadKey() は RTL での実装となっています (すべてのターゲットで常に使えるとは限りません)。詳しくは PASTA/80 のドキュメントを確認してください。
また、PASTA/80 ver 0.99 時点では次のコンパイラ指令が使えるようです。
| 項目 | 指令 | 説明 |
|---|---|---|
| 絶対コード | {$A+} または {$A-} | 絶対コードの生成。再帰的呼び出し (リカーシブコール) が不可能なコードを生成するか、可能なコードを生成するか?再帰を含むコードでは {$A-} を指定しなくてはならない。C 言語の #pragma nonrec に相当。 |
| I/O エラーの制御 | {$I+} または {$I-} | I/O エラーチェックを行うか、行わずに自分で IOResult() で調べるか? |
| インクルードファイル | {$I ファイル名} | インクルードファイル (*.pas) を指定する |
| ライブラリファイルのリンク | {$L ファイル名} | ライブラリファイル (*.asm) を指定する |
| スタックチェック | {$K+} または {$K-} | ローカル変数用のメモリ空間としてスタックが利用可能か調べるか? |
| スタックサイズの指定 | {$M スタックサイズ} | スタックサイズを指定する (デフォルト: 4096) |
| ユーザー割り込みの制御 | {$U+} または {$U-} | すべての場所でユーザー割り込み (CP/M-80 だと〔Ctrl〕+〔C〕) を受け付けるか、入出力時のみユーザー割り込みを受け付けるか? |
条件コンパイル指令は次の通りです。
| 指令 | 説明 |
|---|---|
| {$define <id>} | 指定のシンボルを True に設定する |
| {$undef <id>} | 指定のシンボルを False に設定する |
| {$ifdef <id>} | id が定義されている場合、後続のソースコードをコンパイルする |
| {$ifndef <id>} | id が定義されていない場合、後続のソースコードをコンパイルする |
| {$else} | 直前の {$ifxxx} と次の {$endif} で囲まれたソースコードを、コンパイルするか無視するかを切り替える |
| {$endif} | 最後の {$ifxxx} 指令で開始された条件コンパイルを終了する |
| {$error <msg>} | エラーメッセージを表示してコンパイルを終了する |
ターゲット
ターゲットを追加するには pasta.pas 自体を改変する必要があります。
RTL
各ターゲット用に RTL が用意されており、ビルド時にコンパイルされてリンクされます。
RTL は PASTA/80 に静的リンクされるものではありません。各ターゲットで使われる 8080 / Z80 用のライブラリです。
RTL は最低でも次のルーチンを備えている必要があります。これらは Pascal の標準ルーチンから呼ばれます。
| ルーチン | 説明 |
|---|---|
| __init | 初期化用 |
| __done | 終了時 |
| __newline | 新規行 |
| __putc | 1 文字出力 |
| __puts | 文字列出力 |
| __getline | 文字列入力 |
| __checkbreak | ユーザー割り込みチェック。{$U+} 指令で使われる |
極端な話、標準ルーチンを使わないようにする事もできます。
;
; Startup
;
__init:
call main
;
; Shutdown
;
__done:
rst 0
;
; New line
;
__newline:
ret
;
; Print character to screen
;
__putc:
ret
;
; Print string to screen
;
__puts:
ret
;
; Get Line
;
__getline:
ret
;
; Check Break
;
__checkbreak:
ret
{$i system.pas}
end.
もちろん、これをやってしまうと Write() / Writeln() / Read() / Readln() 等は使えず、独自のルーチンを実装しなくてはならなくなります。{$U+} 指令を指定してもユーザー割り込みでは終了できません。
独自にターゲットを追加する際の最初期では、これ (最小限の実装) でもいいと思います。
PC-G850
PC-G850 は SHARP 製のポケットコンピュータです。
- PC-G850
- PC-G850S
- PC-G850V
- PC-G850VS
というバリエーションがありますが、基本的には同じものと考えて構いません。
『PC-G850VS』は SHARP が世に送り出した最後のポケットコンピュータです。
PC-G850 用 PASTA/80
PC-G850 用のターゲットを追加するには、
- コンパイラスイッチ
--g850を追加 - Run でエミュレータ『g800』を使うようにする
- メモリマップの設定
- RTL の作成
- バイナリ -> Intel Hex 変換 (G850 は Intel Hex 形式のファイルを読み込める)
等の作業が必要です。
『sjASMPlus』には --hex スイッチがあり、*.bin を Intel Hex (*.hex) へと変換する機能があります。
コマンドラインから単独で実行する分には正しく *.hex が出力されるのですが、(少なくとも Windows の場合) 外部プロセスとして Exec() で sjASMPlus を呼び出すと出力ファイル (*.hex) が壊れます。具体的にはバイナリ (*.bin) と Intel Hex (*.hex) が混ざりあったファイルが生成されます。
そして
そして出来上がったのがこちらになります。Windows 用の EXE も配布してあります。
IOCS (BIOS) の機能は大体 RTL に実装してあります。
さらに、エディタとして『Micro』を使えるように改変してあります。
Windows / Linux (WSL2) 以外の環境では試せていません。
See also:
- PASTA/80 (GitHub: @ht-deko)
- Micro (micro-editor.github.io)
- テキストエディタ Micro を Delphi 互換のキーバインドで使う (Qiita)
- SHARP PC-G850 解析資料 (akiyan.com)
- SHARP PC-G850 解析資料 第 6 版 (Vector)
- エミュレータで動かす (POKE COM)
- PC-G800系 IOCSコール (- Version 0 -)
- ポケコン PC-G850の解析とソフト集 (GADGET FACTORY)
- POKE COM
環境設定
Windows の場合、こんな感じで配置してください。
\Pasta80
│ pasta.exe
│
├─<misc>
│
└─<rtl>
アセンブラ (sjasmplus.exe) やエディタ (nano.exe / micro.exe)、エミュレータ (g800.exe) は pasta.exe と同じフォルダかパスの通った場所に置く必要があります。
| 環境 | エミュレータ | オプション |
|---|---|---|
| PC-G850 | g800 | --g850 |
PC-G850 をターゲットにするには IDE の Option で G850 を選択するか、コマンドラインパラメータ --g850 を指定します。
PC-G8xx エミュレータ『g800』 の最新版は IOCS コールのエミュレーションと互換 BASIC インタープリタが載っているため、実機から ROM を吸い出さなくても (実機を所持していなくても) ある程度のプログラムが動作します。
但し、最新版は Windows 環境でしかビルドできないようです。Linux 向けにビルドする場合にはバージョン 0.9.9 のソースコードを使う必要があるようです (要 ROM 吸出し)。
See also:
PC-G850 用 RTL
PC-G850 用 RTL (g850.pas) では次のルーチンが使えます。IOCS (BIOS) を Pascal のコードでそのまま使えるようにした感じです。
{ Print a single character to the LCD }
procedure PutChar(x, y: Byte; c: Char);
{ Print a single character (with Symbol) to the LCD }
procedure PutChar2(x, y: Byte; c: Char);
{ Repeatedly print a single character to the LCD }
procedure PutChars(x, y: Byte; c: Char; n: Byte);
{ Print a string to the LCD }
function PutString(x, y: Byte; s: string): Byte;
{ Write pixel data to LCD }
procedure WritePixels(x, y: Byte; var buf: byte; l: byte);
{ Scroll Up }
procedure ScrollUp;
{ Scroll Down }
procedure ScrollDown(x, y: Byte);
{ Tests if a key has been pressed (no wait) }
function Inkey: Byte;
{ Reads a character from the keyboard (wait) }
function GetChar: Byte;
{ Read pixel data from LCD }
procedure ReadPixels(x, y: Byte; var buf: Byte; l: Byte);
{ Power Off }
procedure PowerOff;
他にも Turbo Pascal 互換のルーチンが実装されています。
procedure ClrScr;
procedure GotoXY(X, Y: byte);
function KeyPressed: Boolean;
function ReadKey: Char;
function ReadKey2: Char; { ReadKey() の別実装 }
function WhereX: Byte;
function WhereY: Byte;
g850.asm では、RTL から呼び出されるアセンブリルーチンの他、標準ルーチンから呼ばれるアセンブリルーチンもすべて実装されています。
__init
__done
__newline
__putc
__puts
__getline
__checkbreak
アセンブラデバッグ用の IOCS も定義されています。
__regout ; レジスタの内容を表示
__reginp_a ; A レジスタに入力 (16進2桁)
__reginp_hl ; HL レジスタに入力 (16進4桁)
いずれにも属しない、その他のルーチンもあります。主に I/O ポート操作を抽象化したルーチンとなっています。
{ Beep }
procedure Beep(level, length, count: Integer);
{ Delay }
procedure Delay(ms: Integer);
{ InitJoystick }
procedure InitJoystick;
{ ScanKeyboard }
procedure ScanKeyboard(var Keys: TKeyArray);
{ ReadJoystick }
function ReadJoystick: byte;
PC-G850 でユーザー割り込みに使われるキーは〔BREAK〕です。
サンプル (WritePixels)
WritePixels() は次のような使い方になります。
program WritePixels;
var
i, l: Integer;
buf: array [0..5] of Byte;
begin
buf[0] := $AA;
buf[1] := $55;
buf[2] := $AA;
buf[3] := $55;
buf[4] := $AA;
buf[5] := $55;
for l:=0 to ScreenHeight-1 do
for i:=0 to ScreenWidth-1 do
WritePixels(i, l, buf[0], 6);
Readln;
end.
サンプル (Beep)
Beep() は次のような使い方になります。ドレミが流れます。
program DoReMi;
const
NOTE: array [1..8] of Byte = (105, 93, 82, 77, 68, 60, 52, 49);
var
a: Integer;
b: Byte;
c: Real;
begin
for a:=1 to 8 do
begin
b := NOTE[a];
c := (650 / (166 + 22 * b)) * 1000;
Beep(b, Trunc(c), 1);
end;
end.
実機はそのままでは音が鳴りません。圧電サウンダを追加してください。
音の長さは BASIC の BEEP とほぼ同じですが、音の高さは若干低いと思います。BEEP 1 に相当するコードは
Beep(7, 1400, 1);
となります。Pascal には可変長パラメータがなく、Turbo Pascal にはデフォルトパラメータもないため、パラメータの省略を行う事はできません。
サンプル (ScanKeyboard)
ScanKeyboard() は次のような使い方になります。〔A〕キーの入力を待ちます。
var
Keys: TKeyArray;
begin
Writeln('Start');
repeat
ScanKeyboard(Keys);
until (Keys[1] and $01) <> 0;
Writeln('Done.');
Readln;
end.
スキャンされたキーの情報は TKeyArray に格納されます。キーが押されていれば、対応するビットが 1 になるため、同時押しの検出が可能です。
| bit | [0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | OFF | A | Z | BASIC | ← | L | I | P | nPr | 2ndF |
| 1 | Q | S | X | TEXT | → | ; | O | BS | →DEG | sin |
| 2 | W | D | C | CAPS | ANS | CONST | INS | π | √ | cos |
| 3 | E | F | V | カナ | 0 | 1 | 4 | 7 | x2 | ln |
| 4 | R | G | B | TAB | . | 2 | 5 | 8 | yx^ | log |
| 5 | T | H | N | SPACE | = | 3 | 6 | 9 | ( | tan |
| 6 | Y | J | M | ↓ | + | - | * | / | 1/x | F⇔E |
| 7 | U | K | , | ↑ | Enter | M+ | R・CM | ) | MDF | CLS |
OUT(11h) と OUT(12h) の結果を配列に返した格好です 1。
サンプル (JoyStick)
11P I/F には PIO モードがあるため、ここにジョイスティック (複数のボタン) を接続する事が可能です。
PIO モード時のピンアウトは次のようになります。ジョイスティックは入力なので、2 番ピン (VCC) と 4~11 番ピン (Bit0~Bit7) の間にボタンを接続します。
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | VCC (+5V) (COM) |
GND | Bit0 | Bit1 | Bit2 | Bit3 | Bit4 | Bit5 | Bit6 | Bit7 |
画像の JoyStick 基板はシルク印刷 (GND と VCC) が逆にはなりますが、ただのスイッチなので無加工で接続できます。基板は 10 ピンなので 1 ピン目を空けて 2 ピン目から接続します。
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|
| GND (COM) |
VCC (+5V) | スティック押し込み | ↑ | ↓ | ← | → | KEY1 | KEY2 | KEY3 |
ジョイスティックの状態を読むコードは次のようになります。もちろん同時押しも判定できます。
program JoyStick;
{$u+}
var
JoyState: Byte;
begin
InitJoystick;
repeat
JoyState := ReadJoystick;
if JoyState <> 0 then
WriteLn(JoyState);
until false;
end.
MSX や X68000 のジョイスティックのうち、単純なボタンで構成されるものなら普通に使えるかと思います。
PC-G850 用グラフィックライブラリ
グラフィックライブラリも作りました。基本的に Turbo Pascal 3.0 のものを踏襲していますが、独自のものもあります。
インクルード指令で rtl\g850grph.pas を追加すると使えます。
{$i rtl\g850grph.pas}
説明
| ルーチン | 説明 |
|---|---|
| Box() | 四角形を描きます |
| Circle() | 円を描きます |
| Draw() | 線を描きます |
| FillBox() | 塗りつぶされた四角形を描きます |
| FillPattern() | Pattern() で登録されたパターンで四角形を塗りつぶします |
| FillScreen() | 画面全体を塗りつぶします |
| FillShape() | 指定された位置から境界まで塗りつぶします |
| GetDotColor() | 指定された位置の色を取得します ⇔ Plot()
|
| GetMask() | 内部マスクパターンテーブルから 8x8 のパターンを取得します (下位ビットが上) ⇔ Mask()
|
| GetPattern() | 内部パターンテーブルから 8x8 のパターンを取得します (下位ビットが上) ⇔ Pattern()
|
| GetSprite() | 指定された位置の 8x8 のパターンを内部パターンテーブルに登録します ⇔ PutSprite()
|
| Mask() | 8x8 のマスクパターンを内部マスクパターンテーブルに登録します (下位ビットが上) ⇔ GetMask()
|
| Pattern() | 8x8 のパターンを内部パターンテーブルに登録します (下位ビットが上) ⇔ GetPattern()
|
| Plot() | 指定された位置に点を描画します ⇔ GetDotColor()
|
| PutSprite() | Pattern() で登録されたパターンを指定された位置に描画します。必要であれば Mask() で登録されたマスクパターンと AND 演算して描画します ⇔ GetSprite()
|
| RepaintLCD() | すべての領域を LCD に描画します |
| RestoreBG() | BG 面から画面全体をブロック転送します ⇔ SaveBG()
|
| SaveBG() | 現在の画面を BG 面へブロック転送します ⇔ RestoreBG()
|
| UpdateLCD() | 指定された領域を LCD に描画します |
仮想 VRAM と BG 面があり、描画は常に VRAM に対して行われます。VRAM の内容は一部または全部を LCD へ描画する事が可能です。VRAM の内容を BG 面へ退避させ、後で合成する事も可能です。
BG 面を使わない事も可能です。
program Skeleton;
{$u+}
{$i rtl\g850grph.pas}
var
ptn: TPattern;
Keys: TKeyArray;
BGBuf: array [0..5] of array [0..143] of Byte;
begin
{ 初期化 }
ClrScr;
{ 背景の生成 }
SetBGAddr(Addr(BGBuf)); // BG 用のバッファを設定 (BG を使う際に必須)
Pattern(ptn); // パターン登録
FillPattern(0, 0, 50, 30, clBlack); // 背景を描く
SaveBG; // VRAM を BG へ
FillScreen(clWhite); // VRAM をクリア (ClrScr でも可)
{ メインループ }
while True do
begin
{ 入力処理 }
ScanKeyboard(Keys);
{ 自分や敵の座標を動かす }
{ 描画処理 }
RestoreBG; // BG を VRAM へ
PutSprite(x, y, False); // 新しい位置にキャラを描く
Box(10, 10, 50, 20, clBlack); // UI を描く
{ 画面への反映 }
RepaintLCD; // VRAM を LCD へ
end;
end.
いわゆるダブルバッファ方式なので、RepaintLCD() あるいは UpdateLCD() を実行しないと描画が LCD に反映されない事に注意が必要です。
スプライトやパターン塗りつぶしで使われる内部パターンテーブルは 1 つしかないので、スプライトデータは Pascal 側で管理する必要があります。
| From | To | ルーチン |
|---|---|---|
| (内部パターンテーブル) | VRAM | PutSprite() FillPattern() |
| (内部パターンテーブル) | TPattern | GetPattern() |
| (内部マスクパターンテーブル) | TPattern | GetMask() |
| TPattern | (内部マスクパターンテーブル) | Mask() |
| TPattern | (内部パターンテーブル) | Pattern() |
| VRAM | (内部パターンテーブル) | GetSprite() |
VRAM や BG、内部パターンテーブルのアドレスは以下のルーチンで取得可能です。
| ルーチン | 説明 |
|---|---|
| GetVramAddr() | VRAM の先頭アドレスを得る |
| GetVramAddrXY() | VRAM のアドレスを (X ピクセル, Y ライン) から得る |
| GetBGAddr() | BG の先頭アドレスを得る |
| GetBGAddrXY() | BG のアドレスを (X ピクセル, Y ライン) から得る |
| GetMaskAddr() | 内部マスクパターンテーブルの先頭アドレスを得る |
| GetPatternAddr() | 内部パターンテーブルの先頭アドレスを得る |
BG、パターンテーブル、マスクパターンテーブルのアドレスを指定する事も可能です。これにより、データコピーせずにスプライトを描画する事が可能になります。
| ルーチン | 説明 |
|---|---|
| SetBGAddr() | BG 用バッファへのアドレスを設定します |
| SetMaskAddr() | マスクパターンテーブル用バッファへのアドレスを設定します |
| SetPatternAddr() | パターンテーブル用バッファへのアドレスを設定します |
Turbo Pascal には mem[] 配列 というものがあるため、これを使った VRAM 等への直接アクセスが可能です。
最新版は GitHub のリポジトリ で確認してください。
サンプル
グラフィックライブラリは次のような使い方になります。
program GraphTest;
{$u+}
{$i rtl\g850grph.pas}
var
ptn: TPattern;
i: Integer;
vram: Integer;
begin
FillBox(8, 8, 39, 39, clBlack);
Box(12, 12, 35, 35, clwhite);
Circle(60, 24, 16, clBlack);
Draw(60, 8, 60, 39, clBlack);
Draw(44, 24, 75, 24, clBlack);
FillShape(62,10, clBlack);
FillShape(58,26, clBlack);
ptn[0] := $70; ptn[1] := $18; ptn[2] := $7D; ptn[3] := $B6;
ptn[4] := $B6; ptn[5] := $7D; ptn[6] := $18; ptn[7] := $70;
Pattern(ptn);
FillPattern(80, 8, 119, 39, clBlack);
Box(124, 15, 132, 23, clBlack);
FillBox(124, 24, 132, 31, clBlack);
Plot(126, 17, clBlack);
Plot(130, 17, clBlack);
Plot(128, 19, clBlack);
Plot(126, 21, clBlack);
Plot(130, 21, clBlack);
Plot(126, 25, clWhite);
Plot(130, 25, clWhite);
Plot(128, 27, clWhite);
Plot(126, 29, clWhite);
Plot(130, 29, clWhite);
PutSprite(136, 40, False);
RepaintLCD;
vram := GetVramAddr;
while True do
begin
GotoXY(0, 0);
Readln;
for i := 0 to 863 do
Mem[vram + i] := not Mem[vram + i];
RepaintLCD;
end;
end.
実行すると次のようになります。
このグラフィックライブラリを利用したグラフィックローダーを生成するアプリケーションを作りました。144x48 サイズの BMP から、グラフィックローダーを生成します。
PC-G850 用 I/O ライブラリ
I/O ライブラリも作りました。11P I/F を PIO モードにする事により、Arduino のような感じで使うことが出来ます。
インクルード指令で rtl\g850io.pas を追加すると使えます。
説明
| ルーチン | 説明 |
|---|---|
| DigitalRead() | 指定したピンの値を読み取ります。その結果は iolHigh (1) または iolLow (0) となります。 |
| DigitalWrite() |
iolHigh (1) または iolLow (0) を、指定したピンに出力します。 |
| PinMode() | ピンの動作を入力 (iomInput) か出力 (iomOutput) に設定します。 |
| SetIFMode() | 11P I/F のモードを設定します (ifmNormal, ifmPIO, ifmUART)。 |
サンプル (PIO)
11P I/F を使って Arduino のような事も可能です。例えば先のジョイスティックの状態を読むコードは次のようにも書けます。
program JoyStick;
{$u+}
{$i rtl\g850io.pas}
var
i, JoyState: byte;
begin
SetIFMode(ifmPIO);
for i:=0 to 7 do
PinMode(i, iomInput);
repeat
JoyState := 0;
for i:=7 downto 0 do
JoyState := (JoyState shl 1) or DigitalRead(i);
if JoyState <> 0 then
Writeln(JoyState);
until false;
end.
以下は Arduino の Blink を模倣した L チカのコードです。
program Blink;
{$u+}
{$i rtl\g850io.pas}
procedure Setup;
begin
SetIFMode(ifmPIO);
PinMode(bit0, iomOutput);
end;
procedure Loop;
begin
DigitalWrite(bit0, iolHigh);
Delay(1000);
DigitalWrite(bit0, iolLow);
Delay(1000);
end;
begin
Setup;
while True do Loop;
end.
抵抗入り LED を用意し、足の短い方を上から 3 番目のピン (GND)、足の長い方を 4 番目のピン (Bit0) に挿して実行すると LED が 1 秒おきに点滅します。
IN / OUT 命令
アセンブラの IN / OUT 命令 (BASIC での INP / OUT 命令) は Turbo Pascal だと Port[] 配列に相当します。
以下のサンプルコードはキースキャンを行い、押されたキーのマトリクスの位置をドットで表示するものです。
program KeyScan;
{$u+}
{$i rtl\g850grph.pas}
var
vram: Integer;
i: byte;
begin
vram := GetVramAddrXY(8, 1); // X = 8, Y = 8 (Line #1)
ClrScr;
Box(7, 7, 18, 16, clBlack);
RepaintLCD;
while True do
begin
for i:=0 to 7 do
begin
Port[$11] := 1 shl i;
mem[vram + i] := Port[$10];
end;
Port[$11] := 0;
for i:=0 to 1 do
begin
Port[$12] := 1 shl i;
mem[vram + i + 8] := Port[$10];
end;
Port[$12] := 0;
UpdateLCD(8, 8, 17, 15);
end;
end.
サンプル (ScanKeyboard) の所にあるマトリクス表を参考にしてください。
インライン機械語や外部アセンブラルーチンを書かなくともポートを操作する事ができます。
実機でのテスト
実機との通信には、ぽよこまだんな氏作『SHARP PC-G850 用 USB シリアル変換モジュール』を使いました。
USB シリアル変換モジュールの自作も、それほど難しくはありません。
Intel Hex の転送には『Tera Term』が使えます。
skyriver 氏作の『DropHx』を組み込んでおくと高速転送できて捗ります。
コマンドラインオプション
この改変版のコマンドラインオプションは次のようになります。
Usage:
pasta { <option> } <input>
Options:
--cpm Sets target to CP/M (default)
--zx48 Sets target to ZX Spectrum 48K
--zx128 Sets target to ZX Spectrum 128K
--zxnext Sets target to ZX Spectrum Next
--agon Sets target to Agon Light/Console8
--g850 Sets target to PC-G850
--bin Generates raw binary file (default)
--3dos Generates binary with +3DOS header
--tap Generates .tap file with loader
--sna Generates .sna snapshot file
--run Generates .run runnable directory
--mos Generates Agon MOSlet
--dep enable dependency analysis
--opt enable peephole optimizations
--ovr enable banked-switched overlays
--release disable assertions and breakpoints
--keepint keep intermediate files (like .asm)
--ide starts interactive mode
--config shows (and checks) the configuration
--version shows just the version number
おわりに
動作確認は PC-G850V で行っています。
PC-G850 系以外の PC-G8xx 機については RTL だけ用意すれば (改変すれば) PASTA/80 が使えるようになると思います。
余談:
「PC-G850V で Turbo Pascal を使う」というのは skyriver 氏作の『EborsyEEP』で実現していたのですが、
今回は CP/M-80 を介さずに Turbo Pascal (Pasta/80) でプログラムが書けるようになりました。
CP/M-80 用のバイナリが吐けるので、ターゲットとして MSX-DOS を追加してみるのもいいかもしれませんね。もちろん、素の MSX や他の 8 bit 機でも!!
See also:














