MCS51 SBC
MCS51 SBC
- はじめに
捨てられない本がたくさんある。
エレクトロニクスライフ1990/8「必ず身に付くワンチップマイコン技術」もその一つだ。その頃には既に就職していて、PC-286Lという、286といいながら80286を使っていない、NECが作ったV30というIntelの8086互換CPUを使った、EPSONが作ったNECのPC-9801の互換機を使っていた。(ああややこしい)
閑話休題
この雑誌は、高周波からオーディオ、デジタル回路まで幅広く扱う良い雑誌だった。
この月の特集は「必ず身に付くワンチップマイコン技術」といいながら、8052AH-BASICを使ったシングル・ボード・コンピュータの特集である。この雑誌を、懐かしく読んでいたら、8052AH-BASICはまだ手に入るのだろうかとふと思い、google先生に聞いてみたら簡単には見つからなかった。
8052AH-BASICは、Intelのi8052の内臓ROMにBASIC-52を焼いたもので、BASIC-52のソースは公開されていて入手可能であることが分かった。
であれば、ROMなしの8032の外部ROMにBASIC-52を焼くかEPROM版の8752にBASIC-52を焼こうと思い8032,8752を探したけれど見つからない。 8031,8051は富士通とか三菱とかNECとか見つかるのだが8032,8052,8752は見つからないのである。セカンドソースのライセンスをしなかったのかもしれない。
ということで、とりあえず若松で見つけた、D8031(uPD8031ぢゃなよ),P80C31,MB8051を買って8031のSBCを作ってみた。
<
- 構成
エレクトロニクスライフの記事を見るとRAMは6264(8Kbyte)のSRAMを使っている。当時6116(2Kbyte)は安くなっていて、6246がお手頃価格になっていたのかな。今時は62256(32K)が\500以下だ。
ここは、贅沢に32KをRAMにしてモニタをROMに書くことにしよう。モニタはどんなに大きくても2Kだ32KSRAM空間は当時とっても贅沢だった。尤も当時プログラムはアセンブラで書いていたので32Kメモリがあっても使えなかった。 しかし今は高級言語で書けるのでRAMは大きい方が良い。
EZ-USBで遊んでいたときにはSDCCでF/Wを書いていのだが、EZ-USBの2Kのプログラム空間はかなり狭くてフルサイズのprintf()が使えないのでprintf_tiny()に手を入れて使っていた経験があるのでRAMは大きい方が良い。
今回の用途は特定用途ではないのでROM化はしないで、プログラムはRAM上で動かして、I/Oは後から拡張できる構成にしよう。
8031 + RAM(62256) + ROM(MON) の構成だと簡単に作れそうだ.。回路図はこんな感じ
サンハヤトのICB-293Uに載りそうである。 - 実装
ということで作ってみた。
実装はこんな感じ - モニタ
プログラムはCで書いて,RAMにロードしで動かすことにしよう。 昔は、高級言語といえばBASICくらいしかなかったので、Cで書けるのは夢のようだ。
EZ-USBで遊んだ時にもSDCCを使っていたのだが、EZ-USBはUSB経由でプログラムをダウンロードできる仕掛けがあるのでプログラムのロードは非常に楽である。しかし、8031SBCでは、プログラムをRAMにロードできる機能を持ったモニタをROMに焼いておかなければならない。
モニタに必要な機能は- プログラムロード
- メモリダンプ
- メモリ書き込み
とりあえずこれくらいあれば使えるだろう.。
モニタを自前で書く場合、8051のアセンブラは書いたことがないのでCで書くとになるけど、問題はどうやってモニタをデバッグするかである。
EPROMはROMライタを買ったので焼けるけどROMイレーサがない。EPROMを使った試行錯誤はとっても大変なので、ここは巨人の肩に乗ることにしよう。
ネットで探して見つけた8031のモニタではPaulmon2 が良さそうである。
- プログラム
さすがにアセンブラでゴリゴリ書くのは大変なので、SDCCを使う。以前EZ-USBで遊んでいたときに使ったことがあるし。
- まずは Hello World
/*
* hello world
* Sep.2011 / Yoshi
*/
#include <stdio.h>
#include <mcs51>
#include "io.h"
#define printf printf_fast
void main(void) {
printf("Hello world!\n");
exit(0);
}
io.c
#include <stdio.h>
#include <mcs51/8051.h>
#include "paulmon2.h"
#include "io.h"
char getchar(void) {
return pm2_cin();
}
void putchar(char c) {
if (c=='\n') pm2_cout('\r');
pm2_cout(c);
}
void exit(char c) __naked {
c;
__asm
ljmp 0x0000
__endasm;
}pm2_cin(),pm2_cout()はPaulmon2への入出力サブルーチンへのインタフェースが記述されていてpaulmon2.cで定義されている。
paulmon2.c, paulmon2.h は親切な人が公開しているのでぐぐると見つかる - コンパイル
#sdcc -mmcs51 --model-small --code-loc 0x8000 hello.c io.rel paulmon2.rel
- 実行結果
- 九九の表
printf_fast() は %3d のように出力幅を指定できるが %02x のように0で埋めることができない. printf_small() はその逆らしい。「帯に短したすきに長し」だ。
EZ-USBで遊んでいたころにsdccを使っていた. EZ-USBはCODE領域が2kしかないので,printf_tiny()しか使えずソースコードに手を入れて %02x がちゃんと出力されるようにして使っていた.
8031SBCはcode領域が32kとれるのでとっても楽である。 - 8Queen問題
8Queen問題のプログラムを書いてみた。8Queen問題といえば再帰プログラムということで再帰で書いたのだが8051/31は内臓RAMが少ないので関数のネストが深くなる再帰プログラムは厳しい。
DATAとSTACKをXRAMに移してもスタックが足りないようだ。よく見てみると、使った覚えのないiDataやBitsが使われている.(20h-2Dh)調べたところ、ライブラリで使われているようで、printf_small()をprintf()に変えると95byte使えるようになった。
フルサイズのprintf()を使っているので、できたバイナリはかなり大きいが、CODEにSTACKは代えられない。--- 8queen.mem --
Internal RAM layout:
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|a|b|c|c|c|c|c|c|
0x10:|c|c|d|e|e| | | | | | | | | | | |
0x20:|B|T|I|I|I|I|I|I|I|I|I|I|I|I|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:| | | | | | | | | | | | | | | | |
0x90:| | | | | | | | | | | | | | | | |
0xa0:| | | | | | | | | | | | | | | | |
0xb0:| | | | | | | | | | | | | | | | |
0xc0:| | | | | | | | | | | | | | | | |
0xd0:| | | | | | | | | | | | | | | | |
0xe0:| | | | | | | | | | | | | | | | |
0xf0:| | | | | | | | | | | | | | | | |
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute
ようやく通った。
-- 8queen.c ---
Internal RAM layout:
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|a|b|b|c|d|e|e| |
0x10:| | | | | | | | | | | | | | | | |
0x20:|T|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:| | | | | | | | | | | | | | | | |
0x90:| | | | | | | | | | | | | | | | |
0xa0:| | | | | | | | | | | | | | | | |
0xb0:| | | | | | | | | | | | | | | | |
0xc0:| | | | | | | | | | | | | | | | |
0xd0:| | | | | | | | | | | | | | | | |
0xe0:| | | | | | | | | | | | | | | | |
0xf0:| | | | | | | | | | | | | | | | |
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute
- 再帰関数
再帰呼び出しする関数は次のとおりである。
char place(char x, char y) __reentrant {
static char n = 1;
if (check(x,y)) {
table[y] = x;
if (y==7) {
printtb(n++);
return FALSE;
} else {
if (place(0, y+1)) {
return TRUE;
} else {
table[y] = 0xff;
if (x==7)
return FALSE;
else return place(x+1,y);
}
}
} else {
if (x==7)
return FALSE;
else return place(x+1, y);
}
}
ほんとうはこう↓したいのだが、 再帰レベルが増えるのでスタックが足りなくなる。
char place(char x, char y) __reentrant {
static char n = 1;
if (7<=y) { /* 解 */
printtb(n++);
return FALSE;
}
if (7<=x) { /* 見つからない */
return FALSE;
}
if (check(x,y)) {
table[y] = x;
place(0, y+1);
table[y] = (u_char)-1;
}
return place(x+1, y);
}
- まずは Hello World
- P8052AH-BASIC
P8052AH-BASIC をとうとう見つけた.
マルツの特価半導体を眺めていたら特価現品限りで8052AHが出ていた。web上の画像はマーキングが消してあったので、P8052AH-BASICかどうかは判らなかったけど、まあ1つ買ってみるかと注文したらP8052AH-BASICだった。じゃあ、8052AH-BASIC SBCを作ってみるかと...
-- 2012/4/16 --作った
-- 2012/11/12 --
コメント