diff --git a/VersionASM b/VersionASM
index efac76a140d7884289d2d79750f30e2215a36d41..4427f5d5bbbb567ef8ffba807f7b63d121e75fbe 100644
--- a/VersionASM
+++ b/VersionASM
@@ -5,8 +5,10 @@
 			GBLA    Module_Version
 			GBLS	Module_MinorVersion
 			GBLS	Module_Date
-Module_MajorVersion	SETS    "4.71"
-Module_Version          SETA    471
+			GBLS	Module_FullVersion
+Module_MajorVersion	SETS    "4.72"
+Module_Version          SETA    472
 Module_MinorVersion	SETS	""
-Module_Date		SETS    "25 Feb 1999"
-		        END
+Module_Date		SETS    "08 Apr 1999"
+Module_FullVersion      SETS    "4.72"
+                        END
diff --git a/VersionNum b/VersionNum
index de8072524793d4d3d8d59bbb67cc58552c4b6761..d6362c112ed3c9592dadb86c9226f51c8eef0e75 100644
--- a/VersionNum
+++ b/VersionNum
@@ -1,14 +1,15 @@
-/* (4.71)
+/* (4.72)
  *
  * This file is automatically maintained by srccommit, do not edit manually.
  *
  */
-#define Module_MajorVersion_CMHG     	4.71
+#define Module_MajorVersion_CMHG     	4.72
 #define Module_MinorVersion_CMHG	
-#define Module_Date_CMHG      		25 Feb 1999
+#define Module_Date_CMHG      		08 Apr 1999
 
-#define Module_MajorVersion     	"4.71"
-#define Module_Version                  471
+#define Module_MajorVersion     	"4.72"
+#define Module_Version                  472
 #define Module_MinorVersion		""
-#define Module_Date      		"25 Feb 1999"
+#define Module_Date      		"08 Apr 1999"
 
+#define Module_FullVersion              "4.72"
diff --git a/s/FlashROM b/s/FlashROM
new file mode 100644
index 0000000000000000000000000000000000000000..981bb41f4ebf7ea9a249ce409cfd8e16293d1237
--- /dev/null
+++ b/s/FlashROM
@@ -0,0 +1,672 @@
+; Copyright 1999 Element 14 Ltd
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;     http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+;
+; This file handles the programming of flash devices in ROM bank 0 with
+; a ROM image transferred via the parallel port. This is for development
+; purposes only, and is only likely to work on the system for which it was
+; designed.
+
+; Flash Memory Settings
+
+FR_FlashBase            * &00000000
+
+FR_DeviceBufferSize     * 32            ; 28F640J5 has 32 byte buffer
+FR_DeviceBlockSizeBits  * 17            ; 28F640J5 has 128KB blocks
+FR_DeviceBlocks         * 64            ; 64 blocks in a 28F640J5
+
+FR_BufferSize           * FR_DeviceBufferSize*2         ; 2 devices
+FR_BlockSizeBits        * FR_DeviceBlockSizeBits+1      ; 2 devices
+FR_BlockSize            * 1 :SHL: FR_BlockSizeBits      ; 256KB
+FR_FlashSize            * FR_BlockSize*FR_DeviceBlocks  ; 16MB
+
+; Sizes of things in memory
+
+FR_StackSize            * 1024
+FR_RAMCodeSize          * 4096
+FR_ScreenSize           * 472*1024
+
+; Addresses of where we put things in memory
+
+FR_PageTableBaseAddr    * DRAM0PhysRam
+FR_StackBase            * FR_PageTableBaseAddr+16*1024+FR_StackSize
+FR_RAMCodeAddr          * FR_StackBase
+FR_ScreenBase           * (FR_RAMCodeAddr+FR_RAMCodeSize+15):AND::NOT:&F
+FR_TransferBuffAddr     * FR_ScreenBase
+
+; Combo chip addresses
+FR_ComboCfgBase         * &03010000 + &3F0*4
+FR_PPortBase            * &03010000 + &278*4
+FR_ECRPortBase          * FR_PPortBase + &402*4
+
+
+; On entry, R11->IOMD
+; Will either reprogram the flash, or branch back to 0.
+; We're in SVC32 mode with the MMU off, by the way.
+
+        ASSERT  IOMD_C_FrontPanelButton <> 0
+ConsiderFlashROM
+        LDRB    R0,[R11,#IOMD_CLINES]
+        TST     R0,#IOMD_C_FrontPanelButton
+        MOVNE   PC,#0
+
+FlashROM
+; Switch CPU to 32-bit mode
+        MOV     R0,#MMUC_L :OR: MMUC_D :OR: MMUC_P      ; Set PROG32 and DATA32
+        ARM_write_control R0
+        MOV     R0,#I32_bit :OR: F32_bit :OR: SVC32_mode
+        msr     AL,CPSR_all,R0
+
+; Initialise various CPU control registers
+        MOV     R0,#IOMD_ROMCR_NSTicks_5 :OR: IOMD_ROMCR_HalfSpeed :OR: IOMD_ROMCR_32bit
+        STRB    R0,[R11,#IOMD_ROMCR0]
+        MOV     R0,#IOMD_CLKCTL_CpuclkHalf :OR: IOMD_CLKCTL_MemclkNormal :OR: IOMD_CLKCTL_IOclkNormal
+        STRB    R0,[R11,#IOMD_CLKCTL]
+        MOV     R0,#IOMD_DRAMWID_RASPre_3 :OR: IOMD_DRAMWID_RASCAS_3 :OR: IOMD_DRAMWID_EDO_Enable :OR: IOMD_DRAMWID_DRAM_32bit
+        STRB    R0,[R11,#IOMD_DRAMWID]
+        MOV     R0,#IOMD_IOTCR_Combo_TypeB :OR: IOMD_IOTCR_Network_TypeC
+        STRB    R0,[R11,#IOMD_IOTCR]
+        MOV     R0,#1                   ; Card 0 type C, the rest type A
+        STRB    R0,[R11,#IOMD_ECTCR]
+        MOV     R0,#IOMD_ASTCR_WaitStates
+        STRB    R0,[R11,#IOMD_ASTCR]
+        MOV     R0,#IOMD_C_ReadMask :OR: IOMD_C_ROMCardVpp              ; Green LED on
+        STRB    R0,[R11,#IOMD_CLINES]
+
+; Main setup
+        MOV     R10,#VIDC               ; Keep R10 pointing to video registers from here on
+        BL      FR_InitVideo
+        BL      FR_FillScreen
+        BL      FR_SetPageTable
+        BL      FR_SetMMU
+        BL      FR_InitParallel         ; Sets R9 to parallel base address - keep it this way
+
+; Copy the rest of code to RAM and jump to it
+        ADR     R0,FR_CopyBlockStart
+        LDR     R1,=FR_RAMCodeAddr
+        MOV     LR,R1
+        LDR     R2,=FR_RAMCodeEnd - FR_CopyBlockStart
+01      LDR     R3,[R0],#4
+        STR     R3,[R1],#4
+        SUBS    R2,R2,#4
+        BNE     %BT01
+        MOV     PC,LR
+
+FR_InitVideo    ROUT
+; Set DMA registers
+        LDR     R1,=FR_ScreenBase
+        ADD     R2,R1,#FR_ScreenSize
+        SUB     R2,R2,#16
+        STR     R1,[R11,#IOMD_VIDSTART]
+        STR     R2,[R11,#IOMD_VIDEND]
+        STR     R1,[R11,#IOMD_VIDINIT]
+        STR     R1,[R11,#IOMD_VIDCUR]
+        STR     R1,[R11,#IOMD_CURSINIT]
+        STR     R1,[R11,#IOMD_CURSCUR]
+        MOV     R1,#0
+        STR     R1,[R11,#IOMD_VIDINITB]
+        MOV     R1,#IOMD_VIDCR_DRAMMode :OR: IOMD_VIDCR_Enable
+        STRB    R1,[R11,#IOMD_VIDCR]
+; Set video registers
+        ADR     R1,FR_InitVidRegs
+        MOV     R2,#(FR_InitVidRegsEnd-FR_InitVidRegs)
+02      LDR     R3,[R1],#4
+        STR     R3,[R10]
+        SUBS    R2,R2,#4
+        BNE     %BT02
+; Set grey palette
+        MOV     R1,#&10000000
+        STR     R1,[R10]
+        MOV     R1,#0
+        MOV     R2,#&000001
+        ORR     R2,R2,#&000100
+        ORR     R2,R2,#&010000
+        MOV     R3,#255
+03      STR     R1,[R10]
+        ADD     R1,R1,R2
+        SUBS    R3,R3,#1
+        BNE     %BT03
+        MOV     PC,LR
+
+; Frequency synthesizer register settings
+FR_ModR        * 8
+FR_ModV        * 9 ; 36 MHz
+; Video control register settings
+FR_FIFO        * 3 ; 12 loads
+FR_BPP         * 3 ; 8bpp
+FR_PixelRate   * 0 ; CK/1
+FR_ClockSource * 0 ; VCLK
+; Horizontal timings
+FR_HSync       * 72
+FR_HBPch       * 86
+FR_HLBdr       * 24
+FR_HDisp       * 800
+FR_HRBdr       * 24
+FR_HFPch       * 18
+FR_HDWR        * FR_HDisp / 4
+; Vertical timings
+FR_VSync       * 2
+FR_VBPch       * 22
+FR_VTBdr       * 0
+FR_VDisp       * 600
+FR_VBBdr       * 0
+FR_VFPch       * 1
+
+FR_InitVidRegs  ROUT
+        DCD     &E0000000+(FR_FIFO:SHL:8)+(FR_BPP:SHL:5)+(FR_PixelRate:SHL:2)+FR_ClockSource
+        DCD     &D0000000+&C288
+        DCD     &80000000+FR_HSync+FR_HLBdr+FR_HDisp+FR_HRBdr+FR_HFPch-8
+        DCD     &81000000+FR_HSync-8
+        DCD     &82000000+FR_HSync+FR_HBPch-12
+        DCD     &83000000+FR_HSync+FR_HBPch+FR_HLBdr-18
+        DCD     &84000000+FR_HSync+FR_HBPch+FR_HLBdr+FR_HDisp-18
+        DCD     &85000000+FR_HSync+FR_HBPch+FR_HLBdr+FR_HDisp+FR_HRBdr-12
+        DCD     &90000000+FR_VSync+FR_VBPch+FR_VTBdr+FR_VDisp+FR_VBBdr+FR_VFPch-2
+        DCD     &91000000+FR_VSync-2
+        DCD     &92000000+FR_VSync+FR_VBPch-1
+        DCD     &93000000+FR_VSync+FR_VBPch+FR_VTBdr-1
+        DCD     &94000000+FR_VSync+FR_VBPch+FR_VTBdr+FR_VDisp-1
+        DCD     &90000000+FR_VSync+FR_VBPch+FR_VTBdr+FR_VDisp+FR_VBBdr-1
+        DCD     &40008000
+        DCD     &C0000000+(1:SHL:12)+3
+        DCD     &D0000000+&C080+((FR_ModV-1):SHL:8)+(FR_ModR-1)
+        DCD     &D0000000+((FR_ModV-1):SHL:8)+(FR_ModR-1)
+        DCD     &F0000000+(1:SHL:16)+(1:SHL:12)+FR_HDWR
+        DCD     &B1000000
+FR_InitVidRegsEnd
+
+FR_FillScreen   ROUT
+        LDR     R1,=FR_ScreenBase
+        MOV     R2,#((256:SHL:16)/600):AND:&00FF
+        ORR     R2,R2,#((256:SHL:16)/600):AND:&FF00
+        MOV     R3,R3
+        MOV     R4,#FR_VDisp
+01      MOV     R5,#FR_HDisp
+        MOV     R0,R3,LSR #16
+        ORR     R0,R0,R0,LSL #8
+        ORR     R0,R0,R0,LSL #16
+02      STR     R0,[R1],#4
+        SUBS    R5,R5,#4
+        BNE     %BT02
+        ADD     R3,R3,R2
+        SUBS    R4,R4,#1
+        BNE     %BT01
+        MOV     PC,LR
+
+;Make a page table that basically gives flat addressing with the C and B bits
+;set approriately for the RAM areas. The RAM areas are set with both the C and B
+;bits on, and all other areas have both C and B off. The flash area must not
+;be made cacheable, or programming won't work.
+;On exit
+; r0-r4 corrupted
+FR_SetPageTable ROUT
+        MOV     R1,#FR_PageTableBaseAddr
+        MOV     R2,#0                           ; Address
+        MOV     R4,#L1_U + L1_Section
+        ORR     R4,R4,#AP_Full * L1_APMult
+01      BICS    R3,R2,#&E0000000                ; Repeats at &00000000, &20000000, &4000000, etc.
+        BICEQ   R4,R4,#L1_C + L1_B              ; Clear B and C bits from start of ROM area
+        CMP     R3,#&10000000
+        ORREQ   R4,R4,#L1_C + L1_B              ; Set B and C bits (RAM area cacheable and bufferable)
+        ORR     R0,R3,R4
+        STR     R0,[R1],#4
+        ADDS    R2,R2,#1024*1024
+        BCC     %BT01
+        MOV     PC,LR
+
+FR_SetMMU       ROUT
+        MOV     R0,#FR_PageTableBaseAddr
+        ARM_MMU_transbase R0
+        MVN     R0,#0                           ; Full access to all domains
+        ARM_MMU_domain R0
+        MOV     R0,#MMUC_L + MMUC_D + MMUC_P + MMUC_W + MMUC_C + MMUC_M
+        ARM_write_control R0                    ; Enable write buffer, cache and MMU
+        MOV     PC,LR
+
+FR_InitParallel ROUT
+        LDR     R1,=FR_ComboCfgBase
+; Enter config mode
+        MOV     R0,#&55
+        STRB    R0,[R1]
+        STRB    R0,[R1]
+; Write CR0
+        MOV     R0,#0
+        STRB    R0,[R1]
+        MOV     R0,#&22
+        STRB    R0,[R1,#4]      ; CR0=&22 (FDC, IDE off)
+; Write CR1
+        MOV     R0,#1
+        STRB    R0,[R1]
+        MOV     R0,#&97
+        STRB    R0,[R1,#4]      ; CR1=&97 (Parallel port on, extended)
+; Write CR4
+        MOV     R0,#4
+        STRB    R0,[R1]
+        MOV     R0,#&04         ; CR4=&03 (ECP & EPP mode)
+        STRB    R0,[R1,#4]
+; Write ECR
+        MOV     R0,#&34         ; PS/2 Parallel Port mode, DMA off, interrupts off
+        LDR     R9,=FR_ECRPortBase
+        STRB    R0,[R9]
+; Exit config mode
+        MOV     R0,#&AA
+        STRB    R0,[R1]
+; Initialise parallel port
+        LDR     R9,=FR_PPortBase
+; Clear nSTROBE bit (puts nACK high) and clear nSLCTIN bit (puts BUSY high) to
+; indicate we are busy and not accepting data.
+        MOV     R0,#&20         ; Input, IRQ off, nSLCTIN clear, nSTROBE clear
+        STRB    R0,[R9,#8]
+; Keep parallel port base address in R9 from here on
+        MOV     PC,LR
+
+        LTORG
+
+FR_CopyBlockStart
+
+; Dont use literals from here on in - the assembler's liable to pull them
+; the non-RAM stuff above.
+
+FR_StackBaseVal    & FR_StackBase
+FR_TransferBuffVal & FR_TransferBuffAddr
+
+; Code from here on gets copied to RAM (at FR_RAMCodeAddr) and run from there.
+
+FR_RAMCodeStart
+        MOV     R0,#IOMD_C_ReadMask :OR: IOMD_C_FrontPanelLED :OR: IOMD_C_ROMCardVpp
+        STRB    R0,[R11,#IOMD_CLINES]                   ; Red LED on
+        LDR     R13,FR_StackBaseVal
+FR_LoopForever
+        MOV     R0,#FR_WaitCmdState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+; Wait for a command header
+        BL      FR_ParallelRead
+        CMP     R0,#'M'
+        BNE     FR_LoopForever
+        BL      FR_ParallelRead
+        CMP     R0,#'P'
+        BNE     FR_LoopForever
+        BL      FR_ParallelRead
+        CMP     R0,#'T'
+        BNE     FR_LoopForever
+        BL      FR_ParallelRead
+        CMP     R0,#'!'
+        BNE     FR_LoopForever
+        BL      FR_ParallelRead
+        CMP     R0,#'P'
+        BEQ     FR_BeginProgram
+        CMP     R0,#'V'
+        BEQ     FR_BeginVerify
+        B       FR_LoopForever
+
+FR_BeginProgram ROUT
+        MOV     R0,#FR_BeginProgramState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+; Read in parameters
+        BL      FR_ReadOffsetAndCount
+; Ready to go - first make the ROM area writable
+        BL      FR_MakeROM0Writable
+01
+; Begin erasing the block
+        MOV     R0,R7
+        BL      FR_StartErase
+; Read the block data into the buffer
+        BL      FR_ReadBlockIntoBuffer
+; Data read in OK - wait for erase to finish
+        BL      FR_CheckErase
+; Now write block
+        MOV     R0,#FR_WriteBlockState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+        MOV     R0,R7
+        LDR     R1,FR_TransferBuffVal
+        BL      FR_DoWriteBlock
+; Verify the block
+        BL      FR_ResetToReadArray
+        MOV     R0,#FR_VerifyBlockState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+        MOV     R0,R7
+        LDR     R1,FR_TransferBuffVal
+        BL      FR_VerifyBlock
+; Block done, on to the next
+        ADD     R7,R7,#FR_BlockSize
+        SUBS    R8,R8,#1
+        BNE     %BT01
+; All finished - make ROM area read-only
+        BL      FR_MakeROM0ReadOnly
+        B       FR_LoopForever
+
+FR_BeginVerify  ROUT
+        MOV     R0,#FR_BeginVerifyState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+; Read in parameters
+        BL      FR_ReadOffsetAndCount
+01
+; Read the block data into the buffer
+        BL      FR_ReadBlockIntoBuffer
+; Data read in OK - now verify
+        MOV     R0,#FR_VerifyBlockState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+        MOV     R0,R7
+        LDR     R1,FR_TransferBuffVal
+        BL      FR_VerifyBlock
+; Block done, on to the next
+        ADD     R7,R7,#FR_BlockSize
+        SUBS    R8,R8,#1
+        BNE     %BT01
+; All done
+        B       FR_LoopForever
+
+FR_ReadOffsetAndCount ROUT
+;Called by the program and verify commands to read their parameters (offset
+;and count) from the parallel port.
+;On exit
+; r7 = Initial offset in Flash ROM area
+; r8 = Block count
+; r0-r3 corrupted
+        STMFD   R13!,{LR}
+; Next incoming word is the block offset to start from
+        BL      FR_ParallelReadWord
+        MOV     R7,R0
+; Check the offset is valid
+        CMP     R7,#FR_FlashSize
+        BHS     FR_OffsetError
+        MOVS    R0,R7,LSL#(32-FR_BlockSizeBits)         ; The offset within the block should be 0
+        BNE     FR_OffsetError
+; Next incoming byte is the number of blocks
+        BL      FR_ParallelRead
+        MOVS    R8,R0
+        BEQ     FR_BlockCountError
+        ADD     R0,R7,R8,LSL #FR_BlockSizeBits
+        CMP     R0,#FR_FlashSize
+        BHI     FR_BlockCountError
+; Turn the offset into a real address
+        ADD     R7,R7,#FR_FlashBase
+        LDMFD   R13!,{PC}
+
+FR_ReadBlockIntoBuffer ROUT
+;Read a block of data from the parallel port into the transfer buffer
+;Corrupts r0-r6
+        STMFD   R13!,{LR}
+        MOV     R0,#FR_ReadBlockState-FR_BorderStateTable
+        BL      FR_ShowStateBorder
+        MOV     R6,#FR_BlockSize
+        LDR     R5,FR_TransferBuffVal
+        MOV     R4,#0                                   ; Checksum
+01      BL      FR_ParallelReadWord
+        STR     R0,[R5],#4
+        ADD     R4,R4,R0
+        SUBS    R6,R6,#4
+        BNE     %BT01
+; Next word is the checksum
+        BL      FR_ParallelReadWord
+        CMP     R0,R4
+        BNE     FR_ChecksumError
+        LDMFD   R13!,{PC}
+
+FR_ChecksumError ROUT
+        MOV     R0,#FR_ChecksumErrorState-FR_BorderStateTable
+        B       FR_Error
+FR_OffsetError
+        MOV     R0,#FR_OffsetErrorState-FR_BorderStateTable
+        B       FR_Error
+FR_BlockCountError
+        MOV     R0,#FR_BlockCountErrorState-FR_BorderStateTable
+FR_Error
+        BL      FR_ShowStateBorder
+; Wait for front panel button to be pressed
+01      LDRB    R0,[R11,#IOMD_CLINES]
+        TST     R0,#IOMD_C_FrontPanelButton
+        BNE     %BT01
+; Start again (note this also resets the stack pointer)
+        B       FR_RAMCodeStart
+
+FR_ShowStateBorder
+        STMFD   R13!,{R1}
+        ADR     R1,FR_BorderStateTable
+        LDR     R0,[R1,R0]
+        STR     R0,[R10]
+        LDMFD   R13!,{R1}
+        MOV     PC,LR
+
+FR_BorderStateTable
+FR_WaitCmdState            DCD &40FF0000   ;Waiting for command - Blue
+FR_BeginProgramState       DCD &40808080   ;Begin Program command - Grey
+FR_BeginVerifyState        DCD &40404040   ;Begin Verify command - Dark grey
+FR_ReadBlockState          DCD &40FFFF00   ;Reading block - Cyan
+FR_WriteBlockState         DCD &4000FF00   ;Writing block - Green
+FR_VerifyBlockState        DCD &40FFFFFF   ;Verifying block - White
+FR_OffsetErrorState        DCD &400000FF   ;Offset error - Red
+FR_BlockCountErrorState    DCD &4000FFFF   ;Block count error - Yellow
+FR_ChecksumErrorState      DCD &40FF00FF   ;Checksum error - Magenta
+FR_EraseErrorState         DCD &40808000   ;Erase error - Dark Cyan
+FR_VoltageRangeErrorState  DCD &40000080   ;Voltage range error - Dark Red
+FR_DeviceProtectErrorState DCD &40800000   ;Device protect error - Dark Blue
+FR_ProgramErrorState       DCD &40008080   ;Program error - Dark Yellow
+FR_VerifyErrorState        DCD &4000BBFF   ;Verify error - Orange
+
+FR_ParallelRead ROUT
+;Read a byte of data from the parallel port
+;On entry
+; r9 = Parallel port base address
+;On exit
+; r0 = byte read
+; r1 corrupted
+;Set nSLCTIN bit (puts BUSY low) and clear nSTROBE bit (puts nACK high),
+;to indicate that we are ready to receive data
+        MOV     R0,#&28                 ; Input, IRQ off, BUSY low, nACK high
+        STRB    R0,[R9,#8]
+;Now wait until nACK bit (nSTROBE) goes low, meaning a byte is ready to be read
+01      LDRB    R0,[R9,#4]
+        TST     R0,#&40
+        BNE     %BT01
+;Read the byte
+        LDRB    R0,[R9]
+;Set nSTROBE bit (puts nACK low) to indicate we read the byte, and
+;clear nSLCTIN bit (puts BUSY high) to indicate we are busy and won't accept
+;any more data
+        MOV     R1,#&21                 ; Input, IRQ off, BUSY high, nACK low
+        STRB    R1,[R9,#8]
+;Make sure nACK bit (nSTROBE) has returned high
+02      LDRB    R0,[R9,#4]
+        TST     R1,#&40
+        BEQ     %BT02
+;Finish nAck pulse by clearing nSTROBE bit (puts nACK high). Keep BUSY high
+        MOV     R1,#&20                 ; Input, IRQ off, BUSY high, nACK high
+        STRB    R1,[R9,#8]
+        MOV     pc,lr
+
+FR_ParallelReadWord ROUT
+;Read a word of data from the parallel port
+;On entry
+; r9 = Parallel port base address
+;On exit
+; r0 = word read
+; r1-r3 corrupted
+        MOV     R3,LR
+        BL      FR_ParallelRead
+        MOV     R2,R0
+        BL      FR_ParallelRead
+        ORR     R2,R2,R0,LSL #8
+        BL      FR_ParallelRead
+        ORR     R2,R2,R0,LSL #16
+        BL      FR_ParallelRead
+        ORR     R0,R2,R0,LSL #24
+        MOV     PC,R3
+
+FR_MakeROM0Writable
+        STMFD   R13!,{R0}
+        LDRB    R0,[R11,#IOMD_ROMCR0]
+        ORR     R0,R0,#&80
+        STRB    R0,[R11,#IOMD_ROMCR0]
+        LDMFD   R13!,{R0}
+        MOV     PC,LR
+
+FR_MakeROM0ReadOnly
+        STMFD   R13!,{R0}
+        LDRB    R0,[R11,#IOMD_ROMCR0]
+        BIC     R0,R0,#&80
+        STRB    R0,[R11,#IOMD_ROMCR0]
+        LDMFD   R13!,{R0}
+        MOV     PC,LR
+
+FR_ResetToReadArray ROUT
+;Reset the flash devices to Read Array mode
+        STMFD   R13!,{R0,R1}
+        MOV     R0,#FR_FlashBase
+        MOV     R1,#&FF
+        ORR     R1,R1,R1,LSL #8
+        STR     R1,[R0]                 ; Write the Read Array command (&FF) to both devices
+        LDMFD   R13,{R0,R1}
+        MOV     PC,LR
+
+FR_StartErase   ROUT
+;Start to erase a block (256KB) of flash memory
+;On entry
+; r0=Block Address (real memory address)
+        STMFD   R13!,{R1}
+        MOV     R1,#&20
+        ORR     R1,R1,R1,LSL #8
+        STR     R1,[R0]                 ; Write the Block Erase command (&20) to both devices
+        MOV     R1,#&D0
+        ORR     R1,R1,R1,LSL #8
+        STR     R1,[R0]                 ; Write the Confirm command (&D0) to both devices
+        LDMFD   R13!,{R1}
+        MOV     PC,LR
+
+FR_CheckErase   ROUT
+;Wait for erase to finish
+        STMFD   R13!,{R0,R1}
+        MOV     R0,#FR_FlashBase
+01      LDR     R1,[R0]                 ; Read the status register
+        TST     R1,#&0080               ; Check even SR.7
+        TSTNE   R1,#&8000               ; Check odd SR.7
+        BEQ     %BT01                   ; Repeat until both set
+        TST     R1,#&0020               ; Check even SR.5
+        TSTEQ   R1,#&2000               ; Check odd SR.5
+        BNE     FR_ceEraseError
+        LDMFD   R13!,{R0,R1}
+        MOV     PC,LR
+FR_ceEraseError
+        MOV     R1,#&50
+        ORR     R1,R1,R1,LSL #8
+        STR     R1,[R0]                 ; Write Clear Status Register command (&50) to beth devices
+        BL      FR_ResetToReadArray
+        BL      FR_MakeROM0ReadOnly
+        LDMFD   R13!,{R0,R1}
+        MOV     R0,#FR_EraseErrorState-FR_BorderStateTable
+        B       FR_Error
+
+FR_DoWriteBlock ROUT
+;Writes a block (256KB) of data to flash memory
+;On entry
+; r0=Block Address (real memory address)
+; r1=Pointer to data to write
+;On exit
+; r0,r1 have advanced by the block size (256KB)
+        STMFD   R13!,{R2,R3,LR}
+        MOV     R2,R1
+        MOV     R1,R0
+        MOV     R3,#FR_BlockSize
+01      BL      FR_DoWriteBuffer
+        SUBS    R3,R3,#FR_BufferSize
+        BNE     %BT01
+        MOV     R0,R1
+        MOV     R1,R2
+        LDMFD   R13!,{R2,R3,PC}
+
+FR_DoWriteBuffer ROUT
+;Write a buffer full of data (64 bytes) to flash memory
+;On entry
+; r0=Block Address
+; r1=Start Address
+; r2=Pointer to data to write
+;On exit
+; r0 preserved
+; r1,r2 have increased by 64 bytes
+        STMFD   R13!,{R3,R4,LR}
+        MOV     R3,#&E8
+        ORR     R3,R3,R3,LSL #8
+01      STR     R3,[R0]                 ; Write the Write to Buffer command (&E8) to both devices
+        LDR     R4,[R0]                 ; Read the XSR
+        TST     R4,#&0080               ; Check even XSR.7
+        TSTNE   R4,#&8000               ; Check odd XSR.7
+        BEQ     %BT01                   ; Repeat until both set
+        MOV     R3,#(FR_DeviceBufferSize:SHR:1)-1       ; No. of (16-bit) words to write - 1
+        ORR     R3,R3,R3,LSL #8
+        STR     R3,[R0]                 ; Write the (16-bit) word count to both devices
+        MOV     R3,#FR_BufferSize
+02      LDR     R4,[R2],#4
+        STR     R4,[R1],#4              ; Write a (16-bit) word of data to each device
+        SUBS    R3,R3,#4
+        BNE     %BT02
+        MOV     R3,#&D0
+        ORR     R3,R3,R3,LSL #8
+        STR     R3,[R0]                 ; Write the Confirm command (&D0) to both devices
+03      LDR     R4,[R0]                 ; Read the status register
+        TST     R4,#&0080               ; Check even SR.7
+        TSTNE   R4,#&8000               ; Check odd SR.7
+        BEQ     %BT03                   ; Repeat until both set
+        TST     R4,#&0008               ; Check even SR.3
+        TSTEQ   R4,#&0800               ; Check odd SR.3
+        BNE     FR_dwbfVoltageRangeError
+        TST     R4,#&0002               ; Check even SR.1
+        TSTEQ   R4,#&0200               ; Check odd SR.1
+        BNE     FR_dwbfDeviceProtectError
+        TST     R4,#&0010               ; Check even SR.4
+        TSTEQ   R4,#&1000               ; Check odd SR.4
+        BNE     FR_dwbfProgramError
+; Programming completed successfully
+        LDMFD   R13!,{R3,R4,PC}
+
+FR_dwbfVoltageRangeError
+        MOV     R0,#FR_VoltageRangeErrorState-FR_BorderStateTable
+        B       FR_dwbfErrorExit
+FR_dwbfDeviceProtectError
+        MOV     R0,#FR_DeviceProtectErrorState-FR_BorderStateTable
+        B       FR_dwbfErrorExit
+FR_dwbfProgramError
+        MOV     R0,#FR_ProgramErrorState-FR_BorderStateTable
+FR_dwbfErrorExit
+        MOV     R3,#&50
+        ORR     R3,R3,R3,LSL #8
+        MOV     R4,#FR_FlashBase
+        STR     R1,[R4]                 ; Write Clear Status Register command (&50) to beth devices
+        BL      FR_ResetToReadArray
+        BL      FR_MakeROM0ReadOnly
+        LDMFD   R13!,{R3,R4,LR}
+        B       FR_Error
+
+FR_VerifyBlock  ROUT
+;Compare a block (256KB) of data to flash memory
+;On entry
+; r0=Block Address (real memory address)
+; r1=Pointer to block to compare
+;On exit
+; r0,r1 have advanced by the block size (256KB)
+        STMFD   R13!,{R2-R4}
+        MOV     R2,#FR_BlockSize
+01      LDR     R3,[R0],#4
+        LDR     R4,[R1],#4
+        CMP     R3,R4
+        BNE     FR_VerifyError
+        SUBS    R2,R2,#4
+        BNE     %BT01
+        LDMFD   R13!,{R2-R4}
+        MOV     PC,LR
+FR_VerifyError
+        MOV     R0,#FR_VerifyErrorState-FR_BorderStateTable
+        B       FR_Error
+
+FR_RAMCodeEnd
+
+        END
diff --git a/s/GetAll b/s/GetAll
index 04d4b5af9064e4818fd511192c908f9fbe5f3ed7..2811a852a7909d87da7fa7dd2d1180719ca69f42 100644
--- a/s/GetAll
+++ b/s/GetAll
@@ -217,6 +217,9 @@ IncludeTestSrc  SETL    {FALSE} :LAND: :LNOT: STB
                 GBLL    RO371Timings
 RO371Timings    SETL    :LNOT: STB
 
+; For development on Customer M hardware only
+                GBLL    ParallelFlashUpgrade
+ParallelFlashUpgrade SETL {FALSE}
 
   [ :LNOT: RO371Timings
 
@@ -484,6 +487,12 @@ ProcessorVectors        SETL    {TRUE}          ; Processor vectors indirected t
 GetUnsqueeze        SETS  "GET s.Unsqueeze"
  |
 GetUnsqueeze        SETS  ""
+ ]
+                    GBLS  GetFlashROM
+ [ ParallelFlashUpgrade
+GetFlashROM         SETS  "GET s.FlashROM"
+ |
+GetFlashROM         SETS  ""
  ]
                     GBLS  GetPublicWS
                     GBLS  GetWorkspace
@@ -630,6 +639,7 @@ largest_rma_size    * (128*1024)                 ; and the ceiling for rma use
         GET     s.Copro15ops ; some macros
 
         GET     s.Kernel
+        $GetFlashROM
         GET     s.NewIRQs
         GET     s.Oscli
         GET     s.SysComms
diff --git a/s/Morris b/s/Morris
index 94d04b9b668760149d205039a786970023501f8a..3e8350b6330bd75bc8392bc20da7301d8fbd5eee 100644
--- a/s/Morris
+++ b/s/Morris
@@ -20,7 +20,6 @@
 
 MorrisHeaderCodeSize * (24*4)   ;Code occupies less than this, we pad to this boundary so POST code
                                 ;has a nicely defined place for its romsize and code entry points
- [ 1 = 1
 ; VECTOR AREA:
 
         LDR     pc, .+ResetIndirection
@@ -44,43 +43,20 @@ MorrisHeaderCodeSize * (24*4)   ;Code occupies less than this, we pad to this bo
 ; produced in 16-in-32 form by extracting hex values from a listing...
 
         DCD     &0000B632, &0000E3A0    ; 20: MOV R11, #IO+IOMDREGS - point at IOMD
-        DCD     &00000000, &0000E3A0    ; 24: MOV R0, #&0 - ROMCR:32b,slow,218.75us,no burst
-        DCD     &00000080, &0000E5CB    ; 28: STRB R0,[R11,#ROMCR0] - switch mode
-        DCD     &0000F000, &0000E3A0    ; 2C: MOV PC, #0 - jump to 0 (this instr pre-fetched)
+        DCD     &00000000, &0000E3A0    ; 28: MOV R0, #&0 - ROMCR:32b,slow,218.75us,no burst
+        DCD     &00000080, &0000E5CB    ; 30: STRB R0,[R11,#ROMCR0] - switch mode
+ [ :LNOT: ParallelFlashUpgrade
+        DCD     &0000F000, &0000E3A0    ; 38: MOV PC, #0 - jump to 0 (this instr pre-fetched)
  |
-;
-; Kludged to cope with Morris Bugs
-;
-
-        ! 0,"<><><><><><><> This kernel version will not softload on a Risc PC <><><><><><><>"
-
-        DCD     &E3A0F010
-        DCD     &FFFFE3A0
-
-        DCD     &E59FF034
-        DCD     &E59FF034
-
-        DCD     &E59FF034
-        DCD     &E59FF034
-
-        DCD     &E59FF034
-        DCD     &E59FF034
-
-        DCD     &E3A0B632
-        DCD     &E3A0E3A0               ; 20: MOV R11, #IO+IOMDREGS - point at IOMD
-
-        DCD     &E3A00000
-        DCD     &E3A0E3A0               ; 24: MOV R0, #&0 - ROMCR:32b,slow,218.75us,no burst
-
-        DCD     &E5CB0080
-        DCD     &E3A0E5CB               ; 28: STRB R0,[R11,#ROMCR0] - switch mode
-
-        DCD     &E59FF0F4
-        DCD     &E5CBE59F    ; 2C: MOV PC, #0 - jump to 0 (this instr pre-fetched)
+; At this point, we know that we're in an ARM 7500-based box with 32-bit ROMs that's
+; just been powered up. These are the conditions under which we may wish to reprogram
+; ourselves.
+CFR_Offset * (ConsiderFlashROM - ROM - ((.-ROM)/2) - 8)/4
+        DCD     CFR_Offset :AND: &FFFF  ; 38: B ConsiderFlashROM (this instr pre-fetched)
+        DCD     &0000EA00 + (CFR_Offset:SHR:16)
  ]
-; vector absolute targets for use from physical vector instructions
-
 
+; vector absolute targets for use from physical vector instructions
 
 UNDEF_VEC       DCD     UndInstInReset-ROM
 SWI_VEC         DCD     SWIInReset    -ROM