PDP-11のエミュレータsimhでは、紙テープ時代のファイルフォーマットをロードして、メモリ上に展開する。一方で、pdp11-aout-gccの出力のa.out形式は、そのままでは使えないので変換してやる必要がある。
下記ブログにその説明が載っており、変換ツールにbin2loadというものを公開している。
The Ancient Bits adventure: Writing PDP11 assembly code from Linux (and running it on bare -simulated- metal!)
####PDP-11(simh)のロードファイルフォーマット
この紙テープ時代のファイルフォーマットは比較的シンプルで、下記のようなブロックを順に並べたものだ。
#####ブロックのフォーマット
サイズ(バイト) | データ |
---|---|
6 | ブロックのヘッダー情報 |
可変 | ブロックのデータ |
1 | ヘッダー含めたチェックサム |
#####ブロック内ヘッダ情報
サイズ(バイト) | データ |
---|---|
1 | 固定値: 1 |
1 | 固定値: 0 |
2 | ブロックのサイズ (リトルエンディアンで格納) |
2 | データがロードされるアドレス (リトルエンディアンで格納) |
#####ファイルのフォーマット
ブロック1 |
ブロック2 |
... |
最後のブロック |
最後のブロックは空のデータを入れて、ヘッダー情報のロードアドレスにエントリーポイントのアドレスを収める。プログラムがロードされてメモリに読み込まれた後、このアドレスから実行される。
a.out形式も単純で、ファイルのヘッダーの後に、textセクション、dataセクション・・・と続く。
今回の変換をするためには、それぞれtextセクションのブロッック、dataセクションのブロックを作ってやれば良い。
で、件のbin2loadと同じものを作ってみた。
エラー処理を全部ハショって、処理の筋を見えやすくした。ソースは下記のとおり。
※注意1:C99で書いたのでコンパイルオプションに-std=c99が必要)
※注意2:リトルエンディアンマシンでしか動かない
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint16_t magic;
uint16_t text;
uint16_t data;
uint16_t bss;
uint16_t syms;
uint16_t entry;
uint16_t trsize;
uint16_t drsize;
} AoutHeader_t;
void
read_aout_data(char *buf, int size, FILE *f)
{
if (size <= 0)
return;
fread(buf, size, 1, f);
}
int
chksum(void *dat, int size)
{
int sum = 0;
unsigned char *p = dat;
while (p < (unsigned char*)(dat+size))
sum += *p++;
return sum;
}
int
write_and_sum(void *dat, int size, FILE *f)
{
if (dat == 0 || size == 0)
return 0;
fwrite(dat, size, 1, f);
return chksum(dat, size);
}
int
write_blk_dat(const char *dat, int dat_size, uint16_t load_adr, FILE *f)
{
struct {
char d1;
char d2;
uint16_t block_size;
uint16_t load_adr;
} block_header = {
1, 0,
.block_size = dat_size+sizeof(block_header),
.load_adr = load_adr
};
int sum = 0;
sum += write_and_sum((void*)&block_header, sizeof(block_header), f);
sum += write_and_sum((void*)dat, dat_size, f);
char csum = 0-(char)(sum & 0xff);
fwrite(&csum, 1, 1, f);
return (sizeof(block_header) + dat_size + 1);
}
int
main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "usage: %s <a.out file> <target file>\n", argv[0]);
return 1;
}
const char *fn_aout = argv[1],
*fn_save = argv[2];
AoutHeader_t h_aout;
FILE *f = fopen(fn_aout, "r");
fread(&h_aout, sizeof(h_aout), 1, f); /* only little endian is OK */
char text_buf[h_aout.text],
data_buf[h_aout.data];
read_aout_data(text_buf, sizeof(text_buf), f);
read_aout_data(data_buf, sizeof(data_buf), f);
fclose(f);
FILE *tf = fopen(fn_save, "w+");
write_blk_dat(text_buf,
sizeof(text_buf),
h_aout.entry,
tf);
write_blk_dat(data_buf,
sizeof(data_buf),
h_aout.entry + sizeof(text_buf),
tf);
/* last block only has entry point address */
write_blk_dat(0, 0, h_aout.entry, tf);
fclose(tf);
}
TARGET = aout2simhbin
OBJS = main.o
CC = gcc
CFLAGS = -std=c99
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET) $(CFLAGS) $(LFLAGS)
.c.o: $<
$(CC) -c $(CFLAGS) $<
clean:
rm -f $(OBJS) $(TARGET)
参考文献
(1) a.outのファイルフォーマット
http://caspar.hazymoon.jp/OpenBSD/annex/a.out.html
(2) simhのロードファイルファーマットが書かれている
The Ancient Bits adventure: Writing PDP11 assembly code from Linux (and running it on bare -simulated- metal!)
上記記事内だと、bin2loadのソースの場所は分かりにくいが、GitHub( https://github.com/jguillaumes/retroutils)にある。