Difference between revisions of "Lo-tech XT-CF Board Technical Reference"

From Lo-tech Wiki
Jump to navigation Jump to search
Lo-tech>James
(Updated to reflect new name)
 
m
 
(One intermediate revision by one other user not shown)
Line 244: Line 244:
 
ret
 
ret
 
</source>
 
</source>
 
== Downloads ==
 
 
*Standard CPLD code [ XSVF file] and [ design files]
 
**A direct port of [[Vintage-Computer XT-IDE Board|the original XT-IDE controller design]]
 
**Port-based byte IO
 
**Universal BIOS controller type "Original XT-IDE"
 
**Performance: 130KB/s at 4.77MHz
 
*Enhanced CPLD code [ XSVF file] and [ design files]
 
**Supports port-based word IO
 
**Supports controller-ID byte at port base+0Fh (returns 03h)
 
**Universal BIOS controller type "Fast XTIDE (CPLD v2 project)"
 
**Performance: 200KB/s at 4.77MHz
 
*Memory-Mapped CPLD code [ XSVF file] and [ design files]
 
**Supports port-based and memory-mapped word IO
 
**Supports controller-ID byte at port base+0Fh (returns 04h)
 
**Universal BIOS controller type "Fast XTIDE (CPLD v2 project)"
 
**Performance: up to 300KB/s at 4.77MHz
 
  
 
== See Also ==
 
== See Also ==
  
 
*[[lo-tech XT-CF Board]]
 
*[[lo-tech XT-CF Board]]

Latest revision as of 17:36, 17 August 2022

Technical Reference manual for the lo-tech XT-CF Board.

CPLD Code Design

The CPLD code has been developed using Xilinx ISE version 13, using schematics, for the XC9572XL CPLD device. The starting point was the code generated by Dangerous Prototypes forum member Pietja, for use with the Dangerous Prototypes XT/IDE v2 board.

ROM_DECODER Module

The ROM decoder matches the PC address bus against the configuration of appropriate DIP switches, providing an asserted (low) signal output when a match is found, that signal being used to enabled the board ROM. It is a direct transcription of a 74521 or 74688 chip, output being disabled when AEN is asserted.

Once enabled, data is transferred from the ROM itself under the control of /MEMR and /MEMW and the low 14-bits of the PC address bus.

IDE_DECODER Module

The IDE decoder matches the PC address bus against two address locations:

  • For port-based IO, A5..8 are matched to the configuration set by the DIP switches on the board (by default, base is 300h), A9 being high and A19 being low
  • For memory-mapped IO, A12..19 (the upper 8 bits) are matched to the value stored in the memory-mapped IO register (a latch), output being asserted also only if A19 in the latch has been set to 1. A10 and A11 must also be low.
  • In both cases, AEN suppresses output when asserted.

If either are matched, CS_IDE is asserted (low). Additionally, CS_IDE_MEM is asserted (low) if the memory-mapped IO address is matched.

Other outputs generate control information, for example to enable the IDE_MUX to provide the controller ID byte when a read to the controller ID port is made.

The decoder also provides mapping of PC address bus lines to the IDE interface address lines, DA0..2, according to the 'Chuck Mod' logic, whereby IDE DA0 is connected to PC address bus A3 (instead of A0, as might be expected):

PC Address Line IDE Address Line
A0 -
A1 DA1
A2 DA2
A3 DA0

This presentation leaves A0 free to be used to switch between low and high bytes of each 16-bit word, thereby enabling faster transfers via word instructions. Otherwise, for example if A3 were used to switch bytes, individual byte transfers and XOR instructions with a bit-mask would be needed, which reduces throughput (significantly).

Note that when CS_IDE is asserted based on memory-mapped IO address match, DA0..2 are held low; because of this the memory-mapped function can be used only for sector data transfers.

IDE_MUX Module

The IDE mux module provides the interface between the 8-bit PC bus, PCBD(7:0), and the 16-bit IDE bus, presented in two 8-bit components as IDE_DH(7:0) and IDE_DL(7:0). The mux works by storing a byte in a latch when required, then subsequently presenting that byte either to the PC data bus (for reads) or the IDE interface (for writes, concurrently with the data from the PC data bus to make up the required 16-bits).

The mux component also provides the logic to present the device ID byte (via port Base+0Fh) and a latch that can be loaded with the memory-mapped IO base address (also via port Base+0Fh).

The mux is enabled by IDE_DECODER module output CS_IDE, being asserted if either memory-mapped or port-mapped addresses match the configured ranges.

Since IO operations are addressed by A0..A9 and memory operations are addressed with A0..A19, use of the /MEMR and /MEMW signals (for memory-mapped IO) must be checked against the memory mapped base address specifically (given by CS_IDE_MEM). Without this check, a memory read matching the IO port range in A0..A9 could trigger some IDE function. The reverse test is not necessary for /IOR and /IOW, since A10..A19 will always be low during IO operation, hence the CS_IDE_MEM signal could not be asserted concurrently.

Port Based IO

The base port address (Base) is configured via the DIP switches. Since the logic uses A0..A4, 32 consecutive ports are used (Base+00h to Base+1Fh).

Base+ Function
00h Sector transfer (read low-byte)
01h Sector transfer (read high-byte)
02h to 0Eh IDE functions (note address line mapping)
0Fh Read controller ID byte or set memory-mapped IO base address
10h Sector transfer (write low-byte)
11h Sector transfer (write high-byte)
12h to 1Fh IDE control register access (note address line mapping)

The controller uses A0 to determine if the low or high order byte is being transferred, and A4 to determine if the operation is read or write:

  • For reads, A0 inverts IDE /CS0 and /CS1, such that the read command is driven on to the IDE bus only when accessing port Base+00h. When A0 is asserted, the two CS lines are switched, the three IDE lines DA0..2 remaining low, yielding a no-op as viewed by the IDE device. This allows the high order byte to be transferred from the latch to the PC data bus.
  • For writes, the operation of the IDE /CS0 and /CS1 inversion based on A0 is itself inverted by A3 being asserted. That is, the IDE device sees the write to port Base+10h as a no-op, allowing the low order byte to be stored in the latch, and the write to port Base+11h as a valid write. The IDE word write will consist of IDE_DL(7:0), being presented from the latch, and IDE_DH(7:0), being presented from the byte on the PC data bus.

Controller specific functions:

  • Reading from port Base+0Fh returns the controller ID byte, a fixed value coded into the CPLD logic:
    • 03h = Port-based 'enhanced' operation (word-transfer logic)
    • 04h = As type 03h, but also with memory-mapped CPLD logic
    • The original board and the 'standard' CPLD code do not return a specific value from this port
  • Memory-mapped IO is enabled by setting the high 8-bits of the required memory-mapped base address via writing to port Base+0Fh, and disabled by writing a value of 0 to the same port. The function is disabled at power-up, and can be enabled and disabled on-the-fly.

Memory Mapped IO

Memory mapped IO is a method of transferring data from a device as though it were system memory (so using A0..19 and /MEMR and /MEMW signals, instead of the /IOR and /IOW signals used for port-based IO). This technique offers a performance advantange on the 8088 CPU, since rep movsw can be used (25 cycles/word) instead of seperate load, store and loop instructions (30 to 48 cycles/word, depending on loop unrolling). (Note that 8-bit ISA bus timing is the same for both methods.)

IDE control and enquiry functions are always completed through the IO ports as detailed above, but sector data can be transferred by either port-based or memory-mapped IO (provided the controller ID port returns 04h).

To enable memory-mapped IO, the high 8-bits of the required memory-mapped base address are loaded through port Base+0Fh, noting that the MSB must be asserted. The card will then enable transfers through that base address in two ranges within a 4KB aligned 1KB block:

Base+ Function
0000h to 01FFh Read window
0200h to 03FFh Write window

Only the transfer of sector data can be performed; IDE DA0..DA2 are held low during such IO. The transfer must be made in asceding sequence since words will be committed to (or read from) the IDE interface synchronously (the card buffer is only one byte, in the IDE_MUX module).

  • As with port-based IO, the controller uses A0 to determine if the low or high byte is being transferred
  • A9 is used to determine if the operation is read or write
  • A1..A8 are not monitored, allowing the CPU to cycle though the 512 bytes in asceding order starting at zero, using rep movsw

For example, if the IO transfer window is to be D800:0000, send D8h to port Base+0Fh. Data can then be read from D800:0000-01FF, or written to D800:0200-03FF.

Once the transfer has been completed, memory-mapped IO function can be disabled by writing zero to port Base+0Fh. Note that port-based IO can be used regardless of whether memory-mapped IO is enabled or not.

Programming Examples

The following code examples illustrate block-mode transfers (i.e. of 1 to 128 sectors) for either port-mapped or memory-mapped controllers. For the sake of simplicity, the memory-mapped base address is hard-coded where required.

Block Read Example

	; Block read routine, with memory-mapped IO if supported
	; On-entry:
	; 	DX    : IDE controller base port (low 4-bits always zero)
	; 	CX    : sector count (max 128)
	; 	ES:DI : target memory buffer address
	; 
	; Corrupts: AX, CX, DX
 
	or		dl, 0x0f	; set DX to conntroller ID port (base+0Fh)
	in		al, dx		; get controller type
	cmp		al, 4		; 4 = memory-mapped capable
	jne	.PortBasedRead		; use port-based IO if not type 4
	mov		al, 0xd8	; set AL to high 8-bits of mem-mapped IO base...
	out		dx, al		; ... send high 8-bits of segment address to controller ID port ...
	xor		ah, ah		; ... and clear AH ...
	xchg		al, ah		; ... and swap AL/AH - AX now contains base address for reads
	push		ds		;
	push		si		; save DS:SI
	mov		ds, ax		; move segment address in AX to DS
	mov		ax, cx		; store total sector count in AX
	xor		cx, cx		; clear CX
.ReadTransferLoop:			; sector-by-sector transfer loop (256 words each iteration)
					; note CX is zero at start of each iteration
	mov		si, cx		; clear SI; DS:SI now has mem-mapped IO base address
	inc		ch		; memory-mapped window is 512 bytes, CX now has 256 (words)
	rep		movsw		; do the transfer from [DS:SI] to [ES:DI], count in CX (256)
	dec		ax		; reduce number of sectors still to get
	jnz	.ReadTransferLoop	; repeat, if there's more to do (zero flag set by dec)
	out		dx, al		; we're done; disable memory-mapped IO (AL will be zero)
	pop		si
	pop		ds		; restore register values
	ret
.PortBasedRead:
	xchg		cl, ch		; Sectors to words (SHL 8)
	and		dl, 0xf0	; restore value in DX (the read IO port)
	shr		cx, 1		; 
	shr		cx, 1		; 
	shr		cx, 1		; words to octa-words (16 bytes - shr 3)
.ReadNextOctaword:			; port-based IO code follows, unrolling improves throughput
	in		ax, dx		; Read 1st word
	stosw				; Store 1st word to [ES:DI]
	in		ax, dx
	stosw				; 2nd
	in		ax, dx
	stosw				; 3rd
	in		ax, dx
	stosw				; 4th
	in		ax, dx		; 
	stosw				; 5th
	in		ax, dx
	stosw				; 6th
	in		ax, dx
	stosw				; 7th
	in		ax, dx
	stosw				; 8th	
	loop	.ReadNextOctaword	; 17 clocks on 8088 hence unrolling
	ret

Block Write Example

	; Block write routine, with memory-mapped IO if supported
	; On-entry:
	; 	DX    : IDE controller base port (low 4-bits always zero)
	; 	CX    : sector count (max 128)
	; 	ES:SI : source memory buffer address
	; 
	; Corrupts: AX, CX, DX
 
	push		ds		; save DS
	mov		ax, es		;
	mov		ds, ax		; move ES to DS via AX; DS:SI now pointer to source memory buffer
	or		dl, 0x0f	; set DX to conntroller ID port (base+0Fh)
	in		al, dx		; get controller type
	cmp		al, 4		; 4 = memory-mapped capable
	jne	.PortBasedWrite		; use port-based IO if not type 4
	mov		al, 0xd8	; set AL to high 8-bits of mem-mapped IO base...
	out		dx, al		; ,.. send high 8-bits of segment address to controller ID port ...
	mov		ah, al		; ... mov AL to AH ...
	mov		al, 0x20	; ... and set AL to write offset.  AX now contains write base address
	push		es		;
	push		di		; save ES:DI
	mov		es, ax		; move segment address in AX to ES
	mov		ax, cx		; store total word count in AX
	xor		cx, cx		; clear CX
.WriteTransferLoop:			; sector-by-sector transfer loop (256 words each iteration)
					; note CX is zero at start of each iteration
	mov		di, cx		; clear DI - ES:DI now has mem-mapped IO write base address
	inc		ch		; memory-mapped window is 512 bytes, CX now has 256 (words)
	rep		movsw		; do the transfer from [DS:SI] to [ES:DI], count in CX (256)
	dec		ax		; reduce number of sectors still to write
	jnz	.WriteTransferLoop	; repeat, if there's more to do (zero flag set by dec)
	out		dx, al		; disable memory-mapped IO (AL will be 0)
	pop		di		;
	pop		es		;
	pop		ds		; restore register values
	ret
.PortBasedWrite:			; Port-based IO routine follows
	xchg		cl, ch		; Sectors to words (SHL 8)
	shr		cx, 1		; 
	shr		cx, 1		; 
	shr		cx, 1		; words to octa-words (16 bytes - shr 3)
	xor		dl, 0x1f	; restore value in DX to Base+10h
.WriteNextOctaword:			; port-based IO code follows, unrolling improves throughput
	lodsw				; Load 1st word from [DS:SI]
	out		dx, ax		; Write 1st word
	lodsw
	out		dx, ax		; 2nd
	lodsw
	out		dx, ax		; 3rd
	lodsw
	out		dx, ax		; 4th
	lodsw				; 
	out		dx, ax		; 5th
	lodsw
	out		dx, ax		; 6th
	lodsw
	out		dx, ax		; 7th
	lodsw
	out		dx, ax		; 8th
	loop	.WriteNextOctaword	; 17 clocks on 8088, hence unrolling
	pop		ds		; restore DS to entry state
	ret

See Also