# HG changeset patch # User Paul Boddie # Date 1466518007 -7200 # Node ID 13c8893af8c79a467db6ce3927b4c0bfdf55caca # Parent 87b0ccdb9bc4c9eac9f5b17e76a78e537ce5c393 Shifted the timing states so that cycle 0 is aligned with the positive edge of any 2MHz CPU clock. Made methods for various access timing tests and replaced the distinct access variable with a test against the horizontal pixel counter (effectively using it as a 2MHz cycle counter). diff -r 87b0ccdb9bc4 -r 13c8893af8c7 ULA.txt --- a/ULA.txt Tue Jun 21 14:34:11 2016 +0200 +++ b/ULA.txt Tue Jun 21 16:06:47 2016 +0200 @@ -52,21 +52,21 @@ 2 MHz cycle: 0 1 ... 16 MHz cycle: 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 ... /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ ... - ~RAS: --\___________/---\___________/- ... - ~CAS: ----\___/-\___/-----\___/-\___/- ... - A B C A B C ... - F S F S ... - a b c a b c ... - f s f s ... + ~RAS: /---\___________/---\___________ ... + ~CAS: /-----\___/-\___/-----\___/-\___ ... + A B C A B C ... + F S F S ... + a b c a b c ... + s f s f ... - ~WE: ......W ... - PHI OUT: ______________/---------------\ ... - CPU (RAM): D L ... - RnW: R ... + ~WE: ......W ... + PHI OUT: \_______________/--------------- ... + CPU (RAM): L D ... + RnW: R ... - PHI OUT: ______/-------\_______/-------\ ... - CPU (ROM): D L D L ... - RnW: R R ... + PHI OUT: \_______/-------\_______/------- ... + CPU (ROM): L D L D ... + RnW: R R ... Here, "A" and "B" respectively indicate the row and first column addresses being latched into the RAM (on a negative edge for ~RAS and ~CAS diff -r 87b0ccdb9bc4 -r 13c8893af8c7 ula.py --- a/ula.py Tue Jun 21 14:34:11 2016 +0200 +++ b/ula.py Tue Jun 21 16:06:47 2016 +0200 @@ -158,6 +158,7 @@ def __init__(self): self.address = 0x1000 + self.data = 0 self.read_not_write = 1 class ULA: @@ -206,7 +207,6 @@ # Internal state. - self.access = 0 # counter used to determine whether a byte needs reading self.have_pixels = 0 # whether pixel data has been read self.pdata = 0 # decoded RAM data for pixel output self.cycle = 1 # 8-state counter within each 2MHz period @@ -321,21 +321,27 @@ self.line_start = self.pixel_address + def access_cycle(self): return (self.x / 8) % self.access_frequency == 0 + def would_access_ram(self): return self.access_cycle() and self.read_pixels() and self.in_line() + def access_ram(self): return not self.nmi and self.would_access_ram() def in_line(self): return self.line < LINES_PER_ROW def in_frame(self): return MIN_PIXELLINE <= self.y < (MIN_PIXELLINE + self.display_height) def inside_frame(self): return MIN_PIXELLINE < self.y < (MIN_PIXELLINE + self.display_height) 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 next_pixel(self): return self.xscale == 1 or (self.xscale == 2 and self.cycle & 0b01010101) or (self.xscale == 4 and self.cycle & 0b00010001) 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. - """ + "Update the state of the ULA for each clock cycle." - # Video signalling. + self.posedge_video() + self.posedge_ram() + self.posedge_pixel() + + def posedge_video(self): + + "Video signalling." # Detect the end of the scanline. @@ -347,8 +353,6 @@ if self.y == MAX_SCANLINE: self.next_frame() - - # Detect any sync conditions. if self.x == 0: @@ -369,27 +373,37 @@ elif self.y == MAX_CSYNC and self.x == MAX_SCANPOS / 2: self.vsync(1) + def posedge_ram(self): + "RAM signalling." # Clock management. - would_access_ram = self.access == 0 and self.read_pixels() and self.in_line() - access_ram = not self.nmi and would_access_ram + # Reset addresses. + + if self.cycle == 1: + self.ram.column_deselect() + self.ram.row_deselect() + + # Read the CPU address, if appropriate. + + if not self.access_ram(): + self.cpu_update_clock() # Set row address (for ULA access only). - if self.cycle == 1: + elif self.cycle == 2: # Either assert a required address or propagate the CPU address. - if access_ram: + if self.access_ram(): self.init_row_address(self.pixel_address) else: self.init_row_address(self.cpu_address) # Latch row address, set column address (for ULA access only). - elif self.cycle == 2: + elif self.cycle == 4: # Select an address needed by the ULA or CPU. @@ -397,14 +411,14 @@ # Either assert a required address or propagate the CPU address. - if access_ram: + if self.access_ram(): self.init_column_address(self.pixel_address, 0) else: self.init_column_address(self.cpu_address, 0) # Latch column address. - elif self.cycle == 4: + elif self.cycle == 8: # Select an address needed by the ULA or CPU. @@ -412,7 +426,7 @@ # Assert the RAM write enable if appropriate. - if access_ram: + if self.access_ram(): self.ram.read_select() else: self.cpu_transfer_select() @@ -421,19 +435,19 @@ # Set column address (for ULA access only). - elif self.cycle == 16: + elif self.cycle == 32: self.ram.column_deselect() # Either assert a required address or propagate the CPU address. - if access_ram: + if self.access_ram(): self.init_column_address(self.pixel_address, 1) else: self.init_column_address(self.cpu_address, 1) # Latch column address. - elif self.cycle == 32: + elif self.cycle == 64: # Select an address needed by the ULA or CPU. @@ -441,31 +455,16 @@ # Read 4 bits (for ULA access only). - elif self.cycle == 64: + elif self.cycle == 128: # Advance to the next column even if an NMI is asserted. - if would_access_ram: + if self.would_access_ram(): self.next_horizontal() - # Reset addresses. - - elif self.cycle == 128: - self.ram.column_deselect() - self.ram.row_deselect() - - # Update the RAM access controller. + def posedge_pixel(self): - self.access = (self.access + 1) % self.access_frequency - - # Read the CPU address, if appropriate. - - if not access_ram: - self.cpu_update_clock() - - - - # Pixel production. + "Pixel production." # For pixels within the frame, obtain and output the value. @@ -488,45 +487,42 @@ """ Update the state of the device. - Cycles handled: _ _ _ * _ _ * * + Cycles handled: * _ _ _ * _ _ * """ # Clock management. - would_access_ram = self.access == 0 and self.read_pixels() and self.in_line() - access_ram = not self.nmi and would_access_ram + # Initialise the pixel buffer if appropriate. Output starts after + # this cycle. + + if self.cycle == 1 and self.have_pixels: + self.pdata = decode(self.data, self.depth) + self.pcycle = 1 + self.have_pixels = 0 # Read 4 bits (for ULA access only). - if self.cycle == 8: + elif self.cycle == 16: # Either read from a required address or transfer CPU data. - if access_ram: + if self.access_ram(): self.data = self.ram.data << 4 else: self.cpu_transfer_high() # Read 4 bits (for ULA access only). - elif self.cycle == 64: + elif self.cycle == 128: # Either read from a required address or transfer CPU data. - if access_ram: + if self.access_ram(): self.data = self.data | self.ram.data self.have_pixels = 1 else: self.cpu_transfer_low() - # Initialise the pixel buffer if appropriate. Output starts after - # this cycle. - - elif 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)