Ein '''Complementary Metal Oxide Semiconductor''' (= '''CMOS''') ist eigentlich ein elektronischer Baustein, aber im Bereich der Betriebssystemprogrammierung ist damit meistens der Batterie-gepufferte SRAM eines [[x86]]- bzw. [[x86-64]]-Computers gemeint. In diesem speichert das [[BIOS]] Informationen beispielsweise über die angeschlossene Hardware. Außerdem wird darin die momentane Uhrzeit und das Datum gespeichert und von einer [[RTC | Echtzeituhr]] aktualisiert. == Aufbau == Im CMOS werden folgende jeweils 1 Byte große Felder gespeichert. Die mit ''BCD'' markierten Einträge sind standardmässig im [[BCD | BCD-Format]] kodiert. Die Kodierung kann aber auch im [[CMOS#0x0B - Statusregister B | Statusregister B]] (Bit 2) geändert werden. Dabei ist allerdings zu beachten, dass die momentanen Werte im Register an die neue Kodierung angepasst werden müssen und dies nicht von selbst geschieht.
{| {{prettytable}} |- ! Offset ! Beschreibung |- | 0x00 || Sekunde (BCD) |- | 0x01 || Alarmsekunde (BCD) |- | 0x02 || Minute (BCD) |- | 0x03 || Alarmminute (BCD) |- | 0x04 || Stunde (BCD) |- | 0x05 || Alarmstunde (BCD) |- | 0x06 || Wochentag (BCD) |- | 0x07 || Tag des Monats (BCD) |- | 0x08 || Monat (BCD) |- | 0x09 || Jahr (letzten zwei Stellen) (BCD) |- | 0x0A || [[#0x0A - Statusregister A | Statusregister A]] |- | 0x0B || [[#0x0B - Statusregister B | Statusregister B]] |- | 0x0C || [[#0x0C - Statusregister C | Statusregister C]] (schreibgeschützt) |- | 0x0D || [[#0x0D - Statusregister D | Statusregister D]] (schreibgeschützt) |- | 0x0E || [[#0x0E - POST-Diagnoesstatusbyte | POST-Diagnosestatusbyte]] |- | 0x0F || [[#0x0F - Shutdown-Statusbyte | Shutdown-Statusbyte]] |- | 0x10 || [[#0x10 - Typ der Diskettenlaufwerke | Typ der Diskettenlaufwerke]] |- | 0x11 || reserviert |- | 0x12 || [[#0x12 - Typ der Festplattenlaufwerke | Typ der Festplattenlaufwerke]] |- | 0x13 || reserviert |- | 0x14 || [[#0x14 - Gerätebyte | Gerätebyte]] |- | 0x15 || Größe des Basisspeichers in kB (niederwertiges Byte) |- | 0x16 || Größe des Basisspeichers in kB (höherwertiges Byte) |- | 0x17 || Größe des Erweiterungsspeichers in kB (niederwertiges Byte) |- | 0x18 || Größe des Erweiterungsspeichers in kB (höherwertiges Byte) |- | 0x19 || Erweiterungsbyte 1. Festplatte |- | 0x1A || Erweiterungsbyte 2. Festplatte |- | 0x1B - 0x2D || Reserviert / vom BIOS abhängig |- | 0x2E || [[#0x2E & 0x2F - CMOS-Prüfsumme | CMOS-Prüfsumme]] (höherwertiges Byte) |- | 0x2F || [[#0x2E & 0x2F - CMOS-Prüfsumme | CMOS-Prüfsumme]] (niederwertiges Byte) |- | 0x30 || Erweiterter Speicher (niederwertiges Byte) |- | 0x31 || Erweiterter Speicher (höherwertiges Byte) |- | 0x32 || Jahrhundert (BCD) |- | 0x33 - 0x3F || Reserviert / vom BIOS abhängig |}

=== 0x0A - Statusregister A ===
{| {{prettytable}} style="width:75%" |- ! Bit ! Beschreibung |- | 7 || Zeit-Update-Zyklus (1 = Update läuft => Zeitangaben undefiniert) |- | 6-4 || Zeit-Basis (Standard = 010b = 32768 Hz) |- | 3-0 || {| {{prettytable}} style="width:95%; text-align:center" |+ Auswahl-Bits zum Einstellen der Wiederholrate des periodischen Interrupts. Frequenz = 65536 Hz / 2Rate-Bits !Rate-Bits !Teiler !Frequenz !Periode !Bemerkung |- | 0000b || 0 || - || - || kein Interrupt |- | 0011b || 23=8 || 8192 Hz || 122.070 µs || Minimum |- | 0100b || 24=16 || 4096 Hz || 244.141 µs || |- | 0101b || 25=32 || 2048 Hz || 488.281 µs || |- style="background:silver" | 0110b || 26=64 || 1024 Hz || 976.562 µs || Standard |- | 0111b || 27=128 || 512 Hz || 1.953125 ms || |- | 1000b || 28=256 || 256 Hz || 3.90625 ms || |- | 1001b || 29=512 || 128 Hz || 7.8125 ms || |- | 1010b || 210=1024 || 64 Hz || 15.625 ms || |- | 1011b || 211=2048 || 32 Hz || 31.25 ms || |- | 1100b || 212=4096 || 16 Hz || 62.5 ms || |- | 1101b || 213=8192 || 8 Hz || 125 ms || |- | 1110b || 214=16384 || 4 Hz || 250 ms || |- | 1111b || 215=32768 || 2 Hz || 500 ms || |} |}

=== 0x0B - Statusregister B ===
{| {{prettytable}} |- ! Bit ! Beschreibung |- | 7 || Zeit-Update (0 = Zeit aktualisieren, 1 = Zeit-update anhalten) |- | 6 || Periodischer Interrupt mit Rate aus Statusregister A (0 = deaktiviert (Standard), 1 = aktiviert) |- | 5 || Alarm Interrupt wenn Uhrzeit = Alarmzeit (0 = deaktiviert (Standard), 1 = aktiviert) |- | 4 || Update-Interrupt (0 = deaktiviert (Standard), 1 = aktiviert) |- | 3 || Rechtecks-Frequenz mit Rate aus Statusregister A erzeugen (0 = deaktiviert (Standard), 1 = aktiviert) |- | 2 || Daten Modus (0 = BCD, 1 = Binär) |- | 1 || 24 Stundenformat (0 = 12 Stunden, 1 = 24 Stunden (Standard)) |- | 0 || Sommerzeit (0 = deaktiviert (Standard), 1 = aktiviert) |}

=== 0x0C - Statusregister C ===
{| {{prettytable}} |- ! Bit ! Beschreibung |- | 7 || Interrupt-Request (1 = RTC hat Interrupt angefordert) |- | 6 || IRQ-Quelle ist periodischer Interrupt (0 = nein, 1 = ja) |- | 5 || IRQ-Quelle ist Alarm-Interrupt (0 = nein, 1 = ja) |- | 4 || IRQ-Interrupt ist Update-Interrupt (0 = nein, 1 = ja) |- | 3-0 || Reserviert |}

=== 0x0D - Statusregister D===
{| {{prettytable}} |- ! Bit ! Beschreibung |- | 7 || Gültigkeit der Daten im CMOS (0 = ungültig, 1 = Daten sind gültig) |- | 6-0 || Reserviert |}

=== 0x0E - [[POST]]-Diagnosestatusbyte ===
{| {{prettytable}} |- ! Bit ! Beschreibung |- | 7 || Stromversorgung (0 = OK, 1 = Stromversorgung war unterbrochen) |- | 6 || CMOS-Prüfsumme (0 = OK, 1 = Prüfsumme falsch) |- | 5 || Konfiguration im CMOS stimmt mit ermittelten Werten überein (0 = OK, 1 = nein) |- | 4 || Speichergröße im CMOS == ermittelte Speichergröße (0 = ja, 1 = nein) |- | 3 || Laufwerk bzw Controllerfehler (0 = OK, 1 = Fehler) |- | 2 || Uhrzeit gültig (0 = OK, 1 = Fehler) |- | 1 || Fehler bei Adapterinitialisierung (0 = OK, 1 = Fehler) |- | 0 || Timeout beim Adapter-ID lesen |}

=== 0x0F - Shutdown-Statusbyte === 80286-Prozessoren konnten nicht vom [[Protected Mode]] in den Realmode zurückgeschaltet werden. Zu diesem Zweck war ein Prozessor-Reset nötig. Das Shutdown-Statusbyte dient dazu dem BIOS anzuzeigen, ob der PC neu gebootet oder der Prozessor nur im Realmode neu gestartet werden soll. {| {{prettytable}} !Wert !Beschreibung |- | 0x00 || Normale Ausführung des POST |- | 0x01 || Chip Satz Initialisierung für Real Mode |- | 0x02 || Power-On Reset durchführen |- | 0x03 || Power-On Reset durchführen |- | 0x04 || int 0x19-reboot |- | 0x05 || EOI an PIC ausgeben, Tastatur resetten, Sprung zur Addresse [0x0040:0x0067] |- | 0x06 || Sprung zur Addresse [0x0040:0x0067] ohne EOI |- | 0x07 || zurück zu Int 0x15, Funktion 0x87 (Block im Extenden Memory verschieben) |- | 0x08 || zurück zum POST Speicher Test |- | 0x09 || zurück zu Int 0x15, Funktion 0x87 (Block im Extenden Memory verschieben) |- | 0x0A || JMP zu [0x0040:0x0067] |- | 0x0B || IRET zu [0x0040:0x0067] |- | 0x0C || RET zu [0x0040:0x0067] |}
=== 0x10 - Typ der [[FDC | Diskettenlaufwerke]] === In diesem Byte wird der Typ der [[FDC | Diskettenlaufwerke]] gespeichert, wobei in den Bits 4-7 der Typ des ersten Laufwerks gespeichert ist und in den Bits 0-3 der Typ des zweiten Laufwerks. Die Typen sind wie folgt kodiert: 0000b = Laufwerk nicht vorhanden 0001b = 5 1/4'' - 360 kB 0010b = 5 1/4'' - 1.2 MB 0011b = 3 1/2'' - 720 kB 0100b = 3 1/2'' - 1.44 MB (sollte eigentlich heutzutage der Standard sein) 0101b - 1111b sind unbenutzt
=== 0x12 - Typ der Festplattenlaufwerke === In diesem Byte wird der Typ der Festplattenlaufwerke gespeichert, wobei in den Bits 4-7 der Typ des ersten Laufwerks gespeichert ist und in den Bits 0-3 der Typ des zweiten Laufwerks. Die Typen sind wie folgt kodiert: 0000b = Laufwerk nicht vorhanden 0001b - 1110b = Typen 1-14 1111b = Typ laut Erweiterungsbyte der Festplatte
=== 0x14 - Gerätebyte === Hier werden Informationen über die allgemeine Hardwarekonfiguration des PCs gespeichert. {| {{prettytable}} ! Bits || 7-6 || 5-4 || 3 || 2 || 1 || 0 |- | || Anzahl Floppys || Display-Typ || Display vorhanden? || Keyboard vorhanden? || Mathematischer Koprozessor vorhanden? || Floppy |} Anzahl Floppys
Auf diesen Wert nur verlassen wenn Floppy=1
00b 1 Floppy-Laufwerk 01b 2 Floppy-Laufwerke Display-Typ 00b BIOS-Display-Adapter 01b CGA (40 Spalten) 10b CGA (80 Spalten) 11b Monochrom === 0x2E & 0x2F - CMOS-Prüfsumme === Die CMOS-Prüfsumme ist eine 16 Bit grosse, byteweise Summe der Werte der Bytes von 0x10 bis 0x2D. == Programmierung == Die Programmierung des CMOS ist denkbar einfach: Die sieben niederwertigen Bits des I/O Ports 0x70 dienen als Offset (siehe Tabelle). Das höchste Bit sollte nicht verändert werden, es dient als [[Non_Maskable_Interrupt | NMI]]-Maskierungsregister. Durch Lesen bzw. Schreiben des I/O Ports 0x71 kann dann der Wert an diesem Offset gelesen bzw. geschrieben werden. === Assembler Beispielcode=== ;Ein Makro um ein Byte aus dem CMOS auszulesen ;Der einzige Parameter ist das Offset des Bytes welches gelesen werden soll ;Rückgabe ist das gelesene Byte in AL %macro read_byte 1 in al, 0x70 ;Bit 7 des Ports sichern and al, 10000000b or al, %1 ;Offset (Parameter 1) dazumixen out 0x70, al ;Offset setzen in al, 0x71 ;Byte lesen %endmacro === C/C++ Beispielcode === #define CMOS_PORT_ADDRESS 0x70 #define CMOS_PORT_DATA 0x71 /** * Liest ein Byte aus den CMOS * @param offset Offset im CMOS * @return Gelesener Wert */ uint8_t cmos_read(uint8_t offset) { uint8_t tmp = inb(CMOS_PORT_ADDRESS); outb(CMOS_PORT_ADDRESS, (tmp & 0x80) | (offset & 0x7F)); return inb(CMOS_PORT_DATA); } /** * Schreibt ein Byte in das CMOS * @param offset Offset im CMOS * @param val Zu schreibender Wert */ void cmos_write(uint8_t offset,uint8_t val) { uint8_t tmp = inb(CMOS_PORT_ADDRESS); outb(CMOS_PORT_ADDRESS, (tmp & 0x80) | (offset & 0x7F)); outb(CMOS_PORT_DATA,val); } == Weblinks == * [http://www.nondot.org/sabre/os/files/MiscHW/RealtimeClockFAQ.txt Real Time Clock / CMOS Setup Reference] (''englisch'') * [http://www.bioscentral.com/misc/cmosmap.htm CMOS-Aufbau] (''englisch'') * [http://bochs.sourceforge.net/doc/docbook/development/cmos-map.html Bochs's CMOS map] (''englisch'') * [http://www.bios-info.de/4p92x846/iscmos.htm BIOS Kompendium] (''deutsch'') [[Category:Hardware]] [[en:CMOS]]