Pic24MC68Kマイコン(その11)CP/M-68KでForth [68K]
「Pic24MC68Kマイコン(その9)ehBASICの移植」の記事で書いたように MC68000 のチップサイズと同程度のサイズのプリント基板でCP/M-68Kが動き、ehBASIC も移植できたので CP/M-68K で動作する BASIC 以外のアプリケーションを探していたところ、Digital Reseaerch Binary Files のサイトに Forth があったことを思い出し、起動しようと試みましたが下記のエラーが発生して動きませんでした。orz
実行ファイルの先頭部分をダンプしてみるとリロケーションビットは立っておらず(=リロケータブルでは無い)テキストセグメントの開始アドレスが 0x0500 になっています(ファイルヘッダの構造は下図を参照してください)
今回開発した自作ボードでは CP/M 本体を 0x0400 からに配置しているので 0x0500 にアプリケーションを置くことはできません。
Forth83 にはソースが同梱されていなかったので他の Forth は無いか探したところ、CP/M-68K 用ではありませんが 68K 用の Fig-FORTH がソース付きで見つかりました。アセンブラソースのコンソール入出力部分を下記のように CP/M-68K 用に書き換え、また CP/M-68K のアセンブラ(as68)が通るように変更しました。
DDT を使って動作確認したところ、下図のように A0 レジスタ設定箇所が ワード指定のため符号拡張されてしまいアクセス違反が発生する状況でした。下記の例では A0 を修正してステップ動作を続行していますが、更にその先で A6 が符号拡張されてしまっています。
Fig-FORTHのソースではデータのポインタを全てワード(2バイト)で扱っているのでデータエリアが 0x8000 以降にあるとアドレス設定時に符号拡張されてしまいうまく動きません(MC68Kはメモリ空間が 16MB あるのに先頭の 32KB の範囲でしか動かせない)。orz
また、コールドスタート時に下記のように $2F00 番地(何故ラベルでは無くイミーディエット値のアドレスなのか?)のメモリに書込みを行っているので CP/M を壊してしまいます。
結果としては Fig-FORTH と最初に書いた Forth83 が動かない原因は、今回開発したボードで CP/M 本体をメモリの先頭に配置したことが大きな原因であるいうことになってしまいました。ネットで入手できる CP/M-68K のアーカイブファイルに CP/M を 0400H に配置したサンプル(CPM400.SR)があり、CP/M の配置場所が実装メモリサイズに依存しない点が気に入り採用したのですが問題が発生する事例に出会ったので、CP/M 本体をメモリの後方に配置することで対応します。
「3チップ構成Pic24CPM68Kマイコン(CP/M-68K起動までの作業まとめ)」の記事で公開している Pic24CPM68K ボード用の CP/M インストール用ファイルも メモリ上位に CP/M を配置するように修正しておきました。
CP/M の Programmer's Guid にもメモリモデルについては下記のように書いてありました。
しかし、CP/M-68K には reloc コマンドが用意されていることや BIOS 内の memory region データで使用可能なメモリの先頭アドレスとサイズを複数設定できる仕様になっていることを考えると TPA が 0x0400 から始まる前提のアプリケーション側にも少し問題があると思います(今回の Fig-FORTH は CP/M 用ではないので当てはまりませんが)
CP/M-68K のメモリ位置の変更は BIOS の memory region 部の値を変えるだけなので直ぐに対応できます(CP/M-68Kはbiosの変更時には A: ドライブのCPM.SYSを差換えるだけなので楽です)
冒頭に書いた Forth83 は1983年に制定された規格で Forth の中では新しいみたいなので気を取り直してこれを動かしてみました。
CP/M-68K 本体をメモリの後方に移動し 0x0400 から TPA として使えるようになったので想定通り Forth83 が動作するようになりました。
ダウンロードしたアーカイブファイルには F83.68K が含まれていたので再構築する必要はないのですが、登録 word をカスタマイズする場合等のために README.TXT に書いてあった再構築手順を書いておきます。
[セルフコンパイルの手順]
日本語のマニュアルがあるといいのですが見つからなかったので F83 TUTOR を参考にしました(まだ読んでいる途中ですが)
動作確認のために hello 表示と階乗ではなく階和?のワードを作成した際のログを貼っておきます。
実は階和の方はWeb上のサービスである Try It Online の gForth 環境で適当に作ったものが動いたので Froth83 でも試して見たものです(まだ慣れていないので冗長な処理になっているかも)。こういう手軽な環境があると今更と言う感じもするのですが、やはり実際のボード上で CP/M-68K を使って動かすと格別な楽しさを感じますね ^^
★追記 2022/02/25 {
Forth のソースファイルである BLK ファイルの作成と編集及び実行ファイルの作成方法について操作方法を確認しましたので操作サンプルを貼っておきます。
}
★追記 2022/02/25 {
Forth でリカーシブコールするためには WORD 定義中に WORD 自身の名前を書くのではなく、RECURSE という WORD を書くことで実現できます。上記で書いた階和をリカーシブコールで再定義したものを以下に示します。少し見易くなりましたね。
同様のロジックで加算を乗算に変えれば階乗の WORD も定義できます。
フィボナッチ数列も再帰呼出しで簡単に書けました。
}
★変更 2022/02/26
再帰呼出しワードの定義はワード名直後に RECURSIVE と書けば定義内で自分自身を名前でコールできるのようなので上記のフィボナッチの定義では RECURSIVE を使うように変更しました。RECURSE という書き方は元々は定義部分のワード名を変更した場合に書き換える箇所を少なくするためのようですが、個人的には自身の名前を使って定義した方がしっくり来て見易いように感じます。
★追記 2022/03/06
Forthでの変数宣言は ”variable 変数名” で可能ですが、配列の使い方について確かめてみました。
create と allot のワードにより配列名に対して領域を確保し、"!" で配列要素に値を設定、"@" で配列要素の値をスタック上に置くことが出来ます。領域確保時のサイズの値はバイト単位で指定し、ディフォルトの整数サイズが2バイトなのでサイズは「要素数x2」の値で設定する必要があります。gForth 等では cells のワードで1ワードのバイト数を掛けることで領域を要素数で指定できるようですが F83 は対応していないようです。
配列宣言、要素への値の設定と読み出しのサンプルを貼っておきます。
[TOP] [ 前へ ] 連載記事 [ 次へ ]
Forth83 起動時のエラー |
---|
P>f83 Insufficient memory or bad file header P>a:dump f83.68k 0000 00 (000000): 601A 0000 70C4 0000 0000 0000 0000 0000 *`...pD..........* 0000 10 (000010): 0000 0000 0000 0000 0500 FFFF 4EF8 32D2 *............Nx2R* 0000 20 (000020): 4EF8 32C6 3E1D 2847 3E1C 2047 4ED0 1003 *Nx2F>.(G>. GNP..* 0000 30 (000030): 0000 8546 4F52 54C8 301E 7044 70BA 701E *...FORTH0.pDp:p.* 0000 40 (000040): 6FCC 0000 3D0D 2A4C 4EF8 0508 1004 0000 *oL..=.*LNx......* 0000 50 (000050): 8445 5849 54A0 053C 3E1E 2A47 4EF8 0508 *.EXIT .<>.*GNx..* 0000 60 (000060): 1004 0532 8655 4E4E 4553 54A0 053C 3D0D *...2.UNNEST .<=.* 0000 70 (000070): 2A5F 3F0C 4EF8 0508 1005 0546 8255 50A0 **_?.Nx.....F.UP * 0001 00 (000080): 0556 32EC 3F14 4EF8 0508 3014 D078 0566 *.V2l?.Nx..0.Px.f* 0001 10 (000090): 3F00 4EF8 0508 1005 0000 8528 4C49 54A9 *?.Nx.......(LIT)* 0001 20 (0000A0): 0586 3F1D 4EF8 0508 1009 0514 8642 5241 *..?.Nx.......BRA* 0001 30 (0000B0): 4E43 48A0 059A 3E15 2A47 4EF8 0508 1009 *NCH ..>.*GNx....* 0001 40 (0000C0): 0000 873F 4252 414E 43C8 05B0 4A5F 67E6 *...?BRA P> |
実行ファイルの先頭部分をダンプしてみるとリロケーションビットは立っておらず(=リロケータブルでは無い)テキストセグメントの開始アドレスが 0x0500 になっています(ファイルヘッダの構造は下図を参照してください)
今回開発した自作ボードでは CP/M 本体を 0x0400 からに配置しているので 0x0500 にアプリケーションを置くことはできません。
CP/M-68K の実行ファイルのヘッダ構造 |
|
Forth83 にはソースが同梱されていなかったので他の Forth は無いか探したところ、CP/M-68K 用ではありませんが 68K 用の Fig-FORTH がソース付きで見つかりました。アセンブラソースのコンソール入出力部分を下記のように CP/M-68K 用に書き換え、また CP/M-68K のアセンブラ(as68)が通るように変更しました。
Fig-FORTH のコンソール入出力部分のCP/M対応へ変更 |
---|
233 BDOS EQU 2 234 235 * putc 236 * D0.B <- data 237 * 238 00000004 48E7C000 XEMIT MOVEM.L D0-D1,-(SP) * save D0,D1 239 00000008 2200 MOVE.L D0,D1 240 0000000A 7002 MOVEQ #2,D0 241 0000000C 4E42 TRAP #BDOS 242 0000000E 4CDF0003 MOVEM.L (SP)+,D0-D1 243 00000012 4E75 RTS 244 * getc 245 * D0.W -> data 246 00000014 2F01 XKEY MOVE.L D1,-(SP) * save D1 247 00000016 303C0006 MOVE.W #6,D0 248 0000001A 323C00FF MOVE.W #$FF,D1 249 0000001E 4E42 TRAP #BDOS 250 00000020 221F MOVE.L (SP)+,D1 251 00000022 4E75 RTS 252 * 253 00000024 700D XCR MOVEQ #ACR,D0 254 00000026 61DC BSR XEMIT 255 00000028 700A MOVEQ #LF,D0 256 0000002A 61D8 BSR XEMIT 257 0000002C 4E75 RTS 258 * 259 * kbhit 260 * D0.W -> 0:none 261 0000002E 2F01 XQTERM MOVE.L D1,-(SP) * save D1 262 00000030 303C0006 MOVE.W #6,D0 263 00000034 323C00FE MOVE.W #$FE,D1 264 00000038 4E42 TRAP #BDOS 265 0000003A 221F MOVE.L (SP)+,D1 266 0000003C 4E75 RTS 267 * 268 0000003E 4E75 XRSLW RTS 269 * |
DDT を使って動作確認したところ、下図のように A0 レジスタ設定箇所が ワード指定のため符号拡張されてしまいアクセス違反が発生する状況でした。下記の例では A0 を修正してステップ動作を続行していますが、更にその先で A6 が符号拡張されてしまっています。
DDT で Fig-FORTH の動作確認 |
---|
P>a:ddt f68k.rel ******************************************************** * DDT-68K 9/20/84 Version 1.3 * * Serial #XXXX-0000 All Rights Reserved * * Copyright 1982,1983,1984,1985 Digital Research Inc. * ******************************************************** File is relocatable text base = 00007000 data base = 00008758 bss base = 00008758 text length = 00001758 data length = 00000000 bss length = 00000002 base page address = 00006F00 initial stack pointer = 000F55DE -t PC=00008696 USP=000F55D6 SSP=0000626C ST=0000=>IM=0 D 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 A 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000F55D6 movea.w #$86E2,A0 .COLDUS -l 00008696 movea.w #$86E2,A0 .COLDUS 0000869A move.w (A0),$2F00 000086A0 movea.w $4(A0),A6 000086A4 movea.w A6,A1 000086A6 move.w #$A,D0 COLD1: 000086AA move.w (A0)+,(A1)+ 000086AC dbf D0,86AA .COLD1 000086B0 adda.w #$1E,A1 000086B4 move.w #$D,D0 COLD2: 000086B8 move.w (A0)+,(A1)+ -t PC=0000869A USP=000F55D6 SSP=0000626C ST=0000=>IM=0 D 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 A FFFF86E2 00000000 00000000 00000000 00000000 00000000 00000000 000F55D6 move.w (A0),$2F00 -xa0 A0=FFFF86E2 86e2 -t PC=000086A0 USP=000F55D6 SSP=0000626C ST=0008=>IM=0 NEG D 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 A 000086E2 00000000 00000000 00000000 00000000 00000000 00000000 000F55D6 movea.w $4(A0),A6 -t PC=000086A4 USP=000F55D6 SSP=0000626C ST=0008=>IM=0 NEG D 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 A 000086E2 00000000 00000000 00000000 00000000 00000000 FFFF9158 000F55D6 movea.w A6,A1 - |
Fig-FORTHのソースではデータのポインタを全てワード(2バイト)で扱っているのでデータエリアが 0x8000 以降にあるとアドレス設定時に符号拡張されてしまいうまく動きません(MC68Kはメモリ空間が 16MB あるのに先頭の 32KB の範囲でしか動かせない)。orz
また、コールドスタート時に下記のように $2F00 番地(何故ラベルでは無くイミーディエット値のアドレスなのか?)のメモリに書込みを行っているので CP/M を壊してしまいます。
Fig-FORTH のコールドスタート処理部分 |
---|
3292 00001694 1696 COLD DC.W 2+* 3293 00001696 307C16E2 MOVE.W #COLDUS,A0 * BOOTLIST 3294 0000169A 33D000002F00 MOVE.W (A0),$2F00 * TOP NFA 3295 000016A0 3C680004 MOVE.W 4(A0),A6 * SET USER POINTE 3296 000016A4 324E MOVE.W A6,A1 * WORK COPY OF US 3297 000016A6 303C000A MOVE.W #$0A,D0 * MOVE ELEVEN BOO 3298 000016AA 32D8 COLD1 MOVE.W (A0)+,(A1)+ * ONE AT A TIME 3299 000016AC 51C8FFFC DBF D0,COLD1 * UNTIL DONE, 3300 000016B0 D2FC001E ADD.W #$1E,A1 * POINT TO BYTE U 3301 000016B4 303C000D MOVE.W #$0D,D0 * MOVE 14 MORE BO |
結果としては Fig-FORTH と最初に書いた Forth83 が動かない原因は、今回開発したボードで CP/M 本体をメモリの先頭に配置したことが大きな原因であるいうことになってしまいました。ネットで入手できる CP/M-68K のアーカイブファイルに CP/M を 0400H に配置したサンプル(CPM400.SR)があり、CP/M の配置場所が実装メモリサイズに依存しない点が気に入り採用したのですが問題が発生する事例に出会ったので、CP/M 本体をメモリの後方に配置することで対応します。
「3チップ構成Pic24CPM68Kマイコン(CP/M-68K起動までの作業まとめ)」の記事で公開している Pic24CPM68K ボード用の CP/M インストール用ファイルも メモリ上位に CP/M を配置するように修正しておきました。
CP/M の Programmer's Guid にもメモリモデルについては下記のように書いてありました。
CP/M-69K Default Memory Model |
|
しかし、CP/M-68K には reloc コマンドが用意されていることや BIOS 内の memory region データで使用可能なメモリの先頭アドレスとサイズを複数設定できる仕様になっていることを考えると TPA が 0x0400 から始まる前提のアプリケーション側にも少し問題があると思います(今回の Fig-FORTH は CP/M 用ではないので当てはまりませんが)
CP/M-68K のメモリ位置の変更は BIOS の memory region 部の値を変えるだけなので直ぐに対応できます(CP/M-68Kはbiosの変更時には A: ドライブのCPM.SYSを差換えるだけなので楽です)
冒頭に書いた Forth83 は1983年に制定された規格で Forth の中では新しいみたいなので気を取り直してこれを動かしてみました。
CP/M-68K 本体をメモリの後方に移動し 0x0400 から TPA として使えるようになったので想定通り Forth83 が動作するようになりました。
ダウンロードしたアーカイブファイルには F83.68K が含まれていたので再構築する必要はないのですが、登録 word をカスタマイズする場合等のために README.TXT に書いてあった再構築手順を書いておきます。
[セルフコンパイルの手順]
- KERNEL.68K の作成
A: ドライブに F83.38K、B: ドライブに META68.BLK と KERNEL68.BLK を配置し、B:ドライブ上で次のコマンドを実行する。B>a:f83 meta68.blk 1 load bye B>
すると A: ドライブに KERNEL.68K が作成されます。
- F83.68K の作成
A: ドライブに EXTEND68.BLK と CPU68000BLK を配置し次のコマンドを実行する。A>kernel extend68 1 load bye A>
すると F83.68K が作成されます。
日本語のマニュアルがあるといいのですが見つからなかったので F83 TUTOR を参考にしました(まだ読んでいる途中ですが)
動作確認のために hello 表示と階乗ではなく階和?のワードを作成した際のログを貼っておきます。
Forth83 での Hello表示例 |
---|
P>f83 68000 Forth 83 Model Version 2.1.0 Modified 03Jun84 : hel begin crlf ." Hello,world" 1- dup 0= until drop ; ok 3 hel Hello,world Hello,world Hello,world ok .s Empty ok : sum dup begin 1- dup rot + swap dup 1 = until drop . ; ok 10 sum 55 ok bye 114 Pages P> |
実は階和の方はWeb上のサービスである Try It Online の gForth 環境で適当に作ったものが動いたので Froth83 でも試して見たものです(まだ慣れていないので冗長な処理になっているかも)。こういう手軽な環境があると今更と言う感じもするのですが、やはり実際のボード上で CP/M-68K を使って動かすと格別な楽しさを感じますね ^^
★追記 2022/02/25 {
Forth のソースファイルである BLK ファイルの作成と編集及び実行ファイルの作成方法について操作方法を確認しましたので操作サンプルを貼っておきます。
blkファイルと実行ファイル作成の操作例 |
---|
P>f83 68000 Forth 83 Model Version 2.1.0 Modified 03Jun84 10 create-file my.blk ok 0 edit Enter your ID: test blk .. Scr # 0 MY.BLK 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 0 ^ ok 0 ^ p \ test how to create block and execute file ok 0 ^\ test how to create block and execute file done 0 modified ok 1 edit Scr # 1 MY.BLK 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 0 ^ ok 0 ^ p \ test block ok 0 ^\ test block u : hel cr ." Hello, world" 0 0 bdos ; ok 1 ^: hel cr ." Hello, world" 0 0 bdos ; done 1 modified ok update ok see hel HEL ? 1 load ok see hel : HEL CR (.") Hello, world 0 0 BDOS ; ok ' hel is boot ok save-system hello.68k ok hel Hello, world P>a:stat hello.68k DRIVE P: USER : 0 RECS BYTES FCBS ATTRIBUTES NAME 227 30K 2 DIR RW P:HELLO .68K ---------------------------------------------- TOTAL: 30K 2 P: RW, FREE SPACE: 618K P>hello Hello, world P> |
}
★追記 2022/02/25 {
Forth でリカーシブコールするためには WORD 定義中に WORD 自身の名前を書くのではなく、RECURSE という WORD を書くことで実現できます。上記で書いた階和をリカーシブコールで再定義したものを以下に示します。少し見易くなりましたね。
同様のロジックで加算を乗算に変えれば階乗の WORD も定義できます。
フィボナッチ数列も再帰呼出しで簡単に書けました。
Forth でのリカーシブコール例 |
---|
P>f83 68000 Forth 83 Model Version 2.1.0 Modified 03Jun84 : sum ( n -- sum ) dup 1 > if dup 1- RECURSE + then ; ok 10 sum . 55 ok ok : fact ( n -- fact ) dup 1 > if dup 1- RECURSE * then ; ok 10 fact . 24320 ok : fibo ( n -- fibo ) RECURSIVE dup 1 > if 1- dup fibo swap 1- fibo + then ; ok : fibo10 11 0 do cr i dup ." fibo( " . ." ) = " fibo . loop ; ok fibo10 fibo( 0 ) = 0 fibo( 1 ) = 1 fibo( 2 ) = 1 fibo( 3 ) = 2 fibo( 4 ) = 3 fibo( 5 ) = 5 fibo( 6 ) = 8 fibo( 7 ) = 13 fibo( 8 ) = 21 fibo( 9 ) = 34 fibo( 10 ) = 55 ok |
}
★変更 2022/02/26
再帰呼出しワードの定義はワード名直後に RECURSIVE と書けば定義内で自分自身を名前でコールできるのようなので上記のフィボナッチの定義では RECURSIVE を使うように変更しました。RECURSE という書き方は元々は定義部分のワード名を変更した場合に書き換える箇所を少なくするためのようですが、個人的には自身の名前を使って定義した方がしっくり来て見易いように感じます。
★追記 2022/03/06
Forthでの変数宣言は ”variable 変数名” で可能ですが、配列の使い方について確かめてみました。
create と allot のワードにより配列名に対して領域を確保し、"!" で配列要素に値を設定、"@" で配列要素の値をスタック上に置くことが出来ます。領域確保時のサイズの値はバイト単位で指定し、ディフォルトの整数サイズが2バイトなのでサイズは「要素数x2」の値で設定する必要があります。gForth 等では cells のワードで1ワードのバイト数を掛けることで領域を要素数で指定できるようですが F83 は対応していないようです。
配列宣言、要素への値の設定と読み出しのサンプルを貼っておきます。
Forth83 での配列宣言と値の設定及び読み出し操作のサンプル |
---|
P>f83 68000 Forth 83 Model Version 2.1.0 Modified 03Jun84 hex ok create abc 8 allot ok : setar ( -- ) 4 0 do i dup 2 * abc + ! loop ; ok : dspar ( -- ) 4 0 do i 2 * abc + @ . loop ; ok setar ok dspar 0 1 2 3 ok abc 10 dump 0 1 2 3 4 5 6 7 8 9 A B C D \/ F 0123456789ABCDVF 70C0 54 D9 70 AC 00 00 70 BA 83 41 42 C3 05 56 00 00 TYp,..p:.ABC.V.. 70D0 00 01 00 02 00 03 00 00 6F CC 85 53 45 54 41 D2 ........oL.SETAR ok abc . 70CE ok |
[TOP] [ 前へ ] 連載記事 [ 次へ ]
コメント 0