Contended memory: Difference between revisions

Jump to navigation Jump to search
Attempt to better demonstrate differences between contention models
(Attempt to better demonstrate differences between contention models)
Line 1: Line 1:
[[category:Hardware]]
[[category:Hardware]]
'''Contended memory''' is a quirk of the ZX Spectrum's hardware design which means that it is on average slower to access those memory areas which are shared with the ULA than it is to access other memory areas. This occurs because the RAM cannot be read by two devices (the ULA and the processor) at once, and the ULA is given higher priority so it can drawn the screen correctly. Therefore, programs which access this "contended memory" (which is from 0x4000 to 0x7fff on the 48K machine, which is the actual 16k version) or try to read from an IO port where the result is provided by the ULA (any port with the low bit reset on the 48K machine) will be slowed if the ULA is reading the screen. This effect occurs only when the actual screen is being drawn; when the border is being drawn or the TV is in either horizontal or vertical refresh, the ULA does not need to access memory and therefore no delays occur.
'''Contended memory''' is a quirk of the ZX Spectrum's hardware design which means that it is on average slower to access those memory areas which are shared with the ULA than it is to access other memory areas. This occurs because the RAM cannot be read by two devices (the ULA and the processor) at once, and the ULA is given higher priority so it can drawn the screen correctly. Therefore, programs which access this "contended memory" (which is from 0x4000 to 0x7fff on the 48K machine, which is the actual 16K version) or try to read from an IO port where the result is provided by the ULA (any port with the low bit reset on the 48K machine) will be slowed if the ULA is reading the screen. This effect occurs only when the actual screen is being drawn; when the border is being drawn or the TV is in either horizontal or vertical refresh, the ULA does not need to access memory and therefore no delays occur.


== General principles ==
== General principles ==
Line 8: Line 8:
== Details  ==
== Details  ==


=== 48K ===
=== 16K/48K ===


On the 48K machine, the memory from 0x4000 to 0x7fff is contended. If the contended memory is accessed 14335<ref>In this document, we label the first tstate which ''begins'' with /INT low as tstate 0; some other resources label this tstate as tstate 1, which means that all tstate counts will be one greater. Note that this is purely a notational difference, and is ''not'' the same as the effect observed in the [[#Timing differences|timing differences]] section, which is a actual difference in behaviour between different machines; when using the notation which labels the first /INT low tstate as tstate 1, the first contended memory cycle is at either 14336 or 14337 tstates.</ref> or 14336 tstates after an interrupt (see the [[#Timing differences|timing differences]] section below for information on the 14335/14336 issue), the Z80 will be delayed for 6 tstates. After 14336 tstates, the delay is 5 tstates. The pattern continues as follows:
On the 48K machine, the memory from 0x4000 to 0x7fff is contended. If the contended memory is accessed 14335<ref>In this document, we label the first tstate which ''begins'' with /INT low as tstate 0; some other resources label this tstate as tstate 1, which means that all tstate counts will be one greater. Note that this is purely a notational difference, and is ''not'' the same as the effect observed in the [[#Timing differences|timing differences]] section, which is a actual difference in behaviour between different machines; when using the notation which labels the first /INT low tstate as tstate 1, the first contended memory cycle is at either 14336 or 14337 tstates.</ref> or 14336 tstates after an interrupt (see the [[#Timing differences|timing differences]] section below for information on the 14335/14336 issue), the Z80 will be delayed for 6 tstates. After 14336 tstates, the delay is 5 tstates. The pattern continues as follows:
Line 67: Line 67:
This pattern (6,5,4,3,2,1,0,0) continues until 14463 tstates after interrupt, at which point there is no delay for 96 tstates while the border and horizontal refresh are drawn. The pattern starts again at 14559 tstates and continues for all 192 lines of screen data. After this, there is no delay until the end of the frame as the bottom border and vertical refresh happen, and no delay until 14335 tstates after the start of the next frame as the top border is drawn.
This pattern (6,5,4,3,2,1,0,0) continues until 14463 tstates after interrupt, at which point there is no delay for 96 tstates while the border and horizontal refresh are drawn. The pattern starts again at 14559 tstates and continues for all 192 lines of screen data. After this, there is no delay until the end of the frame as the bottom border and vertical refresh happen, and no delay until 14335 tstates after the start of the next frame as the top border is drawn.


=== 128K / Grey +2 ===
=== 128K / grey +2 ===


On the 128K and Grey +2 Spectrums, memory pages 1, 3, 5 and 7 are contended. This means that RAM from 0x4000 to 0x7fff is always contended (as memory page 5 is always mapped in there) and RAM from 0xc000 to 0xffff can be contended if page 1, 3, 5 or 7 is paged in there. The 128K and +2 Spectrums also have a different timing pattern from the 48K machine due to their different line and frame lengths: the 6,5,4,3,2,1,0,0 pattern starts 14361 tstates after interrupt, and repeats every 228 tstates rather than 224.
On the 128K and Grey +2 Spectrums, memory pages 1, 3, 5 and 7 are contended. This means that RAM from 0x4000 to 0x7fff is always contended (as memory page 5 is always mapped in there) and RAM from 0xc000 to 0xffff can be contended if page 1, 3, 5 or 7 is paged in there. The 128K and +2 Spectrums also have a different timing pattern from the 48K machine due to their different line and frame lengths: the 6,5,4,3,2,1,0,0 pattern starts 14361 tstates after interrupt, and repeats every 228 tstates rather than 224.


=== Black +2 / +3 ===
=== Black +2 (+2A/B) / +3 ===


The Amstrad ASIC in the Black +2 / +3 differs more significantly in that it applies less contention than the 48K or 128K ULAs. Specifically, it applies contention only if the MREQ line is active, whereas the 48K ULA applies it under all circumstances.
The Amstrad ASIC in the black +2A/+2B and +3 differs more significantly in that it applies less contention than the 48K or 128K/Grey +2 ULAs. Specifically, it applies memory contention only if the MREQ line is active, whereas the 16K/48K ULA applies it under all circumstances.
Unlike the the 128K or Grey +2 the Amstrad ASIC contends pages 4, 5, 6, and 7.
Unlike the the 128K or Grey +2 the Amstrad ASIC contends pages 4, 5, 6, and 7.
In the [[#Instruction breakdown|instruction breakdown table]], contention patterns which differ on the +3 are shown in ''italics''. The timing pattern also differs significantly:
In the [[#Instruction breakdown|instruction breakdown table]], contention patterns which differ on the +2A/+3 are shown in '''bold''' in the '+3 ULA' column, with sections of 48K-specific contention shown in '''bold''' in the '48K/128K ULA' column.  T-state counts associated with the 48K-specific contention still apply to the +2A/+3 pattern, but the contention itself does not.  With 48K-contention excluded, the timings in both columns are identical.
 
The timing pattern also differs significantly:


{| class="wikitable" style="text-align:center;"
{| class="wikitable" style="text-align:center;"
Line 158: Line 160:
== Timing differences ==
== Timing differences ==


It has been observed that on ULA-based machines, the timings may be one tstate later than normal. All timings given in this documnet are for "early timing"; for "late timing", simply add one to add tstate counts given. ASIC-based machines manufactured by Amstrad do not exhibit this behaviour.
It has been observed that on ULA-based machines, the timings may be one tstate later than normal. All timings given in this documnet are for "early timing"; for "late timing", simply add one to add T-state counts given. ASIC-based machines manufactured by Amstrad do not exhibit this behaviour.


The physical reason for this difference is that as the ULA heats up, it drifts from "early timing" to "late timing" due to increased thermal resistance. A machine that has been left off for some time and just switched on will always exhibit "early timing". Some emulators have a "late timing" option to switch the ULA to a "hot" state.
The physical reason for this difference is that as the ULA heats up, it drifts from "early timing" to "late timing" due to increased thermal resistance. A machine that has been left off for some time and just switched on will always exhibit "early timing". Some emulators have a "late timing" option to switch the ULA to a "hot" state.
Line 188: Line 190:
* Access to I/O ports is treated differently to access to memory; full details are given [[Contended IO]]. The delays specified there should be applied when an I/O port is accessed; this is designated by "IO" in the table below.
* Access to I/O ports is treated differently to access to memory; full details are given [[Contended IO]]. The delays specified there should be applied when an I/O port is accessed; this is designated by "IO" in the table below.


In the table below, contention patterns which differ on the +2A/+3 are shown in '''bold''' in the '+3 ULA' column, with sections of 48K-specific contention shown in '''bold''' in the '48K/128K ULA' column.  T-state counts associated with the 48K-specific contention still apply to the +2A/+3 pattern, but the contention itself does not.  With 48K-contention excluded, the timings in both columns are identical.


{| class="wikitable" style="text-align:center;"
{| class="wikitable" style="text-align:center;"
Line 249: Line 252:
|-
|-
| LD A,I
| LD A,I
| rowspan=4 | pc:4,pc+1:4,ir:1
| rowspan=4 | pc:4,pc+1:4,'''ir''':1
| rowspan=4 | ''pc:4,pc+1:5''
| rowspan=4 | '''pc:4,pc+1:5'''
|-
|-
| LD A,R
| LD A,R
Line 259: Line 262:
|-
|-
| INC/DEC dd
| INC/DEC dd
| rowspan=2 | pc:4,ir:1 x 2
| rowspan=2 | pc:4,'''ir''':1 ×2
| rowspan=2 | ''pc:6''
| rowspan=2 | '''pc:6'''
|-
|-
| LD SP,HL
| LD SP,HL
|-
|-
| ADD HL,dd
| ADD HL,dd
| pc:4,ir:1 x 7
| pc:4,'''ir''':1 ×7
| ''pc:11''
| '''pc:11'''
|-
|-
| ADC HL,dd
| ADC HL,dd
| rowspan=2 | pc:4,pc+1:4,ir:1 x 7
| rowspan=2 | pc:4,pc+1:4,'''ir''':1 ×7
| rowspan=2 | ''pc:4,pc+1:11''
| rowspan=2 | '''pc:4,pc+1:11'''
|-
|-
| SBC HL,dd
| SBC HL,dd
Line 291: Line 294:
|-
|-
| LD r,(ii+n)
| LD r,(ii+n)
| rowspan=3 | pc:4,pc+1:4,pc+2:3,pc+2:1 x 5,ii+n:3
| rowspan=3 | pc:4,pc+1:4,pc+2:3,'''pc+2''':1 ×5,ii+n:3
| rowspan=3 | ''pc:4,pc+1:4,pc+2:8,ii+n:3''
| rowspan=3 | '''pc:4,pc+1:4,pc+2:8,ii+n:3'''
|-
|-
| LD (ii+n),r
| LD (ii+n),r
Line 299: Line 302:
|-
|-
| BIT b,(HL)
| BIT b,(HL)
| pc:4,pc+1:4,hl:3,hl:1
| pc:4,pc+1:4,hl:3,'''hl''':1
| ''pc:4,pc+1:4,hl:4''
| '''pc:4,pc+1:4,hl:4'''
|-
|-
| BIT b,(ii+n)
| BIT b,(ii+n)
| pc:4,pc+1:4,pc+2:3,pc+3:3,pc+3:1 x 2,ii+n:3,ii+n:1
| pc:4,pc+1:4,pc+2:3,pc+3:3,'''pc+3''':1 ×2,ii+n:3,'''ii+n''':1
| ''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:4''
| '''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:4'''
|-
|-
| LD dd,nn
| LD dd,nn
Line 319: Line 322:
|-
|-
| LD (ii+n),n
| LD (ii+n),n
| pc:4,pc+1:4,pc+2:3,pc+3:3,pc+3:1 x 2,ii+n:3
| pc:4,pc+1:4,pc+2:3,pc+3:3,'''pc+3''':1 ×2,ii+n:3
| ''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:3''
| '''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:3'''
|-
|-
| LD A,(nn)
| LD A,(nn)
Line 341: Line 344:
|-
|-
| INC/DEC (HL)
| INC/DEC (HL)
| pc:4,hl:3,hl:1,hl(write):3
| pc:4,hl:3,'''hl''':1,hl(write):3
| ''pc:4,hl:4,hl(write):3''
| '''pc:4,hl:4,hl(write):3'''
|-
|-
| SET b,(HL)
| SET b,(HL)
| rowspan=3 | pc:4,pc+1:4,hl:3,hl:1,hl(write):3
| rowspan=3 | pc:4,pc+1:4,hl:3,'''hl''':1,hl(write):3
| rowspan=3 | ''pc:4,pc+1:4,hl:4,hl(write):3''
| rowspan=3 | '''pc:4,pc+1:4,hl:4,hl(write):3'''
|-
|-
| RES b,(HL)
| RES b,(HL)
Line 353: Line 356:
|-
|-
| INC/DEC (ii+n)
| INC/DEC (ii+n)
| pc:4,pc+1:4,pc+2:3,pc+2:1 x 5,ii+n:3,ii+n:1,ii+n(write):3
| pc:4,pc+1:4,pc+2:3,'''pc+2''':1 ×5,ii+n:3,'''ii+n''':1,ii+n(write):3
| ''pc:4,pc+1:4,pc+2:8,ii+n:4,ii+n(write):3''
| '''pc:4,pc+1:4,pc+2:8,ii+n:4,ii+n(write):3'''
|-
|-
| SET b,(ii+n)
| SET b,(ii+n)
| rowspan=3 | pc:4,pc+1:4,pc+2:3,pc+3:3,pc+3:1 x 2,ii+n:3,ii+n:1,ii+n(write):3
| rowspan=3 | pc:4,pc+1:4,pc+2:3,pc+3:3,'''pc+3''':1 x 2,ii+n:3,'''ii+n''':1,ii+n(write):3
| rowspan=3 | ''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:4,ii+n(write):3''
| rowspan=3 | '''pc:4,pc+1:4,pc+2:3,pc+3:5,ii+n:4,ii+n(write):3'''
|-
|-
| RES b,(ii+n)
| RES b,(ii+n)
Line 377: Line 380:
|-
|-
| RET cc
| RET cc
| pc:4,ir:1,[sp:3,sp+1:3]
| pc:4,'''ir''':1,[sp:3,sp+1:3]
| ''pc:5,[sp:3,sp+1:3]''
| '''pc:5,[sp:3,sp+1:3]'''
|-
|-
| PUSH dd
| PUSH dd
| rowspan=2 | pc:4,ir:1,sp-1:3,sp-2:3
| rowspan=2 | pc:4,'''ir''':1,sp-1:3,sp-2:3
| rowspan=2 | pc:5,sp-1:3,sp-2:3
| rowspan=2 | pc:5,sp-1:3,sp-2:3
|-
|-
Line 387: Line 390:
|-
|-
| CALL nn
| CALL nn
| rowspan=2 | pc:4,pc+1:3,pc+2:3,[pc+2:1,sp-1:3,sp-2:3]
| rowspan=2 | pc:4,pc+1:3,pc+2:3,['''pc+2''':1,sp-1:3,sp-2:3]
| rowspan=2 | ''pc:4,pc+1:3,pc+2:3,[1,sp-1:3,sp-2:3]''
| rowspan=2 | '''pc:4,pc+1:3,pc+2:3,[1,sp-1:3,sp-2:3]'''
|-
|-
| CALL cc,nn
| CALL cc,nn
|-
|-
| JR n
| JR n
| rowspan=2 | pc:4,pc+1:3,[pc+1:1 x 5]
| rowspan=2 | pc:4,pc+1:3,['''pc+1''':1 ×5]
| rowspan=2 | ''pc:4,pc+1:3,[5]''
| rowspan=2 | '''pc:4,pc+1:3,[5]'''
|-
|-
| JR cc,n
| JR cc,n
|-
|-
| DJNZ n
| DJNZ n
| pc:4,ir:1,pc+1:3,[pc+1:1 x 5]
| pc:4,'''ir''':1,pc+1:3,['''pc+1''':1 ×5]
| ''pc:5,pc+1:3,[5]''
| '''pc:5,pc+1:3,[5]'''
|-
|-
| RLD
| RLD
| rowspan=2 | pc:4,pc+1:4,hl:3,hl:1 x 4,hl(write):3
| rowspan=2 | pc:4,pc+1:4,hl:3,'''hl''':1 ×4,hl(write):3
| rowspan=2 | ''pc:4,pc+1:4,hl:7,hl(write):3''
| rowspan=2 | '''pc:4,pc+1:4,hl:7,hl(write):3'''
|-
|-
| RRD
| RRD
Line 421: Line 424:
|-
|-
| EX (SP),HL
| EX (SP),HL
| pc:4,sp:3,sp+1:3,sp+1:1,sp+1(write):3,sp(write):3,sp(write):1 x 2
| pc:4,sp:3,sp+1:3,'''sp+1''':1,sp+1(write):3,sp(write):3,'''sp(write)''':1 ×2
| ''pc:4,sp:3,sp+1:4,sp+1(write):3,sp(write):5''
| '''pc:4,sp:3,sp+1:4,sp+1(write):3,sp(write):5'''
|-
|-
| LDI/LDIR
| LDI/LDIR
| rowspan=2 | pc:4,pc+1:4,hl:3,de:3,de:1 x 2,[de:1 x 5]
| rowspan=2 | pc:4,pc+1:4,hl:3,de:3,'''de''':1 ×2,['''de''':1 ×5]
| rowspan=2 | ''pc:4,pc+1:4,hl:3,de:5,[5]''
| rowspan=2 | '''pc:4,pc+1:4,hl:3,de:5,[5]'''
|-
|-
| LDD/LDDR
| LDD/LDDR
|-
|-
| CPI/CPIR
| CPI/CPIR
| rowspan=2 | pc:4,pc+1:4,hl:3,hl:1 x 5,[hl:1 x 5]
| rowspan=2 | pc:4,pc+1:4,hl:3,'''hl''':1 ×5,['''hl''':1 ×5]
| rowspan=2 | ''pc:4,pc+1:4,hl:8,[5]''
| rowspan=2 | '''pc:4,pc+1:4,hl:8,[5]'''
|-
|-
| CPD/CPDR
| CPD/CPDR
|-
|-
| INI/INIR
| INI/INIR
| rowspan=2 | pc:4,pc+1:4,ir:1,IO,hl:3,[hl:1 x 5]
| rowspan=2 | pc:4,pc+1:4,'''ir''':1,IO,hl:3,['''hl''':1 ×5]
| rowspan=2 | ''pc:4,pc+1:5,IO,hl:3,[5]''
| rowspan=2 | '''pc:4,pc+1:5,IO,hl:3,[5]'''
|-
|-
| IND/INDR
| IND/INDR
|-
|-
| OUTI/OTIR
| OUTI/OTIR
| rowspan=2 | pc:4,pc+1:4,ir:1,hl:3,IO,[bc:1 x 5]
| rowspan=2 | pc:4,pc+1:4,'''ir''':1,hl:3,IO,['''bc''':1 ×5]
| rowspan=2 | ''pc:4,pc+1:5,hl:3,IO,[5]''
| rowspan=2 | '''pc:4,pc+1:5,hl:3,IO,[5]'''
|-
|-
| OUTD/OTDR
| OUTD/OTDR

Navigation menu