From 633160bbd0c7c256c02daed6862b085e5c5ce280 Mon Sep 17 00:00:00 2001 From: "Michael R. Cook" Date: Thu, 15 Aug 2024 20:09:47 +0200 Subject: [PATCH 1/4] 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 b1e0c18f8..ed7a99025 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 19b51e22f..cc54d682a 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) From fc350f3da507ba00a01b9c374d362be6a55dac57 Mon Sep 17 00:00:00 2001 From: "Michael R. Cook" Date: Thu, 15 Aug 2024 20:13:03 +0200 Subject: [PATCH 2/4] tzx: change data fields to array of bytes for easier JSON parsing --- format/tzx/testdata/basic_prog1.fqtest | 171 +++++++++++++++---------- format/tzx/tzx.go | 84 ++++++++---- 2 files changed, 162 insertions(+), 93 deletions(-) diff --git a/format/tzx/testdata/basic_prog1.fqtest b/format/tzx/testdata/basic_prog1.fqtest index d9b950d33..d1a73d8fe 100644 --- a/format/tzx/testdata/basic_prog1.fqtest +++ b/format/tzx/testdata/basic_prog1.fqtest @@ -5,77 +5,116 @@ $ fq -d tzx dv basic_prog1.tzx 0x00| 14 | . | minor_version: 20 0x9-0xa (1) | | | blocks[0:3]: 0xa-0xcd (195) | | | [0]{}: block 0xa-0x88 (126) -0x00| 32 | 2 | type: "archive_info" (50) 0xa-0xb (1) -0x00| 7b 00 | {. | length: 123 0xb-0xd (2) -0x00| 09 | . | count: 9 0xd-0xe (1) - | | | archive_info[0:9]: 0xe-0x88 (122) - | | | [0]{}: entry 0xe-0x1a (12) -0x00| 00 | . | id: "title" (0) 0xe-0xf (1) -0x00| 0a| .| length: 10 0xf-0x10 (1) -0x10|66 71 74 65 73 74 70 72 6f 67 |fqtestprog | value: "fqtestprog" 0x10-0x1a (10) - | | | [1]{}: entry 0x1a-0x21 (7) -0x10| 01 | . | id: "publisher" (1) 0x1a-0x1b (1) -0x10| 05 | . | length: 5 0x1b-0x1c (1) -0x10| 77 61 64 65| wade| value: "wader" 0x1c-0x21 (5) + | | | archive_info{}: 0xa-0x88 (126) +0x00| 32 | 2 | type: "archive_info" (50) 0xa-0xb (1) +0x00| 7b 00 | {. | length: 123 0xb-0xd (2) +0x00| 09 | . | count: 9 0xd-0xe (1) + | | | entries[0:9]: 0xe-0x88 (122) + | | | [0]{}: entry 0xe-0x1a (12) +0x00| 00 | . | id: "title" (0) 0xe-0xf (1) +0x00| 0a| .| length: 10 0xf-0x10 (1) +0x10|66 71 74 65 73 74 70 72 6f 67 |fqtestprog | value: "fqtestprog" 0x10-0x1a (10) + | | | [1]{}: entry 0x1a-0x21 (7) +0x10| 01 | . | id: "publisher" (1) 0x1a-0x1b (1) +0x10| 05 | . | length: 5 0x1b-0x1c (1) +0x10| 77 61 64 65| wade| value: "wader" 0x1c-0x21 (5) 0x20|72 |r | - | | | [2]{}: entry 0x21-0x32 (17) -0x20| 02 | . | id: "author" (2) 0x21-0x22 (1) -0x20| 0f | . | length: 15 0x22-0x23 (1) -0x20| 4d 69 63 68 61 65 6c 20 52 2e 20 43 6f| Michael R. Co| value: "Michael R. Cook" 0x23-0x32 (15) + | | | [2]{}: entry 0x21-0x32 (17) +0x20| 02 | . | id: "author" (2) 0x21-0x22 (1) +0x20| 0f | . | length: 15 0x22-0x23 (1) +0x20| 4d 69 63 68 61 65 6c 20 52 2e 20 43 6f| Michael R. Co| value: "Michael R. Cook" 0x23-0x32 (15) 0x30|6f 6b |ok | - | | | [3]{}: entry 0x32-0x38 (6) -0x30| 03 | . | id: "year" (3) 0x32-0x33 (1) -0x30| 04 | . | length: 4 0x33-0x34 (1) -0x30| 32 30 32 34 | 2024 | value: "2024" 0x34-0x38 (4) - | | | [4]{}: entry 0x38-0x41 (9) -0x30| 04 | . | id: "language" (4) 0x38-0x39 (1) -0x30| 07 | . | length: 7 0x39-0x3a (1) -0x30| 45 6e 67 6c 69 73| Englis| value: "English" 0x3a-0x41 (7) + | | | [3]{}: entry 0x32-0x38 (6) +0x30| 03 | . | id: "year" (3) 0x32-0x33 (1) +0x30| 04 | . | length: 4 0x33-0x34 (1) +0x30| 32 30 32 34 | 2024 | value: "2024" 0x34-0x38 (4) + | | | [4]{}: entry 0x38-0x41 (9) +0x30| 04 | . | id: "language" (4) 0x38-0x39 (1) +0x30| 07 | . | length: 7 0x39-0x3a (1) +0x30| 45 6e 67 6c 69 73| Englis| value: "English" 0x3a-0x41 (7) 0x40|68 |h | - | | | [5]{}: entry 0x41-0x4f (14) -0x40| 05 | . | id: "category" (5) 0x41-0x42 (1) -0x40| 0c | . | length: 12 0x42-0x43 (1) -0x40| 54 65 73 74 20 50 72 6f 67 72 61 6d | Test Program | value: "Test Program" 0x43-0x4f (12) - | | | [6]{}: entry 0x4f-0x5c (13) -0x40| 07| .| id: "loader" (7) 0x4f-0x50 (1) -0x50|0b |. | length: 11 0x50-0x51 (1) -0x50| 52 4f 4d 20 74 69 6d 69 6e 67 73 | ROM timings | value: "ROM timings" 0x51-0x5c (11) - | | | [7]{}: entry 0x5c-0x6e (18) -0x50| 08 | . | id: "origin" (8) 0x5c-0x5d (1) -0x50| 10 | . | length: 16 0x5d-0x5e (1) -0x50| 4f 72| Or| value: "Original release" 0x5e-0x6e (16) + | | | [5]{}: entry 0x41-0x4f (14) +0x40| 05 | . | id: "category" (5) 0x41-0x42 (1) +0x40| 0c | . | length: 12 0x42-0x43 (1) +0x40| 54 65 73 74 20 50 72 6f 67 72 61 6d | Test Program | value: "Test Program" 0x43-0x4f (12) + | | | [6]{}: entry 0x4f-0x5c (13) +0x40| 07| .| id: "loader" (7) 0x4f-0x50 (1) +0x50|0b |. | length: 11 0x50-0x51 (1) +0x50| 52 4f 4d 20 74 69 6d 69 6e 67 73 | ROM timings | value: "ROM timings" 0x51-0x5c (11) + | | | [7]{}: entry 0x5c-0x6e (18) +0x50| 08 | . | id: "origin" (8) 0x5c-0x5d (1) +0x50| 10 | . | length: 16 0x5d-0x5e (1) +0x50| 4f 72| Or| value: "Original release" 0x5e-0x6e (16) 0x60|69 67 69 6e 61 6c 20 72 65 6c 65 61 73 65 |iginal release | - | | | [8]{}: entry 0x6e-0x88 (26) -0x60| ff | . | id: "comment" (255) 0x6e-0x6f (1) -0x60| 18| .| length: 24 0x6f-0x70 (1) -0x70|54 5a 58 65 64 20 62 79 20 4d 69 63 68 61 65 6c|TZXed by Michael| value: "TZXed by Michael R. Cook" 0x70-0x88 (24) + | | | [8]{}: entry 0x6e-0x88 (26) +0x60| ff | . | id: "comment" (255) 0x6e-0x6f (1) +0x60| 18| .| length: 24 0x6f-0x70 (1) +0x70|54 5a 58 65 64 20 62 79 20 4d 69 63 68 61 65 6c|TZXed by Michael| value: "TZXed by Michael R. Cook" 0x70-0x88 (24) 0x80|20 52 2e 20 43 6f 6f 6b | R. Cook | | | | [1]{}: block 0x88-0xa0 (24) -0x80| 10 | . | type: "standard_speed_data" (16) 0x88-0x89 (1) -0x80| e8 03 | .. | pause: 1000 0x89-0x8b (2) - |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef| tap{}: (tap) 0x8b-0xa0 (21) - | | | blocks[0:1]: 0x8b-0xa0 (21) - | | | [0]{}: block 0x8b-0xa0 (21) -0x80| 13 00 | .. | length: 19 0x8b-0x8d (2) - | | | header{}: 0x8d-0xa0 (19) -0x80| 00 | . | flag: "standard_speed_data" (0) 0x8d-0x8e (1) -0x80| 00 | . | data_type: "program" (0) 0x8e-0x8f (1) -0x80| 66| f| program_name: "fqTestProg" 0x8f-0x99 (10) + | | | standard_speed_data{}: 0x88-0xa0 (24) +0x80| 10 | . | type: "standard_speed_data" (16) 0x88-0x89 (1) +0x80| e8 03 | .. | pause: 1000 0x89-0x8b (2) + |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef| tap{}: (tap) 0x8b-0xa0 (21) + | | | blocks[0:1]: 0x8b-0xa0 (21) + | | | [0]{}: block 0x8b-0xa0 (21) +0x80| 13 00 | .. | length: 19 0x8b-0x8d (2) + | | | header{}: 0x8d-0xa0 (19) +0x80| 00 | . | flag: "standard_speed_data" (0) 0x8d-0x8e (1) +0x80| 00 | . | data_type: "program" (0) 0x8e-0x8f (1) +0x80| 66| f| program_name: "fqTestProg" 0x8f-0x99 (10) 0x90|71 54 65 73 74 50 72 6f 67 |qTestProg | -0x90| 26 00 | &. | data_length: 38 0x99-0x9b (2) -0x90| 0a 00 | .. | auto_start_line: 10 0x9b-0x9d (2) -0x90| 26 00 | &. | program_length: 38 0x9d-0x9f (2) -0x90| 01| .| checksum: 0x1 (valid) 0x9f-0xa0 (1) +0x90| 26 00 | &. | data_length: 38 0x99-0x9b (2) +0x90| 0a 00 | .. | auto_start_line: 10 0x9b-0x9d (2) +0x90| 26 00 | &. | program_length: 38 0x9d-0x9f (2) +0x90| 01| .| checksum: 0x1 (valid) 0x9f-0xa0 (1) | | | [2]{}: block 0xa0-0xcd (45) -0xa0|10 |. | type: "standard_speed_data" (16) 0xa0-0xa1 (1) -0xa0| e8 03 | .. | pause: 1000 0xa1-0xa3 (2) - |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef| tap{}: (tap) 0xa3-0xcd (42) - | | | blocks[0:1]: 0xa3-0xcd (42) - | | | [0]{}: block 0xa3-0xcd (42) -0xa0| 28 00 | (. | length: 40 0xa3-0xa5 (2) - | | | data{}: 0xa5-0xcd (40) -0xa0| ff | . | flag: "standard_speed_data" (255) 0xa5-0xa6 (1) -0xa0| 00 0a 14 00 20 f5 22 66 71 20| .... ."fq | data: raw bits 0xa6-0xcc (38) -0xb0|69 73 20 74 68 65 20 62 65 73 74 21 22 0d 00 14|is the best!"...| -0xc0|0a 00 ec 31 30 0e 00 00 0a 00 00 0d |...10....... | -0xc0| b6| | .| | checksum: 0xb6 (valid) 0xcc-0xcd (1) + | | | standard_speed_data{}: 0xa0-0xcd (45) +0xa0|10 |. | type: "standard_speed_data" (16) 0xa0-0xa1 (1) +0xa0| e8 03 | .. | pause: 1000 0xa1-0xa3 (2) + |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef| tap{}: (tap) 0xa3-0xcd (42) + | | | blocks[0:1]: 0xa3-0xcd (42) + | | | [0]{}: block 0xa3-0xcd (42) +0xa0| 28 00 | (. | length: 40 0xa3-0xa5 (2) + | | | data{}: 0xa5-0xcd (40) +0xa0| ff | . | flag: "standard_speed_data" (255) 0xa5-0xa6 (1) + | | | bytes[0:38]: 0xa6-0xcc (38) +0xa0| 00 | . | [0]: 0 byte 0xa6-0xa7 (1) +0xa0| 0a | . | [1]: 10 byte 0xa7-0xa8 (1) +0xa0| 14 | . | [2]: 20 byte 0xa8-0xa9 (1) +0xa0| 00 | . | [3]: 0 byte 0xa9-0xaa (1) +0xa0| 20 | | [4]: 32 byte 0xaa-0xab (1) +0xa0| f5 | . | [5]: 245 byte 0xab-0xac (1) +0xa0| 22 | " | [6]: 34 byte 0xac-0xad (1) +0xa0| 66 | f | [7]: 102 byte 0xad-0xae (1) +0xa0| 71 | q | [8]: 113 byte 0xae-0xaf (1) +0xa0| 20| | [9]: 32 byte 0xaf-0xb0 (1) +0xb0|69 |i | [10]: 105 byte 0xb0-0xb1 (1) +0xb0| 73 | s | [11]: 115 byte 0xb1-0xb2 (1) +0xb0| 20 | | [12]: 32 byte 0xb2-0xb3 (1) +0xb0| 74 | t | [13]: 116 byte 0xb3-0xb4 (1) +0xb0| 68 | h | [14]: 104 byte 0xb4-0xb5 (1) +0xb0| 65 | e | [15]: 101 byte 0xb5-0xb6 (1) +0xb0| 20 | | [16]: 32 byte 0xb6-0xb7 (1) +0xb0| 62 | b | [17]: 98 byte 0xb7-0xb8 (1) +0xb0| 65 | e | [18]: 101 byte 0xb8-0xb9 (1) +0xb0| 73 | s | [19]: 115 byte 0xb9-0xba (1) +0xb0| 74 | t | [20]: 116 byte 0xba-0xbb (1) +0xb0| 21 | ! | [21]: 33 byte 0xbb-0xbc (1) +0xb0| 22 | " | [22]: 34 byte 0xbc-0xbd (1) +0xb0| 0d | . | [23]: 13 byte 0xbd-0xbe (1) +0xb0| 00 | . | [24]: 0 byte 0xbe-0xbf (1) +0xb0| 14| .| [25]: 20 byte 0xbf-0xc0 (1) +0xc0|0a |. | [26]: 10 byte 0xc0-0xc1 (1) +0xc0| 00 | . | [27]: 0 byte 0xc1-0xc2 (1) +0xc0| ec | . | [28]: 236 byte 0xc2-0xc3 (1) +0xc0| 31 | 1 | [29]: 49 byte 0xc3-0xc4 (1) +0xc0| 30 | 0 | [30]: 48 byte 0xc4-0xc5 (1) +0xc0| 0e | . | [31]: 14 byte 0xc5-0xc6 (1) +0xc0| 00 | . | [32]: 0 byte 0xc6-0xc7 (1) +0xc0| 00 | . | [33]: 0 byte 0xc7-0xc8 (1) +0xc0| 0a | . | [34]: 10 byte 0xc8-0xc9 (1) +0xc0| 00 | . | [35]: 0 byte 0xc9-0xca (1) +0xc0| 00 | . | [36]: 0 byte 0xca-0xcb (1) +0xc0| 0d | . | [37]: 13 byte 0xcb-0xcc (1) +0xc0| b6| | .| | checksum: 0xb6 (valid) 0xcc-0xcd (1) diff --git a/format/tzx/tzx.go b/format/tzx/tzx.go index 98adb5a02..3a043ef77 100644 --- a/format/tzx/tzx.go +++ b/format/tzx/tzx.go @@ -97,7 +97,11 @@ func decodeBlock(d *decode.D) { length := d.FieldU24("length") // Length of data that follows // Data as in .TAP files - d.FieldRawLen("data", int64(length*8)) + d.FieldArray("data", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 12h (18d) | Pure Tone @@ -131,7 +135,11 @@ func decodeBlock(d *decode.D) { length := d.FieldU24("length") // Length of data that follows // Data as in .TAP files - d.FieldRawLen("data", int64(length*8)) + d.FieldArray("data", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 15h (21d) | Direct Recording @@ -143,11 +151,17 @@ func decodeBlock(d *decode.D) { // The preferred sampling frequencies are 22050 or 44100 Hz // (158 or 79 T-states/sample). 0x15: func(d *decode.D) { - d.FieldU16("t_states") // Number of T-states per sample (bit of data) - d.FieldU16("pause") // Pause after this block in milliseconds (ms.) - d.FieldU8("used_bits") // Used bits (samples) in last byte of data (1-8) - length := d.FieldU24("length") // Length of data that follows - d.FieldRawLen("data", int64(length*8)) // Samples data. Each bit represents a state on the EAR port + d.FieldU16("t_states") // Number of T-states per sample (bit of data) + d.FieldU16("pause") // Pause after this block in milliseconds (ms.) + d.FieldU8("used_bits") // Used bits (samples) in last byte of data (1-8) + length := d.FieldU24("length") // Length of data that follows + + // Samples data. Each bit represents a state on the EAR port + d.FieldArray("data", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 18h (24d) | CSW Recording @@ -165,12 +179,16 @@ func decodeBlock(d *decode.D) { // Sampling rate d.FieldU24("sample_rate") // Compression type - d.FieldU8("compression_type", scalar.UintMapSymStr{0x01: "rle", 0x02: "zrle"}) + d.FieldU8("compression_type", scalar.UintMapSymStr{0x00: "unknown", 0x01: "rle", 0x02: "zrle"}) // Number of stored pulses (after decompression) d.FieldU32("stored_pulse_count") // CSW data, encoded according to the CSW specification - d.FieldRawLen("data", int64(length*8)) + d.FieldArray("data", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 19h (25d) | Generalized Data @@ -194,7 +212,11 @@ func decodeBlock(d *decode.D) { // PilotStreams []PilotRLE // 0x12+ (2*NPP+1)*ASP - PRLE[TOTP] Pilot and sync data stream // DataSymbols []Symbol // 0x12+ (TOTP>0)*((2*NPP+1)*ASP)+TOTP*3 - SYMDEF[ASD] Data symbols definition table // DataStreams []uint8 // 0x12+ (TOTP>0)*((2*NPP+1)*ASP)+ TOTP*3+(2*NPD+1)*ASD - BYTE[DS] Data stream - d.FieldRawLen("data", int64(length*8)) + d.FieldArray("data", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 20h (32d) | Pause Tape Command @@ -340,7 +362,7 @@ func decodeBlock(d *decode.D) { count := d.FieldU8("count") // Number of entries in the archive info // the archive strings - d.FieldArray("archive_info", func(d *decode.D) { + d.FieldArray("entries", func(d *decode.D) { for i := uint64(0); i < count; i++ { d.FieldStruct("entry", func(d *decode.D) { d.FieldU8("id", scalar.UintMapSymStr{ @@ -372,11 +394,11 @@ func decodeBlock(d *decode.D) { for i := uint64(0); i < count; i++ { d.FieldStruct("info", func(d *decode.D) { // Hardware Type ID (computers, printers, mice, etc.) - typeId := d.FieldU8("type", hwInfoTypeMapper) - // Hardware ID (ZX81, Kempston Joystick, etc.) - d.FieldU8("id", hwInfoTypeIdMapper[typeId]) + typeId := d.FieldU8("type_id", hwInfoTypes) + // Hardware Device ID (ZX81, Kempston Joystick, etc.) + d.FieldU8("device_id", hwInfoDevices[typeId]) // Hardware compatibility information - d.FieldU8("info_id", hwInfoIdMapper) + d.FieldU8("info_id", hwInfoCompatibilityInfo) }) } }) @@ -389,7 +411,11 @@ func decodeBlock(d *decode.D) { 0x35: func(d *decode.D) { d.FieldStr("identification", 10, charmap.ISO8859_1) length := d.FieldU32("length") - d.FieldRawLen("info", int64(length*8)) + d.FieldArray("info", func(d *decode.D) { + for i := uint64(0); i < length; i++ { + d.FieldU8("byte") + } + }) }, // ID: 5Ah (90d) | Glue Block @@ -404,22 +430,26 @@ func decodeBlock(d *decode.D) { 0x5A: func(d *decode.D) { // Value: { "XTape!",0x1A,MajR,MinR } // Just skip these 9 bytes and you will end up on the next ID. - d.FieldRawLen("value", int64(9*8)) + d.FieldRawLen("value", 9*8) }, } - blockType := d.FieldU8("type", blockTypeMapper) - + blockType := d.PeekUintBits(8) // Deprecated block types: C64RomType, C64TurboData, EmulationInfo, Snapshot if blockType == 0x16 || blockType == 0x17 || blockType == 0x34 || blockType == 0x40 { d.Fatalf("deprecated block type encountered: %02x", blockType) } - if fn, ok := blocks[blockType]; ok { - fn(d) - } else { - d.Fatalf("block type not valid, got: %02x", blockType) - } + blockLabel := blockTypeMapper[blockType] + d.FieldStruct(blockLabel, func(d *decode.D) { + d.FieldU8("type", blockTypeMapper) + + if fn, ok := blocks[blockType]; ok { + fn(d) + } else { + d.Fatalf("block type not valid, got: %02x", blockType) + } + }) } var blockTypeMapper = scalar.UintMapSymStr{ @@ -454,7 +484,7 @@ var blockTypeMapper = scalar.UintMapSymStr{ 0x5A: "glue_block", } -var hwInfoTypeMapper = scalar.UintMapDescription{ +var hwInfoTypes = scalar.UintMapDescription{ 0x00: "Computers", 0x01: "External storage", 0x02: "ROM/RAM type add-ons", @@ -474,7 +504,7 @@ var hwInfoTypeMapper = scalar.UintMapDescription{ 0x10: "Graphics", } -var hwInfoTypeIdMapper = map[uint64]scalar.UintMapDescription{ +var hwInfoDevices = map[uint64]scalar.UintMapDescription{ 0x00: { // Computers 0x00: "ZX Spectrum 16k", 0x01: "ZX Spectrum 48k, Plus", @@ -647,7 +677,7 @@ var hwInfoTypeIdMapper = map[uint64]scalar.UintMapDescription{ }, } -var hwInfoIdMapper = scalar.UintMapDescription{ +var hwInfoCompatibilityInfo = scalar.UintMapDescription{ 00: "RUNS on this machine or with this hardware, but may or may not use the hardware or special features of the machine.", 01: "USES the hardware or special features of the machine, such as extra memory or a sound chip.", 02: "RUNS but it DOESN'T use the hardware or special features of the machine.", From d38493066ac822da40965194e5e7e39e4b39ba44 Mon Sep 17 00:00:00 2001 From: "Michael R. Cook" Date: Sun, 18 Aug 2024 19:44:37 +0200 Subject: [PATCH 3/4] tap: revert using an array of bytes --- format/tap/tap.go | 10 ++---- format/tap/testdata/basic_prog1.fqtest | 42 ++------------------------ 2 files changed, 5 insertions(+), 47 deletions(-) diff --git a/format/tap/tap.go b/format/tap/tap.go index ed7a99025..d4fdf463d 100644 --- a/format/tap/tap.go +++ b/format/tap/tap.go @@ -58,9 +58,7 @@ func decodeTapBlock(d *decode.D) { d.Fatalf("TAP fragments with 0 bytes are not supported") case 1: d.FieldStruct("data", func(d *decode.D) { - d.FieldArray("bytes", func(d *decode.D) { - d.FieldU8("byte") - }) + d.FieldRawLen("bytes", 8) }) case 19: d.FieldStruct("header", func(d *decode.D) { @@ -167,11 +165,7 @@ func decodeDataBlock(d *decode.D, length uint64) { return s, nil })) // The essential data: length minus the flag/checksum bytes (may be empty) - d.FieldArray("bytes", func(d *decode.D) { - for i := uint64(0); i < length-2; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("bytes", int64(length-2)*8) // 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 cc54d682a..39791d82d 100644 --- a/format/tap/testdata/basic_prog1.fqtest +++ b/format/tap/testdata/basic_prog1.fqtest @@ -15,43 +15,7 @@ $ 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) - | | | 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) +0x10| 00 0a 14 00 20 f5 22 66| .... ."f| bytes: 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....... | 0x30| b6| | .|| checksum: 0xb6 (valid) 0x3e-0x3f (1) From d3a5b8df28556e6820aeb687a5edcbff87406ced Mon Sep 17 00:00:00 2001 From: "Michael R. Cook" Date: Sun, 18 Aug 2024 19:45:02 +0200 Subject: [PATCH 4/4] tzx: revert using an array of bytes --- format/tzx/testdata/basic_prog1.fqtest | 42 ++-------------------- format/tzx/tzx.go | 48 ++++++-------------------- 2 files changed, 14 insertions(+), 76 deletions(-) diff --git a/format/tzx/testdata/basic_prog1.fqtest b/format/tzx/testdata/basic_prog1.fqtest index d1a73d8fe..f1be031d8 100644 --- a/format/tzx/testdata/basic_prog1.fqtest +++ b/format/tzx/testdata/basic_prog1.fqtest @@ -78,43 +78,7 @@ $ fq -d tzx dv basic_prog1.tzx 0xa0| 28 00 | (. | length: 40 0xa3-0xa5 (2) | | | data{}: 0xa5-0xcd (40) 0xa0| ff | . | flag: "standard_speed_data" (255) 0xa5-0xa6 (1) - | | | bytes[0:38]: 0xa6-0xcc (38) -0xa0| 00 | . | [0]: 0 byte 0xa6-0xa7 (1) -0xa0| 0a | . | [1]: 10 byte 0xa7-0xa8 (1) -0xa0| 14 | . | [2]: 20 byte 0xa8-0xa9 (1) -0xa0| 00 | . | [3]: 0 byte 0xa9-0xaa (1) -0xa0| 20 | | [4]: 32 byte 0xaa-0xab (1) -0xa0| f5 | . | [5]: 245 byte 0xab-0xac (1) -0xa0| 22 | " | [6]: 34 byte 0xac-0xad (1) -0xa0| 66 | f | [7]: 102 byte 0xad-0xae (1) -0xa0| 71 | q | [8]: 113 byte 0xae-0xaf (1) -0xa0| 20| | [9]: 32 byte 0xaf-0xb0 (1) -0xb0|69 |i | [10]: 105 byte 0xb0-0xb1 (1) -0xb0| 73 | s | [11]: 115 byte 0xb1-0xb2 (1) -0xb0| 20 | | [12]: 32 byte 0xb2-0xb3 (1) -0xb0| 74 | t | [13]: 116 byte 0xb3-0xb4 (1) -0xb0| 68 | h | [14]: 104 byte 0xb4-0xb5 (1) -0xb0| 65 | e | [15]: 101 byte 0xb5-0xb6 (1) -0xb0| 20 | | [16]: 32 byte 0xb6-0xb7 (1) -0xb0| 62 | b | [17]: 98 byte 0xb7-0xb8 (1) -0xb0| 65 | e | [18]: 101 byte 0xb8-0xb9 (1) -0xb0| 73 | s | [19]: 115 byte 0xb9-0xba (1) -0xb0| 74 | t | [20]: 116 byte 0xba-0xbb (1) -0xb0| 21 | ! | [21]: 33 byte 0xbb-0xbc (1) -0xb0| 22 | " | [22]: 34 byte 0xbc-0xbd (1) -0xb0| 0d | . | [23]: 13 byte 0xbd-0xbe (1) -0xb0| 00 | . | [24]: 0 byte 0xbe-0xbf (1) -0xb0| 14| .| [25]: 20 byte 0xbf-0xc0 (1) -0xc0|0a |. | [26]: 10 byte 0xc0-0xc1 (1) -0xc0| 00 | . | [27]: 0 byte 0xc1-0xc2 (1) -0xc0| ec | . | [28]: 236 byte 0xc2-0xc3 (1) -0xc0| 31 | 1 | [29]: 49 byte 0xc3-0xc4 (1) -0xc0| 30 | 0 | [30]: 48 byte 0xc4-0xc5 (1) -0xc0| 0e | . | [31]: 14 byte 0xc5-0xc6 (1) -0xc0| 00 | . | [32]: 0 byte 0xc6-0xc7 (1) -0xc0| 00 | . | [33]: 0 byte 0xc7-0xc8 (1) -0xc0| 0a | . | [34]: 10 byte 0xc8-0xc9 (1) -0xc0| 00 | . | [35]: 0 byte 0xc9-0xca (1) -0xc0| 00 | . | [36]: 0 byte 0xca-0xcb (1) -0xc0| 0d | . | [37]: 13 byte 0xcb-0xcc (1) +0xa0| 00 0a 14 00 20 f5 22 66 71 20| .... ."fq | bytes: raw bits 0xa6-0xcc (38) +0xb0|69 73 20 74 68 65 20 62 65 73 74 21 22 0d 00 14|is the best!"...| +0xc0|0a 00 ec 31 30 0e 00 00 0a 00 00 0d |...10....... | 0xc0| b6| | .| | checksum: 0xb6 (valid) 0xcc-0xcd (1) diff --git a/format/tzx/tzx.go b/format/tzx/tzx.go index 3a043ef77..47ee98712 100644 --- a/format/tzx/tzx.go +++ b/format/tzx/tzx.go @@ -97,11 +97,7 @@ func decodeBlock(d *decode.D) { length := d.FieldU24("length") // Length of data that follows // Data as in .TAP files - d.FieldArray("data", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("data", int64(length)*8) }, // ID: 12h (18d) | Pure Tone @@ -135,11 +131,7 @@ func decodeBlock(d *decode.D) { length := d.FieldU24("length") // Length of data that follows // Data as in .TAP files - d.FieldArray("data", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("data", int64(length)*8) }, // ID: 15h (21d) | Direct Recording @@ -151,17 +143,11 @@ func decodeBlock(d *decode.D) { // The preferred sampling frequencies are 22050 or 44100 Hz // (158 or 79 T-states/sample). 0x15: func(d *decode.D) { - d.FieldU16("t_states") // Number of T-states per sample (bit of data) - d.FieldU16("pause") // Pause after this block in milliseconds (ms.) - d.FieldU8("used_bits") // Used bits (samples) in last byte of data (1-8) - length := d.FieldU24("length") // Length of data that follows - - // Samples data. Each bit represents a state on the EAR port - d.FieldArray("data", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldU16("t_states") // Number of T-states per sample (bit of data) + d.FieldU16("pause") // Pause after this block in milliseconds (ms.) + d.FieldU8("used_bits") // Used bits (samples) in last byte of data (1-8) + length := d.FieldU24("length") // Length of data that follows + d.FieldRawLen("data", int64(length)*8) // Samples data. Each bit represents a state on the EAR port }, // ID: 18h (24d) | CSW Recording @@ -184,11 +170,7 @@ func decodeBlock(d *decode.D) { d.FieldU32("stored_pulse_count") // CSW data, encoded according to the CSW specification - d.FieldArray("data", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("data", int64(length)*8) }, // ID: 19h (25d) | Generalized Data @@ -212,11 +194,7 @@ func decodeBlock(d *decode.D) { // PilotStreams []PilotRLE // 0x12+ (2*NPP+1)*ASP - PRLE[TOTP] Pilot and sync data stream // DataSymbols []Symbol // 0x12+ (TOTP>0)*((2*NPP+1)*ASP)+TOTP*3 - SYMDEF[ASD] Data symbols definition table // DataStreams []uint8 // 0x12+ (TOTP>0)*((2*NPP+1)*ASP)+ TOTP*3+(2*NPD+1)*ASD - BYTE[DS] Data stream - d.FieldArray("data", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("data", int64(length)*8) }, // ID: 20h (32d) | Pause Tape Command @@ -409,13 +387,9 @@ func decodeBlock(d *decode.D) { // some information written by a utility, extra settings required by a // particular emulator, etc. 0x35: func(d *decode.D) { - d.FieldStr("identification", 10, charmap.ISO8859_1) + d.FieldStr("identification", 16, charmap.ISO8859_1) length := d.FieldU32("length") - d.FieldArray("info", func(d *decode.D) { - for i := uint64(0); i < length; i++ { - d.FieldU8("byte") - } - }) + d.FieldRawLen("info", int64(length)*8) }, // ID: 5Ah (90d) | Glue Block