simh (PDP-11) のロードファイルをa.outファイルから作成する


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!)](http://ancientbits.blogspot.jp/2012/07/programming-barebones-pdp11.html)

####PDP-11(simh)のロードファイルフォーマット
この紙テープ時代のファイルフォーマットは比較的シンプルで、下記のようなブロックを順に並べたものだ。

#####ブロックのフォーマット
|サイズ(バイト)|データ|
|:--:|:--|
|6|ブロックのヘッダー情報|
|可変|ブロックのデータ|
|1|ヘッダー含めたチェックサム|

#####ブロック内ヘッダ情報
|サイズ(バイト)|データ|
|:--:|:--|
|1|固定値: 1|
|1|固定値: 0|
|2|ブロックのサイズ (リトルエンディアンで格納)|
|2|データがロードされるアドレス (リトルエンディアンで格納)|

#####ファイルのフォーマット
||
|:--:|
|ブロック1|
|ブロック2|
|...|
|最後のブロック|

最後のブロックは空のデータを入れて、ヘッダー情報のロードアドレスにエントリーポイントのアドレスを収める。プログラムがロードされてメモリに読み込まれた後、このアドレスから実行される。

a.out形式も単純で、ファイルのヘッダーの後に、textセクション、dataセクション・・・と続く。
今回の変換をするためには、それぞれtextセクションのブロッック、dataセクションのブロックを作ってやれば良い。

で、件のbin2loadと同じものを作ってみた。
エラー処理を全部ハショって、処理の筋を見えやすくした。ソースは下記のとおり。

※注意１：C99で書いたのでコンパイルオプションに-std=c99が必要）
※注意２：リトルエンディアンマシンでしか動かない

```c:aout2simhbin.c
#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);
}
```
```mk:Makefile
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!)](http://ancientbits.blogspot.jp/2012/07/programming-barebones-pdp11.html)　
上記記事内だと、bin2loadのソースの場所は分かりにくいが、GitHub（ https://github.com/jguillaumes/retroutils）にある。