1.1 --- a/ULA.txt Tue Jun 21 14:34:11 2016 +0200
1.2 +++ b/ULA.txt Tue Jun 21 16:06:47 2016 +0200
1.3 @@ -52,21 +52,21 @@
1.4 2 MHz cycle: 0 1 ...
1.5 16 MHz cycle: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 ...
1.6 /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ...
1.7 - ~RAS: --\___________/---\___________/- ...
1.8 - ~CAS: ----\___/-\___/-----\___/-\___/- ...
1.9 - A B C A B C ...
1.10 - F S F S ...
1.11 - a b c a b c ...
1.12 - f s f s ...
1.13 + ~RAS: /---\___________/---\___________ ...
1.14 + ~CAS: /-----\___/-\___/-----\___/-\___ ...
1.15 + A B C A B C ...
1.16 + F S F S ...
1.17 + a b c a b c ...
1.18 + s f s f ...
1.19
1.20 - ~WE: ......W ...
1.21 - PHI OUT: ______________/---------------\ ...
1.22 - CPU (RAM): D L ...
1.23 - RnW: R ...
1.24 + ~WE: ......W ...
1.25 + PHI OUT: \_______________/--------------- ...
1.26 + CPU (RAM): L D ...
1.27 + RnW: R ...
1.28
1.29 - PHI OUT: ______/-------\_______/-------\ ...
1.30 - CPU (ROM): D L D L ...
1.31 - RnW: R R ...
1.32 + PHI OUT: \_______/-------\_______/------- ...
1.33 + CPU (ROM): L D L D ...
1.34 + RnW: R R ...
1.35
1.36 Here, "A" and "B" respectively indicate the row and first column addresses
1.37 being latched into the RAM (on a negative edge for ~RAS and ~CAS
2.1 --- a/ula.py Tue Jun 21 14:34:11 2016 +0200
2.2 +++ b/ula.py Tue Jun 21 16:06:47 2016 +0200
2.3 @@ -158,6 +158,7 @@
2.4
2.5 def __init__(self):
2.6 self.address = 0x1000
2.7 + self.data = 0
2.8 self.read_not_write = 1
2.9
2.10 class ULA:
2.11 @@ -206,7 +207,6 @@
2.12
2.13 # Internal state.
2.14
2.15 - self.access = 0 # counter used to determine whether a byte needs reading
2.16 self.have_pixels = 0 # whether pixel data has been read
2.17 self.pdata = 0 # decoded RAM data for pixel output
2.18 self.cycle = 1 # 8-state counter within each 2MHz period
2.19 @@ -321,21 +321,27 @@
2.20
2.21 self.line_start = self.pixel_address
2.22
2.23 + def access_cycle(self): return (self.x / 8) % self.access_frequency == 0
2.24 + def would_access_ram(self): return self.access_cycle() and self.read_pixels() and self.in_line()
2.25 + def access_ram(self): return not self.nmi and self.would_access_ram()
2.26 def in_line(self): return self.line < LINES_PER_ROW
2.27 def in_frame(self): return MIN_PIXELLINE <= self.y < (MIN_PIXELLINE + self.display_height)
2.28 def inside_frame(self): return MIN_PIXELLINE < self.y < (MIN_PIXELLINE + self.display_height)
2.29 def read_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame()
2.30 def write_pixels(self): return self.pcycle != 0
2.31 - def next_pixel(self): return self.xscale == 1 or (self.xscale == 2 and self.cycle & 0b10101010) or (self.xscale == 4 and self.cycle & 0b10001000)
2.32 + def next_pixel(self): return self.xscale == 1 or (self.xscale == 2 and self.cycle & 0b01010101) or (self.xscale == 4 and self.cycle & 0b00010001)
2.33
2.34 def posedge(self):
2.35
2.36 - """
2.37 - Update the state of the ULA for each clock cycle. This involves updating
2.38 - the pixel colour by reading from the pixel buffer.
2.39 - """
2.40 + "Update the state of the ULA for each clock cycle."
2.41
2.42 - # Video signalling.
2.43 + self.posedge_video()
2.44 + self.posedge_ram()
2.45 + self.posedge_pixel()
2.46 +
2.47 + def posedge_video(self):
2.48 +
2.49 + "Video signalling."
2.50
2.51 # Detect the end of the scanline.
2.52
2.53 @@ -347,8 +353,6 @@
2.54 if self.y == MAX_SCANLINE:
2.55 self.next_frame()
2.56
2.57 -
2.58 -
2.59 # Detect any sync conditions.
2.60
2.61 if self.x == 0:
2.62 @@ -369,27 +373,37 @@
2.63 elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2:
2.64 self.vsync(1)
2.65
2.66 + def posedge_ram(self):
2.67
2.68 + "RAM signalling."
2.69
2.70 # Clock management.
2.71
2.72 - would_access_ram = self.access == 0 and self.read_pixels() and self.in_line()
2.73 - access_ram = not self.nmi and would_access_ram
2.74 + # Reset addresses.
2.75 +
2.76 + if self.cycle == 1:
2.77 + self.ram.column_deselect()
2.78 + self.ram.row_deselect()
2.79 +
2.80 + # Read the CPU address, if appropriate.
2.81 +
2.82 + if not self.access_ram():
2.83 + self.cpu_update_clock()
2.84
2.85 # Set row address (for ULA access only).
2.86
2.87 - if self.cycle == 1:
2.88 + elif self.cycle == 2:
2.89
2.90 # Either assert a required address or propagate the CPU address.
2.91
2.92 - if access_ram:
2.93 + if self.access_ram():
2.94 self.init_row_address(self.pixel_address)
2.95 else:
2.96 self.init_row_address(self.cpu_address)
2.97
2.98 # Latch row address, set column address (for ULA access only).
2.99
2.100 - elif self.cycle == 2:
2.101 + elif self.cycle == 4:
2.102
2.103 # Select an address needed by the ULA or CPU.
2.104
2.105 @@ -397,14 +411,14 @@
2.106
2.107 # Either assert a required address or propagate the CPU address.
2.108
2.109 - if access_ram:
2.110 + if self.access_ram():
2.111 self.init_column_address(self.pixel_address, 0)
2.112 else:
2.113 self.init_column_address(self.cpu_address, 0)
2.114
2.115 # Latch column address.
2.116
2.117 - elif self.cycle == 4:
2.118 + elif self.cycle == 8:
2.119
2.120 # Select an address needed by the ULA or CPU.
2.121
2.122 @@ -412,7 +426,7 @@
2.123
2.124 # Assert the RAM write enable if appropriate.
2.125
2.126 - if access_ram:
2.127 + if self.access_ram():
2.128 self.ram.read_select()
2.129 else:
2.130 self.cpu_transfer_select()
2.131 @@ -421,19 +435,19 @@
2.132
2.133 # Set column address (for ULA access only).
2.134
2.135 - elif self.cycle == 16:
2.136 + elif self.cycle == 32:
2.137 self.ram.column_deselect()
2.138
2.139 # Either assert a required address or propagate the CPU address.
2.140
2.141 - if access_ram:
2.142 + if self.access_ram():
2.143 self.init_column_address(self.pixel_address, 1)
2.144 else:
2.145 self.init_column_address(self.cpu_address, 1)
2.146
2.147 # Latch column address.
2.148
2.149 - elif self.cycle == 32:
2.150 + elif self.cycle == 64:
2.151
2.152 # Select an address needed by the ULA or CPU.
2.153
2.154 @@ -441,31 +455,16 @@
2.155
2.156 # Read 4 bits (for ULA access only).
2.157
2.158 - elif self.cycle == 64:
2.159 + elif self.cycle == 128:
2.160
2.161 # Advance to the next column even if an NMI is asserted.
2.162
2.163 - if would_access_ram:
2.164 + if self.would_access_ram():
2.165 self.next_horizontal()
2.166
2.167 - # Reset addresses.
2.168 -
2.169 - elif self.cycle == 128:
2.170 - self.ram.column_deselect()
2.171 - self.ram.row_deselect()
2.172 -
2.173 - # Update the RAM access controller.
2.174 + def posedge_pixel(self):
2.175
2.176 - self.access = (self.access + 1) % self.access_frequency
2.177 -
2.178 - # Read the CPU address, if appropriate.
2.179 -
2.180 - if not access_ram:
2.181 - self.cpu_update_clock()
2.182 -
2.183 -
2.184 -
2.185 - # Pixel production.
2.186 + "Pixel production."
2.187
2.188 # For pixels within the frame, obtain and output the value.
2.189
2.190 @@ -488,45 +487,42 @@
2.191 """
2.192 Update the state of the device.
2.193
2.194 - Cycles handled: _ _ _ * _ _ * *
2.195 + Cycles handled: * _ _ _ * _ _ *
2.196 """
2.197
2.198 # Clock management.
2.199
2.200 - would_access_ram = self.access == 0 and self.read_pixels() and self.in_line()
2.201 - access_ram = not self.nmi and would_access_ram
2.202 + # Initialise the pixel buffer if appropriate. Output starts after
2.203 + # this cycle.
2.204 +
2.205 + if self.cycle == 1 and self.have_pixels:
2.206 + self.pdata = decode(self.data, self.depth)
2.207 + self.pcycle = 1
2.208 + self.have_pixels = 0
2.209
2.210 # Read 4 bits (for ULA access only).
2.211
2.212 - if self.cycle == 8:
2.213 + elif self.cycle == 16:
2.214
2.215 # Either read from a required address or transfer CPU data.
2.216
2.217 - if access_ram:
2.218 + if self.access_ram():
2.219 self.data = self.ram.data << 4
2.220 else:
2.221 self.cpu_transfer_high()
2.222
2.223 # Read 4 bits (for ULA access only).
2.224
2.225 - elif self.cycle == 64:
2.226 + elif self.cycle == 128:
2.227
2.228 # Either read from a required address or transfer CPU data.
2.229
2.230 - if access_ram:
2.231 + if self.access_ram():
2.232 self.data = self.data | self.ram.data
2.233 self.have_pixels = 1
2.234 else:
2.235 self.cpu_transfer_low()
2.236
2.237 - # Initialise the pixel buffer if appropriate. Output starts after
2.238 - # this cycle.
2.239 -
2.240 - elif self.cycle == 128 and self.have_pixels:
2.241 - self.pdata = decode(self.data, self.depth)
2.242 - self.pcycle = 1
2.243 - self.have_pixels = 0
2.244 -
2.245 # Start a new cycle.
2.246
2.247 self.cycle = rotate(self.cycle, 1)