Ironically, I was going to ask if your initials were B.T. Then I saw it on the board. I met you at the CGE in Dallas.8bitwizard wrote:FYI, I made the Atari 7800 boards which recently got used for the Missing in Action release:
http://atari7800.net/mini-ultra/
http://www.atariage.com/forums/index.ph ... pic=107756
My second cart board
Re: My second cart board
-
- Very interested
- Posts: 159
- Joined: Sat Feb 24, 2007 11:35 pm
- Location: San Antonio, TX
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
How well does your EEPROM mapper tolerates overclocking ?
Mida sa loed ? Nagunii aru ei saa
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 159
- Joined: Sat Feb 24, 2007 11:35 pm
- Location: San Antonio, TX
I have no idea because I have no interest in building an overclock.TmEE co.(TM) wrote:How well does your EEPROM mapper tolerates overclocking ?
It should mostly depend on whether the EEPROM itself will be written too fast. The math I did on the NOP delay cycle counts in the EA code showed that it was running near the maximum speed (about 10us per bit or 100,000bps). But my math may be off. I think it took 1 second to write 2K bytes (about 154 bits per 16 byte page), which adds up to about 40,000bps or half the maximum speed. Maybe inter-page delays and other processing gaps between bits made it take twice as long. Even then, that's only the minimum clock time, and it might work with some overclock.
Er, wait a minute... that 10us (actually 4.7+4.0) is a 2.7 volt spec on the 24C02 data sheet I'm looking at! The 5.5 volt spec is 1.2+0.6us (which I have seen referred to as the 400KHz "fast mode"), so very probably there will be no problem with overclock with most EEPROM chips.
Everything else depends on the speed of the EPROM and GAL, and I wasn't even trying to use the fastest GAL.
And here is the code I was using. This even includes support for using battery RAM in an emulator. (just put battery RAM info in the header where an emulator can see it, and use /TIME on the real hardware)
i2c.h
Code: Select all
; save types
I2C_USE_RAM EQU 0 ; use battery RAM instead of I2C
I2C_7BIT_ADDR EQU 1 ; 7-bit I2C memory address (no device addr)
I2C_BYTE_ADDR EQU 2 ; 1-byte I2C memory address (with device addr)
I2C_WORD_ADDR EQU 3 ; 2-byte I2C memory address (24C32 and larger)
; word (even) address of I2C_port
;I2C_port SET $200000 ; using high ROM area
I2C_port SET $A13000 ; using /TIME
; base address of battery backup RAM
BackupRAM SET $200000
; max number of retries for non-responsive I2C
I2CTOLimit SET 10 ; timeout limit
Code: Select all
; I2C port bit values
SDA_0_SCL_0 EQU $00 ; 00 SDA=0 SCL=0
SDA_0_SCL_1 EQU $40 ; 40 SDA=0 SCL=1
SDA_1_SCL_0 EQU $80 ; 80 SDA=1 SCL=0
SDA_1_SCL_1 EQU $C0 ; C0 SDA=1 SCL=1
;-----------------------------------------------------------------------
; Send I2C start sequence
;-----------------------------------------------------------------------
I2CStart
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_0_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_0_SCL_0,I2C_port
NOP
RTS
;-----------------------------------------------------------------------
; Send I2C stop sequence
;-----------------------------------------------------------------------
I2CStop
MOVE.W #SDA_0_SCL_0,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_0_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
RTS
;-----------------------------------------------------------------------
; Send high bit of D0.B as an I2C data bit
; ENTRY: D0.B = byte to send
; EXIT: D0.B = input value shifed left one bit
; D1 not saved
;-----------------------------------------------------------------------
I2CSendBit
LSL.B #1,D0
ROXR.B #1,D1
BCLR #6,D1 ; SCL = 0
MOVE.W D1,I2C_port
NOP
NOP
NOP
NOP
BSET #6,D1 ; SCL = 1
MOVE.W D1,I2C_port
NOP
NOP
NOP
NOP
BCLR #6,D1 ; SCL = 0
MOVE.W D1,I2C_port
RTS
;-----------------------------------------------------------------------
; Clock in low bit of D0.B as an I2C data bit
; ENTRY: D0.B = existing data byte
; EXIT: D0.W = input value shifted left one bit plus new bit
; D1 not saved
;-----------------------------------------------------------------------
I2CReadBit
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W I2C_port,D1 ; read SDA
MOVE.W #SDA_1_SCL_0,I2C_port
ASL.B #1,D1
ROXL.B #1,D0
RTS
;-----------------------------------------------------------------------
; Send a zero bit as an I2C acknowledge (ACK)
;-----------------------------------------------------------------------
I2CSendAck
MOVE.W #SDA_0_SCL_0,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_0_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_0_SCL_0,I2C_port
NOP
RTS
;-----------------------------------------------------------------------
; Send a one bit as an I2C negative acknowledge (NAK)
;-----------------------------------------------------------------------
I2CSendNak
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
RTS
;-----------------------------------------------------------------------
; Read an I2C bit into D1.bit7 for I2C ACK/NAK acknowledge input
; ENTRY: none
; EXIT: D1.bit7 = I2C data bit (0=ACK 1=NAK)
; N flag = D1.bit7 (BPL=ACK BMI=NAK)
;-----------------------------------------------------------------------
I2CReadAck
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
MOVE.W #SDA_1_SCL_1,I2C_port
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOVE.W I2C_port,D1 ; read SDA
MOVE.W #SDA_1_SCL_0,I2C_port
NOP
TST.B D1
RTS
;-----------------------------------------------------------------------
; Reset I2C bus by sending nine I2C starts and an I2C stop
;
; This will ensure that the EEPROM is not in the middle of an
; incomplete operation by forcing enough stop sequences to
; abort an in-progress byte transfer.
;-----------------------------------------------------------------------
I2CReset
MOVE.W D0,-(A7)
MOVE.W #6500,D0 ; delay a while
.10 NOP
DBRA D0,.10
MOVE.W #9-1,D0 ; do nine I2C starts
.20 MOVE.W #SDA_1_SCL_0,I2C_port ; get SDA ready for next I2C start
NOP
NOP
NOP
NOP
NOP
NOP
BSR.W I2CStart ; send I2C start
DBRA D0,.20
BSR.W I2CStop ; send I2C stop
MOVE.W (A7)+,D0
RTS
;-----------------------------------------------------------------------
; Send 8 bits to I2C, MSB first
; ENTRY: D0.B = data byte to send
; EXIT: D0, D1, D2 not saved
;-----------------------------------------------------------------------
I2CSendByte
MOVEQ #8-1,D2
.10 BSR.W I2CSendBit ; clock out high bit of D0
DBRA D2,.10
RTS
;-----------------------------------------------------------------------
; Read 8 bits from I2C, MSB first
; ENTRY: none
; EXIT: D0.L = data byte with high bits cleared
; D1, D2 not saved
;-----------------------------------------------------------------------
I2CReadByte
MOVEQ #8-1,D2
MOVEQ #0,D0 ; clear high bits of D0
.10 BSR.W I2CReadBit ; clock in low bit of D0
DBRA D2,.10
RTS
;-----------------------------------------------------------------------
; Send an I2C byte and wait for acknowledge
; ENTRY: D0.B = data byte to send
; EXIT: D0, D1, D2 not saved
; N flag set (BMI) if NAK or timeout
;-----------------------------------------------------------------------
I2CSendAckByte
BSR I2CSendByte
BRA I2CReadAck
;-----------------------------------------------------------------------
; Send an I2C memory write address
; ENTRY: D0.B = I2C address (usually $A0, can be any even number $A0-$AE)
; D1.W = memory address
; D4.B = I2C type
; D7.W = I2C timeout counter
; EXIT: Z flag clear (BNE) if failure
; If no failure, I2C bus is ready for writes
; or send a START to begin reads
;-----------------------------------------------------------------------
I2CWriteAddress
MOVEM.L D0-D2,-(A7)
.10 BSR I2CStart ; send I2C start
MOVE.B D4,D1 ; get I2C device type
BEQ.S .98 ; error if I2C_USE_RAM
SUBQ.B #I2C_7BIT_ADDR,D1
BNE.B .20 ; branch if not I2C_7BIT_ADDR
; === send 7-bit address ===
MOVE.B 7(A7),D0 ; get EEPROM memory address LSB
ASL.B #1,D0 ; shift memory address left
; BCLR #0,D0 ; set write flag in device address
BSR I2CSendAckByte ; send memory address
BMI.S .90 ; branch if error
BRA.S .50
; === send byte/word address ===
.20 MOVE.W 2(A7),D0 ; get I2C address
BCLR #0,D0 ; set write flag in device address
SUBQ.B #I2C_WORD_ADDR - I2C_7BIT_ADDR,D1
BEQ.B .30 ; branch if I2C_WORD_ADDR
; set up byte address
MOVE.B 6(A7),D2 ; D2 = page bits
LSL.B #1,D2 ; shift past R/W flag
ANDI.B #$0E,D2 ; mask non-address bits
OR.B D2,D0 ; merge in page bits
BRA.S .40
; set up word address
.30 BSR I2CSendAckByte ; send device address and R/W flag
BMI.S .90
MOVE.B 6(A7),D0 ; get EEPROM memory address MSB
.40 BSR I2CSendAckByte ; send MSB (word) or device address and R/W flag (byte)
BMI.S .90 ; branch if error
MOVE.B 7(A7),D0 ; get EEPROM memory address LSB
BSR I2CSendAckByte ; send LSB
BMI.S .90 ; branch if error
.50 MOVEQ #0,D0 ; return success
BRA.S .99 ; (now start writing data, or send START)
.90 ADDQ.W #1,D7 ; increment timeout counter
BMI.S .98 ; branch if timeout overflow
CMPI.W #I2CTOLimit,D7 ; compare against timeout limit
BPL.S .98 ; branch if timeout overflow
BSR I2CReset ; reset I2C bus
BRA.S .10 ; and try again
.98 MOVEQ #-1,D0 ; return failure
.99 MOVEM.L (A7)+,D0-D2
RTS
;-----------------------------------------------------------------------
; Write one byte to an I2C EEPROM
; ENTRY: D0.B = I2C address
; D1.W = EEPROM memory address
; D7.W = I2C timeout counter
; A0.L = pointer to data byte
; EXIT: Z flag clear (BNE) if failure
; D1.W incremented
; A0.L incremented
;-----------------------------------------------------------------------
I2CWriteByte
MOVEM.L D0-D2,-(A7)
.10 BSR I2CWriteAddress ; send device/memory address
BNE.S .98 ; fail if too many errors
MOVE.B (A0),D0 ; get next data byte
BSR I2CSendAckByte
BMI.S .90
ADDQ.W #1,A0 ; increment buffer address
ADDQ.W #1,6(A7) ; increment memory address
BSR I2CStop ; send STOP to end transfer
MOVEQ #0,D0 ; return success
BRA.S .99
.90 ADDQ.W #1,D7 ; increment timeout counter
BMI.S .98 ; branch if timeout overflow
CMPI.W #I2CTOLimit,D7 ; compare against timeout limit
BPL.S .98 ; branch if timeout overflow
BSR I2CReset ; reset I2C bus
BRA.S .10 ; and try again
.98 MOVEQ #-1,D0 ; return failure
.99 MOVEM.L (A7)+,D0-D2
RTS
;-----------------------------------------------------------------------
; Write a block of memory to an I2C EEPROM
; ENTRY: D0.B = I2C Address
; D1.W = EEPROM memory address
; D2.W = number of bytes to write ($0000 = 65536 bytes)
; D4.B = I2C type
; A0.L = pointer to data buffer
; EXIT: Z flag clear (BNE) if failure
;-----------------------------------------------------------------------
IF 0 ; use I2CWritePages instead!
I2CWriteBlock
MOVEM.L D0-D3/D7/A0,-(A7)
MOVE.W D2,D3 ; D3 = counter
.10 CLR.W D7 ; reset timeout counter before each byte
BSR I2CWriteByte ; write next byte
BNE.S .99 ; fail if too many errors
SUBQ.W #1,D3 ; decrement counter
BNE.S .10 ; loop to write next byte
MOVEQ #0,D0 ; return success
.99 MOVEM.L (A7)+,D0-D3/D7/A0
RTS
ENDIF
;-----------------------------------------------------------------------
; Send an I2C memory read address
; ENTRY: D0.B = I2C address (usually $A0, can be any even number $A0-$AE)
; D1.W = memory address
; D4.B = I2C type
; D7.W = I2C timeout counter
; EXIT: Z flag clear (BNE) if failure
; If no failure, I2C bus is ready for writes
; or send a START to begin reads
;-----------------------------------------------------------------------
I2CReadAddress
MOVEM.L D0-D2,-(A7)
.10 MOVE.B D4,D1 ; get I2C device type
BEQ.S .98 ; error if I2C_USE_RAM
SUBQ.B #I2C_7BIT_ADDR,D1
BNE.B .20 ; branch if not I2C_7BIT_ADDR
; === send 7-bit address ===
BSR I2CStart ; send I2C start
MOVE.B 7(A7),D0 ; get EEPROM memory address LSB
ASL.B #1,D0 ; shift memory address left
BSET #0,D0 ; set read flag in device address
BSR I2CSendAckByte ; send memory address
BMI.S .90 ; branch if error
BRA.S .50
; === send byte/word address ===
.20 MOVE.W 6(A7),D1 ; get memory address
BSR I2CWriteAddress ; send device/memory address
BNE.S .98 ; fail if too many errors
BSR I2CStart ; send START to end write mode
MOVE.W 2(A7),D0 ; get device address
BSET #0,D0 ; set read flag in device address
MOVE.B D4,D1 ; get I2C device type
SUBQ.B #I2C_WORD_ADDR,D1
BEQ.B .30 ; branch if I2C_WORD_ADDR
; set up high bits of byte address
MOVE.B 6(A7),D2 ; D2 = page bits
LSL.B #1,D2 ; shift past R/W flag
ANDI.B #$0E,D2 ; mask non-address bits
OR.B D2,D0 ; merge in page bits
.30 BSR I2CSendAckByte ; send device address and R/W flag
BMI.S .90
.50 MOVEQ #0,D0 ; return success
BPL.S .99
.90 ADDQ.W #1,D7 ; increment timeout counter
BMI.S .98 ; branch if timeout overflow
CMPI.W #I2CTOLimit,D7 ; compare against timeout limit
BPL.S .98 ; branch if timeout overflow
BSR I2CReset ; reset I2C bus
BRA.S .10 ; and try again
.98 MOVEQ #-1,D0 ; return failure
.99 MOVEM.L (A7)+,D0-D2
RTS
;-----------------------------------------------------------------------
; Read a block of data from an I2C EEPROM
; ENTRY: D0.B = I2C Address
; D1.W = EEPROM memory address
; D2.W = number of bytes to read ($0000 = 65536 bytes)
; D4.B = I2C type
; A0.L = pointer to data buffer
; EXIT: Z flag clear (BNE) if failure
;-----------------------------------------------------------------------
I2CReadBlock
MOVEM.L D0-D3/D7/A0-A1,-(A7)
CLR.W D7 ; reset timeout counter
TST.B D4
BEQ.S .50 ; branch if I2C_USE_RAM
; === read block from I2C EEPROM ===
.10 MOVE.W 2(A7),D0 ; D0 = I2C address
MOVE.W 6(A7),D1 ; D1 = EEPROM memory address
MOVE.W 10(A7),D3 ; D3 = counter
MOVE.L 20(A7),A0 ; A0 = buffer address
BSR I2CReadAddress ; send device/memory address
BNE.S .98 ; fail if too many errors
MOVE.W 10(A7),D3 ; get byte count
.20 BSR I2CReadByte ; read next byte
MOVE.B D0,(A0)+ ; store byte in buffer
SUBQ.W #1,D3 ; decrement counter
BEQ.B .30 ; branch if last byte
BSR I2CSendAck ; send ACK to request another byte
BRA.B .20 ; loop for the rest of the data
.30 BSR I2CSendNak ; send NAK to end sequential read
BSR I2CStop ; send STOP to end transfer
MOVEQ #0,D0 ; return success
BRA.S .99
; === read block from backup RAM ===
.50 LEA BackupRAM,A1 ; get base address of backup RAM
ADDA.W D1,A1 ; add offset into backup RAM
ADDA.W D1,A1 ; add offset into backup RAM
.60 MOVE.W (A1)+,D0 ; read next word from memory (data in low byte)
MOVE.B D0,(A0)+ ; write byte to buffer
SUBQ.W #1,D2 ; count the byte
BNE.B .60 ; loop until done
BRA.S .99 ; exit with Z-flag set (success)
.90 ADDQ.W #1,D7 ; increment timeout counter
BMI.S .98 ; branch if timeout overflow
CMPI.W #I2CTOLimit,D7 ; compare against timeout limit
BPL.S .98 ; branch if timeout overflow
BSR I2CReset ; reset I2C bus
BRA.S .10 ; and try again
.98 MOVEQ #-1,D0 ; return failure
.99 MOVEM.L (A7)+,D0-D3/D7/A0-A1
RTS
;-----------------------------------------------------------------------
; Write a block of memory to an I2C EEPROM using page mode
; ENTRY: D0.B = I2C Address
; D1.W = EEPROM memory address
; D2.W = number of bytes to write ($0000 = 65536 bytes)
; D4.B = I2C type
; A0.L = pointer to data buffer
; EXIT: Z flag clear (BNE) if failure
;
; NOTE: * EEPROM memory address must be page-aligned
; * page size depends on the EEPROM size!
;-----------------------------------------------------------------------
I2CWritePage
MOVEM.L D0-D3/D7/A0-A1,-(A7)
CLR.W D7 ; reset timeout counter
TST.B D4
BEQ.S .50 ; branch if I2C_USE_RAM
; === write block to I2C EEPROM ===
.10 MOVE.W 2(A7),D0 ; D0 = I2C address
MOVE.W 6(A7),D1 ; D1 = EEPROM memory address
MOVE.W 10(A7),D3 ; D3 = counter
MOVE.L 20(A7),A0 ; A0 = buffer address
BSR I2CWriteAddress ; send device/memory address
BNE.B .98 ; fail if too many errors
.20 MOVE.B (A0)+,D0 ; get next data byte
BSR I2CSendAckByte
BMI.S .90
SUBQ.W #1,D3 ; decrement counter
BNE.S .20 ; loop to write next byte
BSR I2CStop ; send STOP to end transfer
MOVEQ #0,D0 ; return success
BRA.S .99
; === write block to backup RAM ===
.50 LEA BackupRAM,A1 ; get base address of backup RAM
ADDA.W D1,A1 ; add offset into backup RAM
ADDA.W D1,A1 ; add offset into backup RAM
MOVEQ #0,D0 ; clear high bits of D0
.60 MOVE.B (A0)+,D0 ; read byte from buffer
MOVE.W D0,(A1)+ ; write byte to backup RAM (using low byte only)
SUBQ.W #1,D2 ; count the byte
BNE.B .60 ; loop until done
BRA.S .99 ; exit with Z-flag set (success)
.90 ADDQ.W #1,D7 ; increment timeout counter
BMI.S .98 ; branch if timeout overflow
CMPI.W #I2CTOLimit,D7 ; compare against timeout limit
BPL.S .98 ; branch if timeout overflow
BSR I2CReset ; reset I2C bus
BRA.S .10 ; and try again
.98 MOVEQ #-1,D0 ; return failure
.99 MOVEM.L (A7)+,D0-D3/D7/A0-A1
RTS
;-----------------------------------------------------------------------
; Write multiple pages of memory to an I2C EEPROM
; ENTRY: D0.B = I2C address
; D1.W = EEPROM memory address
; D2.W = number of bytes to write
; D3.W = number of bytes per page
; D4.B = I2C type
; EXIT: Z flag clear (BNE) if failure
;
; NOTE: * D1 must be aligned to the requested page size in D3!
; * D2 must be a multiple of D3!
; * EEPROM chip's actual page size must be a power-of-two multiple
; of the requested page size in D3.
; (ex: actual page size of 64 allows D3 = 1,2,4,8,16,32,64)
; * Check the chip data sheet to determine page size and ROM type!
;-----------------------------------------------------------------------
I2CWritePages
MOVEM.L D0-D3/A0,-(A7)
EXG D2,D3 ; D3 = counter, D2 = bytes per page
.10 BSR I2CWritePage ; write next page
BNE.S .99
ADDA.W D2,A0 ; A0 = buffer address of next page
ADD.W D2,D1 ; D0 = EEPROM address of next page
SUB.W D2,D3 ; subtract page size from counter
BHI.S .10 ; loop if no carry and not zero
MOVEQ #0,D0 ; return success
.99 MOVEM.L (A7)+,D0-D3/A0
RTS
;-----------------------------------------------------------------------
; Attempt to detect memory type
; ENTRY: D0.B = I2C address
; EXIT: D4.B = I2C type guess
;
; NOTE: * This first tries reading the first odd byte of battery RAM,
; complementing it, then writing it back. If the complemented
; value does not read back, lack of battery RAM is assumed.
; Then the test is tried again with the original value from
; battery RAM.
; * If battery RAM is not found, an attempt is made to read the
; first byte of a 7-bit I2C EEPROM. If the EEPROM is not using
; 7-bit addressing, this should fail due to the lack of a valid
; device address.
; * If a 7-bit I2C EEPROM is not found, byte addressing is assumed.
; * Word addressing is not tested to avoid accidental writes
; to a byte-addressed EEPROM chip.
; * Word addressing can not be detected because word-addressed
; I2C EEPROMs will not return an error in a byte-addressed
; mode. The game will have to manually change the I2C type
; to word addressing if that was expected.
; This is not a trivial problem; see US Pat. #6334165.
;-----------------------------------------------------------------------
I2CGetMemType
MOVEM.L D0-D3/A0,-(A7)
; first check for real RAM
MOVEQ #I2C_USE_RAM,D4
LEA BackupRAM+1,A0 ; get base odd address of backup RAM
MOVE.B (A0),D0 ; get first byte of RAM
NOT.B D0
MOVE.B D0,(A0) ; store complement into RAM
CMP.B (A0),D0 ; check if complement was stored
BEQ.B .10
MOVEQ #I2C_7BIT_ADDR,D4 ; if not, it must be I2C
.10 NOT.B D0
MOVE.B D0,(A0) ; put back original value just in case
CMP.B (A0),D0 ; verify original value was stored
BEQ.B .20
MOVEQ #I2C_7BIT_ADDR,D4 ; if not, it must be I2C
.20
TST.B D4 ; exit if I2C_USE_RAM
BEQ.S .99
; now check for 7-bit EEPROM
CLR.W -(A7) ; create temporary 2-byte buffer
MOVE.L A7,A0
; MOVE.W 2+2(A7),D0 ; no device address for 7-bit
MOVEQ #0,D1 ; start EEPROM memory address
MOVEQ #1,D2 ; byte count
; MOVE.B I2C_Type,D4 ; I2C ROM type
BSR I2CReadBlock ; read a block of data from I2C
IF 0
BEQ.S .98 ; exit if 7-bit address
; now check for 8-bit EEPROM
MOVE.W 2+2(A7),D0 ; no device address for 7-bit
MOVEQ #0,D1 ; start EEPROM memory address
MOVEQ #1,D2 ; byte count
MOVEQ #I2C_BYTE_ADDR,D4 ; try byte address
BSR I2CReadBlock ; read a block of data from I2C
BEQ.S .98 ; exit if byte address
MOVEQ #I2C_WORD_ADDR,D4 ; assume word address
ELSE
MOVEQ #I2C_BYTE_ADDR,D4 ; try byte address
ENDIF
.98 ADDQ.L #2,A7 ; delete temporary buffer
.99 MOVEM.L (A7)+,D0-D3/A0
RTS
Code: Select all
i2c.h and i2c.asm are routines to access an I2C EEPROM for save memory.
Include "i2c.h" early in your code, and "i2c.asm" elsewhere in your code.
The subroutines to use are:
I2CReset reset I2C bus by sending nine starts and a stop
I2CReadBlock read a block of data from an I2C EEPROM
I2CWritePages write pages of data to an I2C EEPROM
I2CGetMemType attempt to detect the save memory type
There are other routines, but their direct use is not recommended
unless you have something strange (like a non-EEPROM I2C chip) and
know what you're doing.
=== I2C memory types ===
There are three ways to send a memory address to an I2C EEPROM.
* Send a 7-bit memory address with no device address (some 24C01)
* Send a device address, and an 8-bit memory address, with up to three more
memory address bits sent in the device address (24C01 through 24C16)
* Send a device address, and a 16-bit memory address (24C32 and larger)
Use of a backup battery RAM chip is also supported, to allow use in
emulators that do not properly handle an I2C EEPROM backup memory. The
memory info at $01B0 in the cartridge header should refer to battery RAM
so that the emulator will know to use it. Note that only the low (odd)
address of the backup memory area is used. To use this dual-save support,
the game must identify RAM vs EEPROM.
I2C_USE_RAM use battery RAM instead of I2C
I2C_7BIT_ADDR 7-bit I2C memory address (no device addr)
I2C_BYTE_ADDR 1-byte I2C memory address (with device addr)
I2C_WORD_ADDR 2-byte I2C memory address (24C32 and larger)
NOTE: the code uses the symbol BackupRAM to find backup RAM memory.
Simply changing the $01B0 block in the cartridge header will not
change this!
=== Cartridge header ===
The backup memory type at $01B0 in the cartridge header needs to be set
to allow an emulator to know what kind of backup memory is being used.
However there is apparently no way to tell the emulator which of the three
types of I2C addressing are being used.
Backup memory info for I2C serial EEPROM:
DC.B "RA"
DC.W $E840
DC.L I2C_Port
DC.L I2C_Port+1
Backup memory info for battery RAM:
DC.B "RA"
DC.W $F820
DC.L BackupRAM+1
DC.L BackupRAM+$8000*2-1 ; $2000 = 8K, $8000 = 32K
=== Configurable parameters ===
After including "i2c.h", certain parameters can be overriden by using
the SET directive:
I2C_port $A13000 normally set for /TIME; some carts may use $200000
BackupRAM $200000 address of battery backup RAM; change to an
address in work ram for debugging
I2CTOLimit 10 max number of retries for non-responsive I2C
=== I2CReset ===
Reset I2C bus by sending nine I2C starts and an I2C stop
ENTRY: NONE
EXIT: NONE
NOTE: This will ensure that the EEPROM is not in the middle of an
incomplete operation by forcing enough start sequences to
abort an in-progress byte transfer.
EXAMPLE:
BSR I2CReset ; reset I2C bus
=== I2CReadBlock ===
Read a block of data from an I2C EEPROM
ENTRY: D0.B = I2C Address
D1.W = EEPROM memory address
D2.W = number of bytes to read ($0000 = 65536 bytes)
D4.B = I2C type
A0.L = pointer to data buffer
EXIT: Z flag clear (BNE) if failure
EXAMPLE:
RomSz EQU 512 ; I2C EEPROM size used
PageSz EQU 16 ; I2C EEPROM page size (chip-dependent)
I2C_RomAddr EQU $A0 ; I2C EEPROM device address
I2C_Type EQU I2C_BYTE_ADDR
BSR I2CReset ; reset I2C bus
LEA Buffer,A0 ; data buffer
MOVE.W #I2C_RomAddr,D0 ; device address = first EEPROM
MOVEQ #0,D1 ; start EEPROM memory address
MOVE.L #RomSz,D2 ; byte count
MOVE.B #I2C_Type,D4 ; I2C ROM type
BSR I2CReadBlock ; read a block of data from I2C
=== I2CWritePages ===
Write multiple pages of memory to an I2C EEPROM
ENTRY: D0.B = I2C address
D1.W = EEPROM memory address
D2.W = number of bytes to write
D3.W = number of bytes per page
D4.B = I2C type
EXIT: Z flag clear (BNE) if failure
NOTE: * D1 must be aligned to the requested page size in D3!
* D2 must be a multiple of D3!
* EEPROM chip's actual page size must be a power-of-two multiple
of the requested page size in D3.
(ex: actual page size of 64 allows D3 = 1,2,4,8,16,32,64)
* Check the chip data sheet to determine page size and ROM type!
EXAMPLE:
RomSz EQU 512 ; I2C EEPROM size used
PageSz EQU 16 ; I2C EEPROM page size (chip-dependent)
I2C_RomAddr EQU $A0 ; I2C EEPROM device address
I2C_Type EQU I2C_BYTE_ADDR
BSR I2CReset ; reset I2C bus
LEA Buffer,A0 ; data buffer
MOVE.W #I2C_RomAddr,D0 ; device address = first EEPROM
MOVEQ #0,D1 ; start EEPROM memory address
MOVE.L #RomSz,D2 ; byte count
MOVE.W #PageSz,D3 ; page size
MOVE.B #I2C_Type,D4 ; I2C ROM type
BSR I2CWritePages ; read a block of data from I2C
=== I2CGetMemType ===
Attempt to detect memory type
ENTRY: D0.B = I2C address
EXIT: D4.B = I2C type guess
NOTE: * This first tries reading the first odd byte of battery RAM,
complementing it, then writing it back. If the complemented
value does not read back, lack of battery RAM is assumed.
Then the test is tried again with the original value from
battery RAM.
* If battery RAM is not found, an attempt is made to read the
first byte of a 7-bit I2C EEPROM. If the EEPROM is not using
7-bit addressing, this should fail due to the lack of a valid
device address.
* If a 7-bit I2C EEPROM is not found, byte addressing is assumed.
* Word addressing is not tested to avoid accidental writes
to a byte-addressed EEPROM chip.
* Word addressing can not be detected because word-addressed
I2C EEPROMs will not return an error in a byte-addressed
mode. The game will have to manually change the I2C type
to word addressing if that was expected.
This is not a trivial problem; see US Pat. #6334165.
-
- Very interested
- Posts: 2440
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
I2C code seems pretty slow compared to my MicroWire code, but 2K within 1 second is really fast. Write operations on MW usually take 4ms... I need to wait around quarter of a second to get my EEPROM formatted (load default settings, high scores, etc), and I write only ~100WORDs.
Mida sa loed ? Nagunii aru ei saa
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
-
- Very interested
- Posts: 159
- Joined: Sat Feb 24, 2007 11:35 pm
- Location: San Antonio, TX
I finally built the second of the four boards. As expected, my stupid "ground plane everywhere" noob mistake gave lots of opportunites for solder bridges to short things to ground, and lots of work tracing down the shorts. This time I used a TSOP surface-mount 24C02 which I took out of a broken DVD player. (the DVD player also had a 27C801 8-megabit DIP EPROM in it!)
So I finally started up Eagle and fixed the board design to keep the ground plane away from anything I had to solder.
I guess the next one I should try to build with a TSSOP 24C02 from an old junk 16 or 32 megabyte PC-100 DIMM.
So I finally started up Eagle and fixed the board design to keep the ground plane away from anything I had to solder.
I guess the next one I should try to build with a TSSOP 24C02 from an old junk 16 or 32 megabyte PC-100 DIMM.