1.1 --- a/ula.py Mon Jun 20 17:50:34 2016 +0200
1.2 +++ b/ula.py Mon Jun 20 18:54:52 2016 +0200
1.3 @@ -53,8 +53,9 @@
1.4 i = 0
1.5 limit = MAX_SCANLINE * MAX_SCANPOS
1.6 while i < limit:
1.7 - ula.update()
1.8 + ula.posedge()
1.9 video.update()
1.10 + ula.negedge()
1.11 i += 1
1.12
1.13 return video.screen
1.14 @@ -303,15 +304,16 @@
1.15 def read_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame()
1.16 def write_pixels(self): return self.pcycle != 0
1.17 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.18 - def end_pixels(self): return self.pcycle == 1
1.19
1.20 - def update(self):
1.21 + def posedge(self):
1.22
1.23 """
1.24 Update the state of the ULA for each clock cycle. This involves updating
1.25 the pixel colour by reading from the pixel buffer.
1.26 """
1.27
1.28 + # Video signalling.
1.29 +
1.30 # Detect the end of the scanline.
1.31
1.32 if self.x == MAX_SCANPOS:
1.33 @@ -324,6 +326,28 @@
1.34
1.35
1.36
1.37 + # Detect any sync conditions.
1.38 +
1.39 + if self.x == 0:
1.40 + self.hsync()
1.41 + if self.y == 0:
1.42 + self.vsync()
1.43 + self.irq_vsync = 0
1.44 + elif self.y == MAX_PIXELLINE:
1.45 + self.irq_vsync = 1
1.46 +
1.47 + # Detect the end of hsync.
1.48 +
1.49 + elif self.x == MAX_HSYNC:
1.50 + self.hsync(1)
1.51 +
1.52 + # Detect the end of vsync.
1.53 +
1.54 + elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2:
1.55 + self.vsync(1)
1.56 +
1.57 +
1.58 +
1.59 # Clock management.
1.60
1.61 would_access_ram = self.access == 0 and self.read_pixels() and self.in_line()
1.62 @@ -421,56 +445,13 @@
1.63
1.64 self.access = (self.access + 1) % self.access_frequency
1.65
1.66 - # Initialise the pixel buffer if appropriate.
1.67 -
1.68 - if self.have_pixels:
1.69 - self.pdata = decode(self.data, self.depth)
1.70 - self.pcycle = 1
1.71 - self.have_pixels = 0
1.72 -
1.73 -
1.74 -
1.75 - # Video signalling.
1.76 -
1.77 - # Detect any sync conditions.
1.78 -
1.79 - if self.x == 0:
1.80 - self.hsync()
1.81 - if self.y == 0:
1.82 - self.vsync()
1.83 - self.irq_vsync = 0
1.84 - elif self.y == MAX_PIXELLINE:
1.85 - self.irq_vsync = 1
1.86 -
1.87 - # Detect the end of hsync.
1.88 -
1.89 - elif self.x == MAX_HSYNC:
1.90 - self.hsync(1)
1.91 -
1.92 - # Detect the end of vsync.
1.93 -
1.94 - elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2:
1.95 - self.vsync(1)
1.96 -
1.97 -
1.98 -
1.99 - # Update the state of the device.
1.100 -
1.101 - self.cycle = rotate(self.cycle, 1)
1.102 - self.x += 1
1.103 -
1.104
1.105
1.106 # Pixel production.
1.107
1.108 - # Detect spacing between character rows.
1.109 -
1.110 - if not self.write_pixels():
1.111 - self.video.colour = BLANK
1.112 -
1.113 # For pixels within the frame, obtain and output the value.
1.114
1.115 - else:
1.116 + if self.write_pixels():
1.117 self.output_colour_value()
1.118
1.119 # Scale pixels horizontally, only accessing the next pixel value
1.120 @@ -479,12 +460,27 @@
1.121 if self.next_pixel():
1.122 self.next_pixel_value()
1.123
1.124 - # Finish writing pixels.
1.125 + # Detect spacing between character rows.
1.126 +
1.127 + else:
1.128 + self.video.colour = BLANK
1.129 +
1.130 + def negedge(self):
1.131 +
1.132 + "Update the state of the device."
1.133
1.134 - if self.end_pixels():
1.135 - self.pcycle = 0
1.136 + # Initialise the pixel buffer if appropriate. Output starts after
1.137 + # this cycle.
1.138
1.139 + if self.cycle == 128 and self.have_pixels:
1.140 + self.pdata = decode(self.data, self.depth)
1.141 + self.pcycle = 1
1.142 + self.have_pixels = 0
1.143
1.144 + # Start a new cycle.
1.145 +
1.146 + self.cycle = rotate(self.cycle, 1)
1.147 + self.x += 1
1.148
1.149 def output_colour_value(self):
1.150
1.151 @@ -498,7 +494,7 @@
1.152
1.153 def next_pixel_value(self):
1.154 self.pdata = rotate(self.pdata, self.depth)
1.155 - self.pcycle = rotate(self.pcycle, self.depth)
1.156 + self.pcycle = rotate(self.pcycle, self.depth, zero=True)
1.157
1.158 def wrap_address(self):
1.159 if self.address >= SCREEN_LIMIT:
1.160 @@ -518,18 +514,19 @@
1.161 if self.cpu_read:
1.162 self.cpu_data = self.data | self.ram.data
1.163
1.164 -def rotate(value, depth, width=8):
1.165 +def rotate(value, depth, width=8, zero=False):
1.166
1.167 """
1.168 - Return 'value' rotated by the number of bits given by 'depth' within a
1.169 - storage 'width' given in bits.
1.170 + Return 'value' rotated left by the number of bits given by 'depth', doing so
1.171 + within a value 'width' given in bits. If 'zero' is true, rotate zero bits
1.172 + into the lower bits when rotating.
1.173 """
1.174
1.175 field = width - depth
1.176 top = value >> field
1.177 mask = 2 ** (width - depth) - 1
1.178 rest = value & mask
1.179 - return (rest << depth) | top
1.180 + return (rest << depth) | (not zero and top or 0)
1.181
1.182 def value_of_bits(value, depth):
1.183