# HG changeset patch # User Paul Boddie # Date 1330294762 -3600 # Node ID 4d0b268480eb7b0174f596290ec2769a5ca167fe # Parent be29270ef9f68aae9eed2175a0e2fd0ddec64da4# Parent 4adc54716f92634f1986d3977e30b6c5701f7196 Merged general changes. diff -r be29270ef9f6 -r 4d0b268480eb ULA.txt --- a/ULA.txt Mon Feb 13 22:25:37 2012 +0100 +++ b/ULA.txt Sun Feb 26 23:19:22 2012 +0100 @@ -120,6 +120,42 @@ See: Acorn Electron Service Manual http://acorn.chriswhy.co.uk/docs/Acorn/Manuals/Acorn_ElectronSM.pdf +Interrupts +---------- + +The ULA generates IRQs (maskable interrupts) according to certain conditions +and these conditions are controlled by location &FE00: + + * Vertical sync (bottom of displayed screen) + * 50MHz real time clock + * Transmit data empty + * Receive data full + * High tone detect + +The ULA is also used to clear interrupt conditions through location &FE05. Of +particular significance is bit 7, which must be set if an NMI (non-maskable +interrupt) has occurred and has thus suspended ULA access to memory, restoring +the normal function of the ULA. + +ROM Paging +---------- + +Accessing different ROMs involves bits 0 to 3 of &FE05. Some special ROM +mappings exist: + + 8 keyboard + 9 keyboard (duplicate) + 10 BASIC ROM + 11 BASIC ROM (duplicate) + +Paging in a ROM involves the following procedure: + + 1. Assert ROM page enable (bit 3) together with a ROM number n in bits 0 to + 2, corresponding to ROM number 8+n, such that one of ROMs 12 to 15 is + selected. + 2. Where a ROM numbered from 0 to 7 is to be selected, set bit 3 to zero + whilst writing the desired ROM number n in bits 0 to 2. + Shadow/Expanded Memory ---------------------- diff -r be29270ef9f6 -r 4d0b268480eb ula.py --- a/ula.py Mon Feb 13 22:25:37 2012 +0100 +++ b/ula.py Sun Feb 26 23:19:22 2012 +0100 @@ -18,7 +18,7 @@ MAX_PIXELLINE = MIN_PIXELLINE + MAX_HEIGHT MAX_HSYNC = 75 # the number of cycles in each hsync period -MIN_PIXELPOS = 264 # the first cycle involving pixel generation +MIN_PIXELPOS = 256 # the first cycle involving pixel generation MAX_SCANPOS = 1024 # the number of cycles in each scanline MAX_PIXELPOS = MIN_PIXELPOS + MAX_WIDTH @@ -72,7 +72,7 @@ def update(self): if MIN_PIXELLINE <= self.y < MAX_PIXELLINE: - if MIN_PIXELPOS <= self.x < MAX_PIXELPOS: + if MIN_PIXELPOS + 8 <= self.x < MAX_PIXELPOS + 8: self.screen[self.pos] = self.colour[0]; self.pos += 1 self.screen[self.pos] = self.colour[1]; self.pos += 1 self.screen[self.pos] = self.colour[2]; self.pos += 1 @@ -153,6 +153,11 @@ "Reset the ULA." + # General state. + + self.nmi = 0 # no NMI asserted initially + self.irq_vsync = 0 # no IRQ asserted initially + # Internal state. self.cycle = 0 # counter within each 2MHz period @@ -160,6 +165,8 @@ self.ram_address = 0 # address given to the RAM self.data = 0 # data read from the RAM self.buffer = [BLANK]*8 # pixel buffer for decoded RAM data + self.have_pixels = 0 # whether pixel data has been read + self.writing_pixels = 0 # whether pixel data can be written self.reset_vertical() @@ -278,8 +285,7 @@ def in_frame(self): return MIN_PIXELLINE <= self.y < MAX_PIXELLINE def inside_frame(self): return MIN_PIXELLINE < self.y < MAX_PIXELLINE - def read_pixels(self): return MIN_PIXELPOS - 8 <= self.x < MAX_PIXELPOS - 8 and self.in_frame() - def make_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame() + def read_pixels(self): return MIN_PIXELPOS <= self.x < MAX_PIXELPOS and self.in_frame() def update(self): @@ -302,7 +308,7 @@ # Clock management. - access_ram = self.access == 0 and self.read_pixels() and not self.ssub + access_ram = not self.nmi and self.access == 0 and self.read_pixels() and not self.ssub # Set row address (for ULA access only). @@ -313,6 +319,14 @@ if access_ram: self.ram_address = (self.address & 0xff80) >> 7 + # Initialise the pixel buffer if appropriate. + + if not self.writing_pixels and self.have_pixels: + self.xcounter = self.xscale + self.buffer_index = 0 + self.fill_pixel_buffer() + self.writing_pixels = 1 + # Latch row address, set column address (for ULA access only). elif self.cycle == 1: @@ -374,6 +388,7 @@ if access_ram: self.data = self.data | self.ram.data + self.have_pixels = 1 # Advance to the next column. @@ -402,6 +417,9 @@ 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. @@ -419,34 +437,27 @@ # Detect spacing between character rows. - if not self.make_pixels() or self.ssub: + if not self.writing_pixels or self.ssub: self.video.colour = BLANK # For pixels within the frame, obtain and output the value. else: - # Detect the start of the pixel generation. - if self.x == MIN_PIXELPOS: - self.xcounter = self.xscale - self.buffer_index = 0 - self.fill_pixel_buffer() + self.xcounter -= 1 + self.video.colour = self.buffer[self.buffer_index] # Scale pixels horizontally, only accessing the next pixel value # after the required number of scan positions. - elif self.xcounter == 0: + if self.xcounter == 0: self.xcounter = self.xscale self.buffer_index += 1 - # Fill the pixel buffer, assuming that data is available. + # Handle the buffer empty condition. if self.buffer_index >= self.buffer_limit: - self.buffer_index = 0 - self.fill_pixel_buffer() - - self.xcounter -= 1 - self.video.colour = self.buffer[self.buffer_index] + self.writing_pixels = 0 self.x += 1