1.1 --- a/ula.py Mon Jun 20 15:40:58 2016 +0200
1.2 +++ b/ula.py Mon Jun 20 16:57:12 2016 +0200
1.3 @@ -144,33 +144,6 @@
1.4 self.memory[i << 1] = value >> 4
1.5 self.memory[i << 1 | 0x1] = value & 0xf
1.6
1.7 -class ShiftRegister:
1.8 -
1.9 - """
1.10 - A class representing a shift register, used for the internal state of the
1.11 - ULA within each 2MHz period.
1.12 - """
1.13 -
1.14 - def __init__(self):
1.15 - self.state = [0] * 8
1.16 - self.input = 0
1.17 -
1.18 - def set_input(self, input):
1.19 - self.input = input
1.20 -
1.21 - def shift(self):
1.22 -
1.23 - # NOTE: This is not meant to be "nice" Python, but instead models the
1.24 - # NOTE: propagation of state through the latches.
1.25 -
1.26 - self.state[0], self.state[1], self.state[2], self.state[3], \
1.27 - self.state[4], self.state[5], self.state[6], self.state[7] = \
1.28 - self.input, self.state[0], self.state[1], self.state[2], \
1.29 - self.state[3], self.state[4], self.state[5], self.state[6]
1.30 -
1.31 - def __getitem__(self, i):
1.32 - return self.state[i]
1.33 -
1.34 class ULA:
1.35
1.36 """
1.37 @@ -217,14 +190,9 @@
1.38
1.39 self.access = 0 # counter used to determine whether a byte needs reading
1.40 self.have_pixels = 0 # whether pixel data has been read
1.41 - self.writing_pixels = 0 # whether pixel data can be written
1.42 - self.pdata = [0]*8 # decoded RAM data for pixel output
1.43 -
1.44 - self.cycle = ShiftRegister() # 8-state counter within each 2MHz period
1.45 -
1.46 - self.cycle.set_input(1) # assert the input to set the first state output
1.47 - self.cycle.shift()
1.48 - self.cycle.set_input(0) # reset the input since only one state output will be active
1.49 + self.pdata = 0 # decoded RAM data for pixel output
1.50 + self.cycle = 1 # 8-state counter within each 2MHz period
1.51 + self.pcycle = 0 # 8/4/2-state pixel output counter
1.52
1.53 self.next_frame()
1.54
1.55 @@ -336,6 +304,8 @@
1.56 def in_frame(self): return MIN_PIXELLINE <= self.y < (MIN_PIXELLINE + self.display_height)
1.57 def inside_frame(self): return MIN_PIXELLINE < self.y < (MIN_PIXELLINE + self.display_height)
1.58 def read_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame()
1.59 + def end_pixels(self): return self.pcycle == 1
1.60 + def next_pixel(self): return self.xscale == 1 or (self.xscale == 2 and self.cycle & 0b10101010) or (self.xscale == 4 and self.cycle & 0b10001000)
1.61
1.62 def update(self):
1.63
1.64 @@ -363,7 +333,7 @@
1.65
1.66 # Set row address (for ULA access only).
1.67
1.68 - if self.cycle[0]:
1.69 + if self.cycle == 1:
1.70
1.71 # Either assert a required address or propagate the CPU address.
1.72
1.73 @@ -372,15 +342,9 @@
1.74 else:
1.75 self.init_row_address(self.cpu_address)
1.76
1.77 - # Initialise the pixel buffer if appropriate.
1.78 -
1.79 - if self.have_pixels:
1.80 - self.pdata = decode(self.data, self.depth)
1.81 - self.writing_pixels = 1
1.82 -
1.83 # Latch row address, set column address (for ULA access only).
1.84
1.85 - elif self.cycle[1]:
1.86 + elif self.cycle == 2:
1.87
1.88 # Select an address needed by the ULA or CPU.
1.89
1.90 @@ -395,16 +359,15 @@
1.91
1.92 # Latch column address.
1.93
1.94 - elif self.cycle[2]:
1.95 + elif self.cycle == 4:
1.96
1.97 # Select an address needed by the ULA or CPU.
1.98
1.99 self.ram.column_select(self.ram_address)
1.100
1.101 # Read 4 bits (for ULA access only).
1.102 - # NOTE: Perhaps map alternate bits, not half-bytes.
1.103
1.104 - elif self.cycle[3]:
1.105 + elif self.cycle == 8:
1.106
1.107 # Either read from a required address or transfer CPU data.
1.108
1.109 @@ -415,7 +378,7 @@
1.110
1.111 # Set column address (for ULA access only).
1.112
1.113 - elif self.cycle[4]:
1.114 + elif self.cycle == 16:
1.115 self.ram.column_deselect()
1.116
1.117 # Either assert a required address or propagate the CPU address.
1.118 @@ -427,16 +390,15 @@
1.119
1.120 # Latch column address.
1.121
1.122 - elif self.cycle[5]:
1.123 + elif self.cycle == 32:
1.124
1.125 # Select an address needed by the ULA or CPU.
1.126
1.127 self.ram.column_select(self.ram_address)
1.128
1.129 # Read 4 bits (for ULA access only).
1.130 - # NOTE: Perhaps map alternate bits, not half-bytes.
1.131
1.132 - elif self.cycle[6]:
1.133 + elif self.cycle == 64:
1.134
1.135 # Either read from a required address or transfer CPU data.
1.136
1.137 @@ -453,7 +415,7 @@
1.138
1.139 # Reset addresses.
1.140
1.141 - elif self.cycle[7]:
1.142 + elif self.cycle == 128:
1.143 self.ram.column_deselect()
1.144 self.ram.row_deselect()
1.145
1.146 @@ -461,10 +423,12 @@
1.147
1.148 self.access = (self.access + 1) % self.access_frequency
1.149
1.150 - # Update the state of the device.
1.151 + # Initialise the pixel buffer if appropriate.
1.152
1.153 - self.cycle.set_input(self.cycle[7])
1.154 - self.cycle.shift()
1.155 + if self.have_pixels:
1.156 + self.pdata = decode(self.data, self.depth)
1.157 + self.pcycle = 1
1.158 + self.have_pixels = 0
1.159
1.160
1.161
1.162 @@ -491,14 +455,19 @@
1.163 self.vsync(1)
1.164
1.165
1.166 +
1.167 + # Update the state of the device.
1.168 +
1.169 + self.cycle = rotate(self.cycle, 1)
1.170 self.x += 1
1.171
1.172
1.173 +
1.174 # Pixel production.
1.175
1.176 # Detect spacing between character rows.
1.177
1.178 - if not self.writing_pixels or not self.in_line():
1.179 + if not self.pcycle:
1.180 self.video.colour = BLANK
1.181
1.182 # For pixels within the frame, obtain and output the value.
1.183 @@ -509,13 +478,16 @@
1.184 # Scale pixels horizontally, only accessing the next pixel value
1.185 # after the required number of scan positions.
1.186
1.187 - if self.x % self.xscale == 0:
1.188 + if self.next_pixel():
1.189 self.next_pixel_value()
1.190 + self.pcycle = rotate(self.pcycle, self.depth)
1.191
1.192 - # Finish writing pixels.
1.193 + # Finish writing pixels.
1.194
1.195 - if self.x % PIXEL_POSITIONS == 0:
1.196 - self.writing_pixels = 0
1.197 + if self.end_pixels():
1.198 + self.pcycle = 0
1.199 +
1.200 +
1.201
1.202 def output_colour_value(self):
1.203
1.204 @@ -548,13 +520,16 @@
1.205 if self.cpu_read:
1.206 self.cpu_data = self.data | self.ram.data
1.207
1.208 -def rotate(value, depth):
1.209 -
1.210 - "Return 'value' rotated by the number of bits given by 'depth'."
1.211 +def rotate(value, depth, width=8):
1.212
1.213 - field = 8 - depth
1.214 + """
1.215 + Return 'value' rotated by the number of bits given by 'depth' within a
1.216 + storage 'width' given in bits.
1.217 + """
1.218 +
1.219 + field = width - depth
1.220 top = value >> field
1.221 - mask = 2 ** (8 - depth) - 1
1.222 + mask = 2 ** (width - depth) - 1
1.223 rest = value & mask
1.224 return (rest << depth) | top
1.225