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