From b54cce7a9804fa4087cabcd48195f33a683848ef Mon Sep 17 00:00:00 2001 From: Morten Jagd Christensen Date: Sat, 16 Nov 2024 11:41:22 +0100 Subject: [PATCH] better parsing of disk data in filesys.info, extracted a PL/1 program, documentation --- docs/source/index.rst | 7 ++ docs/source/pl1.rst | 171 +++++++++++++++++++++++++++++++++ src/disks/datamuseum/image.py | 2 + src/disks/fluxsamples/image.py | 6 +- src/emulator.py | 2 +- src/filesys.py | 87 ++++++++++++++--- 6 files changed, 259 insertions(+), 16 deletions(-) create mode 100644 docs/source/pl1.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 8fe2fbe..0391989 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,6 +35,13 @@ from ROMs. filesys + .. toctree:: + :caption: PL/1 + :maxdepth: 3 + :hidden: + + pl1 + .. toctree:: :caption: Emulator :maxdepth: 3 diff --git a/docs/source/pl1.rst b/docs/source/pl1.rst new file mode 100644 index 0000000..b6cf13b --- /dev/null +++ b/docs/source/pl1.rst @@ -0,0 +1,171 @@ + + + +The (partial) Q1 disk images available to us seem to be related to accounting. + + + +.. code-block:: text + + DCL VER FIXED (8) INIT (84020611); + /* PROGRAM-ID. V10RGENA. + DATE-WRITTEN. 1984-02-06. + AUTHOR. OLLE. + UPP EN TABELL I X-LED MED 10 ST VALFRIA KONTON + I Y-LED L[GGER DEN SJ[LV UPP TILL 39 ST IK- + SLAG SOM DEN FINNER KONTERADE F\R ANGIVNA + KONTON. POSTENS BELOPP ADDERAS TILL PASSANDE + TABELLEN. KONTON L[GGES IN MED FORM XKTOFORM + XKTOFIL. PARAMETERN F\R KONTON SKALL TILL- + DELAS ETT NAMN P] TRE TECKEN. + 840206. OBg. */ + DCL RKONTO1 CHAR (3) INIT ('---'), + RKONTO2 CHAR (3) INIT ('---'), + DATUM2 CHAR(6), + DAT1BIN BINARY, + DAT2BIN BINARY, + SPTYP BINARY, /* 1 = PERDAG 2 = BOKDAG */ + STAB(11) BINARY; + + DCL 1 TSTR, + 2 VERNRT FIXED(5), + 2 TEXT CHAR(13), + 2 ANTVERP BINARY; + DCL PK POINTER, + 1 BSTR BASED(PK), + 2 KONTONR CHAR(11); + + DCL PB POINTER, + 1 BELSTR BASED(PB), + 2 VERNR BINARY, + 2 PDATUM BINARY, + 2 BELOPP FIXED(11); + DCL 1 AREA, + 2 CH(255) CHAR(61); + /* 15 555 BYTES */ + + + + DCL 1 IKPOST, + DCL 1 IKPOST, + 2 SDATUM3 FIXED (6), + 2 IKTEXT CHAR (32), + 2 UHMARK CHAR (1); + + DCL 1 XKTOPOST, + 2 XOK CHAR (3), + 2 XKONTO FIXED (5), + 2 XTEXT1 CHAR (14), + 2 XTEXT2 CHAR (14), + 2 XTEXT3 CHAR (14), + 2 XTEXT4 CHAR (14), + 2 XTEXT5 CHAR (14); + + DCL 1 YKTOPOST, + 2 YKTO FIXED (3), + 2 YTEXT CHAR (12); + + DCL 1 RYTEXT (40), + 2 YRADTEXT CHAR (12), + 2 YRADKTO FIXED (3); + + DCL IK POINTER, + 1 RYTEXTREDEF BASED (IK), + 2 YRADTEXTREDEF CHAR (12), + 2 YRADKTOREDEF FIXED (3); + DCL XKTO (10) CHAR (5), + IKSLAG CHAR (3), + SOK CHAR (3); + + + DCL VERTEXT FILE, + VERBELOP FILE, + KSKONTOP FILE, + XKTOFIL FILE, + YKTOFIL FILE; + + +And the first (of seven tracks) of the file **V10RGENA** from the 'fluxsample' disk. + +.. code-block:: text + + DCL BSTRL BINARY INIT(61), + MAX BINARY, + BLOCKANT BINARY, + JK BINARY, + JJ BINARY, + LISTTYP BINARY, /* 1 = KONTOUTDRAG, 2 = SALDOBESKED */ + RCODE BINARY, + KONTOG CHAR(11), + SIDA BINARY INIT(1), + R BINARY INIT(0), + NAMN(2) CHAR(10) INIT('KVV/KWAROS','AROSKRAFT'), + T15BELRED CHAR (15), + KONSTANT BINARY, + BUFF CHAR(20), + T1 CHAR(1), + T61 CHAR(6), + T62 CHAR(6), + T11 CHAR(11), + T13 CHAR(13), + T14 CHAR(14), + T15 CHAR (15), + RUBTEXT CHAR (70), + LASNYCKEL CHAR (1) INIT ('0'), + SWFORSTA CHAR (1) INIT ('J'), + KT8 CHAR(8), + FX6 FIXED(6), + TYP1 BINARY INIT(0), + TYP2 BINARY INIT(0), + OFFSET BINARY INIT(1), + LENGD BINARY INIT(0), + SUMMA (39,10) FIXED (11) INIT ((39)0), + SSUMMA FIXED (11,2), + XL BINARY, /* KOORDINAT I X-LED SUMMATAB */ + YL BINARY, /* KOORDINAT I Y-LED SUMMATAB */ + XANT BINARY, + UANT BINARY, + ??????????????????????????????????????????????????????????????????????????????? + KIND BINARY, + OKEY CHAR (1), + P POINTER, + D CHAR(6) BASED(P), + DATUM CHAR(6), + PP POINTER, + 1 STR BASED(PP), + 2 X CHAR(2), + 2 Y CHAR(2), /* 6 = UKTO, 7 = IKSLAG */ + 2 FIRMA CHAR (1), + 2 OP_KOD BINARY, + 2 RADANT BINARY, + T4 CHAR(4), + ANTAL_KONT BINARY INIT(0), + TOT_ANTAL_KONT BINARY INIT(0), + VERSION CHAR(47) INIT(' TR10KOLJA Version 1.1 830603'); + + + + RUB:PROC; + RUB10: + IF FIRMA = '8' THEN DO; + ??????????????????????????????????????????????????????????????????????????????? + END; + IF FIRMA = '9' THEN DO; + PUT SKIP (2) EDIT ('AROS') (A(40)); + END; + ??????????????????????????????????????????????????????????????????????????????? + (A(8)) (DATUM) (A(6)); + DO J = 1 TO 5; + IF J = 2 THEN DO; + PUT SKIP EDIT ('PARA:') (A) (SUBSTR(SOK,1,2)) (A(4)); + ??????????????????????????????????????????????????????????????????????????????? + IF SUBSTR (SOK,1,1) = 'O' THEN PUT EDIT ('OLJA') (A(4)); + PUT EDIT (' ') (A(6)); + GO TO RUB20; + END; + IF J = 3 THEN DO; + PUT SKIP EDIT ('BEST-NR: ') (A) (RKONTO1) (A(3)) ('-') (A) + (RKONTO2 - '001') (P'999') (' ') (A(3)); + GO TO RUB20; + END; + IF J = 4 THEN DO; diff --git a/src/disks/datamuseum/image.py b/src/disks/datamuseum/image.py index ca98b1f..e00a440 100644 --- a/src/disks/datamuseum/image.py +++ b/src/disks/datamuseum/image.py @@ -16,3 +16,5 @@ track.info(8, fs.data[8], 19, 255) track.info(9, fs.data[9], 19, 255) track.info(70, fs.data[70], 126, 20) + track.info(71, fs.data[71], 126, 20) + track.info(73, fs.data[72], 126, 20) diff --git a/src/disks/fluxsamples/image.py b/src/disks/fluxsamples/image.py index 1ae5a09..1a2c9fe 100644 --- a/src/disks/fluxsamples/image.py +++ b/src/disks/fluxsamples/image.py @@ -5,12 +5,14 @@ from disks.fluxsamples import t0, t1, t2 import filesys +tracks = [t0.data, t1.data, t2.data] fs = filesys.FileSys() -fs.loadtracks([t0.data, t1.data, t2.data]) # +fs.loadtracks(tracks) # if __name__ == '__main__': track = filesys.Track() - track.info(1, fs.data[1], 82, 79) + track.info(0, fs.data[0], 16, 40) + track.info(1, fs.data[1], 77, 79) track.info(2, fs.data[2], 82, 79) diff --git a/src/emulator.py b/src/emulator.py index 91a9744..bbdc249 100644 --- a/src/emulator.py +++ b/src/emulator.py @@ -91,7 +91,7 @@ def kbd_input(self): elif ch == 8224: # opt-t args.decode = not args.decode elif ch == 170: # opt-a misc debug FDs, floppy dump - self.io.floppy.disk.drives[1].dump(0) + self.io.floppy.disk.drives[2].dump(0) # self.ros.index() # self.ros.file() # self.ros.disk() diff --git a/src/filesys.py b/src/filesys.py index 0427f75..b2b9810 100644 --- a/src/filesys.py +++ b/src/filesys.py @@ -1,14 +1,19 @@ class Track: + def __init__(self): + self.overhead = 12 # besides record size: markers, ckecksum 0x10, etc. + self.offset_0x9e = 7 + + # Handle INDEX track which has fixed size def index(self, data, records, record_size): + assert record_size == 40 d = data for record in range(records): - overhead = 8 i = 0 - offset = record * (record_size + overhead) + offset = record * (record_size + self.overhead) assert d[offset + i] == 0x9e, f'{i=}, {d[offset + i]=}' - i += 5 + i += self.offset_0x9e assert d[offset + i] == 0x9b i += 1 @@ -20,23 +25,22 @@ def index(self, data, records, record_size): disk = d[offset + i + 15] ftrack = d[offset + i + 16] + (d[offset + i + 17] << 8) ltrack = d[offset + i + 18] + (d[offset + i + 19] << 8) - assert recno == 0 + #assert recno == 0 if nrecs: - print(f'{filename}: nrecs {nrecs:2}, record size {recsz:3}, recs/trk: {rpt:3}, disk {disk}, first track {ftrack:2}, last track {ltrack:2}') - - - def info(self, track, data, records, record_size): - if track == 0: - self.index(data, records, record_size) + print(f'{filename}: recno {recno}, nrecs {nrecs:2}, record size {recsz:3}, recs/trk: {rpt:3}, disk {disk}, first track {ftrack:2}, last track {ltrack:2}') + # Handle case where segments are loaded according to ROS manual + # separator, address, number of bytes, .... + def loadable(self, track, data, records, record_size): + assert record_size == 255 d = data for record in range(records): - overhead = 8 + print(f'record {record}') firstline = False i = 0 - offset = record * (record_size + overhead) + offset = record * (record_size + self.overhead) assert d[offset + i] == 0x9e, f'{i=}, {d[offset + i]=}' - i += 5 + i += self.offset_0x9e assert d[offset + i] == 0x9b i += 1 while 255 - i >= 5: @@ -78,6 +82,63 @@ def info(self, track, data, records, record_size): print() + def program(self, track, data, records, record_size): + d = data + for record in range(records): + i = 0 + offset = record * (record_size + self.overhead) + assert d[offset + i] == 0x9e, f'{i=}, {d[offset + i]=}' + i += self.offset_0x9e + assert d[offset + i] == 0x9b + i += 1 + + ch = d[offset + i: offset + i + record_size] + print(''.join(list(map(chr, ch)))) + + + def rawdata(self, track, data, records, record_size): + d = data + for record in range(records): + i = 0 + offset = record * (record_size + self.overhead) + assert d[offset + i] == 0x9e, f'{i=}, {d[offset + i]=}' + i += self.offset_0x9e + assert d[offset + i] == 0x9b + i += 1 + + ch = d[offset + i: offset + i + record_size] + #print(''.join(list(map(chr, ch)))) + cha = [ chr(x) if 32 <= x < 127 else '.' for x in ch] + print(f'{record:03}', ''.join(cha)) + + # this output can be filtered for more condensed output: + # python3 image.py | grep -v '\.\.\.\.\.\.\.' | grep -v ' ' + + # INDEX track handled separately + # Currently only works for loadable tracks (record size 255) + def info(self, track, data, records, record_size): + print(f'\nTrack information for track {track}\n') + if track == 0: + self.index(data, records, record_size) + return + + if record_size == 255: + self.loadable(track, data, records, record_size) + return + + if record_size == 79: + self.program(track, data, records, record_size) + return + + if record_size == 20: + self.rawdata(track, data, records, record_size) + return + + print('unsupported record type') + + + + class FileSys: # Possibilities according to "Q1 Lite system overview"