; Copyright 1996 Acorn Computers 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.
;
; > $.Source.PMF.i2cutils

; Authors JBiggs (m2), PFellows, TDobson, AGodwin

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************

; Date       Name  Description
; ----       ----  -----------
; 28-Mar-95  JRH   Added support for E2ROMs and/or CMOS, conditioned on
;                  E2ROMSupport which is defined elsewhere
;                  Uses RTCFitted and NVRamSize in KernelWS
; 03-Jul-96  JRH   Took out code conditioned on :LNOT: NewClockChip
;                  Fixed support for E2ROM. E2 works in the same gross way as
;                  CMOS. Any E2 fitted > 256 bytes will not be accessed by these
;                  routines.
; 07-Dec-96  AMG   Renaissance. Leave this file as is, allowing the E2ROMSupport
;                  switch to disable non-STB bits
; 12-Jun-97  TMD   (Really) fix OTP access problem.
; 17-Sep-98  KJB   Add support for 16K 24C128 EEPROM.
; 21-Sep-98  KJB   Add OS_NVMemory SWI.
; 30-Jul-99  KJB   Add support for 8K 24C64 EEPROM.
; 23-Sep-99  KJB   Remove support for 24C64, add support for 4K and 8K protectable ATMEL parts.

PhysChecksum		*	(((CheckSumCMOS + &30) :MOD: &F0) + &10)

; Device addresses
RTCAddressPHI		*	&a0     ; Philips RTC + 240 byte CMOS
RTCAddressDAL		*	&d0     ; Dallas RTC + 56 byte CMOS
E2ROMAddress2K		*	&e0     ; 24C174 device - 2K
E2ROMAddress2K_OTP	*	&60     ; 24C174 device - OTP section
E2ROMAddress4K		*	&a4     ; 24C32 device - 4K (top 1K protectable)
E2ROMAddress8K_prot	*	&a2     ; 24C64 device - 8K (top 2K protectable)
E2ROMAddress8K		*	&ae     ; 24C64 device - 8K
E2ROMAddress16K		*	&a8     ; 24C128 device - 16K
E2ROMAddress32K		*	&a6     ; 24CS256 device - 32K (top 2K possibly OTP)


; *****************************************************************************
;
;	HexToBCD - Convert byte in hex to BCD
;
; in:	R0 = byte in hex
;
; out:	R0 = byte in BCD (ie R0 := (R0 DIV 10)*16 + R0 MOD 10)
;	All other registers preserved
;

HexToBCD ROUT
	Push	"R1,R2, R14"
	MOV	R1, #10
	DivRem	R2, R0, R1, R14			; R2=R0 DIV 10; R0=R0 MOD 10
	ADD	R0, R0, R2, LSL #4
	Pull	"R1,R2, PC"

; *****************************************************************************
;
;	BCDToHex - Convert byte in BCD to hex
;
; in:	R0 = byte in BCD (ie x*16 + y)
;
; out:	R0 = byte in hex (ie x*10 + y)
;	All other registers preserved
;

BCDToHex ROUT
	Push	"R14"
	MOV	R14, R0, LSR #4			; R14 := x
	ADD	R14, R14, R14, LSL #1		; R14 := x*3
	SUB	R0, R0, R14, LSL #1		; R0 := R0 - x*6 = x*10
	Pull	"PC"


; *****************************************************************************
;
;       HTBSR9 - hex to BCD and store at "next free byte" R9
;
HTBS9   ROUT
        Push    R14
        BL      HexToBCD
        STRB    R0, [R9], #1
        Pull    PC

; *****************************************************************************
;
;	Write - Write a byte of CMOS RAM specified by logical address
;
; in:	R0 = address in CMOS RAM
;	R1 = data
;
; out:	All registers preserved
;

WriteWithError ROUT
        Push    "R0-R4, R14"
        BL      MangleCMOSAddress
        BCC     %FT05

        ADD     R13, R13, #4            ; junk stacked R0
        ADR     R0, ErrorBlock_CoreNotWriteable
 [ International
        BL      TranslateError
 |
        SETV
 ]
        Pull    "R1-R4,PC"

        MakeErrorBlock CoreNotWriteable

WriteWithoutProtection                  ; allowing write to "OTP" and protected section
        Push    "R0-R4, R14"
        BL      MangleCMOSAddress
        Pull    "R0-R4, PC", CS         ; if invalid, then exit
        MOV     R2, R0
        MOV     R3, R1
        CMP     R0, #&10
        MOVLO   R4, #&1000000           ; don't change checksum for OTP
        BLO     %FT10
        B       %FT08                   ; do change checksum for protected region

Write
	Push	"R0-R4, R14"
	BL	MangleCMOSAddress
	Pull	"R0-R4, PC", CS		; if invalid, then exit
05
  [ E2ROMSupport
        CMP     r0, #&10
        Pull    "R0-R4, PC", CC         ; don't write to OTP section
  ]

  [ E2ROMSupport
        MOV     R14, #0                 ; don't write to protected section
        LDRB    R14, [R14, #NVRamWriteSize]
        CMP     R0, R14, LSL #8         ; (note assumption that NVRamWriteSize is
        Pull    "R0-R4, PC", HS         ; outside mangled region).
  ]

	MOV	R2, R0
	MOV	R3, R1
08
 [ ChecksumCMOS
	BL	ReadStraight		; calculate new checksum :
	MOV	R4, R0
        TEQ     R4, R3                  ; don't bother with write if
        Pull    "R0-R4, PC", EQ         ; oldcontents == newcontents

	MOV	R0, #PhysChecksum
	BL	ReadStraight
	SUB	R0, R0, R4		; = oldsum - oldcontents
	ADD	R4, R0, R3		;          + newcontents

	AND	R4, R4, #&FF
	CMPS	R2, #PhysChecksum	; don't write new checksum ...
	ORREQ	R4, R4, #&1000000	; if checksum is being written
 ]
10
	CMP	r2, #&100		; check small cache limit
	BCS	%FT15
	LDR	R1, =CMOSRAMCache	; update cache, but always write to
	STRB	R3, [R1, R2]		; real hardware as well
15

  [ HAL
    	Push	"R2,R3,sb,R12"
    	AddressHAL
    	CallHAL	HAL_NVMemoryType
    	AND 	R0, R0, #NVMemoryFlag_Provision
    	TEQ 	R0, #NVMemoryFlag_None

    	; If there's no NVmemory, all we have is the internal cache.
    	Pull 	"R2,R3,sb,R12", EQ
	Pull	"R0-R4,PC", EQ

    	TEQ 	R0, #NVMemoryFlag_HAL
    	BNE 	%FT20	    	    	; Go and do IIC stuff.

    	; Make the HAL call - we have to write the data into a buffer.
        Pull    "R0"
        STRB    R3, [sp, #-4]!
    	MOV 	R1, sp
    	MOV 	R2, #1
    	CallHAL	HAL_NVMemoryWrite
        TST     R4, #&1000000
        BNE     %FT18
        LDR     R1, =CMOSRAMCache
        STRB    R4, [R1, #PhysChecksum]
        STRB    R4, [sp]
        MOV     R0, #PhysChecksum
    	MOV 	R1, sp
    	MOV 	R2, #1
    	CallHAL	HAL_NVMemoryWrite
18
    	ADD     sp, sp, #4
    	Pull	"R3,sb,R12"
    	Pull	"R0-R4,PC"
20
        Pull    "R2,R3,sb,R12"
  ]


 [ E2ROMSupport
	MOV	R0, R2
	BL	GetI2CAddress		; convert to device address + offset
	MOV	R2, R0			; save the offset
 |
	MOV	R1, #RTCAddressPHI
 ]


        AND     R0, R1, #&FF            ; device address for write
        ORR     R0, R0, #1:SHL:29       ; retry
        TST     R1, #&100               ; NE if two byte offset
        SUB     R13, R13, #4
        MOV     R14, R13
        MOVNE   R1, R2, LSR #8
        STRNEB  R1, [R14], #1           ; offset (MSB)
        STRB    R2, [R14], #1           ; offset (LSB)
        STRB    R3, [R14], #1           ; data
        MOV     R1, R13
        SUB     R2, R14, R13
        BL      IIC_Op
        ADD     R13, R13, #4

	[ ChecksumCMOS
	TST	R4, #&1000000		; loop again to write new checksum
	MOV	R3, R4
	MOV	R2, #PhysChecksum
	ORR	R4, R4, #&1000000	; but ensure it only happens once
	BEQ	%BT10
	]
	Pull	"R0-R4, PC"

; *****************************************************************************
;
;       WriteBlock - Write a block of CMOS RAM specified by logical address
;
; in:   R0 = address in CMOS RAM
;       R1 = address to copy from
;       R2 = length
;
; out:  All registers preserved
;


WriteBlock ROUT
        Push    "R0-R4,R14"
  [     E2ROMSupport
        MOV	R14, #0
        LDRB    R4, [R14, #NVRamWriteSize]
        LDRB	R14, [R14, #NVRamSize]
        MOV     R4, R4, LSL #8
        MOV     R14, R14, LSL #8
  |
        MOV     R14, #240
        MOV     R4, R14
  ]

        CMP     R0, R14
        BHS     %FT90

        ADDS    R3, R0, R2              ; R3 = end address - check unsigned overflow
        BCS     %FT90
        CMP     R3, R14
        BHI     %FT90

        CMP     R0, R4                  ; ignore writes totally outside writable area
        BHS     %FT80

        SUBS    R14, R3, R4
        SUBGT   R2, R2, R14             ; truncate writes partially outside writable area

        TEQ     R2, #0
        BEQ     %FT80

        CMP     R0, #CheckSumCMOS       ; are we going to write the checksum byte?
        BHI     %FT03
        CMP     R3, #CheckSumCMOS
        BHI     %FT05

03
; we're not writing the checksum byte manually, so we need to update it
        MOV     R4, R1
        MOV     R1, #0
        BL      ChecksumBlock           ; find the checksum of what we're about to
        ORR     R3, R1, #&80000000      ; overwrite
        MOV     R1, R4
        B       %FT08

05      MOV     R3, #0
08      MOV     R4, #0
10      BL      WriteSubBlock
        BVS     %FT80
        TEQ     R2, #0
        BNE     %BT10

        TST     R3, #&80000000          ; were we going to write the checksum?
        BEQ     %FT80

        MOV     R0, #CheckSumCMOS
        BL      Read                    ; get old checksum byte
        ADD     R0, R0, R4              ; add new data checksum
        SUB     R1, R0, R3              ; subtract old checksum
        MOV     R0, #CheckSumCMOS
        BL      Write                   ; write back new checksum

80
        Pull    "R0-R4,PC"

90
        ADD     SP, SP, #4              ; junk stacked R0
        ADR     R0, ErrorBlock_CoreNotWriteable
 [ International
        BL      TranslateError
 |
        SETV
 ]
        Pull    "R1-R4,PC"

; *****************************************************************************
;
;       WriteSubBlock - Write a block of CMOS RAM specified by logical address.
;                       Assumes the address is valid, and will only read as much
;                       as it can in a single IIC transaction.
;
; in:   R0 = address in CMOS RAM
;       R1 = address to copy from
;       R2 = length
;
; out:  R0-R2 updated to reflect the amount written.
;       R4 incremented by sum of bytes written.
;
WriteSubBlock ROUT
        Push    "R3,R5-R6,R14"
        MOV     R6, R4
; establish end of the current contiguous block, and the logical->physical address offset.
        CMP     R0, #1                  ; 00 -> 40 uncached
        MOVLO   R3, #1
        MOVLO   R4, #&40-&00
        BLO     %FT10
        CMP     R0, #&C0                ; [01..C0) -> [41..100) cached
        MOVLO   R3, #&C0
        MOVLO   R4, #&41-&01
        BLO     %FT10
        CMP     R0, #&F0                ; [C0..F0) -> [10..40) cached
        MOVLO   R3, #&F0
        MOVLO   R4, #&10-&C0
        BLO     %FT10
        CMP     R0, #&100
        ADDHS   R3, R0, R2              ; [100..) -> [100..) uncached
        MOVHS   R4, #0
        BHS     %FT10

; [F0..100) -> not written
        MOV     R3, #&100
        ADD     R14, R0, R2
        CMP     R3, R14
        MOVHI   R3, R14
        SUB     R14, R3, R0
        ADD     R0, R0, R14
        ADD     R1, R1, R14
        SUB     R2, R2, R14
        Pull    "R3,R5-R6,PC"

; R3 = logical end of current segment (exclusive)
; R4 = offset from logical to physical address for this segment
10
        ADD     R14, R0, R2
        CMP     R3, R14
        MOVHI   R3, R14
 [ E2ROMSupport
; R3 = logical end of possible transaction (exclusive). Now check we don't cross page boundaries.
        MOV	R14, #0
        LDRB	R14, [R14, #NVRamPageSize]
        MOV     R5, #1
        MOV     R14, R5, LSL R14        ; R14 = (1<<pagesize)

        ADD     R5, R0, R4              ; R5 = physical start address
        ADD     R5, R5, R14
        SUB     R14, R14, #1
        BIC     R5, R5, R14             ; R5 = physical end of page with start address in
        SUB     R5, R5, R4              ; R5 = logical end of page with start address in

        CMP     R5, R3
        MOVLO   R3, R5                  ; adjust R3 to not cross page boundary
 ]

        CMP     R0, #&100               ; check it's a cacheable segment
        BHS     %FT15

        LDR     R14, =CMOSRAMCache
        Push    "R3, R4"
        ADD     R3, R3, R4              ; R3 = physical end address
        ADD     R4, R4, R0              ; R4 = physical address
        ADD     R3, R3, R14             ; R3 = cache end address
        ADD     R4, R4, R14             ; R4 = cache address
        SUB     R14, R3, R4             ; R14 = bytes being written
        MOV     R5, R1                  ; remember R1
12      LDRB    R14, [R1], #1           ; update cache copy
        STRB    R14, [R4], #1
        CMP     R4, R3
        BLO     %BT12
        MOV     R1, R5                  ; restore R1, and continue to update real memory
        Pull    "R3, R4"
15
        Push    "R0-R2"
        ADD     R0, R0, R4              ; R0 = physical address
  [ HAL
    	Push	"sb, R12"
    	MOV 	R5, R0	    	    	; save address
    	AddressHAL
    	CallHAL	HAL_NVMemoryType
    	AND 	R0, R0, #NVMemoryFlag_Provision
    	TEQ 	R0, #NVMemoryFlag_None

    	; If there's no NVmemory, tough - we just return.
    	Pull 	"sb,R12", EQ
    	Pull	"R0-R2", EQ
    	MOVEQ 	R2, #0	    	    	; nothing written
	Pull	"R3,R5-R6,PC", EQ

    	TEQ 	R0, #NVMemoryFlag_HAL
    	MOV 	R0, R5	    	    	; restore address
    	BNE 	%FT17	    	    	; do IIC things.

    	; Make the HAL call
    	CallHAL	HAL_NVMemoryWrite    	; returns bytes wrtten in R0
        MOV     R5, R0
    	Pull	"sb,R12"

    	Pull	"R0-R2"

        ADD     R0, R0, R5
        SUB     R2, R2, R5

16      SUBS    R5, R5, #1              ; update checksum
        LDRCS   R14, [R1], #1
        ADDCS   R6, R6, R14
        BCS     %BT16
        MOV     R4, R6

    	Pull	"R3,R5-R6,PC"

17
    	Pull	"sb,R12"
  ]

  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddressPHI
  ]

        MOV     R2, R0                  ; save the offset
        SUB     R13, R13, #12*2+4
        MOV     R14, R13
        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8
        STRNEB  R0, [R14], #1           ; offset (MSB)
        STRB    R2, [R14], #1           ; offset (LSB)

        SUB     R14, R14, R13
        STR     R14, [R13, #12]         ; transfer 1 length

        AND     R14, R1, #&FF
        ORR     R0, R14, #1:SHL:29      ; (retry)
        STR     R0, [R13, #4]           ; transfer 1 address
        ORR     R14, R14, #1:SHL:31     ; (no repeated start)
        STR     R14, [R13, #16]         ; transfer 2 address
        STR     R13, [R13, #8]          ; transfer 1 data

        ADD     R14, R13, #12*2+4
        LDMIA   R14, {R0-R2}
        SUB     R5, R3, R0              ; R5 = bytes being written
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        STMIB   R14, {R0,R2}

        STR     R1, [R13, #20]          ; transfer 2 data
        STR     R5, [R13, #24]          ; transfer 2 length
        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV

        LDR     R1, [R13, #20]          ; recover data pointer
        ADD     R13, R13, #12*2+4+4
20
        LDRB    R0, [R1], #1
        SUBS    R5, R5, #1
        ADD     R6, R6, R0              ; update checksum counter
        BNE     %BT20
                                        ; V clear
        MOV     R4, R6

	Pull	"R0,R2,R3,R5,R6,PC"

; *****************************************************************************
;
;	Read - Read a byte of CMOS RAM specified by logical address
;	ReadStraight - Read a byte of CMOS RAM specified by physical address
;       ReadWithError - Read a byte of CMOS RAM specified by logical address, giving error if out of range
;
; in:	R0 = address in CMOS RAM
;
; out:	R0 = data (illegal address return 0, or error for ReadWithError)
;	All other registers preserved
;

ReadStraight ROUT
	Push	"R1,R2,R14"
	B	%FT10

ReadWithError
        Push    "R1,R2,R14"
        BL      MangleCMOSAddress
        BCC     %FT10
        ADR     R0, ErrorBlock_CoreNotReadable
 [ International
        BL      TranslateError
 |
        SETV
 ]
        Pull    "R1,R2,PC"

        MakeErrorBlock CoreNotReadable

Read
	Push	"R1,R2,R14"
	BL	MangleCMOSAddress
	MOVCS	R0, #0			; pretend illegal addresses contain 0
	Pull	"R1,R2,PC", CS
10
	TEQ	R0, #&40		; is it Econet station number
	BEQ	%FT15	 		; if so then don't use cache
        CMP     R0, #&10                ; don't cache the clock
  [     E2ROMSupport
        BHS     %FT13
        MOV     R14, #0
        LDR     R14, [R14, #RTCFitted]
        TEQ     R14, #0
        BNE     %FT15
  |
        BLO     %FT15
  ]
13	CMP	R0, #&100		; check small cache limit
	LDRCC	R2, =CMOSRAMCache	; if in range
	LDRCCB	R0, [R2, R0]		; read from cache
	Pull	"R1,R2,PC", CC		; and exit
15

; else drop thru into real CMOS reading code

  [ HAL
    	Push	"R3,R4,sb,R12"
    	MOV 	R4, R0	    	    	; save address
    	AddressHAL
    	CallHAL	HAL_NVMemoryType
    	AND 	R0, R0, #NVMemoryFlag_Provision
    	TEQ 	R0, #NVMemoryFlag_None

    	; If there's no NVmemory, pretend addresses contain 0
    	Pull 	"R3,R4,sb,R12", EQ
    	MOVEQ 	R0, #0
	Pull	"R1,R2,PC", EQ

    	TEQ 	R0, #NVMemoryFlag_HAL
    	MOV 	R0, R4	    	    	; restore address
    	BNE 	%FT20

    	; Make the HAL call - we have to provide a buffer.
    	SUB 	sp, sp, #4	    	; make some space on the stack
    	MOV 	R1, sp
    	MOV 	R2, #1
    	CallHAL	HAL_NVMemoryRead
    	LDRB 	R0, [sp], #4	    	; read back from stack and restore
    	Pull	"R3,R4,sb,R12"
    	Pull	"R1,R2,PC"

20
        Pull    "R3,R4, sb,R12"
  ]

  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddressPHI
  ]

        SUB     R13, R13, #2*12+4
        MOV     R14, R13
        TST     R1, #&100
        MOVNE   R2, R0, LSR #8
        STRNEB  R2, [R14], #1           ; offset (MSB)
        STRB    R0, [R14], #1           ; offset (LSB)
        SUB     R14, R14, R13
        STR     R13, [R13, #8]          ; transfer 1 data
        STR     R14, [R13, #12]         ; transfer 1 length
        AND     R14, R1, #&FF
        ORR     R2, R14, #1:SHL:29      ; retry
        STR     R2, [R13, #4]           ; transfer 1 address
        ORR     R14, R14, #1            ; device address for read
        STR     R14, [R13, #16]         ; transfer 2 address
        ADD     R14, R13, #3
        STR     R14, [R13, #20]         ; transfer 2 data
        MOV     R14, #1
        STR     R14, [R13, #24]         ; transfer 2 length
        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV
        LDRB    R0, [R13, #3]
        ADD     R13, R13, #2*12+4

	Pull	"R1,R2,PC"


; *****************************************************************************
;
;       ReadBlock - Read a block of CMOS RAM specified by logical address
;
; in:   R0 = address in CMOS RAM
;       R1 = address to copy to
;       R2 = length
;
; out:  All registers preserved
;


ReadBlock ROUT
        Push    "R0-R3,R14"
  [     E2ROMSupport
        MOV	R14, #0
        LDRB	R14, [R14, #NVRamSize]
        MOV     R14, R14, LSL #8
  |
        MOV     R14, #240
  ]

        CMP     R0, R14
        BHS     %FT90

        ADDS    R3, R0, R2              ; R3 = end address - check unsigned overflow
        BCS     %FT90
        CMP     R3, R14
        BHI     %FT90

        TEQ     R2, #0
        BEQ     %FT80

10      BL      ReadSubBlock
        BVS     %FT80
        TEQ     R2, #0
        BNE     %BT10
80
        Pull    "R0-R3,PC"

90
        ADD     SP, SP, #4              ; junk stacked R0
        ADR     R0, ErrorBlock_CoreNotReadable
 [ International
        BL      TranslateError
 |
        SETV
 ]
        Pull    "R1-R3,PC"

; *****************************************************************************
;
;       ReadSubBlock - Read a block of CMOS RAM specified by logical address.
;                      Assumes the address is valid, and will only read as much
;                      as it can in a single IIC transaction.
;
; in:   R0 = address in CMOS RAM
;       R1 = address to copy to
;       R2 = length
;
; out:  R0-R2 updated to reflect the amount read.
;
ReadSubBlock ROUT
        Push    "R3-R5,R14"
; establish end of the current contiguous block, and the logical->physical address offset.
        CMP     R0, #1                  ; 00 -> 40 uncached
        MOVLO   R3, #1
        MOVLO   R4, #&40-&00
        BLO     %FT10
        CMP     R0, #&C0                ; [01..C0) -> [41..100) cached
        MOVLO   R3, #&C0
        MOVLO   R4, #&41-&01
        BLO     %FT10
        CMP     R0, #&F0                ; [C0..F0) -> [10..40) cached
        MOVLO   R3, #&F0
        MOVLO   R4, #&10-&C0
        BLO     %FT10
        CMP     R0, #&100               ; [F0..100) -> [00..10) cached
        MOVLO   R3, #&100
        MOVLO   R4, #&00-&F0
        ADDHS   R3, R0, R2              ; [100..) -> [100..) uncached
        MOVHS   R4, #0
; R3 = logical end of current segment (exclusive)
; R4 = offset from logical to physical address for this segment
10
        ADD     R14, R0, R2
        CMP     R3, R14
        MOVHI   R3, R14
; R3 = logical end of this transaction (exclusive)
        TEQ     R0, #0                  ; check it's a cacheable segment
        BEQ     %FT15
        CMP     R0, #&100
        BHS     %FT15

        LDR     R14, =CMOSRAMCache
        ADD     R3, R3, R4              ; R3 = physical end address
        ADD     R4, R4, R0              ; R4 = physical address
        ADD     R3, R3, R14             ; R3 = cache end address
        ADD     R4, R4, R14             ; R4 = cache address
        SUB     R14, R3, R4             ; R14 = bytes being read
        ADD     R0, R0, R14             ; update return R0
        SUB     R2, R2, R14             ; update return R2

12      LDRB    R14, [R4], #1
        CMP     R4, R3
        STRB    R14, [R1], #1
        BLO     %BT12
        Pull    "R3-R5,PC"              ; V will be clear
15
        Push    "R0-R2"
        ADD     R0, R0, R4              ; R0 = physical start address
        ADD     R3, R3, R4              ; R3 = physical end address
  [ HAL
    	Push	"sb"
        SUB     R2, R3, R0
    	MOV 	R5, R0	    	    	; save address
    	AddressHAL
    	Push	"R1-R3,R12"
    	CallHAL	HAL_NVMemoryType
    	Pull	"R1-R3,R12"
    	AND 	R0, R0, #NVMemoryFlag_Provision
    	TEQ 	R0, #NVMemoryFlag_None

    	; If there's no NVmemory, tough - we just return.
    	MOVEQ 	R2, #0	    	    	; nothing read
    	Pull 	"sb", EQ
	Pull	"R3-R5,PC", EQ

    	TEQ 	R0, #NVMemoryFlag_HAL
    	MOV 	R0, R5	    	    	; restore address
    	BNE 	%FT17	    	    	; do IIC things.

    	; Make the HAL call
    	Push	"R12"
    	CallHAL	HAL_NVMemoryRead    	; returns bytes read in R0
    	Pull	"R12"
        MOV     R4, R0
    	Pull	"sb"
        Pull    "R0-R2"
        ADD     R0, R0, R4
        ADD     R1, R1, R4
        SUB     R2, R2, R4
    	Pull	"R3-R5,PC"

17
    	Pull	"sb"
  ]

        SUB     R5, R3, R0              ; R5 = bytes being read

  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddressPHI
  ]


        MOV     R2, R0                  ; save the offset
        SUB     R13, R13, #12*2+4
        MOV     R14, R13
        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8
        STRNEB  R0, [R14], #1           ; offset (MSB)
        STRB    R2, [R14], #1           ; offset (LSB)

        SUB     R14, R14, R13
        STR     R14, [R13, #12]         ; transfer 1 length

        AND     R14, R1, #&FF
        ORR     R0, R14, #1:SHL:29      ; retry
        STR     R0, [R13, #4]           ; transfer 1 address
        ORR     R14, R14, #1            ; device address for read
        STR     R14, [R13, #16]         ; transfer 2 address
        STR     R13, [R13, #8]          ; transfer 1 data

        ADD     R14, R13, #12*2+4
        LDMIA   R14, {R0-R2}
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        STMIB   R14, {R0,R2}

        STR     R1, [R13, #20]          ; transfer 2 data
        STR     R5, [R13, #24]          ; transfer 2 length
        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV

        LDR     R1, [R13, #20]          ; recover data pointer
        ADD     R1, R1, R5
        ADD     R13, R13, #12*2+4+4

        CLRV

	Pull	"R0,R2,R3-R5,PC"

; *****************************************************************************
;
;       ChecksumBlock - Checksum a block of CMOS RAM specified by logical address
;                       Assumes the address is valid.
;
; in:   R0 = address in CMOS RAM
;       R1 = initial checksum
;       R2 = length
;
; out:  R1 incremented by sum of bytes in range
;


ChecksumBlock ROUT
        Push    "R0,R2,R14"

10      BL      ChecksumSubBlock
        BVS     %FT80
        TEQ     R2, #0
        BNE     %BT10
80
        Pull    "R0,R2,PC"


; *****************************************************************************
;
;       ChecksumSubBlock - Checksum a block of CMOS RAM specified by logical address.
;                          Assumes the address is valid, and will only read as much
;                          as it can in a single IIC transaction. Skips over
;                          239 (the checksum byte itself), and 240-255 (OTP area).
;
; in:   R0 = address in CMOS RAM
;       R1 = initial checksum
;       R2 = length
;
; out:  R0-R2 updated to reflect the data read.
;
ChecksumSubBlock ROUT
        Push    "R3-R5,R14"
; establish end of the current contiguous block, and the logical->physical address offset.
        CMP     R0, #1                  ; 00 -> 40 uncached
        MOVLO   R3, #1
        MOVLO   R4, #&40-&00
        BLO     %FT10
        CMP     R0, #&C0                ; [01..C0) -> [41..100) cached
        MOVLO   R3, #&C0
        MOVLO   R4, #&41-&01
        BLO     %FT10
        CMP     R0, #&EF                ; [C0..EF) -> [10..3F) cached
        MOVLO   R3, #&EF
        MOVLO   R4, #&10-&C0
        BLO     %FT10
        CMP     R0, #&100
        ADDHS   R3, R0, R2              ; [100..) -> [100..) uncached
        MOVHS   R4, #0
        BHS     %FT10

;  [EF..100) -> not checksummed
        MOV     R3, #&100
        ADD     R14, R0, R2
        CMP     R3, R14
        MOVHI   R3, R14
        SUB     R14, R3, R0
        ADD     R0, R0, R14
        SUB     R2, R2, R14
        Pull    "R3-R5,PC"


; R3 = logical end of current segment (exclusive)
; R4 = offset from logical to physical address for this segment
10
        ADD     R14, R0, R2
        CMP     R3, R14
        MOVHI   R3, R14
; R3 = logical end of this transaction (exclusive)

        TEQ     R0, #0                  ; check it's a cacheable segment
        BEQ     %FT15
        CMP     R0, #&100
        BHS     %FT15

        LDR     R14, =CMOSRAMCache
        ADD     R3, R3, R4              ; R3 = physical end address
        ADD     R4, R4, R0              ; R4 = physical address
        ADD     R3, R3, R14             ; R3 = cache end address
        ADD     R4, R4, R14             ; R4 = cache address
        SUB     R14, R3, R4             ; R14 = bytes being read
        ADD     R0, R0, R14             ; update return R0
        SUB     R2, R2, R14             ; update return R2

12      LDRB    R14, [R4], #1
        CMP     R4, R3
        ADD     R1, R1, R14
        BLO     %BT12
        Pull    "R3-R5,PC"
15
        Push    "R0-R2"
        ADD     R0, R0, R4              ; R0 = physical start address
        ADD     R3, R3, R4              ; R3 = physical end address
  [ HAL
    	Push	"sb,R12"
    	MOV 	R5, R0	    	    	; save address
    	AddressHAL
    	Push	"R1-R3"
    	CallHAL	HAL_NVMemoryType
    	Pull	"R1-R3"
    	AND 	R4, R0, #NVMemoryFlag_Provision
    	MOV 	R0, R5	    	    	; restore address
    	TEQ 	R4, #NVMemoryFlag_None
        TEQNE   R4, #NVMemoryFlag_HAL
        BNE     %FT17                   ; do IIC things.

        SUB     R14, R3, R0
        LDR     R1, [R13,#8]
        LDR     R3, [R13,#16]
        ADD     R1, R1, R14
        SUB     R3, R3, R14
        STR     R1, [R13,#8]
        STR     R3, [R13,#16]

    	TEQ 	R4, #NVMemoryFlag_None
    	; If there's no NVmemory, tough - we just return.
    	Pull 	"sb,R12", EQ
	Pull	"R0-R5,PC", EQ

        Push    "R6"
        MOV     R4, #0
        ADD     R6, R5, R14
        SUB     R13, R13, #4
16
        MOV     R0, R5
        MOV     R1, sp
        MOV     R2, #1
        CallHAL HAL_NVMemoryRead
        LDRB    R14, [R13]
        ADD     R4, R4, R14
        ADD     R5, R5, #1
        TEQ     R5, R6
        BNE     %BT16
        ADD     R13, R13, #4
        Pull    "R6,sb,R12"
        Pull    "R0-R2"
        ADD     R1,R1,R4
        Pull    "R3-R5,PC"
17
        Pull    "sb,R12"
  ]
        SUB     R5, R3, R0              ; R5 = bytes being read

  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddressPHI
  ]

        MOV     R2, R0                  ; save the offset
        SUB     R13, R13, #12*2+4
        MOV     R14, R13
        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8
        STRNEB  R0, [R14], #1           ; offset (MSB)
        STRB    R2, [R14], #1           ; offset (LSB)

        SUB     R14, R14, R13
        STR     R14, [R13, #12]         ; transfer 1 length

        AND     R14, R1, #&FF
        ORR     R0, R14, #1:SHL:29      ; retry
        STR     R0, [R13, #4]           ; transfer 1 address
        ORR     R14, R14, #1            ; device address for read
        ORR     R14, R14, #1:SHL:30     ; checksum only please
        STR     R14, [R13, #16]         ; transfer 2 address
        STR     R13, [R13, #8]          ; transfer 1 data

        ADD     R14, R13, #12*2+4
        LDMIA   R14, {R0-R2}
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        STMIB   R14, {R0,R2}
        MOV     R4, R1                  ; remember checksum

        STR     R5, [R13, #24]          ; transfer 2 length
        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV

        LDR     R1, [R13, #20]          ; read back checksum
        ADD     R1, R1, R4              ; update checksum
        ADD     R13, R13, #12*2+4+4

	Pull	"R0,R2,R3-R5,PC"

; *****************************************************************************
;
;	GetI2CAddress - Convert NVRam physical address to i2c device address
;	                and offset
;
; in:	R0 = NVRam physical address (&00..size of NVRam)
;
; out:	R0 preserved
;
;	C=0 => NVRam address is valid
;	 R0 = physical address within i2c device
;	 R1 = i2c device address for writing. Increment this device address
;	      by 1 for reading. Bit 8 is set if device requires 2-byte physical address.
;
;	C=1 => NVRam address is out of range of CMOS or E2ROM chips
;	 R0 preserved
;	 R1 preserved

  [	E2ROMSupport
GetI2CAddress ROUT
	Push	"R14"
	MOV	R14, #0			; get no 256 byte blocks and calculate end address
	LDRB	R14, [R14, #NVRamSize]
	MOV	R14, R14, LSL #8
	CMP	R0, R14
	Pull	"PC",CS 		; indicate invalid

; address is < end address -> is valid
	MOV	R1, #0
	;LDRB	R1, [R1, #RTCFitted]    ; This causes a headache if both an RTC and NVRAM are fitted
	;TEQ	R1, #0
	;LDREQB  R1, [R1, #NVRamBase]   ; RTC overlaps bottom of NVRAM
	LDRB    R1, [R1, #NVRamBase]    ; RTC overlaps bottom of NVRAM

        CMP     R14, #2*1024            ; is the device bigger than 2K? If so, new addressing scheme
        ORRHI   R1, R1, #&100           ; set magic bit => 2 byte address
        BHI     %FT50

	MOVS	R14, R0, LSR #8		; put top bits of physical address into device address
	ORRNE	R1, R1, R14, LSL #1
	ANDNE	R0, R0, #&FF		; and use address within 256 byte block
50
        CLC
	Pull	"PC"           		; indicate valid

  ]


; *****************************************************************************
;
;	MangleCMOSAddress - Convert from logical to physical address
;
;	Doesn't check if address is larger than the amount of NVRam installed
;
; in:	R0 = logical address (&00...)
;
; out:	C=0 => valid logical address
;	 R0 = physical address (&40..&FF,&10..&3F,&00..0F,&100..)
;
;	C=1 => invalid logical address
;	 R0 preserved
;

MangleCMOSAddress ROUT
 [ E2ROMSupport
	Push	"R14"
	MOV	R14, #0			; read no 256 byte blocks and calculate end address
	LDRB	R14, [R14, #NVRamSize]
	MOV	R14, R14, LSL #8
	CMP	R0, R14			; if >= end address then
	Pull	"R14"
        MOVCS   PC, R14 		;    invalid (exit C set)

	CMP	R0, #&100		; if < end address && >= &100 then
        BLO     %FT05
        CLC
        MOV     PC, R14                 ;    valid (no mungeing)
 ]
05
	CMP	R0, #&F0		; if < &100 && >= &f0 then
        [ E2ROMSupport
        BCC     %FT10
        SUB     R0, R0, #&F0            ;    map &F0->&FF to &00->0F for OTP section
        CLC
        MOV     PC, R14
        |
        MOVCS   PC, R14                 ;    invalid
        ]
10
	ADD	R0, R0, #&40		; now in range &40..&13F
	CMP	R0, #&100
	SUBCS	R0, R0, #(&100-&10)	; now in range &40..&FF, &10..&3F
        CLC
        MOV     PC, R14                 ; valid

; *****************************************************************************
;
;	ValChecksum - test to see if the CMOS checksum is OK
;
;       This routine performs MangleCMOSAddress inherently.
;
;       The checksum does not include physical locations &00->&0F, even
;       if they are OTP section (as this is usually used for a unique id
;       which will be different for every machine and can't be changed).
;
; in:	none
;
; out:	R0 = calculated checksum
;	Z	set if checksum is valid
;	All other registers preserved
;

  [ ChecksumCMOS

ValChecksum	Entry "R1-R2"

        MOV     R0, #0
        MOV     R1, #CMOSxseed
   [ E2ROMSupport
	MOV	R2, #0			; read number of 256 byte blocks and calculate end address
	LDRB	R2, [R2, #NVRamSize]
	MOV	R2, R2, LSL #8
   |
	MOV	R2, #240
   ]
        BL      ChecksumBlock

;
; R1 contains the actual checksum. Compare it with the recorded checksum
;
40
	MOV	R0, #CheckSumCMOS
	BL	Read
	AND	R2, R0, #&FF		; value from checksum location
	AND	R0, R1, #&FF		; calculated value into R0
	CMPS	R0, R2

	EXIT
  ]

; *****************************************************************************
;
;	MakeChecksum - calculate and write a correct checksum
;
; in:	none
;
; out:	R0 = calculated checksum
;	All other registers preserved
;

	[ ChecksumCMOS

MakeChecksum	ROUT
	Push	"R1-R2,R14"
        MOV     R0, #0
        MOV     R1, #CMOSxseed
  [ E2ROMSupport
	MOV	R2, #0
	LDRB	R2, [R2, #NVRamSize]
	MOV	R2, R2, LSL #8
  |
	MOV	R2, #240
  ]
        BL      ChecksumBlock
	MOV	R0, #CheckSumCMOS
	BL	Write
	Pull	"R1-R2,PC"
	]

        LTORG

; *****************************************************************************
;
;	SetTime - Write the CMOS clock time and update 5-byte RealTime
;
; in:	UTC time:
;	R0 = hours
;	R1 = minutes
;	R2 = day of month
;	R3 = month
;	R5 = year (lo)
;	R6 = year (hi)
;	R7 = seconds
;	R8 = centiseconds
;
;	If R0,R2,R5 or R6 is -1 then the time,date,year,century (respectively) will not be written
;

SetTime ROUT
	Push	"R4, R9-R10, R14"	; save registers

	LDR	R10, =ZeroPage
	LDRB	R10, [R10, #RTCFitted]
        TEQ     R10, #0			; no RTC - just set soft copy
        BNE     %FT20

        BL      RegToRealTime
        Pull    "R4, R9-R10, PC"

20
	Push	"R0-R2"
	MOV	R4, R0			; copy hours in R4

	Push	"R1"
	MOVS	R1, R5                  ; year held seperately from the RTC
	MOVPL	R0, #YearCMOS
	BLPL	Write
	MOVS	R1, R6
	MOVPL	R0, #YearCMOS+1
	BLPL	Write
	Pull	"R1"

	CMP	R4, #-1			; are we writing time ?
	BEQ	%FT30

        SUB     R13, R13, #8
        MOV     R9, R13
	TEQ	R10, #RTCAddressPHI
        MOVEQ   R0, #1                  ; offset to start of time (varies)
        MOVNE   R0, #0
        STRB    R0, [R9], #1
        MOVEQ   R0, R8                  ; centiseconds (not for Dallas parts)
        BLEQ    HTBS9
        MOV     R0, R7                  ; seconds
        BL      HTBS9
        MOV     R0, R1                  ; minutes
        BL      HTBS9
        MOV     R0, R4                  ; hours
        BL      HTBS9
        MOV	R6, R2
	BL	%FT45
	MOV	R2, R6			; preserve D-O-M for later
        ADD	R13, R13, #8

30
	CMP	R2, #-1			; are we writing date ?
	BEQ	%FT40

        SUB     R13, R13, #8
        MOV     R9, R13
	TEQ	R10, #RTCAddressPHI
	BNE     %FT35

	; Philips specific handling
        MOV     R0, #5                  ; offset 5
        STRB    R0, [R9], #1
        MOV     R0, R2                  ; day of month
        BL      HexToBCD
        ORR     R0, R0, R5, LSL #6      ; year in bits 6,7; day in bits 0..5
        STRB    R0, [R9], #1
        MOV     R0, R3                  ; months
        BL      HTBS9
	B	%FT37
35
	; Dallas specific handling
	MOV	R0, #4                  ; offset 4
	STRB    R0, [R9], #1
	MOV     R0, R2
	BL	HTBS9
	MOV	R0, R3
	BL	HTBS9
	MOV	R0, R5
	BL	HTBS9			; don't bother setting the D-O-W
37
	BL      %FT45
	ADD	R13, R13, #8

40
	Pull    "R0-R2"
	BL	RTCToRealTime		; update RAM copy
	Pull	"R4, R9-R10, PC"

45
	MOV     R0, R10
        ORR     R0, R0, #1:SHL:29
        MOV     R1, R13
        SUB     R2, R9, R13
        Push    "R14"
        BL      IIC_Op                  ; write the prepared block with retries
	Pull    "PC"

; *****************************************************************************
;
;	ReadTime - Read the CMOS clock time
;
; in:	-
;
; out:	R0 = hours
;	R1 = minutes
;	R2 = days
;	R3 = months
;	R5 = year (lo)
;	R6 = year (hi)
;	R7 = seconds
;	R8 = centiseconds
;

ReadTime ROUT
	Push	"R4, R14"
        SUB     R13, R13, #(12*2)+6+1+1

        LDR	R14, =ZeroPage
        LDRB	R2, [R14, #RTCFitted]
        ORR     R14, R2, #1:SHL:29      ; retry
        STR     R14, [R13, #8]          ; transfer 1 address
        ADD     R0, R14, #1
        STR     R0, [R13, #20]          ; transfer 2 address
        TEQ	R2, #RTCAddressPHI
        MOVEQ   R0, #1			; PHI time starts at +1
        MOVNE   R0, #0			; DAL time starts at +0
        STRB    R0, [R13, #0]
        STR     R13, [R13, #12]         ; transfer 1 dataptr
        MOV     R0, #1
        STR     R0, [R13, #16]          ; transfer 1 length
        ADD     R0, R13, #1
        STR     R0, [R13, #24]          ; transfer 2 dataptr
        MOV     R0, #6
        STR     R0, [R13, #28]          ; transfer 2 length

        ADD     R0, R13, #8
        MOV     R1, #2
        BL      IIC_OpV

	TEQ     R2, #RTCAddressPHI
	BNE	%FT50

	; Philips specific read
        LDRB    R0, [R13, #1]
        BL      BCDToHex
        MOV     R8, R0                  ; centiseconds
        LDRB    R0, [R13, #2]
        BL      BCDToHex
        MOV     R7, R0                  ; seconds
        LDRB    R0, [R13, #3]
        BL      BCDToHex
        MOV     R1, R0                  ; minutes
        LDRB    R0, [R13, #4]
        BL      BCDToHex
        MOV     R4, R0                  ; hours
        LDRB    R0, [R13, #5]
        AND     R0, R0, #&3F            ; day of month (clear year bits)
        BL      BCDToHex
        MOV     R2, R0
        LDRB    R0, [R13, #6]
        AND     R0, R0, #&1F            ; month (clear day of week bits)
        BL      BCDToHex
        MOV     R3, R0
	B	ReadTimeYear

50
	; Dallas specific read
        MOV     R8, #0                  ; centiseconds
        LDRB    R0, [R13, #1]
        BL      BCDToHex
        MOV     R7, R0                  ; seconds
        LDRB    R0, [R13, #2]
        BL      BCDToHex
        MOV     R1, R0                  ; minutes
        LDRB    R0, [R13, #3]
        BL      BCDToHex
        MOV     R4, R0                  ; hours
        LDRB    R0, [R13, #5]
        BL      BCDToHex
        MOV     R2, R0                  ; day of month
        LDRB    R0, [R13, #6]
        BL      BCDToHex
        MOV     R3, R0                  ; month

ReadTimeYear
	ADD     R13, R13, #(12*2)+6+1+1
	MOV	R0, #YearCMOS
	BL	Read
	MOV	R5, R0			; year (lo)
	MOV	R0, #YearCMOS+1
	BL	Read
	MOV	R6, R0			; year (hi)

	MOV	R0, R4			; put hours in R0

	TEQ	R2, #0			; Ensure day/month are non-zero (LRust, fix RP-0370)
	MOVEQ	R2, #1			; No then force 1st
	TEQ	R3, #0			; Invalid month?
	MOVEQ	R3, #1			; Yes then force Jan

	Pull	"R4, PC"

; *****************************************************************************
;
;       InitRTC - Force the clock into a known state (incase of new battery)
;
;  in:  R0 = RTC IIC address

InitRTC
	Push    "R0-R2, R14"
	SUB	R13, R13, #4
	MOV	R1, R13
	TEQ	R0, #RTCAddressPHI
	BEQ	%FT60
	ORR     R0, R0, #1:SHL:29
	MOV	R2, #0			; to write 0 to address ptr
	STRB	R2, [R1]
	MOV	R2, #1
	BL	IIC_Op
	ORR	R0, R0, #1
	BL	IIC_Op
	LDRB	R2, [R1]
	ANDS	R2, R2, #&80		; test clock disabled bit
	BEQ	%FT65
	EOR	R0, R0, #1
	DebugTX "Forced RTC on"
60
	ORR     R0, R0, #1:SHL:29
	MOV     R2, #0
	STR	R2, [R1]
	MOV	R2, #2			; to write 0 to control reg 0
	BL      IIC_Op
65
	ADD	R13, R13, #4
	Pull    "R0-R2, PC"

; *****************************************************************************
;
;	InitCMOSCache - Initialise cache of CMOS RAM
;  in: -
;
;  out: R0 = 0 for failure

InitCMOSCache	Entry "r1-r6, sb,r12"
    [	E2ROMSupport

	; Need to set the slowest speed so we can probe
	LDR     R4, =ZeroPage
	MOV     R3, #10         ; Default speed setting (5�s delays)
	STRB    R3, [R4, #NVRamSpeed]

	; Have we got an RTC ?
	LDR 	R2, =ZeroPage
	MOV	R4, #0
     [  RTCSupport
	MOV	R0, #RTCAddressDAL
	BL	DummyAccess
	BVC	%FT13
	MOV	R0, #RTCAddressPHI
	BL	DummyAccess
	STRVSB  R4, [R2, #RTCFitted]
13
	STRVCB  R0, [R2, #RTCFitted]
	BLVC    InitRTC
     |
        STRB    R4, [R2, #RTCFitted]
     ]

 [ HAL
        AddressHAL
    	CallHAL	HAL_NVMemoryType
    	MOV 	R5, R0
    	ANDS 	R0, R0, #NVMemoryFlag_Provision
        BEQ     InitCMOSCache_NoCMOS

    	; If it's only a maybe, then we probe
    	TEQ 	R0, #NVMemoryFlag_MaybeIIC
    	BEQ 	%FT03

    	; Else we read the size

    	CallHAL	HAL_NVMemorySize	; returns number of bytes but..
    	MOV 	R0, R0, LSR#8	    	; .. expecting no. of 256 blocks
    	STRB 	R0, [R4, #NVRamSize]

        TST     R5, #NVMemoryFlag_ProtectAtEnd
        STREQB  R0, [R4, #NVRamWriteSize]
        BEQ     %FT02
        CallHAL HAL_NVMemoryProtectedSize
        LDRB    R1, [R4, #NVRamSize]
        SUB     R0, R1, R0, LSR#8
        STRB    R0, [R4, #NVRamWriteSize]

02
        CallHAL HAL_NVMemoryPageSize    ; returns size in bytes but..
        TEQ     R0, #0                  ; .. expecting power of 2
        MVNEQ   R0, #0
        MOV     R1, #0
22      MOVS    R0, R0, LSR #1
        ADDNE   R1, R1, #1
        BNE     %BT22
        STRB    R1, [R4, #NVRamPageSize]

    	CallHAL	HAL_NVMemoryIICAddress
    	STRB 	R0, [R4, #NVRamBase]

    	MOV 	R0, #0
    	CallHAL	HAL_IICType
    	MOV 	R3, #10
    	TST 	R0, #IICFlag_Fast
    	MOVNE	R3, #3
    	TST 	R0, #IICFlag_HighSpeed
    	MOVNE	R3, #1
    	STRB	R3, [R4, #NVRamSpeed]

    	; If we're using IIC then read in the cache manually
        AND	R0, R5, #NVMemoryFlag_Provision
    	TEQ 	R0, #NVMemoryFlag_IIC
    	BEQ   	%FT06

    	; Else use HAL routine.
    	MOV 	R0, #0
    	LDR 	R1, =CMOSRAMCache
    	MOV 	R2, #&100
    	CallHAL	HAL_NVMemoryRead
    	TEQ 	R0, #&100
    	MOVNE	R0, #0			; Failure exit condition
    	EXIT
03
 ]

;	No HAL,so determine what hardware we've got fitted by probing,
;	R4 holds the number of 256 byte blocks that we've found

        MOV     R3, #10         ; assume 100kHz to start with
        MOV     R5, #4          ; assume 16 byte page size to start with
        MOV     R6, #0          ; assume not protected

; Have we got a 2K device ?
        MOV     r1, #E2ROMAddress2K
	MOV	r0, #(E2ROMAddress2K+14)
	BL	DummyAccess
	MOVVC	R4, #8
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
	BVC	%FT5

; Have we got a 16K device ?
        MOV     r1, #E2ROMAddress16K
	MOV	r0, #E2ROMAddress16K
	BL	DummyAccess
	MOVVC	R4, #64
        MOVVC   R5, #6          ; 64 byte page size
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
        BVC     %FT5

; Have we got a 4K device?
        MOV     r1, #E2ROMAddress4K
	MOV	r0, #E2ROMAddress4K
	BL	DummyAccess
	MOVVC	R4, #16
        MOVVC   R6, #12         ; Only bottom 3K writable
        MOVVC   R5, #5          ; 32 byte page size
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
        BVC     %FT5

; Have we got an 8K device?
        MOV     r1, #E2ROMAddress8K
	MOV	r0, #E2ROMAddress8K
	BL	DummyAccess
	MOVVC	R4, #32
        MOVVC   R5, #5          ; 32 byte page size
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
        BVC     %FT5

; Have we got a protected 8K device?
        MOV     r1, #E2ROMAddress8K_prot
	MOV	r0, #E2ROMAddress8K_prot
	BL	DummyAccess
	MOVVC	R4, #32
        MOVVC   R6, #24         ; Only bottom 6K writable
        MOVVC   R5, #5          ; 32 byte page size
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
        BVC     %FT5

; Have we got a 32K device?
        MOV     r1, #E2ROMAddress32K
        MOV     r0, #E2ROMAddress32K
        BL      DummyAccess
        MOVVC   R4, #128        ; 128,120,6,1
        MOVVC   R6, #120        ; Only bottom 30K writable
        MOVVC   R5, #6          ; 64 byte page size
        MOVVC   R3, #1          ; Hyper-fast speed setting (0.5�s delays - 1MHz part)
    	BVC 	%FT5

; Any storeage in the Philips RTC? (Dallas capacity is tiny,ignored)
        MOV     R1, #RTCAddressPHI
        MOV     R0, #RTCAddressPHI
    	BL  	DummyAccess
        MOV     R5, #8          ; 256 byte page size for CMOS
        MOVVC   R4, #1
    	BVC 	%FT5

; We ain't got anything!
InitCMOSCache_NoCMOS
        LDR     R2, =ZeroPage
        MOV     R5, #8
        STRB    R5, [R2, #NVRamPageSize]        ; Act as though we have 256 bytes of
        MOV     R1, #1                          ; single page CMOS.
        STRB    R1, [R2, #NVRamSize]
        STRB    R1, [R2, #NVRamWriteSize]
    	MOV 	R0, #0		; Exit failure
    	EXIT

5
	; Set the NVRam count
	STRB    R1, [R2, #NVRamBase]
	STRB	R4, [R2, #NVRamSize]
        STRB    R5, [R2, #NVRamPageSize]
        TEQ     R6, #0
        MOVEQ   R6, R4
        STRB    R6, [R2, #NVRamWriteSize]

        CMP     R3, #I2Cticks   ; clamp speed to maximum bus speed
        MOVLO   R3, #I2Cticks
        STRB    R3, [R2, #NVRamSpeed]
06
	; Initialise the cache
	LDR	R3, =CMOSRAMCache

        TEQ     R4, #8                  ; check for 2K part
	MOVNE	r0, #&00		; if not, then start at 0 anyway and read non-OTP data into location 0..15
        BNE     %FT07
	BL	ReadOTPArea
	MOV	r0, #&10		; read rest of it from 16 onwards
07
        BL      GetI2CAddress           ; and convert to device address and offset
        MOV     R2, R0                  ; save the offset
        MOV     R4, #&100               ; stop at &100
    |
    	; No E2ROM support,assume a Philips RTC
    	MOV	R1, #RTCAddressPHI
    	MOV	R2, #&10
    	MOV	R4, #&100		; stop at address &100
    	LDR	R3, =CMOSRAMCache
    ]

        ; Note - R4 MUST be &100 to prevent crossover between 256-byte pages
        ; (for devices with multiple addresses)
09

        SUB     R13, R13, #2*12+4
        AND     R0, R1, #&FF
        STR     R0, [R13, #4]           ; transfer 1 address
        ADD     R0, R0, #1              ; read address
        STR     R0, [R13, #16]          ; transfer 2 address
        TST     R1, #&100               ; 2-byte address?
        MOV     R14, R13
        MOVNE   R0, R2, LSR #8
        STRNEB  R0, [R14], #1           ; memory word address (MSB)
        STRB    R2, [R14], #1           ; memory word address (LSB)
        STR     R13, [R13, #8]          ; transfer 1 data
        SUB     R14, R14, R13
        STR     R14, [R13, #12]         ; transfer 1 length
        ADD     R14, R3, R2
        STR     R14, [R13, #20]         ; transfer 2 data
        SUB     R14, R4, R2
        STR     R14, [R13, #24]         ; transfer 2 length

        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV

        ADD     R13, R13, #2*12+4
        MOV     R0, #1
        EXIT

   [ E2ROMSupport
ReadOTPArea Entry
        SUB     R13, R13, #2*12+4
        MOV     R0, #0
        STRB    R0, [R13, #0]           ; offset 0
        MOV     R0, #E2ROMAddress2K_OTP
        STR     R0, [R13, #4]           ; transfer 1 address
        STR     R13, [R13, #8]          ; transfer 1 data
        MOV     R0, #1
        STR     R0, [R13, #12]          ; transfer 1 length
        MOV     R0, #E2ROMAddress2K_OTP + 1
        STR     R0, [R13, #16]          ; transfer 2 address
        STR     R3, [R13, #20]          ; transfer 2 data
        MOV     R0, #16
        STR     R0, [R13, #24]          ; transfer 2 length
        ADD     R0, R13, #4
        MOV     R1, #2
        BL      IIC_OpV
        ADD     R13, R13, #2*12+4
        EXIT
   ]

; *****************************************************************************
;
;	DummyAccess - do a dummy access of the specified device to find out
;	              if it is present
;
; in:	R0 = Write address of device
;
; out:	All registers preserved
;	V=0 => device is present
;	V=1 => device is not present

  [ E2ROMSupport
DummyAccess

 [ {TRUE}
        ; Blooming 80321 HW IIC can't do just START address STOP
	Entry   "R0-R2",4
        ORR     R0, R0, #1
        MOV     R1, R13
        MOV     R2, #1
        BL      IIC_Op
 |
	Entry   "R1,R2"
        MOV     R1, #0
        MOV     R2, #0
        BL      IIC_Op
 ]

	EXIT				; Exit with V set appropriately
  ]

; *****************************************************************************
;
;       SWI OS_NVMemory
;
; in:   R0 = reason code
;

NVMemorySWI     Entry
        BL      NVMemorySub
        PullEnv
        ORRVS   LR, LR, #V_bit
        ExitSWIHandler

NVMemorySub
        CMP     R0, #4
        ADDLS   PC, PC, R0, LSL #2
        B       NVMemory_Unknown
        B       NVMemory_Size
        B       NVMemory_Read
        B       NVMemory_Write
        B       NVMemory_ReadBlock
        B       NVMemory_WriteBlock

NVMemory_Unknown
        ADRL    R0, ErrorBlock_HeapBadReason
 [ International
        Push    LR
        BL      TranslateError
        Pull    LR
 ]
        RETURNVS

; -----------------------------------------------------------------------------
; OS_NVMemory 0 - find NV memory size
;
; in:   R0 = 0
;
; out:  R1 = NV memory size in bytes
;
NVMemory_Size
  [     E2ROMSupport
        LDRB    R1, [R0, #NVRamSize]
        MOV     R1, R1, LSL #8
  |
        MOV     R1, #240
  ]
        MOV     PC, LR

; -----------------------------------------------------------------------------
; OS_NVMemory 1 - read a byte
;
; in:   R0 = 1
;       R1 = location
;
; out:  R2 = value
;
NVMemory_Read
        Entry   "R4"
        MRS     R4, CPSR
        BIC     R0, R4, #I32_bit
        MSR     CPSR_c, R0      ; enable interrupts - this may take some time
        MOV     R0, R1
        BL      ReadWithError
        MOVVC   R2, R0
        MOVVC   R0, #1          ; must preserve R0
        ORRVS   R4, R4, #V_bit
        MSR     CPSR_cf, R4     ; restore interrupt state
        EXIT

; -----------------------------------------------------------------------------
; OS_NVMemory 2 - write a byte
;
; in:   R0 = 1
;       R1 = location
;       R2 = value
;
NVMemory_Write ROUT
        [ ProtectStationID
        TEQ     R1, #0          ; just ignore writes to byte 0
        MOVEQ   PC, R14
        ]
        Entry   "R1,R4"
        MRS     R4, CPSR
        BIC     R0, R4, #I32_bit
        MSR     CPSR_c, R0      ; enable interrupts - this may take some time
        MOV     R0, R1
        MOV     R1, R2
        BL      WriteWithError
        MOVVC   R0, #2          ; must preserve R0
        ORRVS   R4, R4, #V_bit
        MSR     CPSR_cf, R4     ; restore interrupt state
        EXIT

; -----------------------------------------------------------------------------
; OS_NVMemory 3 - read a block
;
; in:   R0 = 3
;       R1 = location
;       R2 = buffer
;       R3 = length
;
NVMemory_ReadBlock
        Entry   "R1-R4"
        MOV     R4, PC
        MRS     R4, CPSR
        BIC     R0, R4, #I32_bit
        MSR     CPSR_c, R0      ; enable interrupts - this may take some time
        MOV     R0, R1
        MOV     R1, R2
        MOV     R2, R3
        BL      ReadBlock
        MOVVC   R0, #3          ; must preserve R0
        ORRVS   R4, R4, #V_bit
        MSR     CPSR_cf, R4     ; restore interrupt state
        EXIT

; -----------------------------------------------------------------------------
; OS_NVMemory 4 - write a block
;
; in:   R0 = 3
;       R1 = location
;       R2 = buffer
;       R3 = length
;
NVMemory_WriteBlock ROUT
        Entry   "R1-R4"
        MRS     R4, CPSR
        BIC     R0, R4, #I32_bit
        MSR     CPSR_c, R0      ; enable interrupts - this may take some time
        [ ProtectStationID
        TEQ     R1, #0
        BNE     %FT10
        ADD     R1, R1, #1
        ADD     R2, R2, #1
        TEQ     R3, #0
        SUBNE   R3, R3, #1      ; steer clear of station ID
        ]
10      MOV     R0, R1
        MOV     R1, R2
        MOV     R2, R3
        BL      WriteBlock
        MOVVC   R0, #4          ; must preserve R0
        ORRVS   R4, R4, #V_bit
        MSR     CPSR_cf, R4     ; restore interrupt state
        EXIT

	END