ZX Spectrum ULA: Difference between revisions

Jump to navigation Jump to search
Use the right sorts of dashes
(Move AMI SAGA section to ULA versions subsection)
(Use the right sorts of dashes)
Line 20: 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.


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.
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.
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 ===
=== Keyboard Half-rows ===
Line 58: Line 58:


==== Example ====
==== 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.
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==
==ULA versions==
Line 74: Line 74:
=== 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 /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.


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)''

Navigation menu