kopia lustrzana https://github.com/Wren6991/PicoDVI
151 wiersze
4.0 KiB
Python
Executable File
151 wiersze
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# The key fact is that, if x is even, and the encoder currently has a running
|
|
# imbalance of 0, encoding x followed by x + 1 produces a symbol pair with a
|
|
# net balance of 0.
|
|
#
|
|
# This is a reasonable constraint, because we only want RGB565 (so 6 valid
|
|
# channel data bits -> data is multiple of 4), and can probably tolerate
|
|
# 0.25LSB of noise :)
|
|
#
|
|
# This means that encoding a half-horizontal-resolution scanline buffer is a
|
|
# simple LUT operation for each colour channel, because we have made the
|
|
# encoding process stateless by guaranteeing 0 balance.
|
|
|
|
def popcount(x):
|
|
n = 0
|
|
while x:
|
|
n += 1
|
|
x = x & (x - 1)
|
|
return n
|
|
|
|
# Equivalent to N1(q) - N0(q) in the DVI spec
|
|
def byteimbalance(x):
|
|
return 2 * popcount(x) - 8
|
|
|
|
# This is a direct translation of "Figure 3-5. T.M.D.S. Encode Algorithm" on
|
|
# page 29 of DVI 1.0 spec
|
|
|
|
class TMDSEncode:
|
|
ctrl_syms = {
|
|
0b00: 0b1101010100,
|
|
0b01: 0b0010101011,
|
|
0b10: 0b0101010100,
|
|
0b11: 0b1010101011
|
|
}
|
|
def __init__(self):
|
|
self.imbalance = 0
|
|
|
|
def encode(self, d, c, de):
|
|
if not de:
|
|
self.imbalance = 0
|
|
return self.ctrl_syms[c]
|
|
# Minimise transitions
|
|
q_m = d & 0x1
|
|
if popcount(d) > 4 or (popcount(d) == 4 and not d & 0x1):
|
|
for i in range(7):
|
|
q_m = q_m | (~(q_m >> i ^ d >> i + 1) & 0x1) << i + 1
|
|
else:
|
|
for i in range(7):
|
|
q_m = q_m | ( (q_m >> i ^ d >> i + 1) & 0x1) << i + 1
|
|
q_m = q_m | 0x100
|
|
# Correct DC balance
|
|
inversion_mask = 0x2ff
|
|
q_out = 0
|
|
if self.imbalance == 0 or byteimbalance(q_m & 0xff) == 0:
|
|
q_out = q_m ^ (0 if q_m & 0x100 else inversion_mask)
|
|
if q_m & 0x100:
|
|
self.imbalance += byteimbalance(q_m & 0xff)
|
|
else:
|
|
self.imbalance -= byteimbalance(q_m & 0xff)
|
|
elif (self.imbalance > 0) == (byteimbalance(q_m & 0xff) > 0):
|
|
q_out = q_m ^ inversion_mask
|
|
self.imbalance += ((q_m & 0x100) >> 7) - byteimbalance(q_m & 0xff)
|
|
else:
|
|
q_out = q_m
|
|
self.imbalance += byteimbalance(q_m & 0xff) - ((~q_m & 0x100) >> 7)
|
|
return q_out
|
|
|
|
# Turn a bitmap of width n into n pairs of pseudo-differential bits
|
|
def differentialise(x, n):
|
|
accum = 0
|
|
for i in range(n):
|
|
accum <<= 2
|
|
if x & (1 << (n - 1)):
|
|
accum |= 0b01
|
|
else:
|
|
accum |= 0b10
|
|
x <<= 1
|
|
return accum
|
|
|
|
enc = TMDSEncode()
|
|
|
|
|
|
###
|
|
# Pixel-doubled table:
|
|
|
|
# for i in range(0, 256, 4):
|
|
# sym0 = enc.encode(i, 0, 1)
|
|
# sym1 = enc.encode(i ^ 1, 0, 1)
|
|
# assert(enc.imbalance == 0)
|
|
# print(f"0x{sym0 | (sym1 << 10):05x}u,")
|
|
|
|
###
|
|
# Fullres 1bpp table: (each entry is 2 words, 4 pixels)
|
|
|
|
# (note trick here is that encoding 0x00 or 0xff sets imbalance to -8, and
|
|
# (encoding 0x01 or 0xfe returns imbalance to 0, so we alternate between these
|
|
# (two pairs of dark/light colours. Creates some fairly subtle vertical
|
|
# (banding, but it's cheap.
|
|
|
|
# for i in range(1 << 4):
|
|
# syms = list(enc.encode((0xff if i & 1 << j else 0) ^ j & 0x01, 0, 1) for j in range(4))
|
|
# print(f"0x{syms[0] | syms[1] << 10:05x}, 0x{syms[2] | syms[3] << 10:05x}")
|
|
# assert(enc.imbalance == 0)
|
|
|
|
###
|
|
# Fullres table stuff:
|
|
|
|
# def disptable_format(sym):
|
|
# return sym | ((popcount(sym) * 2 - 10 & 0x3f) << 26)
|
|
|
|
# print("// Non-negative running disparity:")
|
|
# for i in range(0, 256, 4):
|
|
# enc.imbalance = 1
|
|
# print("0x{:08x},".format(disptable_format(enc.encode(i, 0, 1))))
|
|
|
|
# print("// Negative running disparity:")
|
|
# for i in range(0, 256, 4):
|
|
# enc.imbalance = -1
|
|
# print("0x{:08x},".format(disptable_format(enc.encode(i, 0, 1))))
|
|
|
|
###
|
|
# Control symbols:
|
|
|
|
# for i in range(4):
|
|
# sym = enc.encode(0, i, 0)
|
|
# print(f"0x{sym << 10 | sym:05x},")
|
|
|
|
|
|
###
|
|
# Find zero-balance symbols:
|
|
|
|
# for i in range(256):
|
|
# enc.imbalance = 0
|
|
# sym = enc.encode(i, 0, 1)
|
|
# if enc.imbalance == 0:
|
|
# print(f"{i:02x}: {sym:03x}")
|
|
|
|
###
|
|
# Generate 2bpp table based on above experiment:
|
|
|
|
levels_2bpp_even = [0x05, 0x50, 0xaf, 0xfa]
|
|
levels_2bpp_odd = [0x04, 0x51, 0xae, 0xfb]
|
|
|
|
for i1, p1 in enumerate(levels_2bpp_odd):
|
|
for i0, p0 in enumerate(levels_2bpp_even):
|
|
sym0 = enc.encode(p0, 0, 1)
|
|
sym1 = enc.encode(p1, 0, 1)
|
|
assert(enc.imbalance == 0)
|
|
print(f".word 0x{sym1 << 10 | sym0:05x} // {i0:02b}, {i1:02b}")
|