1.1 --- a/ULA.txt Sun Jun 19 17:09:31 2016 +0200
1.2 +++ b/ULA.txt Sun Jun 19 21:21:28 2016 +0200
1.3 @@ -469,6 +469,37 @@
1.4 hardware implementation, and by the time the Electron was planned, it was too
1.5 late to do anything about this somewhat unfortunate choice.
1.6
1.7 +Pixel Layouts
1.8 +-------------
1.9 +
1.10 +The pixel layouts are as follows:
1.11 +
1.12 + Modes Depth (bpp) Pixels (from bits)
1.13 + ----- ----------- ------------------
1.14 + 0, 3, 4, 6 1 7 6 5 4 3 2 1 0
1.15 + 1, 5 2 73 62 51 40
1.16 + 2 4 7531 6420
1.17 +
1.18 +Since the ULA reads a half-byte at a time, one might expect it to attempt to
1.19 +produce pixels for every half-byte, as opposed to handling entire bytes.
1.20 +However, the pixel layout is not conducive to producing pixels as soon as a
1.21 +half-byte has been read for a given full-byte location: in 1bpp modes the
1.22 +first four pixels can indeed be produced, but in 2bpp and 4bpp modes the pixel
1.23 +data is spread across the entire byte in different ways.
1.24 +
1.25 +An alternative arrangement might be as follows:
1.26 +
1.27 + Modes Depth (bpp) Pixels (from bits)
1.28 + ----- ----------- ------------------
1.29 + 0, 3, 4, 6 1 7 6 5 4 3 2 1 0
1.30 + 1, 5 2 76 54 32 10
1.31 + 2 4 7654 3210
1.32 +
1.33 +Just as the mode layouts were presumably decided by compatibility with the BBC
1.34 +Micro, the pixel layouts will have been maintained for similar reasons.
1.35 +Unfortunately, this layout prevents any optimisation of the ULA for handling
1.36 +half-byte pixel data generally.
1.37 +
1.38 Enhancement: The Missing MODE 4
1.39 -------------------------------
1.40
2.1 --- a/ula.py Sun Jun 19 17:09:31 2016 +0200
2.2 +++ b/ula.py Sun Jun 19 21:21:28 2016 +0200
2.3 @@ -526,12 +526,17 @@
2.4 mode.
2.5 """
2.6
2.7 - byte_value = self.data # which should have been read automatically
2.8 + # Rearrange the byte value.
2.9 +
2.10 + byte_value = decode(self.data, self.depth)
2.11
2.12 i = 0
2.13 - for colour in decode(byte_value, self.depth):
2.14 - self.buffer[i] = get_physical_colour(self.palette[colour])
2.15 - i += 1
2.16 + o = 0
2.17 + while i < 8:
2.18 + colour = value_of_bits(byte_value[i:i+self.depth])
2.19 + self.buffer[o] = get_physical_colour(self.palette[colour])
2.20 + i += self.depth
2.21 + o += 1
2.22
2.23 def wrap_address(self):
2.24 if self.address >= SCREEN_LIMIT:
2.25 @@ -551,6 +556,16 @@
2.26 if self.cpu_read:
2.27 self.cpu_data = self.data | self.ram.data
2.28
2.29 +def value_of_bits(bits):
2.30 +
2.31 + "Convert the sequence of 'bits' into a value."
2.32 +
2.33 + value = 0
2.34 + for bit in bits:
2.35 + value *= 2
2.36 + value += bit and 1 or 0
2.37 + return value
2.38 +
2.39 def get_physical_colour(value):
2.40
2.41 """
2.42 @@ -570,11 +585,11 @@
2.43 return (value >> 7, value >> 6 & 1, value >> 5 & 1, value >> 4 & 1,
2.44 value >> 3 & 1, value >> 2 & 1, value >> 1 & 1, value & 1)
2.45 elif depth == 2:
2.46 - return (value >> 6 & 2 | value >> 3 & 1, value >> 5 & 2 | value >> 2 & 1,
2.47 - value >> 4 & 2 | value >> 1 & 1, value >> 3 & 2 | value & 1)
2.48 + return (value >> 7, value >> 3 & 1, value >> 6 & 1, value >> 2 & 1,
2.49 + value >> 5 & 1, value >> 1 & 1, value >> 4 & 1, value & 1)
2.50 elif depth == 4:
2.51 - return (value >> 4 & 8 | value >> 3 & 4 | value >> 2 & 2 | value >> 1 & 1,
2.52 - value >> 3 & 8 | value >> 2 & 4 | value >> 1 & 2 | value & 1)
2.53 + return (value >> 7, value >> 5 & 1, value >> 3 & 1, value >> 1 & 1,
2.54 + value >> 6 & 1, value >> 4 & 1, value >> 2 & 1, value & 1)
2.55 else:
2.56 raise ValueError, "Only depths of 1, 2 and 4 are supported, not %d." % depth
2.57