Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

IIGS battery ram format, reading, writing?

254 views
Skip to first unread message

Jayson Smith

unread,
Feb 4, 2013, 8:11:19 PM2/4/13
to
Hi,

Are there any reference texts, preferably in .txt or other easy-to-read
format, that describe how to read and write the IIGS battery ram, and the
format of the data?
Thanks.


gid...@sasktel.net

unread,
Feb 4, 2013, 8:43:07 PM2/4/13
to
There is a good file on Asimov called BATRAMMER that allows you to read and write to the battery RAM and has a description of what most of the storage locations are for.

Rob

Jayson Smith

unread,
Feb 5, 2013, 4:26:47 AM2/5/13
to
Hi,

Thanks. Batrammer gave me a lot of what I want. But I'd still love to know
the meanings of the various positions in the battery ram. There isn't any
info about that in the Batrammer archive. Surely this is documented
somewhere?

Also, it seems like the clock is not stored within the 256 bytes of battery
ram. Is this correct, or does the GS internally mask the bytes used by the
clock so a careless programmer can't mess with them?
Jayson

<gid...@sasktel.net> wrote in message
news:aa4a14db-4df7-435e...@googlegroups.com...

gid...@sasktel.net

unread,
Feb 5, 2013, 4:26:03 PM2/5/13
to
Here is some notes I came across for all who want to copy and paste this into your documents.

//gs Battery RAM and Clock/Calendar

There are 256 bytes of RAM inside the clock chip in the Apple //gs. These bytes are backed up by the same battery that keeps the clock ticking when you turn off your Apple. You can read and write the battery RAM locations, but not the same as regular RAM. You can either do it the hard way, by direct hardware, or you can do it through the built-in firmware.
First, the easy way. When you turn on your //gs, the power-up routines install a lot of stuff in RAM in banks $E0 and $E1. At the beginning of $E1 there are a lot of JMP opcodes, with long (24-bit) addresses. The one at $E10000 is a jump to the Tool Locater. The Tool Locater is simply a way to access a lot of firmware subroutines without knowing their actual addresses. Instead of calling a firmware subroutine directly, you load up a subroutine number in a register and call the single known address, $E10000.
To keep things organized, the //gs firmware designers require you to call $E10000 with the 65816 in Native mode, with a JSL $E10000. Any parameters the subroutines need must be pushed onto the stack before the JSL, and any results will be on the stack when the subroutine is finished. The carry status will indicate whether the subroutine returned an error code or not, just as in ProDOS MLI. If carry is clear, there was no error; if carry is set, there was an error and the error code is in the A-register. Regardless of the setting of the m- and x-status bits when you call $E10000, it will return with both of them zero (full 16-bit mode).
You tell the Tool Locater which "tool" to call by a code number in the X-register. This is a 16-bit value, so you must have 16-bit mode on for the X-register when you call $E10000 (x-status bit=0). It doesn't matter whether m-status is 0 or 1. The tool code is made up of a tool set number (00-FF, in the low byte) and a tool number (00-FF, in the high byte). The tool code to read all 256 bytes of battery RAM is $0A03; to write 256 bytes out to battery RAM, the tool code is $0903. The following program will read battery RAM:

1000 *SAVE S.BATTERY.RAM
1010 *--------------------------------
1020 .OP 65816
1030 *--------------------------------
1040 R CLC
1050 XCE
1060 REP #$30
1070 *--------------------------------
1080 PEA BUF/256/256
1090 PEA BUF
1100 LDX ##$0A03 READ BATTERY RAM
1110 JSR $E10000
1120 *--------------------------------
1130 SEC
1140 XCE
1150 RTS
1160 *--------------------------------
1170 W CLC
1180 XCE
1190 REP #$30
1200 *--------------------------------
1210 PEA BUF/256/256
1220 PEA BUF
1230 LDX ##$0903 WRITE BATTERY RAM
1240 JSR $E10000
1250 *--------------------------------
1260 SEC
1270 XCE
1280 RTS
1290 *--------------------------------
1300 BUF .EQ $900
1310 *--------------------------------

When I did this on my //gs prototype, this is what I got:
0900-00 00 00 01 00 00 0d 06 02 01 01 00 01 00 00 00-................
0910-00 00 07 06 02 01 01 00 00 00 00 0F 07 00 08 0B-................
0920-01 01 00 00 00 00 01 01 05 00 00 00 03 02 02 02-................
0930-00 00 00 00 00 00 00 0C 08 00 01 02 03 04 05 06-................
0940-07 0A 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D-................
0950-0E 0F FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
0960-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
0970-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
0980-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
0990-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
09A0-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
09B0-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
09C0-C2 CF C2 A0 D3 C1 CE C4 C5 D2 AD C3 C5 C4 C5 D2-BOB SANDER-CEDER
09D0-CC CF C6 A0 FF FF FF FF FF FF FF FF FF FF FF FF-LOF ............
09E0-FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF-................
09F0-FF FF FF FF FF FF FF FF FF FF FF FF 27 CE 8D 64-............'N.d

Those last four bytes are some kind of a check sum, handled automatically by the tool. I suppose that if the checksum is incorrect on power up, you will be popped into the configurator instead of going into a boot. You can read these bytes, but you cannot write them with the tool: the tool will calculate a checksum and write it when you write the other 252 bytes. Bytes $52 through $FB are either used by the operating system or reserved for the future. Just for fun, I have now written my own name in ASCII code into the bytes starting at $C0.
The rest of the bytes are used as shown in the following table. Where two choices are shown, separated by a slash, the left one has a code of $00 and the right choice has a code of $01.

Port 1 Port 2
$00 $0C Printer/Modem
$01 $0D Line Length (Any/40/72/80/132)
$02 $0E Delete LF after CR (No/Yes)
$03 $0F Add LF after CR (No/Yes)
$04 $10 Echo (No/Yes)
$05 $11 Buffer (No/Yes)
$06 $12 Baud Rate
$07 $13 Data & Stop Bits
$08 $14 Parity
$09 $15 DCD Handshake (No/Yes)
$0A $16 DSR Handshake (No/Yes)
$0B $17 XON-XOFF Handshake (No/Yes)

Display Parameters
$18 Color/Monochrome
$19 40/80 Column
$1A Text Color (00-0F)
$1B Background Color (00-0F)
$1C Border Color (00-0F)
$1D 60/50 Hertz Operation
$29 Text Language (0=English)
$2F Flash Rate

Keyboard Parameters
$2A Language (0=English)
$2B Buffering (No/Yes)
$2C Repeat Speed
$2D Repeat Delay
$30 Shift Caps-LowerCase (No/Yes)
$31 Fast Space-Delete Keys (No/Yes)
$32 Dual Speed (Normal/Fast)

Slot Configuration
$21-27 Slot 1-7 Internal/External
$28 Boot Slot

Miscellaneous
$1E User Volume
$1F Bell Volume
$20 System Speed (Normal/Fast)
$2E Double-Click Time
$33 High Mouse Resolution
$34 Date Format
$35 Time Format
$36 Min RAM for Ramdisk
$37 Max RAM for Ramdisk
$38-40 Count & Languages
$41-51 Count & Layouts
$80 AppleTalk Node Number
$81-A1 Operating System Variables

It is possible, as I said before, to talk directly to the battery RAM via I/O addresses. If you learn how to do this, and you use the skill to write values into battery RAM, you will probably do so without properly changing the checksum. In that case you have violated your system, and your next power-up will revert to default values for all parameters. It will stay that way until you reconfigure everything and/or install a proper checksum. The best policy is to use the standard firmware tools for all reading and writing, so that the checksum stays current.

You do not have to read or write the whole battery RAM at once. There are two tools for reading and writing a single byte. Tool Code $0B03 will write one byte, and tool code $0C03 will read one byte. The following code segments illustrate how to do it. The code as shown must be in Native Mode, with both x- and m-bits zero (full 16-bit mode).

WR PEA $00xx xx is new value for byte
PEA $00yy yy is address in battery RAM
LDX ##$0B03 write xx at yy
JSL $E10000
-----
RD PEA $0000 make room for result
PEA $00yy yy is address in battery RAM
LDX ##$0C03 read value at yy
JSL $E10000
PLA get result from stack (00xx)

The Clock/Calendar Chip not only contains the battery RAM; it also contains the date and time information, naturally. There are three tools for reading and writing the time and date. You can read time/date in either hexadecimal format or as an ASCII string, and you can write a new time/date in a hex format. The following code segments illustrate how to use the tools.
Read.Time.Into.ASCII.String

PEA BUFFER/256/256 Hi 16-bits of buffer address
PEA BUFFER Lo 16-bits of buffer address
LDX ##$0F03 Tool Code
JSL $E10000

The date and time will be converted to ASCII (with msb = 1) and stored in BUFFER, according to the formats selected in the configuration menu (stored in battery RAM locations $34 and $35). The most likely choice among North Americans will be the format "mm/dd/yy HH:MM:SS xM", but you have five other possibilities.

Read.Time.Hex
PEA 0 Make room for 8 bytes
PEA 0 to be returned
PEA 0
PEA 0
LDX ##$0D03
JSL $E10000
PLA Get $MMSS (minutes, seconds)
STA MMSS
PLA Get $yyHH (year, hours)
STA YYHH
PLA Get $mmdd (month, day)
STA MMDD
PLA Get day of week (in low byte)
STA DOW

The value for day of week runs from 0 to 6, with 0=Sunday. The value for "day" is 0-30, meaning that you have to add 1 to get the true day number. (Why? This is a little ridiculous!) Likewise, the value for month is 0-11, with 0 standing for January. (I can understand why the hardware might work with 0-based values for day and month, but why couldn't the firmware do the correction to "real" day and month numbers?) The year is specified as the actual year number minus 1900. I hope that means my //gs will still give correct dates after 1999. If the value of the "yy" byte can go all the way to 255, then we could use //gs until the end of the year 2155. Frankly, I think I'll get tired of computers before then.
To write a new date and time out to the Clock chip, you have to push the values onto the stack and call the tool:
Write.New.Time

PEA $mmdd month, day
PEA $yyHH year, hour
PEA $MMSS minute, second
LDX ##$0E03
JSR $E10000

Again, the month and day values are zero-based. Note that you cannot update the day-of-week directly; apparently it is only a CALCULATED value provided when you READ the date/time in hex format.
You might wonder whether anyone would really NEED all the above information. After all, Apple has provided the configuration system to see/modify all those parameters. The problem is you cannot really use that system unless you can SEE. A lot of Apple owners are not able to see, so they use the ECHO or other some other brand of speech synthesizer to speak everything that goes out to the screen. The configuration program cannot be made to speak, as it is now written.

Jayson Smith

unread,
Feb 6, 2013, 2:11:09 AM2/6/13
to
Wow that explains a lot! But I found even more in another Google search. I
found one guy a year or two ago who was wanting to directly modify a bram
file for use in KEGS but kept having the emulated GS reset it on him because
he was failing to update the checksum. If he'd known about this little file
then, he could have almost certainly done what he wanted. I have yet to find
any description of the bram values that is modern enough to account for
differences in ROM 03.

Jayson

Using the IIGS Battery-Backed-Up RAM and Clock From Assembly Language

Neil Parker

npa...@cie.uoregon.edu
par...@corona.uoregon.edu
Version 1.00
January 1994

COPYRIGHT by 1993 Neil Parker
All Rights Reserved

========
Abstract
========
This technical note discusses how a machine-language programmer can access
the Apple IIGS battery-backed-up RAM and clock hardware directly, bypassing
the firmware and system software. The BRAM and clock are normally accessed
through a set of subroutines in the Miscellaneous Toolset, which is the only
Apple-supported method accessing them. Direct manipulation of the hardware
provides access to several features that the Miscelaneous Toolset does not,
such as the "raw" time and the BRAM write-protect register.

===========
Terminology
===========

Terminology:
Bit 0 The least significant bit in a byte
Bit 7 The most significant bit in a byte
BRAM Battery backed-up RAM. Information stored in the
BRAM is not lost when the IIGS is turned off.
CLOCKCTL Clock/BRAM protocol control register
CLOCKDATA Clock/BRAM data register

========================
Accessing the IIGS Clock
========================
The IIGS BRAM/Clock chip seems to be identical to the one documented in
_The Macintosh Family Hardware Reference_. The BRAM/Clock chip uses a
simple protocol to transfer data in and out using two memory locations:

Register Address Function --------- ---------- ------------------------------------
CLOCKCTL 0xC034 Clock/BRAM control register
CLOCKDATA 0xC033 Clock/BRAM command and data register

The protocol for a "write" is:
CLOCKDATA <-- command specifying register to be written
CLOCKCTL <-- bits to initiate transfer
CLOCKDATA <-- data to be written
CLOCKCTL <-- bits to continue transfer

The protocol for a "read" is:
CLOCKDATA <-- command specifying register to be read
CLOCKCTL <-- bits to initiate transfer
CLOCKDATA --> data from BRAM/Clock
at the conclusion of this sequence, CLOCKDATA contains the desired data.

The available commands are shown in the table:

Comamnd Access Purpose
-------- ------ --------------------------------------
z0000001 R/W Clock seconds register lo
z0000101 R/W Clock seconds register next-to-lo
z0001001 R/W Clock seconds register next-to-hi
z0001101 R/W Clock seconds register hi
00110001 W-Only Write to test register
Always set bits 6 & 7 to "0".
00110101 W-Only Write to write-protect register
Setting bit 7 to "1" locks out further
writes to the BRAM/Clock
z010ab01 R/W Access BRAM address 100ab
z1abcd01 R/W Access BRAM address 0abcd
z0111abc R/W Followed by 0defgh00.
Access BRAM address abcdefgh

The "Command" column contains entries beginning with an "z". This letter
is replaced by a "0" or "1" indicating the desired access type. A value
of "0" for this bit specifies a write, while a value of "1" for this bit
specifies a read.

For example, the command to access the low byte of the clock's seconds
register is "z0000001". To write a value to this register, write the
byte "00000001" followed by the new value for the low byte of the seconds
register. To read the value of the low byte of the clock seconds register,
write the byte "10000001", and then read the value from CLOCKDATA.

----------------
Seconds Register
----------------
The four bytes of the seconds register form a 32-bit count of seconds since
midnight, January 1, 1904. They should always be accessed in lo-to-hi
order. When reading the clock, the ROM reads all four registers several
times, until it gets the same value twice in a row. When writing, the ROM
immediately tests the written value by reading it back -- if the values
don't match, it tries again.

----------------------------------------
Test Register and Write-Protect Register
----------------------------------------
The test register and the write-protect register are write-only registers.
Setting the high bit of the write-protect register prevents any of the
other registers (or BRAM locations) from being written to, and clearing it
enables writes to the registers and BRAM locations. The two high-order
bits of the test register are used as control bits during testing, and
should normally be set to 0 (setting them to anything else interferes with
normal clock functioning).

I can't find any code in the IIGS ROM that accesses these registers.

---------------------
Battery Ram Locations
---------------------
The z010ab01 and z1abcd01 commands are holdovers from the early Macintosh
days when there were only 20 bytes of BRAM. The IIGS doesn't seem to use
these functions--it uses the more general z0111abc 0defgh00 command
instead.

Note that the z0111abc 0defgh00 command is two bytes long. For reading,
read the data byte after writing the second byte. For writing, write the
data byte after writing the second byte.

========================
Reading/Writing the BRAM
========================
The CLOCKCTL byte is used to transfer data to and from the BRAM and the
clock. Only the upper three bits of the CLOCKCTL byte at $C034 are used
by the BRAM/Clock; the lower five bits contain the current screen border
and are thus not used by the BRAM/Clock. The bits are organized as:
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| S | T | A | Z | B | B | B | B |
+---+---+---+---+---+---+---+---+
where:
B Screen Border Color (Unused By BRAM/Clock)
S Start Transaction
T Transaction Type (Read/Write)
A Clock Enable Assert
Z This bit should always be set to 0

------------------
Start Transfer Bit
------------------
Bit 7 is the "Start Transaction" bit. Writing a "1" to this bit begins a
read or write transaction. All this means is that the BRAM/Clock begins
processing the value in CLOCKDATA. When the operation has completed, Bit
7 will be reset to "0". Thus, testing this bit allows the programmer to
determine when an operation has completed.

--------------------
Transaction Type Bit
--------------------
Bit 6 specifies the "Transaction Type" bit, either a read or a write. The
values and meanings for this bit are shown below:
0 = Write
1 = Read

-----------------------
Clock Enable Assert Bit
-----------------------
Bit 5 is the "Clock Enable Assert" bit. All the documentation says this
bit should be set to "0" before beginning a transfer, and should be set
back to "1" after the transfer is complete. However, the ROM routines
do the opposite -- they set the bit to "1" at the beginning of a transfer,
and "0" when the transfer completes.

--------------
Remaining Bits
--------------
Bit 4 should always be 0. Bits 0-3 have nothing to do with the BRAM/Clock
-- they control the screen border color.

--------------------
Writing Data To BRAM
--------------------
To write a byte, write the command indicating which location is desired to
CLOCKDATA. Then set bits 7 and 5 of CLOCKCTL, and clear bit 6 of CLOCKCTL.
When bit 7 is cleared by the BRAM, CLOCKDATA can be loaded with the data
value to be written. If you have more data to write from the BRAM, bits 6
and 5 in CLOCKCTL need not be changed between transfers. After each write,
clear bit 5.

A write uses the following protocol:
CLOCKDATA = "write" cmd for desired register or BRAM location
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 0
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] == 0
if this is a two-byte command ("00111abc 0defgh00")
CLOCKDATA = second byte of command
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 0
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] = 0
end if
CLOCKDATA = data to be written
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 0
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] == 0
CLOCKCTL[bit 5] = 0

----------------------
Reading Data From BRAM
----------------------
To read a byte, write the command indicating which location is desired to
CLOCKDATA. Then set bits 7, 6, and 5 of CLOCKCTL. When bit 7 is cleared
by the BRAM, CLOCKDATA contains the data value. If you have more data to
read from the BRAM, bits 6 and 5 in CLOCKCTL need not be changed between
transfers. After each read, clear bit 5.

The algorithm looks like this:
CLOCKDATA = "read" cmd for desired register or BRAM location
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 0
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] == 0
If this is a two-byte command ("10111abc 0defgh00")
CLOCKDATA = second byte of "read" command
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 0
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] == 0
end if
CLOCKCTL[bit 7] = 1
CLOCKCTL[bit 6] = 1
CLOCKCTL[bit 5] = 1
wait until CLOCKCTL[bit 7] = 0
result = CLOCKDATA
CLOCKCTL[bit 5] = 0

------------------
Computing Checksum
------------------
Any change to the BRAM invalidates the checksum unless the programmer
explicitly recomputes the checksum and stores it in the BRAM. An invalid
checksum causes the IIGS to reload the BRAM with default values. The
checksum is a 16-bit number computed by the following algorithm:
initialize checksum to 0
start at end of BRAM buffer (BRAM locations $FA and $FB)
repeat
rotate checksum left 1 bit
checksum = checksum + word from buffer
buffer position = buffer position - 1 byte
until we reach the beginning of the buffer

The checksum is stored in BRAM bytes $FC (low byte) and $FD (high byte).
The checksum exclusive-ORed with the constant $AAAA is stored in BRAM bytes
$FE (low byte) and $FF (high byte).

The source code for computing the BRAM checksum is included in the ROM
disassembly below.

-------
Caveats
-------
The Macintosh documentation recommends that the last access to the
BRAM/Clock
be a write operation, in order to avoid running down the battery. Since the
IIGS ROM code does not seem to heed this advice it is possible that the IIGS
hardware makes the final write unnecessary.

Beware of writing directly to the BRAM. The last few bytes of BRAM are
reserved for a checksum -- if the checksum doesn't match the remaining
contents of the BRAM, then the IIGS will ignore the entire BRAM contents
and reset everything to the defaults. The BRAM will also be ignored and
reset to defaults if any of its values are outside their legal ranges.

======================================
Appendix A: IIGS ROM Clock Access Code
======================================

The following code was disassembled from the IIGS ROM code accessing the
clock and BRAM. This code is for illustrative purposes only, and may
contain transcription errors.

; Copyright by Apple Computer, Inc.
; Disassembly by Neil Parker
; (Warning: There may be some transcription errors below)
;
ClockData equ $C033
ClockCtl equ $C034
BatteryRAM equ $E102C0 ;Battery RAM buffer
ClkErr equ $E103E0 ;Clock read error count
ClkRData equ $E103E1 ;4 bytes--clock read buffer
ClkWData equ $E103E5 ;4 bytes--clock write buffer
DBRE1 equ $FFF882 ;Set DBR to $E1
;
; Subroutine READTIME, at $FF/B5A0 (ROM 1) or $FF/B45A (ROM 0)
; Read the time into CLKRDATA
; Enter in 8-bit native mode
; Returns carry clear for success, carry set for failure
;
; The TOREADTIME vector at $E1/008C jumps to this routine.
;
ReadTime php ;Save interrupt state
sei ;No interrupts allowed
phb
jsr DBRE1 ;Switch to bank $E1
stz |ClkErr ;Start error count at 0
RTTry ldy #0 ;Start read count at 0
RTTry2 inc |ClkErr
beq RTFail ;If error count wraps, fail
ldx #0 ;Start byte count at 0
lda #$FD ;Command--turns into 10000001 below
RTByteLp clc
adc #4 ;Form next sec reg read command
pha ;Save partial cmd on stack
ora #$80 ;Set "read" bit
sep #$40 ;Set overflow bit (indicates read command)
jsr BatRWAY ;Access register
cpy #0 ;First read of regs?
bne RTComp ;If not, compare with previous read
sta |ClkRData,X ;1st read, so store it
bra RTMatch ;Go prepare for reading next byte
RTComp cmp |ClkRData,X ;Does this read match last read?
beq RTMatch ;If so, go prepare for next byte
pla ;Otherwise discard partial command
bra RTTry ;...and try again
RTMatch pla ;Get partial cmd
inx
cpx #4 ;Got all 4 bytes yet?
bcc RTByteLp ;If not, go get more
tyx ;First read?
bne RTGood ;If not, success!
iny ;Otherwise indicate 2nd read
bra RTTry2 ;...and go read again
RTFail plb ;Failure exit--restore old DBR
SECRTL plp ;Restore int state
sec ;Return failure
rtl
RTGood plb
CLCRTL plp
clc ;Return success
rtl
;
; Subroutine WRITETIME, at $FF/B5E4 (ROM 1) or $FF/B49E (ROM 0)
; Writes raw time in CLKWDATA to clock chip
; Enter in 8-bit native mode
; Returns carry clear for success, set for failure
;
; The TOWRITETIME vector at $E1/0088 jumps to this routine.
;
WriteTime php ;Save int state
sei ;No interrupts allowed
lda #0 ;Start error count at 0
pha ;Keep error count on stack
WTTry ldx #0 ;Start byte count at 0
lda #$FD ;Cmd--turns into 00000001 below
WTByteLp clc
adc #4 ;Form next write cmd
pha ;Save it
lda >ClkWData,X ;Get byte to write
tay
lda 1,S ;Get write cmd
jsr BatWAY ;Write the byte
pla ;Get write cmd
inx
cpx #4 ;Done 4 bytes yet?
bcc WTByteLp ;If not, go do the next
jsl ReadTime ;Read back what we just wrote
ldx #3
WTCmpLp lda >ClkRData,X ;Compare data just read
cmp >ClkWData,X ;...to data we wrote
bne WTNotSame ;If not the same, go read again
dex ;Compared all 4 bytes yet?
bpl WTCmpLp ;If not, compare more
pla ;Success--discard error count
bra CLCRTL ;...and exit with success
WTNotSame pla ;Get error count
dec A
bne WTTry ;If not too many errors, try again
bra SECRTL ;Otherwise exit with failure
;
; Subroutine BCHECKSUM, at $FF/B61D (ROM 1) or $FF/B4D7 (ROM 0)
; Calculate the checksum of the BRAM
; Enter with data to be checksummed in BATTERYRAM buffer
; 8-bit native mode
; DBR pointing to bank $E1
; Returns with X=checksum
; A=checksum EORed with constant $AAAA
; 16-bit native mode
; Don't call this routine yourself--it resides in bank $FF, and ends with
; an RTS, not an RTL. If you need to compute a new BRAM checksum yourself,
; you should write your own routine that duplicates the functionality of
; this code.
;
BCheckSum ldx #$FA ;Start at end of buffer
rep #$20 ;16-bit A-reg, since checksum is 16 bits
longa on
lda #0 ;Initialize checksum to 0
BCheck1 rol A
adc |BatteryRAM,X ;Add in current word of buffer
dex
cpx #$FF ;At beginning of buffer yet?
bne BCheck1 ;If not, go do more
rep #$30
longi on
tax ;Else save checksum in X
eor #$AAAA ;Get complement of checksum in A
rts
longa off
longi off
;
; Subroutine BATIO, at $FF/B635 (ROM 1) or $FF/B4EF (ROM 0)
; Read or write a byte from a BRAM location
; Enter with A=value to read (if reading)
; Y=address to read or write
; Overflow flag= 1 to read or 0 to write
; 8-bit native mode
; DBR pointing to bank 0, 1, $E0, or $E1
; If reading, returns byte read in A reg
;
; The READBRAM and WRITEBRAM routines just call this in a loop. Don't
; try to call it yourself--it resides in bank $FF, and ends with an RTS,
; not an RTL.
;
BatIO pha ;Save byte for writing
tya ;Save address to read/write
pha
and #$E0 ;Work on 1st byte: abcdefgh -> v0111abc
lsr A
lsr A
lsr A
lsr A
lsr A
ora #$38
bvc BNoOv
ora #$80
BNoOv xba ;Save 1st byte
pla ;Work on 2nd byte: abcdefgh -> 0defgh00
and #$1F
asl A
asl A
xba ;Get 1st byte back, save 2nd byte
php ;Save overflow bit
jsr BatSend ;Send 1st byte to BRAM/Clock
xba ;Get 2nd byte
BDoTrans jsr BatSend ;Send it
plp ;Restore overflow bit
pla ;Get data to be sent
jsr BatSR ;Read or write byte (depending on V bit)
pha ;Save data read, if any
lda |ClockCtl ;Turn off "clock enable assert" bit
and #$DF
sta |ClockCtl
pla
rts
;
; BATWAY: Write A-reg, then Y-reg to BRAM/Clock chip
;
BatWAY clv ;Force write mode, then fall into...
;
; BATRWAY: Write A-reg, then either write Y-reg or read into A-reg
;
BatRWAY phy
php ;Save overflow bit
bra BDoTrans ;Go do the transaction
;
; BATSEND: Send A-reg to BRAM/Clock chip
;
BatSend clv ;Force write mode, then fall into...
;
; BATSR: Send or receive data to/from BRAM/Clock chip
;
BatSR sta |ClockData ;Put send data (if any) in data reg
lda |ClockCtl ;Get control bits
and #$3F ;Done-status=0, R/W=W
bvs SRVSet ;Overflow set (read mode)?
ora #$A0 ;No: Done-status=1, "assert"=1
bra SRDoIt
SRVSet ora #$E0 ;Yes: Done-status=1, R/W=R, "assert"=1
SRDoIt sta |ClockCtl ;Perform action
SRLoop lda |ClockCtl
bmi SRLoop ;Wait for action to finish
lda |ClockData ;Get data to be read (if any)
rts ;Done

==================================
Appendix B: Battery RAM Memory Map
==================================

Apple Computer's official position is that NONE of the BRAM is free for
programmer use; all 256 bytes are used. The table below describes the
value in each location in the BRAM for ROM 1. Any locations marked
"Reserved" are reserved by Apple for future use; some of these locations
may in fact be used by ROM 3 or by GS/OS. Note that the Port 2 values
at locations $0C through $17 function identically to the Port 1 values
at locations $00 through $0B and have been omitted for brevity.

Location Contents
-------- --------------------------------------------------
$00 Port 1 peripheral
0 = Printer
1 = Modem
2 = Appletalk (ROM 3 only)
$01 Port 1 line length
0 = Unlimited
1 = 40 characters
2 = 72 characters
3 = 80 characters
4 = 132 characters
$02 Port 1 Delete Line Feed After Carriage Return
0 = No
1 = Yes
$03 Port 1 Add Line Feed After Carriage Return
0 = No
1 = Yes
$04 Port 1 Echo
0 = Off
1 = On
$05 Port 1 Buffering
0 = Off
1 = On
$06 Port 1 Baud Rate
0 = 50 Baud
1 = 75 Baud
2 = 110 Baud
$D = 9600 Baud
$E = 19200 Baud
$07 Port 1 Data/Stop Bits
0 = 5 Data Bits, 1 Stop Bit
1 = 5 Data Bits, 2 Stop Bit
2 = 6 Data Bits, 1 Stop Bit
3 = 6 Data Bits, 2 Stop Bit
4 = 7 Data Bits, 1 Stop Bit
5 = 7 Data Bits, 2 Stop Bit
6 = 8 Data Bits, 1 Stop Bit
7 = 8 Data Bits, 2 Stop Bit
$08 Port 1 Parity
0 = Odd
1 = Even
2 = None
$09 Port 1 DCD Handshake
0 = Off
1 = On
$0A Port 1 DSR Handshake
0 = Off
1 = On
$0B Port 1 XON/XOFF Handshake
0 = Off
1 = On

$0C Port 2 Printer/Modem
$0D Port 2 Line Length
$0E Port 2 Delete Line Feed After Carriage Return
$0F Port 2 Add Line Feed After Carriage Return
$10 Port 2 Echo
$11 Port 2 Buffering
$12 Port 2 Baud rate
$13 Port 2 Data/Stop bits
$14 Port 2 Parity
$15 Port 2 DCD Handshake
$16 Port 2 DSR Handshake
$17 Port 2 XON/XOFF Handshake

$18 Display Color/Monochrome
0 = Color
1 = Monochrome
$19 Display 40/80 Columns
0 = 40 columns
1 = 80 columns
$1A Display Text Color
Color = $0 to $F
$1B Display Background Color
Color = $0 to $F
$1C Display Border Color
Color = $0 to $F
$1D 50/60 Hertz
0 = 60 Hertz
1 = 50 Hertz
$1E User Volume
Volume = $0 to $F ($0 is quietest)
$1F Bell Pitch
Pitch = $0 to $F ($0 is lowest)
$20 System Speed
0 = Slow
1 = Fast

$21 Slot 1 Internal/External
0 = Printer
1 = Your Card
$22 Slot 2 Internal/External
0 = Modem
1 = Your Card
$23 Slot 3 Internal/External
0 = 80-Column Card Firmware
1 = Your Card
$24 Slot 4 Internal/External
0 = Mouse
1 = Your Card
$25 Slot 5 Internal/External
0 = Smartport
1 = Your Card
$26 Slot 6 Internal/External
0 = 5.25 Drive
1 = Your Card
$27 Slot 7 Internal/External
0 = AppleTalk
1 = Your Card
$28 Startup Slot
0 = Scan
1-7 = Slot Number
8 = RAM Disk
9 = ROM Disk

$29 Text Display Language
$2A Keyboard Language
$2B Keyboard Buffering
0 = off
1 = on
$2C Keyboard Repeat Speed
Speed = 0 to 7 (0 is slowest)
$2D Keyboard Repeat Delay
0 = shortest delay
...
3 = longest delay
4 = no delay
Speed = 0 to 4
$2E Double-Click Time
0 = longest delay
...
4 = shortest delay
$2F Cursor Flash Rate
0 = fastest flash rate
...
4 = slowest flash rate
$30 Shift Caps/Lowercase
0 = No
1 = Yes
$31 Fast space/delete Keys
0 = No
1 = Yes
$32 Dual Speed Keys
0 = No
1 = Yes
$33 High Speed Mouse
0 = No
1 = Yes

$34 Month/day/year format
0 = MM/DD/YY
1 = DD/MM/YY
2 = YY/MM/DD
$35 24-hr/AM-PM format
0 = 12 Hour Format with AM/PM
1 = 24-Hour format
$36 Minimum RAM for RAM disk
0 = None
$20 = Largest Selectable Size
$37 Maximum RAM for RAM disk
0 = None
$20 = Largest Selectable Size

$38-$40 List of available display languages
$41-$51 List of available keyboard layouts

$52-$58 Reserved (Maybe Used On ROM 3???)
$59 Memory Peeker and Visit Monitor CDA settings
Bit Meaning
0-6 ???
7 Install CDAs at boot time
$5A Keyboard translation setting
0 = none
1-$FE = user-defined
$FF = standard
$5B CloseView settings
Bit Meaning
0-3 magnification
4 cvUseKeys
5 cvMagnify
6 cvInvert
7 cvEnabled
$5E Miscellaneous System 6 settings
Bit Meaning
0 disable close captioning
("visual indication of sounds")
1 0 = daylight savings time
1 = standard time
2 disable automatic daylight savings time
3-4 number of menu blinks
$5F Miscellaneous System 6 settings
Bit Meaning
0 Alphabetize DA Menus
1 Disable Init Icons On Boot
2 Disable QuickDraw Scanline Interrupts
3-5 Reserved
6-7 Set to %10 If This Byte Is Valid
$60 Scaling For WaitUntil Toolbox Call
$61 Reserved For Network Medium Selection
$62 OS For Network Boot
1 = GS/OS
2 = ProDOS 8
$63-$7F Reserved
$80 AppleTalk Node Number
$81 GS/OS Cache Size
0 = Minimum
1 = 32K
2 = 64K
...
$FE = 8128K
$82 Reserved for operating system variables
...
$A1 Reserved for operating system variables
$A2 Reserved
...
$FB Reserved

$FC Checksum (low byte)
$FD Checksum (high byte)
$FE Complement of checksum (low byte)
$FF Complement of checksum (high byte)

============
Bibliography
============

_Inside Macintosh, Volume III_
Apple Computer, Inc.
Addison-Wesley
1985
Contains a description of the old Mac BRAM/Clock chip (the one with only
20 bytes of RAM).

_The Macintosh Family Hardware Reference_
Apple Computer, Inc.
Addison-Wesley.
Contains a description of the newer Mac BRAM/Clock chip and the commands
that it accepts.

_Apple IIGS Hardware Reference_
Apple Computer, Inc.
Addison-Wesley.
1987
Describes the CLOCKDATA and CLOCKCTL registers.

_Apple IIGS Toolbox Reference: Volume 1_
Apple Computer, Inc.
Addison-Wesley.
1988
Describes the contents of most of the Battery RAM locations.

_Apple IIGS Technical Reference_
Michael Fischer.
McGraw-Hill, Inc.
1986
This seems to be the only source that lists the legal values for most of
the BRAM locations.


David Schmidt

unread,
Feb 6, 2013, 6:59:17 AM2/6/13
to
On 2/6/2013 2:11 AM, Jayson Smith wrote:
> Wow that explains a lot! But I found even more in another Google search. I
> found one guy a year or two ago who was wanting to directly modify a bram
> file for use in KEGS but kept having the emulated GS reset it on him because
> he was failing to update the checksum. If he'd known about this little file
> then, he could have almost certainly done what he wanted.

That was me, for GSport. I did run across the checksum algorithm and
was able to get the changes to stick. (I also had to account for
endianness of CPU... a story for another time.) That is what enabled me
to set up a one-time boot from a particular slot and disk image,
reverting to the previous configuration afterwards. Invoking GSport
with an image name on the command line (or from Windows file->open as)
enables this behavior.

0 new messages