joe@acmis.cmis.co.at (Johannes Rupp) (08/31/90)
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 these information by an assembler or C program. Thanks for any information, Johannes -- +------------------------------------------------------------------------+ | CMIS Austria | Voice: (++43-222) 98 109 Ext. 110 | | NIELSEN Austria | Voice: (++43-222) 98 110 Ext. 110 | | Moeringgasse 20 | Fax: (++43-222) 98 110 77 | | A-1150 Vienna, Austria | E-Mail: joe@acmis.cmis.co.at | | | ...!mcsun!tuvie!acmis!joe | +------------------------------------------------------------------------+ -- +------------------------------------------------------------------------+ | CMIS Austria | Voice: (++43-222) 98 109 Ext. 110 | | NIELSEN Austria | Voice: (++43-222) 98 110 Ext. 110 | | Moeringgasse 20 | Fax: (++43-222) 98 110 77 |
phys169@canterbury.ac.nz (09/18/90)
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.