ZX Spectrum ULA: Difference between revisions

Jump to navigation Jump to search
m
→‎5C and 6C ULAs: Remove duplicated pin number
m (Cat: Audio)
m (→‎5C and 6C ULAs: Remove duplicated pin number)
(19 intermediate revisions by 3 users not shown)
Line 2: Line 2:
[[Category:Video]]
[[Category:Video]]
[[Category:Audio]]
[[Category:Audio]]
The '''ULA''' (Uncommitted Logic Array) is a chip which controls most of the interfaces between the Z80 CPU and peripheral functions. On a [[ZX Spectrum 16k/48k]], it is a Ferranti 5C102, 5C112 or 6C001 (the '102 had a timing fault which necessitated an extra board called the "spider mod"; the 6C001 alters the timing of the colour burst signal to improve performance with certain TV sets.  ''(This info from the Spectrum Service Manual; don't know how to do citations on this wiki)'').
The '''ULA''' (Uncommitted Logic Array) is a chip which controls most of the interfaces between the Z80 CPU and peripheral functions.
 
The [[ZX Spectrum 16K/48K]] ULA went through [[#ULA versions|multiple revisions]] and is either a 5C or 6C series Ferranti ULA.
 
On a [[ZX Spectrum 128]], or [[ZX Spectrum +2]] the ULA is the Ferranti 7K010E (later labelled Amstrad 40056)
 
The [[ZX Spectrum +2A/2B, +3/3B|ZX Spectrum +2A, +3, +2B, and +3B]] use an entirely different gate array. See [[The Amstrad gate array]]
 
Sinclair also experimented with an alternative [[#AMI SAGA|AMI SAGA]] chip in a special batch.


== ULA Functions ==
== ULA Functions ==
Line 12: Line 20:
From Z80 code, the ULA functions are accessible on any even-numbered I/O port (as the ULA performs ''very'' partial port decoding), but conventionally port 0xFE is used.
From Z80 code, the ULA functions are accessible on any even-numbered I/O port (as the ULA performs ''very'' partial port decoding), but conventionally port 0xFE is used.


''(list of the port high bytes & their interpretations should go here)''
IN from port xxFE will read the keyboard state and tape input.  Each bit of the high byte selects a keyboard half-row; that row is read if the bit is ''low''.  If a key is pressed on any selected half-row, the corresponding bit of the data byte is ''low''; this accounts for bits d<sub>0</sub>–d<sub>4</sub>.  d<sub>6</sub> is the value at the EAR socket <sup>''(polarity?)''</sup>; d<sub>5</sub> and d<sub>7</sub> are not used.
 
OUT to port xxFE (the high byte is ignored) will set the border colour to {d<sub>2</sub>, d<sub>1</sub>, d<sub>0</sub>}, drive the MIC socket with d<sub>3</sub> and the loudspeaker with d<sub>4</sub>.<sup>''(again, polarity?)''</sup>  d<sub>5</sub>–d<sub>7</sub> are not used.
 
=== Keyboard Half-rows ===
{| class="wikitable" style="border: none; text-align: center;"
! style="width: 5em;" |row
! style="width: 3em;" |d<sub>0</sub>
! style="width: 3em;" |d<sub>1</sub>
! style="width: 3em;" |d<sub>2</sub>
! style="width: 3em;" |d<sub>3</sub>
! style="width: 3em;" |d<sub>4</sub>
|style="background: #ffffff; border: 0px none; padding: 0.2em;"|
! style="width: 5em;" |row
! style="width: 3em;" |d<sub>4</sub>
! style="width: 3em;" |d<sub>3</sub>
! style="width: 3em;" |d<sub>2</sub>
! style="width: 3em;" |d<sub>1</sub>
! style="width: 3em;" |d<sub>0</sub>
|-
|0xF7FE||1||2||3||4||5
|style="background: #ffffff; border: 0px none; padding: 0.2em;"|
|0xEFFE||6||7||8||9||0
|-
|0xFBFE||Q||W||E||R||T
|style="background: #ffffff; border: 0px none; padding: 0.2em;"|
|0xDFFE||Y||U||I||O||P
|-
|0xFDFE||A||S||D||F||G
|style="background: #ffffff; border: 0px none; padding: 0.2em;"|
|0xBFFE||H||J||K||L||''Enter''
|-
|0xFEFE||''Caps''||Z||X||C||V
|style="background: #ffffff; border: 0px none; padding: 0.2em;"|
|0x7FFE||B||N||M||''Sym''||''Space''
|}
 
==== Example ====
If the keys Caps Shift, T, and O are held down, and the Z80 does an IN on port 0xDEFE (57086<sub>10</sub>), this selects the half-rows '''''Caps'''''–'''V''' and '''P'''–'''Y''', so d<sub>0</sub> is low because of the Caps Shift, d<sub>1</sub> is low because of the O, and the T has no effect because its row is not selected.  Thus the returned data byte is 0x1C (that is, 0b00011100, or 28<sub>10</sub>) assuming EAR (d<sub>6</sub>) is low.
 
==ULA versions==
'''Need some text here about the different versions of the ULA and which board revisions they are compatible with'''


''(stuff about keyboard half-rows should go here)''
=== AMI SAGA ===
Sinclair produced a special batch of Spectrums with an AMI SAGA instead of the Ferranti ULA.  Only one machine belonging to this batch has been found and confirmed as having a genuine AMI SAGA board.  The board was of Issue 6A, mounted in a Spectrum+ case, with a sticker instructing that the machine should be sent back to Sinclair Research should it require repair, marked for the attention of the QA manager.  The AMI SAGA was found to run at a much lower temperature than the ULA, and does not require the Spectrum's chroma bias circuitry which was absent from the aforementioned board.  The clock signal is clean enough to drive the Z80 directly, without any amplification, as this is also absent from the special batch board.  One other machine has been reported to be found, but no photographs of this machine have yet been released.


== Contention ==
== Contention ==
{{Main|Contended memory}}
{{Main|Contended memory}}
The ULA has priority for reading from RAM, since it needs to be able to generate the display promptly.  Thus if the Z80 attempts to read/write memory from the range 0x4000 to 0x7FFF (ie. A15 =0, A14 =1) while the display is being generated, the ULA will stop the Z80 (by bringing the /WAIT line low) until it has finished reading VRAM.  This is known as "RAM contention" and the range 0x4000 to 0x7FFF is the "contended RAM".  While the TV scan is within the display area, contention is applied for 6 T-states out of every 8.
The ULA has priority for reading from RAM, since it needs to be able to generate the display promptly.  Thus if the Z80 attempts to read/write memory from the range 0x4000 to 0x7FFF (ie. A15 =0, A14 =1) while the display is being generated, the ULA will stop the Z80 (by disabling the CPU clock) until it has finished reading VRAM.  This is known as "RAM contention" and the range 0x4000 to 0x7FFF is the "contended RAM".  While the TV scan is within the display area, contention is applied for 6 T-states out of every 8.
=== IO Contention ===
 
{{Main|Contended IO}}
=== I/O Contention ===
Weirdly, the ULA also applies contention to IO port reads/writes if A15 =0 and A14 =1.  ''(details should go here; IO contention is more complicated somehow)''
{{Main|Contended I/O}}
Weirdly, the ULA also applies contention to I/O port reads/writes if A15 =0 and A14 =1.  ''(details should go here; I/O contention is more complicated somehow)''
 
=== The Snow Effect ===
=== The Snow Effect ===
{{Copyedit}}
{{Copyedit}}
Contention is also indirectly responsible for the famous "Snow Effect", produced by setting the Z80's 'interrupt vector' (I) register to a value in the range 0x40-0x7F.  This interacts with the Z80's memory refresh cycle: in the 3rd and 4th T-states of the M1 (opcode fetch) cycle, the Z80 pulls the /MREQ and /RFSH lines low, and asserts the R register in the low byte of the address bus; this is for dynamic RAM refresh.  However, when doing this, it also asserts the I register on the high byte of the address bus.  If I is in the range 0x40-0x7f, this will look to the ULA like an access to contended RAM (because the ULA doesn't check the /RD, /WR or /RFSH lines, only the /MREQ).  However, the ULA will only halt the Z80 on the first T-state of any M-cycle.
Contention is also indirectly responsible for the famous "Snow Effect", produced by setting the Z80's 'interrupt vector' (I) register to a value in the range 0x40–0x7F.  This interacts with the Z80's memory refresh cycle: in the 3rd and 4th T-states of the M1 (opcode fetch) cycle, the Z80 pulls the {{overline|MREQ}} and {{overline|RFSH}} lines low, and asserts the R register in the low byte of the address bus; this is for dynamic RAM refresh.  However, when doing this, it also asserts the I register on the high byte of the address bus.  If I is in the range 0x40–0x7f, this will look to the ULA like an access to contended RAM (because the ULA doesn't check the {{overline|RD}}, {{overline|WR}} or {{overline|RFSH}} lines, only the {{overline|MREQ}}).  However, the ULA will only halt the Z80 on the first T-state of any M-cycle.


The result of this is that the ULA won't assert the address it wants on the address bus, but it ''will'' still try to read the byte from RAM.  This means it reads from whatever address was present on the bus, and uses that as the data to put on the screen.  The upshot of all this is that the screen becomes filled with 'noise', almost as though the screen RAM had become corrupted - but the effect can be undone; putting the I register back will cause the screen to return to normal.
The result of this is that the ULA won't assert the address it wants on the address bus, but it ''will'' still try to read the byte from RAM.  This means it reads from whatever address was present on the bus, and uses that as the data to put on the screen.  The upshot of all this is that the screen becomes filled with 'noise', almost as though the screen RAM had become corrupted but the effect can be undone; putting the I register back will cause the screen to return to normal.


''(I think there's more to it than this, I think you have to be running a sequence of 1-cycle instructions or something?  Should probably find an example snow program to include)''
''(I think there's more to it than this, I think you have to be running a sequence of 1-cycle instructions or something?  Should probably find an example snow program to include)''


''(Also when people are talking about the snow effect they always seem to mention Arkanoid; presumably it uses the snow effect at some point)''
''(Also when people are talking about the snow effect they always seem to mention Arkanoid; presumably it uses the snow effect at some point)''
== Flash attribute timing bug ==
The flash attribute suffers from a timing bug, in that the flash signal is not in sync with the pixel with which it is XORed.  On frames where the flash signal is inactive, this bug is not apparent, but where the flash signal is active, and used within an attribute block, flash will also affect the very right-hand edge of the preceding block, and in attribute blocks where flash is not set, flash will fail to affect the very right-hand edge of the preceding block.  This bug only affects instances where there is a transition in pixel value between one character block and the next, but the flash attribute reverses that transition.  As such, this does not affect the Spectrum's flashing cursor, as its graphic has edges matching the surrounding "paper" and not the ink.  No existing emulator known to implement this display artifact.
== Pinouts ==
=== 5C and 6C ULAs ===
<div style="font-family:'Lucida Console', monospace">
{|
| style="text-align:right;" |
{{overline|CAS}}<br>
{{overline|WR}}<br>
{{overline|RD}}<br>
{{overline|WE}}<br>
A<sub>0</sub><br>
A<sub>1</sub><br>
A<sub>2</sub><br>
A<sub>3</sub><br>
A<sub>4</sub><br>
A<sub>5</sub><br>
A<sub>6</sub><br>
{{overline|INT}}<br>
(+5V) V<sub>CC-Logic</sub><br>
(+5V) V<sub>CC-IO</sub><br>
U<br>
V<br>
{{overline|Y}}<br>
D<sub>0</sub><br>
KB<sub>0</sub><br>
KB<sub>1</sub>
| style="border: 1px solid black" |
1&nbsp;°&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;40<br>
2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;39<br>
3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;38<br>
4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;37<br>
5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;36<br>
6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;35<br>
7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;34<br>
8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;33<br>
9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;32<br>
10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;31<br>
11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;30<br>
12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;29<br>
13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;28<br>
14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;27<br>
15&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;26<br>
16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;25<br>
17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;24<br>
18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;23<br>
19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;22<br>
20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;21
||
GND (0V)<br>
OSC<br>
{{overline|MREQ}}<br>
A<sub>15</sub><br>
A<sub>14</sub><br>
{{overline|RAS}}<br>
{{overline|ROMCS}}<br>
{{overline|IOREQ}}<br>
CLOCK<br>
D<sub>7</sub><br>
D<sub>6</sub><br>
D<sub>5</sub><br>
SOUND<br>
D<sub>4</sub><br>
KB<sub>4</sub><br>
D<sub>3</sub><br>
KB<sub>3</sub><br>
KB<sub>2</sub><br>
D<sub>2</sub><br>
D<sub>1</sub>
|}
</div>
=== 7C ULA ===
<div style="font-family:'Lucida Console', monospace">
{|
| style="text-align:right;" |
17M<br>
{{overline|CAS}}<br>
C<br>
DMA<sub>0</sub><br>
DMA<sub>1</sub><br>
DMA<sub>2</sub><br>
DMA<sub>3</sub><br>
DMA<sub>4</sub><br>
DMA<sub>5</sub><br>
DMA<sub>6</sub><br>
DMA<sub>7</sub><br>
VB<br>
(+5V) V<sub>CC-Logic</sub><br>
(+5V) V<sub>CC-IO</sub><br>
{{overline|RD}}<br>
{{overline|WR}}<br>
{{overline|INT}}<br>
{{overline|DRAMWE}}<br>
B<br>
G<br>
R<br>
BRIGHT<br>
SYNC<br>
D<sub>0</sub>
| style="border: 1px solid black" |
1&nbsp;°&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;48<br>
2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;47<br>
3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;46<br>
4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;45<br>
5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;44<br>
6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;43<br>
7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;42<br>
8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;41<br>
9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;40<br>
10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;39<br>
11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;38<br>
12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;37<br>
13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;36<br>
14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;35<br>
15&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;34<br>
16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;33<br>
17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;32<br>
18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;31<br>
19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;30<br>
20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;29<br>
21&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;28<br>
22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;27<br>
23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;26<br>
24&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;25
||
GND (0V)<br>
8.8M<br>
{{overline|8.8M}}<br>
{{overline|MREQ}}<br>
A<sub>15</sub><br>
A<sub>14</sub><br>
{{overline|RAS}}<br>
ROMS<br>
{{overline|IORQ}}<br>
PHICPU<br>
D<sub>7</sub><br>
D<sub>6</sub><br>
D<sub>5</sub><br>
MIC<br>
EAR<br>
D<sub>4</sub><br>
KB<sub>0</sub><br>
D<sub>3</sub><br>
KB<sub>1</sub><br>
KB<sub>2</sub><br>
D<sub>2</sub><br>
D<sub>1</sub><br>
KB<sub>3</sub><br>
KB<sub>4</sub>
|}
</div>

Navigation menu