The Multiface Two

Multiface Two (with a PCB Edge connector)

The Multiface Two is a multi-purpose expansion devices produced by Romantic Robot UK Ltd. in 1988. It has two push button: RESET and STOP. The RESET button simply reset the CPC/Plus at any time whatever the program running is. The STOP button is much more interesting, it will stop the execution of the current program and run instead the built-in MF2 toolkit (or a third party custom program).

The built-in toolkit allow you to view and modify the contents of memory and hardware registers (CRTC, Gate Array, CPU, …), save a screenshots or a complete snapshot of the CPC to tape/disc and finally, return to the program just like nothing happened.

The snapshot feature is obviously what most people were after, it made possible to save your position in a game and continue playing it later (and, of course, to make “backup copy” for your friends :). It was also widely used to cheat in games by modifying the game program in memory to get infinite energy/life/whatever.

External links

Further informations about the Multiface Two can be found on these websites:

Adverts

Amstrad Action 52 (January 1990), page 78Amstrad Action 69 (June 1991), page 45Amstar 41 (January 1991), page 40

User Manual

Description

The Multiface Two plugs in the expansion port of the Amstrad and was manufactured with a double-sided edge connector (for the Amstrad CPC) or Centronic connector (for the Amstrad Plus). Some resellers (eg. Jessico) modded themselves the MF2 with a Centronic connector so they could sell it to CPC owners with Centronic connectors (eg. Schneider CPC). The MF2 also have a thru-port, so that additional devices can be plugged into the back of the MF2. This thru-port is a double sided edge connector.

The Multiface Two has 8Kb of EPROM (where is stored the MF2 Firmware and toolkit), 8Kb of RAM and 3 custom chips (PAL) mainly programmed to monitor the I/O operations of the CPU and record them in the MF2 RAM. There are also two push-buttons, STOP and RESET. The first MF2 design had a physical toggle switch to make the device visible or invisible but the latest versions automatically handle the stealth mechanism with the RESET/STOP push-buttons.

The Buttons

Push button

The STOP button

To STOP any program, at any time, with the STOP button, here is what the Multiface Two does:

  1. Wait for the CPU to finish it's current instruction.
  2. Page it's ROM/RAM from &0000 to &3FFF.
  3. Trigger a Non-Maskable Interruption (NMI)

A Non-Maskable Interruption (NMI) is a high-priority interrupt which can not be ignored by the CPU (eg. like the 300Hz interrupt produced by the Gate Array). When the CPU detects an active NMI signal, it will save it's current interrupt status (the IFF flags), push it's PC onto the stack and jump to the NMI trap at &0066. Since the MF2 paged it's ROM from &0000 to &1FFF, the CPU will jump right into it and execute the Firmware boot-routine of the MF2.

If a Direct Jump program is installed in the MF2 (such as The Insider), it will be executed right after the MF2 firmware-boot, otherwise the built-in toolkit will appear as usual. Note that to disable a direct jump program, just press any key while pressing the STOP button.

The RESET button

The Multiface Two can RESET the Amstrad at any time. It's useful with programs that can not be exited (eg. with CTRL+SHIFT+ESC). Using the RESET button also resets the stealth mechanism of the MF2 to visible mode.

Romantic Robot used various colors for the RESET push-button (white, yellow, green, blue) but the STOP button is always the red one.

Stealth mechanism

The snapshot feature of the Multiface Two obviously pissed off many software companies, thus they started implementing counter-measures to prevent their games from running on a CPC/Plus with such evil device. This was highly ineffective as the MF2 featured a stealth mechanism to make the device invisible.

Visible

When you switch ON your Amstrad or press the RESET button, the MF2 is in visible mode. Anything requiring the MF2 will work (such as running MF2 snapshots or third party softwares like The Insider). The MF2 ROM/RAM bank can be accessed by the CPU at any time.

Invisible

To switch to the invisible mode, you just have to press the STOP button and simply return (press R in the toolkit menu) to the main program. The MF2 ROM/RAM bank is not available anymore. Since most detection routines rely on the MF2 ROM/RAM detection, this does the trick. However there's other means to detect an MF2 or undermine it's features

Screenshot

The screenshot feature of the Multiface Two allow you to take a screen capture of a running program at any time. The screenshot can be reloaded from BASIC and doesn't require any MF2 to be displayed.

The screenshot file is &408C bytes long: The first &4000 bytes hold the uncompressed screen-data and the last &8C bytes hold a small routine to copy the screen-data in the usual video RAM address (&C000-&FFFF), setup screen mode, palette and CRTC configuration (the loader disassembly is shown below).

There are some limitations to this screenshot feature:

  • The MF2 assumes that the video-RAM is always 16Kb, thus fullscreen pictures (like in Titus games or many demos) can not be saved completely.
  • The MF2 assumes that the video-RAM is always located from addresses &C000-&FFFF. However, some games and many demos reconfigure the CRTC to move the video-RAM somewhere else (eg. for a double buffer), thus the screenshot won't work (and might simply display some garbage when reloaded).
  • A screenshot saved on a CPC 464 (with the Amstrad Firmware v1) won't work properly on the later CPC models (with the Amstrad Firmware v2 and greater). You have to modify two bytes in the screenshot data to adjust the firmware palette entry (see the disassembled loader below for further details).

For some reasons, the screenshot file embed some unused code and data (probably a legacy of the MF2 prototype or the former version of the Multiface, who know for sure?). See the disassembled loader below for further details.

Reloading screenshot

You can reload a screenshot file from BASIC as follow:

MEMORY &3FFF:LOAD"SCREEN":CALL &8000

The default load address is &4000. The embedded routine at &8000 will copy the screenshot-data to &C000 (which is, with the default firmware configuration, where is located the video-RAM), setup the video mode, palette and CRTC configuration.

Converting screenshots

There are several third-party tools to convert MF2 screenshots into OCP Art Studio file-format (.SCR and .PAL).
Here are a few of them:

You can also do it very easily from BASIC:

MEMORY &3FFF:LOAD"SCREEN":SAVE"SCREEN.SCR",B,&4000,&4000

This will save the 16Kb screen-datas into a .SCR file that can be loaded with OCP Art Studio (but you will have to restore the palette manually with OCP, whereas most of the tools listed above will also a create the corresponding .PAL along with the .SCR file).

Loader disassembly

Here is a disassembly of the MF2 screenshot loader.

				org &4000
_mf2_data_screenshot_bitmap	; Here are the 16Kb screenshot data
 
				org &8000
mf2_screenshot_display:
				; Disable the interrupts
				di
 
				; Copy the screen-data to the usual
				; video RAM address (&C000).
				ld hl,_mf2_data_screenshot_bitmap
				ld de,&c000
				push hl
				pop bc
				ldir
 
				; Overwrite the current firmware palettes with
				; the screenshot palette. Note that the firmware
				; has two set of colors to manage blinking ink.
				;
				; Both sets will be overwritten with the same
				; palette from the screenshot (no blinking colors)
				ld hl,_mf2_data_screenshot_palette
				push hl
				ld bc,17
				push bc
				; Get the firmware palette entry
				ld de,(_mf2_data_firmware_palette)
				; overwrite the first ink set
				ldir
				pop bc
				pop hl
				; overwrite the second ink set
				ldir
 
				; Right after the palette data, are stored
				; the CRTC registers data.
				; Update the CRTC with these register values.
				ex de,hl
				call _mf2_screenshot_setCRTC
 
				; Set the RMR register to the saved value
				ld b,&7f
				ex de,hl
				ld c,(hl)
				out (c),c
				; Filter the saved value to keep only the
				; video-mode bits 0-1
				ld a,c
				and &03
				; Update the CPU register BC' used by the firmware
				; to store the current RMR configuration.
				exx
				; clear the previous video-mode bits
				res 0,c
				res 1,c
				; merge with the filtered value
				or c
				ld c,a
				exx
				; That's it, restore the interrupts and exit.
				ei
				ret
				; The firmware will automagically update the
				; video mode and palette on the next VSync interrupt.
 
 
 
_mf2_screenshot_setCRTC:	; Setup the 16 CRTC Registers
				ld hl,&bcbd
				ld b,&10
				jr _mf2_out
 
_mf2_screenshot_setPalette:	; Unused routine entry.
				ld hl,&7f7f
_mf2_out:
				ld c,&00
_mf2_out_loop
				push bc
				ld b,h
				out (c),c
				ld a,(de)
				inc de
				; detect Gate Array or CRTC I/O access
				ld b,l
				rl b
				ld b,l
				; If CRTC is accessed, do not filter the value
				jr c,_mf2_out_raw
				; Filter the 8bits value sent to the Gate Array
				; to set a valid INKR command bit-code.
				and &1f
				or &40
_mf2_out_raw
				out (c),a
				pop bc
				inc c
				djnz _mf2_out_loop
				ret
 
; *** Unused by the screenshot routine! ****************************************
_mf2_data_firmware_crtc		; CRTC Reg0 to Reg15 with the default firmware
				; configuration (40*25).
				db &3f,&28,&2e,&8e,&26,&00,&19,&1e
				db &00,&07,&00,&00,&30,&00,&c0,&00
 
; *** PALETTE ******************************************************************
; Hardware color for the screenshot, stored as follow
; BORDER, PEN0, ..., PEN15
_mf2_data_screenshot_palette	ds 17,0
 
; *** CRTC Configuration *******************************************************
; CRTC Reg0 to Reg15 as set when the screenshot was taken.
_mf2_data_screenshot_crtc	ds 16,0
 
; *** FIRMWARE PALETTE ENTRY ***************************************************
; This address will vary depending on the machine the screenshot was taken on.
; Firmware v1.0 (CPC464)   = &B1D9
; Firmware v2.0 and later  = &B7D4
_mf2_data_firmware_palette	dw 0
 
; *** SCREENMODE ***************************************************************
_mf2_data_screenshot_rmr	db 0

Snapshot

A Multiface Two snapshot is a complete dump of the Amstrad's memory and register state saved to tape or disc for later use (just like the snapshot feature of emulators). To achieve this, the Multiface Two is continually monitoring the CPU I/O operations to record all changes made to I/O devices registers (eg. CRTC, PPI, Gate Array, …).

Reloading snapshot

To reload a snapshot, the Multiface Two must be visible since the loader routine is mostly located in it's ROM.

RUN"SNAPSHOT

That's it, a small boot-code is loaded. It will copy the hardware context (all CPU, CRTC, etc registers state when the program was stopped) into the MF2 RAM and call the snapshot loader routine located in the MF2 ROM.

Snapshot file format

(might be done later, anyone care?)

Loader disassembly

			org &AF80
 
			; Disable the interrupt
			di
			; Enable the lower ROM mapping
			exx
			ld bc,&7F89
			out (c),c
			exx
			; Enable the MF2 memory mapping
			ld bc,&FEE8
			out (c),c
			; Copy the hardware registers dump
			; into the MF2 RAM.
			ld hl,_mf2_data_snapshot
			ld de,&3A0F
			ld bc,&00FA
			ldir
			; Copy the Z80 registers dump into
			; the MF2 RAM.
			ld de,&3EE6
			ld bc,&001A
			ldir
			; Call the Snapshot loader in the MF2 ROM.
			jp &002B
_mf2_data_snapshot

Flaws

RAM Detection

The Multiface Two has a built-in routine to detect 128Kb machines (in order to save their extra 64Kb RAM with the snapshot feature). It is executed every time the STOP button is pressed (during the MF2 firmware boot process). This RAM detection routine has a bug on 128Kb+ RAM machines (and only 128Kb+ machines! 64Kb machines are not affected) which makes possible to detect if the MF2 stopped your program or not (it is a post-STOP detection).

The routine performs two tests, the bug is lying in the later. On 128Kb machine, when you press the STOP button, a byte in the extra RAM (in bank 0 of page 0 at &4000) will be overwritten with the value &55 if it's original value was &AA. If the program being stopped is monitoring this particular byte in extra RAM, it can detect if it was stopped or not!

Here is a commented disassembly of the RAM detection routine used by the MF2:

; Disassembly of the Multiface Two RAM detection routine.
 
				org &02C5
mf2_ramCheck:
				; Page out any extended RAM banks
				call _mf2_mmr_reset
 
				; Save the current byte at &4000 in the base64k RAM
				ld hl,&4000
				ld a,(hl)
				push af
 
; *** Test 1 *******************************************************************
 
				; Put a marker (&AA) at &4000 in the base 64K RAM
				ld (hl),&AA
				; Try to page in the extra bank 0 (from &4000 to &7FFF)
				ld bc,&7fc4
				out (c),c
				; Compare the value now at &4000 with the marker's value
				ld a,(hl)
				cp &AA
				; If the values don't match, assume there's 128k
				jr nz,_mf2_ramCheck_set128k
 
				; Since the value at &4000 in the extra bank was
				; not initialized, it might matches the marker.
				; That would be unfortunate, therefore a second
				; test must be done to make sure there's definitely
				; no extra RAM available.
 
; *** Test 2 *******************************************************************
 
				; *************** EPIC FAIL ********************
				; Put a marker (&55) at &4000 in the extra bank.
				; And here's the flaw! The original value is not
				; saved but directly over-written!
				ld (hl),&55
				; **********************************************
 
				; Disable any RAM mapping
				call _mf2_mmr_reset
				; Compare the value at &4000 with the marker's value
				ld a,(hl)
				cp &55
				; If the values match, there's definitely no extra RAM.
				jr z,_mf2_ramCheck_set64k
				; Otherwise, the MF2 assume there's 128k RAM
 
; *** Set result and exit ******************************************************
 
_mf2_ramCheck_set128k
				; Page out any extra bank
				call _mf2_mmr_reset
				; Set the 128k code-result (2)
				ld a,2
_mf2_ramCheck_exit
				; Save the code-result
				ld (&3a96),a
				; Restore the value at &4000 in the base 64k RAM
				pop af
				ld (hl),a
				ret
 
				; Set the 64k code-result
_mf2_ramCheck_set64k		xor a
				jr _mf2_ramCheck_exit
 
 
				; Disable all RAM mapping
				; Output
				;	B=&7F
				;	MMR register (if available) set to &C0
				;	All other registers preserved.
_mf2_mmr_reset:
				push af
				ld a,&c0
				ld b,&7f
				out (c),a
				pop af
				ret

CPU I/O Monitor

The Multiface Two monitors the I/O operations of the CPU and records all changes made to the hardware registers (CRTC, Gate Array, PPI, etc). But there's a flaw in this monitoring system, it does a complete I/O decoding of the upper 8bits of the I/O address instead of a partial I/O decoding like the devices in the CPC/Plus do.

Therefore it is possible to cheat the MF2 by using ghost I/O addresses to access the devices (CRTC, Gate Array, PPI, etc) furtively, thus undermining the snapshot and screenshot features.

Example

From the BASIC interpreter, type and RUN the following program:

10 OUT &BC00,1  ; Select CRTC register 1
20 OUT &BD00,0  ; Set CRTC Reg. 1 to 0 (blank the screen with BORDER everywhere)
30 OUT &0D00,40 ; Set CRTC Reg. 1 to 40 (standard screen width) using ghost I/O address.

Then press the STOP button and Return to the BASIC (press R in the toolkit main menu). The screen is now totally blank!

This is because the MF2 registered that the CRTC Register 1 was set to 0 but missed it was set back to 40 with a ghost I/O address (&0D00), therefore it restored 0 when you pressed R. We could also use a ghost address to select the register, so the MF2 couldn't even tell which register you modified!

Some games (eg. Batman the movie) used this kind of tricks to set all the colors to black, which is quite annoying and difficult to restore manually.

MF2 vs ASIC Sprites

And here we have a winner! This is probably the most awful flaw of the Multiface Two (just my opinion). When the ASIC I/O page is enabled and you press the STOP button (or call manually the MF2 Firmware), it will crash the machine!

In order to display the toolkit on the screen, the MF2 uses the font stored in the Amstrad Firmware which is located in the Lower ROM. On a CPC, the Lower ROM is usually paged from &0000 to &3FFF. But the MF2 ROM/RAM is also paged from &0000 to &3FFF and overlay the Lower ROM, thus the Amstrad Firmware is simply not accessible to the CPU when the MF2 ROM/RAM is paged in.

To circumvent this problem, every-time the MF2 toolkit wants to read the firmware font, a small routine is copied to the base64Kb RAM at &4000 and called. The aim of this routine is to page out the MF2 ROM/RAM (so the Firmware in the Lower ROM is accessible for reading), fetch the font data, page the MF2 ROM/RAM back in and exits.

What's wrong with that? (besides an obvious speed issue) The ASIC is paged from &4000 to &7FFF! When the MF2 is copying the above mentioned routine at &4000, it will in fact copy it in the ASIC I/O page, more precisely, right into the hardware sprite data area. And that, is the problem. This area of the ASIC I/O page only stores the lower 4bits of any bytes written to it (the upper 4bits are automatically cleared to zero). When the CPU will read the opcodes written there to execute the routine, it will just get garbage. That's an epic failure! Besides corrupting the hardware sprite data, the machine will crash.

Here's what the routine should be and what the CPU can decode when it jump to it:

MF2 Routine in RAM

Address	Opcodes		Instruction
&4000	ED 49         	out (c),c
&4002	06 08         	ld b,#08
&4004	4E            	ld c,(hl)
&4005	23            	inc hl
&4006 	E5            	push hl
&4007	C5            	push bc
&4008	06 04         	ld b,#04
&400A	21 04 00      	ld hl,&4032
&400D	AF            	xor a
&400E	CB 01         	rlc c
&4010	30 01         	jr nc,&4013
&4012	B6            	or (hl)
&4013	23            	inc hl
&4014	10 F8         	djnz &400e
&4016	12            	ld (de),a
&4017	13            	inc de
&4018	06 04         	ld b,#04
&401A	21 04 00      	ld hl,&4032
&401D	AF            	xor a
&401E	CB 01         	rlc c
&4020	30 01         	jr nc,&4023
&4022	B6            	or (hl)
&4023	23            	inc hl
&4024	10 F8         	djnz &401e
&4026	12            	ld (de),a
&4027	13            	inc de
&4028	C1            	pop bc
&4029	E1            	pop hl
&402A	10 D8         	djnz &4004
&402C	D9            	exx
&402D	ED 49         	out (c),c
&402F	C3 91 07      	jp #0791

MF2 routine in the sprite RAM

Address	Opcodes		Instruction
&4000	0D		dec c
&4001	09		add hl,bc
&4002	06 08		ld b,&08
&4004	0E 03		ld c,&03
&4006	05		dec b
&4007	05		dec b
&4008	06 04		ld b,&04
&400A	01 02 00	ld bc,&0002
&400D	0F		rrca
&400E	0B		dec bc
&400F	01 00 01	ld bc,&0100
&4012	06 03		ld b,&03
&4014	00		nop
&4015	08		ex af,af
&4016	02		ld (bc),a
&4017	03		inc bc
&4018	06 04		ld b,&04
 
; And I cut the crap here, you get the idea...
; All the upper four bits of the opcodes are
; set to zero... FAIL!

For a ~80€ device claiming to be Plus compatible, well… that sucks. (just my opinion, again :)

Firmware

The MF2 Firmware is 8Kb long and located into a socketed EPROM inside the interface. It is not known how many different MF2 Firmware versions exist and the differences between the known MF2 Firmware are not yet documented, except for their AMSDOS compatibility:

All call addresses to the AMSDOS ROM are hard-coded into the MF2 Firmware. There are two AMSDOS versions (v0.5 on CPC and v0.7 on Plus), therefore two major MF2 Firmware versions also exist. If the MF2 Firmware doesn't match with the AMSDOS version installed in the CPC or Plus, all disc operations of the MF2 will crash the machine.

Version numbers

MF2 firmware version is &0E, that's an Amstrad CPC MF2 (designed for AmsDOS v0.5) You can check your MF2 firmware version by pressing f0 (numeric pad) in the toolkit main menu, a two digits hexadecimal number will be displayed on the screen. This version number corresponds to a simple checksum of the MF2 ROM. The checksum routine can be found at the address &1B7D in the MF2 ROM:

		org &1b7d
		; MF2 Checksum routine
		ld hl,&2000
		xor a
		ex af,af
mf2_checksum
		ex af,af
		dec hl
		xor (hl)
		ex af,af
		ld a,l
		or h
		jr nz,mf2_checksum
		ex af,af
 
		; Output:
		; A = version number

The very simple checksum algorithm used to calculate the MF2 Firmware version number is not collision resistant, thus different MF2 Firmware can actually report the same version number!

Do not trust and rely too much on this official version number.

Download

For Amstrad CPC (AMSDOS v0.5)

For Amstrad Plus (AMSDOS v0.7)

Notes:

  • Version &8D was found on an early MF2 using a toggle-switch for the stealth mode.
  • The two versions &78 are the perfect example of a checksum collision. They both report the same version number but they actually require a different AMSDOS version for their disk operations!

Thanks to SyX and CPCManiaco for the bunch of MF2 ROM dumps!

MF2 ROM Dump utility

MF2 ROM Dump utility.

You can help archiving the various MF2 firmware versions. If the firmware version of your MF2 is not listed here, you can use the following program to dump it and spread it.

Instructions
  1. Transfer it to your Amstrad CPC/Plus.
  2. Plug your MF2 to your Amstrad.
  3. Switch ON your Amstrad (or press the RESET button, the idea is that the stealth mechanism must be disabled).
  4. Run the program: RUN”MF2DUMP”

The program will detect the Multiface Two and dump it's ROM to disc (as MF2.ROM file). You just have to send me this file (or directly a DSK dump of your disc with the MF2.ROM file in it).

Mythology

Encryption

For a very very long time, I've heard of many (French-)people to believe that the 8Kb EPROM (which contains the MF2 Firmware) is encrypted, thus it can't be replaced or rewritten with your own stuff. This is a myth. You can do whatever you want with the EPROM on the MF2 as you would do with a regular EPROM!

MF2 Firmware version

It was also said that bit 7 of the MF2 Firmware version indicated which AMSDOS version the MF2 was using for all it's disc operations. This is not true. To determine the AMSDOS version required for a specific MF2 Firmware version, refer to the download list above. Thanks to NoCash for exposing that!

Compatibility issues

Amstrad CPC 464 vs 664/6128

The screenshot feature of the MF2 directly overwrites the Amstrad Firmware ink buffers to restore the colors of the picture. However, these buffers are not located at the same addresses depending on the Amstrad Firmware version. Before saving the screenshot, the MF2 determines which firmware version is available to select the appropriate addresses to use for the ink buffers and save them into the screenshot file. When the screenshot is later reloaded, it doesn't check anything and uses the saved addresses directly. That's why a MF2 screenshot saved on a Firmware v1 (ie. CPC 464) does not work properly when used on later Amstrad Firmware.

A screenshot saved on CPC 464 with the Amstrad Firmware v1 will not work on a later Firmware (v2, v3 and v4), and vice versa.

This problem can be easily fixed by modifying two bytes in the screenshot loader located at the label _mf2_data_firmware_palette:

10 REM Load a screenshot and fix it for
20 REM a v2 or greater Amstrad Firmware
30 MEMORY &3FFF
40 LOAD "mf2screen.bin",&4000
50 REM Set Palette entry to &B7D4
60 POKE &8089,&B7:POKE &8088,&D4
70 CALL &8000

AmsDOS

As already said, two major versions of the MF2 Firmware exist, one for the Amstrad CPC and one for the Amstrad Plus. To perform it's disc operations, the MF2 Firmware jumps directly into the DOS ROM (Upper ROM 7). This, usually, is considered bad programming practice because of possible compatibility problems. And that exactly what happened with the Amstrad Plus (released much later than the MF2), which has a different (updated) AmsDOS ROM, many addresses to the disc routines have changed… bummer!

Doing the right things™, ie. rewriting complete DOS routines into the MF2 firmware to not depend on third party software, would have taken much more work and also a bigger EPROM to fit it all. The interface was already quite expansive at the time, so dealing with compatibility problems, ie. updating all the DOS call addresses in the MF2 ROM, was probably cheaper and faster.

If you are experiencing bugs or crashes when saving to disc with your MF2, it's most likely that your MF2 is not compatible with the AmsDOS of your Amstrad CPC/Plus.

  • On an Amstrad Plus: you can easily replace the AmsDOS v0.7 (with the older AmsDOS v0.5 or even something better like PARADOS) using a ROMBoard (or a custom cartridge), and your CPC-MF2 will be able to save to disc again.
  • On an Amstrad CPC: well… you're screwed! The DOS ROM can not be replaced without seriously hacking the CPC motherboard (to replace or disable the built-in DOS ROM).
  • In any case, you can also replace or rewrite the EPROM of the MF2 with a compatible Firmware version.

Amstrad Plus/ASIC

The Multiface Two, designed in 1986 (ie. loooong before the Amstrad Plus was released), is really a neat and cleverly made interface for the Amstrad CPC. When the Plus range came out, Romantic Robot released an Amstrad Plus compatible version of it, but they just recompiled the MF2 Firmware for the AmsDOS v0.7 and… well, that's it! The whole interface design remains solely targeted for the Amstrad CPC.

  • The MF2 does not handle at all the Plus features (eg. extended palette, split-screen, sprites, DMAs, …).
  • The MF2 firmware conflicts with the ASIC I/O Page! (ie. Stopping a program when the ASIC I/O is paged-in will simply crash the machine).
  • If you're lucky enough to successfully STOP a Plus specific program, you will run into a lot of problems since the ASIC registers are not handled at all (the toolkit screen might be corrupted with an ASIC splitscreen/scroll or hardware sprites, experience timing problems because of the ASIC programmable interrupts, the screenshot and snapshot features won't work and finally, the main program might crash when you return back to it).

French keyboard (AZERTY)

The MF2 Toolkit assumes an English keyboard layout, therefore some keys on a French keyboard don't match (key A produces a Q, etc). Not a big deal, but still annoying when you're used to the French AZERTY thing.

Third party software

Here is a non exhaustive list of software for the Multiface Two:

  • The Insider by Romantic Robot UK Ltd.
  • Multimag by Magic Software (aka Longshot of Logon System).
  • Soundhakker by The Equalizor of STS.
  • Comparator by Martyn Davis of Verysoft.
  • Gripper by Martyn Davis of Verysoft.
  • TUSS by Richard Wildey of Sentinel Software.

I/O Decoding

No bits can escape me! MUUAAAHHAHAHA!! The Multiface Two has two I/O ports to bank switch it's ROM/RAM.

Like any regular I/O device, the Multiface Two monitors the CPU I/O requests. To do that, the MF2 performs a bit-wise decoding of the I/O address to detect if an I/O request hits one of it's I/O ports. It should be noted that most devices only check for a few bits (only one in most cases) of the 16bits I/O address whereas the MF2 checks for almost all of them!

The MF2 must be visible to use it's I/O ports! When the stealth mode is activated, the MF2 will ignore all requests to it's I/O ports (just like if it wasn't there).

MF2 I/O Port I/O Address mask I/O R/W
&FEE8: Enable ROM/RAM 11111110 1110100x W
&FEEA: Disable ROM/RAM 11111110 1110110x W
I/O Decoding:
x indicate a bit ignored by the I/O decoding.
0 indicate the bit must be clear to select the device.
1 indicate the bit must be set to select the device.
W indicate the device will respond only to an I/O Write operation.
  • Only I/O write requests on these ports will trigger their function (I/O Read requests are ignored).
  • The MF2 don't care at all about the 8bits data sent it's I/O ports (it can be anything).
  • The I/O address bit0 is not decoded and can be set to 0 or 1, therefore each port have (only) one ghost I/O address.

Programming

Accessing the MF2

The Multiface Two uses the Lower ROM mechanism of the CPC to show it's ROM/RAM bank from &0000 to &3FFF. Therefore, you must clear bit 2 of the RMR register to enable the Lower ROM and also tell the MF2 to page it's ROM/RAM using it's dedicated I/O port (&FFE8).

When it's done, the MF2 ROM will be paged from &0000 to &1FFF (8Kb) and it's RAM from &2000 to &3FFF (8Kb).

To access the MF2 ROM/RAM, keep in mind that:

  • The MF2 ROM overlay the IM1 trap at &0038, therefore you should disable the interrupts before paging the MF2 if your program is running in IM1.
  • The Amstrad Firmware can not be used while the MF2 ROM/RAM is enabled! Do not even think about using it's API! It will crash!

Note: ASIC & Lower ROM

On the Amstrad Plus, for some unknown reasons, the MF2 ROM/RAM can be paged directly using it's I/O ports, it is not required to enable the Lower ROM with the RMR register.

If you're writing a Plus specific MF2 software, then you can use this feature to detect both the MF2 and the Plus at the same time: page directly the MF2's ROM/RAM using it's dedicated I/O port and check if it's actually available (but make sure the Lower ROM is disabled tho). If it's not available, there's no (visible) MF2 or your program is running on a CPC, in which cases your program should stop.

With the Amstrad Firmware

It is not recommended to modify directly the RMR register when the Amstrad Firmware is running (it might crash the machine). The Firmware provides a complete API to deal with ROMs and we just have to use it.

Firmware references

Example source code

			; Accessing the MF2 ROM/RAM
			; in a Firmware environment
 
			; The two Firmware routines we need
_KL_LROM_ENABLE		equ &B906
_KL_ROM_RESTORE		equ &B90C
 
			; For obvious reasons, your program must be located
			; outside the address range &0000-&3FFF!
			org &4000
 
			; enable the lower ROM
			call _KL_LROM_ENABLE
			; Save the previous ROM state for later
			push af
			; Ignore interrupts, so we can safely
			; page the MF2 ROM/RAM
			di
			; Enable the MF2 ROM/RAM from &0000-&3FFF
			ld bc,&FEE8
			out (c),c
 
 
			; that's it, now you can do whatever your want
			; with the MF2 ROM/RAM here!
			; The MF2 ROM is available from &0000 to &1FFF
			; The MF2 RAM is available from &2000 to &3FFF
 
 
			; And now we exit properly
			; Disable the MF2 ROM/RAM
			ld bc,&FEEA
			out (c),c
			; Acknowledge Interrupts
			ei
			; Restore the ROM state as it was
			pop af
			call _KL_ROM_RESTORE
			; We're done
			ret

Instead of using the Firmware API to control the Lower ROM, you could also hack the CPU register (BC') reserved for the Firmware too, but this is beyond the scope of this article :)

Without the Amstrad Firmware

If the Amstrad Firmware is not available, then you can bang directly onto the hardware:

			; Ignore the interrupts
			di
			; Configure the RMR Register
			; - Disable Upper ROM
			; - Enable Lower ROM
			; - Select video mode 2
			ld bc,&7F00+%10001010
			out (c),c
			; Enable the MF2 ROM/RAM
			ld bc,&FEE8
			out (c),c
 
 
			; The MF2 ROM is available from &0000 to &1FFF
			; The MF2 RAM is available from &2000 to &3FFF
 
 
			; Disable the MF2 ROM/RAM
			ld bc,&FEEA
			out (c),c
			; Configure the RMR Register
			; - Disable Upper ROM
			; - Disable Lower ROM
			; - Select video mode 2
			ld bc,&7F00+%10001110
			out (c),c
			; Acknowledge Interrupts
			ei

Detection

There are several methods to detect a Multiface Two depending on what you want to achieve. A simple ROM/RAM paging detection is the most appropriate method when you just need to use the features of the MF2. A STOP button detection is most likely used when you want to protect your software from the MF2 (in this case, it should be just one part of a bigger protection scheme, otherwise it won't take long to crack and that would be spoiling the fun! :).

In case of a software protection, you should not poorly crash/stop your program right after it detected it has been stopped. This is like shouting at the (lame cart-)cracker he still have some stuff to hack in your program! Thus you're helping this lousy bastard! Instead, silently hack your own program to spoil its fun (eg. in a game, enemies get harder to kill, level bosses are invulnerable, progressively slow-down the frame-rate of the game, don't save the hi-scores, or whatever… be creative! But more importantly, be evil! :).

ROM/RAM paging

The easiest one is simply to check if the MF2 ROM/RAM can be paged from &0000 to &3FFF. If it can't, there is no MF2 or it's stealth mechanism is activated.

def_mf2_io_enable	equ &FEE8
def_mf2_io_disable	equ &FEEA
 
			; Usage example
			call mf2_detect
			jr z,no_mf2
			; The MF2 ROM/RAM is available
			; Do your things accordingly here.
			ret
no_mf2
			; The MF2 ROM/RAM is not available.
			; There is no MF2 or it's stealth mechanism is activated.
			; Do your thing accordingly here.
			ret
 
 
			; Detect if the MF2 ROM/RAM is available
			; This routine MUST NOT be compiled within &0000-&3FFF!
			;
			; Input
			;   Lower ROM should be enabled.
			;
			; Output
			;   If Flag Z is clear, the MF2 ROM/RAM is available.
			;   HL,DE,BC,F are modified.
			;   Interrupts are enabled.
mf2_detect:
			; Interrupt must be disabled first since the MF2
			; ROM/RAM might be mapped from &0000-&3FFF, it will
			; takeover the IM1 interrupt vector and do unwanted
			; things.
			di
			; Save the word at &0000
			ld hl,(&0000)
			push hl
			; Write a word-tag at &0000
			ld hl,&6128
			ld (&0000),hl
			; Try to map the MF2 ROM/RAM
			ld bc,def_mf2_io_enable
			out (c),c
			; Then check if the tag changed
			ld de,(&0000)
			or a
			sbc hl,de
			; If we get the same value, we assume the MF2 ROM/RAM
			; was not mapped, thus no MF2 is available.
			jr z,mf2_not_available
 
			; If we get something else, then we assume the MF2
			; ROM/RAM is actually mapped from &0000 to &3FFF,
			; therefore an MF2 is available!
			;
			; We could do some more tests here, like verifying in the MF2
			; RAM if we can find matches to the hardware registers
			;(eg. modifying CRTC Register 14 and checking if the value at
			; &3A8E in the MF2 RAM matches the change)
			;
			; Before leaving, we disable the MF2 ROM/RAM mapping.
			ld bc,def_mf2_io_disable
			out (c),c
mf2_not_available
			; We restore the modified word to it's original value.
			pop hl
			ld (&0000),hl
			; Flag Z hold the MF2 ROM/RAM status
			; Z is set - Not available
			; Z is clear - Available
			ei
			ret

STOP Button

If the MF2 stealth mechanism is enabled, there's no way of telling if there is a MF2 available or not… until it is activated to stop your program. You can not prevent your program from being stopped. All you can do is to look for a few trails the MF2 leaves after having stopped a program.

Here are just a few methods to detect if a program has been stopped. You could find more of them with a bit of imagination and curiosity.

Extra RAM flaw

This detection method is relying on a flaw in the MF2 firmware. When the user presses the STOP button, the MF2 automatically checks the amount of RAM available to detect 128Kb+ machines and might corrupt a byte in the extra RAM. This detection is fairly easy to implement but only works on 128Kb+ RAM machines.

Compile the following example and execute it (CALL &8000). The program will loop forever. If you stop it with your multiface, it will exit as soon as you return to it:

			; Must be located outside the &4000-&7FFF range
			; used to map the extra RAM bank.
			org &8000
 
			; Initialize HL with the address tested by the MF2
			ld hl,&4000
			; Map the extra RAM bank tested by the MF2
			ld bc,&7FC4
			out (c),c
 
			; Put a special tag (&AA) at &4000 to trick the
			; first test of the MF2
			ld (hl),&AA
 
			; Now we just have to monitor this tag
_mf2_monitor_extraRAM
			; Read the tag back
			ld a,(hl)
			; If it's value changed to &55, the program
			; has been stopped
			cp &55
			jr nz,_mf2_monitor_extraRAM
_mf2_stopped
			; The program has been stopped,
			; Exit immediately
			ret

Stack monitoring

The STOP button of the Multiface Two produces an interrupt, thus the Z80 will push it's PC (Program Counter) onto the stack before dealing with the interrupt service routine. Therefore, monitoring the values on the stack can reveal if your program has been stopped. This is not an easy detection to set up, you must know exactly what's going on (which can be hard when the Amstrad Firmware is running in the background…) and you have to deal carefully with the stack to keep the detection working.

The following example is very simple but illustrate the idea. Compile it and call it from BASIC. It will loop forever and exit as soon as you return back to it (after you stopped it of course).

		; MF2 Stop detection by monitoring the stack
 
		; Ignore the (maskable) interrupts, that will make things a lot
		; simpler in this example.
		di
		; Push a marker value (eg. zero) into the stack
		ld hl,0
		push hl
		; Get back to the initial stack pointer position
		pop hl
 
mainloop
		; ************************************************************
		; *** Do some stuff here but it must not modify the stack! ***
		; ************************************************************
 
		; Then we check if our marker value is still there
		dec sp
		dec sp
		pop hl
		ld a,h
		or l
		jr z,mainloop
 
		; if it's not, the CPU obviously did something that wasn't expected.
 
		; Exit the program immediately
		ei
		ret

Ghost-buster

This is another detection method relying on a flaw of the Multiface Two, especially it's I/O monitoring system. We know that the MF2 doesn't catch accesses to I/O devices when using ghost addresses. The idea is very simple, just write to an hardware register a first value that the MF2 will register (thus using a standard I/O address) and right after write a second different value that the MF2 won't catch (using a ghost address). When the MF2 will return to your program after it stopped it, it will restore the first value. All your program have to do is to monitor if there's somethin' strange in your neighborhood or if the value of the hardware register is modified (so your program know who ya gonna call).

In the following example, CRTC register 14 is used. It's a light-pen related register that is both readable and writable, so it fits perfectly for our test (but not all CRTC registers are R/W!). We could also use PPI Port A. The point is that the hardware register used must be readable, so we can easily detect when it changes (but you could also use a write-only register and indirectly detect if it has been modified by monitoring timings or anything else that depends on it, but this is a little bit too complicated for an example here :).

		; MF2 Stop detection by monitoring hardware register(s)
 
		; Select CRTC Register 14 (lightpen)
		ld bc,&BC00+14
		out (c),c
 
		; Set CRTC R14 to &55 with the regular
		; CRTC Write register I/O address so the
		; MF2 will catch and record it.
		ld bc,&BD55
		out (c),c
 
		; Set CRTC R14 to &AA with a ghost I/O address
		; (note that this ghost I/O address also hit the
		; printer and upper ROM ports).
		ld bc,&8DAA
		out (c),c
 
		; Then we monitor the CRTC R14 and exit the loop
		; as soon as it's value change (ie. after a STOP).
loop
		ld b,&BF
		in a,(c)
		cp c		; if R14=&AA
		jr z,loop	; we keep looping
 
		; Exit
		ret

Direct Jump

The direct jump is a neat feature of the MF2 that allow programmers to customize the behaviour of the interface with their own program. As said before, when the STOP button is pressed, the MF2 firmware is executed. It starts by initializing some stuff and then checks if a Direct Program is configured or not. If there's actually one configured it will be executed, whatever it is, otherwise the MF2 proceeds as usual and starts it's built-in toolkit.

Setting up a Direct Jump program

Installing a Direct Jump program in the MF2 is piece of cake. It takes 3 parameters:

  • The RMR configuration (ROM) to setup before calling your Direct Jump program.
  • The MMR configuration (RAM) to setup before calling your Direct Jump program.
  • And finally, the address of your Direct Jump program that the MF2 must call.

These 3 parameters must be written in the MF2 RAM along with a 3 characters ASCII string ”RUN” to indicate that your Direct Jump is ready, and that's it.

The RMR and MMR parameters allow the MF2 to call your direct jump program wherever it is located, to some extent. As said before, the MF2 ROM/RAM is paged from &0000 to &3FFF. Therefore, the MF2 can call your direct jump program from &4000 to &FFFF whatever is paged in within this address range (according to the RMR/MMR registers, it can be the base 64Kb RAM, extended RAM, Upper ROM, …). From &0000 to &3FFF however, there's the MF2 ROM/RAM and nothing else.

  • The RMR register value you setup for your direct jump program MUST have it's bit2=0 (Enable Lower ROM) otherwise the MF2 will simply kill itself and most likely crash when setting this up right before calling your direct jump program (at least, on an Amstrad CPC. The Amstrad Plus is somehow immune to this “bug”).
  • You can put your direct jump program into whatever can be paged from &4000 to &FFFF (Base 64Kb RAM, Extended RAM, Upper ROM, …)
  • You can put your direct jump program into the MF2 RAM from &2000 to &37FE.
  • Your direct jump program can be a small wrapper located within &4000 to &FFFF that will call another program located from &0000 to &3FFF (eg. in a Lower ROM or RAM).
  • In case of a Direct Jump program in an Upper ROM, the MF2 doesn't select the Upper ROM id (port &DF00), so you might need a wrapper too in this case.

Entry

When your direct jump program is called:

  • The interrupts are disabled.
  • The stack (SP register) points into the MF2 RAM (from &3EFF and descending)
  • The MF2 firmware saved all the hardware and CPU registers.
  • The lower ROM is paged from &0000 to &3FFF (RMR bit 2=0).
  • The MF2 ROM/RAM is paged from &0000 to &3FFF.
  • The RMR and MMR registers are set to whatever you set them to.

Never, ever, assume the CPC/Plus hardware to be miraculously configured properly for your direct jump program!. If you need to display some stuff, initialize the CRTC and Gate Array. If you need keyboard inputs, initialize the PPI/AY3, and so on.

Exit

For your direct jump program to exit properly:

  • The MF2 ROM/RAM must be paged from &0000 to &3FFF.
  • The stack pointer must be restored to the MF2 RAM (ie. if for some reasons your direct jump changed it's location).

The MF2 will automatically restore all the hardware and CPU registers and let the CPC/Plus proceeds just like nothing happened (assuming that your direct program was properly designed :).

Example

The following example installs a direct jump program that will change the border color to red for a few seconds when the stop button is pressed.

How to compile with WinAPE
  • Make sure the MF2 emulation is enabled (see configuration, memory tab)
  • Open mf2.djump.example.asm in the assembler
  • Compile (press F9)
  • In the emulated CPC, type call&4000 to install the direct jump program in the MF2.
  • From now on, every time you press the STOP button (F11), the border turns red.

Note that pressing the STOP button again, before the first direct jump has exited, will lead to a stack overflow (crash). It's up to you to take care of that.

Source codes
			; Multiface Two - Direct Jump example
 
			; For obvious reasons, your program must be located
			; outside the address range &0000-&3FFF!
			org &4000
 
			; Setup a direct jump
			ld hl,mf2_djp
			ld de,&C000+%10001011
			jp mf2_kl_set_djump
 
			; A dummy direct jump program example
			; It will set the border to red for a few
			; seconds and exit.
mf2_djp:
			; set border red
			ld bc,&7F10
			out (c),c
			ld c,&4C
			out (c),c
			; wait a bit
mf2_djps		ld b,&F5
			in a,(c)
			rra
			jr nc,mf2_djps
			djnz $
			dec c
			jr nz,mf2_djps
			; exit
			ret
 
			; Include the direct jump library
			read "mf2.kl.djump.asm"

And here is the source code for the mf2.kl.djump.asm:

			; The two Firmware routines used to bank-switch the lower ROM
_KL_LROM_ENABLE		equ &B906
_KL_ROM_RESTORE		equ &B90C
 
			; Setup a direct jump
			; Input
			; 	Amstrad Firmware must be available.
			;
			;	HL = Direct Jump address
			;	D  = MMR
			;	E  = RMR
			;
			; Output
			;	Zero flag is set if no MF2 was found
mf2_kl_set_djump:
			; Enable the lower ROM
			call _KL_LROM_ENABLE
			; Save the previous ROM state for later
			push af
			; Save input parameter
			push hl
			; Read a word (16bits) from the Lower ROM
			ld hl,(&0464)
			; Enable the MF2 ROM/RAM from &0000-&3FFF (8Kb)
			di
			ld bc,&FEE8
			out (c),c
			; Read a word (16bits) from the MF2 ROM hopefuly
 			ld bc,(&0464)
			; Compare the bytes from Lower ROM and MF2 ROM
			xor a
			sbc hl,bc
			; If they are not equal, we assume the MF2 ROM is paged,
			; we can proceed and setup the direct jump, otherwise we
			; exit immediately.
			pop hl
			jr z,_mf2_kl_set_djump_exit
				; Set the Jump address
				ld (&2000),hl
				; Set the RMR/MMR values
				ld (&2002),de
				; Set the Direct Jump "RUN" key
				ld a,"R"
				ld hl,"N"*256 + "U"
				ld (&2005),a
				ld (&2006),hl
 
				; Disable the MF2 ROM/RAM
				ld bc,&FEEA
				out (c),c
_mf2_kl_set_djump_exit
			; Acknowledge Interrupts
			ei
			; Restore the ROM state as it was
			pop hl
			push af	; Save the flags
			ld a,h
			call _KL_ROM_RESTORE
			; We're done, restore the flag
			pop af
			ret

MF2 RAM Layout for Direct Jump mode

  • The size are given in byte.
  • The Type column indicate:
USR
Set by the user (ie. your program).
ROM
Set by the MF2 Firmware.
I/O
Set by the MF2 I/O monitoring system (real-time).
FREE
RAM free to be used.
Address Size Type Description
&2000 8 Direct Jump boot configuration
&2000 2USR Address of the Direct Jump program
&2002 1USR RMR configuration
&2003 1USR MMR Configuration
&2004 1ROM Reserved.
&2005 3USR 3 bytes sequence to enable/disable the Direct Jump mode (put the ASCII string “RUN” to enable)
&2008 6135 RAM free to use
&37FF 2049 MF2 Reserved area (stack, I/O monitoring, firmware variable/buffers, …)
A fully detailled address map is on it's way and should be published soon. Until then, you can find one on QuasarNet (in french).

Zap't'Balls

One of the most original (and somehow funny :) direct jump usage I've ever seen is in Zap't'Balls (a game by the former demomaker Elmsoft). If your MF2 is visible, the game installs a direct jump program which, if you try to stop the game, will display the usual MF2 toolkit menu but… :)

(If Flash is installed JavaScript is activated, you can watch a video inside this web page.)

Hardware

Schematics

(might be done later)

PCB

I've only seen two versions of the MF2 PCB so far.

Version 1

Version 2

Datasheets

documentations/expansions/mf2/start.txt · Last modified: 2012/07/17 09:52 by grim