~vertigo/forthbox

9c012acdbfe8dc37dd67d3abd3dc3307ad1f9835 — Samuel A. Falvo II 4 months ago b1026ae
Fix KIA->KIA-II emulation, update docs
5 files changed, 286 insertions(+), 16 deletions(-)

M emulator/CMakeLists.txt
A emulator/fbemu-hw.7.d
M emulator/fbemu-io.7.d
M emulator/fbemu.1
M emulator/main.c
M emulator/CMakeLists.txt => emulator/CMakeLists.txt +9 -2
@@ 13,7 13,7 @@ install(TARGETS fbemu)

add_custom_target(
	man ALL
	DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fbemu-io.7 ${CMAKE_CURRENT_BINARY_DIR}/fbemu.1 ${CMAKE_CURRENT_BINARY_DIR}/fbemu-imf.7
	DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fbemu-io.7 ${CMAKE_CURRENT_BINARY_DIR}/fbemu.1 ${CMAKE_CURRENT_BINARY_DIR}/fbemu-imf.7 ${CMAKE_CURRENT_BINARY_DIR}/fbemu-hw.7
)

add_custom_command(


@@ 35,7 35,14 @@ add_custom_command(
	MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/fbemu.1
)

add_custom_command(
	OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fbemu-hw.7
	COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/mkman ARGS ${CMAKE_CURRENT_SOURCE_DIR}/fbemu-hw.7.d ${CMAKE_CURRENT_BINARY_DIR}/fbemu-hw.7
	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
	MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/fbemu-hw.7.d
)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fbemu.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man1)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fbemu-io.7 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man7)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fbemu-imf.7 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man7)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fbemu-hw.7 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man7)

A emulator/fbemu-hw.7.d => emulator/fbemu-hw.7.d +221 -0
@@ 0,0 1,221 @@
.TH FBEMU-HW 7
.SH NAME
fbemu-hw \- ForthBox Z80 I/O Bridge

.SH SYNOPSIS
.EX
    .as
    .xl
    LDY #number_chars
again:
    LDA $FF0000
    AND #$01
    BNE again
    LDA $FF0001
    STA buffer,X
    INX
    STA $FF0001
    DEY
    BPL again
.EE

.SH DESCRIPTION
The hardware I/O region exposes the
128KiB of memory-mapped status and control resources
available on the RC2014 backplane connector.

The RC2014 backplane connector exposes a Z80-compatible bus running at 7.3728MHz.
Two 64KiB regions are accessible over this bus: one dedicated to I/O, and one dedicated to memory.
Since the ForthBox relies on its own local memory resources to function,
both 64KiB regions are available to the ForthBox as I/O resources.
.IP $FE0000-$FEFFFF 24
Z80 RAM space.
.IP $FF0000-$FFFFFF 24
Z80 I/O space.
.PP
Unlike the "emulator" I/O space,
which allows the software running on the ForthBox
to communicate with the host through the emulator itself,
the "hardware" I/O space provides
.I "virtualized hardware"
which can appear in a real-world logic.

Most Z80-compatible peripherals only decode the lower 8-bits of the address (A0-A7),
leaving the upper address bits (A8-A15) unused.
This creates some difficulty in knowing where to place I/O adapters in I/O space.
For example,
if such a peripheral appears at I/O address $FF0060,
it will usually also be available at
$FF0160, $FF0260, ..., $FFFE60, and $FFFF60.
Check with the peripheral's documentation to see how, or even
.IR if ,
the upper address byte is used.
It might turn out not to be used at all.

For devices which only decode the lower address byte, it is
.I strongly
recommended that you access the device with the upper address byte set to $00.

.SH FORTHBOX PERIPHERALS

When ForthBox boots up,
it expects to find certain peripherals residing at specific addresses.
The addresses are as follows:

.IP $FFx000-$FFx001 24
KIA #1 (PS/2 Keyboard Port)
.IP $FFx100-$FFx101 24
KIA #2 (PS/2 Mouse Port)

.SH PROGRAMMING THE KEYBOARD INTERFACE ADAPTER (KIA-II)

.B NOTE:
The KIA-II core is
.B NOT
compatible with the original KIA core.

Given a slight misnomer for convenience sake,
the Keyboard Interface Adapter really provides a read-only conduit
through which bytes transferred over PS/2 ports can be read
and processed with great ease.

The KIA core provides two registers laid out like so when reading:

.begin dformat
style bitwid 0.5
style recspread 0
style addr off
$00
    --8 STAT
$01
    --8 DATA
.end

and like so when writing:

.begin dformat
style bitwid 0.5
style recspread 0
style addr off
$00
    --8 POP
$01
    --8 ignored
.end

Each of the registers works as follows.

.begin dformat
STAT (r/o)
    7-2 0
    1 F
    0 E
.end
The STAT register provides a view into the overall status of the keyboard input queue.
The
.B F
bit is set if the queue can no longer accept new input from the keyboard.
The
.B E
bit is set if the queue can no longer provide data to the processor (queue is
.IR empty ).

.begin dformat
DATA (r/o)
    7-0 data
.end
The DATA register provides a view into the head of the keyboard input queue.
Note that reading from this register
.I "does not"
pop the queue.
To pop the queue, you'll need to write a byte to the POP register.

The data stream delivered through this register
consists of raw PS/2 keyboard (or, if applicable, PS/2 mouse)
data sequences.
The KIA core does not attempt to interpret the data in any way;
it is
strictly
the responsibility of the device driver software to interpret the data as it deems appropriate.

.begin dformat
POP (w/o)
    7-0 ignored
.end
The POP register, when written to (value is ignored), causes the keyboard queue to pop its head value.

.SH RECOMMENDED PERIPHERAL DESIGN GUIDANCE

If you're looking to make a peripheral that works on any RC2014-compatible bus,
including the expansion bus for the ForthBox,
you should follow these rules to minimize bus contention:
.IP 1. 3n
Decode A1-A7 at a minimum.
Use a 74HCT688 magnitude comparator to select the device.
This allows up to 128 peripherals to be accessed via the I/O bus.
.IP 2. 3n
.B Always decode addresses from high bit to low bit; never the reverse.
This guarantees your device will appear on a natural power-of-two boundary.
For example, a peripheral that decodes A2-A7 will always appear on a boundary of four;
meanwhile, a device that decodes A0-A5 will appear to have its registers scattered like buckshot
throughout the I/O address space, interfering with the configurability of other compliant devices.
For everyone's sanity,
.I please,
.B always
do the former;
.B never
do the latter.
.IP 3. 3n
Use DIP switches (or similar) to let the operator configure where in the I/O space the device resides.
Always provide enough DIP switches to cover the address space you decode.
.B "Do not"
take the approach that IBM, et. al. have done with the ISA bus,
where
.I classes
of devices are assigned specific
.I ranges
of address space in a scattered manner.
People remember the difficulty of configuring ISA cards;
this in large part is because it was hard to keep the I/O space allocation entirely in your head.
Don't do this; make it easy for people to keep track of things, and to place devices where they need/want.
.IP 4. 3n
Try
.B hard
to use as few registers as possible.  They are a
.I very
scarce resource.
For example,
use one register as a command or secondary address register, and the other register as a data register.
While inconvenient for programming purposes,
a lot of functionality can be packed into a small area in I/O space.
.IP 5. 3n
If multiple instances of a type of device is desired,
but you're concerned about how much space the peripheral as a whole would take in I/O space,
then this tip might offer some useful guidance.
Offer a unique command and data register pair for each instance.
Map A0 to register select 0; A8 to register select 1; A9 to register select 2; etc.
This allows a kind of "vertical stacking" of devices in I/O space.
.PP
For example,
each KIA exposes only two registers: a status and a data register.
The status is read-only, while the data register is read/write.
When reading from the data register, it answers with the head of the keyboard queue.
If you write to the data register, the value is ignored;
however, the queue is popped, thus revealing the next available byte (if any).

The VDC-II core also exposes only two registers.
The first is a combined address (on write) or status register (on read).
The second is a bi-directional data register,
accessing resources addressed by the most recently written address.
The status provides time-sensitive information like vertical sync status.

Some devices, like some early Intel parts intended for the 8080, are
.IR command-oriented .
To frame one command from another,
one approach is to write the first byte of a command to one register,
and all remaining bytes to the other.
In this way, address bit A0 is kind of used as a "this byte is the start of a new command" flag.

.SH SEE ALSO
.IR fbemu (1)


M emulator/fbemu-io.7.d => emulator/fbemu-io.7.d +20 -2
@@ 16,6 16,13 @@ LDA $00FE00,X

.SH DESCRIPTION
The address range from $00FE00 to $00FEFF contains the emulator I/O page.
This I/O page
provides a memory-mapped means
by which software running
on the virtualized ForthBox
can communicate
with the host PC on which it is running.
.PP
The page has the following layout.
.begin dformat
style fill on


@@ 86,7 93,7 @@ DMAREG (r/w)
.end
The DMAREG register is used to select
which byte in the $00FE00-$00FEFF address range
is mirrored throughout the DMA/Block Move bank ($FF0000-$FFFFFF).
is mirrored throughout the DMA/Block Move bank ($FD0000-$FDFFFF).
This facility,
for example,
lets you transfer an entire block of data to or from a DASD


@@ 315,13 322,24 @@ Reboot:
    LDA #511            ; Read 512 bytes (one block) from disk0.
    LDX #$0000
    LDY #$4000
    MVN $FF,$00
    MVN $FD,$00

    SEC                 ; Switch back to 6502 mode and dispatch to boot loader.
    XCE
    JMP $4000
.)
.EE

Note that this example relies on the DMAREG feature, which is deprecated.
A future release of this document will illustrate recommended code.

.SH BUGS
The DMAREG feature is deprecated;
.B "do not depend on the DMAREG feature."
The DMAREG feature's block move bank formerly resided in bank $FF.
As of this release, the page now resides in $FD.
.B "This feature is slated for removal in a future release."

.SH SEE ALSO
.IR fbemu (1)


M emulator/fbemu.1 => emulator/fbemu.1 +9 -5
@@ 26,18 26,22 @@ This emulator supports the following memory map layout:
.IP $000000-$00FDFF 24
RAM.
.IP $00FE00-$00FEFF 24
I/O.  See
"Emulator" I/O.  See
.IR fbemu-io (7)
for more information.
.IP $00FF00-$FDFFFF 24
.IP $00FF00-$FCFFFF 24
RAM.
.IP $FE0000-$FEFFFF 24
Bitmapped frame buffer.
.IP $FF0000-$FFFFFF 24
.IP $FD0000-$FDFFFF 24
DMA/Block Move Space.
See
.IR fbemu-io (7)
for more information.
.IP $FE0000-$FEFFFF 24
Bitmapped frame buffer.
.IP $FF0000-$FFFFFF 24
"Hardware" I/O.  See
.IR fbemu-hw (7)
for more information.
.PP
If the
.I -t

M emulator/main.c => emulator/main.c +27 -7
@@ 113,16 113,16 @@ uint8_t kia_read(KIA_t *pk, uint32_t addr) {
	}
}

void kia_write(KIA_t *pk, uint32_t addr, uint8_t _unused_value) {
void kia_write(KIA_t *pk, uint32_t addr, uint8_t value) {
	int reg = addr & 1;

	switch(reg) {
	case 0:
		// does nothing
		if(value == 0) kia_pop(pk);
		break;

	case 1:
		kia_pop(pk);
		// does nothing
		break;
	}
}


@@ 358,8 358,18 @@ uint8_t MEM_readMem(uint32_t addr, uint32_t unused_timestamp, uint32_t emul_flag

	if ((addr >> 16) == 0xFE)
		return video_read_ram(addr);
	if(addr >= 0xFFF000 && addr <= 0xFFFFFF)
		return kia_read(g_kia1, addr);

	if(addr >= 0xFF0000 && addr <= 0xFFFFFF) { // Z80 I/O space
		int ioaddr = addr & 0xFF;
		switch(ioaddr) {
		case 0: case 1:
			return kia_read(g_kia1, addr);

		default:
			return 0xFF;
		}
	}

	if (addr >= 0xFE00 && addr < 0xFF00) {
		/* Don't cause I/O when using debug trace read */
		if (g_debug)


@@ 384,8 394,18 @@ void MEM_writeMem(uint32_t addr, uint8_t value, uint32_t unused_timestamp)

	if ((addr >> 16) == 0xFE)
		video_write_ram(addr, value);
	else if (addr >= 0xFFF000 && addr <= 0xFFFFFF)
		kia_write(g_kia1, addr, value);
	else if (addr >= 0xFF0000 && addr <= 0xFFFFFF) { // Z80 I/O space
		int ioaddr = addr & 0xFF;
		switch(ioaddr) {
		case 0: case 1:
			kia_write(g_kia1, addr, value);
			break;

		default:
			// do nothing
			break;
		}
	}
	else if (addr >= 0xFE00 && addr < 0xFF00)
		io_write(addr, value);
	else if (addr < sizeof(ram))