1.1 --- a/ula.py Sun Dec 11 01:18:22 2011 +0100
1.2 +++ b/ula.py Mon Dec 12 23:27:59 2011 +0100
1.3 @@ -4,29 +4,31 @@
1.4 Acorn Electron ULA simulation.
1.5 """
1.6
1.7 -WIDTH = 640
1.8 -HEIGHT = 512
1.9 -INTENSITY = 255
1.10 -
1.11 -LINES_PER_ROW = 8
1.12 -MAX_HEIGHT = 256
1.13 -SCREEN_LIMIT = 0x8000
1.14 -MAX_MEMORY = 0x10000
1.15 +LINES_PER_ROW = 8 # the number of pixel lines per character row
1.16 +MAX_HEIGHT = 256 # the height of the screen in pixels
1.17 +MAX_SCANLINE = 312 # the number of scanlines in each frame
1.18 +MAX_WIDTH = 640 # the width of the screen in pixels
1.19 +MAX_SCANPOS = 1024 # the number of positions in each scanline
1.20 +SCREEN_LIMIT = 0x8000 # the first address after the screen memory
1.21 +MAX_MEMORY = 0x10000 # the number of addressable memory locations
1.22 BLANK = (0, 0, 0)
1.23
1.24 def update(screen, ula):
1.25
1.26 """
1.27 - Update the 'screen' array by reading from the 'ula'.
1.28 + Update the 'screen' array by reading from the 'ula'. This function
1.29 + effectively has the role of the video circuit, but also provides the clock
1.30 + signal to the ULA.
1.31 """
1.32
1.33 ula.vsync()
1.34 y = 0
1.35 - while y < HEIGHT:
1.36 + while y < MAX_SCANLINE:
1.37 x = 0
1.38 - while x < WIDTH:
1.39 + while x < MAX_SCANPOS:
1.40 colour = ula.get_pixel_colour()
1.41 - screen[x][y] = colour
1.42 + if x < MAX_WIDTH and y < MAX_HEIGHT:
1.43 + screen[x][y] = colour
1.44 x += 1
1.45 ula.hsync()
1.46 y += 1
1.47 @@ -66,14 +68,14 @@
1.48 * screen size in bytes
1.49 * default screen start address
1.50 * horizontal pixel scaling factor
1.51 - * vertical pixel scaling factor
1.52 * line spacing in pixels
1.53 * number of entries in the pixel buffer
1.54 """
1.55
1.56 self.width, self.depth, rows = ULA.modes[mode]
1.57
1.58 - row_size = (self.width * self.depth * LINES_PER_ROW) / 8 # bits per row -> bytes per row
1.59 + self.columns = (self.width * self.depth) / 8 # bits read -> bytes read
1.60 + row_size = self.columns * LINES_PER_ROW
1.61
1.62 # Memory access configuration.
1.63 # Note the limitation on positioning the screen start.
1.64 @@ -84,15 +86,13 @@
1.65
1.66 # Scanline configuration.
1.67
1.68 - self.xscale = WIDTH / self.width # pixel width in display pixels
1.69 - self.yscale = HEIGHT / (rows * LINES_PER_ROW) # pixel height in display pixels
1.70 -
1.71 + self.xscale = MAX_WIDTH / self.width # pixel width in display pixels
1.72 self.spacing = MAX_HEIGHT / rows - LINES_PER_ROW # pixels between rows
1.73
1.74 # Start of unused region.
1.75
1.76 self.footer = rows * LINES_PER_ROW
1.77 - self.margin = MAX_HEIGHT - rows * (LINES_PER_ROW + self.spacing) + self.spacing
1.78 + self.margin = MAX_SCANLINE - rows * (LINES_PER_ROW + self.spacing) + self.spacing
1.79
1.80 # Internal pixel buffer size.
1.81
1.82 @@ -104,7 +104,6 @@
1.83
1.84 self.line_start = self.address = self.screen_start
1.85 self.line = self.line_start % LINES_PER_ROW
1.86 - self.ysub = 0
1.87 self.ssub = 0
1.88 self.reset_horizontal()
1.89
1.90 @@ -113,6 +112,7 @@
1.91 "Reset horizontal state."
1.92
1.93 self.xsub = 0
1.94 + self.column = 0
1.95 self.buffer_index = self.buffer_limit # need refill
1.96
1.97 def hsync(self):
1.98 @@ -126,20 +126,6 @@
1.99 return
1.100
1.101 self.reset_horizontal()
1.102 -
1.103 - # Scale pixels vertically.
1.104 -
1.105 - self.ysub += 1
1.106 -
1.107 - # Re-read the current line if appropriate.
1.108 -
1.109 - if self.ysub < self.yscale:
1.110 - self.address = self.line_start
1.111 - return
1.112 -
1.113 - # Otherwise, move on to the next line.
1.114 -
1.115 - self.ysub = 0
1.116 self.line += 1
1.117
1.118 # If not on a row boundary, move to the next line.
1.119 @@ -158,12 +144,12 @@
1.120 # Test for the footer region.
1.121
1.122 if self.spacing and self.line == self.footer:
1.123 - self.ssub = self.margin * self.yscale
1.124 + self.ssub = self.margin
1.125 return
1.126
1.127 # Support spacing between character rows.
1.128
1.129 - self.ssub = self.spacing * self.yscale
1.130 + self.ssub = self.spacing
1.131
1.132 self.line_start = self.address
1.133
1.134 @@ -186,6 +172,13 @@
1.135
1.136 if self.buffer_index == self.buffer_limit:
1.137 self.buffer_index = 0
1.138 + self.column += 1
1.139 +
1.140 + # Detect the end of the scanline.
1.141 +
1.142 + if self.column > self.columns:
1.143 + return BLANK
1.144 +
1.145 self.fill_pixel_buffer()
1.146
1.147 self.xsub += 1
1.148 @@ -289,8 +282,8 @@
1.149
1.150 x = 0
1.151 screen = []
1.152 - while x < WIDTH:
1.153 - screen.append([(0, 0, 0)] * HEIGHT)
1.154 + while x < MAX_WIDTH:
1.155 + screen.append([(0, 0, 0)] * MAX_HEIGHT)
1.156 x += 1
1.157 return screen
1.158