フォト
無料ブログはココログ

MyList

« 漫画村騒動 <Napsterの二の舞> | トップページ | あ、「やりがい」とかいらないんで、とりあえず残業代ください。 »

2018年4月24日 (火)

lcc1802 <CDP1802用Cコンパイラ>

 以前書いた記事(CDP1802 (15/10/11))のコメントにCDP1802用のCコンパイラの質問があったので調べてみた。 

 結論は、出力されるバイナリが大きいので、RAMを十分載せておけば使える。

lcc1802

 CDP1802は入手したがまだ動かしていない。 (^^;
SBCを作ったとしても、おそらくtinyBASICでも動かしたら満足するのだろう。というわけで、CDP1802用のCコンパイラは調べていなかった。

 調べてみるとlcc1802が見つかった。
lccはOpen SourceのコンパイラでターゲットCPUはAlpha,SPARC,MIPS,x86に対応している。 lcc1802はlccをCDP1802に対応させたもので、COSMAC ELF Membersip Cardのアドオンシールドにarduinoを持たせた 1802 olduino 用に作られたもののようだ。 
https://sites.google.com/site/lcc1802/downloads
からダウンロードできる。

インストール

 lcc42judy.zipをインストールしてみた。
zipを解凍して現れるlcc42ディレクトリをCドライブ直下(C:\)にコピーしろと書いてある。
開発系は C:\Devzにインストールしているので、ここにインストールした。 コンパイル・ドライバ(lcc)が呼び出すコマンドは同じディレクトリを探すので問題はないだろうと考えたのだが、cppが見付からないと怒られた。(^^;
どうやら、プリプロセッサ(cpp.exe)、コンパイラ(rcc.exe)、アセンブラ(asw.exe)のパスは"C:\lcc42\bin"がハードコードしてあるようだ。 仕方がないので、C:\からinkを張っておいた。

エミュレータ

 まだSBCを作っていないのでCOSMACエミュレータ Emma 02で試してみた。
Emma 02はCDP1802が使われている数々のボードやシステムのエミュレーションができる。 その中でシンプルな VELFで試してみた。

Emma02_velf

Emma02_velf_frontpanel

 COSMAC ELFはシリアル・インタフェースを持っていない。Q(#4)をTXD、EF2(#23)をRXDに使ってソフトウェアで実装してある。

 シンプルな COSMAC ELFで試しておけばSBCを作るときにシリアルインタフェースを載せなくて良い。

サンプル

 lcc1802にはサンプルが用意されている。 これらのサンプルはolduino用なので、これを変更してVELFで動くようにする。

 まずはお約束の hello worldから。 examples/helloworldディレクトリにある。

↓helloworld.c

/*
   print the string "hello World!"
*/
#include <nstdlib.h>
void main()
{
printstr("hello World!\n");
}
#include <nstdlib.c>

ライブラリ

 4行目の #include <nstdlib.h>は良いが、最終行の #include <nstdlib.c>は何?である。

 lcc1802はライブラリはなくて、最低限の関数がinclude/nstdlib.cに書いてある。printf()もあるが、最低限の機能だけで出力幅指定はできない。

 文字列出力関数のprintstr()は1文字出力にout(5,d)を呼んでいるが、VELFでは動作しないのでputc()を使うように変更しておく。

↓nstdlib.c内のprintstr()

void printstr(char *ptr){
//    while(*ptr) out(5,*ptr++);
    while(*ptr) putc(*ptr++);
}

 nstdlib.cにはfloatを扱う関数がある。 sdccは賢いので必要な関数だけリンクしてくれるがlcc1802ではnstdlib.cはいつでもコンパイルされるので使わない関数でもオブジェクトに含まれる。float関係の部分だけは "nofloats"が宣言されているとコンパイルしないようになっている。 コマンドラインオプションで -Dnofloatsとすれば良い。

ランタイムライブラリ

 ランタイムライブラリは、include/lcc1802Prolo??.incとinclude/lcc1802Epiloxx.inc。

 これらはのライブラリは、コンパイル済のライブラリをlinkerがリンクするのではなく、ソースに展開している。ソース中にnsddlib.cをインクルードし、アセンブラソース中でlcc1802Prolo??.incとlcc1802Epiloxx.incをインクルードしている。

入出力ルーチン

 IOルーチンは、include/IO1802.incに書いてある。 printf()は1文字出力にputc()を呼んでいる。  lcc1802が想定しているolduinoの1文字出力はout(5,d)を使っているので、VELFでは出力できない。

 IO1802.incに1文字出力(putc())と1文字入力(getc())を書けば良さそうだ。
ここでハタと困った。CDP1802のアセンブラは書いたことが無い。
とりあえず、Emma02にVELFのモニタソース(Emma02\data\Velf\VELFbios-v3.1.LST)があったので、この中の1文字入出力ルーチンを参考に(ほぼコピペ)、Cから引数を渡す部分と戻り値を返す部分を付け加えて、テンポラリ変数をreからrt1に変えた。

↓IO1802.inc

;**********************************************************************
;Transmit Byte via Q connected to RS232 driver
;call with R3 being the previous PC
;Byte to send in D
;Returns with D unaffected
;re.1 = D
;Destroys rt1.0
;----------------------------------------------------------------------
_putc: glo regArg1
txchar: phi rt1
ldi 9 ;9 bits to transmit (1 start + 8 data)
plo rt1
ghi rt1
shl ;set start bit
rshr ;DF=0

txcloop:
bdf $+5 ;10.5   jump to seq to send a 1 bit
req ;11.5   send a 0 bit
br $+5 ;1      jump +5 to next shift
seq ;11.5   send a 1 bit
br $+2 ;1      jump +2 to next shift (NOP for timing)
rshr ;2      shift next bit to DF flag
phi rt1 ;3      save D in rt1.1
DEC rt1 ;4      dec bit count
glo rt1 ;5      get bit count
bz txcret ;6      if 0 then all 9 bits (start and data) sent
ghi rt1 ;7      restore D
NOP ;8.5    pause 1/2 time
br txcloop ;9.5    loop back to send next bit
txcret: ghi rt1 ;7
ghi rt1 ;8
ghi rt1 ;9
NOP ;10.5
seq ;11.5 stop bit
NOP ;1
NOP ;2.5
NOP ;4
NOP ;5.5
NOP ;7
NOP ;8.5
NOP ;9
NOP ;10.5
Cretn

;**********************************************************************
;rx_char
;Receive Byte via EF2 connected to RS232 rt1ceiver
;Recieves 8 bits
;call with R3 being the previous PC
;Returns with Byte received in D and rt1.1
;Destroys rt1.0
;----------------------------------------------------------------------
_getc:
rxchar: ldi 8 ;start bit +7 bits from loop, last bit on returning
plo rt1
ldi 0
rxcw: ;wait for start bit
bn2 rxcw ;each instr takes 9us, we need 104us = 11.5
;delay 1/2 bit time to center samples
NOP ;     Don't test for correct start bit
NOP ;     it will work, if there's too much
NOP ;     noise on the line, shorten the cable!
rxcloop:
NOP ;10.5
b2 $+6 ;11.5 sample rx input bit
ori 80h ;1
br $+4 ;2
phi rt1 ;1
phi rt1 ;2
shr ;3
phi rt1 ;4
DEC rt1 ;5
glo rt1 ;6
bz rxcret ;7
ghi rt1 ;8
br rxcloop ;9
rxcret: ghi rt1 ;8
ghi rt1 ;9
NOP ;10.5
b2 $+4 ;11.5 sample last rx input bit
ori 80h ;1
plo retVal
Cretn

コンパイル

 この変更でIntelHexフォーマットの a.hexが作られるようになる。

C:\Users\user> lcc -Dnofloats helloworld.c
And St. Judy's Compiler FLOATS Across your code...
P2HEX/C V1.42 Beta [Bld 87]
(C) 1992,2013 Alfred Arnold
C:\Users\user\AppData\Local\Temp/lcc124682.p==>>a.hex  (3609 Bytes)

しかし、hellowroldだけで3,609byteとは。 

8Queen

 8Queen問題を解くプログラムをコンパイルしてみる。 再帰を使っているのでちゃんと動くだろうか?

/*
* 8Queen.c
* Yoshi / Apr.2018
*/

#include <nstdlib.h>

#define FALSE 0
#define TRUE (!FALSE)
#define u_char unsigned char
#define u_short unsigned short
#define NL "\r\n"
#define getchar getc
int DEBUG = 0;

char table[8];
void clrtbl(void) { int i; for(i=0;i<8;i++) table[i]=0xFF; }

void printtb(char n) {
    int x, y;
    printf(NL "-- %d --" NL, n);
    for (y=0;y<8;y++) {
        putchar('|');
        for (x=0;x<8;x++) {
            if (table[y]==x)
                 putchar('Q');
            else putchar(' ');
            putchar('|');
        }
        puts(NL);
    }
}

char check(int x, int y) {
    int i;

    if (y==0) return TRUE;
    for (i=y-1; 0<=i; i--) {
        if (2<DEBUG) printf("tb[%d]=%d" NL, i, table[i]);
        if (table[i]==x || table[i]==(x-(y-i)) || table[i]==(x+(y-i)))
            return FALSE;
    }
    return TRUE;
}

char place(int x, int y) {
    static int n = 1;

    if (DEBUG) printf("place(%d,%d):" NL, x, y);

    if (7<y) {  /* 解 */
        printtb(n++);
        return FALSE;
    }
    if (7<x) {  /* 見つからない */
        return FALSE;
    }

    if (check(x,y)) {
        table[y] = x;
        if (n==1) printtb(n);
        place(0, y+1);
        table[y] = (u_char)-1;
    }
    return place(x+1, y);
}

void main(void) {
    printf("8 Queen" NL);
    clrtbl();
    place(0,0);
    exit(0);
}

#include <nstdlib.c>

コンパイルすると、4,542byteだった。

↓実行結果

Emma02_velf_terminal

ちゃんと実行されるようだ。

まとめ

 SBCを作るときはRAMをケチらないで載せておけばlcc1802は使える。今時、32kのRAMを積んでも大した出費ではない。

 残念ながら、オリジナルのCOSMAC ELFではRAMが少ないので使えないだろう。もっともオリジナルのCOSMAC ELFを忠実に作るなら、ハンドアセンブルして、8個のスイッチでプログラムを入力する覚悟が必要だ。

 rcc.exeがASxxxxアセンブラを吐くようにするとSDCCのライブラリが使えるようになる。そして、aslinkを使うと出力がもう少しコンパクトになると思う。 興味はあるが、そこまでやるか... 



最近の投稿】【2017の投稿】【2016の投稿】【2015の投稿】【最近のCOSMAC/CDP1802

 

« 漫画村騒動 <Napsterの二の舞> | トップページ | あ、「やりがい」とかいらないんで、とりあえず残業代ください。 »

CPUボード」カテゴリの記事

CDP1802/COSMAC」カテゴリの記事

プログラミング」カテゴリの記事

コメント

lcc1902 ?
インストールの方法ありがとうございます
随分と長いコードを吐く様ですが今時は32kB載せるのは簡単なので、問題無いと言えば問題無いですね
NASAは開発ツールの開発に失敗したとか書いて有るページを見つけてしまい、こう言うものは無いのかと思ってしまいました

やすいさんコメントありがとうございます。

lcc1802EplroFL.incをちょっと読んでみたのですが、関数コール/リターンがすごく面倒ですね。
C言語に向いていないCPUということでしょうか。でも、Cで書けるのと書けないのでは雲泥の差なので、少々効率が悪くてもありがたいです。

そもそもハードウェアスタックが無い時点でCコンパイラには向いてないかと思いました。
とは言え、あの簡素な?システムは使ってみたいと思ってしまいます。Forthをインプリした方も苦労して居たみたいで。Forthシミュレーターは200倍速いとか笑えました。

スタックを使わないでレジスタだけで書けるくらいのサイズなら楽しいかなと思います。(AT90S1200のような感じ)
基本インデックアドレッシングでインデクスレジスタの指定も間接というのは戸惑いますね。

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: lcc1802 <CDP1802用Cコンパイラ>:

« 漫画村騒動 <Napsterの二の舞> | トップページ | あ、「やりがい」とかいらないんで、とりあえず残業代ください。 »