Bitwise GCR decoding
This is some kind of ongoing basic research pet project of mine, possibly re-inventing the wheel, and so far with fewer opportunities for practical application than i've hoped.
Posting this here because it might be of interest to some and may lead to more insights than i've had so far. =)
Consider the traditional Commodore GCR to raw nibble mapping:$09 %01001 -> %1000 $08
$0a %01010 -> %0000 $00
$0b %01011 -> %0001 $01
$0d %01101 -> %1100 $0c
$0e %01110 -> %0100 $04
$0f %01111 -> %0101 $05
$12 %10010 -> %0010 $02
$13 %10011 -> %0011 $03
$15 %10101 -> %1111 $0f
$16 %10110 -> %0110 $06
$17 %10111 -> %0111 $07
$19 %11001 -> %1001 $09
$1a %11010 -> %1010 $0a
$1b %11011 -> %1011 $0b
$1d %11101 -> %1101 $0d
$1e %11110 -> %1110 $0e
A decoder must know all 5 GCR bits in order to decode a raw nibble.
Consider an alternative GCR to raw nibble mapping:$09 %01001 -> %1111 $0f
$0a %01010 -> %1110 $0e
$0b %01011 -> %1101 $0d
$0d %01101 -> %1100 $0c
$0e %01110 -> %1011 $0b
$0f %01111 -> %1010 $0a
$12 %10010 -> %0100 $04
$13 %10011 -> %0011 $03
$15 %10101 -> %0010 $02
$16 %10110 -> %0001 $01
$17 %10111 -> %0000 $00
$19 %11001 -> %1001 $09
$1a %11010 -> %1000 $08
$1b %11011 -> %0111 $07
$1d %11101 -> %0110 $06
$1e %11110 -> %0101 $05
This mapping has some interesting properties, which become apparent with different representations: [0] 6
/ \
/ \ [0] f
/ \ / \ +0
/ \ / [1] f 1111
/ \ / +2
/ \ +4 [0] d
/ \ / \ +0
/ \ / \ [0] e 1110
/ \ / \ / +1
/ \ / +3 [1] d
/ \ / \ +0
/ +6 [1] a [1] d 1101
/ \
/ \ +0 [0] c
/ \ / \ +0
/ \ / [1] c 1100
/ \ / +2
/ [1] a
/ \ +0
/ \ [0] b 1011
GCR|+16 +8 +4 +2 +1 \ / +1
0| +6 +0 +3 +2 +1 decode [1] a
1| +0 +4 +0 +0 +0 to raw \ +0
\ [1] a 1010
\
\ [0] 3
\ / \ +0
\ / \ [0] 4 0100
\ / \ / +1
\ / +3 [1] 3
\ / \ +0
\ +0 [0] 0 [1] 3 0011
\ / \
\ / \ +0 [0] 2
\ / \ / \ +0
\ / \ / [1] 2 0010
\ / \ / +2
\ / +0 [1] 0
\ / \ +0
\ / \ [0] 1 0001
\ / \ / +1
\ / [1] 0
\ / \ +0
[1] 0 [1] 0 0000
\
\ [0] 9
\ / \ +0
\ / [1] 9 1001
\ / +2
\ +4 [0] 7
\ / \ +0
\ / \ [0] 8 1000
\ / \ / +1
\ / +3 [1] 7
\ / \ +0
[1] 4 [1] 7 0111
\
\ +0 [0] 6
\ / \ +0
\ / [1] 6 0110
\ / +2
[1] 4
\ +0
\ [0] 5 0101
\ / +1
[1] 4
The GCR values form some kind of positional number system.
Each GCR bit position/value pair maps to a decoded value.
The individual decoded values of all 5 GCR nibble bits can be added to decode a raw nibble.
As the add operation is commutative and associative, a decoder can consume GCR nibbles piece-wise in any order of the input bits.
This can be used to decode a GCR stream like so:ldx $1c01 ; 00000111
lda decode01,x; 00001... - nibble 0 complete, nibble 1 partial
[...]
ldx $1c01 ; 11222223
adc decode1,x ; 00001111 - byte 0 complete
[...]
lda decode23,x; 22223... - nibble 2 complete, nibble 3 partial
ldx $1c01 ; 33334444
adc decode3,x ; 22223333 - byte 1 complete
[...]
This concept can also be used to decode a GCR stream on-the-fly while it is sent over the serial bus in bitpairs.
Note that an additional look-up would be required to map the custom-GCR decoded bytes to original standard Commodore GCR decoded bytes.
As the individual decoding tables take the better part of 256 indices each, memory required for them quickly becomes quite big.
This is problematic with the 2 KB of RAM of a 1541.
There may be opportunities to squeeze the tables together or apply other optimisations, considering these 16 mappings which satisfy the bitwise GCR decoding constraints.GCR: 4 3 2 1 0
1. 0: 2, 4, 1, 6, 0
1: 0, 0, 0, 0, 5
2. 0: 1, 2, 3, 6, 0
1: 0, 0, 0, 0, 5
3. 0: 3, 0, 0, 2, 1
1: 0, 2, 8, 0, 0
4. 0: 3, 6, 0, 2, 1
1: 0, 0, 7, 0, 0
5. 0: 6, 0, 3, 2, 1
1: 0, 4, 0, 0, 0
6. 0: 0, 6, 3, 2, 1
1: 5, 0, 0, 0, 0
7. 0: 3, 0, 8, 2, 1
1: 0, 2, 0, 0, 0
8. 0: 6, 0, 0, 4, 2
1: 0, 4, 1, 0, 0
9. 0: 6, 0, 1, 4, 2
1: 0, 4, 0, 0, 0
10. 0: 0, 6, 1, 4, 2
1: 5, 0, 0, 0, 0
11. 0: 1, 2, 0, 0, 3
1: 0, 0, 8, 2, 0
12. 0: 1, 2, 8, 0, 3
1: 0, 0, 0, 2, 0
13. 0: 1, 2, 0, 6, 3
1: 0, 0, 7, 0, 0
14. 0: 2, 4, 0, 0, 6
1: 0, 0, 1, 4, 0
15. 0: 2, 4, 1, 0, 6
1: 0, 0, 0, 4, 0
16. 0: 1, 2, 3, 0, 6
1: 0, 0, 0, 4, 0