A2PDP11 (17) ペーパーテープの読み込み
コンソール ODT を使って、ペーパーテープを読み込んでみます。
Absolute Loader Format
Absolute Loader のフォーマットは、下記に示す 6 バイトのヘッダーを持つブロックで構成されます。
001 スタートフレーム
000 NULL フレーム
xxx データバイト数(下位)
xxx データバイト数(上位)
yyy ロードアドレス(下位)
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