見出し画像

A2PDP11 (17) ペーパーテープの読み込み

コンソール ODT を使って、ペーパーテープを読み込んでみます。

Absolute Loader Format

Absolute Loader のフォーマットは、下記に示す 6 バイトのヘッダーを持つブロックで構成されます。

  1.  001 スタートフレーム

  2. 000 NULL フレーム

  3. xxx データバイト数(下位)

  4. xxx データバイト数(上位)

  5. yyy ロードアドレス(下位)

  6. yyy ロードアドレス(上位)

ヘッダーに続き、データバイト数分のバイナリデータが続きます。
データバイト数が 6 の場合、ロードアドレスはプログラムの開始アドレスを示します。
バイナリデータの後、1 バイトのチェックサムがつきます。
チェックサムは、6 バイトのヘッダとバイナリーデータの和の下位バイトをビット反転したものです。したがって、チェックサムを足すと 0 になります。

Apple II 側ソフトウェア

コンソール ODT のソフトウェアを修正し、Absolute Loader Format のファイルを読み出し、ODT のコマンドに変換してメモリに書き込むようにします。

Control-R で、LoadTape() 関数を実行します。
A.PTAP ファイルを開きます。
SkipHeader() 関数でヘッダの前の 0 を読み飛ばします。
GetWord() 関数で、データバイト数を読み出します。
GetWord() 関数で、ロードアドレスを読み出します。
データバイト数が、6 より大きければ、バイナリデータの下位バイトを読み出します。データバイト数が奇数の場合、最後のデータは下位ビットだけになります。
それ以外の時は、上位バイトを読み出し、下位ビットと合わせてワードデータに合成します。
PutAddress() 関数で、ロードアドレスを 6 桁の 8 進数に変換し、末尾に / をつけて、ODT に入力します。ODT から 7 バイト (6 桁のデータとスペース) を受け取ります。
PutData() 関数で、ワードデータを 6 桁の 8 進数に変換し、末尾に CR をつけて、ODT に入力します。ODT から 2 バイト (CR と LF) を受け取ります。

/*
 * main.c
 * a2tape
 *
 * Created by ushicow on 2024/11/12.
 * Copyright (c) 2024 nanja.info All rights reserved.
 *
 */

#include <stdio.h>
#include <conio.h>
#include <peekpoke.h>
#include <stdint.h>

#define SLOT    7
#define RCSR    (0xc080 + SLOT * 0x10)
#define RBUF    (0xc081 + SLOT * 0x10)
#define XCSR    (0xc082 + SLOT * 0x10)
#define XBUF    (0xc083 + SLOT * 0x10)
#define ERROR   (0xc08f + SLOT * 0x10)

int PutChar(uint8_t wdata) {
    uint8_t rdata;
    
    POKE(RCSR, 128);            // rrdy <= 1
    while (PEEK(RCSR) < 128);   // rstb == 1
    POKE(RBUF, wdata);
    POKE(RCSR, 0);              // rrdy <= 0
    POKE(XCSR, 128);            // xrdy <= 1
    while (PEEK(XCSR) < 128);   // xstb == 1
    POKE(XCSR, 0);              // xrdy <= 0
    rdata = PEEK(XBUF);
    if (rdata != wdata) {
        POKE(ERROR, rdata);
        printf("\n%x=%x\n", rdata, wdata);
    }
//    putchar(rdata);
    return (rdata != wdata);
}

int SkipHeader(FILE *tape)
{
    int c;
    
    while ((c = fgetc(tape)) == 0);
    if (c != 1) {   // Start Marker = 1
        puts("Start Marker not found");
        return (-1);
    }
    c = fgetc(tape);
    if (c != 0) {   // Pad = 0
        puts("Illigal Start Maker");
        return (-1);
    }
    return 0;
}

int GetWord(FILE *tape) {
    int c;
    int l;

    c = fgetc(tape);
    if (c == EOF) {
        return (-1);
    } else {
        l = c;
    }
    c = fgetc(tape);
    if (c == EOF) {
        return (-1);
    } else {
        l = l + c * 0x100;
    }
    return l;
}

int PutAddress(uint16_t addr)  {
    int i;
    int rdata;
    char addr_str[] = "000000";
    
    sprintf(addr_str, "%06lo", (long)addr);
    for (i = 0; i < 6; ++i) {
        if (PutChar(addr_str[i])) {
            return (-1);
        };
    }
    PutChar('/');
    for (i = 0; i < 7; ++i) {
        POKE(XCSR, 128);            // xrdy <= 1
        while (PEEK(XCSR) < 128);   // xstb == 1
        POKE(XCSR, 0);              // xrdy <= 0
        rdata = PEEK(XBUF);
//        putchar(rdata);
    }
    return 0;
}

int PutData(uint16_t data)  {
    int i;
    int rdata;
    char data_str[] = "000000";
    
    sprintf(data_str, "%06lo", (long)data);
    for (i = 0; i < 6; ++i) {
        if (PutChar(data_str[i])) {
            return (-1);
        };
    }
    PutChar(0x0d);   // CR
    for (i = 0; i < 2; ++i) {
        POKE(XCSR, 128);            // xrdy <= 1
        while (PEEK(XCSR) < 128);   // xstb == 1
        POKE(XCSR, 0);              // xrdy <= 0
        rdata = PEEK(XBUF);
//        putchar(rdata);
    }
    return 0;
}


int LoadTape(void)
{
    unsigned char rdata;
    unsigned char wdata = 0;
    FILE *tape;
    int length;
    int address;
    int c;
    uint16_t data;
    uint8_t sum;
    
    printf("Load Tape\n");
    tape = fopen("A.PTAP", "rb");
    if (tape == 0) {
        puts("File Not Found");
        return 1;
    }
    while (1) {
        if (SkipHeader(tape)) {
            break;
        }
        length = GetWord(tape);
        sum = 1 + length / 0x100 + length % 0x100;
        address = GetWord(tape);
        if (address < 0) {
            break;
        }
        sum += address / 0x100 + address % 0x100;
        printf("A:%06o ", address);
        if ((address % 2) == 1) {
            puts("Odd Address");
            break;
        }
        printf("L:%06o \n", length);
        if (length < 6) {
            break;
        } else if ((length % 2) == 1) {
            puts("Odd length");
        } else if (length == 6) {
            puts("Start");
            break;
        }
        while (length > 6) {
            c = fgetc(tape);
            if (c == EOF) {
                break;
            }
            data = c;
            sum += c;
            --length;
            if (length > 6) {
                c = fgetc(tape);
                if (c == EOF) {
                    break;
                }
                data += c * 0x100;
                sum += c;
                --length;
            }
            if (PutAddress(address)) {
                break;
            }
            if (PutData(data)) {
                break;
            }
            address += 2;
        }
        c = fgetc(tape);
        if (c == EOF) {
            break;
        }
        sum += c;
        if (sum) {
            printf("X:%x ", c);
            printf("S:%x ", sum);
            puts("Check sum error");
            break;
        }
    }
    fclose(tape);
    return 0;
}

int main(void)
{
    uint8_t rdata;
    uint8_t wdata = 0;
    char dummy;
    
    printf("Serial Start\n");
    cursor(1);
    POKE(XCSR, 0);                      // xrdy <= 0
    while (1) {
        if (PEEK(XCSR) >= 128) {        // xstb == 1
            POKE(XCSR, 0);              // xrdy <= 0
            rdata = PEEK(XBUF);
            putchar(rdata);
        } else if (PEEK(RCSR) >= 128) { // rstb == 1
            POKE(RBUF, wdata);
            POKE(RCSR, 0);              // rrdy <= 0
        } else if (kbhit()) {
            wdata = cgetc();
            if (wdata == 24) {          // control-X
                return(0);
            } else if (wdata == 18) {   // control-R
                LoadTape();
            }
            POKE(RCSR, 128);            // rrdy <= 1
        } else {
            POKE(XCSR, 128);            // xrdy <= 1
        }
    }
    return 0;
}

BASIC の実行

DEC-11-AJPB-PB.ptap を A.PTAP という名前に変更し、ProDOS ディスクに入れます。
Apple II の A2TAPE プログラムを実行し、Control-R を入力すると、A.PTAP を読み出し、メモリに書き込みます。すべてのデータを読み出すのに、3 分弱かかります。
16104G で BASIC を起動します。

参考文献

PDP-11 Paper Tape Software Programing Handbook, 6.2.3 Absolute Loader Operation, 1973

VaxHaven Paper Tape Archive


いいなと思ったら応援しよう!