My second cart board

For hardware talk only (please avoid ROM dumper stuff)
cdoty
Very interested
Posts: 117
Joined: Wed Nov 29, 2006 2:54 pm
Location: Houston, TX
Contact:

Re: My second cart board

Post by cdoty » Wed Jun 06, 2007 3:16 pm

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
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.

KanedaFr
Administrateur
Posts: 1139
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr » Wed Jun 13, 2007 10:43 am

I have 4 or 5 differents blank PCB from Accolade, Sega, etc...

Do you want a scan of them ?

8bitwizard
Very interested
Posts: 159
Joined: Sat Feb 24, 2007 11:35 pm
Location: San Antonio, TX

Post by 8bitwizard » Thu Jun 14, 2007 1:43 am

KanedaFr wrote:I have 4 or 5 differents blank PCB from Accolade, Sega, etc...

Do you want a scan of them ?
Nope, I've already got the ones that matter. I desoldered them clean and made a full mock-up in Eagle to confirm that I got the schematic right.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Sun Jun 17, 2007 3:26 pm

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

8bitwizard
Very interested
Posts: 159
Joined: Sat Feb 24, 2007 11:35 pm
Location: San Antonio, TX

Post by 8bitwizard » Sun Jun 17, 2007 10:24 pm

TmEE co.(TM) wrote:How well does your EEPROM mapper tolerates overclocking ?
I have no idea because I have no interest in building an overclock.

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
i2c.asm

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
i2c.txt

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.
(thank you IBB for messing up the tabs)

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Mon Jun 18, 2007 9:19 pm

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

8bitwizard
Very interested
Posts: 159
Joined: Sat Feb 24, 2007 11:35 pm
Location: San Antonio, TX

Post by 8bitwizard » Sat Jul 14, 2007 2:53 am

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.

cdoty
Very interested
Posts: 117
Joined: Wed Nov 29, 2006 2:54 pm
Location: Houston, TX
Contact:

Post by cdoty » Thu Sep 20, 2007 5:19 am

8bitwizard wrote:And 65816 is semi-functional, in that it doesn't do some of the addressing mode force characters in the chip manual.
Is there a way to tell the assembler to use 8/16 bit values in 65816 mode?

ca65 uses .I8/.I16 and .A8/.A16, in addition to the REP/SEP instructions.

Post Reply