; 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