Segmented memory on Z80
It might be interesting to think of a way to extend the Spectrum's memory using a segmented model similar to the 80x86 rather than manually keeping track of pages.
By decoding the address bus having at least three segments, Data, Code and Stack operation could be relatively transparent. The memory window might live in the top 32k of RAM and the device would monitor the MREQ and M! signals and decode the instruction on the bus. It would need to provide some additional instructions by using empty areas in the Z80 instruction set to select or switch segments.
This would all require that the device be able to decode Z80 instructions to decide which segment was going to be used but once that's done everything else should fall into place. Luckily, the Z80 instruction set is relatively easy to decode and should require the minimum of logic.
The easiest way to test this out would be to rig up something that would decode a small subset of instructions and see if it works.
It also needs to be decided what kind of granularity the segments are going to have. In the x86 it's 16 bytes, which extends the addressing range to 20 bits or one megabyte. Will that be enough for the Spectrum, or should we make it a definable register in the device?
Starting off I would stick with the code, data, stack and extended segment registers; CS, DS, SS and ES but we could always define additional ones if it was useful.
The plan then would be to be able to extend the assembler mnemonics so that you could write code such as the following (assuming the segments were aligned on 16-byte boundaries):
; Standard screen clearing routine
CLRSCR:
LD DS,$400
LD ES,DS
LD HL,0
LD DE,HL
LD BC,$1AFF
INC DE
LD (HL),L
LDIR
RET
; A faster screen clear routine
FASTCLR:
LD A,I ; Get IFF2 into P/V flag
PUSH AF
DI ; Disable interrupts
LD CS:(SPTMP+1),SP ; Save SP
LD CS:(SSTMP+1),SS ; Save SS
LD B,192+24
LD SS,$400
LD SP,$1B00
LD HL,0
L0: PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ L0
SSTMP: LD SS,0
SPTMP: LD SP,0
POP AF
RET P
EI
L1: RET
; Copy a screen buffer from $A0000 into the display file at $4000
CPYBUF:
LD ES,$A000
LD HL,0
LD DS,$400
LD DE,0
LD BC,1B00
LDIR
Block move and related instructions would by default use ES as the source segment and DS as the destination segment, with HL as the source offset and DE as the destination offset as normal.
Theory of operation:
- Use the CLK signal to synchronise with the clock supplied to the Z80
- Monitor the M1 line to see when an instruction has been fetched
- Is WR active?
- If not. sniff the data bus to see what instruction is being taken out of RAM
- Is the instruction extendable or a prefixed opcode?
- If not, reset the segment override and go to #2
- Is it a segment override prefix?
- If so, set the segment override and go back to #2
- If the current instruction is a memory read provide the appropriate data on D0-D7
- If it's a store then take the value off D0-D7 and place in the correct memory location
- Go back to #2
Links