電子ブロック工房 / PIC https://www.rad51.net/blog/mycom/ IC・トランジスタで出来たコンピューターを設計・製作するためのブログ / Microchip PIC ja Jeans CMS © Weblog http://backend.userland.com/rss https://www.rad51.net/jeans/skins/jeans/images/jeans2.gif 電子ブロック工房 https://www.rad51.net/blog/mycom/ MM Systemの紹介 https://www.rad51.net/blog/mycom/?itemid=966 Microchip社のPIC32MMファミリーのマイクロコントローラーは、低価格で32ビットのCPUが使え、DIPパッケージもあるので、趣味の電子工作にはもってこいの石です。私自身も最近はこれを多用しています。そこで、色々な用途に簡単に使えるよう、最小限のインターフェースを簡単に実装するためのプラットフォームを作成しました。MM Systemと名付けています。

特徴:
 ・PIC32MM0064GPL028を使用
 ・出力用に、4桁の7セグメントLEDを標準装備
 ・入力用に、最大32個までのスイッチを接続化
 ・最大9個までのA/Dコンバーターの読み込みが容易
 ・PIC32MMに標準で組み込まれているSPIやUARTだけでなく、I2C通信も使用可

お気づきの方もいらっしゃると思いますが、これはZK-80 miniを改変したものです。ほとんどの用途では7セグメントLEDは4桁で良いでしょうし、8桁表示は暗くなりますので、MM Systemではとりあえず4桁を標準としました。高輝度の小型7セグLEDを使えば、ダイナミック点灯にトランジスターを使わなくても、実用に十分な明るさが出ます。

下の回路は、もっとも標準だと思われる、7セグメントLEDと8個までのタクトスイッチを接続する例です。
2020-11-29-schematic.png

この回路に、次のMM Systemのレポジトリーから得られるコードをコンパイルして得たファームウェアをダウンロードして実行すると、7セグメントLEDに「1234」と表示されます。
https://github.com/kmorimatsu/mmsystem

2020-11-29-123.4.png

あとは、main.cを目的のためにコード修正するだけです。例えば、上記回路図の8個のキー入力を使いたければ、「led7seg_init(0x08);」のコメントアウトを外して有効化し、「KEY24ON」「KEY25ON」などの値を参照して、キー入力があった際の反応部分のコードを書いたりすればよいです。主に、wile(1){ }の、永久ループ部分にコードを追加することになります。

MM Systemの利用例1:温度計

MM Systemの使い方に入る前に、まず使用例について説明します。どれぐらい簡単に実装できるかの参考になるはずです。

MM Systemと、Microchip社の温度センサー(熱電対)用のチップであるMCP9600を用いて、温度計を作成しました。MCP9600とMM Systemは、I2Cで通信を行います。回路図は以下の通りです。
2020-11-29-mcp9600.png

見て分かる通り、RA0とRA1に、MCP9600 (Adafruit社のボード)を接続しているだけです。ボタン入力は必要ないので省略しています。完成写真は、以下の通りです。
2020-11-29-thermo1.jpg
2020-11-29-thermo2.jpg
2020-11-29-thermo3.jpg

ソースコードは、main.cのmain()関数を以下の様に修正するだけです。
void main(){
    int tempX10;
    unsigned char data[2];
    // Enable interrupt
    __builtin_enable_interrupts();

    // Initializing 7 seg LED system. 
    led7seg_init(0x00); // No key input
    //led7seg_init(0x01); // Use only RB0 for key input
    //led7seg_init(0x08); // Use only RB3 for key input
    //led7seg_init(0x0f); // Use all RB0-3 for key input

    // Example of initializing ADC. RB0 (AN2) and RB1 (AN3) are used.
    //adc_init((1<<2)|(1<<3));
    
    // Initialize I2C if required
    i2c_init();

    // Infinite loop until reset switch will be pressed
    while(1){
    	// Read temperature (doesn't support below 0 degree)
        // Temperature = (UpperByte x 16 + LowerByte / 16)
    	i2c_write8(0x67,0x00);
    	i2c_read(0x67,(char*)&data[0],2);
    	tempX10=(((int)data[1])*10+8)/16;
    	tempX10+=((int)data[0])*160;
    	// Show temperature
        led7seg_dec4(tempX10);
        led7seg_point(2);
        asm volatile("wait");
    }
}

ほとんどの部分はMM Systemそのまま、あるいはコメントアウト部分を若干修正するだけでした。一から書いたコードは、以下の部分だけです。
    int tempX10;
    unsigned char data[2];

    	// Read temperature (doesn't support below 0 degree)
        // Temperature = (UpperByte x 16 + LowerByte / 16)
    	i2c_write8(0x67,0x00);
    	i2c_read(0x67,(char*)&data[0],2);
    	tempX10=(((int)data[1])*10+8)/16;
    	tempX10+=((int)data[0])*160;
    	// Show temperature
        led7seg_dec4(tempX10);
        led7seg_point(2);
やっているのは、I2C通信でMCP9600から2バイトのデーターを取ってきて、「摂氏x10」の値を計算してLEDに表示することです。このように、I2C通信を行ってダイナミック点灯で7セグメントLEDに表示するという、一から書けば結構複雑な処理が、このような数行のコードで簡単に実装できることが、分かると思います。

MM Systemの利用例2:LED 照射器
4種類の異なる色の高輝度LEDを、ミリカンデラで表わされる光度で光らせるためのものです。インターフェースとしては、出力に7セグLEDを、入力に4つのタクトスイッチと、2つの可変抵抗器をA/Dコンバーターに繋げたものを使っています。

2020-12-01-led1.jpg

2020-12-01-led2.jpg

MM Systemのごく初期の作品で、回路がほんの少し違っていますので、回路図の掲載は割愛します。下に、スイッチ入力とA/Dコンバーター部分のコードを紹介します。

// ADC settings
#define VOLUME_COARSE 2
#define VOLUME_FINE 3


void main(){
    int power,select,max;
	// Enable interrupt
	__builtin_enable_interrupts();
    // Initialize LEDs outputs, connected to RA0-RA3. First, all off (output L)
    TRISACLR=LATACLR=0x000F;

    // Initialize 7 seg LED system. Use only RB3 for key input
    led7seg_init(0x08);

    // Initialize ADC. RB0 (AN2; coarse) and RB1 (AN3; fine) are used.
    adc_init((1<<VOLUME_COARSE)|(1<<VOLUME_FINE));

    // Detect key
    while(1){
        // Power reading.
        max=5000;
        power=5*adc_read(VOLUME_COARSE)+(adc_read(VOLUME_FINE)>>5);
        if (max<power) power=max;
        // Display power
        led7seg_dec4(power);
        // Key scan
        select=0; // 468 nm
        max=5000;
        if (KEY24ON) break;
        select=1; // 522 nm
        max=3925;
        if (KEY25ON) break; 
        select=2; // 574 nm
        max=3500;
        if (KEY26ON) break; 
        select=3; // 639 nm
        max=4200;
        if (KEY27ON) break;
        wait_display();
    }
    
    // Output selection
    select_led(select,max);

    // Infinite loop until reset switch will be pressed
    while(1){
        // Power reading.
        power=5*adc_read(VOLUME_COARSE)+(adc_read(VOLUME_FINE)>>5);
        if (max<power) power=max;
        // Display power
        led7seg_dec4(power);
        // PWM setting
        set_power(power);
        // Wait for a display round
        wait_display();
    }
}

タクトスイッチからの入力は、「led7seg_init(0x08);」でRB3からの入力を有効にしておいて、「KEY24ON」などの表記でスイッチが押されているかどうか判定するだけです。A/Dコンバーターについては、「adc_init((1<<VOLUME_COARSE)|(1<<VOLUME_FINE));」で、RB0/RB1からの入力を有効にしておいて、「adc_read(VOLUME_COARSE)」と「adc_read(VOLUME_FINE)」で読んでいます。小難しい設定は、行う必要はありません。

MM Systemの使い方

まずは、GitHubからソースコードを取得してください。最新のコードは、リンク先の「Code」ボタンをクリックしたときに現れる「Download ZIP」をクリックすれば、Zipアーカイブとして得られます(バージョンごとのアーカイブは現在整備中)。

MPLAB X IDEで、「File」→「New Project」を選択し、「Standard Project」が選択されていることを確認して、「Next」を押します。「Device」に「PIC32MM0064GPL028」を選択し、「Next」を押します。「Compiler」にXC32の適当なもの(多くの場合、最新版が良い)選択し、「Next」を押します。「Project Name」にプロジェクト名を入力し、「Project Location」にプロジェクトを保存するフォルダーを指定します。「Encoding」は、好みに応じて「Shift_JIS」か「UTF-8」を選べばよいでしょう。

「プロジェクト名.X」というフォルダーが出来ますので、そこに、ダウンロードしたZipアーカイブの中から、拡張子が「c」の物と「h」の物をコピーします。2020年12月段階のバージョンでは、「adc.c」「adc.h」「i2c.c」「i2c.h」「led7seg.c」「led7seg.h」「main.c」の7つがあります。「Projects」ウィンドウの「Header Files」を右クリックし、「Add Existing Items」を選択します。「ctrl」キーを押しながらすべての「*.h」を選択し「Select」を押してください。同様に、「Source Files」から、すべての「*.c」ファイルを「Select」してください。最後に、「File」メニューから「Project Property」を選択します。「xc32-gcc」という項目がありますのでクリックし、「Option categories」に「optimization」を選んでください。「optimization-level」に「1」を選択して、「OK」ボタンを押してください。これで、ビルドする準備が出来ました。

「Production」メニューの「Build Main Project」を選択、もしくは金づちアイコンをクリックすれば、「BUILD SUCCESSFUL」の表示が出て、ビルドが無事成功したことが分かると思います。回路を組んであれば、PicKitにつないでPCに接続し、「Program Device for Production Main Project」を選択すれば、ファームウェアがダウンロードされて、「1234」がLEDディスプレイに表示されるはずです。

main.c の変更の仕方

基本的には「main.c」を修正するだけで、一部の例外を除いて他のファイルは変更する必要は無いはずです。ただし、MM Systemは現行 CPU のクロック速度が 8 MHz の固定ですから、これを変更したい場合は、他のCファイルも修正が必要です。簡単なプロジェクトなら大概、8 MHz の速度でよいのではないかと思います。

MM Systemは現行、「led7seg」「adc」「i2c」の3つの機能からなります。それぞれにCファイルとHファイルが割り当てられています。順に説明します。

led7seg

MM Systemのコアとなる機能で、4桁7セグメントLEDの表示と、最大32個のスイッチ入力を制御します。必須の機能で、これがないと他の機能が動作しないケース(A/Dコンバーターなど)があります。この機能は、Timer1 を利用していることに注意してください。

main() 関数の冒頭で、led7seg_init() 関数を呼び出してください。引数は 0x00 から 0x0f までの値で、RB0 から RB3 をボタン入力に割り当てるかどうかを指定します。RB3 だけなら 0x08 を、RB0-RB3 を4つとも割り当てる場合は 0x0f を指定します。8個以下のスイッチ数の場合は RB3 を割り当てるのが良いと思われます(RB0-RB2は多機能だが、RB3は純粋なデジタル入出力のみの為)。

led7seg.h を見ていただければ分かりますが、以下の4つの関数が定義されています。
 ・void led7seg_init(int useRB03);
 ・void led7seg_hex(int pos, char num);
 ・void led7seg_point(int pos);
 ・void led7seg_dec4(int num);

led7seg_init() は、先に説明しました。led7seg_hex() は、pos で示される場所(一番左が0,一番右が3)に、num で示される16進数の数字を表示します(16以上の値を指定すると、小数点ドットが点灯します)。led7seg_point() は、指定場所に小数点ドットを表示します。led7seg_dec4() は、4桁10進数の値を表示します。

KEY0ON から KEY31ON までの、32個のマクロが定義されています。これらは、特定のスイッチが ON になっているかどうかを示します。 ON の場合は0以外の値、OFF の場合は0です。KEY0ON - KEY7ON は RB0 に接続されたスイッチ、KEY8ON - KEY15ON は RB1 に接続されたスイッチ、KEY16ON - KEY23ON は RB2 に接続されたスイッチ、KEY24ON - KEY31ON は RB3 に接続されたスイッチです。先に書いた通り、led7seg_init() 関数の引数を正しく設定しないとスイッチ入力が正しく行われない事に注意してください。

adc

A/Dコンバーターを簡単に使用するための物です。adc.h には、次の2つの関数が定義されています。
 ・void adc_init(int used_adc);
 ・unsigned int adc_read(int adc_num);

まず、main() 関数の冒頭で adc_init() を呼び出してください。引数には、どのA/Dコンバーターを利用するかを指定します。値は、PIC32MMのデーターシートを参考にして(あるいは、adc.c を参照して)、AN0 から AN 11 までの数字を参考にして決めます。例えば、RA0 は AN0 、RB0 は AN2 です。AN0 を有効にする場合は引数の最下位ビットを1に、AN2 を有効にする場合は第2ビットを1にします。AN0 と AN2 を使う例では、0x05 ( 0b0101 )を指定してください。

adc_read() を呼び出せば、A/Dコンバーターの値を12ビットの整数値として得ることが出来ます。引数は、上のパラグラフで説明した ANx の x の値を指定してください。

i2c

マスターモードで i2c 機能を使うための機能です。クロックは 100 kHz 固定で、それ以外の速度では使えません。この機能を利用する場合、「i2c.h」ファイルを編集して、どのポートに I2C のラインを接続しているか指定してください。デフォルトでは「#define I2C_SCL_RA0」と「#define I2C_SDA_RA1」が有効で、RA0 に SCL を、RA1 に SDA を接続する設定です。他のポートを使用する場合は、それに応じて、必要な行のみを有効にし、不必要な行はコメントアウトします。

「i2c.h」では、次の関数が定義されています。
 ・void i2c_init();
 ・int i2c_write(char addr, char* data, int len);
 ・char i2c_read(char addr, char* data, int len);
 ・int i2c_write8(char addr, char data);
 ・char i2c_read8(char addr);
 ・int i2c_error();
 ・void _i2c_start();
 ・void _i2c_stop();
 ・char _i2c_write(char data);
 ・char _i2c_read(char ack);

これらのうち、関数名がアンダースコア( _ )で始まるものは、ローレベルの制御用で、特殊な用途の場合にしか必要ないと思いますので、説明は割愛します。その他の関数について、説明します。

i2c_init() を、main() 関数の冒頭で呼んでください。これを行わないと、I2C 機能が使えません。

i2c_write() は、データーを送信する場合に使います。引数は、7ビットの I2C アドレス、送信するデーターを格納した配列へのポインター、送信するデーターのバイト数です。戻り値が0の場合は送信の際にスレーブから ACK が返されており、エラーなく送信したことを示します。0以外の値の場合 NAK が返されており、エラーが起きたことを示します。

i2c_read() は、データーを受信するときに使います。。引数は、7ビットの I2C アドレス、受信するデーターを格納する配列へのポインター、受信するデーターのバイト数です。戻り値が0の場合は I2C アドレスの送信の際にスレーブから ACK が返されており、スレーブ側がデーターを送信を認識したことを示します。

i2c_write8() は、1バイトのデーターを送信する場合に使います。2番目の引数は、送信するデーターです。

i2c_read8() は、1バイトのデーターを受信する場合に使います。戻り値は、受信データーです。エラーが起きたかどうか(ACKがちゃんと返されているかどうか)は、次に述べる i2c_error() で調べてください。

i2c_error() は、上で解説した4つの関数の呼び出しの際に、エラーがあったかどうかを調べるための物です。エラーが無ければ0を、エラーがあれば0以外の値を返します。

MM Systemの今後の発展

今の所、私が使うマイクロコントローラーのメインが PIC32MM0064GPL028 ですので、これを使い続ける限り MM System は私自身が使い続けるだろうし、バージョンアップがされていくと思います。MachiKania type M では、I/O 機器の制御を簡単な BASIC のコードで実現できるように書いてありますが、MM System でもそれと同じ感覚で I/O 機器が使えるようになるのを目標として開発しています。私、個人的には、「MachiKania で接続と簡単なテスト」→「MM System に実装」が定番になりつつあります。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=966 Thu, 03 Dec 2020 13:50:39 PST ITEM966_20201203
KM-Z80 midi 進捗:CP/M https://www.rad51.net/blog/mycom/?itemid=954 KM-Z80 midi 用のアプリケーション第二弾として、CP/KM midi (CP/M 2.2 実行環境)を製作している。特徴は、次の通り。

1)CP/M ver 2.2 (54k system) が走る
2)KM-Z80 midi 上で走る、スタンドアローン設計
3)ディスプレイは液晶表示(NTSCビデオは非対応)
4)RAM領域に56K bytesを割り当て
5)USBメモリー上のディスクイメージファイルを仮想ドライブとして使用
6)仮想ドライブは、1954K bytesの物を4つ割り当て

実行中の様子。MBASICから、STARTREKを起動した。
2019-09-24-start.jpg

CP/M ver 2.2 のアセンブル

PIC32MX170F256B上でCP/Mが走るようにする場合、エミュレーターで使う作業領域を確保しないといけないので、64K bytesのRAMの全てをCP/Mに割り当てる事はできない。56K bytesをCP/Mに、残りの 8K bytes をエミュレーターに割り当てる事とした。CP/Mのメモリーマップは次のようになる。

2019-09-25-mmap.png

これは、54K systemと呼ばれるCP/Mである。CP/Mの本家のバイナリーダウンロードのページから取得される物は、62K systemなので、そのままでは使えない。「PC-6001mkII/6601 CP/M導入メモ・その1」というwebページでソースコードからのアセンブルの方法を解説していたので、それに従い54K systemのCPM.SYSを作製した。

62K systemでアセンブルした結果と54K systemでアセンブルした結果を比較すると、変更点は純粋に幾つかのバイトデーターが同じ値だけずれている。変更箇所をピックアップして、様々なアドレス指定のCP/Mシステムが作製できるwebページを構築した。以下のアドレスでアクセスできる。

https://www.rad51.net/projects/cpm/convert.php
2019-09-25-cpmsys.png

これを用いて作製した54K systemを使う事にした。使用するディスクイメージの取得の仕方は、後述する。

LCDを用いて、横80文字のディスプレイを実現

CP/Mのアプリケーションのほとんどは、横80文字のディスプレイを使う事を前提に作られているようで、「CP/Mが動いた!」という満足を得るだけであれば横40文字でも良いのであろうが、多少なりとも実用性を確保しようとすると、どうしても横80文字が必要なようである。始めNTSCのコンポジットビデオ出力で横80文字を表示させようとしたのであるが、次のような様々な問題に直面した。

1)細かな表示に不向き
2)文字数が多い分、スキャンデーターの構築に時間が掛かる
3)SPIでビデオ信号を作製するにしても、ビデオ信号にCPUリソースの多くを取られる
4)結果、Z80のエミュレーションが2 MHzの速度では行えない

そこで、NTSCでの表示は諦め、ILI9341液晶一本で行く事にした。この液晶ディスプレイは、横320ドット縦240ドットの表示が行える。横80文字表示するには、4x8ドットのフォントを使うしかない。ILI9486を用いた480x320ドットの液晶の利用(6x8ドットや6x12ドットのフォントが使える)も勧められたが、とりあえずCP/Mとして動作させる事を第一目標と決め、4x8ドットのフォントを使って、80x30文字のディスプレイを構築する事とした。

CP/Mにおいては、画面出力は単一のBIOS APIである「CONOUT」でのみ行なわれる。MZ-80Kと違い、仕様としては「CONOUT」の実行に一定以上の時間を費やして、その間Z80 CPUを待たせるというのは、全く問題ないと思われる。そこで、MZ-80Kのシミュレーションとは異なり、定期的に画面描画を行なうのではなく、文字表示をする時のみ(CONOUTの実行時のみ)ILI9341に指令を行なう形式とした。ただし、スクロールアップの際は一画面分描画するので、実行を完了するのに少し時間が掛かる。これは、40年前のCP/Mでも同様であったと思われる。

PIC32で、Z80を積んだPCをエミュレート

KM-Z80 midiと同様、PIC32MX170F256Bを使って、Z80 CPUを積んだPCをエミュレートする。概念図は、以下の通り。

2019-09-25-schematic.png

MREQにはROMとRAMが接続されている。IORQには8255が接続されていて、Port C1の出力で、0000hからのメモリー領域にROMかRAMが選択される方式である。画面表示とキーボード入力も8255に行なわせる事になっているが、エミュレーターでは8255はつかわず、直接値をやりとりする事になっている。ひとまず、HTML5でエミュレーションし、実行を確認してから、KM-Z80 midiに移植した。ソースコードは、ここで管理している

使い方

記事最後のリンク先からHEXファイルを取得し、KM-Z80 midiのアプリケーション変更機能を利用するか、もしくはPicKit等を用いてダウンロードして頂きたい。とりあえず実行すると、以下のようになる。これは、私の CP/KM の仕様である。
2019-09-24-start.jpg

字が小さくて読みづらいが、次のように表示される。
*** CP/KM BIOS KM-2010  1952K bytes x 4 Drives ***


To start CP/M 2.2, a valid disk image containing CPM.SYS is required.
To bobain the disk image, go to:
http://www.rad51.net/projects/cpm/

指示に従ってリンク先に行くと、次のような画面になる。
2019-09-25-diskimages.png

使用するのは 54K system なので、「54K system is here」のリンクをクリックする。次のような画面になる。
2019-09-25-diskimages2.png

さらに指示に従って、本家のバイナリーダウンロードのページに行く。半ば当りに「CP/M 2.2 BINARY」とあるのがそれなのでダウンロードし、先のページにこのファイル(cpm-22-b.zip)をアップロードする。「cpmdisks.zip」がダウンロードされるので、zipアーカイブを解凍し、「cpmdisks」というファイルをUSBメモリーのルートにコピーする。少し煩雑であるが、CP/Mの所有権・著作権の関連でどうしてもこうなってしまうので、ご了承頂きたい。

このUSBメモリーを挿入した状態でCP/KM midiを起動すれば、CP/M 2.2 (54K system)を走らせる事ができる。

なお、ここで使うディスクイメージファイルは、 CpmtoolsGUI というツールで編集することが出来る。CpmtoolsGUIをダウンロードした時に添付されているdiskdefsファイルが、そのまま使える。

20190929 追記

GitHubでver 0.2βをリリースしました。

CP-KM midi ver 0.1α は、GitHubでダウンロードできます。
CP-KM midi ver 0.2β は、GitHubでダウンロードできます。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=954 Wed, 25 Sep 2019 13:20:13 PDT ITEM954_20190925
KM-Z80 midi 進捗:液晶表示 https://www.rad51.net/blog/mycom/?itemid=953 KM-Z80 midi (MZ-80K 互換機)をバージョンアップし、2019年9月現在、ver 0.4としている。ver 0.1からの変更は、以下の通り。

 ・アプリケーションのロード機能を追加。
 ・緑LEDをRB0に繋がず、トランジスターなどを用いてRB1から制御するようにした。
 ・液晶(ILI9341・SPI接続)表示にも対応。
 ・RB5とRB15の機能を入れ替えた。
 ・CMT機能の呼び出しの際、002xhでなく04xxhを呼び出しているプログラムに対応。
 ・FORMなど、分割ファイルの読み込みを行なっているプログラムに対応。
 ・プログラムセーブ時の不具合を解消。
 ・ソースコードのコンパイルにおいて、XC32 ver 1.32だけでなく上位のバージョンの XC32 コンパイラーにも対応。
 ・回路図微修正(LCDのバックライト電圧を3.3vに)。

写真は、SPI接続のILI9341液晶(3.2 インチ)を繋いで実行している所。
2019-09-23-bugfire.jpg

現在の回路図は以下の通り(クリックで拡大表示)。
2019-09-23-schematics.png
2019-09-23-schematics2.png

液晶対応

ver 0.1の回路図を見て頂いて分かるように、当初から液晶表示に対応する予定でいた。使用するのは、ILI9341を用いたSPI接続のインターフェースで、240x320ドットの表示ができるタイプ。これを横置き(320x240)とすれば、8x8ドットの文字が40x30文字表示可能なので、40x25文字が必要なMZ-80Kに使うのに、ちょうど良いサイズ。このタイプの液晶ディスプレイとしては、ILI9341を用いて、4-wire SPIのインターフェースのものが最も一般的で需要が高いようなので、それを選択した。

この液晶の使い方について色々調べてみたところ、次の様な理解になった。

 ・インターフェースとしては、最低限 CS, MOSI, CLK, DC の4本が必要で、液晶からのデーター読み込みもするなら、さらに MISO が1本、合計5本が必要。
 ・キャラクターディスプレイのように使う事はできず、グラフィックディスプレイとして機能する。
 ・1ドットの表示には、16 bitsでカラー指定する。
 ・データーシートにおける SPI の最高速度は、10 MHz。

5本の連絡線は当初から確保してあったが、当初LEDとDCと共通で使うつもりであったRB0を、DC専用として、安定動作を計った。最初の問題が、PICからの信号連絡の方式をどうするかであった。1ドット2バイトなので、8x8ドットの文字表示には 128 バイトのデーター送出が必要である。表示位置もデーター送出しないといけない事を考えると、40x25文字の全てのデーターを送出すると、合計で134125バイト必要である事が分かった。10 MHzのSPIでこれを行なうと、107ミリ秒以上が必要である。実際には連続して送出する事はできないだろうから、200ミリ秒ほど(5 frames per second; 5 fps)になると思われる。この速度では、スクロールゲームなどには対応できないと思われる。

NTSC等と違い、液晶表示の場合は画面を常にリフレッシュし続ける必要はない。MZ-80Kの中でVRAM領域に書き込みがあった時のみ、液晶側にもデーターを送出するだけで、事足りる。この方式をとった場合、ひと文字当り139バイト、1112ビットの送出になる。10 MHzのSPIを使った場合、2 MHzで動くZ80 CPUのクロック数としては、222クロック分になり、この間 Z80 CPU を待たせる事になってしまう。Z80 命令の LDIR 等を使った場合、21クロックで済む所であるから、明らかに遅すぎる。VRAM変更データーをどこかにキャッシュして時間に余裕のある時にSPI送出するにしても、キャッシュが一杯になると誤作動してしまう。やはり、VRAM領域全体をキャッシュのように扱い、これを定期的に液晶側にデーター送出するのが、一番安定した実装方法であろう。

色々調べると、「ESP8266用ILI9341ライブラリの高速化」というブログ記事を見つけた。次のような記述がある。

Adafruit_ILI9341のSPIのクロックの設定は24MH。しかし、問題無く動いていた。調べるとESP8266のSPIクロックは最大80MHz。試しに80MHzでプログラムを動かすと、問題無く動いているように見える。他のプログラムで試した時、80MHzでは書込みミスが発生したので、最終的に40MHzにした。

ILI9341のSPIは、40 MHzぐらいでも問題なく信号のやりとりができるようで、このオーバークロックで実装全体を考えてみた。PIC上でCPUは48 MHzで動いていて、SPI通信の最高速度としても、48 MHzが選択できる(REFOCONを使用)。この速度で使ってみると、確かにエラーはほとんど起らないようだ。定期的に画面データーを送出し続ける場合には、低頻度のエラーは許容できる。割り込みを用いて定期的にVRAMのデーターに基づきSPI送信する方式にして、15 fps の実装が実現できた。上記の写真ではスクロールゲームのBugFireを実行しているが、全く問題なく遊べる事が分かった。

アプリケーションロード機能

このプロジェクトは、MachiKaniaの一つのタイプとして考えている。MachiKaniaの特徴としては、ボタンを押しながら起動する事でブートローダーが立ち上がり、HEXファイルを選択する事でアプリケーションの切り替えができる事が挙げられる。この機能を是非とも実装したい。

KM-Z80 midiの特徴としては、SD/MMCカードではなくUSBメモリーを使っている事がある。この、USBホストのインターフェース部分のプログラムはかなり大きく、ブートローダーにこれを組み入れると大容量の領域を占有してしまう。MachiKaniaの他のタイプと同じメカニズムを採用すると、後にMachiKania BASIC用の容量が足りなくなってしまう事が分かった。方式を変えないといけない。

MachiKania type N/Z/Mの場合、ブートローダーを構築するフラッシュ領域は、アプリケーションを構築するフラッシュ領域と分けられている。従って、ブートローダー上のコードでアプリケーション用のフラッシュ領域を書き換える事ができる(その逆も理論的には可能)。KM-Z80 midiでは、上の段落で述べたように、ブートローダー領域をアプリケーション領域と分ける事ができない。アプリケーション用のフラッシュ領域を書き換えるためには、この領域以外でコード実行しなければならない。唯一の方法は、フラッシュ領域を書き換えるためのコードをRAM上で実行する事である。

この目的のために、RAM上で実行するフラッシュ書き換えプログラムを作成した。GitHubで公開している。このプログラムの特徴は以下の通り

1)RAM上で走るプログラム
2)USB host機能を持つ
3)ファイルシステムIOを持つ
4)Intel HEX ファイルを読み込み可能
5)フラッシュ書き込み機能を持つ

特徴は、リンカースクリプトの以下の記述に現れている。
MEMORY
{
  kseg0_program_mem     (rx)  : ORIGIN = 0xA0003000+0x0490, LENGTH = (0xD000-0x0490) /* modified (ORIGIN = 0x9D001000, LENGTH = 0x1F000) */
  exception_mem               : ORIGIN = 0xA0002000, LENGTH = 0x1000 /* modified  (ORIGIN = 0x9D01F000, LENGTH = 0x1000) */
  debug_exec_mem              : ORIGIN = 0x9FC00490, LENGTH = 0x0 /* dummy (LENGTH = 0x760) */
  kseg0_boot_mem              : ORIGIN = 0x9FC00490, LENGTH = 0x0
  kseg1_boot_mem              : ORIGIN = 0xA0003000, LENGTH = 0x490 /* modified (ORIGIN = 0xBFC00000)*/
  config3                     : ORIGIN = 0xBFC00BF0, LENGTH = 0x4
  config2                     : ORIGIN = 0xBFC00BF4, LENGTH = 0x4
  config1                     : ORIGIN = 0xBFC00BF8, LENGTH = 0x4
  config0                     : ORIGIN = 0xBFC00BFC, LENGTH = 0x4
  kseg1_data_mem       (w!x)  : ORIGIN = 0xA0000000, LENGTH = 0x2000 /* modified (LENGTH = 0x8000) */
  sfrs                        : ORIGIN = 0xBF800000, LENGTH = 0x100000
  configsfrs                  : ORIGIN = 0xBFC00BF0, LENGTH = 0x10
}
0xA0000000-0xA0001FFF をRAM領域、0xA0002000-0xA0002FFF を割り込み領域、0xA0003000-0xA000348F をブート領域、0xA0003490-0xA000FFFF をメインプログラム領域とした。

使用する際は、このhexファイル(fwiter.hex)中の0xA0002000-0xA000FFFFの領域をRAMに書き込み、アプリケーションHEXファイルのファイル名を0xA000FFF0からの領域に書き込んで、0xA0003000からコードを実行すればよい。KM-Z80 midiのそれぞれのアプリケーション内で、ファイル選択してこのfwriteに処理を引き継ぐ用にすれば、PicKit無しでアプリケーションの切り替えができるようになった。

今後の予定

今後の予定としては、次のような事を考えている。

1)MZ-80Kミニチュア版のような格好いい筐体を作製
2)キーボード部のカバーを、紙から3Dプリンター製のものに変えて使いやすく
3)MachiKania BASICも使用出来るようにする
4)CP/Mも使用出来るようにする(ほぼ完成)
5)Fuzixも対応できたらいいなぁ(独り言)

バージョン 0.4 は、GitHubにてダウンロード出来ます。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=953 Mon, 23 Sep 2019 14:18:50 PDT ITEM953_20190923
KM-Z80 midi 制作中 https://www.rad51.net/blog/mycom/?itemid=952
2019-07-15-kmz80midi1.png

特徴は、以下の通り:

1)Sharp MZ-80K 互換機
2)PIC32MX270F256Bを使用した、1チップ設計
3)USB メモリーを使って、プログラムのロードとセーブが可能
4)キーボードを内蔵しており、外付けキーボード(PS/2など)が不要
5)NTSC ビデオ出力(モノクロ;40x25文字)
6)将来的に、液晶表示にも対応予定
7)将来的に、MachiKania BASICも実行可能予定

今回の設計では、MZ-80Kと全く同じ横16個・縦5個のキーボードを実装し、48K RAMのフル装備で、USB メモリーによりプログラムを読み書き出来る仕様とした。いつもの通り、1チップでZ80のエミュレーション・ビデオ信号作製・キーボード読み取り・音出力まで全てこなす事を目指した。何とかDIP-28の石に収まっている。

回路図は以下の通り(クリックで拡大表示)。
2019-08-03-Image.png
2019-07-15-kmz80midi4.png
(注:2019/08/03 回路図を微修正しました。緑LEDをRB0に繋がず、トランジスターを介してRB1に繋ぐようにしました。)
(注:2019-08-24 ver 0.3以降では、回路図が微修正されています。RB5とRB15の機能が入れ替わっているので、ご注意ください。)
ver 0.4の記事を参照して下さい。)

なお、冒頭の写真ではキーボード部分が紙に印刷されたフェイクの様にも見えるが、紙の下は次の写真のようにタクトスイッチが並んでいて、ちゃんとキーボードとして機能する。
2019-07-15-kmz80midi2.png

8つの信号線で78個のタクトスイッチの押下を判断するため、抵抗を用いて様々な電圧を作製し、それをA/Dコンバーターで読み込む事にした。何とか信号線の数が8本におさまり、かつ、誤作動無く動いている。PIC32のプログラムではキーボード読み取りに多少の時間がかかるが、それ用のルーチンはNTSCビデオシグナルの為の待ち時間を利用して実行しているため、パフォーマンスに影響することなく実装出来ている。また、今回の実装ではNTSCビデオシグナルの作製部分のコードを1から書き直し、表示のブレが全くない綺麗な表示にする事が出来た。

7月15日現在、とりあえずMZ-80Kが、ほぼ実機通り動くようになった。プログラムのロードとセーブは、カセットテープインターフェースの代りに、USB メモリーが使えるので、実機よりスピーディーで便利になっている。また、電源投下後にすでにKM-BASICがロードされているので、「GOTO$1200」とすれば、すぐにBASICが使用出来る仕様(いつもの通り)。

今後の予定としては、次のような事を考えている。

1)MZ-80Kミニチュア版のような格好いい筐体を作製
2)キーボード部のカバーを、紙から3Dプリンター製のものに変えて使いやすく
3)NTSCビデオだけでなく、液晶(ILI9341)にも対応
4)MachiKania BASICも使用出来るようにする
5)CP/Mも使用出来るようにする

バージョン 0.1 は、GitHubにてダウンロード出来ます。

ver 0.4の記事を書きました。そちらもご参照ください。)]]>
PIC https://www.rad51.net/blog/mycom/?itemid=952 Mon, 15 Jul 2019 15:32:50 PDT ITEM952_20190715
PIC32MXで、USBメモリーを扱う https://www.rad51.net/blog/mycom/?itemid=939
回路は、以下の通り。なお、パスコンはすべて0.1 μFで。
2017-04-01-schematic.png

最近は、PIC32用のライブラリーは、Harmonyという巨大なフレームワークに統合されているらしく、プロプライエタリーなファームウェアを開発するのには、これはこれで便利なのだろうけれど、小規模なオープンソースファームウェアを開発するのには、向かなさそう。今回は、Microchipが提供しているライブラリーのうち、legacy版の2013_06_15を使うことにした(Microchip Libraries for Applicationsで、「Legacy MLA」タブを選択)。

「USB Host - MSD - Simple Demo - XC32」というのがあったのでこれだろうと調べてみると、どうやら、USBメモリーを認識するとルートディレクトリーに"test.txt"という名のファイルを作成する、シンプルなプログラムのよう。とりあえず、PIC32MX250F128Bのプロジェクトとしてビルド出来るところまでこぎ着け、必要なファイル(*c 及び *.h)だけを抽出して、一つのディレクトリーに収まるプロジェクトとした。

もしこれを、何か他のソフトウェアーに取り込むのなら、USBメモリー読み書き部分のライブラリー(*.a)を作成して、このライブラリーをインクルードする(MachiKaniaが取っている方式)のが、すっきりしそう。

usbhost2のver 0.12は、ここからダウンロードできます。
Microchip社のコードを改変して用いているので、使用の際、ライセンスはMicrochip社のそれに従って下さい。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=939 Sat, 01 Apr 2017 16:51:47 PDT ITEM939_20170401
KM-BASIC for MIPSの紹介 https://www.rad51.net/blog/mycom/?itemid=910 ケンケンさん作成のPIC32テレビゲームシステム(以下、PIC32TVGS)上で動くアプリケーションとして作成しました。表示はテキストのみですが、カラーでの表示が可能です。BASICプログラムを機械語コードにコンパイルしてから実行するので、高速な動作が特徴です。SDカードに複数のBASICプログラムを入れておき、PIC32TVGSの選択画面で選択して実行することが可能です。SDカード上のファイルの配置方法については、ダウンロードファイルのreadme.txtを参照して下さい。記事の最後に、ダウンロードのためのリンクがあります。

文法について

以下は、readme.txtからです。基本的には、MZ-80K用に作成したKM-BASICと同じですが、データー幅が32ビットになったこと、演算子に優先順位を付けたこと、文字列の結合がどこでも出来るようになったことなど、若干の機能向上があります。
<BASIC言語の書式>
BASICプログラムの記述は、行番号式、ラベル式、その混合、いずれの方法でも構
いません。以下、仕様について述べます。


<利用可能な変数型>
利用できる変数の型は、32ビット符号付整数(-2147483648 ~ +2147483647)と、
文字列型の2種類です。文字列の末端部には0x00が付加されます。

A-Zの26個の整数型変数が利用可能です。文字列として扱う場合はA$のように記
述します。ただし、A(整数型)とA$(文字列型)を同時に使用することは出来ま
せん。

整数型の定数は、10進法で記述します。16進法を使う場合、「$1200」のよう
に、頭に「$」を付加するか、「0x1200」の様に表記して下さい。

文字列型の定数は、「"」で囲って記述してください。「"」を使用する場合は、
「CHR$($22)」のように記述することが出来ます。


<命令>
以下、x, y, z等は整数値を、x$, y$, z$は文字列を指します。xxx, yyy, zzz, 
www等は任意のステートメントを指します。[ ]は省略可能で有る事を示します。

命令同士を「:」で区切ることにより、一行で複数のコマンドを処理すること
が出来ます。

REM xxx
	何も実行しない
[LET] x=yyy
	yで示された計算結果を、xに代入する。「LET」は省略可。
[LET] x$=yyy
	yyyで示された文字列(もしくは連結結果)を、x$に代入する。「LET」
	は省略可。
DIM xxx [, yyy [, zzz [, ... ]]]
	整数型の一次元配列を割り当てる。
	xxx,yyy,zzzは、例えば「A(10)」のように記述する。この場合、A(0)から
	A(10)までの11個の整数型変数が確保される。
CLEAR
	すべての文字列型変数と整数型配列を破棄し、整数値を0とする。
PRINT [ xまたはx$ [ ,または; [ yまたはy$ [ ... ]]]]
	ディスプレイに、整数値または文字列を表示する。「;」を使用した場
	合、次の表示が続けて行われる。「,」を使用した場合、10文字ずつ
	に区切って表示される。どちらも使用しない場合、次の表示は行を変え
	て行われる。
POKE x,y
	xで示される物理的アドレスに、yで示される値(1バイト値)を書き込む。
EXEC x[,y[,z[...]]]
	機械語を実行する。ただし、x,y,zは32ビット整数値。
IF x THEN yyy [ ELSE zzz ]
	xが0以外のとき、yyyを、0のときzzzを実行
FOR x=yyy TO zzz [ STEP www ]
NEXT
	yyyで示された計算結果をxに代入し、xの値がzzzになるまで次のNEXT文
	までのステートメントを、繰り返し実行する。繰り返しのたび、xの値は
	wwwずつ増加する(省略された場合は1ずつ)。「NEXT」の次に何も記述
	しないことに注意。
LABEL xxx
	GOTO/GOSUBのジャンプ先を登録する。xxxは、英数字6文字以内の文字列。
GOTO xxx
	xxx行目に移動する。
GOSUB xxx
	現在の実行位置を記憶し、xxx行目に移動する。
RETURN
	最後に実行されたGOSUB文の次のステートメントに移動する。戻り値を指
	定することがができる。この場合の戻り値はGOSUB()関数にて取得が可能。
END
	BASICプログラムを停止する。
SOUND xxx
	効果音を再生する。詳細は、下記<SOUND>の項を参照。xxxは行番号もしく
	はラベル。
MUSIC x$
	BGMを演奏する。詳細は、下記<MUSIC>の項を参照。

RESTORE xxx
	DATA読み出し開始位置を指定。xxxは行番号もしくはラベル。

DATA x[,y[,z[...]]]
	データー列を整数値で指定する。

CURSOR x,y
	カーソル位置指定。

CLS
	スクリーン消去。

COLOR x
	テキスト色指定。

PALETTE n,r,g,b
	パレット指定。

BGCOLOR r,g,b
	背景色指定。

DRAWCOUNT
	DRAWCOUNT値を指定する。DRAWCOUNT値に付いては、DRAWCOUN()関数を
	参照。


<関数>
以下、x, y, zは整数値を、x$, y$, z$は文字列を指します。[ ]は省略可能で有る事
を示します。

PEEK(x)
	xで示される物理アドレスから1バイト読み取り、返す。
RND()
	0から32767までの擬似乱数を返す。
ABS(x)
	xの絶対値を返す。
SGN(x)
	xの符号(-1, 0, または1)を返す。
NOT(x)
	x=0の場合に1を、そうでない場合に0を返す。
ASC(x$)
	文字列の最初の一文字の、アスキーコードを返す。
LEN(x$)
	文字列の長さを返す。
STRNCMP(x$,y$,z)
	2つの文字列のうちz文字を比較し、結果を返す。同じ文字列の場合は0。
CHR$(x)
	xをアスキーコードとする文字を返す。
HEX$(x [,y])
	xの値を、16進数の文字列として返す。yが指定された場合、yバイト長の
	文字列になる。
A$(x [,y])など
	xの値が0の場合、文字列全体を返す。
	xの値が精の場合、xで示される位置より右側の文字列を返す。
	xの値が負のとき、文字列の右側x文字を返す。
	yが指定された場合、y文字分の文字列を返す。

DRAWCOUNT()
	DRAWCOUNT値を得る。DRAWCOUNTは16ビット整数値で、1/60秒ごとに1ずつ
	増える。

KEYS() or KEYS(x)
	キー入力を得る。xの値は以下の通り。xを指定しない場合は、x=63と同じ。
		KEYUP:    1
		KEYDOWN:  2
		KEYLEFT:  4
		KEYRIGHT: 8
		KEYSTART: 16
		KEYFIRE:  32

TVRAM([x])
	ビデオRAMのx番目の内容を、バイト値で返す。xを省略した場合、ビデオ
	RAMの開始位置の物理アドレスを返す。

MUSIC()
	BGMの演奏の残り数を返す。

GOSUB(xxx)
	GOSUBと同じだが、戻り値(RETURNを参照)を得ることが出来る。xxxは、ラベ
	ルもしくは行番号。
 
READ()
	DATA文の後から、一つずつデーター(整数値)を読み出す。


<演算子>
x + y
	整数加算
x - y
	整数減算
x * y
	整数乗算
x / y
	整数除算
x % y
	整数剰余
x = y
	2つの整数値が等しい場合に1、そうでないときに0
x != y
	2つの整数値が等しい場合に0、そうでないときに1
x < y
	xがyより小さい場合に1、そうでないときに0
x <= y
	xがyより小さいか等しい場合に1、そうでないときに0
x > y
	xがyより多きい場合に1、そうでないときに0
x >= y
	xがyより多きいか等しい場合に1、そうでないときに0
x AND y
	xとyの値のビットごとの AND(論理積でないことに注意)
x OR y
	xとyの値のビットごとの OR
x XOR y
	xとyの値のビットごとの XOR
x$ + y$
	文字列の連結


<MUSIC>
MUSIC命令では、BGM用のデーターを文字列で指定します。文字列の書式は、ABC 
notationに準拠しています。ただし、すべての記法が使えるわけではありません。
なお、キーや速度などのデフォルト設定値は以下の通りです。

Q: 1/4=90
L: 1/8
K: C

BGM演奏時に一度に設定できる音の数は、31迄です。これを超えて音楽を再生したい
場合は、MUSIC()関数の戻り値を調べ、その値が十分小さくなってから、次のMUSIC命
令を実行するようにします。

添付のmusic.txtに、使い方に関するサンプルがありますので、参考にして下さい。


<SOUND>
SOUND命令では、DATA列のデーターを、行番号もしくはラベルで指定します。SOUND命
令による効果音再生中は、BGMは再生されません。また、前の効果音が終わる前に次
のSOUND命令を実行すると、前の効果音の再生は停止し、新しい効果音がすぐに再生
されます。

DATA列では、32ビット整数値として、交換音を表現します。この整数値の下位16
ビットは周波数の指定です。2048が440Hz(ラの音)に対応します。上位16ビットは、
音の長さです。1が、1/60秒に相当します。最後に、65535以下の値で、効果音の繰り
返し回数を指定します。これらのデーターの数は、32を超えないようにして下さい。

添付のsound.txtに、使い方に関するサンプルがありますので、参考にして下さい。

プログラムの作成について

PIC32TVGSにはキーボードがありませんので、BASICプログラムはテキストファイルとして別途作成し、SDカードに書き込んでおく方式です。このBASICプログラムを開発するための簡易環境をHTML5で作成したものを提供しています。開発環境はダウンロードファイルに同梱してありますが、以下のリンクから実行することも可能です。
http://hp.vector.co.jp/authors/VA016157/kmbweb03/index.html

サンプルプログラム

3つのサンプルプログラムを同梱しています。

・shoot.txt
シューティングゲームです。「V」を操作して、下から迫る「A」を撃墜して下さい。「A」は、4秒ごとに上に上がってきます。「A」に最上段まで進入されると、ゲームオーバーです。こちらが弾を撃っている間は、「A」はひるんで進んできませんので、4秒少し前に、列のすべての「A」を打ち落とすのがコツです。
2015-04-14-shoot.png
これは、私が中学生の頃、日立のBASICマスター・レベル2用に即興で作成したゲームです。即興の割には友人の間でウケが良かったので、ここに移植してみました。

・music.txt
BGM演奏のサンプルです。曲を選んで、Fireボタンを押して下さい。
2015-04-14-music.png
クラシック音楽に混じってなぜかスタートレックのテーマ曲が入っていますが、気にしないで下さい(笑)。

・sound.txt
効果音出力のサンプルです。音の種類を選んで、Fireボタンを押して下さい。
2015-04-14-sound.png

サンプルプログラムはすべて、開発環境のエミュレーターでも実行できるので、試してみて下さい。

ダウンロード

KM-BASIC for MIPS ver 1.0.5は、ここからダウンロードできます。

LGPLベースのライセンスの、フリーソフトです。ただし、幾つかのライブラリーを使っている関係上で、純粋なLGPLではなく、ある程度の制約があります。詳しくは、readme.txtを参照して下さい。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=910 Tue, 14 Apr 2015 17:51:04 PDT ITEM910_20150414
MIPS用BASICコンパイラーを制作中 https://www.rad51.net/blog/mycom/?itemid=903 ケンケンさんのPIC32MX用SDカードブートローダー(+カラーNTSC出力)用に、整数型BASICコンパイラーを作成することにした。仕様は、以前にMZ-80用に作成したKM-BASICと同じに。必要最小限の仕様。SDカードにBASICのソースコードをテキストファイルで置いておけば、それをコンパイルして即実行するような物を考えている。

9割以上出来上がっているのだが、最後に、RAM上のプログラムの実行権限を得るところで手間取っていて、公開は次の週末以降になる見込み。ここでは、基本的な構造に関する覚え書きなど、記しておく。

演算アルゴリズム
MZ-80用のKM-BASICでは、演算子に優先順位を付けなかった。これを実装すると、コンパイラー自身のコードサイズが大きくなってしまうので、メモリー容量の少ないモデルでは割愛した。今回は、広大(と言っていい)なフラッシュメモリー領域があるので、少々複雑な事をする実装が可能なので、演算の優先順位付きでの実行は、やはり欲しいところ。私が大学生の頃、NECのPC-9801用に整数(16 bits)型BASICコンパイラーを作成したことがあって、その時も実装したのだが、どうやったかどうも思い出せない。もう一度考え直して出来たのが、次のアルゴリズム。ここにメモしておけば、将来同じ事をするときに、無駄に時間を費やすことがないだろう。

2014-07-28-calc.png

演算順位決定ルーチンは、一つの関数(再帰呼び出しされる)で実装されている。実際のコードは、以下の通り。
char* get_value_sub(int pr){
	char* err;
	enum operator op;
	int prevpos;
	// Get a value in $v0.
	err=get_simple_value();
	if (err) return err;
	while(1){
		// Get the operator in op. If not valid operator, simply return without error.
		prevpos=g_srcpos;
		err=get_operator();
		if (err) return 0;
		op=g_last_op;
		// Compair current and previous operators.
		// If the previous operator has higher priolity, return.
		if (pr>=priority(op)) {
			g_srcpos=prevpos;
			return 0;
		}
		// Store $v0 in stack
		g_sdepth+=4;
		if (g_maxsdepth<g_sdepth) g_maxsdepth=g_sdepth;
		check_obj_space(1);
		g_object[g_objpos++]=0xAFA20000|g_sdepth; // sw v0,xx(sp)
		// Get next value.
		err=get_value_sub(priority(op));
		if (err) return err;
		// Get value from stack to $v1.
		check_obj_space(1);
		g_object[g_objpos++]=0x8FA30000|g_sdepth; // lw v1,xx(sp)
		g_sdepth-=4;
		// Calculation. Result will be in $v0.
		err=calculation(op);
		if (err) return err;
	}
}

この基本部分が出来てしまえば、あとは演算子の実装を、しらみつぶしに行なっていくだけ。

文字列の処理
文字列についても、MZ-80用の物よりも少し複雑な処理が行えるようにした。MZ-80用の物では、文字列の長さを80文字までに限定し、かつ、文字列の連結はLETステートメントのみで行える仕様であった。これを、いつでもどこでも文字列の連結が行え、長さの上限が無いようにした。結果として、一時領域の動的な確保とガベージコレクションが必要になった。

一時領域の確保をフレキシブルに行なうため、malloc()を使わず、一から実装することに。色々と試行錯誤した結果、最終的には、いつガベージコレクションを行なうかのタイミングだけで制御するコードになった。

一時的に作成された文字列は、それが変数に代入されるかPRINT文などで使用されるか、いずれかが実行されるまでは有効に保持しておかなければならない。逆に、これらが行なわれた後は、すべて破棄することが出来る。ただし、余り頻繁にガベージコレクションを行なっていては、実行速度の低下を招いてしまう。

そこで、ソースコードでの一行に一度、一時領域の確保をする直前に、それ以前の一時領域をすべて破棄する仕様にした。一時領域確保の際、その行での初めての実行かどうかの判断は、$s6レジスターの値をチェックすることで行なう。$s6レジスターの値が正の場合、初めての実行と見なし、すべての一時領域を破棄した後に、$s6レジスターの値を負にする($s6=0-$s6)。$s6レジスターは、行番号やジャンプ用ラベルの管理にも使用されているので、それについては下記を参照。

FOR~NEXT文の実装
FOR~NEXTには、スタックを用いている。FOR文のところで、現在の実行アドレスをスタックに待避し、NEXTのところでスタックに待避されたアドレスにジャンプするような実装。この実装のため、「~」の部分は、最低一度は実行される仕様である。また、下記で述べるGOSUBにもスタックが用いられているので、FOR~NEXTループ中ではRETURN命令は使用できない。

GOTO/GOSUB/RETURNの実装
GOTOは、純粋にMIPSのジャンプ命令に置換される。ただし、ジャンプ先としては行番号以外にラベルを指定することが出来る。GOSUBの場合は、現在の実行アドレスをスタックに待避した後にジャンプ、RETURNは、待避された実行アドレスへのジャンプである。ON-GOTOは実装していないが、その代りに、"GOTO 100+A"の様に、ジャンプ先をダイナミックに指定することが出来る。

GOTO/GOSUBのジャンプ先が静的な場合、コンパイル後のリンクの際に、ジャンプ先のアドレス部分が書き込まれる。それぞれの行番号とラベルが、どのアドレスに相当するかを調べる方法として、オブジェクト中にMIPSの$s6レジスターに値を代入する命令を挿入するようにした。このコードを走査することで、ジャンプ先を調べることが出来る。"GOTO 100+A"の様に、ジャンプ先をダイナミックに指定している場合も、同じメカニズムを利用している。また、オブジェクトの実行中に、「ゼロで割る」 「一時領域が足りない」などの例外が起きた場合に、$s6レジスターの値を調べれば、どの行で例外が起きたかを調べることが出来る。

追記:2014/8/03
以下、とりあえず動くようにしたバージョンです。hexファイルを、ケンケンさんのSDカードブートローダーから取り込んで下さい。BASICのソースコードを、hexファイルと同じファイル名で拡張子をtxtにして、保存して下さい。文法は、とりあえずこちらを参照。追加の命令もありますので、改めて公表します。
バージョン0.8.6がここからダウンロードできます。まだ、転載不可のライセンスとします。
]]>
PIC https://www.rad51.net/blog/mycom/?itemid=903 Mon, 28 Jul 2014 12:44:43 PDT ITEM903_20140728
SDカードブートローダー用API https://www.rad51.net/blog/mycom/?itemid=901 ケンケンさんのSDカードブートローダーは、PIC32MX150F128Bを搭載した、SDカードのアクセスとカラーのビデオ出力(NTSC)が可能な、ゲーム用のシングルボードコンピューターである。hexファイルをSDカードに書き込むことで、様々なアプリケーションを読み込んで実行することが可能な仕様だ。これ用のアプリケーションを色々と考えるのは、PIC使い(最近はPIC32ばかり)の私にとっては凄く楽しい。

このブートローダーは24KBの大きさがあり、SDカードへのアクセスとカラーのキャラクターディスプレイの機能がすべて含まれている。アプリケーションを作成する場合、これらの機能が使えるようになれば、色々と応用が利くし、バイナリーのサイズも小さく抑えることが出来る。

そこで、アプリケーション側から、ブートローダーのSDカードアクセスと、カラー・キャラクターディスプレイのコードを呼び出して、これらの機能を簡便に使うことが出来るようにしてみた。

Microchip社のCコンパイラーでは、PIC32でアプリケーションを作成する場合の関数呼び出しのための規約があるようで、同じCPU(MIPS 32)を使用する限り、同じようにコードを呼び出して使うことが出来る。従って、基本的には、ブートローダー用のコードが記述されているフラッシュメモリーの物理アドレスに、ジャンプすればよい。やらなければならないことは、次の2つである。

1)ブートローダー側で呼び出したい関数が、どの物理アドレスから実行できるかを調べる。
2)アプリケーション側では、その物理アドレスにジャンプするようなコードを記述する。

1)に付いては、必要な関数のポインターを配列で用意しておき、それを固定の物理アドレスに配置することにした。2)に付いては、関数ポインターを利用する一般的なCのコードを利用することにした。

ブートローダーの改変

関数ポインターの配列は、初め、次のような方法で確保しようとした。コード用のフラッシュメモリーの一番最後のアドレスに埋め込む方式である。
__attribute__((address(0x9D005FD0))) static const int g_addresses[]={
	(int)setcursorcolor,
	(int)printstr,
	(int)setcursor,
	(int)clearscreen,
	(int)init_composite,
	(int)FindNext,
	(int)FindFirst,
	(int)FSfclose,
	(int)FSfread,
	(int)FSfopen,
	(int)FSInit,
	(int)MDD_MediaDetect
};

これはこれでうまく働いたが、コード中では参照されていないこの配列をリンカーが無視しないように、工夫しないといけない。そういった改変を加えると、ブートローダーのサイズがぎりぎりになってしまい、今後コードの改善が必要になった場合にそれを受け付け無くなってしまう可能性があった。他方で、割り込みベクター領域に使われていないフラッシュ領域がたくさんあることに気が付いたので、そこを使うことにした。最終的に、ブートローダー側の変更は、次のアセンブリを"*.s"ファイルとして追加するだけになった。
/* 9D00_0220 */
.section .vector_1,code
	.long MDD_SDSPI_MediaDetect
	.long FSInit
	.long FSfopen
	.long FSfread
	.long FSfclose
	.long FindFirst
	.long FindNext

/* 9D00_0240 */
.section .vector_2,code
	.long drawcount
	.long TVRAM
	.long init_composite
	.long clearscreen
	.long setcursor
	.long printchar
	.long setcursorcolor

アプリケーション側のコード

アプリケーション側では、なるべく簡易で分かりやすいコードにしたいと思ったので、ヘッダーファイルをインクルードして、定義されている関数を呼び出すだけで使えるようにした。ヘッダーでは次のように記述している。
#define fsdpointer ((int**)0x9D000220)
#define fvpointer ((int**)0x9D000240)

関数の実装は、例えば次のように。
void printchar(unsigned char n){
	void (*f)(unsigned char n);
	f=(void (*)(unsigned char n))fvpointer[5];
	f(n);
}

ブートローダー側のキャラクターディスプレイでは、幾つかの割込みも使用されている。これらも実装しておかなければ、文字は表示されない。ここは簡単で、割り込みベクターテーブルに、ブートローダーの割り込みベクターテーブルにジャンプするコードを記述するだけで良く、次のようなアセンブリを一つ用意するだけで済んだ。
.section .vector_6,code
	nop;
	j 0x9d0002c0;
.section .vector_8,code
	nop;
	j 0x9d000300;
.section .vector_10,code
	nop;
	j 0x9d000340;
.section .vector_14,code
	nop;
	j 0x9d0003c0;

このプロジェクトを進める上で、上記の部分までは比較的すんなりいったのだが、最後に難関が一つだけ在った。初め、用意したAPIのうち、キャラクターディスプレイに関する部分は機能したが、SDカードへアクセスする部分が機能せず、これを使うと例外でCPUが停止する不具合に見舞われた。色々調べて、ようやく分かったことは、アプリケーション側とブートローダー側で異なるグローバルポインター(gpレジスター)が使われていて、それにより、予想外のメモリーアクセスが在って、それが例外の引き金になっていたことである。最終的には、リンカースクリプトで同じグローバルポインターの値を利用するように指定することで、回避した。アプリケーション用のリンカースクリプトは、ケンケンさんのサイトで指定されている物を変更して使っているが、主な変更点は、以下の通り。

  kseg1_data_mem       (w!x) : ORIGIN = 0xA0002000, LENGTH = 0x6000

  _gp = ALIGN(16) + 0x7ff0 - 0x2000;

ようするに、ブートローダーで使われているRAM領域、8 kbを使わないように保護することと、その指定によりグローバルポインターが変わってしまうのを元に戻す、というのがポイントである。

最終的に出来上がったAPIを呼び出すための仕組みは、次の4つのファイルをプロジェクトに取り込むことで実装できる。
・api.c
・api.h
・apiint.s
・app_32MX150F128B.ld

これを使えば、今までPIC32を使ったことが無かった人でも、Cの知識さえあれば、容易にSDカードが扱えて、カラーテキストを表示するアプリケーションを作成できるようになるのではないだろうか。

SDカードブートローダ用API ver 0.4.3は、ここからダウンロードできます。
簡単なテストアプリケーションが含まれています。ライセンスは、LGPLです。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=901 Sun, 22 Jun 2014 17:38:30 PDT ITEM901_20140622
KM-Z80 gameの紹介 https://www.rad51.net/blog/mycom/?itemid=896 ケンケンさん製作の、SDカードブートローダー用のソフトウェアで、MZ-80K互換機で動くゲームを楽しむための物です。

MZ-80K用のゲームは、例えば以下のサイトでフリーで配布されています。
http://www.retropc.net/mz-memories/mz700/
http://www.maroon.dti.ne.jp/youkan/mz700/zeplis/

これらのファイルをSDカードから読み込んで楽しむことが出来ます。

ケンケンさんの回路では6個のボタンがありますが、これらのボタンを押したときにどのキーを押したことにするかのスクリプトを作成する必要があります。詳しくは、ダウンロードされるファイルに含まれているreadme.txtを参照して下さい。

MZ-80Kでは、2 MHzのクロックの所、ケンケンさんの回路では3.58 MHzの水晶を使っている加減で、ほんの少し動作が異なる部分がありますが、ゲームを楽しむ分にはほとんど影響ないと思います。

従来のKM-Z80 miniシリーズと比べて表示が若干暗いです。回路を変更すれば明るくできるのですが、今回は回路の変更無しで済ませました。

KM-Z80 game ver 0.5.8は、ここからダウンロードできます。
KM-Z80 game ver 0.6.0は、ここからダウンロードできます。

記事の最初でご紹介した、MZ-80Kのフリーソフトのうち、View Finder V 1.1とBug Fireについて、キー設定スクリプトを書いてみました。V-FINDER.MZFもしくはBUGFIRE.MZFとして保存し、KM-Z80 gameのhexファイルも同じファイル名(拡張子はhexのまま)で保存し、使って下さい。SDカードに保存するファイルは、次の例のようになります。

2014-06-15-dir.png
スクリプトはここからダウンロードできます。

追記 10/14/2014
ver 0.6.0をアップしました。RA1から390Ωの抵抗を介して、Video Outにつなぐと、明るい表示になります。この改造を施さない場合、表示は暗いままですが、実行に支障はありません。]]>
PIC https://www.rad51.net/blog/mycom/?itemid=896 Sun, 15 Jun 2014 18:40:36 PDT ITEM896_20140615
KM-Z80C miniの紹介 https://www.rad51.net/blog/mycom/?itemid=895 KM-Z80 mini (ver 0.3)では、24 KBのRAMを搭載していましたが、KM-Z80C miniでは、48 KBになりました。また、カセットテープインターフェースに加えて、SDカードを用いたプログラムのLOAD/SAVEが行えます。

2014-05-19-IMG_0592.jpg2014-05-19-IMG_0593.jpg

フロントパネル(写真左)には、左から順にカセットテープインターフェース用のジャック(再生・録画兼用)、パイロットランプ(緑・赤)、PS/2キーボードコネクタ、電源スイッチがあります。リアパネル(写真右)には、サウンド・ビデオ用のコネクタに加えて、SDカードの差し込み口があります。

ふたを開けると、こんな感じです。

2014-05-19-IMG_0590.jpg

TQFPのチップを使っているので、筐体の中はシンプルです。DIPほどの存在感が無く、すかすかした感じですね。

LOAD命令を実行すると、SDカードを装着している場合は、ファイル選択画面になります。

2014-05-19-IMG_0596.jpg

キーボードのカーソルキー(PS/2キーボードでは、ALT/CTRLキー;上・左への移動は、shiftキーを使う)でファイルを選択した後に、spaceキーで選択できます。その他は、MZ-80Cと同様に使えます。

回路図は、以下の通りです。

2014-05-19-SCH05.png

KM-Z80C miniver 0.5.3.1は、ここからダウンロードできます。
KM-Z80C miniver 0.5.3.7は、ここからダウンロードできます。

ライセンスは、基本的にはLGPLですが、ソースコードの中には異なるライセンスのファイルも含まれていますので、注意して下さい。
]]>
PIC https://www.rad51.net/blog/mycom/?itemid=895 Mon, 19 May 2014 21:07:52 PDT ITEM895_20140519