Article View: comp.os.msdos.programmer
Article #2278Re: Accessing CMOS RAM
From: phys169
Date: Tue, 18 Sep 1990 08:29
Date: Tue, 18 Sep 1990 08:29
553 lines
23926 bytes
23926 bytes
In article <1990Aug31.055038.2326@acmis.cmis.co.at>, joe@acmis.cmis.co.at (Johannes Rupp) writes:
> I would like to know how to access the contents of the CMOS RAM where the
> PC configuration (like disk drive type, amount of memory, etc.) is
> stored.
>
> Can anybody tell me at which memory address the CMOS starts, or
> what other mechanism I could use to access this information by
> an assembler or C program.
>
For The IBM AT and PS2, you write a number (0-63) to port 70hex, and quickly
read from port 71h (or write to it). Don't write to port 70h without reading
from it in the next few instructions, and don't give it an address over 127.
As with all port accesses, you'll probably need a few time-wasting jumps
between any pairs of IO instructions to let the hardware catch up to the CPU.
The assignment of locations is difficult to summarise, so I've included a Turbo
Pascal program that accesses the CMOS clock & RAM. The executeable of a later
and much nicer version (with automatic determination of disk types, etc) will
be available soon (anyone with an ftp site wanting it can e-mail me now).
For the PC & XT, the base address varies from board to board, and most programs
test a bunch of memory locations until one seems reasonable; successive cells
are at successive addresses, unlike the 2-port method on the AT/PS2, and the
RAM doesn't carry information like disk types.
Hope this helps,
Mark Aitchison, Physics, University of Canterbury, New Zealand.
---snip here----[CMOS.PAS v2.1]---------------------------
program CMOS; {show/edit AT CMOS RAM contents}
{feel free to use & distribute this program non-commercially}
uses DOS,CRT;
const HighlightedAddr : byte = $3F;
HelpInterrupt = $4E;
SaveNibbles : longint = 0; {to implement DEL key}
CMOSport : word = $70; {start by assuming AT or PS2 CMOS RAM}
Unknown = $FFFF; {probably a PC/XT Cmos card, need to look for the port}
NewContents : byte = 0;
NeedToChange : boolean = false;
Addresses : string ='';
Globals : string ='';
SwitchChar : char = '/';
var st : string;
Model : string[9];
reg : registers;
OldCursor,
n,er : integer;
i,j : word;
Contents,
adr : byte;
Keystroke : char;
MachineID : word absolute $F000:$FFFE;
function binary(w : byte) : string;
const
digit : array[0..$F] of char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
var b : byte;
st : string[8];
begin
st:='00000000';
for b:=0 to 7 do
if odd(w shr b) then st[8-b]:='1';
binary:=st;
end;
function hex(w : byte) : string;
const
digit : array[0..$F] of char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
begin
hex:=digit[w shr 4]+digit[w and $0F];
end;
procedure Beep(freq : word);
var i : integer; reg : registers;
begin
sound(freq);
for i:=1 to 10 do begin
intr($28,reg);
delay(15);
end;
nosound;
end;
function HelpInstalled : boolean;
type TSRheader = record Version : word; Name : array[1..3] of char; end;
var HelpPointer : ^TSRheader;
begin
GetIntVec(HelpInterrupt,pointer(HelpPointer));
if ofs(HelpPointer^)<5 then begin HelpInstalled:=false; exit; end;
dec(word(HelpPointer),5);
HelpInstalled:=HelpPointer^.Name='HLP';
end;
procedure Help(topic : string);
var reg : registers;
begin
if not HelpInstalled then begin beep(440); exit; end;
with reg do begin
DS:=seg(topic);
DX:=ofs(topic);
intr(HelpInterrupt,reg);
end;
end;
procedure CursorOff;
begin
with reg do begin
AH:=3; BH:=0;
intr($10,reg); {get present cursor}
OldCursor:=CX;
CX:=$2020;
AH:=1;
intr($10,reg); {set cursor to nothing}
end;
end;
procedure CursorOn;
begin
with reg do begin
CX:=OldCursor;
AH:=1;
intr($10,reg); {set cursor to what it was}
end;
end;
function CmosRam(adr : byte ): byte;
begin
if CmosPort=$70
then begin {AT or PS/2 CMOS}
inline($FA); { cli ;DisableInterrupts }
Port[CmosPort]:=adr; { out 70h,adr }
CmosRam:=Port[$71]; { in CmosRam,71h }
inline($FB); { sti ;EnableInterrupts }
end
else begin
inline($FA); { cli ;DisableInterrupts }
i:=Port[CmosPort+adr]; {read twice in case port strange}
if i<>Port[CmosPort+adr] then {worry};
inline($FB); { sti ;EnableInterrupts }
CmosRam:=i;
end;
end;
procedure SetCmosRam(adr,NewContents : byte );
var i : byte; CheckSum : word;
begin
if CmosPort=$70
then begin {AT or PS/2 CMOS}
inline($FA); { cli ;DisableInterrupts }
Port[$70]:=adr; { out 70h,adr }
Port[$71]:=NewContents; { out 71h,NewContents }
inline($FB); { sti ;EnableInterrupts }
end
else begin
Port[CmosPort+adr]:=NewContents;
if NewContents<>Port[CmosPort+adr] then {worry};
end;
if adr in [$10..$2D]
then begin
CheckSum:=0;
for i:=$10 to $2D do inc(CheckSum,CmosRam(i));
SetCmosRam($2E,hi(CheckSum));
SetCmosRam($2F,lo(CheckSum));
end;
end;
procedure DisplayClockData;
var adr : byte;
begin
for adr:=$00 to $05 do
begin
gotoXY(6,2+adr);
if adr=HighlightedAddr then TextAttr:=$78 else TextAttr:=$0F;
write(hex(CmosRam(adr)));
end;
end;
procedure DisplayOtherData(adr : byte);
const Col : array [0..3] of byte = (6,30,52,68);
DayName : array[0..7] of string[9] = ('Sunday ','Monday ','Tuesday ','WednesDay',
'Thursday ','Friday ','Saturday ','Sunday ');
ScreenTypeName : array[0..3] of string[10] = ('EGA/VGA/etc','CGA 40col','CGA 80col','monochrome');
DisketteTypeName : array[0..4] of string[4] = ('none','360k','1.2M','720k','1.4M');
HardDiskTypeName : array[0..15] of string[5]= ('none','306/4','615/4','615/6','940/8',
'940/6','615/4','462/8','733/5','900/F',
'820/3','855/5','855/7','306/8','733/7','other');
MonthName : array[0..12] of string[3] = ('???','Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec');
var a,b,c,d : byte;
begin
if adr=HighlightedAddr then TextAttr:=$78 else TextAttr:=$0F;
gotoXY(Col[adr shr 4],2+(adr and $0F));
Contents:=CmosRam(adr);
write(hex(Contents));
if CmosPort<>$70 then write(hex(Contents),'h =',Contents:3,' ')
else case adr of
$06 : begin
gotoXY(Col[0]+7,8);
if Contents<7 then write(DayName[Contents])
else write('??? =',Contents:3);
end;
$08 : begin
gotoXY(Col[0]+9,10);
if Contents>9 then if (CmosRam($0B) and 4)=0 then dec(Contents,6);
if Contents in [1..12] then write(MonthName[Contents])
else write('???');
end;
$0A : begin
gotoXY(16,2+$0A);
write(Binary(Contents));
gotoXY(1,22);
a:=(Contents shr 4) and 7;
b:=(Contents and $0F);
if a=2 then write('32.768kHz time base, ')
else write('strange DV=',a,'! ');
if b=6 then write('1024Hz P.I. rate, ')
else write('P.I. rate sel=',b,'? ');
end;
$0B : begin
gotoXY(16,2+$0B);
write(Binary(Contents));
gotoXY(40,22);
if boolean(Contents and $80) then write('FROZEN, ')
else write('counting, ');
if boolean(Contents and $40) then write('P.I. on, ')
else write('P.I. off, ');
if boolean(Contents and $20) then write('Alarm on, ')
else write('Alarm off, ');
writeln('UIE=',boolean(Contents and $10));
if boolean(Contents and $08) then write('Square wave on, ')
else write('Square wave off, ');
if boolean(Contents and $04)
then write('Binary mode; time=',CmosRam(4):2,':',CmosRam(2),':',CmosRam(0),', date:',
CmosRam(7),'/',MonthName[CmosRam(8) mod 13],'/',
(19+(CmosRam($32) and 1)*100+CmosRam(9)))
else write(' BCD mode; time=',hex(CmosRam(4)),':',hex(CmosRam(2)),':',hex(CmosRam(0)),', date:',
hex(CmosRam(7)),'/',MonthName[((CmosRam(8) and $F)+10*(CmosRam(8) shr 4)) mod 13],'/',
hex(CmosRam($32))+hex(CmosRam(9)));
write(', Dayl. Sav.=',odd(Contents));
end;
$0C..$0F: begin gotoXY(16,2+adr); write(Binary(Contents)); end;
$1B..$2D, {"reserved", normally 0}
$11,$13 : write(' =',Contents:3);
$34..$3F: if Contents in [32..126] {"reserved", often junk}
then write(' =',Contents:3,' "',chr(Contents),'"')
else write(' =',Contents:3,' ');
$10 : begin
a:=Contents shr 4;
if a<=4 then write(' A:'+DisketteTypeName[a])
else write(' A:',a:2,'??');
b:=Contents and $0F;
if b<=4 then write(' B:'+DisketteTypeName[b])
else write(' B:',b:2,'??');
end;
$12 : begin
c:=Contents shr 4;
d:=Contents and $0F;
st:=' C:'+HardDiskTypeName[c]+',D:'+HardDiskTypeName[d]+' ';
st[0]:=#15;
write(st);
end;
$14 : begin
write(' equip=',binary(Contents));
intr($11,reg);
gotoXY(1,19);
if (reg.AL and $F3)<>(Contents and $F3)
then write('Equipment flags are ',binary(Contents),'; they should be ',binary(reg.AL))
else write('Equipment: ',succ(Contents shr 6)*(Contents and 1),' diskettes, ',
ScreenTypeName[(Contents shr 4) and 3],' screen'+
', Coprocessor=',boolean(Contents and 2) );
end;
$15 : with reg do
begin
intr($12,reg);
if HighlightedAddr=$16 then TextAttr:=$78;
gotoXY(29,21);
i:=Contents+256*CmosRam($16);
if i=reg.AX
then write('Base: okay ')
else write('Base: should be ',reg.AX,'!');
end;
$16,$18 : begin
if HighlightedAddr=adr-1 then TextAttr:=$78;
write(' =',Contents*256+CmosRam(adr-1):5);
end;
$19 : begin
if HighlightedAddr=$12 then TextAttr:=$78;
if Contents>0 then write(' C: type ',Contents:3)
else write(' ');
end;
$1A : begin
if HighlightedAddr=$12 then TextAttr:=$78;
if Contents>0 then write(' D: type ',Contents:3)
else write(' ');
end;
$33 : begin
gotoXY(6,20);
if boolean(Contents and $80) then write('Info: top 128K of base mem installed. ')
else write('Info: top 128K of base mem not installed.');
write(' "First User"=',boolean(Contents and $40));
if (Contents and $3F)<>0 then write(' STRANGE?')
else write(' ');
end;
$2E : begin
gotoXY(2,21);
if HighlightedAddr=$2F then TextAttr:=$78;
j:=0;
for i:=$10 to $2D do inc(j,CmosRam(i));
i:=Contents*256+CmosRam($2F);
if i<>j then write('Checksum: should be ',j,'!')
else write('Checksum: okay. ');
end;
$31 : begin
gotoXY(51,21);
if HighlightedAddr in [$30,$17,$18] then TextAttr:=$78;
i:=Contents*256+CmosRam($30);
j:=CmosRam($18)*256+CmosRam($17);
if i<>j then write('Ext-mem: should be ',i,'!')
else write('Ext-mem: okay ');
end;
end {of case};
if boolean(adr and 7) then exit;
Contents:=CmosRam(HighlightedAddr);
gotoXY(64,19);
TextAttr:=$78;
write(hex(HighlightedAddr),': ',hex(Contents),'h =',Contents:3);
if Contents in [32..126] then write(' "',char(Contents),'"')
else write(' ');
end;
procedure ProcessKeystroke;
begin
if Keystroke=#0
then case ord(ReadKey) of
00 : Keystroke:=^C; {Break}
59 : Help(MODEL+' CMOS ADDRESS '+hex(HighlightedAddr));
68 : Keystroke:=#27; {F10 also escapes from the program}
72 : dec(HighlightedAddr); {Up arrow}
80 : inc(HighlightedAddr); {Down Arrow}
71 : HighlightedAddr:=0; {Home}
79 : HighlightedAddr:=$3F; {End}
75 : dec(HighlightedAddr,$10); {Left Arrow}
77 : inc(HighlightedAddr,$10); {Right arrow}
else Beep(1234);
end {of case}
else case Keystroke of
'+' : SetCmosRam(HighlightedAddr,CmosRam(HighlightedAddr) +1);
'-' : SetCmosRam(HighlightedAddr,CmosRam(HighlightedAddr) -1);
'$' : {ignore, default is hex};
'=' : begin
gotoXY(64,19); TextAttr:=$86; write('New value=');
TextAttr:=LightRed;
readln(i);
SaveNibbles:=(SaveNibbles shl 8)+CmosRam(HighlightedAddr);
SetCmosRam(HighlightedAddr,i);
end;
'0'..'9' : if HighlightedAddr>0 {cannot easily set seconds this way!}
then begin
i:=CmosRam(HighlightedAddr);
SaveNibbles:=(SaveNibbles shl 4)+(i shr 4);
SetCmosRam(HighlightedAddr,ord(Keystroke)-ord('0')+(i*16) and $FF);
end;
'A'..'F',
'a'..'f' : if HighlightedAddr>0
then begin
i:=CmosRam(HighlightedAddr);
SaveNibbles:=(SaveNibbles shl 4)+(i shr 4);
SetCmosRam(HighlightedAddr,ord(upcase(Keystroke))-ord('A')+10+(i*16) and $FF);
end;
'"',#39 : begin
gotoXY(64,19); write('type character: ');
i:=ord(ReadKey);
if i=0 then i:=ord(ReadKey);
SaveNibbles:=(SaveNibbles shl 8)+CmosRam(HighlightedAddr);
SetCmosRam(HighlightedAddr,i);
inc(HighlightedAddr);
end;
#127,^H : begin
j:=(SaveNibbles and $000F)*16; SaveNibbles:=SaveNibbles shr 4;
i:=CmosRam(HighlightedAddr);
SetCmosRam(HighlightedAddr,j+(i shr 4));
end;
end {of case};
end;
function ATorBetter : boolean; {also set Model name}
begin
ATorBetter:=false; Model:='PC '; {assume PC/XT/Clone/PCjr/etc}
case lo(MachineID) of
$FA,$F8,
$FC : with reg do begin
ATorBetter:=true;
AH:=$C0; intr($15,reg);
if not odd(Flags)
then Model:='PS/2'
else Model:='AT';
end;
end { of case};
end;
procedure CheckGivenParameters;
type string127 = string[127];
var i : byte;
GivenParameters : ^string127;
begin
reg.AX:=$3700; MsDos(reg); SwitchChar:=chr(reg.DL);
GivenParameters:=ptr(PrefixSeg,$80);
for i:=1 to length(GivenParameters^) do
if GivenParameters^[i] in [',',';',^I]
then GivenParameters^[i]:=' '
else GivenParameters^[i]:=upcase(GivenParameters^[i]);
i:=1; n:=-1;
repeat st:=paramstr(i);
if (pos('=',st)>1) or (pos('.',st)>1)
then begin
j:=pos(st,GivenParameters^); if st[1]='.' then inc(j,2);
repeat case GivenParameters^[j] of
'=' : begin
if st[1]<>'=' then insert(' ',GivenParameters^,j);
repeat inc(j) until GivenParameters^[j]<>'=';
if GivenParameters^[j]<>' ' then insert(' ',GivenParameters^,j);
end;
'.' : begin
if st[1]<>'.' then insert(' ',GivenParameters^,j);
repeat inc(j) until GivenParameters^[j]<>'.';
if GivenParameters^[j]<>' ' then insert(' ',GivenParameters^,j);
end;
end;
inc(j);
until (j>length(GivenParameters^)) or (GivenParameters^[j]=' ');
st:=paramstr(i);
end;
if st[1]=SwitchChar then st[1]:='/';
case st[1] of
'=' : begin NeedToChange:=true; delete(st,1,1); end;
'/','-' : Globals:=Globals+'/'+upcase(st[2])+copy(st,3,99);
'.','T' : begin
inc(i); j:=n;
val(paramstr(i),n,er);
for j:=j+1 to n do Addresses:=Addresses+chr(j);
end;
'0'..'9','$' : begin
if st[length(st)]='H' then begin dec(st[0]); insert('$',st,1); end;
val(st,n,er);
if NeedToChange then NewContents:=n
else Addresses:=Addresses+chr(n);
end;
end {of case};
inc(i);
until (i>paramcount);
end;
procedure InteractiveMode;
begin
ClrScr;
DirectVideo:=true;
TextAttr:=Cyan;
writeln('ZDDDDDDDDDDDDDDDDDDDDDDDBDDDCMOS CLOCK/RAMDDDDBDDDDDDDDDDDDDDDBDDDDDDDDDDDDDDD?');
writeln('300: Seconds 310: 320: 330: ex.mem. 3');
writeln('301: Seconds alarm 311: 321: 331: found. 3');
writeln('302: Minutes 312: 322: 332: Century 3');
writeln('303: Minutes alarm 313: 323: 333: "Info" 3');
writeln('304: Hours 314: 324: 334: 3');
writeln('305: Hours alarm 315: Base memory 325: 335: 3');
writeln('306: Day=xxxxxxxxx 316: Kbytes 326: 336: 3');
writeln('307: Day of month 317: Extended mem. 327: 337: 3');
writeln('308: Month 318: Kbytes 328: 338: 3');
writeln('309: Year 319: 329: 339: 3');
writeln('30A: stat.A=xxxxxxxx 31A: 32A: 33A: 3');
writeln('30B: stat.B=xxxxxxxx 31B: 32B: 33B: 3');
writeln('30C: stat.C=xxxxxxxx 31C: 32C: 33C: 3');
writeln('30D: stat.D=xxxxxxxx 31D: 32D: 33D: 3');
writeln('30E: diagn.=xxxxxxxx 31E: 32E: Checksum33E: 3');
writeln('30F: shutd.=xxxxxxxx 31F: 32F: of 10-2D33F: 3');
writeln('@DDDDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDDDDDDDADDDDDDDDDDDDDDDADDDDDDDDDDDDDDDY');
gotoXY(1,24);
TextAttr:=$70; {reverse video, dim}
st:='(ESC=quit)('#24','#25','#26','#27' to move)(+,-,=," to alter)';
if HelpInstalled then st:=st+'(F1=help)';
write(st:48,' CMOS v2.1 (C)1990 M.Aitchison');
TextAttr:=Yellow;
DisplayClockData;
for adr:=$06 to $3F do DisplayOtherData(adr);
repeat
repeat CursorOff;
for adr:=$06 to $3F do
begin
DisplayOtherData(adr);
DisplayClockData;
end;
CursorOn;
until keypressed;
KeyStroke:=ReadKey;
ProcessKeystroke;
until KeyStroke in [#27,^C];
gotoXY(1,24);
TextAttr:=White;
ClrEOL;
end;
procedure FindXtCmosPortAddresses;
var i : byte;
begin
CmosPort:=$200;
(* insert stuff here *)
end;
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}
{{ {}
{{ MAIN PROGRAM: CMOS {}
{{ {}
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}
begin
if not ATorBetter then CmosPort:=Unknown;
if paramcount>0 then CheckGivenParameters;
if pos('/A',Globals)>0 then begin CmosPort:=$70; Model:='AT'; end;
if pos('/X',Globals)>0 then begin CmosPort:=Unknown; Model:='PC/XT'; end;
if pos('/P',Globals)>0 then begin
i:=pos('/P',Globals)+2; j:=i;
repeat if Globals[i]='=' then j:=i+1;
inc(i);
until (i>length(Globals)) or (Globals[i]='/');
st:=copy(Globals,j,i-j);
if st[1]<>'$' then if (st[length(st)] in ['h','H','0'])
then val('$'+st,CmosPort,j)
else val(st,CmosPort,j);
if j<>0 then begin writeln('Invalid Cmos port: ',st); CmosPort:=Unknown; end;
if CmosPort<>$70 then Model:='PC/XT';
end;
if CmosPort=Unknown then FindXTCmosPortAddresses;
if (Addresses<>'') or (Addresses<>'')
then begin
for i:=1 to length(Addresses) do
begin
Assign(output,''); Rewrite(output); {allow standard output}
j:=ord(Addresses[i]);
if NeedToChange
then begin
SetCmosRam(j,NewContents);
if pos('/B',Globals)=0
then writeln('Cmos RAM address ',hex(j),' now contains ',CmosRam(j),
' (decimal, = ',hex(CmosRam(j)),' hex)');
end
else if pos('/B',Globals)>0
then writeln(CmosRam(j))
else writeln(CmosRam(j),' (decimal, = ',hex(CmosRam(j)),' hex) at Cmos RAM addr ',
hex(j),' hex.');
end;
end
else InteractiveMode;
end.
Technical Details
Message-ID:
<1990Sep18.153031.9179@canterbury.ac.nz>Path:
arc1-ca.pugleaf.net!utzoo!attcan!uunet!comp.vuw.ac.nz!virtue!canterbury.ac.nz!phys169References:
<1990Aug31.055038.2326@acmis.cmis.co.at>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!comp.vuw.ac.nz!virtue!canterbury.ac.nz!phys169 From: phys169@canterbury.ac.nz Newsgroups: comp.os.msdos.programmer,comp.sys.ibm.pc.hardware Subject: Re: Accessing CMOS RAM Message-ID: <1990Sep18.153031.9179@canterbury.ac.nz> Date: 18 Sep 90 08:29:20 GMT References: <1990Aug31.055038.2326@acmis.cmis.co.at> Organization: University of Canterbury Lines: 553