Hardware

REMEMOrizer includes the following hardware :-

Memory

REMEMOrizer provides :-

Memory map pictures

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,R00x0000..0x1fff0x2000..0x3fff0x4000..0x7fff0x8000..0xbfff0xc000..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..0x7fff0x8000..0xbfff0xc000..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..0x03fff00 RAM page α ***
0x04000..0x07fff01 RAM page β ***
0x08000..0x0bfff02 RAM page γ **
0x0c000..0x0ffff03 RAM page δ **
0x10000..0x1ffff04..07 virtual cassette area, read/write tape
0x20000..0x2ffff08..0B virtual cassette area, read-only tape
0x30000..0x7ffff0C..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.

Patchable ROMs

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.

Data types

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.

Stack

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.

Operations

The hardware supports these operations :-

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.

Sample code

        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.