Using x86 “protected mode” on our anchor-cluster’s machines A look at some terminal emulation features utilized in the “consoleredirection” mechanism Our remote-access scheme sixteen Core 2 Duo systems ... ‘anchor08’ ‘anchor07’ ‘telnet’ application student workstation ‘anchor06’ ‘anchor05’ ‘pyramid’ ‘anchor04’ CS file-server KVM cable ‘anchor03’ rackmount PC system ‘anchor02’ null-modem serial cables ethernet cables ‘anchor01’ A programming hurdle • When you try to test a ‘boot-time’ program on one of our ‘anchor’ machines, you will encounter a problem when your program enters protected-mode: you can no longer see the current screen-output, nor control your program using keyboard-input, since the normal ‘console redirection’ capability (which ‘telnet’ relies on) has been disabled The console’s output • At boot-time the ‘anchor’ machines use a ‘real-mode’ interrupt-handler in the BIOS to continually transfer information (via the null-modem serial-cable) from the display memory of the local machine to the ‘telnet’ program running on the ‘pyramid’ server • From there it gets sent over the Ethernet network to a student’s computer screen The console’s input • When a student types a keystroke, it gets sent via the network to the ‘telnet’ program running on ‘pyramid’, whereupon it then is forwarded to the ‘anchor’ machine via the null-modem serial-cable connection • This scheme allows us to do our work on the anchor-machines remotely – from our classroom or CS Labs, or even from home The IVT’s role • But this scheme relies on BIOS code that handle’s UART interrupts in ‘real-mode’ • When a program enters ‘protected-mode’, it has to switch from its Table of Interrupt Vectors (used in real-mode) to a different table for dispatching of interrupts to ISRs • At that point we lose all the functionality that BIOS’s interrupt-handlers provided! Our reimplementation • Just as we’ve seen how to regain some of the functionality provided by the real-mode BIOS code, by writing our own protectedmode interrupt-handlers for the keyboard and the timer, we can restore the basic ‘console redirection’ capability by writing our own interrupt-handler for the UART when the CPU is in its ‘protected-mode’ Some details • We need to write “trivial” interrupt-handlers for a few peripherals besides the UART: – For the timer (so we can see something that’s changing on the display screen) – For the keyboard (so we can control when to quit watching our display) – For any ‘spurious’ interrupts that the 8259 PIC delivers to the CPU (so we won’t “crash” due to unwanted General Protection Exceptions) Idea for UART’s RX-interrupts • When our UART receives a new scancode sent by the ‘telnet’ program on ‘pyramid’ we need it to be treated as if it were from the anchor-machine’s keyboard – but the anchor’s keyboard is out-of-our-reach • So we issue a command to the keyboardcontroller to write that scancode into its Output Buffer register (command 0xD2) Idea for UART’s TX-interrupts • When the UART issues its THRE-interrupt (Transmitter Holding Register Empty), we want to output a new byte of information from the anchor-machine’s video memory • The video memory consists of alternating ascii-codes and attribute-codes, requiring distinct treatments for proper display on the remote terminal (or Desktop window) Clearing the screen • Here is an ANSI command-sequence that clears the terminal’s display-screen: char cmd[] = “\033[2J”; int len = strlen( cmd ); write( 1, cmd, len ); Reposition the cursor • Here is an ANSI command-sequence that moves the cursor to row 12, column 40: char cmd[] = “\033[12;40H”; int len = strlen( cmd ); write( 1, cmd, len ); Format of ‘attribute’ bytes VGA text-color attributes byte 7 BLINK 6 BG red 5 BG green 4 BG blue background color 3 FG intense 2 FG red 1 FG green foreground color 0 FG blue ANSI color-codes 0 = black 1 = red 2 = green 3 = brown 4 = blue 5 = magenta 6 = cyan 7 = gray Color-translation table IBM VGA DEC ANSI -------------------------------------------------------------------------------000 = black 000 = black 001 = blue 100 = blue 010 = green 010 = green 011 = cyan 110 = cyan 100 = red 001 = red 101 = magenta 101 = magenta 110 = brown 011 = brown 111 = gray 111 = gray -------------------------------------------------------------------------------- Setting text attributes • Here is an ANSI command-sequence that sets foreground and background colors: char cmd[] = “\033[32;44m”; int len = strlen( cmd ); write( 1, cmd, len ); Cursor visibility commands • Here are ANSI command-sequences that will ‘hide’ or ‘show’ the terminal’s cursor: char hide[] = “\033[?25l”; // lowercase L char show[] = “\033[?25h”; // lowercase H ‘console output’ algorithm unsigned char color_table[ 8 ] = { 0, 4, 2, 6, 1, 5, 3, 7 }; typedef struct { unsigned char ascii, color; } PEL; PEL *vram = (PEL *)0x000B8000; PEL prev = { 0, 0 }; for (int row = 0; row < 24; row++) { printf( “\033[?25l” ); // make the cursor invisible printf( “\033[%d;%dH”, row+1, 1 ); // cursor to row’s beginning for (int col = 0; col < 80; col++) { PEL curr = vram[ row*80 + col ]; if ( curr.color != prev.color ) { int fg = color_table[ (curr.color >> 0)&7 ]; int bg = color_table[ (curr.color >> 4)&7 ]; printf( “\033[%d;%dm”, fg + 30, bg + 40 ); } printf( “%c”, curr.ascii ); prev = curr; } printf( “\033[?25h” ); // make the cursor visible fflush( stdout ); // insure all data written } Programming interface The PC uses eight consecutive I/O-ports to access the UART’s registers 0x03F8 RxD/TxD 0x03F9 0x03FA 0x03FB 0x03FC 0x03FD IER IIR/FCR LCR MCR LSR interrupt enable register receive buffer register and transmitter holding register (also Divisor Latch register) line status register line control register modem control register interrupt identification register and FIFO control register 0x03FE 0x03FF MSR SCR modem status register scratchpad register Modem Control Register 7 6 0 0 5 0 4 3 2 1 0 LOOP BACK OUT2 OUT1 RTS DTR Legend: DTR = Data Terminal Ready (1=yes, 0=no) RTS = Request To Send (1=yes, 0=no) OUT1 = not used (except in loopback mode) OUT2 = enables the UART to issue interrupts LOOPBACK-mode (1=enabled, 0=disabled) Modem Status Register 7 6 DCD RI 5 DSR 4 3 2 1 0 CTS delta DCD delta RI delta DSR delta CTS set if the corresponding bit has changed since the last time this register was read Legend: [---- loopback-mode ----] CTS = Clear To Send (1=yes, 0=no) [bit 0 in Modem Control] DSR = Data Set Ready (1=yes, 0=no) [bit 1 in Modem Control] RI = Ring Indicator (1=yes,0=no) [bit 2 in Modem Control] DCD = Data Carrier Detected (1=yes,0=no) [bit 3 in Modem Control] Line Status Register 7 6 Error in Transmitter idle Rx FIFO 5 THR empty 4 3 Break interrupt Framing error 2 Parity error 1 0 Overrun error Received Data Ready These status-bits indicate errors in the received data This status-bit indicates that the data-transmission has been completed This status-bit indicates that the Transmitter Holding Register is ready to accept a new data byte This status-bit indicates that a new byte of data has arrived (or, in FIFO-mode, that the receiver-FIFO has reached its threshold) Line Control Register 7 6 Divisor Latch access set break 5 stick parity 4 3 2 even parity select parity enable number of stop bits 1 0 word length selection 00 = 5 bits 01 = 6 bits 10 = 7 bits 11 = 8 bits 0 = normal 1 = ‘break’ 0 = not accessible 1 = assessible 0 = 1 stop bit 1 = 2 stop bits 0 = no parity bits 1 = one parity bit 1 = even parity 0 = ‘odd’ parity Interrupt Enable Register 7 0 6 0 5 0 4 3 0 Modem Status change 2 Rx Line Status change 1 0 THR is empty Received data is available If enabled (by setting the bit to 1), the UART will generate an interrupt: (bit 3) whenever modem status changes (bit 2) whenever a receive-error is detected (bit 1) whenever the transmit-buffer is empty (bit 0) whenever the receive-buffer is nonempty Also, in FIFO mode, a ‘timeout’ interrupt will be generated if neither FIFO has been ‘serviced’ for at least four character-clock times FIFO Control Register 7 6 RCVR FIFO trigger-level 00 = 1 byte 01 = 4 bytes 10 = 8 bytes 11 = 14 bytes 5 reserved 4 reserved 3 DMA Mode select 2 XMIT FIFO reset 1 0 RCVR FIFO reset FIFO enable NOTE: DMA is unsupported for the UART on our systems Writing 1 empties the FIFO, writing 0 has no effect Writing 0 will disable the UART’s FIFO-mode, writing 1 will enable FIFO-mode Interrupt Identification Register 7 6 5 0 00 = FIFO-mode has not been enabled 11 = FIFO-mode is currently enabled 4 3 2 1 0 ‘highest priority’ UART interrupt still pending highest 011 = receiver line-status 010 = received data ready 110 = character timeout 001 = Tx Holding Reg empty 000 = modem-status change lowest 1 = No UART interrupts are pending 0 = At least one UART interrupt is pending 0 Response to UART interrupts • In order of highest-to-lowest priority: – 0x06: – 0x04: – 0x0C: – 0x02: – 0x00: Receive errors (input Line Status) Received data (input Rx-Data) FIFO Timeout (input/output Data) THRE (output Tx-Data, or input IIR) Modem state (input Modem Status) How to transmit a byte Read the Line Status Register NO Transmit Holding Register is Empty? YES Write byte to the Transmitter Data Register DONE How to receive a byte Read the Line Status Register NO Received Data is Ready? YES Read byte from the Receiver Data Register DONE Demo-program • We wrote a boot-time program (it’s called ‘xmitvram.s’) that we can run on one of our anchor-cluster machines • It shows how a protected-mode interrupthandler for the serial UART interrupts can send ANSI terminal-control strings (along with ASCII character-codes) to the ‘telnet’ terminal emulator application on ‘pyramid’ In-class exercise • Modify this simple C++ program so that it will print its “Hello” message in colors and be located in the center of the screen: #include <stdio.h> int main( void ) { printf( “Hello, world! \n” ); }