From 633160bbd0c7c256c02daed6862b085e5c5ce280 Mon Sep 17 00:00:00 2001 From: "Michael R. Cook" Date: Thu, 15 Aug 2024 20:09:47 +0200 Subject: [PATCH] tap: handle unoffical block types and minor improvements To make it easier to parse the JSON output the header/data blocks are now wrapped in a FieldStruct, and data fields changed to be an array of uint8 values --- format/tap/tap.go | 56 ++++++++++++++++++++------ format/tap/testdata/basic_prog1.fqtest | 42 +++++++++++++++++-- 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/format/tap/tap.go b/format/tap/tap.go index b1e0c18f..ed7a9902 100644 --- a/format/tap/tap.go +++ b/format/tap/tap.go @@ -6,6 +6,7 @@ import ( "bufio" "bytes" "embed" + "fmt" "golang.org/x/text/encoding/charmap" @@ -54,9 +55,13 @@ func decodeTapBlock(d *decode.D) { // read header, fragment, or data block switch length { case 0: - // fragment with no data + d.Fatalf("TAP fragments with 0 bytes are not supported") case 1: - d.FieldRawLen("data", 8) + d.FieldStruct("data", func(d *decode.D) { + d.FieldArray("bytes", func(d *decode.D) { + d.FieldU8("byte") + }) + }) case 19: d.FieldStruct("header", func(d *decode.D) { decodeHeader(d) @@ -72,15 +77,34 @@ func decodeTapBlock(d *decode.D) { func decodeHeader(d *decode.D) { blockStartPosition := d.Pos() - // Always 0: byte indicating a standard ROM loading header - d.FieldU8("flag", scalar.UintMapSymStr{0: "standard_speed_data"}) + // flag indicating the type of header block, usually 0 (standard speed data) + d.FieldU8("flag", scalar.UintFn(func(s scalar.Uint) (scalar.Uint, error) { + if s.Actual == 0x00 { + s.Sym = "standard_speed_data" + } else { + s.Sym = "custom_data_block" + } + return s, nil + })) + // Header type - dataType := d.FieldU8("data_type", scalar.UintMapSymStr{ - 0x00: "program", - 0x01: "numeric", - 0x02: "alphanumeric", - 0x03: "data", - }) + dataType := d.FieldU8("data_type", scalar.UintFn(func(s scalar.Uint) (scalar.Uint, error) { + switch s.Actual { + case 0x00: + s.Sym = "program" + case 0x01: + s.Sym = "numeric" + case 0x02: + s.Sym = "alphanumeric" + case 0x03: + s.Sym = "data" + default: + // unofficial header types + s.Sym = fmt.Sprintf("unknown%02X", s.Actual) + } + return s, nil + })) + // Loading name of the program. Filled with spaces (0x20) to 10 characters. d.FieldStr("program_name", 10, charmap.ISO8859_1) @@ -120,7 +144,10 @@ func decodeHeader(d *decode.D) { // UnusedWord: 32768. d.FieldU16("unused") default: - d.Fatalf("invalid TAP header type, got: %d", dataType) + // Unofficial header types + d.FieldU16("data_length") + d.FieldU16("unknown1", scalar.UintHex) + d.FieldU16("unknown2", scalar.UintHex) } // Simply all bytes XORed (including flag byte). @@ -140,7 +167,12 @@ func decodeDataBlock(d *decode.D, length uint64) { return s, nil })) // The essential data: length minus the flag/checksum bytes (may be empty) - d.FieldRawLen("data", int64(length-2)*8) + d.FieldArray("bytes", func(d *decode.D) { + for i := uint64(0); i < length-2; i++ { + d.FieldU8("byte") + } + }) + // Simply all bytes (including flag byte) XORed d.FieldU8("checksum", d.UintValidate(calculateChecksum(d, blockStartPosition, d.Pos()-blockStartPosition)), scalar.UintHex) } diff --git a/format/tap/testdata/basic_prog1.fqtest b/format/tap/testdata/basic_prog1.fqtest index 19b51e22..cc54d682 100644 --- a/format/tap/testdata/basic_prog1.fqtest +++ b/format/tap/testdata/basic_prog1.fqtest @@ -15,7 +15,43 @@ $ fq -d tap dv basic_prog1.tap 0x10| 28 00 | (. | length: 40 0x15-0x17 (2) | | | data{}: 0x17-0x3f (40) 0x10| ff | . | flag: "standard_speed_data" (255) 0x17-0x18 (1) -0x10| 00 0a 14 00 20 f5 22 66| .... ."f| data: raw bits 0x18-0x3e (38) -0x20|71 20 69 73 20 74 68 65 20 62 65 73 74 21 22 0d|q is the best!".| -0x30|00 14 0a 00 ec 31 30 0e 00 00 0a 00 00 0d |.....10....... | + | | | bytes[0:38]: 0x18-0x3e (38) +0x10| 00 | . | [0]: 0 byte 0x18-0x19 (1) +0x10| 0a | . | [1]: 10 byte 0x19-0x1a (1) +0x10| 14 | . | [2]: 20 byte 0x1a-0x1b (1) +0x10| 00 | . | [3]: 0 byte 0x1b-0x1c (1) +0x10| 20 | | [4]: 32 byte 0x1c-0x1d (1) +0x10| f5 | . | [5]: 245 byte 0x1d-0x1e (1) +0x10| 22 | " | [6]: 34 byte 0x1e-0x1f (1) +0x10| 66| f| [7]: 102 byte 0x1f-0x20 (1) +0x20|71 |q | [8]: 113 byte 0x20-0x21 (1) +0x20| 20 | | [9]: 32 byte 0x21-0x22 (1) +0x20| 69 | i | [10]: 105 byte 0x22-0x23 (1) +0x20| 73 | s | [11]: 115 byte 0x23-0x24 (1) +0x20| 20 | | [12]: 32 byte 0x24-0x25 (1) +0x20| 74 | t | [13]: 116 byte 0x25-0x26 (1) +0x20| 68 | h | [14]: 104 byte 0x26-0x27 (1) +0x20| 65 | e | [15]: 101 byte 0x27-0x28 (1) +0x20| 20 | | [16]: 32 byte 0x28-0x29 (1) +0x20| 62 | b | [17]: 98 byte 0x29-0x2a (1) +0x20| 65 | e | [18]: 101 byte 0x2a-0x2b (1) +0x20| 73 | s | [19]: 115 byte 0x2b-0x2c (1) +0x20| 74 | t | [20]: 116 byte 0x2c-0x2d (1) +0x20| 21 | ! | [21]: 33 byte 0x2d-0x2e (1) +0x20| 22 | " | [22]: 34 byte 0x2e-0x2f (1) +0x20| 0d| .| [23]: 13 byte 0x2f-0x30 (1) +0x30|00 |. | [24]: 0 byte 0x30-0x31 (1) +0x30| 14 | . | [25]: 20 byte 0x31-0x32 (1) +0x30| 0a | . | [26]: 10 byte 0x32-0x33 (1) +0x30| 00 | . | [27]: 0 byte 0x33-0x34 (1) +0x30| ec | . | [28]: 236 byte 0x34-0x35 (1) +0x30| 31 | 1 | [29]: 49 byte 0x35-0x36 (1) +0x30| 30 | 0 | [30]: 48 byte 0x36-0x37 (1) +0x30| 0e | . | [31]: 14 byte 0x37-0x38 (1) +0x30| 00 | . | [32]: 0 byte 0x38-0x39 (1) +0x30| 00 | . | [33]: 0 byte 0x39-0x3a (1) +0x30| 0a | . | [34]: 10 byte 0x3a-0x3b (1) +0x30| 00 | . | [35]: 0 byte 0x3b-0x3c (1) +0x30| 00 | . | [36]: 0 byte 0x3c-0x3d (1) +0x30| 0d | . | [37]: 13 byte 0x3d-0x3e (1) 0x30| b6| | .|| checksum: 0xb6 (valid) 0x3e-0x3f (1)