# HG changeset patch # User Paul Boddie # Date 1466441692 -7200 # Node ID e116a929dcdf0bc6604c0b593ac729842b54f84b # Parent 59d7fe6a9ba069922c713917213d6aac8258ca07 Introduced positive and negative signal transition update methods in order to separate state transitions from other operations, simplifying the pixel cycle management slightly. diff -r 59d7fe6a9ba0 -r e116a929dcdf ula.py --- a/ula.py Mon Jun 20 17:50:34 2016 +0200 +++ b/ula.py Mon Jun 20 18:54:52 2016 +0200 @@ -53,8 +53,9 @@ i = 0 limit = MAX_SCANLINE * MAX_SCANPOS while i < limit: - ula.update() + ula.posedge() video.update() + ula.negedge() i += 1 return video.screen @@ -303,15 +304,16 @@ def read_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame() def write_pixels(self): return self.pcycle != 0 def next_pixel(self): return self.xscale == 1 or (self.xscale == 2 and self.cycle & 0b10101010) or (self.xscale == 4 and self.cycle & 0b10001000) - def end_pixels(self): return self.pcycle == 1 - def update(self): + def posedge(self): """ Update the state of the ULA for each clock cycle. This involves updating the pixel colour by reading from the pixel buffer. """ + # Video signalling. + # Detect the end of the scanline. if self.x == MAX_SCANPOS: @@ -324,6 +326,28 @@ + # Detect any sync conditions. + + if self.x == 0: + self.hsync() + if self.y == 0: + self.vsync() + self.irq_vsync = 0 + elif self.y == MAX_PIXELLINE: + self.irq_vsync = 1 + + # Detect the end of hsync. + + elif self.x == MAX_HSYNC: + self.hsync(1) + + # Detect the end of vsync. + + elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2: + self.vsync(1) + + + # Clock management. would_access_ram = self.access == 0 and self.read_pixels() and self.in_line() @@ -421,56 +445,13 @@ self.access = (self.access + 1) % self.access_frequency - # Initialise the pixel buffer if appropriate. - - if self.have_pixels: - self.pdata = decode(self.data, self.depth) - self.pcycle = 1 - self.have_pixels = 0 - - - - # Video signalling. - - # Detect any sync conditions. - - if self.x == 0: - self.hsync() - if self.y == 0: - self.vsync() - self.irq_vsync = 0 - elif self.y == MAX_PIXELLINE: - self.irq_vsync = 1 - - # Detect the end of hsync. - - elif self.x == MAX_HSYNC: - self.hsync(1) - - # Detect the end of vsync. - - elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2: - self.vsync(1) - - - - # Update the state of the device. - - self.cycle = rotate(self.cycle, 1) - self.x += 1 - # Pixel production. - # Detect spacing between character rows. - - if not self.write_pixels(): - self.video.colour = BLANK - # For pixels within the frame, obtain and output the value. - else: + if self.write_pixels(): self.output_colour_value() # Scale pixels horizontally, only accessing the next pixel value @@ -479,12 +460,27 @@ if self.next_pixel(): self.next_pixel_value() - # Finish writing pixels. + # Detect spacing between character rows. + + else: + self.video.colour = BLANK + + def negedge(self): + + "Update the state of the device." - if self.end_pixels(): - self.pcycle = 0 + # Initialise the pixel buffer if appropriate. Output starts after + # this cycle. + if self.cycle == 128 and self.have_pixels: + self.pdata = decode(self.data, self.depth) + self.pcycle = 1 + self.have_pixels = 0 + # Start a new cycle. + + self.cycle = rotate(self.cycle, 1) + self.x += 1 def output_colour_value(self): @@ -498,7 +494,7 @@ def next_pixel_value(self): self.pdata = rotate(self.pdata, self.depth) - self.pcycle = rotate(self.pcycle, self.depth) + self.pcycle = rotate(self.pcycle, self.depth, zero=True) def wrap_address(self): if self.address >= SCREEN_LIMIT: @@ -518,18 +514,19 @@ if self.cpu_read: self.cpu_data = self.data | self.ram.data -def rotate(value, depth, width=8): +def rotate(value, depth, width=8, zero=False): """ - Return 'value' rotated by the number of bits given by 'depth' within a - storage 'width' given in bits. + Return 'value' rotated left by the number of bits given by 'depth', doing so + within a value 'width' given in bits. If 'zero' is true, rotate zero bits + into the lower bits when rotating. """ field = width - depth top = value >> field mask = 2 ** (width - depth) - 1 rest = value & mask - return (rest << depth) | top + return (rest << depth) | (not zero and top or 0) def value_of_bits(value, depth):