Battler wrote on 2020-10-05, 19:18:
So, if I understand correctly, on PS/2 Model 25-8086 and 30-8086, keyboard, mouse, and RTC alarm are all on IRQ 1, and IRQ 1 always issues INT 71h, whose BIOS handler then demultiplexes it to INT 70h, IRQ 1 INT (with base read from port 63h), and INT 73h. Looks like I'll need to add this special behavior when I add those two machines to my emulator some day, so thanks for the information. :p
That's my understanding from disassembling the PS/2 Model 30 (non-286). The complete keyboard interface is non-standard on that machine. It describes some of the PS/2 Model 30 specific stuff on the mainboard. Together with reverse engineering of BIOS code, I gathered the following information about the "standard" keyboard ports:
- Port 60h is a simple 8-bit latch - no hardware function associated. (Reference page 24: "The output port 60h is used by BIOS to store keystrokes")
- Port 61h behaves as usual (Reference page 24, only bits 0/1 (speaker control) and 4/5 (NMI control) seem to be used)
- Port 62h behaves XT-like (Reference page 25, I don't know whether timer 2 output on pin 5 is PS/2 specific, though)
The actual communication witht the keyboard happens on ports 66 to 6A, though
- Port 66h
- Bit 0: 1=Some error happened on Port 1 (likely parity error)
- Bit 1: 1=Some error happened on Port 2 (likely parity error)
- Bit 2: 0=Keyboard is locked (implementation in software: IRQ1 drops all bytes if this bit is clear)
- Bit 3: software-controlled, no HW function. If set, keyboard on port 1, mouse on port 2; if clear; mouse on port 1, keyboard on port 2.
- Bit 4: must be set for a short time after reading or writing the data byte for port 1 (most likely: releases clock low after RX; maybe allows releasing clock after last TX bit, too?)
- Bit 5: will be cleared for some time before sending to port 1 (most likely: force clock low on port 1)
- Bit 6: must be set for a short time after reading or writing the data byte for port 2
- Bit 7: will be cleared for some time before sending to port 2 (most likely: force clock low on port 2)
- Port 67h: Data buffer (in or out) for Port 1
- Port 68h: Data buffer (in or out) for Port 2
- Port 69h:
- Bit 2: (guessed) 1=allow NMIs
- Bit 3: pulsed high during clock low on TX start on port 1 (most likey: start TX state machine - generate start bit)
- Bit 4: pulsed high during clock low on TX start on port 2 (most likey: start TX state machine - generate start bit)
- Port 6Ah:
- Bit 0: Seems to be some feedback of the density select line from the floppy drive. Possibly due to a bridge between pins 2 and 4 on a drive (HD only?)
- Bit 2: 1 = byte from PS/2 port 1 available on port 67
- Bit 5: 1 = byte from PS/2 port 2 available on port 68
- Port A0h: PS/2 interrupt status
- Bit 2: 1= IRQ1 due to PS/2 port 1
- Bit 3: 1 = IRQ1 due to PS/2 port 2
- Port A1h: Further interrupt controller extension
- Bit 0: 0 = IRQ1 from Real-Time clock enabled. (1 = masked)
Furthermore, the system gate array implements two more ports, that are not keyboard/mouse related:
- Port 65h: Reference page 21: it enables on-board components and controls the direction of the parallel port.
- Port 6Bh: Reference page 35: Controls memory mapping and pinpoints parity errors to either on-board RAM (128K) or SIP/SIMM sockets (512K)
The real-time clock of the PS/2 model 30-8086 is a MM58167B RTC chip. That chip has 32 registers that are directly mapped into I/O space:
- Port E0-EF: Registers 0-15 of the
- Port B0-BF: Registers 16-31 of the MM58167B RTC chip
That chip has some RAM that is used to store the alarm time, but it is implemented in a creative way by IBM to alleviate some shortcomings. One main principle of abusing the alarm time RAM for general purpose data storage is understanding that nibble values C, D,E and F all mean "don't care". The chip works with nibbles due to its BCD nature. Each nibble thus allows to store two general-purpose bits that don't affect the operation of the RTC. The shortcomings of that RTC and the associated solutions are:
- As the AT BIOS (which is compatible regarding RTC alarms) only supports seconds, minutes, hours and day-of-week for alarms, only registers 0Ah (seconds), 0Bh (minutes), 0Ch (hours) and 0Dh (day-of-week) are used for alarm time. This leaves registers 8 (milliseconds), 9 (centiseconds), 0Eh (day-of-month) and 0Fh (month) open for general-purpos use.
- The chip has no year support at all. The current year (two digits, BCD) is stored (directly, not "hidden" in C-F) in the alarm centiseconds byte, register 9. Also, a century bit is stored in the low nibble of the alarm day-of-month (register 0Eh). Bit 1 in that register is unused and always zero. If the century bit is set, the century is "odd", i.e. 19xx. If the century bit is clear, the century is "even", i.e. 20xx.
- Because the year is stored in RAM, it is not automatically updated when the date wraps from Dec 31st to Jan 1st. IBM introduces the concept of "RTC maintainance" to fix up the RTC in different circumstances. Maintainance happens (during POST) once per month at maximum. The mont number of the last RTC maintainance is hidden in the alarm milliseconds nibble (register 8, upper nibble) and day-of-week nibble (register number 0Dh, lower-nibble), by putting C-to-F-encoding the two low bits of the month number in the low nibble of register 0Dh and C-to-F-encoding the high to bits of the month number into the high nibble of register 8. If the last maintainance month is not equal to the current month, clock maintainance is performed and the last maintainance month is updated inside the RTC chip. If during maintainance the current month is lower than the last maintainance month, the year is incremented.
- As the RTC doesn't know about years, it doesn't handle leap years. If during clock maintaince, it is detected that a leap day happened since the last maintaince, the RTC date is moved back one day. In case the maintainance happens on supposed Mar 1st (i.e. Feb 29th), the date is encoded as Mar, 0th. This enables "last maintaince March" to work as intended and prevent multiple adjustment of the RTC on multiple boots, and I don't know whether the RTC correctly jumps to Mar 1st after Feb 29th, as it does for Feb 28th.
- There is no way to find out whether the battery is still good and the time/date is valid. A nibble-wise checksum is calculated over register 8 (high nibble only), registers 9 to 0Ch and the low nibble of register 0Eh. The checksum (which is cut to 6 bits, which is still wider than a nibble) is XORed with 0x15, and hidden in the alarm day-of-month register (0Eh, high nibble) and the alarm month register (0Fh). The high two bits of the xorred checksum is stored in register 0Eh, high nibble. The central two bits (of the xorred 6-bit checksum) is stored in register 0Fh, high nibble and the low two bits are stored into register 0Fh, low nibble. All three two-bit pieces are encoded as 0Ch to 0Fh.
Setting the alarm works through INT 1Ah,AL=06 and the alarm callback is on INT 4Ah, just as on the IBM AT.
Ignore the IBM
specification suggestion about IRQ sharing. I never saw any hardware implementing that scheme.