REMEMOrizer includes the following hardware :-
REMEMOrizer provides :-
In these pictures, ROMs are shown with their names and are 8KB in size and RAM pages are assigned letters and are 16KB in size. RAM pages α to δ are the normal 64KB present in an MTX512. RAM pages a to t are extra pages, which are used as 320KB of RAM Disc.
MTX+REMEMOrizer logical memory map, as seen in RELCPMH=0
mode :-
R2,R1,R0 | 0x0000..0x1fff | 0x2000..0x3fff | 0x4000..0x7fff | 0x8000..0xbfff | 0xc000..0xffff | P3,P2,P1,P0 |
---|---|---|---|---|---|---|
0 | OS * | BASIC * | γ ** | β *** | α *** | 0 |
1 | ASSEM | a | δ ** | 1 | ||
2 | ROM 2 ***** | c | b | 2 | ||
3 | e | d | 3 | |||
4 | CP/M boot ROM | g | f | 4 | ||
5 | SDX ROM **** | i | h | 5 | ||
6 | k | j | 6 | |||
7 | m | l | 7 | |||
o | n | 8 | ||||
q | p | 9 | ||||
s | r | A | ||||
t | B | |||||
C | ||||||
D | ||||||
E | ||||||
any SRAM | any SRAM | F |
* These ROMs are the REMEMOrizer modified versions if the ROMs jumper is set "high", or if set "low" the MTX motherboard is expected to provide them.
** These RAM pages are provided by REMEMOrizer if the MTX500 jumper is set "high", or if set "low" the MTX motherboard is expected to provide 64KB RAM.
*** These RAM pages are provided by REMEMOrizer if the MTX500 jumper is set "high", and the ROMs jumper is set "high" and REMEMOrizer notices that your MTX doesn't have any memory at all!
**** The first 6KB of this ROM contains regular SDX ROM code. The next 1KB has been stolen, and contains virtual tape code. The last 1KB is in fact writable, and contains virtual tape variables!
***** REMEMOrizer with bitstream r3 onwards can emulate a ROM card with
upto 16 sub-pages, but to do this it must commandeer RAM disk pages e to t.
Page register 4 (port 0xd3
) is used to enable this feature,
decide whether the ROM is writeable, decide whether writes to the sub-page
register should be allowed to change which SRAM page is visible, and specify
the visible SRAM page.
MTX+REMEMOrizer logical memory map, as seen in RELCPMH=1
mode :-
0x0000..0x3fff | 0x4000..0x7fff | 0x8000..0xbfff | 0xc000..0xffff | P3,P2,P1,P0 |
---|---|---|---|---|
δ ** | γ ** | β *** | α *** | 0 |
a | b | c | 1 | |
d | e | f | 2 | |
g | h | i | 3 | |
j | k | l | 4 | |
m | n | o | 5 | |
p | q | r | 6 | |
s | t | 7 | ||
8 | ||||
9 | ||||
A | ||||
B | ||||
C | ||||
D | ||||
E | ||||
any SRAM ****** | any SRAM | any SRAM | F |
In RAM page 15, it is possible to address any 16KB page of SRAM.
Which page is visible at 0x4000..0x7fff
is controlled by page
register 1 (port 0xd0
), and
which page is visible at 0x8000..0xbfff
is controlled by page
register 2 (port 0xd1
).
****** REMEMOrizer with bitstream r3 onwards can address any SRAM page in
0x0000-0x3fff
, as controlled by page register 3
(port 0xd2
).
REMEMOrizer SRAM physical memory map :-
Address | Page(s) | Content |
---|---|---|
0x00000..0x03fff | 00 | RAM page α *** |
0x04000..0x07fff | 01 | RAM page β *** |
0x08000..0x0bfff | 02 | RAM page γ ** |
0x0c000..0x0ffff | 03 | RAM page δ ** |
0x10000..0x1ffff | 04..07 | virtual cassette area, read/write tape |
0x20000..0x2ffff | 08..0B | virtual cassette area, read-only tape |
0x30000..0x7ffff | 0C..1F | RAM pages a to t, RAM Disc area |
This physical map is shown as it is what you see through the "any SRAM" windows in the normal memory map, when you set the page registers appropriately.
Note that although REMEMOrizer provides images of ROMs,
and although ROM stands for Read-Only Memory,
there is in fact a backdoor allowing programs such as
REZPATCH.COM to unprotect ROMs,
write to them, and then protect them again.
Cassette tape
By default REMEMOrizer does not load from or save to cassette tape.
Almost all of the Memotech library on cassette has been converted into
.MTX
file format.
Instead, REMEMOrizer supports "virtual cassette tapes".
"Virtual cassette tapes" are implemented as hidden areas of SRAM, per the diagram above. There are two virtual tapes, 64KB each.
Looking at the known library of Memotech cassettes, almost all of them will fit within 64KB.
The REMEMOrizer supplied OS ROM is patched to jump to virtual tape code at the end of SDX ROM 5. The last portion of this ROM is in fact writeable, and is where the virtual tape logic keeps its variables.
Virtual cassette tapes are accessed from CP/M using the
REZTAPE command.
SD Card
REMEMOrizer includes an SD Card. SD Cards between 64MB and 1GB are supported. Only 64MB of data may be stored on them. REMEMOrizer considers them to contain 8 8MB partitions. This is somewhat generous, as the entire Memotech software library will fit comfortably within one 8MB partition.
It accesses this using an SPI interface.
It has hardware support for driving the SPI interface so that byte transfer
speed is effectively limited by the Z80.
It has a novel feature in that reading data from SPI on one port triggers
the sending of an 0xff
byte to trigger the next transfer.
The means that reading of data from SD Card needn't be twice as slow
as writing it (as it would otherwise be).
Compared to REMEMOTECH, REMEMOrizers SD Card is slow. REMEMOTECH can transfer one byte in 21T cycles (at upto 25MHz), because the SPI logic is driven at 50MHz. REMEMOrizer can transfer one byte in 28T cycles (at 4MHz), because the SPI logic has to be driven from the CPU clock. Having said this, in a simple speed trial REMEMOrizer is still over 4x faster than SDX floppy, and massively faster than cassette tape.
Unfortunately the fact that CP/M sectors are 128 bytes and SD Card blocks are 512 bytes makes the whole thing somewhat inefficient. To read a 128 byte sector, we must read the enclosing 512 byte block. And to write a 128 byte sector, we must read the enclosing 512 byte block, modify a part of it, then write it back. Even with this handicap, its still usable. Clever driver software helps improve things.
A green LED flashes when SD Card is being accessed and for a couple of seconds afterwards, and the intent is that the user doesn't remove the SD Card until the LED goes off. This simple feature allows the SD Card driver code to go faster.
Note that the net suggests that 8MB is the largest disk size CP/M 2.2
can cope with, due to how it does its internal arithmetic.
Even if you could go larger than this, you'd start to have memory problems,
as CP/M keeps allocation and check vectors in (scarce) high memory,
and these are related to the size of the disk.
80 column card
REMEMOrizer implements a video card which is largely compatible with the original FDX 80 column card.
It outputs in 8 colours to VGA, 640x480 at 60Hz. I have no plans to output RGB or Composite video, like the FDX did.
In addition to the normal 80x24 mode, it also supports 80x48 mode. To do this it has 8KB of memory, rather than 4KB.
It supports accesses to ports 0x30, 0x31, 0x32, 0x33, 0x38 and 0x39. Inputting from port 0x30 does not cause the bell to ring.
It emulates a subset of the 6845 CRTC registers (as per datasheet), specifically registers 10, 12, 13, 14 and 15. In addition, it has REMEMOrizer special register 31, in which bit 0 controls whether it is 80x24 or 80x48.
The normal Memotech alphanumeric font is present in on-chip ROM in
the FPGA.
The graphics characters are programmatically generated from the graphic
character number, saving 2.5KB of scarce on-chip memory.
Speculator
The original Speculator used a small RAM representing Spectrum hardware. This sat behind a small number of Spectrum specific ports. Ports also existed to allow the Speculator code the opposite kind of access.
eg: Spectrum code outputs to port 0FEH to set the border colour. Speculator code inputs from port 07EH to read this value, then programs VDP register 7.
REMEMOrizer r3 implements ports 01FH, 0FEH, 07EH, 07FH, 0FBH, 0xxFEH, 0xx7EH, and 0FFH. The xx's signify that a full 16 bit I/O address decode is performed, which is needed for the emulated keyboard support.
The original Speculator hardware partially address decodes ports x11xxxxx and xxx11111 binary, and the original Speculator code tests all these port ranges. We therefore require modified Speculator code which doesn't test everything, much as the Tatung Einstein version didn't.
REMEMOrizer implements the delayed NMI mode correctly (port 0FFH, bit 0). When this bit is enabled, an NMI leaks out, as per original Speculator hardware, and as relied upon by Speculator software.
The hardware support above is much in line with the emulation of Speculator hardware provided in MEMU.
REMEMOrizer tries to implement the NMI immediate bit (port 0FFH, bit 1). Unfortunately the INT_n signal from the motherboard is very dirty and I must deglitch it - by the time I have done this, I am too late, the maskable interrupt will have started to run. Tornado Low Level is the only known game to need immediate NMI mode.
REMEMOrizer also implements a second NMI enabled bit (port 0FFH, bit 2). NMIs are enabled if either NMI enabled bit is set. When this bit is enabled, an NMI does not leak out. REZSPEC will disable interrupts before switching to ROM 2 and enable after switching back, and we need this not to cause spurious interrupts.
REMEMOrizers Speculator support also records how many T states there were between sound port transitions, and makes this readable via ports 0B0H and 0B1H.
REMEMOrizers Speculator support also includes hardware intended to improve screen update. The hardware snoops the Z80 bus and records which character cells and attributes have been changed, attribute values, and where the flashing attributes are. It provides port access for selecting screen rows, determining the position and length of runs of changed cells, and marking cells as unchanged. It also calculates the Z80 address and VDP address for the start of the run.
It should be possible to run the SPEC1A.mtx
Speculator
software, although once loaded, it will try to load a Spectrum game from tape.
Alternatively, use REZSPEC.
Accelerator
REMEMOrizer r3 includes a rudimentary arithmetic accelerator. Once enabled this appears in ports 0A0H to 0A5H.
The accelerator uses quite a lot of FPGA resources, and as a result the FPGA is pretty much full.
It supports 32 bit integers (unsigned and signed).
It also supports the MTX BASIC floating point format. This is a 5 byte format, comprised of
A floating point value is of the form :-
(-1)^s * 1.m * 2^(e-81H)
so 5.0 would be :-
(-1)^0 & 1.01 *2^(83H-81H)
and would represented by MTX BASIC in memory as :-
offset value meaning 0 00 mantissa bits -24..-31 1 00 mantissa bits -16..-23 2 00 mantissa bits -8..-15 3 20 sign is 0, and mantissa bits -1..-7 4 83 exponent
Zero (both integer and floating point) has the special representation
of 00 00 00 00 00
.
The hardware supports an 8 element stack and includes forth-like operations to manipulate it.
The C_LIT
operation pushes 0 onto the stack.
The top-of-stack can then be modified to your desired value
by writing to ports, or by using other operations that explicitly set it.
The hardware doesn't bounds check the use of the stack. Its up to you to ensure you don't push or pop too many times.
The hardware supports these operations :-
C_INIT
(sets result to R_OK
and empties stack),
C_OK
(sets result to R_OK
)
C_LIT
,
C_DUP
,
C_DROP
,
C_SWAP
,
C_OVER
C_1
(sets top of stack to 1)
C_INEG
,
C_INOT
,
C_ILSL
,
C_ILSR
,
C_IASR
,
C_IABS
,
C_ISGN
C_IADD
,
C_ISUB
,
C_UMUL
,
C_SMUL
,
C_UDIV
,
C_SDIV
,
C_UMOD
,
C_SMOD
C_HMUL
C_1P0
(sets top of stack to 1.0),
C_2P0
(sets top of stack to 2.0),
... and many other useful constants
C_FNEG
,
C_FABS
,
C_FSGN
,
C_FINT
C_FADD
,
C_FSUB
,
C_FMUL
,
C_FDIV
C_UTOF
,
C_FTOU
Division by zero is detected.
You may wonder why there are separate C_UMUL
and
C_SMUL
.
They do produce the same bit pattern, but only in the bottom 32 bits.
The accelerator computes a full 64 bit product, and you can use the
C_HMUL
operation to push the high 32 bits on to the stack.
The floating point calculations incorporate rounding, so (1.0/3.0)*3.0 does evaluate to 1.0, rather than 0.9999..
The floating point calculations do also detect overflow and underflow conditions.
After instructing an operation, reading result register returns
R_BUSY
until the operation completes, and then it finally returns
R_OK
, R_DIV0
, R_OVER
or
R_UNDR
.
Most operations take a cycle or two, and as this is much quicker than
the Z80 can issue instructions, there is no point in polling.
However, the divide and modulo related instructions take 34 cycles.
INCLUDE PORTS.INC ; P_ port values INCLUDE NUMACCEL.INC ; C_ command and R_ result values ; enable accelerator IN A,(P_RIZEQ) OR 40H OUT (P_RIZEQ),A ; push 1.0, ie: + 1.0 x 2^0 LD A,C_LIT OUT (P_NCMD),A LD A,081H OUT (P_EXP),A LD A,000H OUT (P_MAN3),A OUT (P_MAN2),A OUT (P_MAN1),A OUT (P_MAN0),A ; push 3.0, ie: + 1.1 x 2^1 LD A,C_LIT OUT (P_NCMD),A LD A,082H OUT (P_EXP),A LD A,040H OUT (P_MAN3),A LD A,000H OUT (P_MAN2),A OUT (P_MAN1),A OUT (P_MAN0),A ; fdiv LD A,C_FDIV OUT (P_NCMD),A WAIT: IN A,(P_NRES) CP R_BUSY JR Z,WAIT ; with these operands, the result will be R_OK ; with other operands, could be R_DIV0, R_OVER or R_UNDR ; query the top-of-stack value IN A,(P_EXP) ; will be 7F IN A,(P_MAN3) ; will be 2A IN A,(P_MAN2) ; will be AA IN A,(P_MAN1) ; will be AA IN A,(P_MAN0) ; will be AB (note rounding) ; ie: + 1.01010101.. x 2^-2 ; drop the result LD A,C_DROP OUT (P_NCMD),A
REZNUMT.COM is a test for the
accelerator, and
REZNUM.COM is a program which
enables the accelerator and patches the MTX BASIC ROM to use it.
Sound
The Memotech sound chip is programmed by writing to port 6 and strobing the output by reading from port 3.
On a normal MTX, when you input from port 3, the result is 3. There is nothing in the circuit to explicitly ensure this is the case. Its just happens that way - the 3 is still on the bus from the opcode fetch, so thats the data which is returned. Wait a short while, and the bus will float high.
With REMEMOrizer the bus floats high much quicker, probably as a consequence of the FPGA pulling it high, so without any further intervention, inputting from port 3 returns 0xff.
This is a problem, because Pothole Pete and Son of Pete both input from port 3 and keep doing so until the result is 3. Its as if the programmers believed that getting 3 back indicates the operation was complete.
Accordingly, REMEMOrizer will explicitly drive the bus to the value 3 when you input from port 3.
This same hack appears in MEMU and REMEMOTECH.