1
1
mirror of https://github.com/wader/fq.git synced 2024-11-22 15:45:45 +03:00

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
This commit is contained in:
Michael R. Cook 2024-08-15 20:09:47 +02:00
parent 56beac25b0
commit 633160bbd0
2 changed files with 83 additions and 15 deletions

View File

@ -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)
}

View File

@ -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)