LoginSignup
2
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-11-22

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:リトルエンディアンマシンでしか動かない

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);
}
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!) 
上記記事内だと、bin2loadのソースの場所は分かりにくいが、GitHub( https://github.com/jguillaumes/retroutils)にある。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2