$TITLE('iRMX II/III Console Driver for AT Platform') $OPTIMIZE(3) $COMPACT( -CONST IN CODE- HAS xconsole) $IF r_32 $LARGE( BUILDER $EXPORTS s120$console$data$t; $EXPORTS s120$video$data$t) $ENDIF /*****************************************************************************\ * * Copyright Intel Corporation 1987, 1988 * All rights reserved * * For Intel customers licensed for the iRMX II/III System 120 under an * Intel Software License Agreement, this source code and object code * derived therefrom are licensed for use on a single central processing * unit for internal use only. Necessary backup copies are permitted. * Object Code derived from this source code is a Class I software product * under the Intel Software License Agreement and is subject to the terms * and conditions of that agreement. * * For the right to make incorporations, or to transfer this software to * third parties, contact Intel corporation. * \****************************************************************************/ /*****************************************************************************\ * Title: CONSOLE.P28 * * History: * * 09/20/89 iRMX II/III System 120 device driver example - version 1.0 * \****************************************************************************/ $eject /* * Abstract: * * This driver provides an interface between the terminal support code and * the system 120 keyboard and monitor. * * This source file contains the following procedures: * * i120$console$init * i120$console$finish * i120$console$setup * i120$console$check * i120$console$utility * i120$console$output$task * i120$screen$on$off * i120$flush$kb$int * i120$set$cursor * i120$set$video$ram$ptr * i120$get$adapter$type */ xconsole : DO; $include (:F1:comon.lit) $include (:F1:nutyp.lit) $include (:F0:error.lit) $include (:F0:xtsdtn.lit) $include (:F0:nuclus.ext) $include (:F1:console.lit) $include (:F1:keyboard.lit) /* * The following procedure is called when the Keyboard Interrupt character is * detected. The object module is included in the system 120 device driver * library ( /rmx386/ios/xdr120.lib ) */ $IF r_32 sdm_break: PROCEDURE EXTERNAL; END sdm_break; $ELSE sdm_break: PROCEDURE (level, cdata_t, driver_ih) EXTERNAL; DECLARE level WORD, cdata_t TOKEN, driver_ih SIZE$OF$OFFSET; END sdm_break; $ENDIF /* * Declaration for Delay Procedure. The object module is included in the * device driver library ( /rmx386/ios/xcmdrv.lib ) */ g$delay: PROCEDURE(unit, delay_factor) EXTERNAL; DECLARE unit WORD, delay_factor WORD; END g$delay; $IF r_32 DECLARE s120$console$data$t SELECTOR EXTERNAL, s120$video$data$t SELECTOR EXTERNAL; $ENDIF $SUBTITLE('Console Init procedure') /* * Title: i120$console$init * * Abstract: * This procedure creates and initializes the keyboard data segment, * creates the output task and other driver resources. This procedure * sets the status field in the TSC data structure as follows: * * - If initialization is successful, set status to E$OK. * - If error, set status to E$IO. * * Note: This procedure does not initialize the display adapter. * It is assumed that the display adapter is initialized * by the initialization routine in the BIOS ROM after the * system is powered on. * * This procedure reads the index register address field * in the shared data structure and determines the type of * display adapter (EGA or MDA) is being used as a console * output device. * * Calling sequence: CALL i120$console$init(tsc$data$ptr); * * Parameters: * tsc$data$ptr POINTER to the beginning of the TSC data area. * * Calls: * RQ$CREATE$SEGMENT RQ$DELETE$SEGMENT * RQE$CREATE$DESCRIPTOR * RQ$CREATE$MAILBOX * RQ$CREATE$TASK RQ$DELETE$TASK * RQ$GET$TASK$TOKENS * RQ$CATALOG$OBJECT RQ$UNCATALOG$OBJECT * G$DEALY RQ$GET$PRIORITY * * i120$set$cursor * */ $eject i120$console$init : PROCEDURE (ddata$sel,ddata$offset) PUBLIC REENTRANT; DECLARE ddata$sel SELECTOR, ddata$offset SIZE$OF$OFFSET, ad BASED ddata$sel DRIVER$DATA$STRUCT, ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, ads$word WORD AT (@ads$sel); DECLARE seg TOKEN, kb$seg BASED seg KB$SEG$MODEL, root$job TOKEN, board$type BYTE, temp BYTE; DECLARE mon$present BYTE, mon$t TOKEN, mon$info BASED mon$t STRUCTURE (monitor BYTE), NO$MONITOR LITERALLY '0'; /* * Set up pointer to the shared data structure. */ $IF r_32 ads$sel = SELECTOR(@s120$console$data$t); $ELSE ads$word = ABSOLUTE$ADDR$WORD; $ENDIF $eject /* * initialize some fields in the driver data structures */ ad.buffered$device = TRUE; ad.output$buffer$size = 0FFFFH; ad.raw$size = DEFAULT$RAW$SIZE; ad.raw$in = 0; ad.raw$out = 0; ad.crt$len = SCREEN$LENGTH; /* see console data structure */ ad.console$state = 0; /* in the TSC data area */ ad.routine$index = 0; ad.screen$on = TRUE; ad.del$const = 10; /* initial value */ ad.int3$pending = 0; ads.table$offset = 0; /* normal case */ ads.display$w = DEFAULT$ATTRIB; /* default screen color is white on black */ ads.column = 0; ads.row = 0; ads.kb$flag = 0; /* * Get the token for the root job */ root$job = RQ$GET$TASK$TOKENS( ROOT$JOB$TOKEN, @ad.status ); /* * Check if debug monitor present. If there is one, assume it is SDM. */ mon$t = rq$lookup$object (root$job, @(9,'RQMONITOR'), 0, @ad.status ); IF (ad.status = E$OK) AND (mon$info.monitor <> NO$MONITOR) THEN mon$present = TRUE; ELSE mon$present = FALSE; ad.mon$present = mon$present; /* * Look up for the delay constant in the Root Object Directory. */ sys$info$sel = rq$lookup$object (root$job,@(9,'RQSYSINFO'), 0,@ad.status ); /* * Only use the delay constant in the sys$info structure if it is valid * otherwise, use the default value */ IF ad.status = E$OK THEN ad.del$const = sys$info.delay$const; /* * Create raw input buffer */ ad.raw$data$p = BUILD$PTR(RQ$CREATE$SEGMENT(DEFAULT$RAW$SIZE,@ad.status), 0); IF ad.status <> E$OK THEN GOTO INIT$ERROR$1; /* * Create a segment for the keyboard translation table */ seg = RQ$CREATE$SEGMENT( SIZE(kb$seg), @ad.status ); IF ad.status <> E$OK THEN GOTO INIT$ERROR$2; $eject /* * Initialize the keyboard translation table */ kb$seg.n$keys = N$KEY$CODES; CALL MOVB( @types, @kb$seg.types, N$KEY$CODES ); CALL MOVB( @values, @kb$seg.values, N$KEY$CODES * 10); ads.kb$seg$sel = seg; /* * Catalog the keyboard table segment in the root job directory */ CALL RQ$CATALOG$OBJECT( root$job, seg, @(9,'PC_KB_TAB'), @ad.status ); IF ad.status <> E$OK THEN GOTO INIT$ERROR$3; /* * Create the output mailbox */ ad.output$mbx = RQ$CREATE$MAILBOX( FIFO$Q, @ad.status ); IF ad.status <> E$OK THEN GOTO INIT$ERROR$3; /* * Determine the type of the display adapter is being used by the SDM, then * set up the video RAM pointer for the driver. * * Important: The shared data structure is located at absolute address 400:0H * (28:0 in protected mode). The value in the index register * address field [in this structure] is evaluated to determine the * type of the display adapter that the driver should be using. * * 32 bits!!! BLD FILE will handle placement of shared data structure - * reference to it will now be via a SELECTOR named * s120$console$data$t */ $eject IF mon$present AND ((ads.index$reg$addr = MONO$INDEX$REG) OR (ads.index$reg$addr = COLOR$INDEX$REG)) THEN /* * Means that the SDM is using one of the display adapters and that the * shared data structure is already set up. */ DO; IF ads.index$reg$addr = MONO$INDEX$REG THEN /* * set video RAM pointer to the starting address of the display * RAM on the monochrome display adapter. */ CALL i120$set$video$ram$ptr(ddata$sel, ads$sel, MONO$CARD, TRUE); ELSE /* * set video RAM pointer to the starting address of the display * RAM on the color (or EGA) card. */ CALL i120$set$video$ram$ptr(ddata$sel, ads$sel, COLOR$CARD, TRUE); END; ELSE /* * No SDM, or SDM may have been using the serial port. */ DO; board$type = i120$get$adapter$type; DO CASE board$type; /* CASE 0 - there is no display adapter in the system */ DO; ad.status = E$IO; END; /* CASE 1 - only the mono adapter is present */ DO; CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,MONO$CARD,FALSE); END; /* CASE 2 - only the color adapter (or EGA) is present */ DO; CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,COLOR$CARD,FALSE); END; /* CASE 3 - both color and mono display adapter are present */ DO; CALL i120$set$video$ram$ptr(ddata$sel,ads$sel,COLOR$CARD,FALSE); END; END; /* CASE board$type */ END; $eject IF ad.status <> E$OK THEN GOTO INIT$ERROR$3; /* * Blank the display by filling the display RAM with the data pattern * stored in ads.display$w */ CALL SETW( ads.display$w, ads.video$ram$p, ad.crt$len ); /* * Set the cursor to upper left corner (0,0) */ CALL i120$set$cursor(ads$sel); /* * Issue a reset command to start the keyboard reset and a keyboard * internal self test */ OUTPUT(KEYB$DATA$PORT) = KB$RESET$CMD; /* * Wait for the keyboard self test to complete and do a dummy read */ CALL g$delay(10, ad.del$const); /* some delay */ temp = INPUT(KEYB$DATA$PORT); /* * get the task priority. This priority is the same as the * priority level assigned in the DUIB. */ temp = RQ$GET$PRIORITY( THIS$TASK$PRIO, @ad.status ); IF ad.status <> E$OK THEN GOTO INIT$ERROR$3; /* * create an output task, use the same priority */ ad.output$task = RQ$CREATE$TASK( temp, @i120$console$output$task, ddata$sel, NIL, 512, 0, @ad.status ); IF ad.status = E$OK THEN DO; ad.status = E$OK; /* initialization is done */ RETURN; END; INIT$ERROR$4: CALL RQ$DELETE$TASK( ad.output$task, @ad.status ); INIT$ERROR$3: seg = RQ$LOOKUP$OBJECT( root$job, @(9,'PC_KB_TAB'), 0, @ad.status ); CALL RQ$UNCATALOG$OBJECT( root$job, @(9,'PC_KB_TAB'), @ad.status ); CALL RQ$DELETE$SEGMENT( seg, @ad.status ); INIT$ERROR$2: CALL RQ$DELETE$SEGMENT( ads.kb$seg$sel, @ad.status ); INIT$ERROR$1: CALL RQ$DELETE$SEGMENT( SELECTOR$OF(ad.raw$data$p), @ad.status ); ad.status = E$IO; /* inform the TSC that the initialization has failled */ RETURN; END i120$console$init; $SUBTITLE('Console Finish procedure') /* * Title: i120$console$finish * * Abstract: * The terminal support code calls this procedure when a user detaches * the console. When called, this procedure deletes the output task, * the screen save task, the output mailbox, and other driver resources * created by the i120$console$init procedure. * * Calling sequence: CALL i120$console$finish(tsc$data$ptr); * * Parameters: * tsc$data$ptr POINTER to the beginning of the TSC data area. * * Calls: * RQ$DELETE$TASK * RQ$DELETE$MAILBOX * RQE$DELETE$DESCRIPTOR * RQ$DELETE$SEGMENT * RQ$GET$TASK$TOKENS * RQ$LOOKUP$OBJECT * RQ$UNCATALOG$OBJECT * */ i120$console$finish : PROCEDURE(ddata$sel,ddata$offset) PUBLIC REENTRANT; DECLARE ddata$sel SELECTOR, ddata$offset SIZE$OF$OFFSET, ad BASED ddata$sel DRIVER$DATA$STRUCT, ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, ads$word WORD AT (@ads$sel), status WORD, root$job TOKEN, seg TOKEN; $eject /* * Set up pointer to the shared data structure. */ $IF r_32 ads$sel = SELECTOR(@s120$console$data$t); $ELSE ads$word = ABSOLUTE$ADDR$WORD; $ENDIF /* * Delete the output task, and other resources created by the * initialization routine. */ CALL RQ$DELETE$TASK( ad.output$task, @status ); CALL RQ$DELETE$MAILBOX( ad.output$mbx, @status ); CALL RQ$DELETE$SEGMENT( SELECTOR$OF(ad.raw$data$p), @status ); /* * Remove the descriptor for the video RAM. */ CALL RQE$DELETE$DESCRIPTOR( SELECTOR$OF(ads.video$ramp), @status ); /* * Get the token of the keyboard data segment */ root$job = RQ$GET$TASK$TOKENS( ROOT$JOB$TOKEN, @status ); seg = RQ$LOOKUP$OBJECT( root$job, @(9,'PC_KB_TAB'), 0, @status ); /* * Uncatalog the keyboard data table, delete the keyboard data segment */ CALL RQ$UNCATALOG$OBJECT( root$job, @(9,'PC_KB_TAB'), @status ); CALL RQ$DELETE$SEGMENT( seg, @status ); END i120$console$finish; $SUBTITLE('Console Setup procedure') /* * Title: i120$console$setup * * Abstract: * This procedure performs the attach device and detach device * operations according to both the buffered$device and the * buff$output$state flags (see the iRMX device driver user's * guide for details). * * Calling sequence: CALL i120$console$setup(tsc$data$ptr); * * Parameters: * tsc$data$ptr POINTER to the beginning of the TSC data area. * * Calls: * NONE */ i120$console$setup : PROCEDURE(ddata$sel,ddata$offset ) PUBLIC REENTRANT; DECLARE ddata$sel SELECTOR, ddata$offset SIZE$OF$OFFSET, ad BASED ddata$sel DRIVER$DATA$STRUCT; IF NOT(ad.buffered$device) THEN DO; IF (ad.buff$output$state AND OUTPUT$SEMAPHORE) = 0 THEN DO; /* case 1: Attach, recognized by Buffer$device = False and Output$semaphore = 0 */ ad.raw$out = ad.raw$in; ad.buffered$device= TRUE; ad.buff$output$state = ad.buff$output$state OR OUTPUT$SEMAPHORE; ad.special$modes = 0; ad.high$water$mark= 0; ad.low$water$mark = 0; ad.fc$on$char = 0; ad.fc$off$char = 0; CALL SETB(0, @ad.special$char, 4); END; ELSE DO; /* case 2: Detach, recognized by Buffer$device = False and Output$semaphore = 1 */ ad.buff$output$state = ad.buff$output$state AND NOT(OUTPUT$SEMAPHORE); END; END; /* case 3: Change attributes (not relevant here); recognized by Buffer$device=True */ END i120$console$setup; $SUBTITLE('Console Check procedure') /* * Title: i120$console$check * * Abstract: * This procedure handles interrupt generated by the keyboard. * When called, this procedure reads the keyboard scan code. * If the code read from the keyboard is not valid, then the * interrupt$type field in the TSC data area is set to zero * to indicate the keyboard interrupt did not occurred. * * If the scan code is valid, translate the code into an ASCII * character and copy the character into the raw input buffer and * signal the Terminal Support Code to process the character. * Else, process special character read from the keyboard. * * Calling sequence: CALL i120$console$check(tsc$data$ptr); * * Parameters: * tsc$data$ptr POINTER to the beginning of the TSC data area. * * Calls: * TIME */ i120$console$check : PROCEDURE(ddata$sel,ddata$offset) PUBLIC REENTRANT; DECLARE ddata$sel SELECTOR, ddata$offset SIZE$OF$OFFSET, ad BASED ddata$sel DRIVER$DATA$STRUCT, ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, ads$word WORD AT (@ads$sel), dinfo$p POINTER, dinfo BASED dinfo$p DINFO$SYP120$CONSOLE, raw$buffer$p POINTER, raw$buffer BASED raw$buffer$p (1) BYTE, seg TOKEN, kb$seg BASED seg KB$SEG$MODEL, $eject level WORD, status WORD, index WORD, temp WORD, scan$code BYTE, function$key BYTE, char BYTE, raw$out BYTE, raw$in BYTE, old$LED$status BYTE; dinfo$p = ad.dinfo$p; level = dinfo.interrupt$level; /* * Set up pointers to the shared data structure and the keyboard * translation table */ $IF r_32 ads$sel = SELECTOR(@s120$console$data$t); $ELSE ads$word = ABSOLUTE$ADDR$WORD; $ENDIF seg = ads.kb$seg$sel; /* * if the display is OFF then turn it ON */ IF NOT(ad.screen$on) THEN CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$ON); /* * Save the current keyboard LED's status * ( the NumLock, CapsLock, and ScrollLock indicator ). */ old$LED$status = ads.kb$flag; /* * Read the keyboard scan code */ scan$code = INPUT(KEYB$DATA$PORT); $IF r_32 IF ad.int3$pending THEN DO; /* * Key-release interrupt for cntl-alt-break; instruct SDM to break at * the CS:IP of the interrupted task */ ad.int3$pending = FALSE; call sdm_break; GOTO not$a$real$char; END; $ENDIF IF (ad.console$state AND raw$mode) <> 0 THEN DO; char = scan$code + 20H; IF scan$code = CLOSE$ALT$KEY THEN ad.console$state= ad.console$state AND NOT raw$mode; GOTO char$found; END; $eject char = scan$code AND 7FH; IF (char = 0) OR (char > HIGHEST$SCAN$CODE) THEN GOTO not$a$real$char; /* * If this is a function key then set a flag to indicate that the driver will * have to send a NUL character to the user before the actual function code. */ IF ((char >= NORM$FUNCT$MIN) AND (char <= NORM$FUNCT$MAX)) OR ((char >= SHCT$FUNCT$MIN) AND (char <= SHCT$FUNCT$MAX)) THEN function$key = TRUE; ELSE function$key = FALSE; /* * Index tells the meaning of the key being pressed. */ index = kb$seg.types(char); /* * Adjust the index if the key is released */ IF scan$code >= BREAK$CONTACT THEN index = index + BREAK$TYPE$OFFSET; /* * Get the real character from the keyboard translation table */ char = kb$seg.values(char + ads.table$offset); $eject DO CASE index; /* CASE 0 - simple char, just return value */ DO; GOTO char$found; END; /* CASE 1 - ctl key is pressed */ DO; IF (ads.kb$flag AND CTL$FLAG) = 0 AND (ads.kb$flag AND ALT$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR CTL$FLAG; ads.table$offset = 8 * N$KEY$CODES; END; END; /* CASE 2 - left or right shift key is pressed */ DO; IF (ads.kb$flag AND SHIFT$FLAG) = 0 AND (ads.kb$flag AND ALT$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR SHIFT$FLAG; IF (ads.kb$flag AND CTL$FLAG) = 0 THEN ads.table$offset = ads.table$offset + N$KEY$CODES; END; END; /* CASE 3 - ALT key is pressed */ DO; IF (ads.kb$flag AND CTL$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR ALT$FLAG; ads.table$offset = 9 * N$KEY$CODES; END; ELSE ads.kb$flag = ads.kb$flag OR ALT$FLAG; END; /* CASE 4 - CapsLock key is pressed */ DO; IF (ads.kb$flag AND CTL$FLAG) = 0 AND (ads.kb$flag AND ALT$FLAG) = 0 THEN DO; IF (ads.kb$flag AND CAPS$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR CAPS$FLAG; ads.table$offset = ads.table$offset + 2 * N$KEY$CODES; END; ELSE DO; ads.kb$flag = ads.kb$flag AND NOT CAPS$FLAG; ads.table$offset = ads.table$offset - 2 * N$KEY$CODES; END; END; END; $eject /* CASE 5 - NumLock key is pressed */ DO; IF (ads.kb$flag AND CTL$FLAG) = 0 AND (ads.kb$flag AND ALT$FLAG) = 0 THEN DO; IF (ads.kb$flag AND NUM$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR NUM$FLAG; ads.table$offset = ads.table$offset + 4 * N$KEY$CODES; END; ELSE DO; ads.kb$flag = ads.kb$flag AND NOT NUM$FLAG; ads.table$offset = ads.table$offset - 4 * N$KEY$CODES; END; END; END; /* CASE 6 - Scroll Lock key is pressed */ DO; IF (ads.kb$flag AND (ALT$FLAG OR CTL$FLAG)) = (ALT$FLAG OR CTL$FLAG) THEN DO; /* * Ctl-Alt-Break key combination has been pressed. If the * keyboard interrupt option is enabled, and this is the * falling edge interrupt, then instruct SDM to break at * the CS:IP of the interrupted task */ IF (dinfo.reset$int$en AND ad.mon$present) THEN $IF r_32 ad.int3$pending = TRUE; /*will be handled on key-release*/ $ELSE CALL sdm_break(level, ddata$sel, OFFSET$OF(@i120$flush$kb$int)); $ENDIF GOTO not$a$real$char; END; IF (ads.kb$flag AND ALT$FLAG) = 0 THEN DO; IF (ads.kb$flag AND SCROLL$FLAG) = 0 THEN DO; ads.kb$flag = ads.kb$flag OR SCROLL$FLAG; char = CONTROL$S; END; ELSE DO; ads.kb$flag = ads.kb$flag AND NOT SCROLL$FLAG; char = CONTROL$Q; END; END; GOTO char$found; END; $eject /* CASE 7 - DEL key is pressed */ DO; IF (ads.kb$flag AND (ALT$FLAG OR CTL$FLAG)) = (ALT$FLAG OR CTL$FLAG) THEN DO; /* * CTL-ALT-DEL keys combination has been pressed; if the * keyboard reset option is enable then.. */ IF dinfo.reset$int$en THEN OUTPUT(KEYB$SPECIAL$PORT)= 0FEH; /* then reset the CPU board */ END; GOTO char$found; END; /* CASE 8 - released simple char, ignore */ DO; END; /* CASE 9 - ctl key is released */ DO; ads.kb$flag = ads.kb$flag AND NOT CTL$FLAG; ads.table$offset = 0; IF (ads.kb$flag AND CAPS$FLAG) <> 0 THEN ads.table$offset = 2 * N$KEY$CODES; IF (ads.kb$flag AND NUM$FLAG) <> 0 THEN ads.table$offset = ads.table$offset + (4 * N$KEY$CODES); IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN ads.table$offset = ads.table$offset + N$KEY$CODES; END; /* CASE 10 - left or right shift key is released */ DO; IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN DO; ads.kb$flag = ads.kb$flag AND NOT SHIFT$FLAG; IF (ads.kb$flag AND CTL$FLAG) = 0 THEN ads.table$offset= ads.table$offset - N$KEY$CODES; END; END; $eject /* CASE 11 - ALT key is released */ DO; ads.kb$flag = ads.kb$flag AND NOT ALT$FLAG; ads.table$offset = 0; IF (ads.kb$flag AND CAPS$FLAG) <> 0 THEN ads.table$offset = 2 * N$KEY$CODES; IF (ads.kb$flag AND NUM$FLAG) <> 0 THEN ads.table$offset = ads.table$offset + (4 * N$KEY$CODES); IF (ads.kb$flag AND SHIFT$FLAG) <> 0 THEN ads.table$offset = ads.table$offset + N$KEY$CODES; END; /* CASE 12 - CapsLock key is released */ DO; END; /* CASE 13 - NumLock key is released */ DO; END; /* CASE 14 - Scroll Lock key is released */ DO; END; /* CASE 15 - Del key is released */ DO; END; END /* CASE index */; /* * Got here because the character is either invalid or it is a keyboard * control character */ not$a$real$char: ad.interrupt$type = NONE; ad.interrupting$unit = 0; GOTO check$leds; $eject char$found: IF char <> 0 THEN DO; raw$in = ad.raw$in; raw$out = ad.raw$out; IF raw$in <> raw$out - 1 THEN /* If there is room in the */ DO; /* raw input buffer */ IF ((ads.kb$flag AND ALT$FLAG) <> 0) OR (function$key = TRUE) THEN /* * Must be a function key. Send a NUL before the function code */ DO; function$key = FALSE; raw$buffer$p = ad.raw$data$p; raw$buffer(raw$in) = FUNCTION$KEY$CODE; ad.raw$in = raw$in + 1; END; /* * Now move the character into raw input buffer */ raw$in = ad.raw$in; raw$out = ad.raw$out; IF raw$in <> raw$out - 1 THEN DO; raw$buffer$p = ad.raw$data$p; raw$buffer(raw$in) = char; ad.raw$in = raw$in + 1; END; END; /* * Say that this is an input interrupt. The interrupting unit * is always zero */ ad.interrupt$type = INPUT$INTERRUPT; ad.interrupting$unit = 0; END; $eject check$leds: IF (old$LED$status AND INDICATOR$BITS) <> (ads.kb$flag AND INDICATOR$BITS) THEN /* * Update keyboard status indicator(s) if either the Num Lock, * the CapsLock, or the Scroll Lock key was pressed. */ DO; OUTPUT(KEYB$DATA$PORT) = TOGGLE$LED$CMD; CALL g$delay(10, ad.del$const); /* some delay */ OUTPUT(KEYB$DATA$PORT) = ads.kb$flag AND INDICATOR$BITS; END; END i120$console$check; $SUBTITLE('Console Utility procedure') /* * Title: i120$console$utility * * Abstract: * This procedure performs operation specified by the Terminal * Support Code (TSC). The TSC sets up the FUNCTION$ID field in * the BUFFERED$DEVICE$DATA structure to specify the function * to be performed before calling this procedure. Refer to the * iRMX Device Driver's Guide ( terminal utility procedure section) * for a description of the FUNCTION$ID field. * * Calling sequence: CALL i120$console$utility(tsc$data$ptr); * * Parameters: * tsc$data$ptr POINTER to the beginning of the TSC data area. * * Calls: * RQ$SLEEP * RQ$SEND$MESSAGE * */ i120$console$utility: PROCEDURE(ddata$sel,ddata$offset) REENTRANT PUBLIC; DECLARE ddata$sel SELECTOR, ddata$offset WORD, ad BASED ddata$sel DRIVER$DATA$STRUCT, output$buffer$ptr POINTER, output$buffer BASED output$buffer$ptr (1) BYTE, index WORD, temp WORD, output$count WORD, status WORD, seg TOKEN, output$seg BASED seg STRUCTURE( count WORD, text (1) BYTE); $eject IF (ad.function$id >= 0) AND (ad.function$id <= MAX$FUNCT$ID) THEN DO CASE ad.function$id; /* CASE 0 OUTPUT$CALL */ DO; IF (ad.buff$output$state AND OUTPUT$STOPPED) <> 0 THEN RETURN; output$count = ad.out$count; output$buffer$ptr= ad.user$buffer$p; /* fill output count */ ad.out$count = 0; IF (ad.buff$output$state AND OUTPUT$SCROLL) <> 0 THEN DO; index = 0; DO WHILE (ad.output$scroll$count > 0) AND (index < output$count); temp = FINDB(@output$buffer(index), LF, (output$count - index)); IF temp = 0FFFFH THEN index = output$count; ELSE DO; index = index + temp + 1; ad.output$scroll$count = ad.output$scroll$count - 1; END; END; IF index < output$count THEN DO; ad.out$count = output$count - index; output$count = index; END; END; /* if SCROLLING */ ad.console$state = ad.console$state AND NOT cancel$output; IF output$count = 0 THEN RETURN; $eject case0$again: seg = RQ$CREATE$SEGMENT(output$count + 2,@status); IF status <> 0 THEN DO; CALL RQ$SLEEP(10,@status); GOTO case0$again; END; output$seg.count = output$count; CALL MOVB(output$buffer$ptr, @output$seg.text, output$count); CALL RQ$SEND$MESSAGE(ad.output$mbx,seg,SELECTOR$OF(NIL),@status); END; /* CASE 1 ACKNOWLEDGE$INPUT */ DO; ad.buff$input$state = ad.buff$input$state AND NOT(DOUBLE(SEND$INPUT$CMD)); ad.in$count = 0; END; /* CASE 2 RESTART$INPUT */ DO; ad.buff$input$state = ad.buff$input$state AND NOT(DOUBLE(INPUT$FULL)); END; /* CASE 3 CHANGE$UNIT$ATTRIBUTES */ DO; END; /* CASE 4 CHANGE$MODEM$CONTROLS */ DO; END; /* CASE 5 GET$UNITS$AVAILABLE */ DO; ad.units$available= 0FFFFH; END; /* CASE 6 cancel$output */ DO; ad.console$state= ad.console$state OR cancel$output; END; /* CASE 7 CONTROL$OUTPUT */ DO; END; $eject /* CASE 8 ECHO$CALL */ DO; IF (ad.buff$output$state AND OUTPUT$STOPPED) <> 0 THEN RETURN; output$count = ad.echo$count; output$buffer$ptr = ad.echo$buffer$p; ad.echo$count = 0; IF (ad.buff$output$state AND OUTPUT$SCROLL) <> 0 THEN DO; index = 0; DO WHILE (ad.output$scroll$count > 0) AND (index < output$count); temp = FINDB(@output$buffer(index),LF, (output$count - index)); IF temp = 0FFFFH THEN index = output$count; ELSE DO; index = index + temp + 1; ad.output$scroll$count = ad.output$scroll$count - 1; END; END; IF index < output$count THEN DO; ad.echo$count = output$count - index; output$count = index; END; END; /* if SCROLLING */ ad.echo$buffer$p = @output$buffer(output$count); ad.console$state = ad.console$state AND NOT cancel$output; IF output$count = 0 THEN RETURN; case8$again: seg = RQ$CREATE$SEGMENT(output$count + 2,@status); IF status <> 0 THEN DO; CALL RQ$SLEEP(10,@status); GOTO case8$again; END; $eject output$seg.count = output$count; CALL MOVB(output$buffer$ptr, @output$seg.text, output$count); CALL RQ$SEND$MESSAGE(ad.output$mbx,seg,SELECTOR$OF(NIL),@status); END; /* CASE 9 FLUSH$INPUT */ DO; ad.buff$input$state = ad.buff$input$state AND NOT INPUT$FULL; ad.rawout = ad.rawin; END; /* CASE 10 UPDATE$RAW$IN */ DO; END; END; /* DO CASE */ END i120$console$utility; $SUBTITLE('Output task') /* * Title: i120$console$output$task * * Abstract: * This task is created by the i120$console$init procedure. * As this driver is a buffered driver, outputs must be done in * parallel with normal processing; the output task is written to * support this feature. * * This task does the actual character output to the display, it * also handles screen scrolling, erase line, cursor absolute, etc. * Information to be displayed is stored in the output$segment; * when processing is completed, this task deletes the segment. * * Parameters: * * Calls: RQ$SLEEP G$DELAY * */ i120$console$output$task: PROCEDURE REENTRANT; DECLARE ddata$sel SELECTOR, ad BASED ddata$sel DRIVER$DATA$STRUCT, ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, ads$word WORD AT (@ads$sel), dinfo$p POINTER, dinfo BASED dinfo$p DINFO$SYP120$CONSOLE, seg TOKEN, output$seg BASED seg STRUCTURE( count WORD, text (1) BYTE), video$p POINTER, video$ram BASED video$p (1) WORD, index WORD, output$count WORD, char BYTE, temp BYTE, timeout$value WORD, status WORD; $eject /* * Pick up the selector of the TSC's data segment */ ddata$sel = SELECTOR$OF(@show$ds); /* * Set up pointer to the shared data structure. */ $IF r_32 ads$sel = SELECTOR(@s120$console$data$t); $ELSE ads$word = ABSOLUTE$ADDR$WORD; $ENDIF /* * Set up the video RAM pointer */ video$p = ads.video$ram$p; dinfo$p = ad.dinfo$p; IF dinfo.screen$timeout = NO$TIME$LIMIT THEN timeout$value = dinfo.screen$timeout; ELSE timeout$value = dinfo.screen$timeout / 2; DO FOREVER; wait$here: seg = RQ$RECEIVE$MESSAGE(ad.output$mbx, timeout$value, NIL, @status); IF status = E$TIME THEN DO; CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$OFF); GOTO wait$here; END; ELSE DO; /* if the display is being turned off then turn it on */ IF NOT(ad.screen$on) THEN CALL i120$screen$on$off(ddata$sel, ads$sel, TURN$ON); END; DO index = 0 TO output$seg.count-1; DO WHILE (ad.buff$output$state AND OUTPUT$STOPPED) <> 0; CALL RQ$SLEEP(5, @status); END; IF (ad.console$state AND cancel$output) <> 0 THEN DO; ad.console$state = ad.console$state AND NOT cancel$output; GOTO end$of$output; END; char = output$seg.text(index); $eject DO CASE ( ad.routine$index ); /* routine$index = 0 - ordinary character */ DO; IF (char < DISPLAYABLE$CHAR) AND ((ad.console$state AND literal$mode) = 0) THEN DO CASE char; /* CASE 0 - */ DO; END; /* CASE 1 - erase rest of screen */ DO; CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(ads.row * CRT$COLS + ads.column), crt$cols * (CRT$ROWS - ads.row-1) + (CRT$COLS-ads.column)); END; /* CASE 2 - erase line */ DO; CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(ads.row * CRT$COLS), CRT$COLS); END; /* CASE 3 - erase rest of line */ DO; CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(ads.row * CRT$COLS + ads.column), CRT$COLS - ads.column); END; /* CASE 4 - cursor absolute */ DO; ad.routine$index = 1; END; /* CASE 5 - line delete */ DO; CALL MOVW(@video$ram((ads.row+1) * CRT$COLS), @video$ram(ads.row * CRT$COLS), (LAST$ROW - ads.row) * CRT$COLS); CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(LAST$ROW * CRT$COLS), CRT$COLS); END; /* CASE 6 - line insert */ DO; CALL MOVRW(@video$ram(ads.row * CRT$COLS), @video$ram((ads.row+1) * CRT$COLS), (LAST$ROW - ads.row) * CRT$COLS); CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(ads.row * CRT$COLS), CRT$COLS); END; $eject /* CASE 7 - bell */ DO; /* save contents of the diagnostic register */ char = INPUT( DIAG$REG$ADDR ); /* disable speaker */ OUTPUT(DIAG$REG$ADDR)= char AND 0FCH; CALL g$delay(2, ad.del$const); /* some delay */ /* set up the timer count */ OUTPUT(TIMER$CNT$WORD$REG)= 0B6H; CALL g$delay(2, ad.del$const); OUTPUT(TIMER$CNT$DATA2)= 33H; CALL g$delay(2, ad.del$const); OUTPUT(TIMER$CNT$DATA2)= 5H; /* enable the speaker */ CALL g$delay(2, ad.del$const); OUTPUT(DIAG$REG$ADDR )= ( char AND 0FCH ) OR 3H; CALL RQ$SLEEP(4,@status); /* some delay */ /* restore the contents of the diagnostic register */ OUTPUT(DIAG$REG$ADDR)= char; END; /* CASE 8 - backspace */ DO; IF ads.column = 0 THEN DO; IF ads.row > 0 THEN DO; ads.row = ads.row - 1; ads.column = LAST$COL; END; END; ELSE ads.column = ads.column - 1; END; /* CASE 9 - tab */ DO; END; $eject /* CASE 0A - linefeed */ DO; IF ads.row >= LAST$ROW THEN DO; CALL MOVW(@video$ram(CRT$COLS), @video$ram(0), CRT$COLS*LAST$ROW); CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(CRT$COLS * LAST$ROW), CRT$COLS); END; ELSE ads.row = ads.row + 1; END; /* CASE 0B - set color */ DO; ad.routine$index = 3; END; /* CASE 0C - erase screen */ DO; CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(0), ad.crt$len); ads.row = 0; ads.column = 0; END; /* CASE 0D - CR */ DO; ads.column = 0; END; /* CASE 0E - literal */ DO; ad.console$state = ad.console$state OR literal$mode; END; /* CASE 0F - set raw mode */ DO; ad.console$state = ad.console$state OR raw$mode; END; /* CASE 10 - set cooked mode */ DO; ad.console$state = ad.console$state AND NOT raw$mode; END; /* CASE 11 - X-ON */ DO; END; /* CASE 12 */ DO; END; /* CASE 13 - X-OFF */ DO; END; /* CASE 14 */ DO; END; /* CASE 15 */ DO; END; /* CASE 16 */ DO; END; /* CASE 17 */ DO; END; /* CASE 18 */ DO; END; $eject /* CASE 19 - cursor right */ DO; IF ads.column >= LAST$COL THEN DO; IF ads.row < LAST$ROW THEN DO; ads.row = ads.row + 1; ads.column = 0; END; END; ELSE DO; ads.column = ads.column + 1; END; END; /* CASE 1A */ DO; END; /* CASE 1B */ DO; END; /* CASE 1C - cursor down */ DO; IF ads.row <> LAST$ROW THEN ads.row = ads.row + 1; END; /* CASE 1D - cursor home */ DO; ads.row = 0; ads.column = 0; END; /* CASE 1E - cursor up */ DO; IF ads.row <> 0 THEN ads.row = ads.row - 1; END; /* CASE 1F - cursor left */ DO; IF ads.column = 0 THEN DO; IF ads.row > 0 THEN DO; ads.row = ads.row - 1; ads.column = LAST$COL; END; END; ELSE ads.column = ads.column - 1; END; END; /* CASE char */ $eject /* process normal character */ ELSE DO; ad.console$state = ad.console$state AND NOT literal$mode; /* write the characrer to display ram at current cursor pos. */ video$ram(ads.row * CRT$COLS + ads.column) = ads.display$w + char; /* update cursor position */ ads.column = ads.column + 1; IF ads.column > LAST$COL THEN DO; ads.column = 0; IF ads.row >= LAST$ROW THEN DO; /* scroll the display up one line */ CALL MOVW(@video$ram(CRT$COLS), @video$ram(0), CRT$COLS * LAST$ROW); /* erase the last line on the screen */ CALL SETW(ads.display$w + BLANK$CHARACTER, @video$ram(CRT$COLS * LAST$ROW), CRT$COLS); END; ELSE ads.row = ads.row + 1; END; END; END /* of case 0 => routine$index = 0 */; /* routine$index = 1 - second byte of absolute cursor position */ DO; ad.routine$index = 2; temp = char - 20H; IF temp < CRT$ROWS THEN ads.row = temp; ELSE ads.row = 0; END; /* routine$index = 2 - third byte of absolute cursor position */ DO; ad.routine$index = 0; temp = char - 20H; IF temp <= CRT$COLS THEN ads.column = temp; ELSE ads.column = 0; END; $eject /* routine$index = 3 - second byte of set foreground color */ DO; ad.routine$index = 0; ads.display$w = char * 256; END; END; /* CASE routine$index */ END /* of DO index */; end$of$output: CALL i120$set$cursor(ads$sel); CALL RQ$DELETE$SEGMENT(seg, @status); END /* DO FOREVER */; END i120$console$output$task; $SUBTITLE('Local procedure: Set screen on/off') /* * Title: i120$screen$on$off * * Abstract: * This procedure enables or disables the CRT bases on the value of * the 'flag' byte passed by the caller. It works only with the CGA * or the monochrome adapter. EGA support may be added in the future. * * Calling sequence: * CALL i120$screen$on$off(ddata$sel, ads$sel, flag); * * Parameters: * ddata$sel SELECTOR of the TSC data area. * ads$sel SELECTOR of the SHARED$DATA$STRUCT structure. * flag BYTE if 0 then turn the screen OFF * else turn the screen ON * Calls: None */ i120$screen$on$off: PROCEDURE(ddata$sel, ads$sel, flag) REENTRANT; DECLARE ddata$sel SELECTOR, ad BASED ddata$sel DRIVER$DATA$STRUCT, ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, flag BYTE; IF flag THEN /* turn the screen ON */ DO; OUTPUT(ads.index$reg$addr + MODE$REG$OFFSET) = ads.video$enable; ad.screen$on = TRUE; END; ELSE /* turn the screen OFF */ DO; IF ad.screen$on THEN ad.screen$on = FALSE; ELSE DO; OUTPUT(ads.index$reg$addr + MODE$REG$OFFSET) = ads.video$disable; ad.screen$on = FALSE; END; END; END i120$screen$on$off; $SUBTITLE('Local procedure: Set Cursor') /* * Title: i120$set$cursor * * Abstract: * This procedure is used to update the cursor location on the * screen. It does so by sending the cursor to the address specified * by ads.row and ads.column (see the SHARED$DATA$STRUCT structure). * This procedure does not perform any boundaries checking. * * Calling sequence: CALL i120$set$cursor(ads$sel); * * Parameters: * ads$sel SELECTOR of the SHARED$DATA$STRUCT structure. * * Calls: NONE */ i120$set$cursor : PROCEDURE (ads$sel) REENTRANT; DECLARE ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, value WORD; /* Calculate the absolute cursor address */ value = ads.row * CRT$COLS + ads.column; /* Output cursor address to CRT controller */ OUTPUT(ads.index$reg$addr) = select$R14; OUTPUT(ads.index$reg$addr + data$reg$offset) = HIGH(value); OUTPUT(ads.index$reg$addr) = select$R15; OUTPUT(ads.index$reg$addr + data$reg$offset) = LOW(value); END i120$set$cursor; $SUBTITLE('Local procedure: Set video RAM pointer') /* * Title: i120$set$video$ram$ptr * * Abstract: * This procedure sets the video RAM pointer to the starting address * of the display adapter indicated by the adapter$type. * * Calling sequence: CALL i120$set$video$ram$ptr(ddata$sel, * ads$sel, adapter$type, mon$present); * * Parameters: * ddata$sel SELECTOR of the TSC data area. * ads$sel SELECTOR of the SHARED$DATA$STRUCT structure. * adapter$type BYTE indicates the type of display adapter used. * mon$present BYTE indicates if debug monitor present * * Calls: RQE$CREATE$DESCRIPTOR */ i120$set$video$ram$ptr : PROCEDURE (ddata$sel,ads$sel,card$type,mon$present) REENTRANT; DECLARE ddata$sel SELECTOR, ddata$offset SIZE$OF$OFFSET, ad BASED ddata$sel DRIVER$DATA$STRUCT; DECLARE ads$sel TOKEN, ads BASED ads$sel SHARED$DATA$STRUCT, card$type BYTE, mon$present BYTE; IF card$type = MONO$CARD THEN DO; $IF r_32 IF (ads.index$reg$addr = MONO$INDEX$REG) AND (mon$present) THEN DO; /* SDM already set up link to the video board */ ads.video$disable = MON$DISABLE; ads.video$enable = MON$ENABLE; END; ELSE DO; $ENDIF /* * set video RAM pointer to the starting address of the display * RAM on the monochrome display adapter. */ ads.video$ram$p = BUILD$PTR( RQE$CREATE$DESCRIPTOR(mono$base$addr, (ad.crt$len)*2, @ad.status), 0); ads.video$disable = MON$DISABLE; ads.video$enable = MON$ENABLE; ads.index$reg$addr = MONO$INDEX$REG; $IF r_32 END; $ENDIF END; $eject ELSE DO; $IF r_32 IF (ads.index$reg$addr = COLOR$INDEX$REG) AND (mon$present) THEN DO; /* SDM already set up link to the video board */ ads.video$disable = COL$DISABLE; ads.video$enable = COL$ENABLE; END; ELSE DO; $ENDIF /* * set video RAM pointer to the starting address of the display RAM * on the color display adapter. */ ads.video$ram$p = BUILD$PTR( RQE$CREATE$DESCRIPTOR(color$base$addr, (ad.crt$len)*2, @ad.status), 0); ads.index$reg$addr = COLOR$INDEX$REG; ads.video$disable = COL$DISABLE; ads.video$enable = COL$ENABLE; $IF r_32 END; $ENDIF END; END i120$set$video$ram$ptr; $SUBTITLE('Local procedure: Get adapter type') /* * Title: i120$get$adapter$type * * Abstract: * This procedure performs a test to determine the type of display * adapter is present in the system. The meaning of the value * return to the user (board$type) is as follows: * * 0 no diaplay adapter found * 1 only the monochrome adapter is present * 2 only the color adapter is present * 3 both the monochrome and the color adapter are present * * Calling sequence: board$type = i120$get$adapter$type; * * Parameters: * None * * Calls: RQ$SLEEP * */ i120$get$adapter$type : PROCEDURE BYTE REENTRANT; DECLARE status WORD, temp1 BOOLEAN, temp2 BOOLEAN, board$type BYTE; temp1, temp2 = FALSE; /* * Write test data to the monochrome adapter */ OUTPUT(MONO$INDEX$REG) = select$R14; CALL RQ$SLEEP(1, @status); /* some delay */ OUTPUT(MONO$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN; CALL RQ$SLEEP(1, @status); /* some delay */ /* * Write test data to the color adapter */ OUTPUT(COLOR$INDEX$REG) = select$R14; CALL RQ$SLEEP(1, @status); /* some delay */ OUTPUT(COLOR$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN; CALL RQ$SLEEP(1, @status); /* some delay */ $eject /* * Zero indicates that there is no diaplay adapter present */ board$type = 0; /* * Read test data from the display adapters */ IF (INPUT(MONO$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN) THEN temp1 = TRUE; CALL RQ$SLEEP(1, @status); /* some delay */ IF (INPUT(COLOR$INDEX$REG + DATA$REG$OFFSET) = TEST$PATTERN) THEN temp2 = TRUE; IF temp1 AND temp2 THEN board$type = 3; /* both adapters are present */ ELSE DO; IF temp1 THEN board$type = 1; /* monochrome adapter is present */ ELSE IF temp2 THEN board$type = 2; /* color adapter is present */ END; RETURN(board$type); END i120$get$adapter$type; $SUBTITLE('Local procedure: Flush keyboard interrupt') /* * Title: i120$flush$kb$int * * ABSTRACT: * Handles the key released interrupt after ctrl-alt-brk. * It is called just before jumping to SDM. * This procedure reads the keyboard data port to flush the keyboard * interrupt. * * CALLING SEQUENCE: CALL i120$flush$kb$int(cdata$t); * * INTERFACE VARIABLES: * cdata$t - TOKEN for controller data segment * not used by this driver */ i120$flush$kb$int: PROCEDURE (cdata$t) REENTRANT; DECLARE dummy BYTE, cdata$t SELECTOR; /* read the keyboard to flush keyboard interrupt */ dummy = INPUT(KEYB$DATA$PORT); END i120$flush$kb$int; END xconsole;