; 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.
; amg 7/12/96 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)

PollMax		*	150	; Number of times to poll for an Ack (increase if you clock faster - need to
                                ; allow 5ms for write cycle).

; Device addresses
RTCAddress		*	&a0     ; traditional RTC / 240 byte CMOS
  [	E2ROMSupport
E2ROMAddress		*	&a8     ; 24C08 device - 512 byte or 1K
E2ROMAddress2K		*	&e0     ; 24C174 device - 2K
E2ROMAddress2K_OTP	*	&60     ; 24C174 device - OTP section
E2ROMAddress4K          *       &a4     ; 24C32 device - 4K (top 1K protectable)
E2ROMAddress8K          *       &a2     ; 24C64 device - 8K (top 2K protectable)
E2ROMAddress16K         *       &a8     ; 24C128 device - 16K
  ]

; *****************************************************************************
;
;	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"

; *****************************************************************************
;
;	SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
;
; out:	All registers preserved, including PSR
;

SetC1C0 ROUT
	Push	"R0-R2,R14"
	ADD	R0, R0, R1, LSL #1	; R0 := C0 + C1*2

	[ AssemblingArthur :LOR: Module
	MOV	R2, #0				; prepare to index soft copy
	LDRB	R1, [R2, #IOCControlSoftCopy]	; read soft copy
	BIC	R1, R1, #&03			; clear clock and data
	ORR	R0, R1, R0			; put in new clock and data
	ORR	R0, R0, #&C0			; make sure two test bits are
						; always set to 1 !
	STRB	R0, [R2, #IOCControlSoftCopy]	; store back to soft copy
	|
	ORR	R0, R0, #&FC			; set other bits to 1
	]

	MOV	R2, #IOC
	STRB	R0, [R2, #IOCControl]

 [ ClockNVMemoryFast
        [ :LNOT: :DEF: TestHarness
        MOV	R0, #0
        LDRB	R0, [R0, #NVRamSpeed]
        |
        LDRB	R0, NVSpeed
        ]
        TEQ     R0, #0
        MOVEQ   R0, #10                         ; default value if speed not checked yet
 |
        MOV     R0, #10                         ; 5�s delay
 ]
	BL	DoMicroDelay

	Pull	"R0-R2,PC",,^

; *****************************************************************************
;
;	DoMicroDelay - Delay for >= R0/2 microseconds
;
; in:	R0 = time delay in 1/2 microsecond units
;	R2 -> IOC
;	On ARM600, we may or may not be in a 32-bit mode
;
; out:	R0,R1 corrupted
;

DoMicroDelay ROUT
	Push	R14

	STRB	R0, [R2, #Timer0LR]	; copy counter into output latch
	LDRB	R1, [R2, #Timer0CL]	; R1 := low output latch
10
	STRB	R0, [R2, #Timer0LR]	; copy counter into output latch
	LDRB	R14, [R2, #Timer0CL]	; R14 := low output latch
	TEQ	R14, R1			; unchanged ?
	BEQ	%BT10			; then loop
	MOV	R1, R14			; copy anyway
	SUBS	R0, R0, #1		; decrement count
	BNE	%BT10			; loop if not finished

	Pull	PC

	LTORG

; *****************************************************************************
;
;	ClockData - Clock a bit of data down the IIC bus
;
; in:	R0 = data bit
;
; out:	All registers preserved, including PSR
;

ClockData ROUT
	Push	"R1, R14"

	MOV	R1, #0			; Clock lo
	BL	SetC1C0

 [ {TRUE}
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling

	MOV	R1,PC
	ORR	R1,R1,#I_bit
	TEQP	PC,R1
 ]
	MOV	R1, #1			; Clock hi
	BL	SetC1C0

; Delay here must be >= 4.0 microsecs

	MOV	R1, #0			; Clock lo
	BL	SetC1C0

	Pull	"R1, PC",,^

ClockData0 ROUT				; Clock a zero bit
	Push	"R0, R14"
	MOV	R0, #0
	BL	ClockData
	Pull	"R0, PC",,^

; *****************************************************************************
;
;	Start - Send the Start signal
;
; out:	All registers preserved, including PSR
;

Start	ROUT
	Push	"R0-R2,R14"

	MOV	R0, #1			; clock HI, data HI
	MOV	R1, #1
	BL	SetC1C0

; Delay here must be >= 4.7 microsecs (1.3 for fast device)

	MOV	R0, #0			; clock HI, data LO
	MOV	R1, #1
	BL	SetC1C0

 [ :LNOT: ClockNVMemoryFast
; Hold start condition for BMU

	MOV	R2, #IOC
        MOV     R0, #10
	BL	DoMicroDelay
 ]

; Delay here must be >= 4.0 microsecs (0.6 for fast device)

	MOV	R0, #0			; clock LO, data LO
	MOV	R1, #0
	BL	SetC1C0

	Pull	"R0-R2,PC",,^

; *****************************************************************************
;
;	Acknowledge - Check acknowledge after transmitting a byte
;
; out:	All registers preserved
;	V=0 => acknowledge received
;	V=1 => no acknowledge received
;

Acknowledge ROUT
	Push	"R0-R2,R14"

	MOV	R0, #1			; clock LO, data HI
	MOV	R1, #0
	BL	SetC1C0

 [ {TRUE}
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling

	MOV	R1,PC
	Push	"R1"
	ORR	R1,R1,#I_bit
	TEQP	PC,R1
 ]
	MOV	R0, #1			; clock HI, data HI
	MOV	R1, #1
	BL	SetC1C0

; Delay here must be >= 4.0 microsecs (0.6 for fast device)

	MOV	R2, #IOC
	LDRB	R2, [R2, #IOCControl]	; get the data from IOC

	MOV	R0, #0
	MOV	R1, #0			; clock lo
	BL	SetC1C0

 [ {TRUE}
	Pull	"R1"
	TEQP	PC,R1
 ]

	TST	R2, #1			; should be LO for correct acknowledge
	MOV	R2, PC
	BICEQ	R2, R2, #V_bit		; clear V if correct acknowledge
	ORRNE	R2, R2, #V_bit		; set V if no acknowledge
	TEQP	R2, #0

	Pull	"R0-R2,PC"

; *****************************************************************************
;
;	Stop - Send the Stop signal
;
; out:	All registers preserved, including PSR
;

Stop	ROUT
	Push	"R0,R1,R14"

	MOV	R0, #0			; clock HI, data LO
	MOV	R1, #1
	BL	SetC1C0

; Delay here must be >= 4.0 microsecs (0.6 for fast device)

	MOV	R0, #1			; clock HI, data HI
	MOV	R1, #1
	BL	SetC1C0

	Pull	"R0,R1,PC",,^

; *****************************************************************************
;
;	TXByte - Transmit a byte
;
; in:	R0 = byte to be transmitted
;
; out:	All registers preserved, including PSR
;

TXByte	ROUT
	Push	"R0-R2,R14"
	MOV	R1, #&80		; 2^7 the bit mask
	MOV	R2, R0			; byte goes into R2
10
	ANDS	R0, R2, R1		; zero if bit is zero
	MOVNE	R0, #1
	BL	ClockData		; send the bit
	MOVS	R1, R1, LSR #1
	BNE	%BT10
	Pull	"R0-R2,PC",,^

; *****************************************************************************
;
;	StartTXPollAck - Transmit start and a byte and poll for acknowledge
;
;	This is intended for devices with a slow internal write cycle which
;	don't ack until the write cycle is finished ( eg ATMEL AT24C01A/x )
;
; in:	R0 = byte to be transmitted
;
; out:	All registers preserved, including PSR
;

StartTXPollAck	ROUT
	Push	"R1,R14"
	MOV	R1, #0
10
	INC	R1
	CMP	R1, #PollMax
	Pull	"R1,pc", EQ

	BL	Start
	BL	TXByte
	BL	Acknowledge
	BVS	%BT10
 [	{FALSE}
	BREG	R1, "i2c tries:"
 ]
	Pull	"R1,pc",,^

; *****************************************************************************
;
;	RXByte - Receive a byte
;
; out:	R0 = byte received
;	All other registers preserved, including PSR
;

RXByte	ROUT
	Push	"R1-R4, R14"
	MOV	R3, #0			; byte:=0
	MOV	R2, #IOC
	MOV	R4, #7

	MOV	R0, #1			; clock LO, data HI
	MOV	R1, #0
	BL	SetC1C0
10
 [ {TRUE}
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling

	MOV	R1,PC
	Push	"R1"
	ORR	R1,R1,#I_bit
	TEQP	PC,R1
 ]
	MOV	R0, #1			; pulse clock HI
	MOV	R1, #1
	BL	SetC1C0

	LDRB	R1, [R2, #IOCControl]	; get the data from IOC
	AND	R1, R1, #1
	ADD	R3, R1, R3, LSL #1	; byte:=byte*2+(IOC?0)AND1

	MOV	R0, #1			; return clock LO
	MOV	R1, #0
	BL	SetC1C0

 [ {TRUE}
	Pull	"R1"
	TEQP	PC,R1
 ]
	SUBS	R4, R4, #1
	BCS	%BT10

	MOV	R0, R3			; return the result in R0
	Pull	"R1-R4, PC",,^

; *****************************************************************************

HTBTA	ROUT
	Push	R14
	BL	HexToBCD
	BL	TXAck
	Pull	PC

TXAck	ROUT
	Push	R14
	BL	TXByte
	BL	Acknowledge
	Pull	PC

CD0RBTH ROUT
	Push	R14
	BL	ClockData0
	BL	RXByte
	BL	BCDToHex
	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
 ]
        Pull    "R1-R4,R14"
        ORRS    PC, LR, #V_bit

        MakeErrorBlock CoreNotWriteable

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
  ]

        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
 [ ChecksumCMOS
	BL	ReadStraight		; calculate new checksum :
	MOV	R4, R0
	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
 [ CacheCMOSRAM
	CMP	r2, #&100		; check small cache limit
	BCS	%FT15
  [ :LNOT: :DEF: TestHarness
	LDR	R1, =CMOSRAMCache	; update cache, but always write to
  |
        ADR     R1, i2cWorkSpace
  ]
	STRB	R3, [R1, R2]		; real hardware as well
15
 ]
 [ E2ROMSupport
	MOV	R0, R2
	BL	GetI2CAddress		; convert to device address + offset
	MOV	R2, R0			; save the offset
 |
	MOV	R1, #RTCAddress
 ]

	MOV	R0, R1			; device address for write
	BL	StartTXPollAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; offset (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; offset (LSB)
	BL	TXAck

	MOV	R0, R3			; data
	BL	TXAck

	BL	Stop
	[ 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
        [ :LNOT: :DEF: TestHarness
        MOV	R14, #0
        LDRB    R4, [R14, #NVRamWriteSize]
        LDRB	R14, [R14, #NVRamSize]
        MOV     R4, R4, LSL #8
        |
        LDRB	R14, NVSize
        MOV     R4, R14
        ]
        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
        BHS     %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
 ]
95
        Pull    "R1-R4,LR"
        ORRS    PC, LR, #V_bit

; *****************************************************************************
;
;       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.
        [ :LNOT: :DEF: TestHarness
        MOV	R14, #0
        LDRB	R14, [R14, #NVRamPageSize]
        |
        LDRB	R14, NVPageSize
        ]
        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
 ]

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

  [ :LNOT: :DEF: TestHarness
        LDR     R14, =CMOSRAMCache
  |
        ADR     R14, i2cWorkSpace
  ]
        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
  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddress
  ]

	MOV	R2, R0			; save the offset
	MOV	R0, R1			; device address for write
	BL	StartTXPollAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; offset (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; offset (LSB)
	BL	TXAck

        Pull    "R0-R2"
        SUB     R5, R3, R0              ; R5 = bytes being written
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        Push    "R0,R2"
20
        LDRB    R0, [R1], #1
        ADD     R6, R6, R0              ; update checksum counter
        BL      TXAck
        SUBS    R5, R5, #1
        BNE     %BT20

	BL	Stop

        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
 ]
        Pull    "R1,R2,R14"
        ORRS    PC, LR, #V_bit

        MakeErrorBlock CoreNotReadable

Read
	Push	"R1,R2,R14"
	BL	MangleCMOSAddress
	MOVCS	R0, #0			; pretend illegal addresses contain 0
	Pull	"R1,R2,PC", CS
10
  [	CacheCMOSRAM
	TEQ	R0, #&40		; is it Econet station number
	BEQ	%FT15	 		; if so then don't use cache
	CMP	R0, #&100		; check small cache limit
  [ :LNOT: :DEF: TestHarness
	LDRCC	R2, =CMOSRAMCache	; if in range
  |
	ADRCC	R2, i2cWorkSpace
  ]
	LDRCCB	R0, [R2, R0]		; read from cache
	Pull	"R1,R2,PC", CC		; and exit
15

; else drop thru into real CMOS reading code
  ]

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

	MOV	R2, R0			; save the offset
	MOV	R0, R1			; device address for write
	BL	StartTXPollAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; offset (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; offset (LSB)
	BL	TXAck

	BL	Start

	ADD	R0, R1, #1		; device address for read
	BL	TXAck

	BL	RXByte			; returned in R0
	MOV	R2, R0			; copy to R2 for now

	MOV	R0, #1
	BL	ClockData

	BL	Stop

	MOV	R0, R2			; return the result

	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
        [ :LNOT: :DEF: TestHarness
        MOV	R14, #0
        LDRB	R14, [R14, #NVRamSize]
        |
        LDRB	R14, NVSize
        ]
        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
 ]
95
        Pull    "R1-R3,LR"
        ORRS    PC, LR, #V_bit

; *****************************************************************************
;
;       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)
  [     CacheCMOSRAM
        TEQ     R0, #0                  ; check it's a cacheable segment
        BEQ     %FT15
        CMP     R0, #&100
        BHS     %FT15

  [ :LNOT: :DEF: TestHarness
        LDR     R14, =CMOSRAMCache
  |
        ADR     R14, i2cWorkSpace
  ]
        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",,^
  ]
15
        Push    "R0-R2"
        ADD     R0, R0, R4              ; R0 = physical address
  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddress
  ]

	MOV	R2, R0			; save the offset
	MOV	R0, R1			; device address for write
	BL	StartTXPollAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; offset (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; offset (LSB)
	BL	TXAck

	BL	Start

	ADD	R0, R1, #1		; device address for read
	BL	TXAck

        Pull    "R0-R2"
        SUB     R5, R3, R0              ; R5 = bytes being read
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        Push    "R0,R2"
20
        BL      RXByte
        STRB    R0, [R1], #1
        SUBS    R5, R5, #1
        MOVNE   R0, #0                  ; not done - ACK byte
	MOVEQ   R0, #1                  ; done - no ACK
	BL	ClockData
        BNE     %BT20

	BL	Stop

	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)

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

  [ :LNOT: :DEF: TestHarness
        LDR     R14, =CMOSRAMCache
  |
        ADR     R14, i2cWorkSpace
  ]
        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 address
  [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
  |
	MOV	R1, #RTCAddress
  ]

	MOV	R2, R0			; save the offset
	MOV	R0, R1			; device address for write
	BL	StartTXPollAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; offset (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; offset (LSB)
	BL	TXAck

	BL	Start

	ADD	R0, R1, #1		; device address for read
	BL	TXAck

        Pull    "R0-R2"
        SUB     R5, R3, R0              ; R5 = bytes being read
        ADD     R0, R0, R5              ; update return R0
        SUB     R2, R2, R5              ; update return R2
        Push    "R0,R2"
20
        BL      RXByte
        ADD     R1, R1, R0
        SUBS    R5, R5, #1
        MOVNE   R0, #0                  ; not done - ACK byte
	MOVEQ   R0, #1                  ; done - no ACK
	BL	ClockData
        BNE     %BT20

	BL	Stop

	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"
	[ :LNOT: :DEF: TestHarness
	MOV	R14, #0			; get no 256 byte blocks and calculate end address
	LDRB	R14, [R14, #NVRamSize]
	|
	LDRB	R14, NVSize
	]
	MOV	R14, R14, LSL #8
	CMP	R0, R14
	Pull	"R14",CS
	ORRCSS	PC, R14, #C_bit		; indicate invalid

; address is < end address -> is valid
	[ :LNOT: :DEF: TestHarness
	MOV	R1, #0
	LDRB	R1, [R1, #RTCFitted]
	|
	LDRB	R1, RTCFlag
	]
	TEQ	R1, #0
	MOVNE	R1, #RTCAddress
	[ :LNOT: :DEF: TestHarness
	LDREQB  R1, [R1, #NVRamBase]
	|
	LDREQB  R1, NVBase
	]

        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
	Pull	"R14"
	BICS	PC, R14, #C_bit		; 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"
	[ :LNOT: :DEF: TestHarness
	MOV	R14, #0			; read no 256 byte blocks and calculate end address
	LDRB	R14, [R14, #NVRamSize]
	|
	LDRB	R14, NVSize
	]
	MOV	R14, R14, LSL #8
	CMP	R0, R14			; if >= end address then
	Pull	"R14"
	ORRCSS	PC, R14, #C_bit		;    invalid

	CMP	R0, #&100		; if < end address && >= &100 then
	BICCSS	PC, R14, #C_bit		;    valid (no mungeing)
 ]

	CMP	R0, #&F0		; if < &100 && >= &f0 then
        [ E2ROMSupport
        SUBCS   R0, R0, #&F0            ;    map &F0->&FF to &00->0F for OTP section
        BICCSS  PC, R14, #C_bit
        |
	ORRCSS	PC, R14, #C_bit		;    invalid
        ]

	ADD	R0, R0, #&40		; now in range &40..&13F
	CMP	R0, #&100
	SUBCS	R0, R0, #(&100-&10)	; now in range &40..&FF, &10..&3F
	BICS	PC, R14, #C_bit		; 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
	[ :LNOT: :DEF: TestHarness
	MOV	R2, #0			; read number of 256 byte blocks and calculate end address
	LDRB	R2, [R2, #NVRamSize]
	|
	LDRB	R2, NVSize
	]
	MOV	R2, R2, LSL #8
        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
	[ :LNOT: :DEF: TestHarness
	MOV	R2, #0
	LDRB	R2, [R2, #NVRamSize]
	|
	LDRB	R2, NVSize
	]
	MOV	R2, R2, LSL #8
        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
;
;	Any of the above, if -1, will not be written to
;

SetTime ROUT
	Push	"R4, R14"		; save registers

	[ :LNOT: :DEF: TestHarness
	MOV	R14, #0
	LDRB	R14, [R14, #RTCFitted]
	|
	LDRB	R14, RTCFlag
	]

        TEQ     R14, #0
        BNE     %FT20

; no RTC - just set soft copy

 [ :LNOT: :DEF: TestHarness
        BL      RegToRealTime
 ]
        Pull    "R4, PC"

20
; write year to CMOS RAM

	MOV	R4, R0			; save hours in R4

	Push	"R1"
	MOVS	R1, R5
	MOVPL	R0, #YearCMOS
	BLPL	Write
	MOVS	R1, R6
	MOVPL	R0, #YearCMOS+1
	BLPL	Write

	CMP	R4, #-1			; are we writing time ?
	Pull	"R1",EQ			; [no, then skip]
	BEQ	%FT30

	MOV	R0, #&01		; start at address 1
    [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
	MOV	R0, R1			; write address
    |
	MOV	R0, #RTCAddress
    ]
	Pull	"R1"
	BL	StartTXPollAck
	MOV	R0, #&01		; offset 1
	BL	TXAck
	MOV	R0, R8			; centiseconds
	BL	HTBTA
	MOV	R0, R7			; seconds
	BL	HTBTA
	MOV	R0, R1			; minutes
	BL	HTBTA
	MOV	R0, R4			; hours
	BL	HTBTA
	BL	Stop

30
	CMP	R2, #-1			; are we writing date ?
	BEQ	%FT40			; [no, then skip]

	MOV	R0, #&05		; start at address 5
    [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
	MOV	R0, R1			; write address
    |
	MOV	R0, #RTCAddress
    ]
	BL	StartTXPollAck
	MOV	R0, #&05		; offset 5
	BL	TXAck
	MOV	R0, R2			; day of month
	BL	HexToBCD
	ORR	R0, R0, R5, LSL #6	; year in bits 6,7; day in bits 0..5
	BL	TXAck
	MOV	R0, R3			; months
	BL	HTBTA
	BL	Stop

40
	MOV	R0, R4			; put hours back in R0
  [ :LNOT: :DEF: TestHarness
	BL	RTCToRealTime
  ]
	Pull	"R4, 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"

	MOV	R0, #&01		; start at address 1
    [	E2ROMSupport
	BL	GetI2CAddress		; convert to device address and offset
	MOV	R0, R1			; write address
    |
	MOV	R0, #RTCAddress
    ]
	BL	StartTXPollAck
	MOV	R0, #&01		; offset 1
	BL	TXAck
	BL	Start
    [	E2ROMSupport
	ADD	R0, R1, #1		; read address
    |
	MOV	R0, #(RTCAddress+1)	; read address
    ]
	BL	TXAck
	BL	RXByte
	BL	BCDToHex
	MOV	R8, R0			; centiseconds
	BL	CD0RBTH
	MOV	R7, R0			; seconds
	BL	CD0RBTH
	MOV	R1, R0			; minutes
	BL	CD0RBTH
	MOV	R4, R0			; hours
	BL	ClockData0
	BL	RXByte
	AND	R0, R0, #&3F		; day of month (clear year bits)
	BL	BCDToHex
	MOV	R2, R0
	BL	ClockData0
	BL	RXByte
	AND	R0, R0, #&1F		; month (clear day of week bits)
	BL	BCDToHex
	MOV	R3, R0
	MOV	R0, #1
	BL	ClockData
	BL	Stop

	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

; Ensure day/month are non-zero, fixes RTCToRealTime
 [ {TRUE}				; LRust, fix RP-0370
	TEQ	R2, #0			; Valid day?
	MOVEQ	R2, #1			; No then force 1st
	TEQ	R3, #0			; Invalid month?
	MOVEQ	R3, #1			; Yes then force Jan
 ]
	Pull	"R4, PC"

; *****************************************************************************
;
;	InitCMOSCache - Initialise cache of CMOS RAM

  [	CacheCMOSRAM

InitCMOSCache	ENTRY "r0-r6"
    [	E2ROMSupport

; Need to set the slowest speed so we can probe

        MOV     R2, #0
      [ ClockNVMemoryFast
        MOV     R3, #10         ; Default speed setting (5�s delays)
	[ :LNOT: :DEF: TestHarness
        STRB    R3, [R2, #NVRamSpeed]
	|
        STRB    R3, NVSpeed
	]
      ]

;	First determine what hardware we've got fitted, R4 holds the number of
;	256 byte blocks that we've found

	; Have we got an RTC ?
	MOV	R0, #RTCAddress
	BL	DummyAccess
	MOVVS	R4, #0
	MOVVC	R4, #1
	[ :LNOT: :DEF: TestHarness
	STRB	R4, [R2, #RTCFitted]
	|
	STRB	R4, RTCFlag
	]
        MOV     R5, #4          ; assume 16 byte page size to start with
        MOV     R6, #0          ; assume not protected

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

; Have we got a 1K E� ?
        MOV     r1, #E2ROMAddress
	MOV	r0, #(E2ROMAddress+6)
	BL	DummyAccess
	MOVVC	R4, #4
	BVC	%FT5

; Have we got 512 bytes of E� ?
	MOV	r0, #(E2ROMAddress+2)
	BL	DummyAccess
	MOVVC	R4, #2
	BVC	%FT5

; Have we got a 16K device ? (Note that this probe would make a 24C08 device respond -
; but if we've gotten this far it would have to be a < 512 byte version; we've never
; used such a device).

        MOV     r1, #E2ROMAddress16K
	MOV	r0, #E2ROMAddress16K
	BL	DummyAccess
	MOVVC	R4, #64
        MOVVC   R5, #6          ; 64 byte page size
      [ ClockNVMemoryFast
        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
      [ IOMD_C_EEPROMProtect <> 0
        MOVVC   R6, #12         ; Only bottom 3K writable
      ]
        MOVVC   R5, #5          ; 32 byte page size
      [ ClockNVMemoryFast
        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
      [ IOMD_C_EEPROMProtect <> 0
        MOVVC   R6, #24         ; Only bottom 6K writable
      ]
        MOVVC   R5, #5          ; 32 byte page size
      [ ClockNVMemoryFast
        MOVVC   R3, #3          ; Fast speed setting (1.5�s delays)
      ]

	MOVVS   R1, #RTCAddress
        MOVVS   R5, #8          ; 256 byte page size for CMOS

5
	; Set the NVRam count
	[ :LNOT: :DEF: TestHarness
	STRB    R1, [R2, #NVRamBase]
	STRB	R4, [R2, #NVRamSize]
        STRB    R5, [R2, #NVRamPageSize]
        TEQ     R6, #0
        MOVEQ   R6, R4
        STRB    R6, [R2, #NVRamWriteSize]
      [ ClockNVMemoryFast
        STRB    R3, [R2, #NVRamSpeed]
      ]
	LDR	R3, =CMOSRAMCache
	|
	STRB    R1, NVBase
	STRB	R4, NVSize
        STRB    R5, NVPageSize
      [ ClockNVMemoryFast
        STRB    R3, NVSpeed
      ]
	ADR	R3, i2cWorkSpace
	]

	; Initialise the cache
        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
    |
	MOV	R1, #RTCAddress
	MOV	R2, #&10
	MOV	R4, #&100		; stop at address &100
        [	:LNOT: :DEF: TestHarness
	LDR	R3, =CMOSRAMCache
        |
	ADR	R3, i2cWorkSpace
        ]
    ]
09
	BL	Start

	MOV	R0, R1			; write address
	BL	TXAck

        TST     R1, #&100               ; 2-byte address?
        MOVNE   R0, R2, LSR #8          ; memory word address (MSB)
        BLNE    TXAck

	AND	R0, R2, #&FF		; memory word address (LSB)
	BL	TXAck

	BL	Start

	ORR	R0, R1, #1		; read address
	BL	TXAck

10
	BL	RXByte			; returned in R0
	STRB	R0, [R3, R2]
	ADD	R2, R2, #1		; increment R2 to phys. address
	CMP	R2, R4			; stop when we hit end address
	BGE	%FT20

	TST	R2, #&FF		; still in same 256 byte block?
	MOVNE	R0, #0			; yes => not done .. ACK that byte
	MOVEQ	R0, #1			; no => finish off reading block
	BL	ClockData
	BNE	%BT10			; yes => continue reading

   [ E2ROMSupport
	BL	Stop
	MOV	R0, R2			; in next 256 byte block so get device + address
	BL	GetI2CAddress
	B	%BT09			; and start next block
   ]
20
	MOV	R0, #1			; finish off reading block
	BL	ClockData
	BL	Stop

   [ E2ROMSupport
        TEQ     R4, #&100               ; have we reached the end?
        EXIT    EQ                      ; exit if so

        MOV     R0, R4                  ; start next block from R4
        MOV     R4, #&100               ; stop at &100
        BL      GetI2CAddress           ; convert to device address and offset
        MOV     R2, R0                  ; save the offset
        B       %BT09                   ; and go round again
   |
	EXIT
   ]

   [ E2ROMSupport
ReadOTPArea Entry
	BL	Start
	MOV	r0, #E2ROMAddress2K_OTP
	BL	TXAck
	MOV	r0, #0				; OTP memory word address
	BL	TXAck
	BL	Start
	MOV	r0, #E2ROMAddress2K_OTP + 1	; switch to read
	BL	TXAck
	MOV	r2, #0
10
	BL	RXByte
	STRB	r0, [r3, r2]
	ADD	r2, r2, #1
	TEQ	r2, #16
	MOVNE	r0, #0				; not done, so clock zero
	MOVEQ	r0, #1
	BL	ClockData
	BNE	%BT10
	BL	Stop
	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

; Have we got an RTC ? do a read from location 0 to find out

  [ E2ROMSupport
DummyAccess	ENTRY

	BL	Start
	BL	TXAck			; do write and set V if no ack
	BL	Stop			; Doesn't change the PSR
	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
 ]
        ORRS    PC, LR, #V_bit

; -----------------------------------------------------------------------------
; 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
  ]
        MOVS    PC, LR

; -----------------------------------------------------------------------------
; OS_NVMemory 1 - read a byte
;
; in:   R0 = 1
;       R1 = location
;
; out:  R2 = value
;
NVMemory_Read
        ENTRY   "R4"
        MOV     R4, PC
        TEQP    PC, #SVC_mode   ; 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
        TEQP    PC, 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"
        MOV     R4, PC
        TEQP    PC, #SVC_mode   ; 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
        TEQP    PC, 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
        TEQP    PC, #SVC_mode   ; 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
        TEQP    PC, 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"
        MOV     R4, PC
        TEQP    PC, #SVC_mode   ; 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
        TEQP    PC, R4          ; restore interrupt state
        EXIT

	END