以前書いた記事(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で試してみた。


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だった。
↓実行結果

ちゃんと実行されるようだ。
まとめ
SBCを作るときはRAMをケチらないで載せておけばlcc1802は使える。今時、32kのRAMを積んでも大した出費ではない。
残念ながら、オリジナルのCOSMAC ELFではRAMが少ないので使えないだろう。もっともオリジナルのCOSMAC ELFを忠実に作るなら、ハンドアセンブルして、8個のスイッチでプログラムを入力する覚悟が必要だ。
rcc.exeがASxxxxアセンブラを吐くようにするとSDCCのライブラリが使えるようになる。そして、aslinkを使うと出力がもう少しコンパクトになると思う。 興味はあるが、そこまでやるか...
【最近の投稿】【2017の投稿】【2016の投稿】【2015の投稿】【最近のCOSMAC/CDP1802】
最近のコメント