COMPUTER OPERATIONS RECORDER AND TRAINING SYSTEM

Abstract: A method for detecting and recording signals from an input device operatively connected to a digital computer and output from a target program accessible by the computer, the method comprising the steps of: a) loading recorder means into ROM of the computer; b) accessing a format table file with the recorder to get data representing predefined recording characteristics of the target program and configure the recorder to the target program; c) monitoring and interceding in the control of the operations of the computer with the recorder; d) accessing the target program with the digital computer; e) recording to a datafile signals from the input device, the signals representing input to the target program, and a sequence of screens produced by the target program.

Description: COϊϊFϋT?EFR" OPERATIONS RECORDER AND TRAINING SYSTEM BACKGROUND. OF THE INVENTION A. Technical Field The present invention relates to a computer system wherein one computer program, or a cluster of computer programs, is used to make a recording of another computer program in operation. More particularly, the present invention forms a computer-readable file of records of inputs to, and outputs from, the other computer program. The file may then be edited and manipulated to produce a computer-assisted educational exercise, including a simulation of the second computer program in operation. B. Background Art It is advantageous to teach computer skills by having a student experience "real-life" situations. However, it is often preferable to provide the student with certain computer skills before allowing them to access a computer program, particularly where incorrect student input may lead to untenable results or where the access time is expensive. Because it is also expensive to provide such computer skills to a large number of students, students have been provided with computer- assisted instruction, and student testing, along with a simulation of a "live" computer program. The closer the simulated experience is to the real use of the computer, the easier it is for the students to transfer their learning to actual computer operations. In order to make the simulation of the computer program targeted for learning ("target program") , one approach has been to have computer programmers write a computer program to simulate the target program. Instruc¬ tional information and student testing capabilities may be added to the simulation. However, writing one computer program to realistically simulate another computer program is often difficult and time consuming, and is also likely to be very expensive. U.S. patents 4,637,797 and 4,701,130 (collectively, "the WHITNEY patents") disclose a software training system including a computer, a cassette tape player system, and software for running the computer. This software includes the target program which the student is learning to use and courseware which defines a multiplicity of events which correspond to different portions of a predefined training lesson. The tape player system is attached by an interface to one of the computer's input/output ports. The interface allows the computer to turn the tape player on and off, and also transmits keystroke data from the left track of a stereo cassette tape player, to the computer. Oral instructions or other sounds recorded on the right track of the stereo cassette are played over a speaker. The training system works by alternately turning on the tape to give the student oral instructions and to get keystroke data from the tape, and then turning off the tape player while the student enters keystrokes. The keystrokes entered by the student are compared with a filter specified -by the currently active event in the courseware. If a correct entry is made, the tape player is turned back on and the lesson continues. Certain predefined keystrokes from the tape player cause a new event from the courseware to be selected and for the tape player to be turned off. Furthermore, if the student fails to make the correct keystrokes within a preselected time period, the training system enters the correct answer for him and proceeds with the next portion of the training session. The WHITNEY patents appear to use a co-resident program that records the user inputs, which are later fed back to the target program during the execution of the tutorial to obtain screen outputs. While this approach may be acceptable for limiting incorrect student input, it requires accessing the target program each time that a student is to be trained. Thus, the WHITNEY patents do not address the problem of expensive access time. U.S. Patent No. 4,662,013 ("CERCHIO," one of the inventors herein) discloses an interactive training/expert computer system that includes a computer subsystem for executing computer programs. A keyboard is used for entering data into the system, and a CRT displays the results of executing a computer program in the computer subsystem. A preselected computer program is resident in the computer subsystem. A tutor module interrupts the flow of data from the keyboard, interpreting, and manipulating the input data, selectively generating messages in response to the input data, and selectively allowing a subset of the input data to be processed by the preselected computer program. The interpreting, manipulating, generating, and allowing functions are dynamically determined in accordance with predefined criteria dependent on the contextual circumstances in the running of the preselected computer program. Although the CERCHIO invention involves an interactive software training system in which a co- resident "courseware module" serves as a tutorial co- program, the patent is not directed at how to make the courseware module, particularly a course module that functions without accessing the target program for each education exercise. II. DISCLOSURE OF THE INVENITON SUMMARY OF THE INVENTION It is an object of the present invention to provide a system and a method to produce a recording of the use of a computer program. It is another object of the present invention to provide a system and a method for producing a recording of the use of a computer program operating on a local or a remote host computer. It is another object of the present invention to provide a system and a method for using the recording to simulate the use of the computer program in computer-based training. It is another object of the present invention to provide a system and a method for using the recording to simulate the use of the computer program. It is yet another object of the present invention to provide a system and a method for editing and modifying the recording to produce a computer-based training system. It is a further object of the present invention to provide a system and a method for using the recording to test student learning with regard to use of the computer program. Other objects and advantages of the present invention will become apparent from the following summary, drawings, and detailed description of the invention and its embodiments. The present invention is designed to capture and/or create information and format it into a computer- based training exercise. In an automatic recorder of digital computer operations driven by a first program and user input, the recorder includes: a first digital computer; a memory device connected to the computer; and means for automatically recording in the memory the operations and the input of the first program. The recorder may further include: a second digital computer connected to the first digital computer for hosting the first program and receiving the input via the first computer. Other features of the present invention are detailed below. Due to the complexity of the invention and the corresponding amount of information needed to describe it, the following outline is provided: I. BACKGROUND OF THE INVENTION A. Field of Invention B. Description of the Related Art II. SUMMARY OF THE INVENTION III. BRIEF DESCRIPTION OF THE DRAWINGS IV. DETAILED DESCRIPTION OF THE INVENTION AND A FIRST PREFERRED EMBODIMENT A. General Features 1. CBT Exercise System Overview a) Playback b) Students c) Help d) Modify e) Single Screen Utility f) Miscellaneous Functions B. Recording Emulator 8a 1. Emulator Flow Charts C. CBT Generic Recorder 8b 1. Sampling Control 2. Recording Algorithms 3. Data Management 4. File Management 5. Configuration 6. Generic Recorder Figures 7. CBT State Program Structure D. Menu Structure E. QSCRs F. Playback 1. Playback with Control Loop 2. Playback Flow Charts a) Automatic Playback (Page 1 F2) b) Manual Playback (Page 1 - Fl) c) Instructional Playback d) Proficiency Playback 3. Print Exercise 4. Modify a) Options Menu b) Modifying Screens c) Selection Error d) Typing Mode e) Color/ASCII f) LPW G. Screen Utility (Page 1 - F7) H. Student 1. Student Start-Up Flowchart 2. Course Directory 3. Critique 4. Help I. Search Utility J. File Functions 1. Copy Exercise to Drive x (Page la - Fl- F4) 2. Delete Exercise (Page la - F6) 3. Rename Exercise (Page la - F5) 4. Append (Page la - F7) 5. Replace Pathway (Page la - F8) K. File Structures 1. FTF File 2. Exercise File 3. Exercise Directory 4. Defaults (Page 1 - F10) 5. Other Files L. Management Interface M. Global Variables Header File (CBT V) N. Generic Recorder Code V. SECOND PREFERRED EMBODIMENT Non-Linear Impacts Exercise File Layout New Features Playback Print Modify Option Menu Mode Screen Information Cursor Information Screen Utility 3270 Recorder VI. CLAIMS III. BRIEF DESCRIPTION OF THE DRAWINGS FIG. 1 is a block diagram of the present invention. FIG. 2 is a block diagram of the present invention that provides further detail of elements of FIG. 1. FIG. 3 is a block diagram that provides further detail of recording emulator 8a of FIG. 1. FIG. 4 is a block diagram that provides further detail of the record screen step 86 of FIG. 3. FIG. 5 is a block diagram that provides further detail to the process key step 92 of FIG. 3. FIG. 6 is a block diagram of the open file step 96 of FIG. 3. FIG. 7 is a state diagram of the generic recorder 8b of FIG. 1. FIG. 8 is a block diagram of the recorder data flow and memory organization of the generic recorder 8b of FIG. 1. FIG. 9 is a block diagram of the resident portion of generic recorder 8b of FIG. 1 and interrupts. FIG. 10 is a block diagram of the monitored interrupt 222 of FIG. 9. FIG. 11 is a block diagram of the multiplex interrupt 224 of FIG. 9. FIG. 12 is a block diagram of the clock interrupt 226 of FIG. 9. FIG. 13 is a block diagram of the process keyboard 258 of FIG. 12. FIG. 14 is a block diagram of the record key 280 of FIG. 13. FIG. 15 is a block diagram of the C clock code 260 of FIG. 12. FIG. 16 is a block diagram of the process flags 302 of FIG. 15. IG. 17 is a block diagram of the screen differs 312 of FIG. 15. FIG. 18 is a block diagram of the tmp=cmp screen 376 Of FIG. 17. FIG. 19 is a block diagram of tmp in ignore region 382 of FIG. 17. FIG. 20 is a block diagram of the record screen 334 of FIG. 15. FIG. 21 is a block diagram of the keyboard interrupt 228 of FIG. 9. FIG. 22 is a block diagram of the playback 22 of FIG. 2. FIG. 23 is a block diagram of the process playback record step 522 of FIG. 22. FIG. 24 is a block diagram of the automatic playback step 24 of FIG. 23. FIG. 25 is a block diagram of the manual playback step 26 of FIG. 23. FIG. 26 is a block diagram of the instructional playback step 28 of FIG. 23. FIG. 27 is a block diagram of the proficiency playback step 30 of FIG. 23. FIG. 28 is a block diagram of the print step 42 of FIG. 23. FIG. 29 is a block diagram of the overall structure of modify 44 of FIG. 23. FIG. 30 is a block diagram of the options menu mode 52 of FIG. 29. FIG. 31 is a block diagram of the selection error mode 48 of FIG. 29. FIG. 32 is a block diagram of the typing error mode 50 of FIG. 29. FIG. 33 is a block diagram of the color/ASCII mode 54 of FIG. 29. FIG. 34 is a block diagram of the LPW modify mode 58 of FIG. 29. IG. 35 is a block diagram of the single screen utility 56 of FIG. 2. FIG. 36 is a block diagram of the process option 1000 of single screen utility 56 of FIG. 2. FIG. 37 is a block diagram of the student diskette 32 of FIG. 2. FIG. 38 is a block diagram of the course directory step 36 of the student directory shown in FIG. 37. FIG. 39 is a block diagram of the search function 68 of FIG. 2. FIG. 40 is a block diagram of the copy function 60 Of FIG. 2. FIG. 41 is a block diagram of the delete function 62 of FIG. 2. FIG. 42 is a block diagram of the rename function 64 of FIG. 2. FIG. 43 is a block diagram of the append file function 66 of FIG. 2. FIG. I 1 is a block diagram of of the present invention. FIG. 2' is a block diagram of Data Files 10a'. FIG. 22' is a block diagram of Playback 22'. FIG. 28* is a block diagram of Print 45. FIG. 29' is a block diagram of modify 44. FIG. 30A is a block diagram of Option Menu Mode 52. FIG. 35' is a block diagram of Screen Utility 56. FIG. 44 is a block diagram of Screen Information. FIG. 45 is a block diagram of Cursor Information. FIG. 46 is a block diagram of Initialize Recorder 2000. FIG. 47 is a block diagram of Initialize Recorder 2000. FIG. 48 is a block diagram of Install KB Interrupt 2002. FIG. 49 is a block diagram of Monitor Keyboard Interrupt 2004. FIG. 50 is a block diagram of Process Screen 2006. FIG. 51 is a block diagram of Reset Interrupts 2008 . FIG. 52 is a block diagram of Process Duplicate Single Filenames 2010. FIG. 53 is a block diagram of Parse Key Table, FIG. 54 is a block diagram of Read Key Table, FIG. 55 is a block diagram of Keyboard Interrupt. FIG. 56 is a block diagram of Process Key. FIG. 57 is a block diagram of Screen Arrived, FIG. 58 is a block diagram of Process Displayable Key. FIG. 59 is a block diagram of Process Function Key. FIG. 60 is a block diagram of Autoexit Screen Record. FIG. 61 is a block diagram of Write to File, FIG. 62 is a block diagram of Process Temporary Write. FIG. 63 is a block diagram of Process final Write. IV. BEST MODE FOR CARRYING OUT THE INVENTION - DETAILED DESCRIPTION OF THE INVENTION AND A FIRST PREFERRED EMBODIMENT The present invention involves one or more computer programs in a digital system adapted to record the input to and output from a target computer program. There are 19 C source code modules, though herein, the one or more computer programs may be referenced in the singular as "program, "code," or "logic." One of the modules is a large assembly language module called cbt.asm, and other, smaller assembly modules include vidlib.asm, vidlib2.asm, int24.asm, model.asm, pdepvdad.asm, pdephdad.asm, and pr40sub.asm. There is a header file, cbt.h, and a variables file, cbt.v, also included in the modules. The recording, authoring, and exercise playback functions, when compiled and linked, become the program titled CBTP.EXE. The program is written using Microsoft C version 5.1 and linked with the medium memory module using only the standard Microsoft library. The programs may be run on a personal computer, such as a PC or a PS2 (available from International Business Machines Corporation ("IBM")), or another compatible computer. The personal computer should have 64OK memory, a keyboard, a monitor or CRT — preferably adapted for color display, and, if desired, a pointer device such as a mouse or a light pen. Herein, the term "input device" is used to encompass such input devices, as a keyboard, mouse, light pen, etc. that produce electrical signals representing, for example, keystrokes as input to the target program 4. The personal computer may be used alone to operate both the program of this present invention and the computer program targeted for recording. Alternatively, the personal computer operated by the present invention can be linked (by modem or cable and appropriate emulator software) to a host computer, such as an IBM 4300 series mainframe computer having the second program operating thereon. As an overview of the present invention, a person experienced with the target program logs onto the personal computer. The person then uses one of two alternative approaches to record the target program. The first approach is via the use of a recording emulator which, by inserting calls to other routines of the present invention described hereinafter, a terminal emulator may be modified to allow it to record its use in a format that is useful for simulation/record-processing software called the "CBT." By using such an adapted emulator to access a mainframe host application, the experienced user may record the use thereof. The resulting recording may then be replayed as a simulation or modified as described below to turn the recording into a tutorial exercise. While connected to a host via the recording emulator, the user may at any time start or stop recording with the present invention. Although a recording emulator is an effective way to produce simulations and recordings which can be modified into student tutorials, because the recording emulator essentially records its own use, it is limited to recording those host transactions that this emulator supports. Because it may be desired to record target applications other than the emulation of a host terminal session, this invention also includes a second approach to recording the target program called the "generic recorder." Since the generic recorder operates outside of its target application, the target need not be modified to be recorded. The generic recorder is a terminate-stay- resident ("TSR") program because, after it is loaded into the computer, it will stay resident and act unobtrusively in the background until it is called upon. This recorder uses a Format Table File ("FTF") that describes the recording characteristics of the program to be recorded. The FTF file also handles any specialized keyboard requirements of the target program. Once the generic recorder is loaded, the user loads the proper FTF file before starting to record. It is possible to have just one FTF file that is always loaded automatically with the recorder, or to create batch files that load the proper FTF file when beginning the target application program. Another FTF file may be loaded to record still another program without having to reload the generic recorder. Once the generic recorder and the appropriate FTF file are loaded, the user calls up the target program and can commence recording at any time or times. The computer system does not record until a "start record hot key" has been pressed. The user may optionally record just a single screen or start a process of automatically recording screens. If recording several screens, the system will record until the user hits the "end record hot key" to complete the recording and put the generic recorder into its unobtrusive background state again. The user can continue with other transactions involving the target program, as described. Each time that the start record hot key is hit, the user is asked to enter a file name. Sequentially, by entering different file names, the user can continue to make several recordings without signing-off the target program. The end result of the use of one of the above- mentioned means for recording is at least one recording in a file of a computer that can then become available to the CBT system for processing into a simulation and/or tutorial for subsequent presentation to a student. Note that the CBT is a computer program that is an application program in itself. Thus, the CBT can use a recording (i.e. , a data file) made with either the recording emulator or the generic recorder. The user may then call up the recording and edit it to produce a simulation in several formats. One format adds educational information to guide a student through the simulation. Another format involves essentially no instruction, but tests the correctness of the student interaction with the simulation. A. General Features FIG. 1 shows the general features of the present invention. A personal computer 2 is running target computer program 4 that is the subject of a future training exercise. Program 4 may, for example, be an application program, like the customized software that operates a hospital, a word processing program, MS DOS, or an emulator. Indeed, program 4 may be essentially any program that is well-behaved in the DOS environment, i.e. , any DOS program that does not improperly interfere with the interrupt processing of the computer 2. Optionally, computer 2 can be connected to a host computer 6 by virtue of the contents of computer 2. Computer 2 contains means for recording 8a and 8b, data files 10, and CBT 12. Means for recording 8 includes recording emulator 8a and a generic recorder 8b. Host 6 and computer 2 may be connected by means of the recording emulator 8a. The line between the host 6 and target program 4 is hyphenated to indicate that the data file 10 may optionally be produced by the recording emulator 8a connected to host 6 and engaged in operations involving a target program 4 that resides at host 6. Means for recording 8 link to CBT 12 via at least one data file 10. Computer 2 also contains at least one FTF file 14b, which links the CBT 12 to the configuration program 18 for processing data received from the FTF 14b file. Data file 10 may be comprised of a file of records of computer screens produced during the operation of program 4 and records of information input by the user. Although this data could be stored in separate files, or some other configuration, a file of records is preferred for efficiently keeping track of the sequence of the operations of program 4 with relatively more straightforward logic than other alternatives. Alternative configurations of the means for recording 8 are also viable. For example, the recording emulator 8a and CBT 12 could be integral as one program or a linked cluster of programs, as is suggested by a dotted line there between. In this alternative configuration, the controlling functions may be located in a terminal emulator that normally provides the link between the PC computer and mainframe environments. For CBT purposes, "hooks" may be placed in the emulator 8a to allow control to be passed to the CBT 12 system. In effect, emulator 8a would be the main program with a control loop that allows branching to CBT 12 code when characters are typed, pointer device selections are made, or CBT-set interrupt conditions are met. For the most part, a control loop code handles all of the pointer device and keyboard input activities in either this alternative configuration or in the configuration detailed herein. With the hooks, CBT 12 would get "first shot" at the input keys. Only when the PC computer 6 is actually connected to the mainframe would the emulator 8a/CBT 12 have its own keystrokes and control the operation. Such an emulator 8a/CBT 12 combination is available from TDS Healthcare Systems Corporation in Atlanta, Georgia — U.S.A. Although there are advantages and disadvantages to this alternative, assuming that source code for an emulator is publically available, it is not emphasized herein so that others will not be limited to one emulator. Further, it should be noted that an emulator similar to recording emulator 8a, like most emulators, has a built in FTF file 14a and initialization routine 16. While this is efficient, it does not have the flexibility of the configurable FTF file 14b of the generic recorder 8b. 1. CBT Exercise System Overview There are three different kinds of users contemplated for the present invention: developers, authors, and students. As used herein, "developer" refers to a computer programmer who, for example, may be using the system to create FTF files or to otherwise have access to files or code segments that are not generally accessible to other users. Developers have access to all aspects of the present invention. The term "author" refers to the user that, for example, records the target program 4 and creates educational exercises. A "student" refers to a user seeking to learn the target program 4, and has access to CBT 12 essentially limited thereto. Access to different parts of the system is controlled by a file called CTR.PEW. The batch files that run CBTP will transfer the proper CTRx file into CTR.PEW. These files have the following designations: developers have DEV; students have STU; and authors have a blank. Developers can do anything, while authors and students are restricted to exercise files and files with a TDS extension. Authors can make a recording and do everything necessary to create and customize exercises. Authors also have the ability to delete files from the system, copy files, etc. Students can only run exercises. They get a student start-up screen, a registration screen, and then a course directory from which they can select the exercises. Alternatively, the present invention could be configured to allow students to hotkey to the mainframe and practice on it, after which they would be returned to the course directory. For the majority of the application, the actual, real data appears on the screen. When modifing an exercise, all of the screens will appear, including any error messages, even if one selects to jump ahead to a specific page or END to hurry along. The program writes the screen from the existing file, then reads the screen and stores that in the new file. Control files are mostly "screens" that have been compressed and stored. The screens are decompressed and read into the control variables. One major example of screen reading is the concept of a QSCR ("Q-Screen") . Any screen that has those four letters in the first four screen positions (row 0, columns 0-3) is treated differently. The QSCR is followed by a space and is then followed by a single letter which describes various options for that screen. For example, a QSCR N is visible only to the author and never in a subsequently discussed playback mode, while a QSCR X identifies an exercise statement. These options are detailed later in this document. Figure 2 outlines the main features of CBT 12. a) Playback Authors have access, via author menu 20, to playback 22 which has four modes: automatic playback 24, manual playback 26, instructional playback 28, and proficiency playback 30. Automatic playback 24 reproduces the recording of program 4 by automatically sequencing through the recorded screens with predetermined timing, which is adaptably changeable. The second mode of playback 22 is the manual mode 26, which allows the user to review the recording by stepping through it one screen at a time. Manual mode 26 allows spending as much time as needed at any particular point. This, for example, allows students to completely grasp a lesson presented on a screen of an exercise before moving to another screen. When ready, the user gives the proper input keystroke and the recording sequences to the next "frame" of data file 10. Instructional playback 28 provides a step-by-step sequence of the QSCR instructional screens, along with the computer " sc e ns -" that simulate the program 4. It requires a correct answer to advance and provides hints and error messages. The final mode of playback 22 is proficiency playback 30, which provides minimum educational guidance, but tests students on the correctness of their handling of the exercise with prompts and responses predetermined by the author(s) . Student access to the CBT 12 is much more limited than that of authors, but aspects of playback 22 may be made available to them. Students are provided with a diskette 32 which they may use separately from the rest of CBT 12. Students also get a special start-up screen, and, when a student has finished his/her studies, the CBT 12 will return the computer 2 to the start-up screen to wait for the next student to log on to do exercises. Authors are allowed full access so that they can review their work and see their exercises in all forms. Any exercise may then be saved as one which only allows the student access to those of the four modes as may be appropriate for the exercise at issue. b) Students The first step for the student is registration 34, which requires the student to input information that can later be used to identify the student, including a password. The set of data on each student, stored in course directory 36, also allows an author to track the progress of each student. Each student need only register once but will have to reenter the registration step 34 to enter the password, in order to gain access to the course directory 36 and the exercises therein. The course directory 36 is a directory that includes a list of the different exercises available to students. A course is a set of exercises. In the course directory, students may alternatively choose to enter critique 38, normally the last task accomplished after completing the exercises. Critique 38 allows students to provide feedback about their computer-assisted instruction and possibly other teaching as well. Critique 38 presents a list of questions to, and receives responses from, the student. The answers may be accessed by authors for use in evaluating the course. A management system interface 40 keeps track of information on the individual students and the different tutorials available to the individual users. Alternatively, in the course directory, students may choose an exercise in a course. At this point, a playback 22 mode selection must be made by the student from the alternatives available to the student. The student selects one of the four previously discussed modes 24, 26, 28, or 30 available in playback 22. Once the mode has been selected, the exercise in a data file 10 is accessed. After completing the exercise, the next step is to update the directory 42. If the student has undertaken a test in proficiency playback 30, part of update directory 42 involves calculating and storing student test score information. An exercise is flagged as completed when the highest mode available to the student for the particular exercise at issue has been completed. The modes go from automatic 24 to manual 26 to instructional 28 to proficiency 30. c) Help Help system 44 can either be state driven or based on name or number parameters. Herein, the help system is state-driven. Thus, when the user hits the "help" key, it calls a function that first determines where the user was in CBT 12 (e.g. , in student registration 34, etc.) and then displays one or more appropriate screens giving the user the helpful informa¬ tion. If there is more than one help screen, the user can go forward and backward through the help screens. When the user escapes from the help system 44, he or she is returned to where the user was when the help key was hit. Because this help system 44 is available throughout CBT 12, it is not illustrated in FIG. 2 as being connected by a line to the CBT 12. d) Modify Playback 22 is linked to print 45, to facilitate producing hard copy of the data file 10. This is in addition to a subsequently discussed print page option. Playback 22 also is linked to modify 46 which faciliates changing the data file 10 to produce an exercise. Modify 44 is made up of a number of functions that allow the author to make certain kinds of changes. The author can make or change: a basic page 47 (e.g.. a screen recorded to data file 10) ; a selection error 48 (i.e.. a selection of a function key to change the basic page or screen, and an associated message to be displayed in response to a student input of the wrong function key) ; or a typing error 50 (i.e.. text to change the typing expected on the basic page or screen and a message for typing the wrong text). Corresponding functions 42, 46, and 50, respectively, enable these activities. Within these functions, the author may toggle to options menu 52 to choose from a number of different operations, including: insert, delete, save, replace, list, print page, and go forward. These operations allow manipulating the recorded data. Note that not all of these options are always available depending on what the user was doing before coming to the options menu 52, as is more fully discussed below. Color/ASCII 54 is also accessable from functions 47, 48, and 50, and is essentially the screen-painting part of the CBT 12 system. Color/ASCII 54 allows the author to change the color at any character position on the screen (if it is a color screen) and type any graphic character. Note that these text-graphic characters are not all found on a keyboard. They could be entered from the keyboard through the use of the "alt" key being depressed while a number is entered. These options are more easily accessed via a chart displayed in color/ASCII 54. Color/ASCII 54 (and options menu 52) may also be entered from a single screen utility 56, as is more fully discussed below. LPW 57 involves an "action key." An action key causes the screen to change and the exercise to advance to the next page. For example, when recording, the user may have hit an enter key to advance in the target program 4. Thus, the recording would show the screen change and that an enter key was the key that prompted it. Likewise, selecting an item at a row and column with a pointer device might trigger the screen change, and the device hit status, and row and column data are saved in the datafile 10 record. The "LPW" or light pen window is simply a way to bring up the action key information on the screen to allow the user to change it. In sum, the functions of modify 46 allow the author to take the recording of a sample run of target program 4, recorded via data file 10, and transform this record into an exercise. Modify 46 allows the user to add in instructional comments and helpful hints on screens to allow the subsequent student(s) to learn the functioning of the target program 4. Modify 46 also allows the author to change the function key(s) or text that produces a new screen in the record, as well as to create error messages. Further, modify 46 allows the author to rearrange and change the appearance of the screen to make an exercise more palatable and understandable to the student. e) Single Screen Utility The single screen utility 56 is used to create help screens and other special screens. Unlike modify 46, the single screen utility 56 is not exercise driven, though it can be used to make or change screens which can later be incorporated into an exercise. In general, the single screen utility 56 allows creating screens from scratch or changing screens-stored as part of data file 10. As previously mentioned, the utility 56 also allows access to color/ASCII 54 and options menu 52, and some options therein. Utility 56 is useful for updating control files, creating screen templates, creating single screens, and performing certain editing functions. f) Miscellaneous Functions CBT 12 also has file functions 58 for manipulating the data files 10. Copy 60 makes a copy of any existing file 10 in whatever disk drive directory the user specifies. Delete 62 deletes any existing file 10. Rename 64 renames any existing file 10, and append 66 takes two files 10, ' and joins them together to form one larger file. These functions process the data files as files and do not access the records individually. Search 68 is logically separate from the file functions 58. Search 68 allows the user to search through one or more files 10 for the occurrence of an input string. Defaults 70 allows the author to specify certain default conditions for the operation of the CBT 12 system, such as the printer identification, the automatic playback 24 wait time, and the disk drive where an exercise will be found. B. Recording Emulator 8a FIG. 3 is a more detailed flow chart of the recording emulator 8a. The recording emulator 8a is a standard terminal emulator. Recording emulator 8a is adapted with hooks in it to allow access to the remainder of CBT 12 if a flag is set for CBT 12 in the FTF configuration file. Recording emulator 8a uses control loop returns. In the control loop that switches back and forth from the CBT 12 progra (s) and the recording emulator 8a main loop, there is a need to be able to direct what happens. CBT 12 has "states" which tell what to do when events occur, such as when a key is hit and a function called CBT_Character_Trap is called. Further, there is a need to "interrupt" recording emulator 8a, to get it to send control back to CBT 12 under other conditions. There are two variables that provide this service, p_Reset and p_Play_Int. Both are tested in the CBT_Low_lnterrupt function in CBT1.C, which is called everytime recording emulator 8a reaches the end of the main loop and before it restarts the loop. This provides a way to get control back to the CBT 12. The p_Reset variable has six possible values. One of the six is reserved. The value three means that the user is a student and is used in the programs to display the proper screens (students get a different start-up screen, for example) . The value five is set when a subsequently discussed critique page being sought was not found and returns back to the critique start. The value two is essentially a "press any key" type and will then return to the start-up screen values. If p_Reset is 11, it is in a timer situation signifying a waiting condition. When the wait condition is met (e.g.. when the user finishes an exercise playback 22) , the value of this variable will become one. Besides the tests in CBT_Low_Interupt, there is a special reset handling routine of CBT_Character_Trap that handles the actual processing for the p_Reset values of two, three, and five. P_Play_Int, found mostly in CBT_Low_Interrupt, is really a playback state holder having normal values between 0 and 30 during the playback 22 routine. The value four, essentially is the normal value for "go off to playback 22 again," and this is also the value when the user selects an exercise mode just before it brings in the exercise. During playback 22, the values of two, three, and five are found for timing waits or return locations after a visit to recording emulator 8a for an action. There is one other artificial situation when p_Play_Int is set to 88 during a process involving the subsequently discussed registration page, which is handling its own keystrokes, but then has to leave to get help 44 if called. This flag is then used to handle location when the registration page is called again after help 44 and prevents the CBT 12 system from reloading or parsing registration data. P_Play_Int has a subset, p_Play_Int_Code which is set in the playback 22 functions to more specifically route the user to where he or she was. This is usually effective when p_Play_Int has come in with a normal value of four. In addition, P_Int is used to take the value of P_Play_Int on entry into playback and holds that value for processing while in playback, zeroing out p_Play_Int so that it must be reset again while outside of playback to refine the effects of the action taken. 1. Emulator Flow Charts In FIG. 3 the recording emulator 8a begins with initialization step 72 which loads the FTF file 14b. If CBT 12 is to be supported, the user will set a flag to indicate that CBT 12 is intended (hence active) . Thus, branch 74 tests whether the CBT 12 is active with respect to recording emulator 8a. If the CBT 12 is not active, the recording emulator 8a moves to exit point 76 to run as a normal emulator without CBT 12. Emulators, by design. are intended to connect to a host and this is part of their initialization 16 process. However, if the CBT 12 is active, the recording emulator 8a progresses to CBT 12 processing where a normal escape from the emulator 8a must always be examined first in CBT 12 to see if CBT 12 is finished. CBT 12 determines when the recording emulator 8a will be able to quit. Thus, from block 78 on, the system is in the control loop where it is constantly evaluating the state, as is more fully discussed below. Generally, however, the control loop is concerned with whether it is recording or not, whether any keys or pointer hits have been found, or whether any hot keys or escape keys have been pressed. Thus, processing begins through this loop starting at the evaluation at branch 78 which would not be true on first entry but which is always the first test thereafter. A yes at branch 78 leads to quit 80. Otherwise, the recording emulator 8a proceeds to branch 82 where it sees if a new screen message has arrived. If it has, the recording emulator 8a proceeds to branch 84 to test if a record flag is set. If it is set, the screen is recorded in step 86. If not, the screen is ignored. The emulator 8a loops back to escape flag 78 from record screen 86. The recording emulator 8a proceeds from branch 84 or branch 82 to a point where the recording emulator 8a tests whether a key was hit, at branch 88. If it was, the recording emulator 8a proceeds to process key step 92, and thereafter back to branch 78. At branch 90, if no key was hit, the recording emulator 8a proceeds to branch 94. If the user presses the hot key, a test at branch 94 will send the recording emulator 8a to open a data file 10 at step 96 and will start recording before looping back to escape flag set 78. However, if the answer coming out of branches 88 or 94 is no (i.e.. if there is no new screen and no key was hit) , the recording emulator 8a proceeds to branch 98 to determine whether there is a pointer hit (i.e. , was the mouse or light pen clicked) . If there is a pointer hit, the recording emulator 8a proceeds to branch 100 where, if the record flag is on, the recording emulator 8a proceeds to test whether the hit and/or pointer location are valid at step 102. Thereafter, the recording emulator 8a loops back to the escape flag setting query at 78. If the answer coming out of branches 98 or 102 is no, then the recording emulator 8a proceeds to a CBT option 104, which allows exit from the recording emulator 8a. This option is to allow the CBT 12 to get control of program 4 if needed, for example, to keep the recording emulator 8b from interrupting any CBT 12 processes, such as storing a file or reading in multiple records. FIG. 4 details the record screen step 86. Step 86 begins with an inquiry at branch 106 to determine whether a typing buffer is open. If the typing buffer has previously been opened, before recording the next screen, it must be closed and written to a typing record in file 10, because the information in the buffer must belong to the previous screen. Otherwise, the recording emulator 8a proceeds to build a typing header in step 108. Because every screen record has a header record associated therewith, the recording emulator 8a proceeds to write the typing record to the file at step 110. Next, the recording emulator 8a proceeds to step 112 which closes the typing buffer and joins the no fork from the typing buffer open query of 106 at branch 114. Branch 114 tests whether there is a function key in the typing buffer. If there is, the recording emulator 8a proceeds to build function key header bytes at step 116. If a function key was hit to trigger the screen record, the header will be constructed to store the identity of the key. Otherwise, the recording emulator 8a builds unsolicited header bytes at step 118, i.e. , because where no key triggered the screen that came, the header must say so. The recording emulator 8a proceeds from either step 116 or step 118 to step 120 to finish the header record. A function key or an unsolicited flag is only part of the header. Thus, the rest of the header is filled. Thereafter, the recording emulator 8a writes the file to memory at step 122 and exits at 124 back to the diagram set forth in FIG. 3. The process key step 92 of FIG. 3 that evaluates the keyboard character input is detailed in FIG. 5. Process key step 92 begins by checking whether an escape key was selected at branch 126. If the escape key was depressed, the recording emulator 8a proceeds to step 128 to set an escape flag. Thus, upon exiting at 130 to FIG. 3, the escape flag set test 78 will trigger quitting the recording emulator 8a. At branch 126, if the key depressed was not the escape key, then the recording emulator 8a proceeds to branch 132 to check whether the key depressed was an end of record key. If it was, the recording emulator 8a proceeds to end recording step 134, which writes a "last record" flag and closes the file. Next, the recording emulator 8a proceeds to step 136, which sets record flag off (i.e.. the hot key used to record is set to "off" so that the system will not record more data) . The recording emulator 8a then proceeds to return 130. If the key being evaluated in branch 132 is not the end record key, the recording emulator 8a proceeds to branch 138 to check whether it is a function key. At this branch, if it is a function key, the recording emulator 8a determines whether the typing buffer is open at branch 140. If the typing buffer is open, then a typing header is constructed at step 142. The header is written to the data file 10 at step 144 and the typing buffer is closed at step 146 before return 130. If the typing buffer is not open at 140, the function key is stored in a buffer at step 144 before return 130. In FIG. 4, the function key in the buffer will be recognized at branch 114 and processed as previously discussed. In FIG. 5, at branch 138, if the key is not a function key, the recording emulator 8a proceeds to branch 150 and accepts the input key if it is a letter or number. In either case, the recording emulator 8a proceeds to branch 152 to check if the typing buffer is open. If the buffer is not open, step 154 opens the buffer. Once the typing buffer is open, step 154 places the letter or number key in the buffer at block 156 which then leads to return 130. The alternative from type character branch 150 leads to step 158, an unknown key test 158. The recording emulator 8a ignores the key found and returns at block 130. To further understand the typing buffer, consider that it is a temporary storage area. If the user types "DOG", the computer is directed by the CBT 12 to first put a "D" in the buffer and then leave to await the next user input. Next time that the user types, i.e. , the "0", the computer is directed to return to the buffer and amend the contents of the buffer to hold "DO". When the user types "G", the same process happens, until the user hits the enter key. The enter key triggers writing out the contents of the buffer as a typing record to the data file 10. Further, the enter key is the function key put in the header. The typing buffer is then closed to await the next typing. FIG. 6 details the step open file 96 of FIG. 3. To open a file, the recording emulator 8a asks the user to input or find a file name at step 160. The recording emulator 8a then checks whether the file name already exists at branch 162. If it exists, then the recording emulator 8a asks whether the user wishes to overwrite the existing file at branch 164. If the user does not want to overwrite the file, then the recording emulator 8a asks the user for a new file name at branch 164 and loops back to step 160. If overwrite is desired at branch 164 or if the file did not exist at branch 162, the recording emulator 8a proceeds to open the file at step 166, and sets the record flag on at step 168 and proceeds to return 170. An escape (not shown) allows exiting if the user decides not to input a file name. C. CBT Generic Recorder 8b Before getting into the details of the generic recorder figures, the concepts of sampling and recording algorithms and an overview of the how the recorder works should be understood. The generic recorder 8b in FIG. 1, is conceptually divided into two separately executable sub- parts. The first sub-part (i.e. , program) is for doing the actual recording of state changes. This sub-part becomes resident at the time of execution and maintains a watch on the computer states by intercepting several of the hardware and software interrupts. The hardware devices monitored are the system clock, keyboard, and pointer device. With regard to software interrupts, an interrupt service routine ("ISR") has addresses or vectors that are numbered and correspond to fundamental routines that operate the computer. The software interrupts monitored are: 1) ISR 5 Print Screen; 2) ISR 10 Video BIOS; 3) ISR 13 DASD BIOS; 4) ISR 23 Control break from the BIOS; 5) ISR 28 DOS idle interrupt; 6) ISR 21 DOS command request; 7) ISR 2F DOS multiplex interrupt; and 8) ISR 24 DOS critical error. All of these interrupts are monitored continuously except the last one, ISR 24. This interrupt is only intercepted during the recorder's disk read/write operations. At all other times this interrupt is set to the recorded task's critical interrupt handler. The second sub-part of the general recorder 8b is the configuration manager 18. This sub-part is for interpreting the FTF file 14 and producing data structures within the resident recorder's memory space that control the operations of the generic recorder 8b. Such things as sampling algorithm selections for a sample rate, hold off times, start and stop sequences, and function key sequences may all be defined within the FTF file 14 to later be translated by this sub-part for the generic recorder 8b, as is subsequently discussed. This dual sub- part configuration allows the recorder to be reconfigured between programs for long, complex recording sessions. It also allows for custom tailoring of the generic recorder 8b even to the specific data being recorded. 1. Sampling Control The generic recorder 8b is developed for flexibility and configurability. These attributes allow recording the operations of a large number of target programs 4. The design is optimized to allow the recording of a minimum of target program 4 transition states. These states are useless in education demon¬ stration and would simply take up space in the memory of the computer. These transition states would also require additional time on the part of the author of an educational exercise to edit out the superfluous information. The number of transition states are limited by control of: 1) a variable sample rate; 2) function key recognition; 3) function key hold off periods; 4) function key minimum and maximum settle times; 5) screen change hold off times; and 6) ignore regions. Variable sample rate allows the recorder to adapt to each target program 4 by examining screen changes at a rate optimal to the performance of the target program 4. Applications programs with little screen change and much computational time need only be sampled once or twice a second. Other application programs with dynamic, unsolicited screen changes need to be sampled more frequently so that the author may be able to single out these state changes and thoroughly discuss them in CBT exercises. The sample rate in this recorder program may be set to as many as 17 samples per second. This is the sample rate of an MS DOS clock in the personal computer 2. Given that there may be a faster source of interrupts on the host machine 2, the sampling interrupt routine may be coupled to the clock of host 6 to attain an even higher sampling rate. Another aspect of the sampling rate is that it may be modified by the recording algorithm. 2. Recording Algorithms The generic recorder 8b uses several forms of recording to perform its task. These algorithms modify the sample rate of the recorder, minimizing the superfluous recorded data and the impact of the recorder on the target application. This is done through the function key recognition and other hold off mechanisms during the recording session. The basic repetitive sampling of the screen occurs at a fixed rate until a screen change occurs or until a function key character is pressed on the keyboard. These events allow the sample rate to vary, thereby giving the target program 4 more access time to the central processing unit ("CPU") of computer 2 to perform its operation. These events also avoid needless intermediate recorded screens. When the user inputs instructions in computer 2 that will cause a relatively large amount of processing by target program 4, the input is first recognized by generic recorder 8b before the target program 4 receives the input. The generic recorder 8b transmits the instructions to program 4 and then goes into a background mode for a sufficient amount of time to allow the target program 4 to finish processing the input. Two techniques are used to effect this sample rate change: 1) "minimum/maximum times to change;" and 2) "fixed hold-off time to change." When the generic recorder 8b recognizes a command sequence to be sent to the target program 4, the minimum/maximum times to change flexibly specifies a minimum number of clock ticks to wait. This minimum number of ticks is specified in the FTF file 14. After that elapsed time, the generic recorder 8b starts sampling the screen again. The generic recorder 8b also waits for the screen to "settle" before it performs the recording of the screen resulting from the particular command. This maximum wait, or "settle time," is again specified in the configuration file of data. The settling of the screen is determined by sampling the screen in the same state twice in a row. Identical screens indicate that the target program 4 has finished processing -the input and is now in an idle mode. The screen is then recorded to a data file 10. The second form of adaptive recording used in generic recorder 8b, is both function key hold off and unsolicited screen change record hold off. Function key hold off is similar to the above minimum/maximum sequence except that no maximum amount of time is specified. The generic recorder 8b, after a fixed amount of time, will sample the screen and record it to the data file 10. The unsolicited screen hold off is for a different situation. There are some application programs that process data in real-time and display that data upon the screen at regular or irregular intervals. These applications produce output without user input. There are two time periods involved in this type of screen state change. First, there is the periodicity of the information generation itself. This time must be taken into account when selecting the all- over sample rate to be used for the target program 4. The second period of time is the time that it takes for the target program 4 to update the screen. This update may be taken into account by specifying a hold off rate in the FTF file 14. This is a specified period of time that the generic recorder 8b will wait, once a sample finds that the screen has changed without user input. The generic recorder 8b will turn off all sampling for a set period of time, allowing the target program 4 the maximum CPU time to complete its calculations and display. The generic recorder 8b then will record the resulting screen to the data file 10. The final recording algorithm is for screen ignores. Frequently, an application program will have a status area of the screen that is of little or no educational value. This portion of the screen may be updated with every keystroke, such as a cursor position read out. The data may be updated synchronously with the keyboard such as a display of a ticking clock. The recorder would normally register these changes and then record screens per change that occurred. This would generate an untoward number of data screen records that may have to be edited out of the data file. The generic recorder 8b is adapted to prevent this by allowing the FTF file 14 to specify a region of the screen that is to be ignored during screen comparing. Any changes within this rectangular region are not considered as a screen change and therefore do not trigger a screen record. The generic recorder 8b allows for separate regions to be defined for both 40 to 80 column video modes. The generic recorder 8b further allows for a trigger phrase on the screen when the ignore region algorithm should be invoked. This trigger phrase is an ASCII string ' displayed on the screen whenever the user wishes the generic recorder 8b to ignore the region of the screen. The phrase may be at any location on the screen. The FTF file 14 specifies the row and column of the phrase along with the ASCII characters to seek. When a difference is found in a screen and an ignore region is specified for that video mode, the trigger phrase is first checked and then the ignore region is called if indeed the string does exist on the screen within the specified position. The row and column may be set to zero by specifying an empty string ("") to indicate that no trigger must be sought before employing the ignore algorithm. In this case, the screen compare routine calls the ignore region compare routine whenever a difference is found in the specified video column mode. 3. Data Management The screen and type-in data for the generic recorder 8b are placed in a ring buffer which is fixed in size at the time that the generic recorder 8b program is compiled. The begin ring pointer and end ring pointer respectively define the beginning and end of this buffer. The write ring pointer defines the point which data goes into the ring. This data is placed into the ring buffer character-by-character, generated by the type-in record and screen record algorithms. The "begin ring write" and "end ring write" pointers define the area of the ring last written to the file. The begin ring write pointer is constantly monitored whenever data is added to the ring buffer. An overrun condition exists whenever the write ring pointer equals the begin ring write pointer. The end ring write pointer defines the currently completed data structure within the ring buffer. This pointer is only moved when data structure has the final record trailer attached to it and is, therefore, ready to be written to the data file 10. Screen records are completed at the time of recording. The record-screen routine places the header in the ring buffer, compresses the screen data into the buffer and then appends the standard trailer at the end of the record. The video mode state is then examined to discern whether or not the screen is in 80 column mode. If it is not in the 80 column mode, the screen record is finished, the end ring write pointer is set to reflect that it is finished, and the routine then returns to the caller. If it is in the 80 column mode, the above procedure is repeated for the second half of the 80 column screen data. The header of this screen record is adjusted accordingly and the end ring write pointer is updated before routine return. The keyboard data type-in record is built up over an extended period of time with the program 4 requiring processing time in-between all recorded keystrokes. A type-in buffer is therefore built through multiple entries into the keyboard section of the code. This requires that some state information be held about the status of the current type-in buffer. The type-in flag tells if a type-in buffer is in progress. The size 1 and size 2 pointer point to the size of the type-in buffer in the data ring buffer. These bytes are updated every time a key is recorded into the buffer. The type-in record therefore "grows" as time passes to hold all of the typed characters in the user's input stream. The end ring write pointer is not updated every time a character is recorded into the ring buffer because, for the CBT 12 to recognize the type-in record, a trailer must be appended to the end of the data. This trailer is appended whenever a screen record is triggered if indeed, there is a type in buffer in progress. The ring buffer write routine is triggered by a fixed number of writes into the ring buffer. The recorder compares the number of writes to the ring buffer to a constant that is defined when the program is compiled. If the number of writes to the ring exceeds this constant, then the write flag is set and the ring buffer is written to disk at the first DOS legal time. This algorithm may be expanded to allow for a write, whenever the buffer reaches a certain percentage fill point that may be specified by the ASCII FTF file. 4. File Management The generic recorder 8b produces formatted instruction files called "paths". The generic recorder 8b requests the name of the file that the recordings are to be placed into at record initialization. This file is then examined to first see if it exists. If it does not exist in the current directory, then the generic recorder 8b creates the file and the recording session appears in the file as target program 4 operations progress. If the file already exists the generic recorder 8b must either append to the existing file, overwrite it, or ask the user for a new file name. This is done upon the first opening of the file. This is indicated by a "chk-exists" flag. The user replies to any of the above options. In the case of appending, the generic recorder 8b must erase the end of file record from the file to which the current recording sessions data is to be appended. This is easily done for this is a fixed length record. The session is then added to the file, ring buffer segment by ring buffer segment, one at a time. The overwrite option merely has the generic recorder 8b create a file on top of the existing file. In the case of a rename, the recorder turns off and then awaits the user to press the start record sequence to input the new file name. 5. configuration The configuration program 18 is written in C and receives information from the generic recorder 8b through the DOS multiplex interrupt. This routine first inquires for the residency of the generic recorder 8b and then requests the generic recorder 8b for the address of the configuration data structure that the generic recorder 8b uses for its operations. An INIT function then reads the FTF file 14, defaulting to a FTF file extension, as is more fully discussed below. 6. Generic Recorder Figures The accompanying state diagram of FIG. 7 shows the possible states of execution for the generic recorder 8b. Being a state diagram, FIG. 7 does not describe process and data flow. Rather, it shows the various states in which one might find the computer 6 after the generic recorder 8b is started. Transitions between states are indicated by the arrows on FIG. 7. The disk symbol CFG FTF File 172 is not a state, but it is placed on FIG. 7 to show that configure 174 requires configuration data stored in a disk FTF file 14. These states may be generalized to configure, target program execution, recorder interrupts and recorder cleanup. Very little of the code is straightforward, linear execution of code in the normal DOS environment. The interrupt states lend a level of complexity to the system that demands exacting care. Based on data from the CFG FTF file 172, configure state 174 performs the following tasks: (1) initialize internal data structures from the function table file in FTF 172; (2) set the key interpretation tables; and (3) arm the start and stop record key sequence. The target program state 178 is entered after configuration 174. The target program state 178 runs as in the regular DOS environment 176, except during the keyboard interrupts 180 and the hardware clock interrupts 182. During a keyboard interrupt 180, control is first sent to the recorder's keyboard interrupt routine. The actions that may be performed are: (1) nothing, the recorder is not activated; (2) recognize the activation key sequence; (3) store away the character generated from the keyboard; and (4) post an I/O cycle for the clock interrupt at 182. During a clock interrupt, the generic recorder 8b moves from the target " program " 178 to the clock state 186. From this point, the actions that may be performed are: (1) nothing, the recorder is not active; (2) compare the screen to its previous state at 188; (3) store the present screen; and (4) perform a disk I/O at 192. Wait disk I/O legal 190 is a state in which the recorder may not perform disk access because some other non- interruptible process is incomplete within the personal computer 6. An example would be that the computer 6 was serving a disk I/O request from the target 4 application when the clock interrupt occured. The generic recorder 8b must delay until it is legal to perform the disk I/O request. FIG. 8 shows the data flow and memory organization between the configure state 174 (while an INIT program 194 is running) and the resident recorder 196. The INIT program 194 reads a specified FTF configuration file 14 from a disk of computer 2. Using an internal, machine-specific, key definition table 198, INIT 194 engages compile routines 200 to compile configuration data and key configuration table data and to insert them into the memory of resident recorder 196 at CFG data 202 and key KFG table 204, respectively. The resident recorder portion 196 performs the recording task chiefly through the keyboard interrupts at 206 and clock hardware interrupts at 208. During recording, both trigger a screen compare routine 210, which compares the present and previous screens to determine if the recorder needs to take any action, as is discussed below. The CFG data 202 tells the interrupts when it is necessary to record the screen to the data file 10. The compress format box 212 represents the previously discussed ring data buffer. The ring data buffer is written to the disk at disc I/O 214, and guided by the stored data path information at 216. The start-up prompt 218 is a screen with the file name request field that appears at record start-up. Most of the memory used by the resident portion 196 of the generic " recorder 8b is contained within the recorder's ring buffer. Several pointers are maintained within this structure. The first pointer is the beginning of the next write to disk. This is the data at risk. The system begins the write to the disk file 10 at this mark. The next is, of course, the next write into buffer pointer. This pointer is the last valid information in the buffer and the end of the write to disk area. The next pointer points at the last type character buffer in the ring. The key CFG table 202 is the master table for interpreting keystrokes from host 6's BIOS. Due to the vast differences between machine keyboards, the BIOS keyboard interrupt routine is called by the generic recorder 8b keyboard interrupt routine. The resulting keystrokes interpreted by the BIOS are maintained within a separate ring buffer within the generic recorder 8b. The scan codes are the same across both PC's and PS2*s of IBM. The start-up sequences, however, must be decoded for each variety of machine. Machine recognition code is provided with a variable that contains the machine type and therefore the keyboard type for direct key number decoding for the turn on and turn off key sequences. FIG. 9 shows the generic recorder 8b divided into the four major parts: (1) monitored interrupt 222; (2) screen multiplex interrupt 224; (3) clock interrupt 226; and (4) keyboard interrupt 228. The generic recorder 8b terminate-stay-resident portion 196 replaces several normal interrupts so that whenever the operating system calls these interrupts, the resident portion 196 will have an opportunity to examine them before they reach the operating system. FIG. 9 describes the fundamental TSR recorder 196 operations, whether in its inobtrusive background state or in its active recording state. The generic recorder 8b reacts whenever one of the interrupts listed in FIG. 9 is called by the operating system. The resident portion 196 replaces the operating system interrupt with a call to a routine of its own from which it will chain back to the normal operating system interrupt. For simplicity, several interrupts are grouped together in FIG. 9 as monitored interrupts 222, since these are examined by resident portion 196 only to see if they are active. This is because the generic recorder 8b must wait while the operating system is busy with a task involving the recorder 8b's ability to do disk I/O. FIG. 10 shows the logic of a general monitored interrupt 222. This control structure is placed into the print screen, video BIOS, and DASD BIOS to maintain the status of the screen. FIG. 10 describes a section of code that is inserted into the aforementioned DOS interrupt vectors. The control transfer may be made to this segment of code through either a hardware generated interrupt 180 or a software interrupt 186 in the target program 178. Each of these routines has a status variable in the resident recorder 196. The status is set at stop 230 to mark that the interrupt is in the process of being serviced. The service routine, which was the original address within the vector table, is then called at stop 232 as if it was a subroutine. The original interrupt service routine does the DOS, hardware, or target application 4's requested work and then returns to the monitored interrupt 310 routine. The monitored interrupt 310 routine then marks the state of the particular interrupt as non-active at block 234 and returns at iret 236 to DOS or the target program 4, whichever was the calling agent. These interrupts, DASD, print screen, and video BIOS are monitored because they are not reentrant and the recorder uses some of these functions. Thus, the recorder cannot execute some of its routines when these interrupts are active. Therefore, the generic recorder 8b must know when these interrupts are active and does so by checking the status variables that these monitors set at step 230 and reset 234. FIG. 9 also shows multiplex interrupt 224 which is described in more detail in FIG. 11. This interrupt will first check at branch 238 to see if the code issued is in the command code for the generic recorder 8b. If it is not, the normal multiplex interrupt routine is called at chain interrupt 240. If the code is the generic recorder 8b's code, the parameter sent to the interrupt will be interpreted. Existence test 242 sees if this code is already present and, if so, calls iret 244 to exit with a flag saying that the system is already loaded. The parameter being examined at branch 242 could instead be a return buffer command, which is tested at branch 246. If it is, inquiry is made at branch 246 as to the location of the buffer that will be used by the C code portion of the generic recorder 8b to fill in control values. However, if the parameter is not a return buffer command, there is simply an iret 250. The third interrupt connection in FIG. 9 is clock interrupt 226, which is further detailed in FIG. 12. The hardware clock interrupt routine is inserted into the hardware clock interrupt stream. All comparison work and screen recording is done within the C code. Most of the major processing is done during this clock interrupt 226. The two major subsections of the clock interrupt 226 are process keyboard information 258, which is detailed in FIG. 13 and which is further detailed in FIG. 14 (regarding handling the record key details) and the C clock code 260, which is detailed in FIG. 15 and other, subsequent figures. FIG. 16 describes a process flags function 302 subset of the C clock code 260, and FIG. 20 describes the record screen 334 function. FIG. 17 details a screen differs 312 operation, which has two subsets: 376 tmp = cmp screen of FIG. 18, and 382 tmp in ignore region which is detailed in FIG. 19. FIG. 12 shows the flow of an assembly routine clock interrupt 226 that traps the system clock interrupts. Upon receiving the clock interrupt signal from a 8253 timer chip in the computer 6, which occurs 18.2 times a second, a counter is set at step 252. The counter referred to is an integer, single word, that is incremented every time this hardware clock interrupt occurs. This counter is used to track timing within the recorder 196. The interrupt 226 routine then moves to branch 254 and checks if the clock interrupt is already being processed. The clock interrupt 226 is called periodically whenever the 8253 timer chip generates an interrupt. This means that the hardware frequently will cause the interrupt to occur while the recorder is still executing code from the last clock interrupt. Branch 254 prevents the routine from re-entering while it is still being executed from the previous request by exiting back to the interrupt point at iret 262. If-branch 254 returns a negative value, the routine moves to branch 256 and checks if the generic recorder 8b is turned on. If it is, the routine moves to step 258 process keyboard and then calls the C clock processing routine 260. If branch 256 returns a no, the interrupt routine is exited at iret 262. The final interrupt listed in FIG. 9 is the keyboard interrupt 228, a detailed discussion of which is postponed until FIG. 21. FIG. 13 shows process keyboard 258, which is called within the clock interrupt 226 to allow the generic recorder 8b to process any keyboard interrupt queued data. This routine examines the configuration table in CFG data 202 so that generic recorder 8b knows how to handle any special key. Special handling includes ignoring and triggering recorded screens. Process keyboard 258 then branchs at 264, which checks to see if a key is buffered. If it is not, process keyboard 258 returns at 266. If it is buffered, branch 268 checks to see if the key is specified in the configuration table of CFG data 202. If it is, then branch 270 tests whether it can ignore the key. If the key cannot be ignored, branch 272 tests if it is a function key. If it is a function key or if it can ignore the key, step 274 sets record wait to minimum/maximum or holdoff. If the key is not specified in the configuration table in CFG data 202 at branch 268 or if it is not a function key in branch 272, then branch 276 checks to see if the key is a type-in character key. If it is not a type-in character key, branch 278 tests if it is an ASCII character. If it is or if it is a type-in character key, step 280 records the key and loops back to branch 264. Otherwise, out of branch 278, the character is ignored while also looping back to branch 264. FIG. 14 further describes record key 280, as shown in FIG. 13. This routine places a type-in in a buffer in the main ring buffer. The keys are recorded with a compatible format within the CBT 12. More particularly, record key 280, at branch 282, tests for an auto exit- condition, where the cursor has jumped to a place not next to the last character typed. If yes, step 284 puts a tab function key in to close the previous type- in and opens a new type-in. If no, branch 286 tests whether to record a type-in buffer. If not recording a type-in buffer then, step 288 sets up a type-in header. Otherwise, branch 286, or the output of stop 288, leads to branch 290 which checks for a type-in position flag. If the flag is on, step 292 gets the position from the compare screen routines subsequently discussed. Otherwise, in step 294, the cursor position is obtained from the hardware. In either case, step 296 records the key and location, updates buffer size, then exits at return 298. The clock interrupt 226 of FIG. 12 is shown with more detail in FIG. 15. The clock interrupt 226 is divided into an assembly code jacket which sets up the C code environment and stack described above as FIG. 12 and the C code in FIG. 15. The C code manages the compare routines and recorder control flags. The flag management is detailed in FIG. 16 and the screen comparison routines are detailed in FIGS. 17 and 18. The C code clock interrupt routine 260 in FIG. 15 is called when the generic recorder 8b is active and a clock interrupt has occurred as described above. The clock routine 260 begins at branch 300 to test for any flags. See FIG. 16 below. If there is a flag, the recorder 8b proceeds to step 302 to process the flag(s) . Otherwise, or if no flag or flags at 300, the recorder tests for, waiting for record, at branch 304. Waiting for record is a flag that indicates to the recorder 8b that a condition exists that requires the recording of the screen into the ring buffer. However, to prevent partly changed screens from being recorded, this integer counter is decremented for the FTF File 14 specified time 304, either in min/max fashion 328 or holdoff fashion 316. When the elapse time occurs, then the recording to the ring buffer is performed by the record screen routine 334. See FIG. 20. If not waiting for a record at 304, the recorder 8b proceeds to bump the sample rate count at step 306. The sample rate counter is used to count the number of clock interrupts that must occur between examinations of the screen to detect changes. The sample rate interval in clock ticks is specified in the sample rate variable in the FTF configuration file 14. This prevents the recorder from using too much of the personal computer 6's processor time while waiting for the target application program 4 to perform some action. Thereafter, the recorder 8b checks whether a time to check the screen has been specified at branch 308, and if not, returns at exit 310 to the calling assembler routine at FIG. 12. If a time to check the screen has been specified at branch 308, the recorder 8b calls the screen different test 312b, which is described in detail in FIG. 17. Generally, though, the screen different test 312b compares the current video refresh memory with a copy of the last recorded video refresh memory, and inquires about a different. If no difference is returned, the recorder 8b returns at exit 314 to the assembly routine at FIG. 12, and otherwise proceeds to branch 316 before a return at exit 320. The set wait to holdoff step 318 is defined in the FTF configuration file as the elapsed time for the recorder 8b to wait once a screen change has been detected via screen different 312 (FIG. 17) . With further regard to branch 304, if waiting for a record is positive, the recorder 8b proceeds to step 322 to decrement the wait count. The C clock interrupt routine uses one wait count variable for delaying all screen records. This is done for simplicity. Once the system is in a state where it is delaying for the screen to be recorded (to inhibit intermediate screen recordings) , this integer variable is decremented at step 322 to zero and then the screen record is executed. The recorder 8b proceeds to branch 324 where, if the decrementing has not produced a zero, the recorder 8b returns at exit 326. Otherwise, at branch 328, the recorder 8b checks if the minimum/maximum routine is active. If active, the recorder 8b checks if it has waited for max at branch 330. In the case of min/max, the elapsed time is set to max after min is waited. When the screen is the same, which is determined by screen different 312, and during the max period for two consecutive samples, the screen is recorded. Screen different 312 appears twice in FIG. 15 to illustrate that it is the same routine, called at different places, with different logic output controlled by its input. If the screen is different at point 312a, the recorder exits 332 without recording the screen. If a no is returned from 312, 328, or 316, or a yes at 330 (which means the screen has continued to change until the maximum time has elapsed) , the recorder 8b records a screen at step 334 and sets the counts to zeros at step 336 before exiting at 338. Generally, a screen comparison is performed at 312 if the sample rate has elapsed. At that time, the screen will be recorded at 334. If the ring buffer needs to be written to the disk, then this routine sets the ring write flag to true. The assembly code then performs the checks necessary for disk I/O. If the checks affirm that it is possible to write to the disk without interfering with DOS, then the write ring routine is invoked. Other¬ wise, the buffer will not be written till a subsequent clock tick or keyboard press. FIG. 16 details the process flags step 302 of FIG. 15, i.e. , flag processing during the clock interrupt 260. FIG. 16 depicts the actions taken when control flags are set. If any flag is on, then the C clock interrupt 260 process flags 302 code is executed. This code is part of the C clock interrupt 260 code. Thus, all returns made in FIG. 16 are to the calling assembly code. The major flags that are considered are the user request end record, write data buffer ring, buffer overrun, user file name request, and user file exists flags. The last two flags are used to get file name information from the user. The overrun flag is set whenever information may be destroyed in the ring buffer before it is written out to the disk file 10. The overrun condition is when the recorder tries to place more information into the ring buffer than there is free room to hold it. This happens when the recorder does not get the opportunity to write to disk but is still getting a chance to process screen and keyboard data into the ring buffer. Writing to disk is the only way that the ring buffer is emptied. The ring buffer is filled by recording screens at step 334 and keystrokes (FIG. 20) . The recorder cannot write to the disk when the DOS operating system is already being called by the target application program 4. This is a DOS reentrance problem. DOS is a single tasking operating system and does only one operation at a time. Therefore, if there is a long series of DOS calls that cause many changes on the screen and the recorder never gets control when the target application program alone is executing (i.e. , not in a DOS call) then an overrun may happen. The first two control flags checked, at branches 340 and 344 deal with this situation by allowing the clock interrupt 260 to return at 346 with the done flag or write ring flag existing. If a test at branch 340 write ring flag is on, an attempt is made to write to the disk at step 342. Therefore, the process flags 302 routine proceeds to check for a done flag at branch 344. Otherwise, at branch 340, the process flags 302 routine proceeds to the done flag branch 344. If user indicates that the recording should stop, via a keyboard interrupt done flag, process flags 302 returns via exit 346. Otherwise, the overrun flag is checked at branch 348, which is set as described above. If the overrun flag test returns positive, the buffer must be emptied; thus it is necessary to cancel all flags, set write ring to true, and return from 350 via 352. Otherwise, if the get file name flag is yes at branch 354, obtain the file name in step 356 so that an exercise recording can be started. Thereafter, or if there is no flag set in branch 354, process flags 302 test whether the file exists. At 358, if the file does not exist, the logic proceeds to create a file at 370. Otherwise, the user is asked whether to overwrite, append, or cancel at step 360. If the user selects to cancel, process flags 302 cancels all flags and sets write to true at 364 before return 366. Otherwise, if no is detected at branch 368, there is a return at exit 374. Finally, if overwriting is desired, step 370 creates a file and returns at 372. FIG. 17 shows screen differs 312, which checks to see if the present screen is significantly different from the previous one. This routine is the driver of the assembly code compare routine tmp=cmp_screen 376, which checks the current configuration and coordinates all the screen compare routines. The screen differs 312 is called after every sample time is elapsed, and calls an assembly routine 376 which compares the screen to see if any target application screen writes have occurred since the last sample was taken. If there is no difference, i.e.. if tmp==0, then return to 380. If only one difference is found it is interpreted as a single typed character. The routine then checks if the typing is in an ignore region of the screen by calling the tmp in ignore region at branch 382. If a positive response is returned, the screen differs routine exits at iret 384 and ignores the screen change. If the change was not in an ignore region, the new screen is saved at step 386. If the type-in position flag is set at branch 388, the character and its position are recorded in the small type-in ring buffers at step 392. Thereafter, 390 and 394 indicate returns. FIG. 18 details tmp=cmp_screen, an assembly routine which uses the 8086 block compare instructions, to quickly compare the video with the last recorded copy. If only one difference is found, the recorded buffer is updated and the location of the difference is returned. If no differences have been found, branch 402 sets the value of cmp_screen to zero and exits through return 404. If more than one difference has been found, branch 406 sets the value of cmp_screen to 1, and returns through 408. This value of cmp_screen is interpreted in branch 378 of FIG. 17 and will cause the new screen to be recorded if the value equals 1, provided the difference is not in an ignore region as tested by the routine in FIG. 19 discussed more fully below. IG. 19 details the tmp in ignore region 382 routine in FIG. 17 used to ignore a rectangular region of the screen. This region is dictated in the SYS configuration file 14. The region ignore may be triggered by a specific phrase appearing on the screen at a specific location. Branch 414 checks for the specific string that should mark the beginning of an ignore region. If not found, the routine exits with a negative value at 416. If a qualify string is found, the routine checks if the changed character falls within the specified row and column range of the ignore region. If it is outside the row range, branch 418 returns a negative value and exits at 420. If outside the column range, branch 422 returns a negative value and exits at point 424. If the difference is within the ignore region (branches 414, 418, and 422 all return positives) , the routine returns a positive value through point 426, and screen differs of FIG. 17 subsequently ignores the screen change and exits at 384. FIG 20. Describes in more detail the record screen step 334 of C clock code at FIG. 15. This routine checks the type-in mode at branch 428 and if active, allows the routine to finish the typing input at step 430. Once finished, the header and screen data is re-ordered at step 432 in the buffer for saving. If a 40 column screen is being recorded, the routine exits at 436. If an 80 column screen is present, step 430 reorders the second header and screen and exits through 440. FIG. 21 is the keyboard interrupt 228. This interrupt is monitored first for the "on key" sequence. When this sequence is detected then the interrupt sets the flags to start recording and to query the user for the file name. Once recording, characters are buffered by the keyboard interrupt and then examined by the clock interrupt where they are recorded. The off key sequence causes a flag to be set and the clock interrupt performs the cleanup task. Interrupt entry 228 KB INT, then step 442, gets the BIOS pointers; and then step 446 calls the BIOS and branch 448 tests for an key up. If yes, iret from the interrupt all release key interrupts are virtually ignored 450. If no, branch 452 tests to see whether to dump the character. If yes, branch 454 tests if the ROM code is in use. If it is, then step 456 replaces the BIOS pointer. If it is not, then there is returns at iret 462. Branch 458 tests if the character is in the handler. If it is, then there is a return at iret 460. Otherwise, branch 464 tests if there is a shutting down request. If there is, then branch 482 tests whether to write the request. If yes, step 484 calls write A to disk routine, then returns at iret 486. If no, branch 466 checks if the character is turned on in branch 466. If it is not, branch 468 performs a single screen request. Thereafter, if no, step 470 set flags for single screen, then returns at iret 472. If yes, branch 474 checks for a turn on recording request. If there is none, there is a return at iret 480. If there is, branch 476 set flags for recording, then returns at iret 478. If step 466 returned yes, then the system is recording. Step 490 checks for a turn off key sequence request. If yes, step 492 set flags to shut down, then returns at iret 494. If no, branch 496 checks for a BIOS OK. If there is none, then step 498 brings on a translate key. Either way the key is then recorded to a small temporary buffer in step 500 where it will be processed by the next clock interrupt. The write request flag is then checked in branch 482, and the write routine called in step 484, etc. 7. CBT State Program Structure Since all the keystrokes and pointer selections are handled in a control loop outside the functions, the CBT 12 code must keep track of where it is at any moment so that on return the information received can be processed properly. This is, in effect, state-driven processing. The major states are referred to by page number. The author menu 20 is page 1. When the system starts initially or returns from completing a task, it usually finds its way back to the author menu 20 or page l state for authors (again note that students have their own first page which they see at start-up and they never have access to the author menu 20 or the authoring functions) . When on page 1 there are certain expectations about what keystrokes are acceptable and what actions to take when those keys are hit. The code will test if p_CBT_Page — 1. There is also a page la which is a submenu off the author menu 20 for the file functions. This is really a separate state in that it has different choices off the menu and two file input fields instead of one. However, as the two are similar in so many ways they can be looked upon as one entity. Page 3 handles the defaults (F10 off the author menu 20) . There are two screens in this mode, one for the session drive and automatic mode time delay, and the other for the super password, default time display, disk drive, light pen adjustment field, and printer type. Page 4 is the single screen utility 56 mode (F7 off the author menu 20) , much like the modify 46 mode for an exercise, but has some special differences. It is, in effect, a one screen work area into which one can bring in, change, and save a single screen. Page 4 is used for the modification of control screens by developers, and for the creation of QSCRs by authors and developers. Two additional submodes (color-ASCII 54 and options menu 52) are available in page 4. The remaining page state is zero and it holds the states and substates involved in selecting and playing back an exercise. Each of these additional states for page zero has a flag value to determine if it is operational. The student registration page is a one screen state that is flagged by whether or not p_Reg_Page is set. When the user runs the critique 38 routine, which allows them to rate the course, he or she will see four screens and the state is identified by p_Crit_Page. If the student has registered properly (or authors and developers have run a student diskette 32 session by selecting F9 from the author menu 20) , the p_Dir_Page flag is set and the course directory is displayed to allow selection of an exercise. Once an exercise is selected (including by entry of its name and selecting Fl, F2, or F4 off the author menu 20 for authors and developers) , the screen that shows what modes are available for that exercise (automatic display 24, manual display 26, instructional 28, or proficiency 30) is called QSCR 4 and this provides another state. Finally, the CBT 12 goes into playback mode and the exercise is run. However, authors and developers can enter modify 46 mode with the selection of an exercise name and entering F4 off the author menu 20. So the user can be in p_Playback_SW and additionally p_Modify_Mode_SW. Modify 46 is where most of the work to customize the exercise is done. In this mode one can also have additional substates. One can bring up the light pen window 57 which will be up if P_LP_Modify-Mode is true. Color/ASCII 54 mode may be selected and identified or the options menu 52 may be accessed for editing (the options menu is active if p_Err_Mess_Select = TRUE) . In modify 46 mode, multiple edits will add an edit_mode flag to identify operators (i.e. D for deleting) . D. Menu Structure Conceptually, it is helpful to understand this invention by looking at each of the menu options to illustrate the possible system flow. The author menu 20 comes up with the following choices: (Fl) Manual Playback (FIG. 25) (F2) Automatic Playback (FIG. 24) (F3) Connect to Live System to Record (Optional) (F4) Modify (FIGS. 29-34) (F5) Print Exercise (FIG. 28) (F6) File Functions (Submenu) (F7) Screen Utility (FIGS. 35-36) (F8) Simulate Student Exercise (FIGS. 22-27) (F9) Student Diskette Session (FIGS. 37-38) (F10) Change Defaults (text) With the exception of the file functions at F6, each of these goes off to a particular task. File functions will take the user to a submenu which is as follows: (Fl) Copy Exercise to Drive A (FIG. 40) (F2) Copy Exercise to Drive B (FIG. 40) (F3) Copy Exercise to Drive C (FIG. 40) (F4) Copy Exercise to Drive D (FIG. 40) (F5) Rename Exercise (FIG. 42) (F6) Delete Exercise " (FIG. 41) (F7) Append (FIG. 43) (F8) Replace Pathway (Text) E. QSCRs A QSCR could be defined as a screen which is added to the recorded pathway since almost every added screen is a QSCR. They must have QSCR in the first four characters in the upper left hand corner of the screen (from screen coordinates 0,0 to 0,3) . It is not uncommon for authors to think of QSCRs as instructional screens since those are the ones that they make. However, the selection error messages are stored automatically as QSCR 2. And the screen that allows selecting the mode to run an exercise for the student is a QSCR 4. One other special QSCR is the QSCR X which is the exercise statement that students can review while doing an exercise by hitting the PgUp key. It will retrieve the last QSCR X that had been seen in the exercise. If none has been seen yet, it will do nothing. The flag p_QSCR_X_Exists is set (first to three then to TRUE once the screen itself is saved in the temporary storage) . All the other QSCRs affect whether a screen is seen or not during playback 22 in the four modes (manual 26, automatic 24, instruction 28, and proficiency 30). A QSCR P will show up in proficiency 30 mode only, whereas a QSCR P will show up in all modes but proficiency 30. QSCR M will show up in manual 26 mode only, while a QSCR M will not be seen if the mode is manual 26 or automatic 24. A QSCR A is automatic 24 only, a QSCR C instructional 28 mode only, and a QSCR B is seen only if manual or automatic. QSCR N will not be seen by users in any mode (it is for authors making notes) , while QSCR with no letter (QSCR blank) will be seen in all modes. There is one other — QSCR G — which provides the option of placing a time delay for the particular screen. All of the playback 22 QSCRs are handled in the code in CBT8.C in P_Display_l which makes the determination of whether to go on to display the screen (done in the function P_Display_2) or move forward. F. Playback With further regard to playback 22 in FIG. 2, recall that an exercise can be "played" in several modes. The mode will determine what screens will be displayed and whether help or error messages will be shown. The four basic playback 22 modes are automatic playback 24, manual playback 26, instructional playback 28, and proficiency playback 30. An exercise may come with the acceptable modes attached to it from a management system. When selected from the course directory or from the author menu, the proper QSCR 4 screen with the available modes will be displayed. The variations available of those four options are housed in a series of compressed screen files CBTS4x.SCR with the x being a number from 0 to 7. The screen will not show the characters to the left of each entry — either (M) , (A) , (C) , or (P) — as they are printed blue on blue. They are there so the line can be pointer device selected — the pointer device routine searches to the left for the parenthesis to read the proper option [(See routine Plpsrch in CBT1.C)]. What users do see is the bar, currently white on red although this can be changed in a control file called stO.pew, and this bar works going up and down by reading the attribute in the first position where an option description is written. This must be yellow foreground. Thus, if modes are available with a gap because one option is not available, the program will search for the next yellow start line and skip the space when displaying the bar. This color key is crucial to the prop ~ er ~ operation of the bar. The Check_For_PTR_Simul routine that handles this is in CBT2A.C. Each mode has a different intent. The proficiency playback 30 mode, or the test mode, does not provide the user with any substantive assistance in the way of procedure lines or explanatory material and requires the correct answer or action be selected before the screen is advanced. The number of correct answers is kept so that a score can be returned. There are variables for number of consecutive errors and total errors and so on, all software and user driven, so that students may be taken out of the exercise and required to start again or run again at a mode where they can learn again. On those exercises without proficiency, the highest mode is instruction. Here, the user is again required to select the proper selection mirroring the expert's selections. However, there are procedure lines and other explanatory materials to guide one along the way, and hitting Page Up will always bring up the exercise statement screen — the one that explains the purpose of the exercise. The other two options are different only in whether the computer or the user advances the screens without valid selection being a requirement. One is manual 26, which requires users to pointer select or enter any function key to advance. The other is automatic 28, where the system will run the screens with a specific time delay and highlight the proper choices as it goes along. The impact of automatic 24 on playback 22 should be apparent (p_Select_Time is one variable tested to see if the mode is automatic) . The instructional 28 and proficiency 30 modes are referred to as the teaching modes and the effects of whether it is teaching mode or not are littered throughout playback 22. The highest mode of an exercise must be completed for the student to get a completion flag for the exercise in their course directory an credit: for the exercise. Several of the author menu 20 and file function items effectively run their way through the playback functions found in CBT8.C. Even when printing an exercise, it is really printing while doing an automatic playback 24, reflecting the fact that the screen is the real data, though unlike automatic playback, printouts see all screens in an exercise. Because the user can enter playback in various modes (Automatic 24, Manual 26, Instructional 28, Proficiency 30) and user-levels (student, author, developer) and options (printing 45 or modifying 46) , the code has an numerous conditional statements and blocks of code that may not be activated during any particular playback 22. In addition, it may be necessary to leave CBT 12 code and go back to the control loop for input. This requires setting flags to remember where the user was, what the user was doing, who the user is and the like, which adds to the complexity of the playback code. Note that any statement in a function in the playback 22 functions loop that is a return with TRUE or FALSE as a condition is actually relinquishing control back to the control loop and some flags will have been set before that call to determine where playback 22 will pick up from. Let's take a simplified approach first and pretend, for the sake of elucidation, that input is obtained locally, to see the actual flow of the playback process itself. The loop begins by initializing all the variables, opening the exercise file, loading in as much as will fit in the big buffer, and then reading the first few records into staging areas. In effect, the first screen will find its way into the buffer that is ready for video display. If an error screen is affiliated with it, that error screen will have been read and staged in the error buffer (p_Err_Buf) . Then the next screen is read and, normally, the process will be stopped as a flag indicating the first screen is a stopping place should have been encountered in header byte seven of the screen following the first screen. This staging process is a key to the understanding of what is happening in playback 22. In essence, each read fills a buffer pbuff and then has its header read into p_Tempbuff. Depending upon the situation, that information is then moved somewhere and the next screen is read into pbuff. Eventually, the buffers ptbuff and p_Low_Buff house the screen displayed (they are filled and then copied to the screen such that the screen and the buffers both hold the same information) . Remember that the default screen size is expected to be 40-character so it takes two buffers to hold an 80-column screen. If an error message was found for the screen it would be moved from pbuff to p_Err_Buf and that would empty pbuff again so another read would be made. At the end, if along the way that stopping byte was found, the final filling of the buffer with nowhere to put the information is the end of this staging. When completed there will always be something in ptbuff (and if 80-column a second half in p_Low_Buff) and in pbuff with the first 38 bytes of pbuff also sitting in p_Tempbuff. P_Tempbuff holds the header information frequently referred to in the code when looking for the next record to see what it tells about what is to come and about what is displayed. It should be noted that if there is typing it comes in a typing record which will set flags and fill p_Typ_Buf in the staging. Note that two QSCRs back-to-back would mean only two reads since a QSCR has no error message and the second QSCR has no place to go so the staging stops. An exercise screen with error and typing might do several reads. Also note that pbuff houses only one record and this means that an 80-column screen is only half-read when the user has stopped reading (the important part — the header which will later tell that the user has an 80- column screen and needs another read — is in pbuff) . Still another caution: as the buffers include reading the three bytes that precede the header but the staged buffer locations do not. Thus, there can be some confusion about the location of a byte. For example, while offset 7 in p_Tempbuff houses the vital From Code flag, it is found at offset 10 in pbuff. In Playback 22, the user has staged information in the various buffers and has data sitting in p_Tempbuff to further describe what's ahead. Now the playback 22 loop begins. The functions are, in order, P_Display_l, P_Display_2, P_Display_3, P_Save_l, P_Move_l, P_Move_3 (Move2 is missing — it became part of 1), P_Move_4, P_Move_5, P_Move_6, P_Play_Sel_l, P_Play_Sel_2, P_Play_Sel_3, P_Proceed_l, P_Proceed_2, P_Proceed_3. These go through till the exercise is over. Depending on who the user is and what the user is doing, some of those may have no use and be skipped. P_DISPLAY__1 This sets up the display of the screen on the video. It checks whether the screen will in fact be displayed (if, for example, it is a QSCR C it is not supposed to display in any mode except instructional and would therefore be skipped and jump the user past the remaining display routines. P_DISPLAY_2 Now the system actually puts the screen onto the video by poking ptbuff to the video buffer (and p_Low_Buff if the screen is an 80-column screen) . P DISPLAY 3 This function will check any page condition (like GoForward or print through some page) to see if it has been met, will increment the displayed page number for modify 44, and finally test whether it should now be waiting for a keystroke or continue forward. P SAVE 1 Before saving what is on the screen the system makes sure the user is not deleting or in the deletion phase of a replacement (in which case the system won't write) . Otherwise, the system saves it in the file if modifying. For both modify 44 and non-modify modes it does some checking for last record. P MOVE 1 This will lead to the next mode — the Selection Error Mode — if an error screen exists. P MOVE 3 If the user is printing, the system now prints. P MOVE 4 If the screen is a QSCR, the system tosses out any error message. In modify 44 this is where the EndOfExe.SCR is displayed if the last record has been reached. P MOVE 5 Now the system is ready to stage the next block since the user has left the original screen. Any errors, etc., use the error buffer and don't need the original screen anymore. While the next screen is now buffered in ptbuff, it will not display until the user reaches the start of this loop again. The system also handles the typing at this point. This is function P_Playback_Typing. P MOVE 6 Gets ready for the next user action. P_PLAY_SEL_1 Basically timer tests. P PLAY SEL 2 Displays the auto block highlight of the correct entry. P PLAY SEL 3 Handles the intensity of the top and bottom lines, something handled differently by the host and CBT. This type of screen adjustment is application-dependent. P PROCEED 1 This tests the user's input. P PROCEED 2 This is another time test. P PROCEED 3 Timers and returns- to handle what to do next. 1. Playback with Control Loop With the above introduction to the flow of playback, add the leaving and returning issues presented by the fact that keystrokes and such are captured from outside the function. In the playback 22 loop there are various exit points. Normal movement through the loop happens when each function returns a value called P_CONTINUE which is in effect an incrementing value returned to the loop to say go on to the next. A new location may be set before that return so the loop increment is reset to a particular playback 22 function to skip unneeded functions and jump ahead. As to the third exit, whenever a playback 22 function returns with FALSE, it is the same as "go to" the control loop to await further action. When an action triggers the return, there are two basic variables involved. The first is P_Play_Int (transferred into P_Int later) that handles the larger issue of what the user was when the user left (a zero means the user is starting for the first time) . If the user is modifying, p_Play_Int will come in with a value of four and the user has an additional variety of locations the user may return to above and beyond those handled by P_Play_Int. There is another variable set when the user leaves called P_Play_Int_Code which will identify the function to be reentered. In the normal process, after the screen has been displayed in function P_Display_3 the program goes to get the next keystroke in modify mode. P_Play_Int_Code is set to return to the next function, P_Save_l. The exits may be timer delays or tests used in the automatic playback 24, where screen advances are at timed intervals. Sometimes the logic waits for the correct answer to be selected. Sometimes the logic waits for the keys to advance to the next screen in modify 44 mode. 2. Playback Flow Charts FIG. 22 details playback 22 of FIG. 2. Step 502, open file, opens the file for reading in the data. Open file leads to step 504 to load the big buffer bytes into RAM which reads in a large piece of the file 10 from memory to RAM of computer 2. Thereafter, step 504 leads to branch 506 which checks whether a working buffer is empty. If it is not empty then playback 22 proceeds to branch 508 where, if the record in the buffer holds an error record, the error record is detected. Playback 22 proceeds to step 510 to move that record from the working buffer to error buffer, and then to step 512 empty working buffer so that the buffer can be refilled by the next record. Thereafter, playback 22 proceeds in a loop back to 506, the branch which determines whether the working buffer is empty. At branch 508, if there was no error record, then playback 22 proceeds to step 514 to determine if there is a typing record. If there is, playback 22 proceeds to 516, a step which moves the working buffer data to the typing buffer, and then loops back to branch 506 via step 512 as shown in the FIG. 22. With further regard to the branch 514, if there is no typing record, playback 22 proceeds to branch 518 which checks whether a virtual screen buffer, which houses the basic page 47, is empty. If the buffer is empty, playback 22 proceeds to step 520, which moves the working buffer data to the virtual screen buffer because, if it is not an error or typing, then it must be a basic page. Thereafter, playback 22 again loops back into the program via step 512. However, if branch 518 determines that the virtual screen buffer is not empty, then all buffers are full, there is no place to store data. Thus, it is now necessary to process the current data. Playback 22 then proceeds to step 522 process playback record which is discussed below. Thereafter, playback 22 proceeds to branch 524, which checks whether the current working buffer data holds the last record flag indicating the exercise is at the end. If the record was set, then the CBT 12 returns to the program at 526. The loop at 506 will read data into the working buffer and find it's proper buffer (error, typing, virtual screen) , until the virtual screen buffer is full, at which time that set of records is processed. At this point the next record is still in the working buffer. Thus, after process playback record 522, the next record header is checked for the last record flag. The error, typing, and virtual screen buffer flags are set to empty at step 528 and the process con¬ tinues as the loop finds the next record in the working buffer when it returns to 506. FIG. 23 shows a more detailed explanation of the process playback step 522. In FIG. 23, the CBT 12 begins with step 538, which tests whether or not to display, as some screens are not displayed depending on the playback 22 mode or user type. With respect to 538, if the answer is no, then the program goes via return 542 back to the program as shown in FIG. 23. Otherwise, at 540, the CBT 12 proceeds to step 544 to move the virtual screen buffer contents to the screen, as is more fully described below. Thereafter, the CBT 12 proceeds to branch 546 which tests whether the tutorial is being played back in the automatic mode. If the answer is yes, the CBT 12 proceeds to automatic playback step 24 and then onto return 542. Otherwise, at branch 546, the CBT 12 proceeds to determine whether the playback is in the manual mode. If the answer is yes, the CBT 12 proceeds to step 26 manual playback and then onto return 542. Otherwise, at branch 548, the CBT 12 proceeds to branch 550 to determine whether it is being played back in the instruction mode. If the answer is yes, the CBT 12 proceeds to step 28, instructional playback, and then onto return 542. At branch 550, if the instructional mode query is negative, the CBT 12 proceeds to branch 552 to determine whether it is being played back in the proficiency mode. If yes, then the CBT 12 proceeds to step 30 proficiency playback 30 and then onto return 542. At branch 552, if a negative is returned, then the CBT 12 proceeds to query whether to enter the print mode. If the answer is yes, the CBT 12 proceeds to step 42 to commence printing and then onto return 542. At branch 28, if a negative is returned, the program proceeds to branch 556 to determine whether enter the modify mode 44. If the answer returned is yes, then the CBT 12 proceeds to step 44 modify and then onto return 542. If the answer returned at the modify mode query of 556 is no, then step 558 is reached. If this happens, there must be an error condition which is processed, and then the program moves to return 542. a) Automatic Playback (Page 1 F2) Like Manual Playback 26, in this mode if called from the author menu, the name of the exercise desired is entered in the exercise name field on Page 1 and the required flag is set by calling P_Automatic_Playback in CBT7.C. Students would select the exercise from the course directory. The function will set p_Select_Time to whatever value is in p_Select_Timel or to 0. PPlaybck is called as the last option in P_Automatic_Playback. Normal stopping places in playback 22 are ignored when in automatic 24 mode. For example, since p_Stop_Each_Screen_SW is FALSE, P_Display_3 simply continues on without going back to the control loop. Most functions have no action to do in automatic 24 mode. P_Move_5 reads the block and automatically display any typing, and P_Play_Sel_l will set a timer interrupt and return until that automatic time delay has been met (the logic sets pseudo interrupt P_Play_Int to 1 and when playback 22 is called again, that interrupt sends the logic back to this function to check the clock again) . There is a final return to the last step in the playback 22 loop, P_Proceed_3, before displaying the next screen. This is to be sure that nothing critical is happening in the control log that requires attention. FIG. 24 details the automatic playback step 24. The CBT 12 commences with a wait time step 560, which causes the program to wait the time delay specified in this automatic playback mode before proceeding to the next screen. Thereafter, the CBT 12 proceeds to branch 562 to determine whether this screen has a user selectable item i.e.. if it is in playback 22 in instructional or proficiency mode. If it is not, the CBT 12 proceeds to return 564. Otherwise, at branch 562 the CBT 12 proceeds to 566 put highlight block on the correct choice to show the right answer. Thereafter, the CBT 12 goes to return 564. Automatic playback 24 mode proceeds without user intervention. b) Manual Playback (Page 1 - Fl) In the normal student playback 22 (or when simulating student with F8 or running a student diskette session with F9) , this mode is selected from the QSCR 4 choices. By selecting 1, Psnot4ck sets the key variables. If called from the author menu, however, the name of the exercise desired to run is in the exercise name field on Page 1 and the required flag is set by calling P_Automatic_Playback in CBT7.C. PPlaybck is called as the last option in P_Automatic_Playback and the user is on his way. In essence, the flow will stop at each raw page and wait for the user to hit any function key to advance. The P_Display_3 function finishes putting up that screen and then returns to await input. In manual mode, once any function key is pressed the answer is assumed correct and the exercise will continue forward to the next page. Any typing will be done for the user. FIG. 25 provides the details of step manual playback 26. The CBT 12 proceeds to step 568 to wait for any user input. In manual mode the screen will change on any user input. It will wait as long as the user wants (unlike automatic playback 24, which plays back in a fixed time interval) before cycling to the next screen. Thereafter, the CBT 12 proceeds to branch 570 to determine whether the input is a selectable item. If it is not a selectable item, the CBT 12 proceeds return 572. However, if a selectable item has been entered at branch 570, the CBT 12 proceeds to step 574 to put highlight block on correct choice (see discussion of FIG. 27 above) . Thereafter, the CBT 12 proceeds again to return 572. c) Instructional Playback FIG. 26 provides the details of step 28, instructional playback. This mode commences with step 576, display procedure line. If procedure line exists, as in instructional mode an extra line of 'advice* is often put on the screen for help, it is displayed. Thereafter, the program CBT 12 proceeds to branch 578 which determines if a type-in record exists. If yes, the CBT 12 proceeds to step 580 get and test type-in, which has the user input the typing, and the program test it against the expected response on file. Thereafter, the CBT 12 proceeds to branch 582, to check the result of the test. If the answer is no, (the response is not correct) then the program CBT 12 proceeds to 584, error count, to see if the maximum number of incorrect attempts has been reached. If this is not the case, then the flow proceeds to step 586 display appropriate error level, where it displays the error message and increments the number of errors. Thereafter, the CBT 12 loops back to step 580 get and test type-in. However, if an affirmative answer is returned at branch 584 (that is, the user has exceeded the max number of attempts) , CBT 12 proceeds to display the correct answer at step 588, and then loops back to 580 again to have the user enter the correct answer now displayed. This looping can be exited by entering a correct answer at branch 582 which, like returning a negative answer at branch 578, leads to step 590 get test action key which is the function key or pointer selection that is required to take the next action which gets the next screen. Step 590 leads to branch 592, which tests the input for the right answer. If a no is returned at branch 592, the CBT 12 proceeds to branch 594, to check to see if the maximum number of incorrect attempts has been reached. If a no is returned at 594, the CBT 12 proceeds to step 596, to display appropriate error message, and increment the error count, and loop back to step 590. Alternatively, if the number of incorrect answers reaches its limit, an affirmative answer is returned at branch 594, and the program proceeds to display correct answer at step 598. Instructional playback 28 then loops back to step 590, and can be exited by returning a yes at branch 592 which leads to return 600. d) Proficiency Playback In proficiency there are two error counts — one to test number of errors on any one item and one for testing the number of errors on the total exercise. Exceeding the maximum on either aborts the exercise. The first case is to protect against the user being stuck on a subpage forever if he is unable or unwilling to find the correct answer. With regard to FIG. 27, detail is shown of step proficiency playback or test mode 30. The flow proceeds to branch 602 to see if a type-in record exists. If an affirmative answer is returned, the CBT 12 proceeds step 604 to get and test type-in. Thereafter, the CBT 12 proceeds to branch 606. If it is not the correct answer at 606, the CBT 12 proceeds to branch 608 to ask if this is the first error made in this selection. If a no is returned, then the CBT 12 proceeds to branch 610 to ask if the error count is the maximum for this item. If a yes is returned at 610, the logic proceeds to quit 616. Otherwise, at branch 610, the CBT 12 proceeds to step 614, to display an appropriate error message and increment the item error count. The logic then loops back to step 604. At branch 608, if a yes is returned, the logic proceeds to step 612 to increment the maximum error count. Only the first error on an item increments this exercise error count because an error on a particular selection should only count as one error in the exercise, even though there may be multiple errors made on that single selection. The program then moves to step 604 to continue looping. However, if a no is returned at branch 602 or a correct answer is returned at 606, the CBT 12 proceeds to branch 618, to check if the maximum error count has been exceeded. If it has, then the CBT 12 proceeds to quit 616. Otherwise, the CBT 12 proceeds to step 620, to test the action key for the function key or pointer hit required to advance to next page. This step leads to branch 622 to verify the user input as correct. A negative answer returned at this branch will lead to branch 624, to check if this is the first error of this selection. Again incrementing the exercise error total only once but allowing, in branch 632, for more than one error on the item's specified error count. If a no is entered, the CBT 12 proceeds to branch 626, to check the error count max for this item. If a no is again returned, the CBT 12 proceeds to step 628, to display the appropriate error message, increment the error counter, and then loops back to 620. Alternatively, if a yes is entered at branch 624 the CBT 12 proceeds to step 630, the maximum error count is incremented and then moves to the test at branch 632 of the max error counter. If a yes is return from either branches 626 or 632, because the user has exceeded either the max allowed item or exercise errors, the flow leads to quit 616. However, if a no is returned at step 632, the CBT 12 loops back to step 620 via step 628. This looping is exited at branch 622 by receiving a yes when a correct action key is entered, to lead to branch 634. At branch 634, the last record flag is checked to see if the end of the exercise has been reached. If it has not, the CBT 12 proceeds to return 636. Otherwise, the CBT 12 proceeds from step 634 to calculate and display score at step 638, before also ending at return 636. 3. Print Exercise (Page 1 - F5) Printing an exercise calls the playback 22 routine with some special flags set. The exercise will flash on the screen while printing, which is like the automatic playback 24 in that the system advances through the exercise on its own. When F5 is selected off the author menu 20 the CBT_Charaσter_Trap function evaluates the keystroke. In this case, it ends up calling a function to handle page one activity and that translates the F5 to a 'P', which is interpreted in CBT7.C in the page 1 routine that handles the selections. This translation is to preserve an original implementation. The flags are then set (p_Print_All is set TRUE, p_Dont_Print set FALSE, p_First_Print to TRUE, p_Modify_Mode to TRUE, p_Repeat_Playback to FALSE, and p_Stop_Eaσh_Screen_SW to TRUE) , and then reset to FALSE before starting the printing. After these flags are set PPlaybck is called. The exercise is loaded and the initialization function P_Play_Init will then set additional flags if p_Print_All is TRUE. These are p_Start_Page_Nbr as 1, p_End_Page_Nbr as 999, p_Wait_Each_Char_SW as TRUE, p_B_F_Number as 10,000 (this is to assure that the logic goes to the end because this is one test of whether the user has finished) . The logic does reset p_Stop_Each_Screen_SW to FALSE. Then beginning the loop to put up screens, with the switches set so the user does not stop at each screen, screens keep on displaying. P_Move_3 will actually test p_Start_Page and p_End_Page and, if they have been set and meet the test, a print is required and the logic calls p_Print_This_One. In the print routines (in CBT14.C), the error screens, if any, are read into p_Scr_Buff into fixed locations such that if there is a selection error it will always appear at a certain row on the printed page, a typing error at the following rows, and finally the page itself. Each page is printed with header information. FIG. 28 details the print step 42. This step starts at step 640 and prints the screen on the printer device. Thereafter, the CBT 12 checks to determine if an error message exists (i.e. f was there data in the error buffer) at branch 642. If there is an error message, the error message is also printed by means of step 644. If no error message is determined at branch 642, or after the error message has been printed at 644, the CBT 12 proceeds to typing buffer 646. From this branch, a yes response leads to step 648 which prints typing buffer information. Thereafter, or if a no is returned from branch 646, the CBT 12 proceeds to return 650. 4. Modify (Page 1- F4) To modify an exercise by selecting F4 off the page 1 menu, information on any individual screen can be changed by typing in new information. Colors and graphic attributes may also be changed. The expected location of the pointer hit, the function key needed to advance, or any error messages may also be changed. By selecting this option, the user will set the p_Modify_Mode_SW to TRUE, p_Stop_Each_Screen_SW to TRUE, p_Repeat_Playback to FALSE, and p_Select_Time will be set to zero. The user is off to PPlaybck. When the user modifies an exercise, he is frequently returned to the control loop for the next action unless one of the "hurrying" situations has been specified (e.g. the END key — see below) . It is a full step-by-step process. Even if advancing in a hurry, each screen mode will be flashed onto the screen and eventually saved as the new file. Recall that the screen is the real data and modify will always rewrite the file if the user advances all the way to the end (an ESC key will exit without saving if done before the end) . The user can enter modify mode and go through to the end without change and have the same exercise as before, but it will still build the new file along the way and save it (see subsequent discussion regarding the .100 file). There are four "modify states" which describe the type of page being displayed. The basic page is the raw page (R) . If there is typing, the typing error message state (T) is next. The selection error page (B) is the more normal second mode to appear. Any extra edit pages would show up as (E) . For the purpose of talking about pages, only an R type increments the page counter with the others being subsets of that R page. The three display functions will put the screen up, but in modify 44, P_Display_3 must also deal with page numbers and the row and column location of the cursor. The logic is here that determines whether the page number needs to be incremented before display. In addition, this function also tests whether the page number has reached the end point of the go forward command (if end page is greater than the current one the logic keeps going) . In P_Save_l, deleting screens (and the delete part of a replace) are handled by not writing the screen to the .100 file. Keep in mind that when a screen is deleted information about the prior screen is also deleted. That information must be saved to pass to the first screen that follows the one(s) being deleted. P_Move_l leads to the selection error mode. If there is such a mode for the screen and the user is stopping at each screen, the user will return to the control loop with p_Play_Int_Code equal to two for returning back to the next function. P_Move_3 handles the print test — recall that all printing is done like a hurrying modify 44 mode. If the page falls between the start and end page numbers, the page is printed. QSCRs have no error messages so P_Move_4 kills them if they were found. If the last record is reached, the logic will get the ENDOFEXE.SCR, a screen to inform the author that the last record has been reached from the disk. Generally, modify 44 does not involve selection testing in any way so the P_Play_Sel and P_Proceed functions have no real impact. As to the keys hit to affect the way modify works, the keys and their actions are found in CBT2B.C. If advancing by using the F9 key, one will advance one modify state at a time, seeing any error and extra edits screens. If F10 is depressed, the system will be advanced through any error and extra edits to the next raw page. The F10 key activates a flag, p_Speed_Modify, that will keep the user going until the page is a raw page, at which time the flag will be reset to FALSE and the logic will stop at that display and wait for input again. If the END key is depressed, the logic will be put into a hurrying mode which is like a fast automatic display where the screens will all fly by until stopped or until the exercise reaches the end. A '+' or ESC will stop the hurrying. When END is depressed, it sets P_Play_Int_Code += 50 and p_End_Flag to TRUE. Then, in playback, p_Stop_Each_Screen_SW becomes FALSE and p_B_F_Number is set to 10,000 to assure that the logic will head toward the end. Insert and Delete have word wrapping word processing implications depending on the borders encountered — word wrapping uses the discovery of a box character or the borders of the screen as the wrap border. Two control key combinations can be used if the screen being modified is a 40-column screen. Control-V will center that 40 column screen in 80 column mode (a centered reduction) . Control-L will take the screen and reduce it and place it on the left side of the screen — i.e.. the 40 columns in 80 modes takes the first 40 characters of each line, leaving the other 40 for instructional data. F2 will toggle the options menu 52. If F6 (or Shift-6) is hit, the logic will be put into color/ASCII 54 mode where the color or graphic attributes can be changed and boxes can be built. This key toggles to turn on and off this mode (although ESC will also turn this mode off) . The last line of the screen will have a bar which, when F6 is hit, allows shortcuts for the color, graphic, or window options. For example, C may be hit to get the full color chart displayed. Selecting F5 will bring up the light pen window which allows a change in the location of the selection that should be made to advance from the raw page. This information will be the row and column of the pointer device hit if the information is within the valid screen rows on a page. Currently a 25 as the row indicates that the action can be a function key and the determination of which key is found on the window and placed into the column specification. Note also that F5 can only be used when in selection error mode since the information it saves is, as noted before, information about the record before — the raw page. For display purposes, the row and column are incremented by one so users can see the count begin at one, not zero, for the top line. This is also the case whenever the cursor row and column are shown on the screen. When modifying an exercise, the letter M will appear in the lower right hand corner of the screen. The letter M is put there by calls to Pchangec. P_Blink_Marker handles putting into line one (actually line zero) the page number and the modify state name. P_PutRC puts up the row and column location of the cursor. Modifying is at the heart of the system because it is what allows pathways to be turned into planned, organized exercises. FIGS. 29-34 provide a more detailed description of the modify 44 subsystem of the CBT 12. FIG. 29 provides a flow chart of the overall structure, logical functions, and sequence of steps of modify 44, and FIGS. 30-34 describe in further detail various subparts of modify 44. Modify 44 uses a .100 file for the screen data. Therefore, to modify requires "rewriting" the file from start to finish. When proceeding through the .000 file in linear order, and screen to screen, another copy of the screens is being written to a newly opened file which has the same name but an extension of .100. At the end of the modify 44, the .100 file has all of the screens with any changes. The original .000 file is then replaced by the .100 file which now becomes the exercise. Because of this, the flow is structured to go forward in modify 44 mode. However, some liberties have been taken with repositioning the original file and the .100 file in multiple edit mode. This involved repositioning the pointer in the .100 file and starting the linear write. For example, an insert might add several screens to the .100 file and then have the user says no on saving the insert. This requires the pointer to be moved back so that next write will overwrite the beginning of the inserted material. Since a last record is written, effectively an end-of-file marker, it will not matter that there is additional material in that .100 file from an aborted insert that did not get overwritten. The linear "rewrite" is also the reason that all information passes from the .000 to the screen and then to the .100 file. Hence if choosing to modify and then hitting the END key advances to the end, all the .000 screens will flash on the CRT. Another key reason for this is that the modify routine will "correct" some deficiencies that might have occurred in processing a file. For instance, if an exercise screen was turned into a QSCR with an error message, the next modify would recognize the QSCR and skip the error message display. Without the display, no error record is written to the .100 file and hence the file is cleaned up. FIG. 29 begins at entry point 44. The initial step 652 of modify 44 is to wait for input from the user. At branch 654, the program checks for the status of the options menu 52. If the options menu 52 is active, the program enters options menu mode 52, as is more fully discussed below. If the options menu 52 is not up, branch 656 checks the status of the options menu toggle key. If the toggle key is in the active position, modify mode 44 proceeds to step 658 to turn on the options mode menu 44. After the options menu mode 52 has been turned on, the logic returns to wait for more input at step 652. Returning to branch 656, if the menu toggle key status is not obtained in the options menu mode 52, the program proceeds to branch 660 to check the status of the print or forward flag. If the print or forward flag is set, the program moves on to branch 662 to see if a stop key or stop page has been reached. If this is the case, the program then returns to the wait for input step at 652. Otherwise, the program moves on to branch 664 to see if printing is desired. If the answer is yes, the program moves on to step 666 and prints a page or pages, as per the user's request. However, at this point this mode is processing one page at a time in a loop so that the first page is printed, and, if more pages exist to be printed, the flag for print or forward has been set. Printing stops when the flag is turned off again. Next, the program proceeds to save the printed screen or screens at step 668. If the response to the query at branch 664 was no, modify 44 skips the print pages step 666 and proceeds directly to save screen(s) at step 668. Once the screen(s) have been saved, modify 44 exits and returns at 670 to the point from which modify was entered. Returning to branch 660, if the print or forward flag was not set, the program continues to branch 672, where the program checks the status of the page advance key. At this section, the logic is testing for keys. If the key struck is the page advance key, that key will skip the error modes and go to the next basic page. There is also a key for advancing to the next mode which moves from the basic page 47 to selection error 48 or, if in selection error, to typing error, if there is one. The mode advance continues through the exercise one mode at a time. More specifically, if the answer at branch 672 is positive, the program moves on to branch 674 and checks whether the multiple delete 48 or replace 52 modes are active. If the reply is yes, the program exits at 670 from modify 44 and returns to the point from which it was called. If the status at branch 674 is negative, the program moves to save 676 where a screen is saved before returning through exit point 670 to the point at which modify 44 was entered. Returning to branch point 672, if the test for the page advance key was negative, modify 44 moves to branch point 678 where the status of the mode advance key is checked. If no, the program moves to step 696 to check the color/ASCII toggle key at branch 696. Otherwise, the CBT 12 moves to branch 680 to check for a multiple mode. Delete, save, replace, etc., can be for more than one page and hence when selected, the user is placed into multiple mode. This mode is ended by hitting the mark key and appropriate actions are then finalized. If the response is yes, the logic moves on to branch 682 to check for multiple delete or replace 682 modes. If the answer is yes, the program leaves modify 44 through exit point 670. In the alternative, the p og m moves to step 684 which saves screen(s) and advances the page. Thereafter, the CBT 12 exits modify 44 through point 670. Returning to branch 680, if the program is not selected to run in multiple mode, it moves on to branch point 686 to check for an error message. If an error message is desired at branch 686, the program checks to see if the error mode has been completed. If it has not, the logic advances to the selection error mode 48, where the error message can be modified. After the execution of the selection error mode in step 48, the program returns to its original wait for input step 652. If the response at branch 688 is positive, or the status at branch 686 is negative, the program moves on to branch 690 to see if the typing error mode is to be selected. This is a routine for editing a typing error message. If the response is positive, the program moves to branch 692 to check if the typing error mode has been completed. If the program has already modified the error message, for this screen, the response is no, and the program moves on to step 50. After executing the typing error mode of step 50, the program returns to the wait for step 652. However, if the response at branch 692 is positive, or the response at branch 690 was negative, modify 44 moves on to step 694. At step 694, the program executes a save, and then exits from modify 44 through exit point 670. Note that in an exercise, there is the basic page, and usually a selection error mode 42 that allows editing the error message that will be displayed during playback 22. The typing mode 50 allows changing the typing that the user inputs or any typing error message. Returning to branch point 678, if the mode advance key status is negative, the modify 44 moves on to branch point 696 and checks the status of the color/ASCII toggle key. If the key is toggled on, the modify 44 moves on to step 54 in FIG. 29 and enters the color/ASCII mode. After executing step 54, modify 44 returns to the original wait for input step 652. If the color/ASCII key is toggled off, the modify 44 moves on to branch 698 to check if the escape key has been struck. If it has, the modify 44 moves on to step 700 and verifies the particular desire triggered by the escape key. Modify 44 proceeds to branch 702 to see if the escape has been verified. If the answer is yes, the program executes a quit through exit point 702. That is, at this point, the program quits modifying the exercise and restores the exercise as it was before the user started, and returns the user to the menus. Returning to branch 698, if none of the escape keys were struck, modify 44 moves on to branch 706 to check the status of the LPW action key. If the key is toggled on, modify 44 proceeds to step 58. The LPW mode 58, as in FIG. 2, is entered. After the execution of step 58, modify 44 returns to its original wait for input step 652. If the light pen toggle key is off, from branch 706, modify 44 proceeds to branch 708 to check if a character has been typed. If no character has been typed, modify 44 returns to the wait for input step 652. If a character has been typed, however, modify 44 moves to step 710 which echoes the character typed at the cursor location on the screen. Once this has been accomplished, the program returns to the wait for input step 652. a) Options Menu FIG. 30 describes in further detail the functions and logical progression of the options menu mode 52. The options menu mode 52 in FIG. 30 is entered from step 52 in FIG. 29. FIG. 30 describes the various optional functions the user may employ to do editing. In general, this part of the program presents the user with a menu of various options and then accesses the various functions as the user chooses them. The portion of modify 44 described in FIG. 30 allows the user to enter an option key, then executes the function triggered by that key, and returns to subsystem modify 44. Most of the options available to the author while in modify mode 44 or in screen- utility mode 56 can be found on this separate menu called the options menu 52 which is displayed on the bottom two (if 80 column) or four (if 40 column) lines of the screen. This menu is on the screen when p_Err_Mess_Select is set to TRUE. In modify mode 44, if the user advances through the various stages past the raw page to the error modes, the user will have the error message and options menu 52 pop up at the same time. The function P_Err_Mess_Options in CBT3.C controls the menu; with TRUE as a parameter it will put it up and FALSE will take it down. Along the way either the selection error window or the typing error routines will be called (depending on whether p_Type_Err_Mess is TRUE or not) though if no error awaits those routines will do nothing. The p_Do_Authors_Menu and p_Undo_Authors Menu control here and they both save and display the old area of the screen displaced by the menu and windows. The menu can be light pen selected or chosen from a keyboard input of a plus followed by the letter of the choice as shown in the menu. The plus part is handled by the versatile variable p_Select_SW which is set TRUE when a plus key is hit. Light pen selection will set that variable TRUE and send the character in the parenthesis down just as if the key had been hit on the keyboard. (The select switch is used for other options like page one choices where an Fl is turned into a plus one and processed. The P_Select_ SW variable stops the logic when the user is in modify and hits the END key — this variable must always be reset FALSE to avoid having unexpected results when a key is hit) . P_Err_Mess_Options will do a little bookkeeping before handling the options selected. P_Err_Mess_Options tests the printer, sets off p_Delete_Screen_SW if on and the next key was not an F to verify the delete, and tests some conditions against the EndOfExe screen (the last screen message displayed in modify mode) . There are five options " that are allowed in both modify 44 and screen utility mode 56 — L, R, S, P (though it will only do the one page showing in ScrUtil) and F. The other options are allowed only in modify mode. Option P is for printing, which will evaluate the start and end pages based upon the input entries sitting in those fields in the menu. The function P_Setup_No_Print_Pages will see the start and stop pages and p_B_F_Number is set to p_End_Page_Nbr. The flag p_Stop_Each_Screen_SW is set FALSE so the playback will continue on until the condition is met (i.e. , the end page is reached) . Then the p_Play_Int flag is set to four and the menu is taken off the screen * Option L ' is the list directory which will display a directory based upon the file specification in the lower right hand corner of the options menu 52. A flag will be set indicating a directory is showing (p_Author_Dir_SW) and the calls to the directory writing functions in CBT7.C will be made. Note that the file specification allowed is based upon who the user is (developers can do anything but authors are restricted to TDS and 000 files) and can include wildcards (PR*.TDS is all TDS files beginning with PR and that is all that would be displayed) . The name is filled in and conditions explored in P_Setup_File_Name in CBT3.C. Option R is for replace and will act differently on ScrUtil then modify mode. The replace in Single Screen Utility 56 is really more like an import a single screen facility in replacing whatever was active or showing. The function insert_Single from CBT18.C is called to display the file (screen) indicated. In modify 44, any replace, whether single or multiple, will follow the deletion of other screens. Thus, at this stage, there is a verification that the replace file exists, temporary pointers are set to the existing file to restate the condition if the user decides later not to accept the replacement, and there is a return to playback 22 after taking off the options menu 52. Replace is like a delete followed by an insert. In modify 46, replace will act on an edit_mode of R and simply not write the screens until the user indicates the stop place of the delete (using option M which follows) . Options S, or save, acts upon the file name shown in the lower right corner of the options menu 52. If there is a single screen extension (like TDS) identified by the user, a simple Save_Screen call is made and the save is finished. If on ScrUtil this is the only save possible so any invalid file extension would cause a return. For multiple saves, the edit_mode flag is set to S, the output file open using open_edit_mode_save_file in CBT18.C, and the p_Play_Int set to four for a return to playback 22. While in S edit_mode copies of the screens will be written to both the standard .100 file and to a file for the new save. The M option will close out the save. Option F, the delete screen option, allows the user to flag a screen for deletion. The first time encountered, F sets the p_Delete_Screen_SW flag to TRUE. It must then get another plus F or light pen F to actually call the function P_Delete_Screen. Any other key will reset the flag to FALSE and requires the user to select F again to start the delete. If delete, the directory will be reset (through a recursive call to this function with an L parameter) . Option G allows going forward in the exercise in modify mode. There are two options — a number that will bring up to that page number, or an F# where # is the number of pages to be advanced (i.e. , the user is on page six and, to go forward five pages, enters either F5 or 11) . The function P_Setup_No_B_F_Pages is called to set the ending page. p_Stop_Each_Screen_SW is set FALSE so the exercise will run until the desired page (the ending page) is reached. The options menu 52 is taken off and the variable watched is p_B_F_Number. Options 0-9 and X will fall through into the insert section for a single insert of a screen. The character will be placed in front of the word NEW to call a file called 4NEW.SCR for a blank 40 columns screen and is also +4 on the menu, or 8NEW.SCR for an 80-column screen which is set when the filename is blank - see function P_Setup_File_Name. In essence, p_Make_QSCR is set to the character and insert_single will then be called in the insert option (see Option I, below ) . If there is a p_Make_QSCR value that file will be called through the p_Get_Or_MAKE_QSCR function and the screen will be displayed. Option I, insert, will be selected by +1 but the first part of the code can come from the 0-9 and X options above because those cases fall through to insert. If there is no proper single file extension in the file specification found in the lower right corner of the options menu 52, or it is not on a raw page, the system will return. Otherwise, insert_single in CBT18.C handles the insert. This inserted file is not a part of an exercise until the program moves past, where the file is written to the .100 file. In order to delete or save that screen and any possible changes to the file, the edit_mode value of I is retained after the insert. There is also a need to reset the p_Record_Type at the EndOfExe screen if the user inserted at that spot so that the end screen will be redisplayed after the inserted screen is saved. A multiple insert will save the existing file pointers (.000 and .100 , though if the .100 doesn't exist yet, a value of 0L is set) and then calls insert_exercise in CBT18.C. Once completed, the user will verify that the insert is accepted and if NOT the files will be reset to their old state. Insert_Exercise wrote the inserted screens to the .100 file and in a manner not visible to the user. In addition, the screen that was displayed when the insert was selected will still be the one on the screen even if the insert is accepted. The value of p_Play_Int_Code is dependent upon whether the insert is accepted or not, and in all cases the options menu 52 is their removed and exercise modification continues. Option D, delete, will always require an M option to indicate a stopping place so there is not single or multiple. The edit_mode flag is set to D and p_Speed_Modify set to true so the user can only advance and thereby delete from raw page to raw page. The screen up at the start will be the first deleted. A +D followed immediately by +M will delete just that screen and any affiliated screens (error and typing and extra edit screens) . If the edit_mode is I on entry, a single screen has just been inserted and only in this case will a +D by itself actually delete (there is no advance and no reset since the screen is not "real" yet — the program just reset what was in ptbuff[] from the last real screen of the exercise) . Option M, the mark, indicates the end of a multiple edit action — replace, save, or delete. If edit_mode is R a number of .000 pages have been 'skipped' in the .100 file and the new single or multiple file will be inserted (using insert_single or insert_exercise) . The page number for display must reflect the pages not written and the new pages written. If a user decides not to do the replace at the end, the pointers saved before are used to reinstate the condition before +R was hit. If edit_mode was D the header information found in bytes 7-8-9 of the first screen deleted will be placed into the header of the first screen that is NOT deleted (the one which +M was hit from) since this information is always about the screen that precedes and the current one is now the one following, not the one(s) deleted. In addition, the page count is adjusted. Finally, if the edit_mode was S, a final P_Saveb call with parameter X is made so that a last record is placed at the end of the screens being saved thus far. The system then continues processing the current screen. The edit_mode variable is used primarily in CBT3.C and in the playback 22 functions to control multiple insert, save, replace, and delete (although it is used briefly in a single insert and a ScrUtil replace to deal with being able to undo the action) . Simplistically speaking, an S will be read by the playback 22 routines to write a second copy of the output file starting from where the S was hit until an M is hit. A, D, or R will flag the situation so that when it comes time to write the screen to the .100 file the write will not be done and an incrementer of the number of times (or pages) this is done for is made so an adjustment can be made. In the case of D this is all that is required and an M finished things. With an R, the code now differs in that an insert is now required as the second half. Almost all edit_mode and multiple routines not part of the original CBT 1.0 can be found in CBT18.C. To properly reinstate the condition as it was before a multiple in the case of the user saying that he does NOT accept the action, the p_Tempbuff_Full flag must be set to FALSE to begin staging from the pointer indicating the active record. p_Tempbuff[2] must also be set to zero and p_Err_Mess_Waiting to FALSE, to avoid adverse impacts on how the restaging is done. The page number is then decremented and will be incremented again when the screen is restaged. Because delete, insert, replace, and save all have different actions and are caught at different places in the loop, there are some localized impacts in both the accepting and rejecting of any of those options as an attempt was made to keep from making any real changes in the heart of the playback code since it had proven stable. P_Setup_File_Name is used in the options menu to weed out certain improper conditions and then to set up both p_File_Name_file which is the file specification used for the processing and p_Save_File_Name_File which is the variable used for what is displayed in the lower right corner of the options menu 52. There are checks for DOS characters (those allowed are A-Z, 0-9, _ , -, and blank). If listing directories the asterisk and question mark are allowed. If not a developer the user can only have files with TDS or 000 extensions and 000 is allowed only if the option is R, I, S, or L. File names like .EXE or .COM are not allowed. This function also handles the list situation if a name is blank the function substitutes an asterisk so that all files will be listed. The cursor movement on the options menu 52 is handled with the Cursor Table assignments with functions like P_Wrap_Table_Set in CBT3.C providing the start/stop values for p_Cursor_Table[] . These table values are used as offsets for positioning the cursor. In FIG. 30, starting from entry 52, the program reads an option keystroke entered by the user at step 712. The input keystroke is examined to determine which of the various option keys the user has entered. Branch 714 checks to see if the option mode toggle key has been pressed. If the option mode toggle key is the one that has been entered, options menu mode 52 moves to step 716 which turns off the option mode. The logic then exits through return 718 to modify 44 at step 52 in FIG. 29. If the input key was not the option mode toggle key, the program moves on to branch 720, which checks if the program is in multiple edit mode. If the answer is yes, the program moves on to branch 722 and the part of FIG. 30 that describes the multiple mode functions. Branch 722 checks to see if the input is the marked end key, whereupon the program exits through point 718 and returns to modify 44 as described above. If the mark end key was the input, the options menu mode 52 moves to branch 724, which checks to see if the function is the save mode. If this is the case, the program moves to step 726, where the save file is closed and the multiple flag is turned off. The program then moves on to step 728 and verifies multiple command, which is the last chance for the user to indicate the intent to make this choice, and then exits from options menu mode 52 through point 718 as previously described. At branch 724, if the option desired was not the save mode, the prcrgraia moves " o to branch 7 0, which inquires whether the desired function is the delete mode. If yes, the program moves on to step 732, where the multiple delete flag is turned off, and then on to step 734. At step 734, the verify multiple command is executed. The program then exits the options menu mode 52 at point 718 and returns to modify 44. At branch 730, if the desired option was not delete, the program moves on to branch 736 and checks if the .desired option is replace. If yes, the program moves to step 738 where the multiple replace flag is turned off. Then, at step 740, a new file is inserted, and, at step 742, the verify multiple command step is executed prior to a return 718 to modify 44. If the program is in multiple edit mode at branch 736, the mark end key has been pressed at branch 722, but the mode is not save, delete or replace, the program automatically exits at point 718 and returns to modify 44. At branch 720, if multiple mode is not selected, options menu mode 52 continues to branch 744, which also checks if the mark end key has been input. If the answer is yes, the options menu mode 52 returns through exit point 718, as previously described, i.e. , to modify 44. If the mark end key has not been pressed, the program moves on to branch 746. At branch 746, the program examines the key entered at step 712 and asks if it is the exercise statement key. If the answer is yes, the program moves to step 748 where an exercise statement template screen is inserted. An exercise statement template screen is just a special screen that outlines the objectives of the exercise for the student. The template screen is inserted at the spot in the exercise that has now been reached (i.e.. the student is advancing in the exercise page-by-page) . After step 748 has been executed, the program exits from the option menu mode 52 through point 718, and returns to modify 44 at point 52. At branch 746, if the input does not match the exercise statement key, the program moves on to branch 750. At branc 750, the input is checked to see if it is the blank 40 character screen key. If yes, the program moves to step 752, which causes a blank 40 column screen to be inserted. These inserts are being made into the tutorial screen file after being "moved" over to make room. Modify 44 reads the old file and then saves to a new file. Thus, insert is actually just writing to the new file 10. Then, when the next old file screen is read and written, the new file 10 is located in sequence after the previously inserted file 10. At the end, the new file 10 is given the name of the old file, thus deleting the old file 10. Once step 752 has been executed, the program exits the options menu mode 52 through point 718 and returns to modify 44 at point 52. At branch 750, if the input does not match the blank 40 character screen key, the program moves to branch 754. At branch 754, the input is checked for a match with the delete screen key. If the delete screen key has been pressed, the program moves to step 756 to carry out the required function. Step 756 sets the multiple mode delete flag and saves the current pointers. The program then moves to step 758 where the exercises advance to the next page. After step 758, the program exits the options menu mode 52 through point 718 and returns to modify 44. At branch 754, if the input key is not the delete screen key, the options menu mode 52 moves on to branch point 760 and asks if the input key is the replace key. If the answer is yes, the options menu mode 52 moves to step 762 to set the multiple mode replace flag and save the current pointers. After this is accomplished, the options menu mode 52 moves to step 764. Step 764 tests the validity of the replacement file. At branch point 764, the options menu mode 52 asks, is the replacement a valid file. If the answer is yes, the program exits from the option menu mode 52 through exit point 718 and returns to modify 44 at step 52. If the replacement file is not a valid file, options menu mode 52 indicates this and returns to require reentry. At braπctr 760, if the input key is not the replace key or a valid file, the options menu mode 52 progresses to ' branch point 768. Branch 768 checks whether the input key is the save key. If it is, the options menu mode 52 moves to branch 770. Branch point 770 checks to see if the file name entered, that is the destination file for the save, is a valid name. If it is, branch 772 asks whether the file name is an exercise file name. Exercises are files with an extension tacked onto the name (.000). However, the user can automatically save single screen 774 which are not exercises but which are files. If the user saves using a single screen name, it is not necessary to set a multiple flag. If the answer at 772 is no, the program moves to step 774 and saves the screen in the file named at branch 770. After the screen has been saved, the program exits from options menu 52 through exit point 718 and returns to modify 44. If the file name examined at branch 772 is an exercise name, step 776 sets the multiple mode save flag. From step 776, the program advances to the next page of the tutorial at step 778. The program then exits from the option menu mode 52 through exit point 718 and returns to modify 44. At branch 768, if the input key is not the save key, the program continues to branch 780. Branch ' 780 checks to see if the input key is the insert key. If the answer is yes, the options menu mode 52 moves to branch 782 to see if the input file name is a valid file name. If the file name is not valid, like replace, options menu mode 52 would show the error in name on the screen. If the file name is valid, branch 784 checks to see if the input file name is the same as the exercise file name. If not, the options menu mode 52 moves to step 786 which inserts the present screen into the file identified by the file name. The options menu mode 52 then exits at exit point 718 and returns to modify 44. If the response at branch 784 is yes, the options menu mode 52 continues to step 788. Step 788 inserts the screen into the exercise file and saves the current pointers. The options menu mode 52 moves σirtor step 790, where- a verify multiple operation is conducted, and after completion, returns to modify 44 through exit point 718. Returning to branch 780, if the input key is not the insert key, the options menu mode 52 moves to branch 792, which checks the input to see if the list directory key has been struck. If yes, the options menu mode 52 moves to step 794 and builds a directory from the file specification and displays it on the screen. After the directory has been displayed, the options menu mode 52 exits through exit point 718 and returns to modify 44. Returning to branch 792, if the input key is not the list directory key, the options menu mode 52 moves to branch 796 to check for the go forward key. If the input key matches the go forward key, the options menu mode 52 moves to step 798, which evaluates the number of pages to advance and sets the advance page flag. After this is accomplished, the options menu mode 52 exits through exit point 718 and returns to modify 44 at step 52. Returning to branch 796, if the input key is not the go forward key, the options menu mode 52 moves on to branch 800 to check for the print key. If the input key is the print key, step 802 sets the number of pages to print by setting the appropriate flag. After step 802, the options menu mode 52 exits through exit point 718 and returns to modify 44. At branch 800, if the input key is not the print key, the options menu mode 52 advances to branch 804 to check the input key to see if it is the delete file key. If yes, at branch 806, the options menu mode 52 asks if the specified file exists. If the file does exist, branch 808 asks the user to verify the delete. This extra step prevents any accidental deletions caused by the user. After the delete command has been verified, step 810 deletes the file from the file directory. After the file has been deleted or if the responses to branch point 806 or 808 were negative, the options menu mode 52 returns to modify 44 through exit point 718. Returning- to branch 804, if the input key was not the delete file key, the options menu mode 52 advances to branch 812. Branch 812 checks to see if the directory list is up on the screen. If yes, the options menu mode 52 moves to branch 814 and asks if the key is a valid list mover. If yes, the options menu mode 52 moves to step 820 and moves the list highlight bar. After completing step 820, the options menu mode 52 leaves at exit point 718. If the response at branch point 814 is negative, branch 816 checks to see if the input, usually the enter key, is to make a selection from the directory list. If the answer is yes, the options menu mode 52 moves to step 818 where the selected choice is put -into the file name slot. After step 818, or if the response to branch 816 was negative, options menu mode 52 exits through point 718 and returns to modify 44 at step 52. b) Modifying Screens In modify mode 46 and in screen utility mode 56, an author can modify recorded screens or create new screens. Once in modify mode 46 (p_CBT_Page is zero and p_Modify_Mode_SW is set to TRUE) or in screen utility mode 56 (p_CBT_Page is four) , any page can be changed using special editing functions. The situation is that the screen has been displayed and input is now desired. If alphnumeric keys are typed, they will appear wherever the cursor is and wrap around will be in effect. The standard creation of boxes and colors and graphics come from hitting F6 which will toggle the color/ASCII line across the last line of the screen. The flag p_Clr_ASCII_SW will tell the user if the color/ASCII mode, as F6 is known, is on. With that area there are two flags for when the user is changing color or ASCII graphics characters, p_Color_Mode and p_ASCII_Mode, that determine whether the respective window is on the screen. These color/graphic issues are toggled and controlled with the function Pattrib in CBT4.C. This function handles- calling, processing", and leaving color/ASCII mode 54 although ESC or an F2 for options menu 52 can also take down, restore, or leave these modes. The current color and attribute can be found on the first line of the screen just after the row and column. They are placed there when the function P_Put_RC is called. When the user is entering color/ SCII mode by hitting F6 or Shift-6, the 25th line display is shown which has the current most logical colors displayed with the numbers 0-7 along with the letter key codes for other options like C to see the color chart and S for single-line box or D for double-line box. With this line up, the keys would be handled by Pattrib as indications of the action to take. If the key is C the function will call P_Display_ASCII with a C parameter to bring up the color chart, set p_Color_Mode, and await further action above. If a G it will call p_Display_ASCII with an A and put up the graphics chart, set the p_ASCII_Mode, and return for action. Note that a parameter of R is then used to restore the old screen underneath the window when the user has finished making a selection. Note also that it is currently possible to make a choice outside of the displayed window and the system will accept that graphic or color attribute at the cursor position as the one selected. Most of the other options will call one of four functions — Psetchr, Psetatt, P_Color_Box, and P_Line_Box. The Psetchr function sets the current cursor location character or graphic to the current active ASCII character code or the one reflected in the choice made from the possible choices on the 25th line, while Psetatt will set the attribute at the cursor location to the current color or the one reflecting the color number selected on the 25th line. The coloring and lining of boxes, used in creating, painting, and erasing or removing things related to and within boxes, are also handled in this function. The box functions must worry about start and stop locations and use the upper left and lower right corner markings to determine the box- size-. The first time the color box function is called the row-column is marked, and the second time will result in getting the row-column pair to end and a box will be filled with the color attribute from the character at the first coordinate — an N colors the box but a B parameter will color and blank out the area as well. The p_Color_Box_SW variable toggles whether it is first time in or not, just as p_Line_Box_SW does for the line routines. The line box function is similar in operation with S for single and D for double lines and an R for blank border. The S-D-R parameter is evaluated, in effect, with the second call which actually draws. Besides drawing boxes and changing colors, the user can use other editing tools much like word processing to put anything on the screen. There is a minimal word wrap facility that essentially uses the border or window characters as the begin/end boundaries for any wrapping around while typing. Arrow keys will get the user out of any window unless the options menu 52 is up, which is the case when the user edits an error message window, for example. The CTRL-L and CTRL-V combinations can have an effect on a 40-character mode screen being edited. The screen mode is switched to 80 character mode and if the option is CTRL-L the original 40 will be flush left in columns 0-39. If the CTRL-V is chosen, the 40 are centered (hence in 19-59) . The function is P_Center_Display in CBT3.C. c) Selection Error FIG. 31 is a more detailed description of the logical functioning of the selection error mode step 48 of FIG. 30. Generally, the selection error mode 48 allows creating and editing error messages. FIG. 31 explains how error messages are created and modified and how the screens containing the errors can be changed. From its call in step 48 in FIG. 30, the selection- error"mode 48 enters the flow chart of FIG. 31 through entry point 48. At this point, the selection error mode 48 saves the current screen at step 822. Next, in step 824, the selection error mode 48 displays the error message and the options menu 52 window. Later, when an exercise is displayed via playback 22, the error message formed in step 824 will explain to the user what error has occurred, and provide the user with options as to how to proceed. When the error message in the option menu 52 has been displayed, the program moves to step 826 and waits for an input keystroke. Once an input key has been struck, the program moves to branch 828 to see if the option menu error message has been displayed. If it has not, the program proceeds to branch 830 to check the status of the option message toggle key. If the option message toggle key is on, the program moves to step 832 to restore the option menu error message and return to step 826. If the reply at branch 828 is yes, or at branch 830 is no, the program proceeds to branch 834 to check the status of the advance key. If this key has been hit, step 836 accepts the present screen as it is displayed. That is, the user has finished changing error message or didn't make a change. After step 836 has been executed, the program exits the selection error mode 48 through exit point 838 and reenters modify 44 at step 48 of FIG. 29. At branch 834, if the advance key has not been hit, the selection error mode 48 proceeds to branch 840 to see that a valid option menu item has been selected. If a valid item has been selected, branch 842 asks the user if he would like to print the present screen. If the response is yes, step 844 prints the screen. After the screen has been printed, the selection error mode 48 returns to step 826 and awaits further input. If, at branch 842, the user did not want to print the screen, branch 846 and asks the user if he would like to save the present screen on disk. If the answer is yes, step 848 saves the present screen. Once the screen has been saved, or if the user has responded no to the screen save query, the selection error mode 48 returns to step 826 and awaits further input. If, at branch 840, a valid option menu item has not been selected, the selection error mode 48 moves to branch 850 and checks to see that a valid key has been pressed. If it has not, then step 826 gets a new input. However, if a valid key has been pressed, step 852 is where the input keystroke is processed, and the error message is changed accordingly. From step 852, the selection error mode 48 returns to step 826 to await further input. The selection error mode 48 automatically exits when the advance key is hit. d) Typing Mode FIG. 32 details the typing error modify mode 50, which saves the current screen at step 854, which saves what will be 'replaced' by the window so it can be reshown or restored if user toggles into options menu 52. This leads to the display typing error message window 856 before reaching step 858, which gets input keystrokes. This option allows the user to 'exit' the typing error message. The CBT 12 proceeds to branch 860, where it checks if the options menu 52 and the typing message are on the screen. If they are not, branch 862 asks if the key input at step 858 is the options menu 52 toggle key. If yes, step 864 restores the option menu typing message and loops back to get input at step 858. Otherwise, at branch 862, the typing error mode 50 proceeds to branch 866, which looks at the cursor to see if it is in a valid typing location. If it is not, mode 50 loops back to get input keys at step 858. However, if it is, the typing mode 50 proceeds to step 868 to change the type-in character by putting the input key onto the screen, and then loops back to get input keys at step 858. However, if a yes is entered at branch 860, the typing mode 50 proceeds to branch 870 to check if the advance key was hit. If the advance page or advance mode key is hit (see 'modify' keys) the user is satisfied with what is on the screen. Thus, if a yes is entered, the typing mode 50 leads to step 872 which accepts the screen as displayed and leads- cr retαrir 874. If a no is entered at branch 870, the flow leads to branch 876 which checks whether a valid key has been entered. If a valid key has not been entered, the program loops back to step 858 to get input keys. Otherwise, the typing mode 50 proceeds to branch 878, which allows the user to enter a value that will set the level of matching required for the type-in to be accepted. The author could specify that anything is an acceptable type-in or may specify up to an exact match with all letters exactly as intended. If a yes is returned, the typing mode 50 proceeds to step 880 to change level of accuracy required, which in turn leads back to step 858 get input keys. If there is a no at branch 878, the typing mode 50 proceeds to step 882 to process the key as a change in typing error message itself, that is, the key hit in step 858 was in the typing error message box and it is used to edit the message. e) Color/ASCII FIG. 33 describes in further detail the functioning of color/ SCII 54 from FIG. 2. The program enters the color/ASCII 54 from modify 44 in FIG. 29. In general, this function allows the user to modify the color or graphic components of stored screens. The flow chart of FIG. 33 begins at entry point 54. Branch point 884 checks to see if the color/ASCII mode is already on. If it is, the program moves to step 886 and turns the color/ASCII mode off. The program then exits from this function mode through exit point 888 and returns to modify 44. Returning to branch 884, if the color/ASCII mode is not on, the program moves to step 890 and turns the mode on. The program then moves to step 892 and waits for an input keystroke. Branch 894 checks the input keystroke to see if it is the color toggle key. If it is, branch 896 checks to see if the color mode is on. If it is not, the color/ASCII 54 moves to step 898, turns on the color chart, and then returns to step 892 to await further input. If at branch 896, the color mode is on, the color/ASCII 54 moves to step 900, turns the color chart off, and returns to step 892 to await further input. If the result at branch 894 is negative, the color/ASCII 54 moves to branch 902. At branch 902, the color/ASCII 54 checks for the graphic toggle key. If this key was input, the logic moves to branch 904. Branch 904 checks the status of the graphic mode. If graphic is on, the color/ASCII 54 moves to step 906, turns the graphic chart off, then returns to step 892 to await further input. If at branch 904 the graphic chart is off, the color/ASCII 54 moves to step 908, turns graphic chart on and returns to branch point 892 to await further input. If the graphic toggle key was not the input key, the color/ASCII 54 moves from branch 902 to branch 910. Branch 910 checks the input character to see if it is the box character. If it is, the color/ASCII 54 moves to branch 912 and checks if the box start has been marked. If at branch 912 the box has been marked, the color/ASCII 54 moves to step 914 and draws a box. If it has not, the color/ASCII 54 returns to step 892, after marking the box start in step 916, to await further input. Note that the user hits a special key when in color/ASCII mode 54 to indicate that the user wants to start a box with that spot as the upper left cor¬ ner. Because the box is now marked, the next time through, the box character will draw the box using the second spot as lower right. If, at branch 910, the input character is not the box character, the color/ASCII 54 moves to branch 918 to see if the attribute code has been entered, and if so moves the color/ASCII 54 to step 920. Step 920 changes the attribute at the cursor and advances the cursor one space. From step 920 the color/ASCII 54 returns to step 892 to await further input. If the input key is not the attribute key, the color/ASCII 54 moves from branch 918 to branch 922 to see if the graphic code key has been input. If it has, step 924 changes the graphic at the cursor to the active graphic and advances the cursor. After step 924, the color/ASCII 54 returns to step 892 to await further input. If, at step 922, the input key is not the graphic code key, the color/ASCII 54 moves to branch 926. Branch 926 checks for the color/graphic code and if it matches the input key moves to step 928. Step 928 changes both the color and graphic at the cursor and advances the cursor. From step 928 color/ASCII 54 returns to step 892 to await further input. If the response at 926 was no, the color/ASCII 54 moves to branch 930. Branch 930 checks for the paint code key. If the paint code has been entered, the color/ASCII 54 moves to branch 932 and checks the paint start point. If the paint start has been marked, step 934 paints the marked area with the active attribute. After step 934, the color/ASCII 54 returns to step 892 to await further input. If the paint start has not been marked, the color/ASCII 54 moves to step 936, marks the paint start and moves to step 892 to await further input. If the paint code has not been entered, the color/ASCII 54 moves from branch 930 to branch 938. The branch 938 checks for erase code. If the erase code has been entered, the color/ASCII 54 moves to branch 940 and checks for the erase start marker. If the erase start has been marked, the color/ASCII 54 moves to branch 942 and erases the marked area. After the erasure, the color/ASCII 54 moves to step 892 to await further input. If at branch 940, the erase start has not been marked, the color/ASCII 54 moves to step 944 and marks the erase start. After step 944, the color/ASCII 54 returns to step 892 to await further input. If, at branch 938, the input key is not the erase code, the color/ASCII 54 moves to step 946. If the color/ASCII 54 has reached step 946, the input key is not a valid key within the C/A mode. At this point, the color/ASCII 54 returns to step 892 and awaits further input. Note that there is no space on the flow chart that will allow an exit from color/ASCII mode 54 past branch 884, the color/ASCII toggle key. However, in fact the logic actually leaves color/ASCII after every keystroke. K. LPW CBT 12 will react to any pointer hit, putting together the row and column, but before it can act the CBT_Pointer_Trap in CBT1.C will check whether the hit is valid to handle and/or whether the program should either process it or not. If the CBT12 is to process a light pen hit the user can evaluate it several ways. The CBT12 can simply look at the row and see if it is a valid row on the screen showing as in a selection from the exercise directory, going on then to highlight that row's entry. The CBT12 can take the location and begin a search to the left top find the parenthesis and then take the character between parentheses as the selection, examples being found on the options menu 52 or on the Page 1 menu (handled by function Plpsrch in CBT1.C) . To know what to highlight is often a case in CBT of looking for two blanks between choices. Another feature comes from the use of functions keys as pseudo-light pen hits with the row equal to 25 and the column equal to the code for the desired function key. This is how a recorded screen tells what key was hit if a function key was hit to advance instead of a light pen or pointer hit. Function keys are translated that way and passed through CBT_Pointer_Trap for some processing on occasion. Light pens, mice, or other alternate devices to the keyboard simply make for having to look at a few places to see where the input came from, and evaluating it properly. In the control loop, a pointer hit is first and then the keyboard. FIG. 34 is a flow chart that describes in detail the change light pen modify mode 58 from FIG. 2. Light pen modify mode 58 changes the action key to advance from a screen in playback 22. FIG. 34 begins at entry point 58, which corresponds to step 58 from FIG. 29. The first step in FIG. 34 is step 950, where in the light pen modify window is displayed on the screen. After displaying the modify window, step 952 and awaits user input. When the user has hit a key, branch 954 checks to see if the input key is the escape key or the light pen modify toggle key. If it is not, branch point 956 checks to see if the input key is some other valid key. If it is not, the modify mode 58 returns to step 952 and waits further input. If at branch point 956 the input key is a valid key, the modify mode 58 moves to step 958. Depending on the particular key input at step 952, step 958 changes the action key or light pen location values accordingly. After step 958 has been accomplished, the LP modify mode 58 returns to step 952 to await further input. If the input key is the escape or light pen modify toggle key, the modify mode 58 moves from branch 954 to step 960, which turns off the light pen modify mode, to exit at point 962 and returns to modify 44. G. Screen Utility (Page 1 - F7) In the simplest sense, this function is a single screen option that allows users to create, change, and save single screens using screen painting and editing functions. These functions are effectively also available in modify 44 mode, except that p_CBT_Page is equal to 4, whereas in modify 44, it is equal to zero. The major use for this option is in the creation of instructional screens (QSCRs) and, for developers, in the modification of control files. Since most control files are in fact nothing more than stored screens which are read in without display and assigned to their proper variables, these screens are to be modifiable. When beginning this function the system displays a screen (with Screen Utility written in a box) . The options menu 52 is displayed across the bottom on the assumption the user will want to immediately bring in a different screen or bring in a blank 80 column screen to author (done by a +R with a blank file name) . While the options menu 52 has many options, there are only a few of those in this mode. One is to (R)Replace. Conceptually, this is like bring in the screen from file — it essentially replaces the current display with the one requested. Generally, to look at a screen or change a default or control screen, this option should be used. The user can also (S)Save so that whatever is created can be saved. The option to (L)List files is available here as well. The Page 1 menu selection of F7 triggers the call to this function. The logic begins by resetting the key to the RESETKEY (F7) and setting the Function_Key_SW to TRUE again, before calling the CBT_Character_Trap function. This time CBT2B.C does the page one translation and, if the user is not on page la, the logic turns the key into the SP2KEY. Thus, when the logic falls on down the line of calls in the trap, it hits the function key call, and finds that it is on page 1 with SP2KEY hit. This sets p_CBT_Page to 4, pcolorl to FALSE, displays the screen utility screen, sets p_Clr_ASCII_SW to FALSE, turns off p_Select_SW, and then sets the cursor and goes to the control loop. FIG. 35 describes the functions associated with the single screen utility 56 shown in FIG. 2. In general, the screen utility 56 allows the user to manipulate screens. These manipulations include changing the color or adding alphanumeric, or graphic characters to a screen so that the screen: could eventually be inserted as is in a tutorial file; or could be a template screen that would be inserted into a tutorial and then customized; or could be a system control file screen. The utility 56 can also be used to add help message data to an existing screen. The screen utility flow chart begins at point 56 on FIG. 35. Step 966 loads a default screen identifying that the user is in screen utility. Step 968 displays the option menu 52. The program moves to step 970 and awaits user input. Branch 972 checks for the escape key. If struck, the escape key causes the program to abort the screen utility 56 function and exit through point 974. If the escape key has not been struck, the program moves to branch 976 and checks for the color/ASCII toggle key. If the color/ASCII toggle key has been hit, branch 978 checks to see if the color/ASCII 54 mode is already on. If yes, the program moves to 980, turns the color/ASCII flag off, and returns to step 970 to await further input. If the color/ASCII mode is not on, the program branches instead to step 982 and turns the color/ASCII flag on. The program them returns to step 970 to await further input. If at branch 976 the color/ASCII toggle key was not struck the program moves to branch 984 and checks for the options menu toggle key. If the option menu toggle key has been struck, the program branches to branch 986 and checks the status of the option menu 52. If it is on, the program turns it off at step 988 and if it was off the program turns it on at step 990. After toggling the option menu mode either on or off, the program returns to 970 to await further input. If the input key was not the option menu toggle key, the program moves to branch 992. Branch 992 checks if the color/ASCII flag is on, whereupon the program branches to 994. Branch 994 checks if the input keystroke is a valid color/ASCII option key. If yes, the program proceeds to 54 and calls the function process color/ASCII. This is the same function as color/ASCII 54 shown on FIG. 2 and explained in the part of the specification that describes modify 44. If the input was not a valid color/ASCII key or the color/ASCII flag was not on the program moves to branch 996, which checks if the option menu 52 is on and, if so, branch 998 checks if the input is a valid option menu key. If yes, the program moves to step 1000 and calls process option function, as is described more fully below. Once 1000 has been completed, the program returns to program 970 and awaits further input. If the option menu mode was not on, or the input was not a valid option menu key, the program moves to branch 1002. Branch 1002 asks if the input is a valid keystroke other than the keys examined in branches 972, 976, 984, 992, and 996. If the keystroke is valid at 1002, step 1004 displays the keystroke at the cursor location, and returns to step 970 to await further input. If at branch 1002 the input is not a valid keystroke, the program ignores the keystroke in step 1006 and returns to step 970 to await further input. FIG. 36 is a flow chart describing the sub- function, process option, entered at point 1000 on FIG. 35. This function allows the user to perform a number of operations on the subject screen. The flow chart is entered at point 1000 and proceeds immediately to branch 1008. Branch 1008 checks to see if the input is the exercise statement key, and if so, moves to step 1010 and inserts an exercise statement template screen. The program then returns to step 54 through exit point 1012. If the response at branch 1008 is negative, the program moves to branch 1014 and checks if the input is the blank 40 character screen key. If yes, the program moves to step 1016 inserts a blank 40 column screen, and then returns to step 54 through exit point 1012. Branch 1014 checks for the replace key. The replace keystroke, moves the program to step 1016. Step 1016 checks to see that the present file name is a valid file name. If the file name is valid at branch 1018 the program moves to 1020 and inserts the named screen, replacing the current screen at that point in the file. The program then returns to step 56 through exit point 1012. If at branch 1018 the file name is not a valid file name, branch 1022 checks if the file name is blank. If the file name is blank, the program inserts a blank 80 column screen, replacing the current screen in step 1024. In either case, the program then returns to point 56 through exist point 1012. If at branch 1014 the replace key has not been struck, the pro¬ gram moves to branch 1026 and checks for the save key. If the save key has been struck, the program moves to branch 1028 and checks that the present file name is a valid file name. If not, the program aborts at this point and returns to step 54 through exit point 1012. If the file name-is- alid, the program- mo es " to step 1030 and saves the present screen in the specified file, before returning to point 56 through exit point 1012. If at branch 1026, the save key has not been struck the program moves to 1032 and checks for the print key. If the print key has been struck, the program moves to step 1034, and prints the present screen on the output printer. The program then returns to step 56 through exit point 1012. If the exercise statement key, blank 40 character screen key, replace key, save key, or print key have not been struck, the program will end up at step 1036. At this step, no legal option has been entered, so the program aborts.the process option sub-function and returns to step 56 of the screen utility function. H. Student The author or the developer may run the student subsystem from a diskette like the students by changing the session drive to A (using defaults —F10 off the author menu 20) before running this option of F9 off the author menu. Selected from the CBT7.C page 1 options, the p_CBT_Page is reset to zero and the function P_Start_Student in CBT4.C is called. This will display the student start-up screen and set p_Reset to three and p_Temp_Student_SW to TRUE. It positions the cursor off the page and calls the control loop to await a keystroke. That keystroke goes through CBT_Character_Trap, gets to P_T_Char_Process_Reset in CBT2B.C which will reset - 101 - the p_CBT_Page to zero, set pdemof to 33, and call the registration page with P_Reg_Page. That function will, of course, display the registration page (see subsequent registration page 5 descriptions for processing information) . Upon successful entry, p_Reset is put to zero, pdemof to 33 to be sure, and PMasterm is called. Based on the current state, pdemof being 33 is the key, the next call is to bring in and write the course 10 directory information from the diskette (all this in PMasterm). The function P_Dir_Page found in CBT10.C is called. It displays the directory (note if returning from an exercise, the program would return to this area of PMasterm with the flag p_Dir_Page_Was_SW set so it would 15 not get the directory again from disk but merely display by calling P_Dir_Page with a FALSE parameter) . After display of a directory, the logic returns with the state P_Dir_Page_SW set TRUE. CBT_Character_Trap or CBT_Pointer_Trap will take the input and, with a valid 20 selection, the user will be ready to go forward to the mode selection or QSCR 4 screen. A selection on the directory page is handled by Process_Selection in CBT10.C and, if valid, the functions sets p_Dir_Page FALSE and p_Sn4_Page_SW and p_QSCR_4_SW to 25 TRUE. The exercise name is placed in the file specification and it is necessary to select the mode to run the exercise in. The QSCR 4 is displayed and the system waits for a keystroke or pointer hit. From the traps, the hit is evaluated and the 30 character passed to the function Psnot4ck which sets the flags. This function is called from CBT_Pointer_Trap or from CBT_Character_Trap based on p_Sn4_Page_SW being TRUE. There is a condition that allows a call in the playback key handling in CBT2B.C to allow a change of mode during 35 the playback 22 by inserting a QSCR 4 into the exercise. This option has not been used. Psnot4ck, found in CBT1.C, will set p_Play_Mode_Flag (to 'A' for automatic 24, 'M' for manual playback 26, 'C for instruction 28, and 'P' for proficiency 30) and p_Select_Time (as 100 for instruction and proficiency 30 and 101 for manual 26 with the automatic 24 time being whatever was found in variable auto_demo_time — set from the default screen) . In addition, the instruction 28 and proficiency 30 modes will set the p__Teaching_Mode_SW to TRUE. When running playback 22 there are branches on p_Select_Time and on p_Teaching_Mode_SW. The flags for automatic 24, manual 26, instructional 28, and proficiency 30 are A, M, C, and P, respectively. The function calling Psnot4ck will also set p_Play_Int to four before returning above. This will cause CBT_Low_Interrupt to call the function that starts the playback - Pplaybck. On return from the exercise, pdemof will be 33 and p_Dir_Page_Was_SW will be TRUE and the user can continue in this directory - exercise loop for as long as the user wishes. With further regard to student interaction, the CBT 12 provides a student registration page. Once the student hits the Enter key from the first introductory screen (or once the author/developer has used the F9 option the page one menu) , the state of the system is identified by p_Reg_Page_SW being TRUE and the function P_Reg_Page is called. The majority of registration code can be found in CBT9.C. The process begins by the reading of the registration file (CBTREG.DAT) off the diskette. This is an ASCII text file that can also come from the management system and is rewritten if the information is changed or entered for the first time (keep in mind that as an ASCII text file the rewrite requires an end-of-file hex 1A) . Once the file is opened and the data read into the array p_Reg_File_900, that array is parsed to give values to the various fields, if any. The temporary holding variables, all declared in the CBT9.C source file outside any functions ; are last_name[], first__name[] , middle_initial, soc_sec_no[] , department[] , work_center[] and entered_pwd[] . The parsing is done by parse_reg_file. If there is a password already, the p_Ident_Good flag is set to TRUE. The registration page itself is then called from disk and displayed. The information on file is filled in and the process of entering the data begins. If there is a password, the cursor is placed on the password field where the user can enter the password and continue to the course directory. However, they could use TAB or arrow keys to update any information. If no password exists, the cursor is taken to the first field since it is a new registration. The student is then asked to enter data field by field. The student cannot leave a required field if there is no data in it, and these are last name, first name, student id, and password. The system now captures keystrokes at the local level. However, it still needs to leave for help upon hitting Shift-Fl for help. It therefore sets up a global variable to hold the field that it was on when help was requested, and sets P_Play_Int to 88 so that on return to the Reg_Page function (it will come back due to the state) it will not reread the file or retrieve the screen again. The input function is Full_Fill_In and will fill in the data (if the p_Play_Int was not 88) for all fields before going to the field sent down as a parameter for its first entry. There is a loop on the variable value that is incremented after each successful entry to bring one through the possible cases and, finally, to the password field. Password entry is either a call to create__new_password for a new user — this requiring the entry of the password and a second entry to verify that no typing error was made — or to enter_yerify_password where the test of an existing password is made. Enter_verify will accept either the real password, or a super password that the administrator of the system would know in order to help students who forget their password to continue. There is a default password of RANDALL that comes with the system and this is p_Sup_Ident_Code. If no password is found on the default control screen then it places the super password into p_Def_Ident_Code. Otherwise the default one is p_Def_Ident_Code. Once that default password is entered, the program will call create_new_password just as if the student were new. If the user accepts the entries, the p_Reg_File_900 array is reassembled using a sprintf with the temporary fields in the function build_reg_file, and then written out with a final CR,LF, and CTRLZ in P_Write_Reg_Data. This file would now be ready for the management system importation if the information on students was being tracked. Once the user has completed the registration, the P_Reg_Page function will set p_Reg_Page_SW to FALSE and pdemof to 33 and sends the user back though PMasterm. In effect, this will call the course directory (based on the value of pdemof) . Developers can hit the END key when the registration page is displayed while running a student diskette session to bypass having to enter any data including password. 1. Student Start-Up Flowchart FIG. 37 explains the limited mode of student interaction with the program, and this subsystem may well be provided to the students on a diskette separate from the rest of the program. FIG. 37 commences with step 1038 which displays a first screen 1038, which is a welcome or introductory screen. After the first screen is displayed, there is a wait for a start-up key in step 1040. This start-up key input leads to the load registration template screen 1042, in which the registration page with blanks where data will go, is displayed. Step 1044 allows a student to load in registration data, including; name, ID, work number, and a password at the end. Step 1044 leads to branch 1046 which queries whether the student has been previously registered by checking if any data already exists. If no previous registration, the CBT 12 proceeds to request registration data to be entered by the user in step 1048. This leads to branch 1050 which checks whether all the registration data has been successfully completed and entered. If it has not, then the program CBT 12 loops back to the display first screen 1038. Otherwise, if the user- as previously been registered as determined in branch 1046, or has completed entering the registration data successfully as determined in branch 1050, the CBT 12 proceeds to entering a password in step 1052. Thereafter, in branch 1054, the password is examined to determine whether it is valid. If it is not valid, the CBT 12 proceeds through branch 1056 which counts whether there have been more than three errors entered in an attempt to provide a valid password. If there have not been more than three errors branch 1056 loops back to allowing the student to re-enter the password. Otherwise, the program loops back to display first screen 1038. However, once the student has entered a valid password, the CBT 12 proceeds to course directory 36. 2. Course Directory The course directory is organized such that items are indented based upon their significance, and a highlight bar will only fall on those items that are the most indented — the exercises themselves. To be on the course directory is to have completed the registration page and been returned through PMasterm, which will note that pdemof is still 33 and check the flag P_Dir_Page_Was_SW. The first time this will be FALSE and the user will call P_Dir_Page with a parameter of TRUE. If the user has returned from an exercise, the switch would be TRUE and the routine would be called with FALSE so as not to reload the directory. P_Dir_Page (found in CBT10.C along with most of the course directory code) will set the p_Dir_Page_SW TRUE so that whenever the user leaves for keystrokes the user will know where to return and what to do with the keys. It will then call the routine Copy_Course_Dir. Copy is actually loaded into the proper location. It first reads in the first record into p_Temp_600 on the assumption this will be the 600 record or header record for the directory (if it is not it will rewind the file and try again) . Byte nine in the array will be a '6' if the header record is found and, if so, the name field is read and placed as the title in the directory header line. Then the file is read and each line (the file is ASCII up to 80 characters with CR and LF at the end) is assigned to its place in the array course_dir[] [] . A count of the number of records is kept in recount to be used later for writing and for highlight bar movement when needing to know the end. The directory is then displayed using Course_Dir_Display. This builds a screen buffer for display of each line, handling the indentation problem (in byte 9 a '0' is an exercise file and it gets its own indent and color attribute assigned — likewise module names and comments have their indent and color based on their value in byte 9) . In addition, if byte 45 is not blank the exercise has been completed and the display is set with a dim attribute. If a start date exists the started column gets an X. If there are not enough rows to fill a screen, the remaining lines are written as blanks. Finally, the highlight bar is placed on the current position — at the start this window_Pos is the first line it finds in the display with byte 9 equal to zero. The user then returns to the control loop to await a keystroke. The valid keys to continue to process would be Up Arrow, Down Arrow, Page Up, Page Down, End, Home and Enter. When those keys are hit, the CBT2B.C routines will call P_Course_Dir_Page and send down a character representing the key. If it is Enter, the function process_selection is called just as if that row had been pointer selected. Otherwise, the calls are used to move the cursor to the next valid position (Up Arrow goes up to the first exercise record it finds before the current Window_Pos, Down Arrow goes down one. Page Up and Page Down go to the next or previous page full (currently 20 per page) and then seek the first exercise entry from the top of the window. Home starts the user at the top of the first page again (finding the first exercise from the top) and End goes to the last page and finds the first exercise field starting- from " the bottom. When the valid entry is found for the bar, that is made the Window_Pos. The bar is then moved there. This goes on until Enter or ESC is hit. As there can be multiple diskettes for any student course, the directory has the diskette number on which the exercise can be found sitting in byte 11. When a selection is made, the value of that byte is compared with the current header record value of byte 56 and, if they are not the same, the system will ask that the proper diskette be loaded. It does this by placing that 11th byte value into p_Exercise_Name and calling P_GP_Mess_Display. This will place the value into the proper spot on a message asking for the proper diskette number. When the message is displayed, the directory simply becomes active again as if no choice had been made and the user may or may not actually enter the proper diskette. The test would be made again when an exercise is selected. An ESC will lead to P_Setup_To_Exit in CBT10.C which checks if the user on diskette one, the required diskette, or not. If not it will ask for diskette one and not allow an exit until that one is mounted. Critiques are an exception here. The critique flag (byte 66) is checked and if yes the system wants diskette one, even if the directory says it is on another diskette (it is added to the last diskette in the directory file but that number is ignored) . Once an exercise has been selected and completed, the system will run through PMasterm again and this time find p_Dir_Page_Was_SW is TRUE and call the P_Dir_Page function with FALSE and re-display the directory again. However, this time the objective is to update the status of the directory based upon what happened. Update_Status is called to start the exercise — it puts in a start date if none was there, increments tϊrernumber- σt tries- by one, and sets: the timer " . Af er " the exercise the function is called with an L parameter where it puts in the time, a completion date if the mode done was the highest mode the exercise runs in (these go, in order, manual, automatic, instruction, and proficiency). One special difference handled elsewhere is that an ESC from the critique will mark all flags as if never called, including erasing the start date. When the user has finished with the directory and chooses to ESC, the directory will be written to the disk. If the proper disk is accessible the completion flag will be written to the temporary header array p_Temp_600 and that record will be written, followed by all of the course_dir[] [] through a loop which will then end by writing out the hex 1A required for the text file. Byte 57, the completion flag in the header record, is changed to incomplete at startup. A proper exit marks it complete again. Thus, an improper exit will be noted if diskettes are changed improperly or an incorrect exit was made in the process, because diskette #1 would still have an incomplete flag. With regard to FIG. 38, the details of the course directory 36 are shown. This step commences by reading the directory file in step 1058. The student disk is read for the directory of courses available on the specific disk. If multiple diskettes exist, the first (or diskette #1) , should be used at start. Thereafter, the CBT 12 checks whether the proper diskette has been loaded at branch 1060. The program will only accept properly formatted student diskettes. If the loaded disk is wrong. - 109 - a proper diskette is requested at step 1062, which loops back to read directory file 1058. If the diskette is the proper one, the CBT 12 proceeds to display the directory file on the screen in step 1064 and onto requesting student input at step 1066. Thereafter, if the student elects to hotkey to the host at branch 1068 by selecting a special key to branch out of CBT temporarily, the CBT 12 will connect to the host by means of step 1070. Step 1070 leads to read directory file which, whe - the stαdent- disconnects from the host or application, will return then to CBT at the point of reading in the directory file again. At branch 1068, if the student does not select to hotkey to the host, the CBT 12 proceeds to determine if an exercise has been selected at branch 1072. If the key selected at 1072 was to select an exercise off the directory, the routine moves to branch 1078. If a no is returned, the CBT 12 proceeds to branch 1074, which changes the location of the directory highlight bar. This just moves the highlighter to another location (valid keys would be Home, End, Page Up, Page Down, etc.). If yes at 1074, step 1076 moves the highlight bar as per the key and, thereafter, returns to get student input 1066. If a highlight move key is not entered, the invalid key - ignore step 1080 is reached. This step ignores what was an invalid input and loops back to read directory file 1058. If a yes is returned at branch 1072, the program proceeds to query whether the student desires to enter a critique using our special evaluation screens (one for each directory) at branch 1078. If a yes is returned, then the CBT 12 proceeds to step 38 critique, and thereafter loops back to read directory file 1058. If it is not a critique at branch 1078, the program asks whether the exercise selected is on the loaded diskette, at branch 1082. If it is not, then the program requests the proper diskette at step 1084 and loops back to the query at branch 1082. If the exercise is on the loaded diskette, then the CBT 12 proceeds to step 1086 to display the allowable modes at this point in the program. After the user enters a mode, branch 1088 examines whether a valid choice was made. If a valid choice was not made, the choice is examined to determine if it was an escape key that was depressed. If the escape key was depressed, then the CBT 12 proceeds back to read directory file 1058. If the escape was not depressed, branch 1090 steers the CBT 12 back to step 1086 to allow the student to make a choice of mode. Once a valid choice has been selected as determined at branch" 1088, the CBT 12 proceeds to step 22. At this point the program accesses playback 22 and runs through a tutorial session as explained above. Thereafter, the program proceeds to update directory 42, incrementing the number of attempts, scores (if in a proficiency mode) , and noting that the exercise is completed if the user has finished the highest mode for the exercise. Branch 1092 then examines if the highest mode has been completed. The modes are, in order, automatic, manual, instruction, proficiency. If an exercise has not finished its highest mode the user is returned to the mode selection screen. Finishing the highest mode completes the exercise, so the user goes back to the directory. All exercises do not have all modes, so instruction can often be the highest mode. If the answer at branch 1092 is no, the CBT 12 returns to display allowable modes at step 1086. Otherwise, the CBT 12 proceeds out of branch 1092 back to read directory file step 1058. 3. Critique A critique is a special exercise selectable from the course directory that provides users the opportunity to rate the course they have taken. It is another of the possible links between the management and CBT 12 system in that information is placed into a special file in a special format for a management system to import. That file, CBTCRIT.DAT, consists of eight blanks (which used to hold an exercise name when critiques could be done on each exercise) , a block of up to 50 bytes to hold the rating - Ill - values of 1-5, and a final string to hold the generalized comments. As a basic ASCII text file it must be ended with hex 1A. The critique consists of a variable number of pages, each page being a compressed screen file on disk named CBTCRITx.SCR. The x is the incremented page number starting with 1 and currently ending with 4. The first is loaded and processed and, upon completion, the number is incremented and th next page- brought in. As this is a flexible situation, the page consists of one-character fields which have braces around them and which have the special attribute defined as p_Field_Att_Req. The cursor location routines then seek from where they start to the next spot that matches that attribute in advancing through the page. A value must be entered in order to leave one of these fields. It also stops on a p_Field_Att_Opt although this is only used for the comment field. This whole process begins when the user selects the critique in CBT10.C form the course directory and the function process_selection is called. It looks at the information on the exercise selected and checks if the critique flag is yes. If it is it sets p_Critique_Flag to TRUE and returns to Pmasterm with pdemof equal to 33 and p_Dir_Page_Was_SW set TRUE. When Pmasterm (in CBT1.C) is called it then reads those flags and calls P_Crit_Page(TRUE) to start the process. P_Crit_Page (found in CBT15.C) sets the p_Crit_Page_SW to TRUE (note that p_Reg_Page_SW remains TRUE also) so that any exit to the control loop for keystrokes will know where to return. The function then reads the diskette to see if it is diskette #1 (the system wants diskette #1 to have the most recent data read and written). The file name held in p_File_Name_csr[] is read in and the number 1 is added when the function is called for the first time (the parameter passed was TRUE) . The file (a screen) is then displayed and the cursor position set to 0,0 from whence begins the first search for a valid input field. That search is handled by the function P_Reg_Adj_Cursor with a parameter C the first call. Function keys are handled first in CBT2A.C in P_T_Char_Crit while normal characters find their way into P_T_Char_Char_Play. The only function keys allowed are the four arrows, the F2 key, Backspace, and the Enter key (called RETURNKEY in the control loop) . Normally, the user starts entering their ratings and the characters are tested to see that the number is between 1 and 5, with a valid entry causing the cursor call to advance to the next valid empty field location. The cursor stays put if the user is on the last field already and awaits a Return (the Enter key on the PC) to go to the next page. If the row is > 24 in the cursor location loop looking for a valid field, p_Ok_For_Next__Crit is made TRUE. (If the user is on the comment field of the last page, an optional field, the user will be allowed to type in anything and arrows, backspace, and enter would allow movement there, although word wrap on the borders is not allowed.) Return is one of the valid keys found in P_T_Char_Crit. If p_Ok_For_Next_Crit, return will call P_Get_Next_Crit_Page is there is another page to retrieve. That function sets p_Ok_For_Next_Crit off again, reads p_Crit_Buffer in a loop until it hits a null to advance past any information entered on any previous pages, then reads in the screen fields and adds them to the array. A null is put at the end (ready for the next time this function is called to seek the first null) , and finally calls P_Crit_Page with a FALSE parameter. P_Crit_Page will now add one to the file name, load the new screen and display it, set the cursor at 0,0 again, and search for the first valid field (again using p_Req_Field) , to be ready to process keys again. The beginning of the CBT2B.C P_T_Char_Fn_Key processing routine tests if for an F2 and a return value for P_Check_Reg_Field. If F2 and P_Check_Reg_Field returns set to true and p_Ok_For_Next_Crit, the system tests whether the user is at the last page. If so, the program will call P_Write_Crit_Data to complete the process. This test looks at the page information at the top of the screen to see if it is on the same page as there are pages i.e. , the user is on page 4 of 4, and is thus at the end. P_Check_Reg_Field effectively checks that the required field has been entered into. The Up Arrow and Down Arrow keys do the literal movements. The write function builds the output file by copying eight blanks to the start of critique_buffer, then adding in the P_Crit_Buffer array until a null is found. It then reads in the comment from the screen and adds a CR, LF, CTRLZ. Then the file is written by putc-ing the array critique_buffer. P_Init_Crit_Virtual_File initialized critique_buffer to 320 nulls at start-up, and p_Crit_Buffer to 50 nulls although less than half of those bytes are currently used. As with any exercise, it is possible to ESC and it will be as if the user had never started. Developers, as always, can leave a critique with special keys — F2 at any time. 4. Help At any time and at any place, the user can hit Shift-Fl to get on-line help. Help system 44 is not field-specific but state-specific. It is based upon which state and mode flags are active. The hotkey (Shift-Fl) is evaluated as a very early part of CBT_Character_Trap in CBT2A.C. P_T_Char_Start_Help is called and if the user is not in help mode and the hotkey was hit, a flag called p_Start_Help_Soon_SW is set TRUE and the user returns up to the control loop. In the control loop in the CBT_Low_Interrupt routine (CBT1.C), Help system 44 checks that flag and, if TRUE, resets the flag and sets p_Help_Mode equal to TRUE. Pphelp is then called to display the proper help screen and then P_Get_Help_Fn_Char is called to process a help screen. Pphelp in CBT15.C is called while in a help mode. p_CBT_Page is used as a flag in that if the user is just starting help, the page is the normal zero to five, but if the user has a help screen up on the screen, the value is incremented by 10 so that it is 10 to 15. That 10 is added every time the help screen is displayed (and subtracted every time the loop where page is 10-15 is entered so that the proper page number is back when the time comes to evaluate states) . The file specification array pdhelp[] is filled with the first three characters based upon the state, followed by the number of the help screen in the sequence if more than one screen of help is available for the option, followed by the period and the file extension hip. The current screen is then held in a buffer with a call to Hold_Screen and the help screen is displayed. CBT_Pointer_Ok is set FALSE so the light pen will not be possible on help screens. Once the screen is displayed, the function P_Get_Help_Fn_Char is called (located in CBT7.C) to setup the menu line for the option of Next, Back and Exit at its initial location and saves that in p_Save_Help_Fn_Char. At this point the program is waiting action. Any key hit will be processed by the function P_T_Char_Process_Help (again high in priority in CBT2A.C) . If Enter is hit, the program will accept the current highlighted option Back or Next (the letter of which was saved in p_Save_Help_Fn_Char) . Page Down and 'B' act the same, calling Pphelp with a back parameter. Arrows keys call P_Get_Help_Fn_Char with an N to move the bar to the proper next option highlight. An 'N' calls the next help page through Pphelp and again resets the bar to the initialized location through a call to P_Get_Help_Fn_Char. An 'E' or another Shift-Fl designates completion of help and calls Preturn found in CBT15.C. - 115 - Preturn will set p_CBT_Page back to its normal state, p_Help_Page_Nbr to 0 again and p_Help_Mode to FALSE. It will also reactivate the light pen with CBT_Pointer_Ok. The old screen before help was called will be restored and the program will be back in the control loop waiting action just as before the hotkey was hit (nothing changed the old state flags — p_Help_Mode was just processed with higher priority and handled all the keys etc.) . In incrementing through help screen pages, a blank in the right hand corner will indicate that the last screen has been reached. I. Search Utility FIG. 39 contains a flow of the sub-program search 44 of the CBT 12. In general, search 44 allows the user to search through a specified file for the occurrence of an input string. The chart shows the logical flow from the input of the search string, the choice of search file, and through the actual search process itself. The program enters the•sub-program search 44 through entry point 68. The first step 1094 inputs the user's search string or strings. At branch 1096, there is a check to see whether the input is valid. If there are no valid strings, the logic moves to step 1098, provides a message to that effect, and exits from the search sub¬ system through exit point 1100. If the input string is a valid entry, the logic moves to step 1102. In step 1102, the input is compressed to match the compression of the stored exercises. The program then moves to step 1104 and asks the user to input a file specification. Branch 1106 checks for a valid file name. If no valid file name was input, the program moves to step 1108 and provides a no match message and exits through exit point 1100. If a valid file name has been entered, the logic moves from branch 1106 to step 1110. Step 1110 opens the search result output file. Step 1112 tries to read the next file in the input file list. Branch 1114 asks if the user is finished with the input files, i.e.. has finished searching. If yes, the logic moves to step 1116 and closes the output file and then exits through exit point 1100. If the user is not finished with the files, the logic moves to step 1118. Step 1118 reads in a header from a record in the input file. The program then moves on to branch 1120 on FIG. 39b. At branch 1120, there is a check if this is the last record in the input file. If yes, the logic returns to step 1112 and tries to read the next file on the list. If the record examined in branch 1120 is not the last record in the file, the logic moves on to branch 1122. Branch 1122 examines the record to make sure that it is a basic page from a stored tutorial. If not, the logic moves to step 1124 to display an appropriate error message, and then returns to step 1118 to read in another record header. If the record was in the proper format, the program moves from branch 1122 to step 1126. Step 1126 searches through the record for the input string. Branch 1128 checks for the existence of a match. If there is no match, i.e.. the input string was not found on the recorded page, the program loops back to step 1118 and inputs another record. If a match has occurred, branch 1130 checks for a second input string. If there is only a single search string, step 1132 writes the page and file name to output file. After the data has been written, step 1118 reads in another record. If there is a second input string, the program moves from branch 1130 to step 1134. Step 1134 repeats the search of step 1126 using the second input string. If there is no match, branch 1136 returns to step 1118 to read in another record. If the second search string resulted in a match, branch 1138 checks for the existence of a third input string. If there are only two input strings, the program moves to step 1132 and writes out the page and file name data where the match occurred. If there is a third input string to search, step 1140 executes this search. Branch 1142 checks for the existence of a match, if the third string was not found, the program loops back to step 1118 and reads in another record. If all three search strings were found on the present page, step 1132 writes the page and file name data to the output file. The program then loops back to step 1118 and reads in another record. The program continues through this flow of reading in records and then searching them for the input search string, until the last record of the file is reached. The program also cycles through all the input file names to be searched. Once all of the files have been searched, the user exits the sub-program search 44. J. File Functions Selecting F6 on the authors menu will bring up the file functions menu. It sets a flag called p_Pagela to TRUE. For most processing, the state is still has p_CBT_Page equal to 1 and this is a subset of that. There are a only a handful of differences between 1 and la. Most functions in la can be found in CBT7.C The most obvious one is in the CBT7.C function P_CBT_Pagel which processes the options for both page 1 and la. It will call P_CBT_Pagela if on page 1 and the user hits F6. That screen will be displayed and the return is to the control loop to wait for the keystroke or action that will recall the P_CBT_Pagel, this time with the page la flag set so it looks at those options instead. One other difference is in the function in CBT2A.C that redefines the keys for page 1 in that an F5 translates to a 5 in page la, but because printing used to be something else, a P if on page 1 only. Another is that since the file functions page has two exercise name entry fields, a tab moves from field one to field two and Plpsrch does a pointer search and puts a successful exercise name to the correct field. The final difference is in the help screen that is displayed. An ESC from pagela will reset the system to page 1 again and set p_pagela to FALSE. 1. Copy Exercise to Drive x (Page la - F1-F4) The four copy options all do exactly the same thing. They call P_Copy_Exercise in CBT7.C with the drive name as a parameter. Drive B will be tested using bios_equiplist and not allowed if no second diskette drive exists. First the user tests the two file names using the standard functions of P_Check_Name_Typed for the top name and P_Get_New_Exer_Name for the target name. There is a test, if the target file already exists, on whether to overwrite. Then, if proceeding, a simple getc from one file and putc to the other occurs. At the conclusion of the process, P_Display_Pagela is called again and the wait above for action is begun again. FIG. 40 is a flow chart describing the overall logic of the copy exercise function 48. This sub-program simply makes a copy of any stored tutorial exercise in whatever disk drive directory the user specifies. The program enters the copy exercise sub-program at point 60 and immediately gets the name of the exercise to copy from the user in step 1144. Branch 1146 checks to see if the user has hit the escape key. If the escape key has been struck, the program automatically returns from the copy exercise sub-program through exit point 1148. If the input from step 1144 was not the escape key, branch 1150 checks to see if the input is the name for a valid existing exercise. If not, the program returns to step 1144 to input another name. Once a valid exercise name has been entered, the logic moves to step 1152. Step 1152 allows the user to input the desired destination drive. Branch 1154 again checks to see if the escape key has been hit, and if it has, the logic automatically exits through exit point 1148. Branch 1156 checks the input to see if it is a valid drive specification. If not, the logic returns to step 1152 and requests a new drive. Once a valid drive name has been input, the logic moves to step 1158 and executes the copy of the file. Once the file has been successfully copied, the program exits through point 1148 and returns to the point at which the copy exercise was called. 2. Delete Exercise (Page la - F6) Delete Exercise begins with getting the file name and verifying it exists. It then sets a flag and requires the user to hit F6 again to verify the request. F6 sets the p_Delete_Exercise_SW switch TRUE and displays a message indicating- the need to depress F6 again to delete the file. Then the system returns to await a keystroke. Anything but F6 will set the flag back to false. The F6 will set it to FALSE and then call p_Delete_Exercise. P_Delete_Exercise simply checks the name again and attempts to call remove. After successful deletion it will blank the exercise name field. It ignores the second field in this process. The return will be to page la. FIG. 41 is a flow chart describing the program module delete 62 from FIG. 2. This option is a simple delete file function. The function first gets the name of the file to be deleted in step 1160, and branch 1164 checks to make sure that the input is a valid file name. Branch 1162 and branch 1168 trap the escape keystroke and automatically exit delete 62 when the escape key is struck. If a valid file name has been entered, the program makes sure the user wants to delete the file in step 1166. When the delete order has been verified at branch 1170, the program moves to step 1172 and deletes the file. If the user decides against deleting the file at branch 1170, the program exits the delete function through exit point 1174. After a verified deletion has taken place, the program automatically exits from function delete 62. 3. Rename Exercise (Page la - F5) Not unlike copy 60 rename 64 gets the two exercise names and then calls a rename function to do the work. The rename field, which is the New Name field, is then switched to the exercise name field since the exercise has now been renamed, and page la is re-displayed. FIG. 42 shows the flow chart for the rename 64 exercise function. The function takes the name of any existing exercise file and replaces it with whatever new name the user desires. The function gets the name of the exercise to be renamed at step 1176, checks the validity of the name at branch 1180, and renames the file at step 1182. If at any point the escape key has been struck, branch 1178 traps the key-stroke and exits the rename exercise through point 1184. Once a file has been renamed, the program automatically exits the function rename 64 through exit point 1184. 4. Append (Page la - F7) This effectively all happens in CBT18.C once the append_exercise is called from the page one menu in CBT7.C. This no longer treats the screen as the data, but does a reasonably straight forward file read. It builds the .100 file just as in modify 46. The first file is read and written up to but not including the last record. The first record of the second file is written again to the .100 file. A From Code of 1 is put into the first record of the second file so that it forces the last valid record of the first file to be a stopping point in playback 22. At the end of the second record, the end record can be written and the same routines that end a modify on a single file modification can be called. It is necessary to keep track of the page numbers that count for selections as against the real total number of pages so that those bytes can be written at the front three bytes of the file for proficiency testing. - 121 - Append_exercise calls a special routine called Get_Write_lst_Append to read in the old file 10. Insert_exercise routine is called for multiple_edit insert and replace_j?athway to continue to write out records. Insert_last_record is called to set up the proper codes in the header and used to write a proper last record at the conclusion of append_exercise. There is an append_flag variable so that the verify_action routine can know what message to place in the confirmation request — in this case, append 66. FIG. 43 contains a flow chart showing the logic of the function append file 66 from FIG. 2. This function allows the user to take two stored files and append them together to form one larger, recorded file. The performing of this function is relatively simple. Entering the flow chart at point 66, the name of the first file to be operated on is entered at step 1186. Branch 1188 checks for the escape keystroke. If at this point the escape key has been struck, there is an exit from the append file 66 function through point 1190. If the escape key has not been struck, the input os cjecled to make sure it is a valid file name at branch 1192. If the input is a valid name, the file is opened at step 1194. If the input name is not valid, the program loops back to step 1186 and the user is asked to re-input the desired file name. Once the first file has been opened, step 1196 asks the user to input the name of the second file. Branch 1198 again checks to see if the escape key has been struck. If the escape key has been struck, the present append attempt is aborted and there is a return to step 1186 to start the process over. If the escape key has not been struck, branch 1200 checks the second input name. If the name is not valid the program loops back to step 1196 and asks the user to re-input the name of the second file. Once a valid second file has been named, step 1202 opens the second file. Step 1204 then opens the append output file. At step 1206 the actual append process begins by reading in the first record of the first input file. The flow chart then moves on to branch 1208. Branch 1208 checks for the end of file pointer. If not at the end of the first file, the program moves to step 1210 and writes the present record to the output file, then loops back to step 1206 and reads in the next record. The loop continues until the entire first file has been read and written to the output file. When the last record in the file has been reached in branch 1208, the program moves to step 1212 and begins to read the second file named by the user. Branch 1214 checks for the last record in the second file. Step 1216 writes the present record to the named output file. The program loops through step 1212, branch 1214, and step 1216, reading records in from the file and writing them to the output file, until the end of the second file is reached. At the last record, step 1218 writes out the last record to the output file. Branch 1220 asks the user to accept the completed append. If the user responds negatively, step 1222 erases the output file, aborts the append process, and exits from function append 66 through point 1190. If the append is accepted, the program closes the output file and input files in step 1224 and then exits through points 1190. 5. Replace Pathway (Page la - F8) Like append file 66, this function is called directly from the Page 1 menu in CBT7.C, in this case by calling replace_pathway, and all operations then happen mostly in CBT18.C. The process begins by opening the old file and reading it into the big buffer. The function Get_01d_Header_Write_Exercise reads the old file and writes to the .100 file until it finds the first non-QSCR which is presumed to be the first actual pathway exercise field. At this point, the logic will skip along the .000 file without any writing until it finds another QSCR. This is presumed to be the end of the original recorded pathway. The pointer is kept for later reference. The logic now switches to the new second file intended for use as the replacement. Calling insert_exercise, the new file is read and written to the .100 file. At the completion, and assuming the user verifies the replacement, the function Finish_01d_Ex_Block will return 5 to the original .000 file and pick up writing again straight through to the last record. There are some key assumptions made about this process. If the old file had a QSCR in the middle of the pathway, all the exercise screens in the second half will 10 stay part of the exercise. What will be inserted is the entire second file. So if the file has QSCRs in it and is not just a "pathway", those QSCRs will be written anyway. There is a test to be sure that if the first file has no QSCR to stop at after having found the first 15 exercise screen, the replacement is still made and a last record from the second file is simply tagged on to finish the new exercise. K. File Structures Some of the key files used by the system include 20 the FTF file, the exercise file, the exercise directory, the defaults file, and the control files. Examples of these files now follows 1. FTF File A sample FTF file 14, suitable for recording an 25 IRMA 3270 emulator is as follows: Application_Name "IRMA3270" ;This file test the parsing and input of the CBT recorder Sample_Rate 1 30 Record_Holdoff 2 * Function_Holdoff 2 Start_Record F9-A Stop_Record F8-A Record_Screen F6-A 35 Type_in_Position Yes No KBD Rom Yes - 124 - Autoexit_Code 24 134 Ignore_Region_80 24 0 24 79 0 0 "" ;Ignore_Attributes Yes Begin_Key_Table Fl Fl-A Fl-C F2 F2-A F2-C F3 F4 F5 F6 F7 F8 F9 F10 KEY1-A KEY1-C KEY2-A KEY2-C KEY3-A KEY3-C KEY4-A KEY4-C KEY5-A KEY5-C KEY6-A KEY6-C KEY7-A KEY7-C KEY8-A KEY8-C KEY9-A KEY9-C KEYO-A KEYO-C KEY—A KEY—C KEY=-A KEY=-C TAB ENTER UP DOWN LEFT RIGHT 2. Exercise Files With regard to an exercise file 10, an exercise begins with a series of recorded screens stored in an exercise file (having the extension 000) . That recorded pathway will be comprised of "raw" screens — those from which some keystroke or pointer hit is required to advance — and "extra edit" screens which are not stopping points but are recordings of the extra screens in response to the actions taken at the raw screen. One benefit of the CBT file layout and recording capability is that it allows for the recording of multiple screens with timing information in a way that allows an "expert" recording which can then be played back. That simple exercise can be played back immediately on the CBT system. However, for default error messages to be placed in the pathway, the users must run through the modify 46 process one time (this is usually done selecting F4 off the author menu 20 and hitting the END key) . Authors would then begin the process of customizing the exercise by surrounding the pathway with instructional screens (a QSCR) and by providing more specific error messages and adding procedure lines. This may include some standard QSCR insertions to cover the objectives of the exercise and the key points to be covered. The recorded exercise ends with a tag record called the "last record." Any modified exercise must also have a last record flag in the file. When the system loads the exercise, it will begin by filling the buffer with as much exercise as will fit. Normally the whole exercise will fit in the buffer. If an EOF is hit before a last record is found, a system error will be generated and processing will stop. That exercise will be unusable. The actual file layout is based on a 40-character per line screen. To handle an 80-character per line screen, two records are needed. The first record will identify in its header that it is an 80-character screen. A special sequence is also placed in the final few bytes of that record to indicate the one that follows will finish the 80-character screen. Because the system has a 40-column base screen, any 80-column screen is handled in two parts, the top half and the lower half. In addition, the mode that the video display is in must display the 40 as 40 and the 80 as 80. Because an 80-column screen requires "two" screens of 40, it is normally stored in playback in ptbuff[] (the top half) and p_Low_Buff (the bottom half) . When written to video memory with pokes, it is done it two stages. It is worth noting that when staging screens in playback 22, one may stop after loading pbuff[] with just the first half of an 80-column screen. That screen's header becomes p_Tempbuff[] which is critical to operation, but the bottom half won't come in until the upper half moves on to ptbuff[] . One identification of 40 or 80 is found in byte 10 of the header with the test: if the value of the byte & 0x80 equals 0x80 it is an 80-column screen. Another flag set when an 80 is in effect is Have_80_Column_Data_SW which is used by both CBT and recording emulator 8a. Other flags include p_80col_SW, p_80col (set TRUE if 80) , p_Cols (set as 40 or 80) , and p_80col_cols which is set to 80. If the screen is 40, the TRUES become FALSE and the 80's become 40 (which means p_80col_cols is 40 also). The p_Cols value is very significant because it is used in calculating where to display characters on the screen with the calculation row times p_Cols, etc. Most of the flags are taken care of in the function P_Change_Screen_To. It takes two parameters, one for 40 or 80 sent as the values 40 and 80, and the other a true/false which says if false, only the flags and variables need to be reset (the presumption is the screen mode is the one the user wants already) . All screen blocks are together in one linear exercise that is loaded into a big RAM area called a "big buffer." Optionally, this could be a so-called "virtual buffer" that is a 64K block loaded in and handled by a separate assembly language module. However, it is preferable that memory be allocated directly in the program with a far pointer. The variable used is essentially 64K and writes in and out of it are simply moving through the offset in that array. This array is named vbuf2. The far calls need a segment and offset and new variables to get the segments. The routines that read and write the files, are found in CBT6.C. They make calls to routines still named vbuf_fetch and vbuf_store, vbuf2_fetch and vbuf2_store. They effectively poke into the vbuf or vbuf2 arrays, using the segment and offset. The offset is the location within that array added to the actual offset location of the vbuf2 array within the segment. These fetch and store routines can be found in CBT17.C. An exercise file consists of several variable length records all sharing a common format. The first three bytes are used as an identifier (though in the first record of the file they are used to provide information about the exercise used for testing and scoring purposes) . The next 22 bytes provide header information that describes the contents of the record. This is followed by a variable length string comprised of the data that was on the screen, compressed using a standard algorithm, and then the attributes on the screen, also stored in compressed form. There are five bytes attached to the end of the record, the last four of which are unused in 40-column screens. An 80-column screen will use three of those bytes to house the sequence to identify that the user reached the second record that makes up that screen. The first of those five bytes is used by the program for keeping track of what part of the file is currently loaded in memory. There is an exception to the standard record type to handle any typing that was done on the screen that was recorded. Typing buffers have as their variable string a loop of five-byte components that hold the timing and location considerations along with the actual character typed for each character. The basic layout of the 22-byte header information is as follows: Byte No. Description [0],[1] Buffer length minus 1 (including Control data and Display data) . Byte [1] is units (to 99). Byte [0] is 100's. This is referred to as "p_SizeMinusl". [2] Record type (p_Record_Type) . =0, display recorded in buffer. =1, type-in recorded in buffer. =3, display recorded in buffer but LAST buffer [3], [4] Timerl. This is the time from the last screen write to the Light Pen selection which caused the screen write which is recorded in this buffer. 129 - [5], [6] Timer2. This is the time from the Light Pen selection (or uninvited screen write) to the screen write which is recorded in this buffer. If [5],bit 0x80 = 1, this is a screen recorded from the host. [7] From code(p_From_LP_Sel) . = 0, this screen write was preceded by Screen write. = 1, this screen write was preceded by Light Pen Hit. = 2, this screen write was preceded by "Request Init." [8], [9] Light Pen address of the Light Pen or Function Key selection which immediately preceded this screen write. [8] is row, [9] is column - integers. [10], [11] Cursor position of cursor on the screen in this buffer. [10] is 100's, [11] is units. - integers. [12] If TRUE, this is an error display - active. [13]-[17] Used in Playback mode - Reserved for this. [18]-[21] Extension (format is .xxx) is not used. The typing buffer, identified as such when byte two in the header has a value of one, has the loop format beginning at byte three in the header and the remainder of the header is not used as a header but as storage for these typing blocks. The block below shows the five-bytes beginning at byte three but note the second block would begin at eight, the third at 13 and so on. - 130 - [3], [4] Timer 1. This is the time from the last typed character to this one. [5] Typed character. [6], [7] Address in the screen buffer where typed character was typed (screen buffer is character, attribute format) . [6] is 100's, [7] is units. If the character is hex FF, it indicates a new field is starting due to a function key having been hit when recording from the host. The typing cursor is moved to the new location but no character would be displayed for that block of five. Reviewing the header information in more detail, the first two bytes are very important in the loading and saving of files. When an exercise is read into the buffer it is done on a record-by-record basis. The only way the size of the record is known is through these two bytes. The read functions will read five bytes (the three that precede each header and then these two bytes) , determine the number of bytes that follows in the record, and read those bytes in. The first byte after that initial five will hold the record type and is significant at read time. When it has a value of three the last record has been reached and reading will cease. The two size bytes are called p_SizeMinusl. The next key byte is seven. With a value of 0 in this byte, a screen would be displayed but would not stop for input. The screen following would be written over it immediately. This category is primarily the "extra edit" mode — screens the mainframe generated in response to a keystroke or pointer hit before the next resting place. A special case of this — the mainframe's message that it recognizes a request has been initiated — is handled by a value in byte seven of two. The screen itself under that message is not saved again and this screen cannot be stopped even in modify mode. - 131 - A one in byte seven of the header indicates a stopping point, but not at the screen attached with the header. Rather, the stopping point is at the previous screen. The system stages screens in a temporary buffer before placement on the monitor. So after a screen is displaced, the temporary buffer is examined to see what's in the header to know to stop. Bytes eight and nine will then indicate the key or pointer hit location that would allow the user to leave the screen. Thus, if the user has a screen up asking to select the proper action and the user points to row 11 column 22, the screen itself was already recorded when it was received from the host. The action will generate another screen and that screen will get in its header the From Code of one with the values 11 and 22. In this way the system can know at playback time what is a valid response to leave the first screen (a pointer hit at row 11 column 22) . Thus, to have the full information about any screen one must look at the header of the screen that follows. A QSCR is always followed by a header with a From Code of one in order to stop and show users these instructional screens, and modify must always be able to stop at QSCRs so they can be changed by users. Default error messages are generated by the system. They can be modified by the user. The main selection error message is also identified by a QSCR. It is a QSCR 2. Note that a QSCR does not have an error message but all other raw pages will have one created. Typing records will also have an error message generated but these will not be a separate record. They are found in the typing record itself and are not QSCR 2 records. 3. Exercise Directory The exercise directory is the list of available exercises that will appear throughout the system for authors and developers (students will only see the course directory described above) . This list will reflect those available on the active drive (default starts at C for non-students usually) . An exercise list appears at the start-up on the non-student page one. The only time the user actually can bring it up to see when it's not there is in the List (+L) function off the author's menu when the user is modifying an exercise or a screen (F4 Modify or F7 Screen Utilities) . The directory entries themselves are read into a structure called disp_window of which display_array is an array of 500. The system uses the dos_findfirst and dos_findnext functions to build the exercise list (see CBT7.C) . ' Each name is read into the array and using binary_insert is inserted in alphabetical order in the list as it grows and fills the display_array. When the last file is found, a filecnt variable tells how many were read in to tell where the end is. The first 'page' worth is then placed on the screen on the shell that came as part of the page one screen itself. Thus the middle of that shell is used for read/write purposes. The disk drive is diplayed in the shell using pdisk (the session drive indicator which can be changed and, when it is, requires the system to rebuild the directory) . Variables are kept for the Top_Of_Window which tells the number of the file now at the top (if 0 its first page and thus setting this 0 and redisplaying will be what Home does) , and Window_Pos which gives the location of the current choice — the highlight bar location. The keys may be processed based on current values and desired effects. At this point in the program, Up Arrow, Down Arrow, Page Up, Page Down, Home, End or Enter key and the result will be reflected on the directory. A function key will be reflected on the menu options. A letter or number will be reflected on the exercise name field along with leftarrow, rightarrow, and backspace. (Enter off the - 133 - directory also displays the name of the exercise selected automatically in the exercise field) . On page 1A, file functions, the user has two file fields with the directory. The only difference is that the Enter will take the choice selected and put it in the exercise name field that is active (there are two because it handles copy and rename etc.). The p_Dir_Write functions build the directory for those pages, but because it also handles the directory function for the author's menu and the wildcards and differing extensions allow a directory of files other than just directories, this function builds the string for the filesearch specification. In the author menu the selected item shows up in the file name location in the lower right of the screen. The function p_Dir_Display handles putting up the right entries and handling the keystrokes and redisplay. 4) Defaults (Page 1 - F10) Called as F10 off the author menu, this sets p_CBT_Page equal to three. It has two screens attached, however. If the user selects F10, the current exercise name is saved using function Psavefld before the page is officially changed to three. The first default screen is then loaded and displayed using function pdispr. There are three choices that can be made. The session drive is first. It allows the user to change the drive where the exercises are to be found and the change will be immediate. It will last until the user comes in to change it again or until the user leaves the program. Another session default is the time delay for automatic playback display. Because these two blocks both require an entry and are both active at the same time, the typical menu bar (the white on red bar in the exercise directory, course directory, or QSCR4 screen) is not applicable. A new display attribute (found by reading the screen itself) is used for the highlight. A CBT5.C function, Prev, calls the proper highlighting by giving the block parameters and the location of the one to highlight. This is called once for each block at start-up. The third option is F10 again which will display the standard default screen — the permanent one. After the initial display the system awaits the user entry of the appropriate function key or an ESC to exit. At this- point, any keystroke is evaluated in Character_Trap translation of Page 3 information, and then handled in a CBT7.C function P_CBT_Page3. The selection of F1-F4 will change the drive, though if drive B is chosen and there was none, the change would not be allowed. The variable pdisk is updated at this point and that is the one used throughout for the disk drive in file names in the system. F5-F9 will change the session time (listed in the case of this function as 5-9) . The F10 (translated to 0) results in a call to the standard default screen which is found and displayed with the logic returns above again to await action. It will set p_CBT_Page to five (not three now) . This way, the F2 key saves the defaults back to the control loop automatically. The user is then sent back to the control loop. Fields here include the permanent disk drive default, the super password used when students forget theirs, the auto_demo_time which sets the normal delay in automatic playback, light pen adjustment figures that allow the user to fine tune the light pen, and a choice of two printers. Here, only F2 and ESC have any effect. These are defined in CBT2A.C for ESC which has as its default the resetting of the system to page one. F2 is handled with the SP2KEY routine in CBT2B.C that says if the page is five call Save_Screen so these new defaults will then be made permanent. Note that Page five is really not used in the system except here. It is a catchall for future options. With regard to other file layouts, besides the main exercise file detailed elsewhere, there are a few other structures that could be called files. These are mostly compressed screens that are loaded into global variables, normally in CBT1.C. The primary files are stO.pew, sto.pew, st9.pew, and default.pew (which is the copy of default?.tds that is appropriate — i.e.. s for students and x for everyone else) . St9.pew is the easiest and has been noted before. It is the array of error messages with a three-byte finish that indicates the display row, the time to be displayed, and the last number of the message used for programmer convenience in looking at the file. The first screen (use ScrUtil — F7 off the page one menu — then enter file name sto.pew and +R to display) holds several variables and constitutes a file. A screen is easier to look at as row,col and not linear. The row and column here are the computer's row and column. The ScrUtil function adds one so that row,col of 0,0 is 1,1 when the user is referring to the screen. These will show up as offsets into pbuff when loaded starting after the 22-byte header. Therefore an entry's offset is determined by adding 22 then 40*row (the screen is 40 column) plus the column offset and finally multiplying that by two (remember the pc has a pair of bytes for the character-attribute pair) , i.e. , pbuff[22+(40*2+l)*2] . The file layout of for the course directory file is composed of the header record and then the exercise records, all of the same length. The header layout is as follows: A - 12 Chars. - Always "xxxxxxxx.601" B - 24 Chars. - Course Title. C - 1 Chars. - (Not Used) D - 8 Chars. - (Not Used) E - 8 Chars. - (Subdivided Field) 2 Chars. - (Not Used) . 2 Chars. - (Not Used) . 2 Chars. - No. Errors, this "Selection", allowed. 2 Chars. - No. Consecutive "Selections" with an Error, allowed. F - 2 Chars. - Session No. G - 5 Chars. - (Subdivided Field) 1 Char. - Display Score Flag(Y or N) . 1 Char. - Current Disk Number. 1 Char. - Disk "Complete" Flag(0 = No, 1 = Yes) . 2 Char. - (Not used) H - 4 Chars. - Always "0"-Score. 1 - 2 Chars. - (Not Used) J - 1 Chars. - (Not Used) Using the same essentially fixed length record size as the header, the remaining records in the file follow the normal exercise file layout which is as follows: A - 12 Chars. - Exercise Number. Col. 0-7 - 8 Chars. - Exercise Number. Col. 8 - 1 Char. - Always "." Col. 9 - 1 Char. - Exercise "Level". 7=module header 8=module comment 0=exercise Col. 10 - 1 Char. - Always "0". Col. 11 - 1 Char. - Disk Number, in Multple Disk Series. B - 24 Chars. - Exercise Name. C - 1 Chars. - Option Override(0 thru 7) . D - 8 Chars. - Start Date. E - 8 Chars. - Completion Date. F - 2 Chars. - Number of Tries. G - 5 Chars. - Elapsed Time, in Minutes. H - 4 Chars. - Score. I - 2 Chars. - Maximum Errors(percent of total). - 137 - J - 1 Chars. - Critique Flag(IF Y this Exercise is Critique) . critique skips over QSCR4 stuff and requires disk 1 be installed ROW COL VARIABLE DESCRIPTION 2 p_Window_Background. Used for 'normal' directory pg current¬ ly blue on grey. p_Dfalt_Attribute. p_Fl_Rows. Number of rows in 40-col Authors Menu (4). p_F_Key_Set. Current function key interpreter (S) . p_Error_Cnt_θk_SW. If E student error handling ok. p_Bar_Background. Used for menu highlight bar now white on red. p_Timel. Time in half-seconds after display of typing Accepted message before putting up author's typing, (see 20,23). 2 p_Time2. Time delay between author typing from above and allowing user to continue. OW COL VARIABLE DESCRIPTION 10 p_25th_Color. Attribute for 25th line in teaching mode. 11 p_Field_Att_Opt. Optional field attribute in the critique, (used to be registration too) . 12 p_Field_Att_Req. Required field attribute in critique/reg where exit is not allowed without entry. 13-21 22-27 p_Insert_Name. The word INSERT. 2 28-33 p_Block_Mark_Name. The word MARK. 34-39 p_Block_Move_Name. The word BLOCK. 3-6 0-39 p_S2_Fn_Buffer. The default authors menu template. 0-19 p_Err_Mess_Print_Title. Message 'Select Err Window. ' 20-39 p_Type_Print_Title. Message 'Typing Error Window. • 0-14 p_Top_Mes_T. Message 'Typing Error Mode. ' 8 20-39 p_F2_Buffer. Words 'EDIT MENU. 0-19 p_Type_Err_Title_C. Currently 'Try Again 1 for QSC4-mode C. OW COL VARIABLE DESCRIPTION 20-39 p_Type_Err_Title_P. Currently 'Type-in error # ' for proficiency mode 0 0-19 p_Err_Mess_Title_C. Currently 'Try Again' in instructional mode. 0 20-39- p_Err_Mess_Title_P. Currently 'Error No.' in proficiency mode. 1 0-19 p_Top_Mes_E. 'Last Screen 1 for top line mode message if last screen. 11-16 20-39 p_Type_Error_Blk. The full typing error block to be displayed. 12-16 0-19 p_Err_Mess_Init_Blk. The default error message screen to be displayed. 18-23 0-12 p_Third_Error_Buf, The third error window for a selection error — now 'select or press the itme shown in It. blue. • 18-23 13-25 p_Third_Terr_Buf. The typing third error window now •Type the item shown in It. blue on top line. * ROW COL VARIABLE DESCRIPTION 18-23 26-39 p_Typing_Sub_Buf. Message when typing is accepted but then expert typing replaces it. The second file used is sto.pew (note this is the letter oh as opposed to the number zero above) . ROW COL. VARIABLE DESCRIPTION 2-9 0-39 p_Id_Mess[] Six password messages used in registration password handling. 10 0-14 p_Top_Mes_R. Message *Scr Edit Mode' for top line mode message. 10 15-19 p_Page_No_Chars. Message 'Page =' for top line. 10 20-34 p_Top_Mes_B. Message 'Select Err Mode* for top line mode message. 11 0-9 p_Sup_Ident_Code. Super password to help unlock student diskettes (RANDALL) . 11 20-33 p_RC_Nbrs. 'Row = Col = ' for top line. 12 0-1 p_Dir_St_Row. Start row for display of directory. 12 2-3 p_Dir_St_Col. Start column for directory. - 141 - OW COL VARIABLE DESCRIPTION 12 4-5 pfldsrl. Field 1 start rov (9). 12 6-7 pfldnrl. Field 1 number of rows (5) . 12 8-9 pfldscl. Field 1 start column (46). 12 10-11 pfldncl. Field 1 number columns (16) . 12 12-13 . pfldsr2 Field 2 start row (9). 12 14-15 pfldnr2. Field 2 number of rows (4) . 12 16-17 pfldsc2, Field 2 start column (12). 12 18-19 pfldnc2. Field 2 number columns (14) . 12 20-21 pfieldlr. Row for Name field Pg 1 (6). 12 22-23 pfieldlc. Column Name Field Pg 1 (19). 12 24-25 pfieldln. Length of Name field Pg 1 (8). 12 28-29 pfield2r. Row for Name Field Pg 2 (7). 12 30-31 pfield2c. Column Name field Pg 2 (20) . OW COL VARIABLE DESCRIPTION 12 32-33 pfield2n. Length of Name field Pg 2 (8) . 12 36-37 p_Auto_Row. Row for display of auto-time on Page one (9). 12 38-39 p_Auto_Col. Column for display of auto-time on Page one (32). 13 p_Dir_Color_Row 13 p_Dir_Color_Indent[0] 13 p_Dir_Color_Indent[1] 13 p_Dir_Color_Indent[2] 13 p_Dir_Color_Indent[ ] 13 p_Dir_Color_Dim 14 0-9 p_Print_Page_Heading The word 'Exercise 14 25-29 p_Print_Page. The word 'Page: ' 14 30-34 p_Print_Date. The word *Date: ' 14 35-39 p_Print_Time. The word 'Time: * 15 0-1 p_No_Sel_Allowed. Failing grade (70) 15 4-5 p_No_This_Sel_Allowed. Return if this no. errors on this one selection. OW COL VARIABLE DESCRIPTION 5 8-9 p_No_Consec_Allowed. Return if no. consecutive errors reached. 6 0 CBT_Bckgrnd_Enable_S . Allows for coloring of field when LP hit. 7 0-35 Mono_Attributes[] 12 three-character Color_Attributes[] sets with the first being the last char of the char sent from the host and the next being the mono attr. and the third the color. 8 -no variable - If Y sets color above. 19 0-24 p_STO_R19_Err_Mess, •No file name given' message. 20 0-24 p_STO_R20_Err_Mess. 'CONTACT SYSTEM ADMIN ' message. 21 0-39 p_Color_Menu. The color/graphics line displayed when F6 is hit in modify or ScrUtil. The most obvious control file is the defaults file which is default.pew at run time. The students are given a copy of defaults.tds as their default file, other users get defaultx.tds. The following is the layout for the main defaultx.tds. OW COL VARIABLE DESCRIPTION pdisk Reads in current disk drive to use for exercises. 0-7 p_Def_Ident_Code The supervisors password to unlock student diskettes (if one exists it overwrites the super password from sto.pew) . auto demo time. A digit from 0-9 that changes the default time for automatic demonstration mode. no variable If 'N' it kills the light pen and the directory. resets OPT_Color[0] If 'N' sets OPT_Colσr[0] to N. 14 0-1 p_Ptr_Adjustment Positive adjustment figure. 15 0-1 p_Ptr_Adjustment Negative adjustment figure. 17 p_Printer_Type A 1 if IBM a 2 if T-2330. There is another file used for the different user types that sets a file called CTR.PEW. The proper CTR.PEW is copied from the options of CTRS.PEW (student), CTRA.PEW (author), CTRD.PEW (developer), and CTRDM.PEW (automis) . The only piece of data used is the first three characters of the screen. If they are 'STU' it sets student flags, id 'DEV developer mode, if blank it defaults to author. L. Management Interface Because there is a need to constantly go back and forth between Control Loop and CBT, leaving CBT in one state or another, the logic is littered with global variables. In addition, copies of values in the global variables are stored as the logic proceeds so they can be restated if they need to be in pristine state for Control Loop. unsigned int p_Big_Block_Size (PEW_BUFFER_SIZE) ; unsigned char pbuff[P_SCREEN_SIZE] ; /* working buffer */ unsigned char p_Typ_Buff[P_SCREEN_SIZE] ; /* typing buffer */ unsigned char ptbuff[P_SCREEN_SIZE] ; /* buffer for top half scr */ unsigned char p_Scr_Buff[P_SCREEN_SIZE] ; /* actually only holds hdr */ unsigned char p_Low_Buff[P_SCREEN_SIZE] ; /* buffer low-half scr */ unsigned char p_Err_Buff[P_SCREEN_SIZE] ; /* Error buffer */ unsigned char p_Tempbuff[38] ; /* Remainder of buffer is saved in virtual mem. */ int p_Reset; int p_CBT_Page; /* CBT "Page" no. (1, la, 2, 3, 4 or 5) */ int p_Pagela =FALSE; /* Indicates CBT Page la (subset of 1) */ int p_Playback_SW; /* Playback mode indicator */ int p_MIS_Recording_SW; /* Recording mode indicator */ int p_Select_SW; /* Set when + Key pressed, Modify mode */ int p_Modify_Mode_SW; /* Modify mode indicator (Playback also set*/ int p_Help_Mode; /* Help mode indicator */ int p_Clr_ASCII_SW; /* Color/ASCII mode Indicator */ int p_Student_Mode; /* Student mode (not simulated student */ int p_Student_MIS_SW; /* Set when student is in MIS mode */ int p_Temp_Student_SW; /* Set when Author chooses student mode */ int p_Student_Set_Mixed; /* Set=student diskettes mixed up & recovering*/ int p_Disk_l_Already; /* Disk 1 has been seen, but this is not it */ int p_Last_Disk_Nbr; /* Last disk no. of student set */ int p_Scan_All_Disks_SW; /* Set of disks mixed, set=scan all disks */ int p_Current_Disk; /* Current disk no. */ int p_MIS_Page_SW; /* Set when student or author is in MIS */ int p_Ptr_Modify_Mode; /* Mode allowing changes to Light Pen sel */ int p_Dev_Mode = TRUE; /* Development mode (allows special options*/ int ppbck_Only = FALSE; /* If = TRUE, Only Playback allowed. */ int p_Auto_MIS_Flag = FALSE; /* Indicates if Auto-MIS mode */ int p_Author_Dir_SW = FALSE; /* Indicates Author Directory mode */ int p_Play_Mode_Flag =0 ; /* M= Manual , Construction or P=Proff . */ int p_Pstart_Flag = TRUE; /* Switch set indicating initial startup */ int pdemof =0; /* 0=reg 3=student 33=temp in playback */ int pdebug =0; /* If not = 0 (set = 4) offset for PQ debug routines */ int p_Reg_Page_SW = FALSE; /* Indicates Registration Page mode */ int p_Crit_Page_SW = FALSE; /* indicates Critique page series */ int p_Dir_Page_SW = FALSE; /* Indicates course Directory Page mode */ int p_Sn4_Page_SW = FALSE; /* Indicates now displaying QSCR 4 */ int p_Dir_Page_Was_SW = FALSE; /* Indicates have seen a Directory page */ int p_Hex_Color_Mode; /* Type Hex Color Attribute mode */ int p_Hex_Color_4bits; /* Store 1st hex char */ int p_Hex_Char_Mode; /* Type Hex Character mode */ int p_Hex_Char_4bits; /* Store 1st hex char */ char p_ASCII_Char = 0x02; /* Current stored char in ColorASCII */ char p_Color_Attr = 0x71; /* Current stored attr in ColorASCII */ int p_ASCII_Mode; /* ASCII character selection mode */ int p_Color_Mode; /* Color attribute selection mode */ int p_Save_Color_Mode_Flag; /* Indicates that Top Line was */ /* "Displayed" when screen saved for Color mode */ char p_Color_Save; /* Saves color for painting */ i nt p_Color_XY_Save;/*Save row,col for color paint option in ' ~' mode*/ int p_Line_XY_Save;/* Save row,col for box drawing option in '~' mode*/ int p_Line_Box_SW; /* Indicates top, left corner marked for box */ int p_Color_Box_SW; /* Indicates top, left corner marked for paint */ unsigned char p_Top_Row_Attr ; /* check is used or just set in CBT1 */ unsigned char p_Tab_Color ; /* Tab field attribute */ int p_80col ; unsigned char ptemp[160]; int p_Alt_Cursor_SW = FALSE; /* Indicates alternate cursor being used */ int p_Alt_Cursor_Loc = 0; /* Screen location of alternate cursor*/ int p_Ident_Code_SW = FALSE; /* When set, cursor is in Ident. field*/ int p_Ident_Verify_SW = FALSE; /* If TRUE, verify Ident. of student */ char p_Cur_Att_Save = 0x11; /* Reg page saved att for alt typing cursor */ int p_Mess6_SW = FALSE; /*Indicates if err message should be erased */ int p_First_Ident_SW = TRUE; /* Indicates first identification message */ unsigned char p_Reg_File_900[200] ; /* Buffer for reading Reg file */ int p_Reg_Sz = 200; /* Size of the Registration file*/ int p_Crit_Sz = 1000; /* Max. size of the Critique file*/ unsigned char p_Crit_Buffer[PCRSIZE] ; /* Critique Buffer */ unsigned char p_Ident_Code[15] ; /* Identification code from Dir.*/ unsigned char p_Ident_Code2[15] ; /* Newly typed Identification c. */ unsigned char p_Sup_Ident_Code[15] ; /* Master Ident. code(from STO.PEW) */ 5 unsigned char p_Def_Ident_Code[15] ; /* defaults.tds ident code */ unsigned char p_Id_Mess[40*8] ; /* 8 Messages for Reg. mode(STO.PEW) */ int p_Field_Att_Opt; /* Non-required field 10 attribute */ int p_Field_Att_Req; /* Required field attribute */ int p_Critique_Flag = FALSE; /* A Critique has been selected */ 15 int p_Crit_Data_Added_SW = FALSE; /* If TRUE, new added to old data */ /* next two - why here? */ int p_End_Flag; /* Indicates End key pressed in Modify mode */ 20 int p_Typing_Was ; /* Indicates that there was typing last time */ /* */ int p_Ok_For_Next_Crit; /* It is OK to display next Critique page */ 25 int p_This_Is_Critique_Flag; /* This Dir sel is of a Critique */ int p_Tempbuff_Full = FALSE; /* Switch set if p_Tempbuff is full */ int p_Cheat_Mode = TRUE; /* On for Authors-allows F7 key 30 to "cheat */ * * int p_Top_Line_Display_SW = FALSE; /* Set=top line disp and saved in Mod */ int p_Save_Blk_Ok = TRUE; /* Set = now ok to save block in Modify mode */ 35 int p_Go__To_End EXTVAL3(FALSE) ; /* Set in Mod = run to the end(printing) */ - 150 - int p_Dir_Color_Row; /* From STO.PEW, color of dir. rows. */ char p_Dir_Color_Indent[4] ; /* From STO.PEW - Indenting colors */ int p_Dir_Color_Dim; /* From STO.PEW - "Half Intensity" color */ unsigned char p_Student_Course_Id[10] ; /* Stores Course Name */ int p_Dir_Counter; /* Location on Dir shell of current pg of entries */ int p_Last_Dir_Ptr_Row; /* Saved, last selected directory row no.*/ unsigned char p_Bar_Background; /* Color of directory background bar */ unsigned char p_Dfalt_Attribute; /* "bar " for Default page */ int p_Saved_Dir_Attr_SW; /* Set=attribute for the color bar is saved */ unsigned char p_Saved_Dir_Attr; /* Saved Dir attr on QSCR 4 for bar */ int p_G$Offset_40_Bar; /* Color bar screen location in dir & QSCR4 */ int p_No_Sel_Misses; /* No. sel or type-ins where error(or more) was made */ int p_No_Misses_This_Sel; /* No. errors on this selection or type-in */ int p_No_Consec_Misses; /* No. consec. sel or Type-ins of >= 1 error */ int p_No_Sel_Allowed; /* Min. % score before student "terminated */ int p_No_Sel_Allowed_l; /* Min % score if != blan (from dir.) override */ int p_No_This_Sel_Allowed; /* No.errors allowed on one sel. or type-in */ int p_No_Consec_Allowed; /* No. consec. sel./type-ins allowed with err */ int p_Total_Score; /* No Pages - No. pages with error */ - 151 - int p_Total_Pages; /* No. of pages in an exercise(not */ /* counting extra pages */ int p_Official_Score; /* Final, % student score */ int p_No_Type_Fields; /* Number of Type in fields in the Exer. */ unsigned char p_Temp_600[80] ; /* Buffer for- the 600 directory record */ int pscore; /* Score */ int p_Dir_Page_Nbr EXTVAL3(0); /* Directory page no., currently displayed */ int p_Dir_Last_Page_Nbr EXTVAL3(0); /* Directory last page no. */ int p_Dir_Attr; /* Attribute used for directory page(obtained)*/ /* from the shell */ int p_Dir_Page_Upper; /* Contain page no. of upper half page */ int p_Dir_Page_Lower; /* Contain page no. of lower half page */ unsigned char p_Current_Dir_U_L;/* Is 'U* or *L', arg. to P_Write_Dir_Page */ int p_Mode_0ption_0verride EXTVAL3(0); /* Directory Mode Option Override */ int p_Display_Score_Flag EXTVAL3(TRUE) ;/* Directory switch allowing score */ /* display */ int p_Elapsed_Time EXTVAL3(0); /* Student elapsed time,this exer. */ unsigned char p_Temp_Dir_Store[4] ; /* Temporary storage for results of */ /* Exercise */ unsigned char p_D0S_Date[8] ; /* DOS Date(ASCII) */ unsigned char p_D0S_Time[5] ; /* DOS Time(ASCII) */ unsigned char p_DOS_Date_S[8] ;/* DOS Date, start */ int p_DOS_Min_Bin; /* DOS Minutes, end */ int p_DOS_Min_Bin_S; /* DOS Minutes,start */ int p_DOS_Sec_Bin; /* DOS Sec, end */ int p_DOS_Sec_Bin_S; /* DOS Sec, start */ int pfldsrl; /* sr=starting row */ int pfldnrl; /* nr=no. rows */ int pfldscl; /* sc=starting col */ int pfldncl; /* nc=no. cols */ int pfldsr2; /* sr=starting row */ int pfldnr2; /* nr=no. rows */ int pfldsc2; /* sc=starting col */ int pfldnc2; /* nc=no. cols */ /*—Defines type-in fields on CBT page 3 */ int pfieldlr EXTVAL3(4); /* Row—Name, type-in field,p-l */ int pfieldlc EXTVAL3(13) ; /* Col—Name, type-in field,p-l */ int pfieldln EXTVAL3(8); /* Length—Name, type-in field,p-l */ int pfield2r EXTVAL3(7) ; /* Row p_2 */ int pfield2c EXTVAL3(13) ; /* Col p-2 */ int pfield2n EXTVAL3(8) ; /* Length p-2 */ int p_Auto_Row; /* Location on page 1 of automatic playback */ /* time */ int p_Auto_Col; /* Location on page 1 of automatic playback */ /* time */ int p_Dir_St_Row; /* Starting row of the Author directory */ int p_Dir_St_Col; /* Starting col of the Author directory */ unsigned char p_Exercise_Name[12] ; /* Exercise name */ int p_Set_Record_Type; /* Set so that Pscrtrap will set R.T. = 3 */ int p_p_From_2_Flag; /* Recording indicator that p_From_Ptr_Sel=2 */ int p_Set_Next_p_From; /* Recording mode, set next p_From_Ptr_Sel */ int p_Disable_Pscrtrap_SW EXTVAL3(FALSE) ;/* Skip saving buffer in Pscrtrap,*/ /* if TRUE */ int p_Line_Speed; /* Choice of simulated line speed for playback */ int p_Record_Type; /* See pew4, 22 byte header desc. */ int p_Repeat_Playback_SW; /* If set, repeat, continously, Playback */ int p__Throw_Away_Typing;/* If set, throws away any typing, till reset*/ int p_No_Full_Int_SW; /* If set(in Playback) will stop Full */ /* of top two & bottom two rows */ int p_Equal_Flag; /* Equal sign in 25th row(repeat Proceedure */ /* line for student */ int p_QSCR_4_SW; /* Display is QSCR 4 */ unsigned char p_QSCR_4__0ption; /* Option selected from QSCR 4 */ unsigned char p_Highlight_Color; /* Automatic playback, displayed block*/ int p_Highlight_Time; /* " */ int p_Select_Time; /* Selection time or code, see PEW3.C writeup */ /* This time will have 1 sec. added to it */ int p_Select_Timel; /* p_Select_Time, minus 1 */ int p_Save_p_Select_Time EXTVAL3(0); /* Save location for p_Select_Time */ int p_Blk_Cnt; /* Block count within exercise */ int p_Blk_Cnt_a; /* Incremented when recording from MIS- to make */ /* sure that p_Blk_Cnt gets incremented */ unsigned char p_F_Key_Set; /* Set to the table number of the Fn. key*/ /* set to be used(from STx.PEW) */ int auto_demo_time EXTVAL3(2); int p_Type_Buff_Length; int p_In_Type_Buff_Ptr; int p_Out_Type_Buff_Ptr; unsigned char p_Temp_Type_Char; /* Working storage by char, routine */ int p_Typing_Save_Flag; /* Indicates area under error message,saved*/ int p_First_Type_Loc; /* First screen buffer location of any */ /* typed character */ int p_Last_Type_Loc; /* Last screen buffer location of any */ /* typed character */ int p_Type_Start_Loc; /* Screen location of the first typed char. */ int p_End_Of_Type_Blk; /* End of Typing block */ unsigned char p_Del_Typed_Chars[9] ; /* Stores the characters to be disregarded*/ /* in checking students typing(option 1 */ 5 unsigned char p_25th_Color; /* Color of Author message in the 25th row */ int p_Play_Int_Code, p_Play_Int; int p_Int; /* Copy of p_Play_Int for local 10 use. */ int p_Raw_Page_Now EXTVAL3(FALSE) ; /*Indicates on basic page, in Modify mode*/ unsigned char p_Modify_State; /* 'B' - Selection Error state */ 15 /* 'R' - Basic Page state */ /* Ε' - Extra Screen state */ /* I -J I _ Typing Error state 20 */ unsigned char p_Playback_State; /* State of Playback program for re-entry */ int p_Stop_Each_Screen_SW;/* Modify mode, wait for fn. key, each time*/ 25 int p_Wait_Each_Char_SW; /* Modify mode, wait for time between each */ /* character on the screen */ int p_Speed_Modify EXTVAL3(FALSE) ; /* Switch indicating hurry in Modify mode*/ 30 /* after pressing the End key » «- */ int p_RC_Flag; /* Flag indicating Row & Column is displayed in */ /* top row. Modify mode 35 */ unsigned char p_RC_Nbrs[30] ; /* Row & Col display field */ - 156 - unsigned char p_F2_Buffer[80] ; /* Contains Top Row 15 cols messag (sto.pew)*/ unsigned char p_F2_Buf_Save[80] ; /* Save area for Top row, 15 cols. */ unsigned char p_Err_Mess_Buf_Save[8*22*2] ; /* Save area */ unsigned char p_Err_Mess_Bufl_Save[18*16*2] ; /* Save area */ unsigned char p_Err_Mess_Init_Buf[5*40*2] ; /* Error message initial mess. */ unsigned char p_Third_Error_Buf[6*15*2] ; /* Third sel. error message */ unsigned char p_Third_Terr_Buf[6*15*2]; /* Third typing error message */ unsigned char p_Typing_Sub_Buf[6*15*2] ; /* Save area for substit mess. */ unsigned char p_Typing_Sub_Save[6*15*2] ; /* Save area for Typing 3rd er.*/ unsigned char p_Fl_Fn_Buffer[40*2*4] ; /* Top row mess.-Author menu */ unsigned char p_Fl_Fn_Save[40*2*4] ; /* Save area for Author menu */ unsigned char p_S2_Fn_Buffer[40*2*10] ; unsigned char p_S2_Fn_Save[40*2*10] ; unsigned char p_Err_Mess_Err_Title_C[40*2] ; /* Error message Title, Instr. */ unsigned char p_Err_Mess_Err_Title_P[40*2] ; /* Error message Title, Proff. */ unsigned char p_Type_Err_Title_C[20*2] ; /* Type Err. ess. Title, Instr */ unsigned char p_Type_Err_Title_P[20*2] ; /* Type Err.Mess. Title,Proff. */ unsigned char p_Save_File_Name[25] ; /* Save area for menu file name*/ unsigned char p_Page_No_Save[10] ; /* Page no. (top row) save */ unsigned char p_Top_Mes_E[30] ; /* Top message - Extra Page error mode */ - 157 - unsigned char p_Top_Mes_T[30] ; /* Top message - Typ. error mode */ unsigned char p_Top_Mes_R[30] ; /* Top message - Basic Page mode */ unsigned char p_Top_Mes_S[30] ; /* Top message - Last screen mode */ unsigned char p_Top_Mes_B[30] ; /* Top message - Sel. error mode */ unsigned char p_Top_Mes_M[30] ; /* Top message - Mulitple Edit */ unsigned char p_Top_Mes_Buf[50] /* Save buffer */ unsigned char p_Top_Mes_Bufl[30] ; /* Save buffer */ unsigned char p_Type_Error_Buf[240] ; /* Save buffer */ unsigned char p_Type_Error_Blk[240] ; /* Save buffer */ unsigned char p_Type_Expert_Buf[330] ; /* Save buffer */ unsigned char p_Type_Student_Buf[330] ;/* Save buffer */ unsigned char p_Dont_Stop_Char; /* If TRUE, dont wait between typed chars */ unsigned char p_Light_Pen_Row; /* Light pen row of current selection */ unsigned char p_Light_Pen_Col; /* Light pen column of current selection */ int p_Ptr_R_Save; /* Temporary saved light pen row */ int p_Ptr_C_Save; /* Temporary saved light pen column */ int p_Ptr_Save_Row; /* Saved row for inserting in next screen */ int p_Ptr_Save_Col; /* Saved col. " */ int p_Cursor_Save_Row; /* Saved cursor for inserting in header */ int p_Cursor_Save_Col; /* " */ int p_From_Ptr_Sel; /* Flag indicating previous screen had light pen */ /* selection(see pew4 22 byte header desc. */ int p_Save_p_From_Modify_SW;/* Switch indicating saved p_From_Ptr_Sel */ int p_Save_p_From_Modify; /* Save p_From_Ptr_Sel, for Modify mode */ int p_SizeMinusl; /* See pew4, 22 byte header */ int p_Save_G$Offset_40; /* Save Screen position */ int p_Throw_Away_Extra; /* Switch to throw away "Extra Screens", */ /* until reset */ int pdclctcf; /* If — 0, set to 1. If 1, do delete */ int p_Delete_Exercise_SW EXTVAL3(FALSE) ; /* If set FALSE, warns user and */ /* sets TRUE. If TRUE, executes. */ int p_Delete_Screen_SW EXTVAL3(FALSE) ; /* If set FALSE, warns user and */ /* sets TRUE. If TRUE, executes. */ int p_Err_Mess_Select;/* Authors menu is up(perhaps with error mess. */ int p_Err_Mess_Exists; /* Error message is in the exercise */ int p_Err_Mess_Waiting; /* The error message buffer is staged */ int p_Err_Mess_New; /* Sets up for new(first) Student Error */ /* Message */ int p_QSCR_X_Exists; /* A QSCR X has been encountered and stored*/ int p_Err_Mess_Kill; /* Under no circumstances, use Error Mess */ /* even if there is one */ int p_Err_Mess_Now EXTVAL3(FALSE) ; /* Switch to indicate an Error Message */ /* exists and is ready(staged */ - 159 - int p_Err_Mess_Was; /* An error message was put up last */ int p_Type_Err_Mess EXTVAL3(FALSE) ; /* Indicates that it is time to */ 5 /* go into Typing error message mode */ int p_Type_Err_Mess_Is EXTVAL3(FALSE) ; /* Indicates that there is a typing*/ /* error message(Exercise has been 10 */ /* modified) */ int p_Type_Error_New EXTVAL3(FALSE) ; /* Indicates a new typing error */ 15 /* message exists */ int p_Typing_Now EXTVAL3(FALSE) ; /* It is time to process typing */ int p_Typing_Exists EXTVAL3(FALSE) ; /* A typing buffer exists for this */ 20 /* Display screen */ int p_Typing_Skip_One EXTVAL3(FALSE) ; /* Skip one typing field, Training */ /* mode */ /* No. of columns in error /* to 20 by Pstart /* Number of columns in author /* error window(do not change from /* current set at 20 /* Number of rows in the author int p_Error_Window; /* Switch that indicates that an error */ /* exists */ int p_24row_Block; /* This is flag to allow old, recorded */ /* screens(24 rows) to be used. */ int p_Save_25th; /* Switch indicating need to save 25th row */ int p_Make_QSCR; /* If 1= 0, and numeric or 'X' is shell */ /* to be used for replace or insert */ int p_Cursor_Cnt; /* Position in the cursor table of the */ /* typing cursor(author's menu). */ int p_Save_01d_Cursor; /* Saves cursor pos. when putting up error */ /* window */ int p_Page_No_Exer; /* Modify mode page no. */ int p_Page_No_Real; /* No. pages student has seen displayed */ /* during exercise */ int p_No_QSCR; /* If TRUE, No QSCR */ int p_QSCR_Kind; /* If != 0, is QSCR # (where # is from a */ /* list described in pew4 file. */ int p_Delete_Page_No; /* Page no. being deleted */ int p_Save_Ptr_For_Delete EXTVAL3(FALSE) ; /* Saved Light Pen coordinates */ /* from a deleted screen */ - 161 - /* (to be added to next 22 byte V /* header) . */ int p_Word_Wrap_Flag; /* Switch that word wrap occurred */ int p_No_Time_Flag; /* A switch that allows 0 time between - */ /* screens in automatic playback mode */ unsigned char p_Color_B; /* 3rd student error highlight attribute */ /* from STO.PEW */ int p_Line_Length; /* Set to desired line length for */ /* P_Search_For_Left routine */ int p_Ident_Line_No EXTVAL3(19); /* Row no. of the student identification */ /* field on the Registration page */ int p_Ident_Good; /* Switch indicating that a good */ /* identification code was typed by student*/ int p_G$Offset; /* copy of G$Offset_40(screen buffer pos.) */ int p_Teaching_Mode_SW; /* Set after Instructional or */ /* Profficiency mode selected on QSCR 4 */ int p_QSCR_l_Mode_SW EXTVAL3(0); /* Exercise statement now displayed */ int p_Teaching_Error_SW; /* Set when error made on this */ /* selection or type-in. */ int p_Nbr_Errors_This_Sel; /* Number of such errors */ int p_Skip_Display_SW; /* Don't display this screen */ int p_Prof_Mode_SW; /* Profficiency mode */ int p_Experts_Light_Pen_Row; /* Experts L.P. sel.- test student with */ int p_Experts_Light_Pen_Col; /* Experts L.P. sel.- test student with */ int p Timel; /* Time after displaying OK message to */ /* student before displaying typing */ int p_Time2; /* Time after displaying typing to */ /* student before going on */ unsigned int p_Blk_Cnt_x; /* Big Block No. (60K . blocks) */ unsigned int p_Big_Block_Ptr; /* Pointer within big block */ int p_Back_Up_Table_Nbr EXTVAL3(P_BACKJJP_TABLE_CNT) ; /* Set to 50 */ /* Back-up tables(for documentation see P_Backup writeup */ unsigned char p_Back_Up_Table[P_BACK_UP_TABLE_CNT*4] ;/*4 Bytes for each entry*/ unsigned char p_Back_Up_Tablel[P_BACK_UP_TABLE_CNT*4] ; int p_Prdskx_Flag EXTVAL3(FALSE) ; /* TRUE after first time(used by backup */ /* table update */ int p_Big_Block_Nbr; /* File pointer for each start of buffer */ /* (in Big Block) */ long int p_Save_Saveb; /* Current file pointer, saved, for the */ /* new, Modified Exercise, for the * backup */ * 5 /* file. This sync point is used for . the */ /* table */ long int p_Save_Saveb_01d; /* Previous- file pointer(old 10 p_Save_Saveb)*/ long int p_Big_Block_Cur_Nbr; /* Pointer in the Big Block of last */ /* Block in the Big Block(at the end */ 15 unsigned int p__Last__p_Big_Block_Ptr; /* Last Big Block pointer */ unsigned int p_Real_Last_Ptr; /* added for multiple del test */ unsigned int p_Save_p_Big_Block_Ptr; /* Previous to last 20 pointer */ unsigned int p_Save_p_Big_Block_Ptr_a; /* Saved pointer in Big Block*/ /* at last record in block */ unsigned int p_Last_p_Blk_Cnt_x; /* Last Big Block 25 number */ unsigned int p_Save_p_Blk_Cnt_x; /* Previous to last B.B. nbr */ unsigned int p_Cur_p_Blk_Cnt_x; /* Big Block number - current*/ 30 int p_Back_Up_Table_Ptr; /* Pointer of next location in the */ /* backup table */ int p_First_Backup_Ready; /* Switch indicating start of backup */ 35 unsigned char p_Back_Up_Mess_Yes; /* Switch to cause display of message */ - 164 /* at completion of backup unsigned char p_Back_Up_Save[40*3*2] ; /* Save buffer for message */ unsigned int p_Virtual_Offset; /* Offset into the virtual buf */ int p_Virtual_Read_Flag; /* Indicates for read */ int p_Virtual_Save[30] ; /* Saves pointers */ int p_Virtual_Temp_Ptr; /* Pointer within 50 char.buff */ unsigned char p_Virtual_Temp_Buf[50] ; /* Buffer for blocking input */ int p_Auto_Err_Mess; /* Modify mode, indicates to */ /* put up error message for */ /* selection or typing mode */ int p_Save_Module_Vblk; /* Save Module Name virtual */ /* block no. */ int p_Save_Module_Line; /* Save Module Name virtual */ /* line no. */ int p_B_F_Number; /* Page number to stop on */ /* when printing - 165 - unsigned char p_Start_Page_Nbr; /* Starting page number for printing */ unsigned char p_End_Page_Nbr; /* Ending page number for printing */ unsigned char p_Print_All; /* Switch set indicating to print the Exercise */ int p_First_Print; /* First print action(used to set up the printer*/ int p_Dont_Print; /* Set FALSE, reset TRUE by PR40SUB routine */ char p_Printer_Type; /* ASCII number for the printer type */ int p_Page_Printed; /* Page no. of page just printed */ unsigned char p_Err_Mess_Print_Title[20] ; /* Title for Error Message */ /* (Printout) . */ unsigned char p_Type_Print_Title[20] ; /* Title for Typing Error Message */ /* (Printout) . */ unsigned char p_Print_Page_Heading[20] ; /* Printout Page Heading */ unsigned char p_Print_Page[20] ; /* Printout Page No. Heading */ unsigned char p_Print_Date[14] ; /* Printout Date Heading */ unsigned char p_Print_Time[ll] ; /* Printout Time Heading */ unsigned char p_Module_Name[8] ; /* Printout Exercise Name Heading*/ #ifdef CBTMAIN else they are decleared here as external unsigned char p_Pr_Narrl[] = { 0, OxlB, 'W' , O',0xlB ,0x12, 0, 0, 0, 0 }; unsigned char p_Pr_Narr2[] = { 0, OxlB, '[• , 'l' , •1', ' ', 0, 0, 0, 0 }; . - 166 - unsigned char p_Pr_Widel[] = ( 0, OxlB, 'W', '1', 0, 0, 0, 0, 0, 0 } ; unsigned char p_Pr_Wide2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char p_Under_Onl []= { 0, OxlB, 0x2D, 0x01, 0, 0, 0, 0, 0, 0 } ; unsigned char p_Under_Off1[]= { 0, OxlB, 0x2D, 0, 0, 0, 0, 0, 0, 0 }; unsigned char p_Under_0n2 []= { 0, OxlB, 91, 52, 109, 0, 0, 0, 0, 0 }; unsigned char p_Under_0ff2[]= { 0, OxlB, 91, 48, 109, 0, 0, 0, 0, 0 } ; int p_QSCR_Screen; /* Thisis a QSCR, now displayed */ unsigned char p_Save_LP[3] ; /* Saves Light Pen coord, when deleting */ int p_Help_Page_Nbr; /* Number of the Help page(start with 0) */ int p_G_Help_Page_Nbr; /* Number of the Generic Help page */ int p_First_Help EXTVAL3(TRUE) ; /* First Help page yet to be displayed */ unsigned char p_Save_Help_Fn_Char; /* See P_Get_Help_Fn_Char */ int p_Save_Help_Fn_Nbr EXTVAL3(0); /* for complete description, see */ unsigned char p_Save_Help_Fn_Attrl EXTVAL3 (0x6F) ; /* Phelp function */ unsigned char p_Save_Help_Fn_Attr2 EXTVAL3(0x67) ; /* description. */ int p_Save_Help_Flag; /* Indicates that Top Line was */ /* "Displayed" when screen saved */ /* for Help mode */ - 167 - int p_Start_Help_Soon_SW EXTVAL3(FALSE) ; /* Requests the interrupt routine */ /* to start Help mode */ int pp; /* universal for loop variable */ unsigned char pchar; /* CBT copy of G$key(char. just typed */ unsigned char ppchar; /* Temporary copy of pchar */ unsigned char pchara; /* Another copy of pchar */ int p_PS2_SW EXTVAL3(FALSE) ; /* Indicates PS2 computer */ int p_80col_SW EXTVAL3(FALSE) ; /* 80 or 40 cols. switch */ int p_Cols EXTVAL3(40); /* Number of cols on the screen(40 or 80)*/ unsigned char p_80col_cols EXTVAL3(40) ;/* Num of cols on the scr(40 or 80)*/ int Mode_80_Column_SW EXTVAL3(FALSE) ; /* Switch, indicating 80 cols,if TRUE*/ unsigned char p_General_Meεs[40*22] ; /* Stores ST9.PEW messages */ unsigned char p_General_Mess_A[22] ; /* Stores ST9.PEW attributes */ int p_Temp_Size; /* Contains PSZE (2000) */ int p_Format_SW EXTVAL3(FALSE) ; /* Switch indicating 80 cols. */ int p_MIS_Start; /* First set when starting MIS(Student or Author) */ int p_Startup_Flag EXTVAL3(FALSE) ; /* This indicates that it is system */ /* startup */ int p_Stop_MIS_Temp_S ; /* Stops MIS from talking to the Control Loop */ /* while on page 2 or Help mode */ int p_Save_New_Cursor_Loc;/*Cursor pos. saved from MIS(going to p.2) */ unsigned char p_Save_Buf[480] ; /* Working buffer */ unsigned char p_Color_Menu[100] ; /* Displayed in 25th row in Color Mode */ int p_Save_p_From; /* Save p_From_Ptr_Sel, for recording */ unsigned char pnumbf[16]; /* Working buffer for Exercise name, from screen */ #ifdef CBTMAIN unsigned char pnames[] = " "; /* Edited, saved Exercise name */ #else extern unsigned char pnames[] ; #endif unsigned char p_Buf[160] ; /* Working buffer */ unsigned char p_Extra_Buffer[24] ; /* Working Buffer */ unsigned char p_Working_Buf[80] ; /* Working Buffer */ unsigned char p_Save[20] ; /* Working Buffer */ unsigned char p_S_25th[80] ; /* Buffer for saving 40 chars.in 25th row*/ unsigned char p_Save_25th_Sc Del[80] ;/*Buff for saving 40 chars in 25th row*/ unsigned char p_Save_25th_Buffer[80] ;/*Buff for saving 40 chars in 25th row*/ unsigned char p_Insert_Save_Buffer[12] ; /* Saves screen for Insert message */ unsigned char p_Page_No_Chars[20] ; /* Buffer for screen page no. */ unsigned char p_Insert_Name[12] ; /* Buffer for Insert screen message */ int p_Dir_Count; /* No directory entries */ unsigned char p_STO_R19_Err_Mess[50] ; /* Error message on STO.PEW, row 19 */ unsigned char p_STO_R20_Err_Mess[50] ; /* Error message on STO.PEW, row 20 */ int p_Error_Code; /* Error code for trouble shooting disk problems */ int p_Error_Cnt_OK_SW;/* Allows Errors to be counted */ int p_CurLPCol; /* Copy of CurLPCol */ int p_Ptr_Adjustment; /* Adjustment for Light Pen Cursor */ int p_Dir_Pointer_Ok; /* Ok to use LP on Page 1 */ int p_Dir_Scroll_Row EXTVAL3(0); /* Start on row 0 of Directory, Page 1 */ int p_Only_STT; /* Switch to only allow display of Default.pew */ /* (on page 4) */ unsigned char p_New_Name[] EXTVAL3(" ") ; /* New name for RENAME, etc */ int ptrace; /* Debugging trace switch */ int pcolorl EXTVAL3(1); /* Flag to cause 25th line on "M" series */ int p_Insert_Page_SW EXTVAL3(0); /* Modify mode, ready to insert screen */ unsigned char pdisk EXTVAL3('B') ; /* Recording & Playback disk drive */ int p_Dir_0k EXTVAL3(FALSE) ; /* Allows display of directory(Page 1 & la) */ int p_Dont_Poke; /* Flag for Pdispr indicating Don't poke */ int p_Only_Poke; /* Flag for Pdispr indicating Don't copy */ /* to ptbuff[] */ int p_First_Compare_Out; /* For future compression - Unused */ int p_First_Compare_In; /* For future compression - Unused */ REMOTE MODE int p_Int_MIS_Remote_Flag EXTVAL3(FALSE) ; /* This Flag is set TRUE only by */ /* the CBT Pmasterm, at initialization, after first */ /* starting with MIS. When return from subsequent */ /* sessions as MIS terminal, it is used to send an */ /* interrupt to MIS each 30 sees. This is to keep */ /* the RT1215 awake. It is checked in Ppewint2. */ /* These are for timing p_Int_MIS_Remote_Flag */ unsigned int p_Remote_Off_Key_Reset; unsigned int p_Remote_Off_Key_High; unsigned int p_Remote_Off_Key_Low; unsigned int p_Remote_Off_Key_Intv; unsigned int p_Remote_Int_Intv; unsigned int p_MIS_Off_Key_Reset; unsigned int p_MIS_Off_Key_High; unsigned int p_MIS_Off_Key_Low; unsigned int p_MIS_Int_Intv; /*—p_Cursor_Table[] is the basic Cursor control for the Error */ /*—Selection Menu in Modify mode or page 4 */ /*—p_Cursor_Tablel[] then is used to provide a modified copy */ /*—which depends on whether or not an error display is used with */ /*—the menu. Each pair of entries provides the first location and */ /*—the last location of a (screen) field. */ =____.__.__.________.__.__.==__.*/ #ifdef CBTMAIN int p_Cursor_Table[] = { 40*4+22, 40*4+37, 40*5+22, 40*5+37, 40*6+22, 40*6+37, 40*7+22, 40*7+37, 40*8+22, 40*8+37, 40*9+22, 40*9+37, 40*21+37, 40*21+39, 40*22+33, 40*22+35, 40*22+37, 40*22+39, 40*23+26, 40*23+26, 40*23+28, 40*23+35, 40*23+37, 40*23+39, '\0' } /* */ int p_Cursor_Tablel[] = { 40*4+22, 40*4+37, 40*5+22, 40*5+37, 40*6+22, 40*6+37, 40*7+22, 40*7+37, 40*8+22, 40*8+37, 40*9+22, 40*9+37, 40*21+37, 40*21+39, 40*22+33, 40*22+35, 40*22+37, 40*22+39, 40*23+26, 40*23+26, 40*23+28, 40*23+35, 40*23+37, 40*23+39, •\0' - 172 - } #else extern int p_Cursor__Table[] ; extern int p_Cursor_Tablel[] ; #endif int fpin_SW , fpinl_SW , fpin2_SW , fpin3_SW , fpin9_SW ; int fpinx_SW , fpto_SW , fptol_SW , fpto2_SW , fpto3_SW ; int fptox_SW , fptoy_SW ; FILE *fpin, *fpinl, *fpto, *fptol; FILE *fpin2, *fpto2; FILE *fpinx, *fptox; #ifdef CBTMAIN else extern unsigned char [] unsigned char p_File_Name_mas[ = "MAS.PEW unsigned char p_File_Name_masl ] = "MAS1.PEW unsigned char p_File_Name_mat[ = "MAT.PEW unsigned char p_Fi1e_Name__ma11 ] = "MAT1.PEW unsigned char p_File_Name_mar[ = "MAR.PEW unsigned char p_File_Name_sys[ = "SYS.PEW unsigned char p_Fi1e_Name_sys1 ] = "SYS1.PEW unsigned char p_File_Name_syt[ = "SYT.PEW unsigned char p_Fi1e_Name_sy11 ] = "SYT1.PEW unsigned char p_File_Name_syr[ = "SYR.PEW unsigned char p_File_Name_rec[ = "REC.PEW unsigned char p_File_Name_recl ] = "REC1.PEW unsigned char p__File_Name_spc[ = "SPC.PEW unsigned char p__File_Name_spcl ] = "SPC1.PEW unsigned char p__File_Name_hlp[ = "HLP.000 unsigned char p__File_Name_sto[ = "sto.pew unsigned char p__File_Name_stol ] = "stol.pew unsigned char p__File_Name_st9[ = "st9.pew unsigned char p__Fi1e_Name_s191 ] = "st91.pew unsigned char p__File_Name_stO[ = "sto.pew unsigned char p__File_Name_st01 ] = "stOl.pew unsigned char p__File_Name_stl[ = "stl.pew - 173 - unsigned char p_File_Name_err[] = "err.pew "; /* Stu > errs allowed*/ unsigned char p_File_Name_lst[] = "1st.pew "; unsigned char p_File_Name_doa[] — "doa.pew II unsigned char p_File_Name_ctr[] - "ctr.pew II . I unsigned char p_File_Name_asc[] = "asc.pew I"t;. /* ASCII & Color Window */ unsigned char p_File_Name_lp[] = "lpw.pew "; /* Pointer change window */ unsigned char p_File_Name_chp[] = "STR.HLP "; unsigned char p_File_Name_dmo[] = "DEMO unsigned char p_File_Name_fil[] = "fil.pew unsigned char p_File_Name_fill[] = "fill.pe unsigned char p_File_Name_tch[] ■= "B:tch.pe No. will be set=pdisk*/ unsigned char p_File_Name_exc[] = "B:excpe No. will be =pdisk */ unsigned char p_File_Name_file[]="B:xxxxxxxx.tut";/*Disk No. will be=pdisk*/ unsigned char p_File_Name_r901[] = "CBTREG1.SCR"; /*Disk No. will be set */ unsigned char p_File_Name_r900[] = "B:CBTREG.DAT"; /*Disk No. will be set */ unsigned char p_File_Name_d900[] = "B:CBTDIR.DAT"; /*Disk No. will be set */ unsigned char p_File_Name_cdat[] = "B:CBTCRIT.DAT"; /*Disk No. will be set */ unsigned char p_File_Name_4080[] = "XNEW.SCR"; /* Option screen */ unsigned char p_File_Name_801[] = "SPLIT.TDS"; /* Split screen shell */ unsigned char p_File_Name_stt[] = "default.pew •i; /* Default display which*/ /* DEFAULTX.TDS or DEFAULTS.TDS are copied into */ unsigned char p_File_Name_d901[] = "CBTDIR.SCR"; /*Disk No. will be default */ unsigned char p_File_Name_s4x[] = "CBTS4X.SCR"; /*Disk No. will be default */ unsigned char p_File_Name_cscr[] = "CBTCRIT1.SCR"; /*Disk No. will be dfault*/ unsigned char p_File_Name_dir[] = "STP.PEW "; /* Directory shell, page 1 */ unsigned char p_File_Name_dirl[] = "STD.PEW "; /* Author's Directory shell */ unsigned char p_File_Name_shel[] = "STB.PEW "; /* Screen shell for STD.PEW */ unsigned char p_System_Dir[] = "DIR A:*.000>TEMPDIRP.$P$"; 27. MESSAGES unsigned char p_Wait_Mess[] = " Please wait a moment "; unsigned char p_Complete_Mess[] = " Complete "; 28. FILE NAME WORKING BUFFERS unsigned char pd[16] ; unsigned char pdhelp[16] ; unsigned char prr[8]; unsigned char pdd[16] ; unsigned char pdap[20]; unsigned char pdapx[20] ; unsigned char pdapxa[20] ; unsigned char p_FCB[260]; unsigned char p_FCB_Data[260] ; unsigned char p_FCB_Extra[260]; unsigned int pint; unsigned int ptimerl, ptimer2; unsigned int ptimerlf, ptimer2f; long int pclock, pclocks, pclockt; unsigned int pClock_High; unsigned int pClock_Low; - 175 - unsigned int pDate_Reset; int p_Spec_Int_24H[2] ; int p_Saved_Int_23H[2] ; int p_Saved_Int_24H[2] ; int p_Seg_Saved; int p_Seg_Savedl; int Error_24H_Code_ft; unsigned char p_Block_Mark_Name[12] ; unsigned char p_Block_Move_Name[12] ; int p_Block_Move_Enable EXTVAL3(FALSE) ; int p_Insert_SW EXTVAL3(FALSE) ; int p_Block_Move_Flag EXTVAL3(FALSE) ; int p_Block_Move_Start EXTVAL3(FALSE) ; int p_Block_Move_End EXTVAL3(FALSE) ; int p_Save_Block_Start EXTVAL3(FALSE) ; int p_Save_Block_End EXTVAL3(FALSE) ; int p_Save_For_Insert EXTVAL3 (FALSE) ; unsigned char p_Save_Move_Char EXTVAL3(FALSE) ; int p_Save_Move_Loc EXTVAL3(FALSE) ; int p_Block_S_C EXTVAL3(0); int p_Block_S_R EXTVAL3(0) ; int p_Block_E_C EXTVAL3(0); int p_Block_E_R EXTVAL3(0); int p_QSCR_W_Disable_SW; /* Disables special option for QSCR W, */ /* temporarily. */ int p_Save_Screen_Type_D$; int p_Save_Have_80_Columns; int p_Dont_Convert; /* Don't convert going from Control Loop system */ int G$Offset_40;/*This is always the current screen buffer location*/ int Screen_Type_D$ ; int Backup_G$RowMap[26]; extern char Color_Attributes[] ; extern char Mono_Attributes[] ; unsigned char far vbuf[32768] ; /* allocates memory for vbuf */ unsigned char far course_dir[700] [80] ; /* allocates stu.dir.mem */ unsigned char critique_buffer[320] ; /* critque buffer memory */ unsigned char far vbuf2[65536] ; /* vbuf2 memory allocation */ unsigned int vbuf_segment; /* poke/peek need segment and offset */ unsigned int vbuf2_segment; /* find the seg/off */ unsigned int vbuf_offset; /* for each of these far arrays */ unsigned int vbuf2_offset; union REGS Inregs_ft; union REGS Outregs_ft; struct SREGS Segregs_ft; unsigned int Save_Int_Seg_ft EXTVAL3(0); unsigned int Save_Int_Loc_ft EXTVAL3(0); int Error_24H_Code_ft EXTVAL3(0); extern int INT_24H_PROG_FT; extern int INT 23H PROG; int Ret_ft EXTVAL3(0); char Paul_ft[10] EXTVAL3({ 0 }); - 177 - union { char byts[4] ; int wrds[2] ; int (*ptr) ; } Spec_Int_24H_ft_S EXTVAL3({ 0 }); union { char byts[4] ; int wrds[2] ; int (*ptr) ; } Saved_Int_24H_ft_s EXTVAL3 ({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Spec_Int_24H_ft_p EXTVAL3({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Saved_Int_24H_ft_p EXTVAL3({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Spec_Int_23H_p EXTVAL3({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Saved_Int_23H_p EXTVAL3({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Spec_Int_23H_s EXTVAL3({ 0 }); union { long byts; int wrds[2] ; int (*ptr) ; } Saved_Int_23H_s EXTVAL3({ 0 }); unsigned char window_borders[5] ; struct disp_window { unsigned char display[9]; } *thestruct, *newstruct, display_array[500] ; int filecnt EXTVAL3(0) ; unsigned char window_line[80] ; int window_count; unsigned char p_Window_Background; int Top_Of_Window ; int Pos_In_Window; COURSE DIR int rec_count; /* count of actual courses in directory */ int Window_Top; /* offset value in file of one now at top */ int Window_Pos; /* bar offset location in window */ unsigned char edit_mode EXTVAL3(0) ; /* (D)elete (I)nsert (R)eplace (S)ave */ unsigned char multiple EXTVAL3(0); /* flag for whether action is single screen (TDS) or multiple (000) */ . unsigned int edit_mode_old_block_ptr EXTVAL3(0) ; /* location in original file when edit mode entered */ long int edit_mode_new_block_ptr EXTVAL3(0) ; /* location in .100 mod file when edit mode entered */ long int edit_mode_old_block_nbr EXTVAL3(0); unsigned char reset_file_to_start EXTVAL3(0) ; /* for resetting multiple edits to start of file if refused at pgl */ L. Global Variables Header File The Management Interface (40) communicates with the CBT through the use of standard ASCII files, thus making it possible to provide customized management. The files that provide this interface for the current embodiment are the course directory (36) and the critique (38). Currently, the management system is envisioned as one that puts together exercises into course modules and prepares diskettes with those courses linked together by ASCII file that is the course directly. The format of the file is listed in this document. When students use the diskettes, they update the course directory which then is imported back into the management system to update any files on the courses and or students. The critique file is attached to the same diskette as there is one per course. Defaults 70 and control file information could also be handled by the management system. How developed a management one may wish to create depends upon one's needs. It is easy to develop rosters, scheduling, and complete business-wide courses modules for different user-types. N. Generic Recorder Code VI. SECOND PREFERRED EMBODIMENT FIG. 2' describes a second preferred embodiment having essentially the same functionality for CBT 12. The second embodiment is substantially the same as the embodiment shown in FIG. 2. However, the keyboard input is localized within the functions. Also, there is a non- linear exercise flow that is most noticeable in the process of modifying an exercise. Thus, as shown in FIG. 2', modify 46' has been moved into a direct association with author Menu 20'. Print 45' also moved because it too no longer requires a linear flow. Both had been part of playback 22 in FIG. 2. In addition, several of the features of modify 46' are renamed to better describe the user options. The LPW 57, selection error 48, and typing error 50 on Figure 2 are now embodied in cursor information 1303 in Figure 2', the second part of a screen's information 1301. Defaults 70', while unchanged, is moved to visually establish the optional link to the management interface 40'. A suggested implementation would allow users to modify these defaults by changing the values on an input screen and a logical place to do so would be within management interface 40'. Another difference is the addition of one new playback mode called test playback 31. This form of playback allows building a log of answers to be used for item analysis, as is described in detail later. Most changes in the second embodiment can be considered improvements in implementation or improvements for ease of maintenance, rather than a change in functionality. For example, the original system uses QSCR modes to identify the playback mode in which an exercise screen would be available. This required entering the letters QSCR in the upper left hand corner of a screen along with a one-letter code. In the second embodiment, a window allows the author to simply answer "Y" for yes or "N" for no to determine whether the screen will be available in any particular mode. Another improvement is that additional feedback or error messages can now be created for an individual screen, allowing a better interaction with the user. This includes branching the user to another screen based upon their answer. However, the error process itself is unchanged. The first embodiment had 19 modules coded in the "C" programming language, all named CBT xx with xx - 181 - being the sequential number. The alternative approach categorizes the functions and places them into modules named for the category. Thus, the modules in the second embodiment have the names modify.c, playback.c, keyboard.c, help fns.c, etc. Additionally, many variable names have been changed to give a clearer description of their use. Other state variables that were used to return users to the proper place when the centralized key or pointer event occurs are not necessary when the events are more local to the function. A. Non-linear Impacts To allow users to follow a more random path through an exercise, the second embodiment changes the processing of exercises. Rather than accessing a large block of the exercise at a time, only the desired screen is accessed. Thus, the user decides what screen is wanted next and that screen is found and displayed. To simplify and speed up this process, the header information attached to the screen has been expanded, and an index has been attached to the start of the exercise. User selections made from the index expedite locating a screen within the exercise file. The processing of a screen in playback 22' is, however, essentially the same once the proper screen is found. As before, all basic screens are compressed for storage and decompressed when displayed. Data compression and decompression algorithms remain the same in both embodiments. B. Exercise File Layout The crux of the changes can be found in the extended screen header, the new index, and the exercise header located at the beginning of the exercise file. As stated before, these changes are primarily to allow more feedback/error messages to be saved for each screen and to allow a more user friendly way of getting the same information (such as which modes as exercise screen will be displayed in where the value is now in the header and index and not on the screen itself as the previous QSCR example showed) . The exercise file 10' now begins with 16 bytes called an exercise header, which can be used for any specific needs. The second embodiment uses the first byte to store the number of the screen that starts the exercise and the second byte to identity the type of application that was recorded or exercise created. The other 14 bytes are reserved for future potential uses. Following the exercise header is the index. There is a 16-byte record for each basic page in the exercise followed by a null record indicating the end. The 16 bytes are broken down as follows: 0 ID of the screen 1-8 Name of the screen 9-12 Size of the block 13 Next Screen 14 Display Modes 15 Status As exercises need not be linear, the author can quickly move to the intended screen by giving each screen . a unique identification number and, if desired, a name. These are the first bytes in the index. The size of the screen block (including error messages and other screens attached to it) is kept in a four-byte long integer. The next byte identifies the next screen to be retrieved. Display modes is a byte that, based on its value, identifies the modes in which the screen is visible. If the index value shows the screen is not visible in the current playback mode, the screen need not be retrieved and decompressed. Instead, the display merely advances to the next screen as specified in the index record. The status byte is used to indicate the screen originally stored has been changed. A "D" for deleted or "R" will result in skipping that screen when the file is rewritten. If the screen was replaced, the new screen with the same ID is inserted in the modified file when the file is rewritten. The status byte is only changed in modify 44, and, once modified, the exercise will have a null in all _ 5 status bytes used for playback purposes. The bytes in the index are also found in the individual screen headers. The size found in the index covers the basic screen and all of its parts, including any typing records, error or feedback messages, overlay 10 windows, or extra edit screens. Each of these screens has its own header with its individual size included. The total is the block size — the number of bytes to be read to bring in the whole basic page and its parts. The individual screen header has been expanded 15 from 22 to 48 bytes to include five sets of answer- feedback blocks instead of just one. These 48-byte header records are as follows: 0 ID of the screen 1-8 Name of the screen 20 9-12 Size of the screen 13 Screen type 14 Video type 15 Next screen 16 Display modes 25 17 Cursos row 18 Cursor column 18-22 A five-character array of rows 24-27 A five-character array of columns 29-33 A five-character array of correct flags , 30 34-37 A five-character array of feedback ids τ 38-43 A five-character array of quadrants * 44 A branched flag 45 A backup o.k. flag 46 Number of tries allowed 35 47 An exact flag Screen type identifies the type of record being read, which can be the seven allowed for the basic page (Selection, Typing, Multiple Choice, Sequential, Binary, Any Key, or Timed) or a selection error, typing error, extra edit screen, or an overlay window. Video-type specifies a video mode for the screen (40 for 40 column, 80 for 80 column, and "G" for graphics) . Next screen and display modes are as duplicates of the same values in the index. The cursor row and column identify where the cursor would go on the screen if a visible cursor is desired. In the first embodiment, there is one row- column combination to identify the proper selection to leave a screen referred to as the 'action key. ' Recall that function keys use a row 25 designation and the key value is then found in the column value. In the second embodiment, up to five combinations are allowed to permit the author to designate alternative selections. These combinations can be anticipated errors, which can then get a separate and more interactive feedback message. Alternatively, some combinations may be used to accommodate more than one right answer. The array of rows and columns holds those alternative selections, the array of feedback values holds the feedback message that goes with the selection. The correct array indicates whether the selection is correct or not for scoring purposes. The quadrant array indicates where on the screen the message will be displayed, although both embodiments currently use the same default location. Note that most of those screen header values are found in the first embodiment, but in a format that is more difficult to maintain. For example, in the first embodiment, the video type is combined with the row value of the selection and is deciphered; the screen type is found in a numerical record type byte, and the size is two-bytes (which may not be large enough for potential future uses) . A further embellishment over the first embodiment involves an extra use of the multiple feedback - 185 - feature. This capability allows authors to indicate that users should be branched to another screen based upon their answers rather than based upon a feedback window. » By setting the value in the header called "branched" to 5 "yes," the feedback window array will be interpreted as a screen to go to, rather than feedback window to display. The backup block flag allows authors to make it impossible for students to backup past this screen. Number of tries overrides the default value for any 10 exercise on an individual screen. Exact is a code for type-ins that indicates whether type-in answers should exactly match the expert's type-in in order for the answer to be judged correct. These are also not new values as the original did use number of tries and had a flag in the 15 type-in error window to identify the exact match. In summary, the file layouts were changed to make CBT 12 easier to maintain and to make the most efficient use of the non-linear approach to the files. C. New Features 20 There are a few features added to the functionality of the first embodiment. One such feature involves the use of overlay instructional windows. This new screen type can be added to each screen block to allow a window of information to be created and displayed over 25 the basic screen in playback 22. The student can then toggle that window on and off. This new feature allows authors to add instructional information without destroying the basic underlying screen. Another new feature is an item analysis test * 30 exercise. This exercise requires students to answer all questions and keeps a log of the answers. This log can be used to review the answers of students. The log also provides authors with better summary information, e.g. , how many students answered each question correctly, and 35 what were some of the common errors. This information is to help authors tailor their exercises, to improve the . . - 186 - areas where errors occur most, or to tailor the test questions themselves. The code [provided in the Appendix] also includes a suggested control program that has links to 5 other applications. Control program acts as the parent program. Issues such as security which could limit user access can be handled here. Control Program can selectively determine how users connect to other programs and other computers. Control program can also be used to 0 link to the management interface 40 or to connect to the recorders. Such controls provide flexibility for tailoring the system to meet each user's needs. D. Playback Reflecting the changed exercise file and the 5 non-linear flow allowed, the playback process illustrated in FIGS. 22 and 23 is changed as shown in FIG. 22', which uses the same numbers as 22 and 23 where relevant, except that the apostrophe "'" is used as a suffix to distinguish between the embodiments. 0 Figure 22' begins with open file 502', which is now followed by read control file data 1230. The latter step reads the index for the exercise and the exercise header information, as previously described. The outside processing loop now begins with find 5 screen 1232. Find screen 1232 looks in the index for the offset location of the screen desired (at the beginning of the exercise, it is the start screen from the header) . The index record for that screen reveals which display modes are specified. Next screen display = playback mode 0 1234 checks whether the playback mode is allowed. If the playback mode is not an allowed mode, the logic proceeds to set next screen branch 1236. In the index record for the current screen there is the value of the next screen it will call. The desired screen is set to that number 5 and that screen is searched for in find screen 1232. This ιnor. wi11 stσo when a screen that has a mode that matches - 187 - If the screen is allowed for display, the logic goes to read header bytes 530', which reads the actual screen data stored in the header bytes. Next, in read through 1238, there is a test to determine if the read is 5 successful. If not through reading, the logic gets the + size of the record from the header, in block 534'. The logic reads that many bytes and decompress the data in block 536'. The header bytes tell where to put certain information. If the screen is a basic page 1239, the 10 block read moves to the screen 1241 and the logic returns to read header bytes 530'. If not a basic page, the logic checks if the screen is a selection error in block 508• and, if so, the screen is moved to error buffer 510'. Otherwise, the logic checks if the screen is a typing 15 record in block 514*. If it is, the screen is moved to a typing buffer in block 516' . If instead the screen is a typing error, block 1240 would place the screen in typing error buffer 1242. If the screen is an overlay window record, from block 1244, the screen is sent to overlay 20 buffer 1246. If the record is the last record 524', the exercise is complete and the system returns 526'. All of the other options return to read header bytes 530' for the next record until all bytes for that page block have been read and read through 1238 gives a "yes." 25 At this point, if in automatic mode 546', automatic playback 24* is called. If in manual mode 548', manual playback 26' is called. If in instructional mode 550', the logic goes to instructional playback 28', and if in proficiency mode 552', proficiency playback 30' is 30 called. If in test mode 1248, test playback 1250 would be * called. After processing the screen, the logic returns to- r set next screen 1236 to retrieve the next screen. As noted earlier, more than one error message can be saved for each screen. The author can select up to 35 five anticipated answers for which a different message can be displayed. Thus, FIG. 26, items 586 and 596, and FIG. 27, items 614 and 628, Display Appropriate Error Messages/++Errors would select the appropriate error message based upon the user selection. If the selection matched one of the anticipated answers, the feedback window attached to that answer would be the appropriate one to display. E. Print With the addition of screen identification and non-linear flow, the number of screen printouts that can be helpful to the user grows. Thus, FIG. 28, which described the printout of an exercise, has been extended to FIG. 28', which provides four different reports. The user can print a full exercise, which includes all information about the screen, in either screen ID order, or in exercise "flow" order (i.e. , the start screen followed by the screen that it calls, etc., essentially building a linear exercise as in the first embodiment) . In addition, the user can print just the screens or just the summary information. The print process, however, remains the same for all four report options as shown in FIG. 28'. That is, the logic must find out which screen to get next (ID or flow) and which value(s) to print once the screen has been retrieved. In FIG. 28', printing begins by opening the file in block 1252. Then get control information 1254 examines the index and sets the screen to the first screen. Once that is accomplished, a main control loop begins. The question ID order 1256, if answered yes, will get the next ID in block 1258 before proceeding to the end of file test in block 1260. If the question ID order block 1256 is answered no, next screen 1262 will find the next screen in the flow based upon the next screen flag in the current screen header, before performing an end of file test in block 1260. If block 1260 finds that it is the end of the file, at return 1264 the logic returns to the author menu 20. Otherwise, the logic proceeds to read in screen information at block 1266. Once the screen is found, if screen 1270 is called and the user is returned to the print control loop. Otherwise, the print summary information block 1272 is called. Once the summary information is printed, if the report is summary only, as tested in block 1274, the user returns to the print control loop for the next screen. If the report is not summary only, the logic will call print screen 1276 to print the screen. Next, the logic will proceed to test whether other information exists on the screen that should be printed. If an error exists, i.e. , if block 1278 is yes, a print errors 1280 routine is called to print the error message(s) . otherwise, the logic continues to test for overlays. If an overlay exists at block 1282, the overlay window is then printed at block 1284. After the printing, or if no overlay existed, the logic returns to the main control loop for another screen. F. Modify 44' Most of the changes in the way of the CBT 12' works are implemented in modify 44' and are principally concerned with making it easier for authors to build and maintain a non-linear exercise. However, it is more a change in location, size and operation for ease of use and not a change in functionality. The first embodiment has FIG. 29, which describes the modify overview; FIG. 30, which describes the options menu; FIG. 31, which describes the selection error; FIG. 32, which describes the typing mode; FIG. 33, which describes the color/ASCII mode; and FIG. 34, which describes the LPW. In the second embodiment, FIG. 31, 32 and 34 are incorporated into Screen Information FIG. 44 and Cursor Information FIG. 45. In the second embodiment, the author is taken to the basic screen desired, rather than being forced to proceed in a linear fashion. Thus, users are on a page and from that point can go to: the options menu 52'; the color/graphics mode 54'; the screen information mode 1301 (the new screen representation identifying the type of screen and modes in which the screen is played back) ; the cursor information screen 1303, where answers and feedback messages are created or modified; the go to screen 1304, from which the user can enter an ID for a screen to jump to the screen; block mode 1296; overlay window mode 1299; or a next screen page advancement mode 672' . To better understand the functioning of modify 44', imagine the author on the first page of an exercise. The author could jump to modify an overlay window by hitting the F8 key and would, when finished, be returned to the first page. The author might then choose the "go to" function to advance to screen 8. There the author might choose to amend an error message by selecting the cursor information (F5) . The second embodiment provides a freer flow through the exercise than the first embodiment, although it should again be noted that the functionality is essentially the same in building the screen. Figure 29' shows the modify overview. The logic begins with Open File 1286 followed by Get Control Information 1288 and Set 1st Screen 1290 so that the exercise file would then be open, the index data read, and the first screen ready to be displayed. The next step, Get/Display Screen 1292, involves a control loop used when the screen is to be changed or redisplayed. Once the screen is displayed, the CBT 12' waits for input 652'. This wait is another main control point for all modify actions that do not require a change or redisplay of screen. The input returned can be one of 11 conditions. The first group of activities return to the same screen awaiting more input. The first test is for color/ASCII toggle key 696', which calls color/ASCII mode 54' if yes. The second test shows block mode key 1294 which, if yes, calls block mode 1296. Overlay key? step 1298 calls overlay Mode 1299 and Screen Information Key 1300 calls screen information mode 1301. If yes, and cursor 1303 if yes. All of these modes are called if their key was found, as explained in greater detail below. If it was none of those keys, the CBT 12' tests if the escape key was pressed in block 698'. If yes, the user is asked for a verification in block 700'. If verified in block 702', the system quits at block 704'. Otherwise, the logic goes back to wait for input at block 652' . If it was not the escape key either, the next test is for option menu toggle key 656' which, if affirmative, calls option menu mode 52*. That choice will return to Get or redisplay the screen 1292 when completed. Note that the logic does not necessarily set the next screen as this can now also be done in option menu mode 52. Go to Key 1304 and page advance key 672' both check if a Change Flag Set? 1306 If any changes were made to the screen, a new block must be written in write new block 1308. This new block is written to a second file where modified screen blocks are housed. The index to the original file marks the screen's record saying it has been replaced. Whether a change was made or not, these options would then need to get the next screen to be displayed. In "go to" it would call set selected screen 1310 to get the screen number entered, while in page advance 672' would set to whatever the current page calls as its next screen. Mode advance key? 678' handles only the extra edit screens that are created when a recording receives screens from the application that were not triggered by a user action. If an extra screen exists at block 1312, the screen will be displayed and the logic will proceed back to wait for input 652'. Otherwise, the logic acts like page advance, proceeds to set to next screen 1314, and then returns to get/display screen 1292. The remaining test checks typed character 708'. If a typing character is found, an echo character will be Otherwise, or after the character has been echoed, users are returned to wait for input 652' for the next action. G. Option Menu Mode 52' While modifying an exercise, it is often desirable to bring in additional screens, create new screens, or delete screens. The options menu remains the gateway to these changes as well as to the ability to print a part of an exercise. The more localized keyboard functions make FIG. 30' simpler than FIG. 30 from the original application. The list directory functions in FIG. 30 are incorporated into the other functions in the get file name 1316 step. A file name input function provides a list directory when- the Page Up key is pressed. The user may then select the file from an acceptable list. In FIG. 30', when in modify 44', the option menu will be displayed if the user pressed F2. The CBT 12• waits for the user to input a key and reads the key in block 712' . If the key was the option mode toggle key 714', the menu is removed with turn off option mode 716' and the screen is reset in reset current screen 1318 before returning to modify 44' . If the key indicates a desire to insert a blank 40-column screen from which the user will then create a new screen in the exercise, an affirmative answer to is blank 40-char screen key 750' calls insert blank 40- column screen 752'. At this point, it is necessary to reset screen pointers 1320. The screen that had been displayed becomes the next screen for the new screen inserted. The screen that had pointed to the old screen is changed to point to the inserted screen. Now the logic returns to read key 712'. Is blank 80-char screen key 1322 allows the exact same process as blank 40 but for an 80 character blank screen and calls insert blank 80-column Screen 1324. If the answers to the insert blank screen steps - 193 - 800'. If yes, the logic proceeds to set number of pages 802' and then to Print 802 to determine what pages to print. This logic will then print the pages via block 1326. After printing, the system resets the current 5 screen to the original screen and returns block 718' to modify 44'. A negative answer to is key print? 800' leads to the next test. If is key insert 780' is yes, the logic proceeds to get file name 1316. If the name entered is a valid 10 single screen name 1328 the system will insert single screen 1330, which acts like the insertion of blank screens except that the screen now comes with data. The logic then proceeds to reset current screen 1318 and returns to modify 44' . If the screen name is not a single 15 screen but is an exercise, the system proceeds to insert exercise 1332 and, after saving the new screens into the current exercise, resets current screen 1318 and returns block 718'. If not the insert key, is save key? 768 checks 20 whether the key denotes an instruction to save a screen. If yes, get file name 1334 gets the name of the screen to save. If a valid single screen name is found in block 1336, the system will go to save single screen 774'. The logic would then reset current screen 1318 and return 718. 25 If not a single screen name in block 1336, the logic sets multiple mode save flag 776 and saves current pointers 776 before act on screen for multiple mode 1338, in this case saving the screen to a file, and then get/display next screen 1340 before going back to await a valid keystroke 30 at read key 712'. In a multiple save each screen is saved * t as encountered until the Mark command in option menu 52' is found as described below. Delete 754* and Replace 760' act much the same way in setting a mode, acting on the screen, and 35 displaying the next screen while awaiting a Mark command to end the multiple action. Is Delete Screen Key? 754' calls set multiple mode delete flag - Save Current multiple mode 1338 and get/display next screen 1340 and then read key 712' to restart the process. A no in is delete key 754' brings a test for is replace key? 760'. Again, if yes, set multiple mode replace flag - save current pointers 762'. However, when replacing a file, a file name is needed, hence, the next step is get replace file name 1342. The logic then progresses to act on screen for multiple mode 1338, deleting the screen to be replaced, and then to get/display next screen 1340, followed by Read Key 712'. Save, Replace, and Delete are all in multiple mode if those keys were selected and they await the option menu key mark as is key mark end key 744" to stop. If yes at block 744, the system checks which option it is. If multiple mode replace set 1344 is yes, the logic must now insert new file 1346 and set delete flag on deleted screens 1348 in the index file before resetting the current screen and return 718' to modify 44'. If not replace is multiple mode delete set 1350? [as set in Step 756] If yes, set delete flag on deleted screens 1348 and reset current screen 1318 and return 718' to modify 44' . If not delete is Multiple Mode Save Set? 1352. If so, close/write new file 1354 of saved screens and reset current screen 1318 before return 718' to modify 44 « . The only remaining key option that is tested is page advance key 1356. If not the key designating a page advance, the key hit was invalid and users loop to read key 712' for another key. If an advance key a test for multiple mode set 1358 is required because only that mode allows this key to be used. If not in multiple mode, page advance would act as in modify 44'. If multiple mode, it must take an action on the screen before leaving (in delete or replace it must flag the screen as being deleted and in save it must save a copy of the screen to the new file). If multiple and page advanced are true, the system will act on screen for multiple mode 1338 (save, replace, - 195 - or delete) and proceed to get/display the next screen 1340. This continues until the mark key stops this chain of events. H. Screen Utility The screen utility 56 creates, edits, and saves single screens. These can be incorporated into exercises as instructional screens, and are used by the system for development some of its control files. FIG. 35 outlines the new Screen Utility 56 processing which is the single screen processing/editing function. To begin, the user must get file name 1347 which leads to the test valid single screen file name? 1349. If not a valid name the program will loop back to allow another try. When a valid name has been entered display screen 1351 will put it on the monitor and the user input loop then begins at get user input 970*. If the escape key? 972* the user is returned. If not, the test is made on whether it is the color/ASCII toggle key 976*. If so, it calls the Process Color/ASCII 54* and on return proceeds back to get user input 970'. If not color/ASCII then is it block mode key? 1353. If so. Block Mode 1355 is called and upon return the user loops back to 970*. If not Block Mode Key 1353 then is it option mode toggle key 984*. If so, only the save display option is allowed so display with save option only 1357 will show the option menu with save highlighted and the only allowed choice. The save file? 1026 if no loops back to 970', but a yes tests for a valid file name 1028'. A valid file name calls save screen 1030' before remove option menu 1359 and returns to 970'. An invalid file name just calls remove option menu xxx and returns to 970'. If the key pressed was not the option menu toggle key, the next test was for a valid keystrok 1002' . If not the loop returns to 970* for another key. If yes, display key at cursor location 1004' will put the key on the monitor and return for more input at 970' . . - 196 - I. Screen Information 1301 Screen Information 1301 centrally houses the information about the screen itself, including its name, the location of the next screen in the exercise, the modes 5 it in which the screen may be displayed, and the type of screen that it is. Much of the data found in the screen header and index files for an exercise, the layout of which was described earlier, is entered here. At any point in the loop, if escape key pressed 10 1362 is yes, the logic will go via return 1364 to modify 44'. Otherwise, the first step is to create/modify screen name 1366. This eight-character name must be unique, so name already exists? 1368 will return for another name if yes. Otherwise a no proceeds to 15 create/modify next screen information 1370. This step also verifies the existence of the screen selected. In this case, however, an affirmative answer to screen exists? 1372 means it is okay to proceed to the next question, whereas a negative means another screen name or 20 ID must be entered because a screen cannot be called if it does not e_ist. (It should be noted that a screen called the last screen always exists and can be used as the next screen holder if the user has not yet decided what screen should come next — the user could then modify this at a 25 later time. - Two types of possible screens are timed screens, screens displayed for a period of time before automatically proceeding to the next screen, and any screens which will advance a user when any key has been 30 pressed. Input timed screen 1374 tests whether the screen type is timed or not. If it is timed 1376 the system will get time 1378 and skip to enter playback modes 1380 since the other steps in FIG. 44' affect only screens that have 35 answers required. If the screen is not Timed, the next step after block 1374 is input any key screen 1382. An any key screen 1384 will advance when the user hits any key. If any key screen 1384 is yes the logic skips to - 197 - enter playback modes 1380. If not, the logic proceeds to input branched 1386 to determine if the screen is a branched screen or not. If branched? 1388 is yes, the flag is set in set branch flags 1390, before going to input number of tries allowed 1392. A no to branched goes directly to input number of tries allowed 1392. Which playback mode a screen will appear in is handled by entering a Y if yes to each mode. All users must enter into the enter playback modes allowed 1380 fields although answering no to all modes means the screen will appear only in modify 44' and not in playback 22' (this is useful for authors to keep notes) . Next step is to enter up help screen 1394 which is a yes or no prompt that affects screen playback. This screen will come up when the user asks for exercise-specific help. If any key or timed screen 1396 are true, the logic loops back to escape key pressed 1362 to finish the processing. Otherwise, the logic goes to get screen type 1398. Selection, typing, multiple choice, sequential or binary are the available types. Because a screen must have one and only one type, zero or more than one type with yes? 1400, if affirmative, will send users back to get screen type 1398, whereas a no will Set Type 1402, as one valid type was selected, and return. J. Cursor Information - 1303 Cursor information 1303 is a second part of the information about a particular screen. The Cursor information 1303 is automatically brought up after completion of screen information 1301, when in modify 44'. Cursor information 1303 is separated from screen information 1301 so that users can also go to it directly without going through screen information 1301. This information pertains to the answers users make based upon the five screen types found here: selection, typing, multiple choice, sequential, and binary. Fundamentally, they are all the same. They all set a cursor row and column if one is desired. They are set for up to five possible answers with corresponding flags for whether the answer is correct or not (it is possible to have more than one correct answer) . A feedback window goes with each answer. The real differences are in the types of allowable answers (i.e.. a selection of an item or entry of some form of typing) . In ' all cases there is a loop of entry of answers until the fields are all filled or an enter key is pressed, at which point a default feedback window is processed. The default feedback window has a message that comes if the user selected something not in the list of anticipated answers for the screen. Cursor information 1303 begins by checking whether the screen is selection type screen? 1406 If so, it does get/set cursor row and column 1408. A five choice loop test begins with five choices or enter key? 1410. If no, the logic begins with get answer row and column 1412, which gets the anticipated answer. Next, get/set correct flag 1414 is to determine whether the answer is correct or not. Yes or No. Thereafter, get/set feedback window 1416 puts information into a feedback window for this choice. More than one answer can have the same feedback window because each of the up to five possible feedback windows is identified by a number. The choice is incremented at increment choice count 1418 then loops to five choices or enter key 1410. If that is yes, the get/set default message 1420 is called before a return 1422 to modify 44' . Thus, each time information on an answer is entered, the count is incremented until five because five is all that are allowed. If not selection screen type 1406, the question is typing screen type 1424, a screen which expects a typed message as its answer. If so, it does the same as selection 1406, except that the answers are strings of data in get answer typing 1426 as opposed to get answer row and column 1412. If not typing is it multiple choice type? 1428, awaiting one of a list of possible choices. If yes, it is just like selection type screen 1406, except - 199 - that the answer is stored with a row value of 26 in get answer row and column 1430. Since 25 in the row says it is a function key, to store a letter as an answer, row 26 is used with the code for the letter stored in the column. Multiply choice answers tend to be A, B, C, D, etc. If not multiple, is sequential type? 1432. This accommodates a matching question and thus requires a row and column for up to five possible values. Hence an extra item " at the- start " of the loop-, getrcuτs ~ or row column for answer 1434, tells where each of the answer fields are. The answers use get answer typing 1436 because the answers are like a typing field. Possible answers of matching AEBCD is like a type in of AEBCD when comparisons are made. If not sequential, the remaining choice is binary type 1438 which is distinguished from selection by its binary or two-choice option. Thus after getting the cursor row and column 1408d the choice loop is two choices or enter key? 1440 as the test. K. 3270 Recorder The 3270 recorder 8c is a third embodiment of the recording methodology. It combines the keyboard interrupt facilities found in the Generic Recorder 8b with the emulator-driven screen arrival processing found in recording emulator 8a to create recordings. This is useful where users can use an API (Application Programming Interface) to an emulator but cannot actually have access to the emulator code itself to create recording emulator 8a. It is not possible to link CBT 12 and a recorder 8c into a single program, nor is it advantageous to link CBT 12 with any recorder as one program. The memory constraints of the computer 2 would require such a program to lose basic functionality since all functions will not fit in memory without a complicated overlay scheme. In addition, when code must serve two different masters or purposes, compromises in design and function must be made. The additional maintenance problems and the limits for growth make a split between recorders and playback/authoring systems a preferred embodiment. Such a recorder as 8c is documented in the following discussion and the related figures. It uses a 3270-emulator from NSA, a commercially available product called Adapt SNA 3270, to allow recordings of anything the emulator has access to such as CICS applications on an IBM mainframe computer. The files created are in a format readable for playback and modification within the CBT system. This embodiment of a recorder has three program modules: one C module and two 8086 assembly modules. The C module [{NSAMAGIC.C}] contains code which handles initialization and setup procedures and also handles most of the processing required for arriving screens. The assembly module [{NSAINT.ASM}] contains a replacement keyboard interrupt handler. This handler gathers keystroke and cursor information into a buffer which will code. The third module [{10.ASM}] contains functions used in the creation and manipulation of file input and output (I/O) . To use the API 1902, the user program is sent as a parameter call to the emulator 1900 program which then knows to set up the API interface. It sets up software interrupt 100 as the communication channel and messages will go back and forth through that interrupt code. The user program sets up how it intends to use the API 1902 by sending some startup values. As the interrupt is executed, parameters are sent to the API 1902 and return values may be examined by the C code as well. After the initialization and setup procedures have been accomplished, control is given to the emulator 1900. The emulator 1900 will remain in control except upon receipt of a new screen, when control will be returned to the C code. Once the C code has finished processing the contents of the typing buffer and the new screen, it will return control to the emulator 1900. Initialization consists of connecting to the 3270 API and reading and parsing the format table file (FTF) 14c. An interrupt is executed giving ownership of the display to the emulator 1900. The next interrupt executed establishes the initial connection to the emulator 1900. Two additional interrupts are executed in order to ensure the proper setup of the emulator 1900. At this time the FTF file 14c is opened and parsing begins. If the FTF file 14c cannot be found, execution of the recorder will be halted. The parsing routine will try to match the key or token the user has defined in the recorder's internal key table. If found, the FTF file's definitions replace the recorder's definitions and become part of the file the recorder uses to place values into the exercise file. In this way, the FTF file 14c can define the name and stored values for any key. The internal recorder file uses a set of values for standard 3270 keys that was arbitrarily designed in. Since the PC computer is not a real 3270-keyboard, and since different emulators assign or map 3270-specific keys to different PC keys, the FTF file is a logical way for the program to know what value it should store when a key is pressed. For example, one emulator may assign PA1 to CTRL-J on a PC and another may use Fl. The FTF file 14c would tell the recorder which key on the PC equates to PA1 so that a PA1 notation would be stored in the exercise file. (Note that CBT 12 playback needs an FTF file so that the student will hit the right key for PA1 on his computer) . After the initialization process, a replacement keyboard interrupt handler is installed. The original handler address is saved for eventual restoration. The interrupt handler examines each key and determines what action to take. If a record key is pressed, the appropriate flags (i.e. , single flag for a single screen, multiple flag if more than one screen) are set and a prompt is displayed to ask the user to enter the file name. Keystrokes defining the file name are placed in a separate buffer. The interrupt handler copies the screen data into a global buffer and a C routine is called to process either the multiple screen record or the single screen record. A multiple screen C routine merely initializes some variables and returns to the handler. The single screen C routine initializes variables, compresses the screen data, stores the data in the large ring buffer storage area, and writes the contents of the ring buffer to disk. If the defined file name is a duplicate, the data is written to a temporary file and, upon exiting the program, the user is notified of the file via the monitor. As each new screen arrives the logic is repeated. After a recording has begun, the handler will place each keystroke in a typing buffer along with the cursor location. When a screen arrives and the emulator gives control to the user program, typing buffer will be parsed by the C code. When control is returned to the C code upon receipt of a new screen, a file name check is made to determine that the file has not been opened previously. If the file name is a duplicate, the user will be asked via the monitor for permission to overwrite the existing file. If permission is not given, the user is instructed to restart the recording and assign a new file name. After checking for duplicate files, the screen is compressed and the screen header elements are defined. A flag is set to indicate that a compressed screen is ready to be stored. The typing buffer will be parsed to see if there are keystrokes to process. A local typing buffer will be created containing displayable characters. The backspace and left arrow keys are processed only if a local typing buffer for non-recording uses has been opened. This is primarily when monitoring user information such as the file name for the recording. In all other instances, arrow keys are ignored in the recording procedure. The location of each character is checked as the local typing buffer is created. If a character is pressed that does not display on the screen, or displays at a location more than one space from the previous character, this will constitute an automatic exit condition. The activity performed when this condition is encountered is described in the next paragraph. While 3270 applications tend to be page driven, the recorder and CBT 12 are set-up to allow a more detailed field by field playback and evaluation. Thus, any time the 3270 screen advances to the next field or a function key that does not display on the screen is pressed, the recorder will trigger a new screen record. In this way it can save all of the desired information and create the tools for authors to tailor the simulation to their needs. They can then give feedback on each entry if they prefer this to the 'page' nature of 3270, or modify +-v.o pr-or iπσ to allow oa e processing. As the 3270 emulator 1900 lets the user code know a screen has arrived, the typing, buffer will hold all keys hit since the last 'official* screen change. If, in evaluating the typing buffer the recorder 1904 discovers a change in location (a new field) or a function key, it will record the original screen with the changes to that point and save the screen and the proper header information in the ring buffer. If typed characters preceded the record trigger it would write those out in the typing record, with its header, to the ring buffer. A default error would also be stored. Thus, it is possible to have several recorded screens where only one 'real' screen change has occurred. Again, this provides a way to maintain an option to do field by field evaluation in CBT 12 and places the recording into line with other recorded applications for CBT 12. To save space and match the CBT 12 file layout, the screen data is then compressed and the screen header is updated to reflect the number of bytes it requires in a compressed state. If the ring buffer is full or the finish flag has been set after all keystrokes'have been processed, the contents of the ring buffer will be written to the disk. If the ring buffer was full and the finish flag was not set, a temporary file is opened to store the contents of ring buffer to handle this overrun condition. The ring buffer is written to the file and then cleared for the new data to come. Because the format of the disk file requires an index to be stored before any of the individual screen data, a temporary file is used. The index is created at the end and the ring buffer and temporary fϊle are then merged. When the finish flag is set, a wait message is displayed on monitor. If multiple screens were recorded, an exercise header is defined and written. The exercise screen ID array is updated and written. The last record index element and a NULL index element are added and the exercise index is written. A null branch record is written and the last screen header is setup and stored in ring buffer. If a temporary file was used, the data from that file is written to a defined file. The contents of ring buffer are then written to the file. All files are 5 then closed. A "wait" message is removed from the monitor % and a "record complete" message is displayed for a few * seconds. If a single screen was recorded, just the contents of the ring buffer (i.e. , the screen header and compressed screen data xxx) are written to the file. All 10 applicable flags are then reset to initialize the recorder again. When the user finally exits the recorder 1904 control loop the original keyboard handler is restored. The user is notified, via a message on the monitor, of any 15 duplicate single screen files. The user must decide to overwrite or define new file names. The new file names are also checked for existence and the recorder program loops until a unique file name is defined. It should be noted that a more advanced API 1902 20 might allow users to get keyboard values through API calls and bypass having to takeover the actual keyboard interrupt, but the basic operations to achieve a recording would remain the same. FIG. 46 outlines the basic steps in the 3270 25 recorder 8c. The process begins with initialize recorder 2000 which handles the emulator connection and the FTF file 14c. This leads to Install KB Interrupt 2002 which replaces the computer keyboard interrupt with replacement code to allow the recorder to get the first shot at 30 keyboard entries. The third step in FIG. 46, monitor keyboard interrupt 2004, is actually something that happens from this point on outside of the normal flow. * This process is called whenever a key is pressed regardless of what else may be happening at that moment. 35 The interrupt watches for hot keys to start and stop a recording, to enter filenames, and to keep track of keys pressed between screens when a recording is being made. Process screens 2006 is the next linear step in the process. This routine is notified by the emulator whenever a screen arrives and, if recording, processes the screen and keys buffered by the keyboard interrupt. If not recording, the routine just returns control back to the emulator. When the user has completed the desired processing and indicates a desire to exit, the step reset interrupts 2008 is called to put the computer back into a normal operating state. This is followed by process duplicate single file names 2010 which cleans up any conflicts with names entered for single screens — a necessity because of the complications of doing disk input/output operations while in the middle of interrupts. FIG. 47 details the initialize recorder 2000 step. This begins with setup/connect to emulator 2014. The emulator API 1902 requires that the name of the recorder program be passed as a parameter to establish the link to an interrupt that will be used to communicate between the recorder and the emulator. Once this is completed, the recorder checks for an FTF file 2016. If the answer to FTF file 14c exists 2018 is no, the recorder will quit 2020 as an FTF file 14c is required. If the FTF file 14c exists, the FTF file 14c must be interpreted and the recorder set to use its information. Thus step parse key table 2022 handles this translation and is detailed in Figure 53. Once the parse is complete the recorder returns step 2024 to install the keyboard interrupt. FIG. 48 indicates the original keyboard interrupt is saved in save original handler 2026 as it will be called during keyboard processing and restored at the recorder's completion. Then install replacement interrupt 2028 puts the recorder's keyboard logic routine into the address the computer calls whenever a key is pressed. Thereafter, there is return 2030. FIG. 49 discusses the monitor keyboard interrupt 2004. If a key pressed? 2032 is no it simply returns 2034, but if yes, the computer calls the keyboard interrupt 2036 which is detailed in FIG. 55. The last step in FIG. FIG. 50 process screen 2006 handles the steps taken once the emulator 8c is active and uses an API interrupt to notify the recorder 2038 a screen has arrived. This stops the emulator 1900 processing while > 5 awaiting action by the recorder. If a screen arrived * from Emulator 1900 the recorder handles the screen in * screen arrived 2040 which is detailed in FIG. 57. When a screen arrives a test for the finish flag set? 2042 indicates whether write to file 2044 must be called to 10 write out and close the recording exercise file being built. The write to file 2044 is detailed in FIG. 61. Otherwise, there is a return 2046. FIG. 51 further explains FIG. 48 and sets the keyboard interrupts. Reset interrupts 2008 will reinstall 15 original interrupt 2048 so that when the recorder leaves the computer is left in a proper state of operation at return 2050. FIG. 52, another overview figure, handles the process duplicate single filename 2010 by first checking 20 if there are any files to process 2052, which would only be the case if the single screen record names entered were duplicates of file names already on the disk. If there are files to process, one of two conditions arises. The file existed? 2054, if yes, calls display existing file 25 message 2056 to tell users of the conflict. If no, the users are told of an error condition in display file creation error message 2058. Both yes and no branch to define new file name? 2060 which allows users to choose to rename the file in question. A yes will go to get new 30 file name 2062 and if that name is in conflict again, the file exists? 2064 step will catch it. A yes at file * exists 2064 will return users to the top of the processing loop at any files to process 2052 to redo the steps already described. A no to file exists 2064 will rename 35 the file that was temporarily stored under a system- defined name in rename temporary file 2066. If the define new file name 2060 step was no, the user is allowed a choice to overwrite file? 2068 which will overwrite the file whose name was a duplicate with the new file. Yes to overwrite file calls rename temporary file 2066 and a no restarts looping again at any files to process? 2052. When there are no more files to process the function returns to complete the exit from the recorder. FIG. 53 outlines the steps involved in parsing the FTF file 2070 required for the recorder 2022 to properly configure the user's keyboard. The first step is to see if there is a Token to Process? 2072 and if none the program simply has a return at step 2074. If there is a token, the first test is whether the Token=BEGIN_KEY_TABLE 2076. This is the beginning of the keystroke part of an FTF file 14c which would follow any configuration information. If that token is found, the routine read key table 2078 is called (as detailed in Figure 54) . If no, the next test looks to see if Token=KEY WORD 2080 and if yes calls setup scan code and shift state for key word 2082, which identifies the new values for the key word that the recorder 1904 will use. If the token is not a key word, a message display unknown token message 2084 alerts the user to FTF file 14c deficiencies and the user is returned to Token to Process? 2072 until no more tokens exist. FIG. 54 is the key table loop 2078 that fills the key table the recorder will be using in its operations and begins with get token 2088. Next comes the get key value from key table 2090 that attempts to find the key in the table or recorder 1904. If not key value found? 2092 a message display unknown key message 2094 is given to the user, but if found, setup scan code/shift state for key 2096. The test equivalent available name in 3270 2098 checks if the key defined has an equivalent in the 3270 keystroke definitions. If not, an error condition exists at display error message 2100 which then returns at step 2102. The information on the user's desired recorder row and column definition for the 3270 key is then tested to. see if the row and column values are available. Row number available 2104 is the first test and if successful there is an assign row number 2106. Column number available? 2108 j, if affirmative, will lead to assign column number 2110. Both tests row number available 2104 and column number available 2108 call display error message 2100 and return at step if negative. The increment key count 2112 adds the key to the list and a test is made to see if key count < maximum? 2114. If so, the recorder returns for another token at get Token 2088. Otherwise display error message 2100 is called and the program returns. The recorder builds key tables to get the row-column value that tells what key was pressed and gets the name the user wishes displayed when that key is pressed. (An application may use keys named PA1 of PF1, or it may use other names for the standard keys and the FTF file xxx allows entering the desired name.) FIG. 55 outlines the keyboard interrupt 2118 which is Assembly language code that replaces the computer's own keyboard interrupt routine and is called in. its place whenever a key is pressed. The interrupt begins by get input value from keyboard port 2120 — the actual interrupt call. The recorder allows the normal keyboard interrupt to be called to do its processing in call old keyboard handler 2122 but only after saving the location in the keyboard ring buffer of the operating system where a key would be placed. In this way, the recorder 1904 can, if it decides "eat" the key under certain conditions (such as when a file name for the recording is entered) . Eating the key keeps the emulator 1900, and hence the target application, from seeing it. On return from the old interrupt the test is was key a release key? 2124 The keyboard sends a scan code when you depress the key and another when you release it, and releases can be ignored so a yes will just return step 2126 out of the interrupt. A no will test was key written by old handler? 2128 as some keys will be eaten by the old keyboard routine as well. A no leads to return 2126 to return the program from the interrupt. A yes requires the recorder to get shift state 2130 which gets the state of shifted toggle keys like the Alt and Control keys. A test is then made to see if the finish flag on? 2132 condition is on and, if so, the return at step 2126 is made as the user has indicated an end to the recording. If not, the question is creating file name? 2134. If a file name is created for special processing, keystrokes are handled and put on the screen without letting the emulator 1900 or target program 4 know. The routine process file name 2136 handles this. If not creating a file name is the recording flag on? 2138. If it is already on the test for stop key pressed? 2140 is made to see if we are to stop recording. If not process key 2141 (FIG. 56) is called to handle the keystroke. If stop 2140 is yes the finish flag set 2142 process is started, the flag that places an "r" in the lower corner of the screen to let users know they are recording is reset to remove recording flag from screen 2144, the operating system keybaord buffer is reset to eat the stop key in reset keyboard tail pointer, and the program returns (later the finish flag will be read and the code will close out the recording) . If the answer to recording flag on? 2138 was no a test is made to see if the key is single screen key pressed? 2146. If yes, the set single flag 2148 notes it and set recording flag 2150 prepares for recording with a start of display file name prompt 2152. If not single, is multiple screen key pressed? 2154. If yes, the set recording flag 2150 operation is performed and display file name prompt 2152 before a return at step 2126. The single flag being set is what differentiates a single screen recording from a multiple. In any case the next keystroke will reenter this interrupt with different flags set so that the file name will now be processed. FIG. 56 is the process key 2141 routine that is called whenever recording and a valid key has been pressed to process. The first step is to save the key 2160. The test will then be made on new screen flag set? 2162. This is because when a new screen arrives the recorder will not get a chance to see that screen until after it has been put up in order to record it. Thus once the screen arrives condition has been met the old keyboard buffer is cleared out and this flag set so that on the first key entered after the screen is put up the recorder will record the untouched screen before processing that keystroke and allowing the emulator to put it on the screen. If the new screen flag set? 2162 was yes, the call is to clear new screen flag 2164 from where copy screen into Data Buffer Last_CRT 2166 will put the screen into memory and the restore saved key 2168 routine puts the key back into play (the save is a protection in case the input/output processing of saving the file creates a condition where the key is lost) . If there is no new screen the key is retrieved in get key from keyboard buffer 2170 and then place key into local buffer 2172 puts it into the keyboard buffer being saved by the recorder — all keystrokes between screens arriving that will later be procssed by the recorder. It will then update pointer to next location 2174 moving the buffer pointer to the next available spot. Is this spot end of buffer? 2176. If yes, the buffer is resituated in reset buffer pointer top start 2177 before joining the no track at get cursor information - put into local buffer 2178. The local typing buffer keeps the key and the row and column of the cursor when the key was pressed. These are used for autoexit tests described in FIGS. 58-60. The program will now update buffer pointer to next location 2180 and again test end of buffer? 2176A with a reset buffer pointer to start 2182 if yes. The program has now processed the key and returns at step 2184. FIG. 57 describes what happens when a screen arrived 2140 condition occurs. The first step is cleanup which places the "r" in the corner of the screen to let users know they are recording — place record flag in corner 2186. The first real test is input file open? 2188. If not it is at the start of a recording and must call check file exist 2190 to open the file. If file opened successfully 2192 is answered no the program returns at step 2194, but if yes, there is a check if the compress flag is on? 2196. If so, record screen 2198 is called to place the screen into the ring buffer. If not, the loop begins to process the keyboard buffer that has been accumulated since the last screen arrived. Keystrokes to process? 2200 does the test. If yes, the first test is is the current key a function key? 2202 If yes, process function key 2204 is called (detailed in FIG. 59) . If not is this a displayable key? 2206, i.e.. one that would or should show up on the screen. If so process displayable key 2208 is called (detailed in FIG. 58 to follow) . If not a displayable key is the typing flag on? 2210. If no, the system returns to keystrokes to process 2200 for the next key, and if yes but backspace or left arrow is no it also returns to keystrokes to process 2200. However, backspace and left arrow are keys 2212 that act to erase the last key hit and as such are corrections which the local typing buffer uses to simply backup in its processing to erase the key and so it will modify local typing buffer 2214 and update cursor position/ get row-col data 2215 to reset the typing buffer before returning to keystrokes to process? 2200. When all keystrokes have been processed the test for ring buffer full? 2216 checks whether the need arises to write out the buffer to avoid overrun problems. If yes, it calls write new file 2218 (see FIG. 61), and if no calls finish flag set? 2220 which also calls write to file 2218 if yes. If no, the routine falls to set new screen flag 2222 as they keys are all processed and a new screen is about to be put up that will be recorded when the first keystroke is pressed. After that the routine will pass control back to the emulator in return to emulator 2224. FIG. 58 handles the processing of keys that can normally be displayed on the monitor and handles evaluating whether the location of the key indicates an autoexit record is required. These tests presume that a normal type-in indicates one character will follow the next on the screen and if the cursor position is not that, or if the character pressed did not show up on the screen at all, an autoexit condition is triggered which will close the first type-in and record the screen. The beginning tests if row-col data is available? 2226 because this will be used to get the expected screen location with calculate screen offset 2228. The next test is to see if typing flag on? 2230 and if not the call is to record key 2232 (this will open a typing record and set the flag on when the flag is not on) . If the flag is on, and typing has already commenced, is the cursor at last position + 1 2234 where it would be expected. If so and if cursor at last location 2236 and if did char display at offset? 2238 then update offset 2240 and record key 2232 before update cursor position 2242 and return 2244. If the cursor at last location was yes but did char display at offset is no, an autoexit is required and the branch is to autoexit screen record 2246. If not at the last position + 1, did the character display at calculated offset? 2248 If not, did character display at calc'd offset 2248 will update offset 2250 and call record key 2232 and update cursor location 2242 before a return 2244. Autoexit would now be required if did char display after previous one? 2252 is yes, another keystroke to process? xxx is no, keystroke displayable? 2256 is no, row-col available 2258 is no, and did char display before this one? 2260 is no. Otherwise the chain falls into update cursor location and offset 2262 to get things righted again before the call to autoexit screen record 2246. Once the screen is recorded, the key is now processed in record key 2232 to open a new typing buffer, and the cursor is updated in update cursor location and there is a return 2244. FIG. 59 process function key 2204 handles the key processing when the key found was a function key and not an alphanumeric or displayable key. The start checks if row-col data is available 2266 and if so it calculates actual row, column 2268 when the key is pressed. A function key can come in the middle of a typing buffer being processed if it has not triggered a new screen from the emulator xxx so a test is made more keystrokes to process? 2270 If yes, there are two checks: the typing flag on 2272 and (if not) an arrow key? 2274. If typing flag 2272 was on and the key was not backspace or left arrow? 2274, there is a test for arrow key? 2276 If not the arrow key the next call is to record this screen as an autoexit screen record 2246 since there was no triggering of a new screen. Next there is return 2278 to the screen arrived xxx routine. Arrow keys are ignored and backspace and left arrow used to reposition a typing buffer, erasing a mistake, so these function keys do not trigger the automatic record if backspace or left arrow was yes, update the local typing buffer 2294 to reflect the 'erasure' and return. If there were no more keystrokes to process? 2270 this is the last key in the buffer which triggers the new screen. If the screen store flag on? 2280 there is a return 2282. If on, the test typing flag on? 2284 will indicate whether a typing record must also be written along with the screen and error messages. If yes to typing the call is to store screen 2286 and then process typing record 2288 which incorporates the additional error screen writes. If typing was not on, the store screen and error data 2290 routine is called. In all cases the final routine is clear screen store flag 2292 before return 2282. FIG. 60 autoexit screen record 2246 handles the processing when a condition has been encountered where an additional screen must be recorded between actual screens from notification by the emulator. If the typing flag on? 2296 is yes it means typing was done before this call and the screen to be recorded at this point must reflect those characters on the screen. Thus the original screen recorded when it first arrived is amended with put typing buffer data in screen buffer 2298. If no typing, store screen flag on? 2300 tests and, if no, the routine falls down to record screen 2302. Then there is a reset last cursor position 2304 before return 2306. A yes on store screen flag on? 2300 will update key value and row col in header 2308 followed by a store screen in ring buffer 2310, both of which update the information on the screen to record and place it for recording. If typing flag on? 2312 yes, process typing record 2314 sets up the typingrecords and stores them in the ring buffer. Otherwise there is setup header and error screen 2316, increment index to include error 2318, and store error message in ring buffer 2320, all of which essentially place the information on the screen in the buffer for eventual writing. The clear store screen Flag 2322 is then reset and the record screen 2302 does the compression of the screen and sets the store flag for the next go around. FIG. 61 write to file 2218 is concerned with writing on two conditions. When the recording is to be finished it will write the final version of the file. Otherwise it is called when the ring buffer is full. To avoid an overrun, the buffer is written to file xxx to allow more screens to then be recorded in the now initialized ring buffer. Write to file 2218 tests if finish flag set? 2326 and if so, process final write 2327 (FIG. 63) is called. Otherwise process temporary file (FIG. 62) is called before return xxx. FIG. 62 is a flowchart for process temporary write 2328. If a temporary file open? 2332 A call is made directly to write ring buffer to temp file 2334 before a return 2336. If not open, a file must be created and creation successful? 2338 tests for this condition. A successful opening calls set temp file open switch 2340 (used in the evaluation at temporary file open 2332) before the write ring buffer to temp file 2334 and retun 2336. If the creation successful? 2338 step fails, a display error message 2342 process occurs and the exercise is at risk. FIG. 63 gives a picture of how the final file looks as well as handling the process final write 2344 step. The initial steps fall in order starting with remove record flag from screen 2346 because recording is over. Then comes setup/write exercise header 2348, the 16 bytes that begin an exercise file by identifying the first screen to display and the type of exercise recorded. Setup/write ID Array 2350 writes a 256-byte array which has values set to 1 if the value of the offset in the array has been used for a screen ID in the exercise (in other words, if a screen with ID 8 exists, this array at offset 8 will have a value of 1) . Next comes the index with setup/write index array 2352 (the 16-bytes blocks that speed up processing — see the exercise file layout for the second embodiment two) . Finally a record is written that CBT 12 uses for keeping branched screen information 2353. The first test is is temporary file open? 2354. If one has been opened, the file must be processed first as it holds the beginning screens and the ring buffer the latest ones. Close file/clear temp file open switch 2356 is followed by open temporary file 2358. Thereafter a test for end of file? 2360 is called in a loop with a no calling read/write up to IK of data from temp file 2362. This process writes out the temp file until end of file? 2360 is yes. Then close temporary file 2364 is called. At this point, a no to temporary file open? 2354 would also lead to setup header/write last entry to ring buffer 2368 which is now called to complete the exercise. Then the ring buffer is added to any previous written screens, if any, in write ring buffer to exercise file 2370. The screen header for the last screen recorded must now be changed to reflect that the next screen in playback is the last screen or end of exercise record. This is handled in seek last screen header and flag for last record 2372. The file is then closed in close file 2374, and the user is told that the recording is done in display complete message and clear flags 2376. The routine then returns at step 2378. Generid Recotder > ■1989 TDS Healtheafd Systems Corporation All R gh s Reserved > /* initialization C cod# tot HA tC V #include <stdio.h> finclude <stddef.h> Iinclude "magic.h" I c - O*) ϊ ? c & 0x20 : c i 0) (c==9)) ? 1 f ( 0) fdefine isdigit(c) (((c>^θt)tt(o<» , 9»)J?l:θ) int oρen_file(char far *) } int creat_file(char fat *H int close_file(int) ,' int get_filec(int, char far *) int write_file(int ,chat fat *, char far *) int seek_eof(int) ; int message(char fat * t iht) t int cmp_screen() i int move_screen(char fat *) I int last_crt, last_char__under, disρlay_page, video_colS, video_len char inGetTextf flifdef DEBUG struct tes_control_ρrm_- _- t&p fvf struct res_conttol_J.tm._l } Ujr wfvp = ifv. felse _ int fv> * struct res_control_pt__.___S at *f ρ = MULL,* flendif /* key work table */ /t*his table is searched with a binary search whenever a keywork token is found in the FTF. The table defines the token constant and the token as the index into the table that has the pointer that points to a null terminated string that has the reference token. The numbers are placed literally because a fdefine fundef #define #undef sequence that would take generate the numbers automatically... maybe latter. New entries must be in ALPHA order! */ /***** NOTE: this is the first piece of transient data it is overwritten */ char *key_work_table[] = { " ", /* no token 0 */ "APPLICATION_NAME" , fdefine APPLICATION_NAME 1 "AUTOEXIT_CODE", fdefine AUTOEXIT__CODE 2 » FUNCTION_HOLDOFF", #define FUNCTION_HOLDOFF 3 "IGNORE_ATTRIBUTES» , #define IGNORE_ATTRIBUTES 4 "IGNORE_CASE", #define IGNORE_CASE . 5 "IGNOREJKEYPAD", #define IGNORE_KEYPAD 6 "IGNORE_REGION40", #define IGNORE_REGION40 7 "IGNORE_REGION80 » . #define IGNORE_REGION80 8 "NO_KBD_ROM" , #define NO_KBD_ROM 9 "PAGE", fdefine PAGE 10 "RECQRD_HOLDOFF", fdefine RECORD_H0LDOFF 11 "RECORD_PAINT", fdefine RECORD_PAINT 12 "RECORD_SCREEN" , fdefine RECORD__SCREEN 13 "RECORD_SCROLL", fdefine RECORD_SCROLL 14 "RECORD_SOUND", fdefine RECORD_SOUND 15 » SAMPLE_RATE", fdefine SAMPLE_RATE 16 "SCROLL_LINE", fdefine SCROLL_LINE 17 "SETTLE_TIME_MAX" , fdefine SETTLE_TIME_MAX 18 "SETTLE_TIME_MIN", fdefine SETTLE_TIME_MIN 19 "START_RECORD", fdefine START_RECORD 20 "STOP_RECORD" , fdefine STOP_RECORD 21 "TYPE_IN_POSITION», fdefine TYPE_IN_POSITION 22 "ZDUMMY" fdefine DUMMY . 23 }; fdefine MAX_KEY_WORD 23 /* shift control flags from bios */ fdefine INS_SHIFT 0x80 fdefine CAPS_SHIFT 0x40 fdefine NUM_SHIFT 0x20 fdefine SCROLL_SHIFT 0x10 fdefine ALT_SHIFT 0x08 fdefine CTL_SHIFT 0x04 fdefine LEFT_SHIFT 0x02 fdefine RIGHT_SHIFT 0x01 fdefine LR_SHIFT 0x03 /* */ * /* variables used just within this module */ * static int err_count; /* compile error count */ static int file; /* DOS file handle */ static int res_key__index; /* index into resident key table */ /* */ /* key table */ /* This is the master table for interpreting key strokes from the target machine"s BIOS. Due to the vast differences between machine's keyboards the BIOS key- board interrupt routine is called by the CBT recorder key board interrupt routine and the resulting keystrokes in the BIOS maintained ring buffer are examined. The scan codes are the same across all series of IBM machines both PS2's and PC's. The startup sequences, however, must be decoded for each variety of machine. Machine recognition code is provided with a variable that contains the machine type and therefor the keyboard type for direct key number decoding for the turn on and turn off key sequences. */ struct key_table_s { char *name, /* asscii name in the FTF file */ scan_code, /* BIOS returned scan code for the key */ char_code, /* BIOS returned ASCII code for the key */ shift_flags; /* map of the shift keys for the key */ ) key_table[] - /* table is in ascending order for binary search */ { { "BKSPACE", OxOE, 0, 0 }, { "DEL", 0x53, 0, 0 ) , { "DOWN", 0x50, 0, 0 }, { "END", 0X4F, 0, 0 ) , { "ENTER",0xlC, 0, 0 }, { "ESCAPE",Oxl, 0, 0 } { "Fl", 0X3B, 0, 0 ), { "Fl-A", 0x68, 0, 0 },{ "Fl-C", 0X5E, 0, 0 },{ "Fl-S", 0x54, 0, 0 }, { "F10", 0x44, 0, 0 }, { "F10-A", 0x71, 0, 0},{ "F10- C", 0X67, 0, 0 },{ "F10-S", 0X5D, 0, 0 }, { "Fll", 0X86, 0, 0 }, { "F12", 0x85, 0, 0 ), { "F2", 0X3C, 0, 0 }, { "F2-A", 0x69, 0, 0 },{ "F2-C", 0X5F, 0, 0 },{ "F2-S", 0x55, 0, 0 }, { "F3", 0X3D, 0, 0 }, { "F3-A", 0x6A, 0, 0 },{ "F3-C", 0X60, 0, 0 },{ "F3-S", 0X56, 0, 0 }, { "F4", 0X3E, 0, 0 ), { "F4-A", 0x6B, 0, 0 },{ "F4-C", 0X61, 0, 0 },{ "F4-S", 0X57, 0, 0 }, { »F5», 0X3F, 0, 0 }, { "F5-A", 0X6C, 0, x62, 0, 0 },{ "F5-S", 0x58, 0, 0 }, { "F6", 0X40, 0, 0 }, { "F6-A", 0X6D, 0, 0x63, 0, 0 ),{ "F6-S", 0X59, 0, 0 }, { "F7", 0x41, 0, 0 }, { "F7-A", 0X6E, 0, x64, 0, 0 },{ "F7-S", 0X5A, 0, 0 }, { "F8", 0X42, 0, 0 }, { "F8-A", 0X6F, 0, 0x65, 0, 0 },{ "F8-S", 0X5B, 0, 0 }, { "F9", 0x43, 0, 0 ), { "F9-A", 0x70, 0, 0x66, 0, 0 ),{ "F9-S", 0X5C, 0, 0), { "INS", 0x52, 0, 0 }, { "HOME", 0X47, 0, 0 { "KEY!", 0X02, '!', LR_SHIFT ), { »KEY\"",0X2B, •/», 0 }, { "KEYf", 0x04, ' ' , LR_SHIFT } , { "KEY$", 0x05, '$', LR_SHIFT }, { "KEY%", 0x06, '%', LR_SHIFT }, { "KEY&", 0X08, •&', LR_SHIFT }, { "KEY*", 0x28, '\' ', 0 }, { "KEY(", OXOA, '(', LR_SHIFT ), { "KEY)", OxOB, ') ', LR_SHIFT }, { "KEY*", 0X09, '*', LR_SHIFT }, { "KEY+", OxOD, '+', LR__SHIFT }, "KEY-", OxOC, "KEY/", 0X2B, "KEYO-A", 0X81, "KEY1-A", 0X78, "KEY2-A", 0X79, "KEY3-A", 0x7A, "KEY4-A", 0X7B, "KEY5-A", 0X7C, "KEY6-A", 0X7D, "KEY8-A", 0X7F, "KEY9-A", 0X80, '9', 0 ), { "KEY:", 0X27, •:', LR_SHIFT }, { "KEY;", 0x27, •;', 0 ) , { "KEY<", OxOD, '<', LR_SHIFT }, { "KEY=", OxOA, '=', 0 }, { "KEY>", 0X34, '>', LR_SHIFT }, { "KEY?", 0x35, «?', LR_SHIFT }, "KEY§" { "KEYA", OxlE, 'A', 0 }, "KEYB" "KEYC", 0X2E, •C 0 }, "KEYD" "KEYE", 0x12, •E 0 }, "KEYF" "KEYG", 0x22, •G 0 ), "KEYH" "KEYI", 0x17, • I 0 }, "KEYJ" "KEYK", 0X25, 'K 0 }, "KEYL" "KEYM", 0x32, » M 0 }. "KEYN" "KEYO", 0x18, •O 0 }, "KEYP" "KEYQ", OxlO, 'Q 0 ), "KEYR" "KEYS", OxlF, •s 0 }, "KEYT" "KEYU", 0x16, •U 0 }, "KEYV" "KEYW", Oxll, •w 0 >, "KEYX" "KEYY", 0X15, Ύ 0 }, "KEYZ" "KEY[», 0x2C, ■ [ 0 }, "KEY\\ { "KEY]", 0X2C, •];, 0 ) , "KEY " "KEY_", 0x2C, I 0 ) , "KEY * « "KEY{", 0x2C, '{ 0 >, "KEY " "KEY)", 0X2C, 0 }, "KEY"" "LEFT", 0x4B, o. > , "PGDN" 0x51, 0, 0 }, "PGUP" 0x49, 0, 0 }, "RIGHT", 0X4D, o, }/ "SPACE « , 0x39, 0, 0 }, "TAB", OxOF, 0, o } "UP", 0x48, 0, 0 } fdefine LAST_KTABLE_ENTRY 150 }; int *next_char_in, *next_char_out• int typed_chars[100] ; int Ticks; /* prototypes */ struct key__table_s *get__key_entry(char *token) ; unsigned char get_shift_states(char *ptr) ; struct key_table_s *search_key_table(char *token) ; read_file(char *) ; struct res_control_prms_s far *get_fv() ; /* main program */ main(int ac, char *av[]) { if (fvp == NULL)print ("TDS Magic Screen Recorder Configuration\n") ; else printf ("TDS_ MagicScreen Recorder Configuration Checker\n") ; if (fvp == NULL) && Jmagic_present() ) { fprint (stderr, " This program requires the magic TSR/n") ; exit(l) ; } if (fvp == NULL) fvp = get_fv(); fvp->function_holdoff = 1; fvp->record_holdoff = 1? fvp->sample_rate = 2; fvp->scroll_line = 0; fvp->settle_time_min = 2; fvp->settle_time_max = 5 fvp->display_page =- 0; fvp->ignore_case = 0; fvp->ignore_keypad - 0; fvp->record__paint = 0; fvp->record_sound = 0; fvp->start_key ■= 0; fvp->start_shifts = 0; fvp->stop_key = 0; fvp->stop_shifts = 0; fvp->single_rec_key = 0; fvp->single_rec_shifts = 0; fvp->typein_pos_flag = 0; fvp->kb_rom_flag = 0; fvp->typein_auto_keya = 0; fvp->typein_auto_keyb = 0; fvp->application_name[0] = 0; fvp->res_key_table[0] .scan_code = 0; fvp->ig80.flag = 0; fvp->ig40.flag = 0; read_file(av[l] ; } /* C library routines - e must define them here to get proper alignment */ char *strchr(char *s, char c) { while(*s) if (*s = c)return(s); else s++; return (NULL) ; } stricmp(char *sl, char *s2) { while(*sl && *s2) if (toupper(*sl) { = toupper(*s2))break; else sl++, s2++; return(*sl - *s2) ; } strcmp(char *sl, char *s2) { while (*sl && *2s) if (*sl != *s2)break; else sl++, s2+t; return(*sl - *s2) ; ) strncmp(char *sl, char *s2, int n) { while(*sl && *s2 && n~) if (*sl != *s2)break; else sl++, s2++; return(*sl - *s2) ? } strcpy(char *sl, char *s2) I while (*s2)*sl++ = *s2++; *sl = 0 ) strcat(char *sl, char *s2) { while(*sl)sl++; while (*s2) *sl++ = *s2+f ; *sl = 0 ; } strien(char *s) { register int i = 0; while(*s++)i++; return(i) ; } atoi(char *s) { register int i = 0; while(*s) i = (i * 10) + (*s++ - «0«); } GetKBDRomFlag () { return(0) ; } /* /********************************************** /* This routine returns true if the string is just numbers in it */ /********************************************** is_number(char *s) { while(*s) if (!isdigit(*s)) eturn(0) ; else s++; return(1) ; } /************************************************ /* get_key_entry - this routine searches the key table above and returns a pointer to the entry having the same name as the given token. The search is performed in binary fashion, starting from the middle of the table and searching the half of the alpha ordered table in the direction indicated by the cmpstr return. A word on not finding the entry. To simplify the binary search algorythm we just place a limit on the number of iterations that the search can be performed. At first glance this may seem wasteful of clocks in the case of failure. Well, it is, but the rest of the system will not run unless the FTF is read in without any errors so the error path is only taken in the development of the FTF file. Once the FTF exists then the search will always succeed quickly. */ /************************************************* sruct key_table_s *search_key_table(char *token) { int incr •= sizeof(key_table) / sizeof(struct key_table_s) / 2; int limit = sizeof(key_table) / sizeof(struct key_table_s) ; struct key_table_s *entry = &key_table[incr] ; register int tmp? char *ptr; do { tmp = strcmp(token, entry->name) ; if (tmp = 0)return(entry) ; if (incr !=l)incr /= 2; if (tmp > 0)entry += incr; else entry -= incr; if ((entry < &key_table[0]) (entry > &key_table{LAST_KTABLE_ENTRy]) ) return(NULL) ; } while(limit—) ; return(NULL) ; } struct key_table_s *get_key_entry(char *token) { struct key_table_s *entry? char *hyphen; if ((entry = search_key__table(token)) != NULL)retur (entry) ; hyphen = strchr(token, '-'); if (hyphen && *(hyphen+l) == •-')hyphen++; /* handle the KEY—x case */ if (hyphen && *(hyphen →l) 1 = 0) hyphen = 0; else retur (NULL) ; entry = search_key_table(token) ; ♦hyphen ■= '-' ; retur (entry) ; } T*his procedure scans the FTF given token looking for the shift state modifers -L -R -S -A or __C. It then returns a shift state character indicating which states are set. */ /************************************************* unsigned char get_shift_states(char *prt) { register unsigned char retval = 0; while ((ptr = strchr(ptr, '-')) !== NULL) { ptr++; switch(*ptr) { case *S' retval = LR_SHIFT; break; case 'R' retval ■= RIGHT_SHIFT; break; case 'L' retval - LEFT_SHIFT; break; cas___- 'A-* retval =-ALT_SKIF ; break; case •C retval - CTL SHIFT; break; } } return (retval) ; } /* */ /******************************* ****************** / V*************************************************/ /* get_token - read a token from the FTF file This routine parses out all.spaces in the FTF file returning each token in the file in a provided buffer. A token is defined as a sequence of ASCII characters surrounded by white space. White space includes space CR, LF and TAB. The token parser recognizes double quotes and returns the entire text found in the quotes as a single token. The parser also filters out comments which are a white space followed by a semi¬ colon. When this sequence is found everything till the end of the line is ignored and the token parser is restarted. */ /*************************************************/ int get_token(char *token) { char inp = ' ', /* input character */ inquote = 0, /* flag indicates processing a quoted string */ *ptr; /* work pointer for filling in token V int ret = 1; /* return value > 1 if a string was parsed */ top: /* for restarting the parse */ ptr = token while (isspace(inp) ) /*skip spaces */ if (get_filec(file, &inp) — 0) return (0); /* at eof */ if (inp =- •;') { /* comment field? */ while (inp != «\n') if (get_filec(file, &inp) ■— 0) return (0) ; goto top; /* past comment look again */ } do { if (isspace(inp) && linφiote) break; /* end of token */ if (inp = '\"')inquote = "inquote, ret++; else *ptr++ = inp; if (ptr - token > MAX_TOKEN_SIZE-l) /* check for limit */ { message("maximum token length exceeded\r\n", 31); goto top; } } while(get_filec(file, &inp)) ; *ptr = 0; /* null terminate */ if (ptr = token) return (0) ; /* eof */ } /*************************************************/ /* bool_token - decide if a token indicates true or false This routine looks at the token and provides an error message if the token is not a bollean indicator. */ /*************************************************/ char bool_tok(char *ptr) { if (stricmp(ptr, "YES") — 0) return (1) ; if (stricmp(ptr, "TRUE") — 0) return (1) ; if (stricmp(ptr, "NO") — 0) return (0) ; if (stricmp(ptr, "FALSE") == 0) return (0); message ("Expected a bollean indicator\r\n", 30); return (0) ; ) /*************************************************/ /* key_word - look up a token in the key word table This routine binaryly searches the key word table for the given token. */ /♦A***********************************************/ int key_word(char *token) ( register int i - MAX_KEY_WORD / 2; register int j = i; int tmp; int limit = MAX_KEY_WORD; while (limit—) { if ((tmp = stricmp (token, key__word_table[i])) =-= 0) return(i) ; if ((i -= 1) :: (i > MAX_KEY_WORD))return (0) ; if (j != l)j /=2; if (tmp < 0) i -=j; else i +=j; } return (0) ; } /*************************************************/ /* set_key_word - perform the actions for a key word in a file This routine performs the work for each of the key words in the FTF. */ /*************************************************/ set_key_word (int key) { register int i; char token [MAX_TOKEN_SIZE]; struct key_table_s *key_ref; int a, b, c, d; switch (key) { case APPLICATION_NAME; get_token(token) ; for (i =0; i < 80; i++) if (token [i]) fvp->application_name[i] = token[i]; else break; break; case AUTOEXIT_CODE; get_token (token) ; fvp->type in_auto_keya « atoi (token) ; get_token (token) ; fvp->type in_auto_keyb =■= atoi (token) ; break; case FUNCTION_HOLDOFF; get__token (token) ; fvp->function_holdoff = atoi (token) ; break; case RECORD_HOLDOFF; get_token (token) ; fvp->record_holdoff = atoi (token) ; break; case SAMPLE_RATE; get_token (token) ; fvp->sample_rate = atoi (token) ; break; case SCROLL_LINE; get__token (token) ; fvp->scroll_line = atoi (token) ; break; case PAGE: get_token (token) ; fvp->display__page = atoi (token) ; break; case SETTLE_TIME_MIN: get__token (token) ; fvp->settle_time__min = atoi (token) ; if (fvp->settle_time_max <= fvp- > settl e_time_min ) fprintf (stderr, "SETTLE_TIME_MAX less then SETTLE_TIME_MIN\n") ; break; case SETTLE_TIME_MAX; get_token (token) ; fvp->settle_time_max = atoi (token) ; if (fvp->settle_time_max <- fvp- >settle_time_min) fprintf(stderr, "SETTLE_TIME_MAX less then SETTLE_TIME_MIN\n") ; break; case NO_KBD_ROM; get_token (token) ; fvp->kb_rom_flag = bool_tok (token) ; break; case TYPE_IN_POSITION; get_token (token) ; fvp->typein_pos_flag = bool_tok (token) ; break; case IGNORE_CASE; get_token (token).; fvp->ignore_case =5 bool_tok (token) ; break; case IGNORE_KEYPAD? get_token (token) ; fvp->ignore_keypad - bool_tok (token) ; break; case IGNORE_ATTRIBUTES: get_token (token) ; fvp->ignore_attr = bool_tok (token) ; break; case IGNORE_REGION 0; get_token (token) ; a = atoi (token) ; get_token (token) ; b = atoi (token) ; get_token (token) ; c = atoi (token) ; get_token (token) ; d = atoi (token) ; fvp->ig40.flag = 1; fvp->ig40.min = (a * 40 + b) * 2; get_token (token) ; a = atoi (token) ; get__token (token) ; b = atoi (token) ; fvp->ig40.qualpos = (a * 40 + b) 2; get_token (token) ; for (i = 0; i < 40; if (token[i]) fvp->ig40.string[i] = token [i]; else break; fvp->ig40.qlen - i; if (token[0]) fvp->ig40.flag = 2; break; case IGNORE_REGION80; get_token (token) ; a = atoi (token) ; get__token (token) ; b = atoi (token) ; get__token (token) ; c = atoi (token) ; get_token (token) ; d = atoi (token) ; fvp->ig80.flag = lj fvp->ig80.min = (a * 80 b) * 2; fvp->ig80.max = (c * 80 d) * 2; fvp->ig80.xmin = b * 2; fvp->ig80.xmax = d * 2; if (fvp->ig80.max <= fvp->ig80.min fvp- >ig80.xmax <= fvp->ig80.xmin) fprintf(stderr, "IGNORE 80 with bad coordinates\n") ; get_token (token) ; a = atoi (token) ; get__token (token) ; b = atoi (token) ; fvp->ig80.qualpos = (a * 80 + b) * 2; get_token (token) ; for (i = 0; i < 80; i++) if (token[i]) fvp->ig80.string[i] = token [i]; else break; fvp->ig80.qlen = i; if (token[0]) fvp->ig80.flag = 2; break ; case RECORD_PAINTϊ get_token (token) ; fvp->record__paint = bool_tok (token) ; break; case RECORD__SOUND; get_token (token) ; fvp->record__sound = bool_tok (token) ; break; case START_RECORD; get_token (token) ; if ((key_ref = get_key_entry (token)) == NULL) { message ("Unknown Start Key specifier", 30); message (token, strlen (token)) ; message ("\r\n", 2); break; ) if (*token == 'F' && (*(token + 2) == '-' :: *(token + 3) == "-")) while (strchr(key_ref->name, '-') !■= NULL)key_ref—; fvp->start_key = key_ref->scan_code; fvp->start_shifts = key_ref->shift_flags; fvp->start_shifts := get_shift_states (token) ; break; case STOP_RECORD; get_token (token) ; if ((key_ref = get_key_entry (token)) == NULL) { message ("Unknown Start Key specifier", 30); message (token, strlen (token)); message ("\r\n", 2); break; ) if (*token == 'F' && (*(token + 2) == '-' :: *(token + 3) == '-')) while (strchr (key_ref->name, •-•) !=NULL)key_ref—; fvp->stop_key = key_ref->scan_code; fvp->stop_shifts = key_ref->shift_flags; fvp->stop_shifts := get_shift_states (token) ; break; case RECORD_SCREEN; get_token (token) ; if ((key_ref = get_key_entry (token) ) == NULL) { message ("Unknown Start Key specifier", 30); message (token, strlen (token)) ; message ("\r\n", 2); break; } if (*token == *F' && (*(token + 2) == •-• :: *(token + 3) = •- ■ )) while (strchr (key_ref->name, •-•) !=NULL)key_re —; fvp->single_rec_key = key_ref->scan_code; fvp->single_rec_shifts = key_ref->shift_flags; fvp->single_rec_shifts := get_shift_states (token) ; break; } } /*************************************************/ /* This procedure scans the FTF token by token compiling the resident key table. The parse is data driven with each new token dictating what the ensuing token(s) should be. Premature ending of the FTF is accounted for along with unrecognized key words. */ /*************************************************/ read_key_table() { int num_keys = 0; int tmp; char token[MAX_TOKEN_SIZE]; char *ptr; struct key_table_s *ref; struct res_key_table_s far *entry -= &fvp- >res_key_table[0] ; whi1e (get__ oken(token)) { ref = get_key_entry (token) ; if (ref ~ NULL) if ((strncmp(token, "HEX", 3) == 0) *__£. isxdigit (token[3]) && isxdigit (token[4])f memset(entry, 0, size of (struct res_key_table_s ) ) ; entry->scan_code = HEXtoi (token[3]) * 16; entry->scan_code += HEXtoi (token[4]); ) else { message ("Unknown Key identifier", 23); - 235 - message (token, strlen (token)); message ("\r\n", 2); continue; } else { entry->scan_code = ref->scan_code; entry->shift_ lags = ref->shift_flags; } /* scan for shift state modifiers */ entry->shift_flags := get_shift_states (token) ; /* get the expected ascii code */ entry->ascii_code =• ref->char_code; /* modify to shift state */ if (entry->shift_flags & ALT_SHIFT) entry->ascii_code = 0; else if (entry->shift_flags & CTL_SHIFT) entry->ascii_code -= 0x40; /* expect to get the type declaration for the key */ if (! (tmp = get_token (token)) ) { message ("Unexpected end of file\n\r", 24); break; } if (tmp > 1 && ! (tmp = get_(token))) /* skip- by optional name */ { message ("Unexpected end of file\n\r", 24); break; } if (stricmp (token, "FUNCTION") == 0) entry- >ctl_type = FUNCTION_KEY; else if (stricmp (token, "IGNORE") ■== 0) entry->ctl_type =IGNORE_KEY; else if (stricmp (token, "TYPED") == 0) entry->ctl_type =TYPED_KEY; else { message ("Unknown Key type specifier", 25); message (token, strlen (token)) ; message ("\r\n", 2); break; } if (entry->ctl_type == FUNCTION_KEY) { if (!get_token (token)) - 236 - message ("Unexpected end of file\n\r" , 24) ; break; } if (lis_number (token)) { message ("Function Key requires a row number\n\r", 36); continue; } entry->row = atoi (token) ; if (!get_token (token)) { message ("Unexpected end of file\n\r", 24); break; } if (Jis_number (token)) { message ("Function Key requires a column number\r\n", 39); continue; } entry->col = atoi (token) ; } entry++; if (++num_keys ■— MAX_TSR_KEYTAB) { entry—; entry->scan_code = 0; message("Fatal error number of keys exceeds maximum\n\r", 45); ab_exit(l) ; } inemset (entry, 0, size of (struct res_key_tabie_s ) ) ; ) } /*************************************************/ /* read_file - read the FTF file and set the key table flags and values This routine is called once upon entry into the cbt recorder the text is located in the discarded text segment and the data is in the discarded region also. This routine and the rest of the c routines in this file are used only to read the FTF in and set the tables for the keys, values for the parameters and the state of the control flags. Some assembly routines are required for screen and file 10. These routines are in the assembly initialization file and are also discarded after use. The C stdlib io package is not used because it would be near impossible to seperate out the components when it comes time to discard the code and data. It also reduces the size of the module because most of the C code error checking and buffering is not required. */ /************************************************* read-file(char *name) { int kw; char token[180]; strcpy(token, name); if (strchr(name, '.') == NULL) /* setup extension if necessary */ strcat(token, ".FTF"); file = open__file(token) ; if (Jfile) { message ("Cannot find FTF file\r\n", 22); ab-exit(l) ; ) while (get_token(token)) { if (stricmp(token, » BEGIN_KEY_TABLE") == 0) read_key_table() ; else if ((kw = key_wor (token)) != 0) set_key_word(kw) ; else { err_count++; message ("Unknown Token in file - ", 25); message (token, strle (token)) ; message ("\n\r", 2); ) ) close_file (file) ; } /* transient_init - called from assembly code entrance */ extern int InstallTSR () ; transient_init (char far *farargline) { char *tmp; char arg[80] ; char *argline = arg; int i; for (i - 0; i < 80; i++)arg[i] = *farargline++; while(isspace(*argline)) if (*argline — '\r') { message ("Usage: magic file <.ftf>\n\r", 25); ab_exit(l) ; } else argline++; tmp = strchr(argline, f \r'); if (tmp)*tmp = 0; tmp = strchr(argline, ' .'); if (tmp = NULL)strcat(argline, ".FTF"); read_file(argline) ; message ("FTF file read\r\n" / 15); return((DEFAULT_RING_SIZE + (2*(BUF_SIZE))) /16 +1) ; } /* MAGIC.C - C routines for the CBT recorder */ /* * P R O P R I E T A R Y N O T I C E * * THIS DOCUMENT DESCRIBES A PROPRIETARY SOFTWARE PRODUCT OF TDS * HEALTHCARE SYSTEMS CORPORATION (TDS) , CONTAINS TRADE SECRET * AND PROPRIETARY INFORMATION, IS AND SHALL REMAIN AN UNPUB- * LISHED WORK PROTECTED BY COMMON LAW COPYRIGHT, IS LOANED FOR * LIMITED PURPOSES ONLY, REMAINS THE PROPERTY OF TDS, AND IS TO * BE RETURNED UPON REQUEST OR COMPLETION OF THE PURPOSE OF THE * LOAN, WHICHEVER IS SOONER. ACCEPTANCE OR RECEIPT OF THIS * DOCUMENT BY THE AGENT AND/OR PRINCIPLE TO WHOM IT IS LOANED IS * AN ACKNOWLEDGEMENT THEREBY THAT IT IS BEING RECEIVED IN CONFI- * DENCE AND THAT IN CONSIDERATION FOR THE LOAN, NEITHER IT NOR * ANY OF THE INFORMATION IT CONTAINS WILL BE REPRODUCED, USED, * OR DISCLOSED, IN WHOLE OR IN PART, BY OR TO ANYONE NOT HAVING * A NEED-TO-KNOW CONSISTENT WITH THE PURPOSE OF THE LOAN OR ANY- * ONE NOT EMPLOYED BY SAID AGENT AND/OR PRINCIPAL WITHOUT THE * WRITTEN CONSENT OF TDS. THE PROPRIETARY SOFTWARE PRODUCT * DESCRIBED BY THIS DOCUMENT SHALL NOT BE USED, REPRODUCED, OR * DISCLOSED, IN WHOLE OR IN PART, WITHOUT THE WRITTEN CONSENT * OF TDS. * Author Gerard J. Cerchio - a contractor * Written July 1989 * Version 1.00 include <stddef.h.> include "magic.h" fdefine TRUE 1 fdefine FALSE 0 fdefine RING_WRITE_TRIGGER 3 int display_page = 0; void show_text (char far *, int) ; void get_text (char far *, char far *, int) ; void msg(char far *) ; int move_screen (char far *) ; int cmp_screen (char far *) ; int open_file (char far *) ; int creat_file (char far *) ; int write_file (int, char far *, int); int P_Compress_Gen (char *, char *, char *, int) ; int memcpy (char far *, char far *, int) ; int ringstore (unsigned char *, unsigned int); int Compress (char *, char *).; char *do_compress (char *, char *, int) ; /* data used throughout the record system */ extern struct res_control__prms_s fv; extern int *next_char_in, *next_char_out; extern int typed_chars[100]; struct res_key_table_s *last_function_keys = NULL; /* last key press */ /* counter bumped by clock /* number of colums on /* size of reg buffer */ unsigned int last_function_key=0; /* number of last function key */ unsigned int major_buf_num=0; /* write bloc number */ unsigned int minor__buf_num=--0; /* record number */ char file_name[18]; /* storage for file name */ char single_prompt[] = "Single File Name: "; char multi-prompt[] = "Recording File Name: "; /* ring buffer management pointers */ /* the screen and type in data for the recorder is placed in a ring buffer which is allocated at the end of the resident sections of code and data overwriting the initialization sections and usually extending out into the MS DOS memory. When the program TSR's this buffer space is preserved as part of the resident task. The beginning and end of the ring pointers are therefore set as part of initialization. The begin_ring and end_ring define these points. The write_ring defines the point which data goes into the ring. The begin_ring_write and end_ring_write define the area of the ring last written to the file */ int _acrtused; char ring[DEFAULT_RING_SIZE]; /* screen buffer ring */ char *end_ring = &ring[DEFAULT_RING_SIZE - 1]; /* last byte in ring */ char *being_ring = ring; /* pointer to start of ring */ char *being__ring_write = ring; /* current write place on ring */ char *end_ring_write = ring; /* end of last write from ring */ char *write_ring_ptr = ring; /* start of write into ring */ int num_ring_writes = 0; /* number of wring buffer writes */ /* buffers */ - 241 - char last_crt[0x1004]; /* storage for reference screen */ /* special type in data */ /* A type in buffer is built through multiple entries into the keyboard section of the code. This requires that some state information be held in about the status of the current type in buffer. The flag tells if a typein is in progress. The sizel and size2 pointer point to the size of the typein buffer. These bytes are updated every time a key is recorded.. into, the- buffer.. */ char recording_typein = 0; /* flag for typein in process */ char *typein_sizel; /* pointer to size 1st byte */ char *typein_size2; /* pointer to second size byte */ /* work area for compression */ char tmp_buf[BUF_SIZE]; int accumulated_ticks = 0; /* counter for sample rate */ int waiting_for_record = 0; /* counter for record trigger */ int waiting_for_settle = 0; /* flag for settle trigger */ char did_lastw_flag = 0; /* flag for indicating last write to buffer */ char write_ring_flag = 0; /* flag for indicating write of buffer */ char HotFlag •= 0; /* hot flag */ char in_write_ring = 0; /* file being written flag */ char chk_exist_flag = 0; /* first time file is written */ char FinFlag = 0; /* Finished record flag */ char *last_curpos = NULL; /* last position of curpos data in typein */ char last_scr_char[10] ; /* last single char change on screen */ int last_scr__pos[10] ; /* position of last_scr_char */ int last_scr_in = 0; /* index for charater typin ring */ int last_scr_out = 0; /* index for charater typin ring */ int last__typein_pos = -1; /* last typein char position */ int last_char_under = 0; /* last single char under a typed char */ char over_run •= 0; /*************************************************/ /* GetKBDRomFlag - Dumm routine needed to return the state of the fv.kb_rom flag because the io.asm cannot declare an extrn structure which is what the fv area is so that the init routine can place the data into the TSR routine in a reasonably organized structure. */ /*************************************************/ GetKBDRomFlag() { return (FinFlag ? 0 : fv.kb_rom_flag) ; } /*************************************************/ /* Clock interrupt - this routine is called when the recorder is active and the clock interrupt happened. The routine calls the screen compare if it is time to and record the screen if necessary. This routine returns 0 if all work is done. If the ring buffer needs to be written to disk then this routine returns 1. The assembly code then performs the checks necessary to clear for disk 10. If the checks work out then the write_ring routine is invoked. Otherwise the buffer will not be written till a subsequent clock tick. */ /*************************************************/ int Clock_tick() { register int file; /*msg("Clock_tick") ;*/ if (!got_file_name :: over_run ;; in_write_ring :: FinFlag :: chk_exist_flag ;: single_flag ) { if (over_run) { FinFlag -= 1; recording_typein - got_file_name = 0; write_ring_flag = 1; return; ) if (in_write_ring) return (0) ; if (FinFlag && !did_lastw_flag) { record_screen(1) ; did_lastw_flag = 1; ) if (FinFlag) return(1) ; if (chk_exist_flag == 2) * { * get_text ("File exists, Append, Overwrite, or Escape? <A,0,Esc>», &chk_exist_flag, i) ; if (chk_exist_flag «= OxlB) { write_ring_flag = FinFlag = got_file_name = chk_exist_ lag = HotFlag = 0; return; ) ) if (single_flag) retur (single_screen()) ; = if (waiting_for_record) /* waiting for record hold off time? */ { if ((waiting_for_settle == 2) && !screen_different()) { waiting_for_record = 1; r waiting_for_settle ■= 0; } if (—wiating_for_record) return(0) ; /* keep waiting */ if (waiting_for_settle ■— 1) { waiting_for_record = fv.settle_time_max - f v . settle_time_min ; waiting_for_settle = 2 ; return (0) ; ) /* finished waiting do the screen record */ ) else /* inbetween sample times */ { if (++accumlated_ticks < fv.sample_rate) /* don't do anything */ return (write_ring_flag) ; ) if (!screen_different() ) /* screen not different... done */ { accumlated_ticks = 0; return (write_ring_ lag) ; if (fv.record_holdoff) /* screen different... wait?? */ { waiting_for_record = fv.record_holdoff; return (0) ; } if (fv.settle_time_min) { waiting_for_record = fy.settle_time_min; waiting_for_settle = l ' ;.- return (0) ; } /* detected a change do record there is no hold off time */ ) /* time to record the screen */ waiting_for_settle = accumlated_ticks = 0; /* reset counter & flag */ record_screen(l) ; if (num_ring_writes > RING_WRITE_TRIGGER) write_ring_flag = TRUE; return (write_ring_flag) ; } snap_header() { register int i; for (i = 0; i < 4; i++)ringstorec (0) ; ringstorec (0x36) ; ringstorec (0) ; ringstorec (0) ; ringstorec (0x83) ; ringstorec (0) ; - 245 - ringstorec (0) ; if (video_cols == 80) { ringstorec (0x99) ; ringstorec (0x64) , } else { ringstorec (0) ; ringstorec (0) ; } for (i = 0; i 10; i++)ringstorec (0) ; ) single_screen() { register int unsigned i; if (write_ring_flag) return (write_ring_flag) ; get_text(single_prompt, file_name, 0); if (*file_name == 0) return (HotFlag = single_flag = 0) ; move_screen(last_crt) ; snap_header() ; i = Compress (last_crt, tmp_buf) ; ringstore (tmp_buf, i+1) ; if (video_cols == 80) { ringstorec (0) ; ringstorec (0) ; ringstorec (0x8F) ; ringstorec (0xF8) ; ringstorec (0x87) ; snap-header () ; i = Compress (&last_crt[2000] , tmp__bu ) ; ringstore(tmp_buf, i+1); for(i = l; i < 6; i++) ringstorec(i) ; ) end_ring_write = write_ring_ptr; return (write_ring_flag = TRUE) : /*************************************************/ /* screen_different - see if the screen is significantly different This routine is called after every sample time is elapsed. The routine calls an assembly routine which compares the screen to see if any difference is on it since the last sample was taken. If there is only one difference it is taken as a single typed character. If the type-in position flag is set the character & its position is recorded in the small ring buffers. If there is a difference and ignore region areas are active this routine calls first the ignore region qualifying routine to see if it is necessary to further look at the screen. If the screen qualifies for ignore region then scrdifig is called with the proper paramters and the screen is compared without considering the IGNORE_40 or _80 section of the screen as is appropriate to current screen,display ir.o_de_____ / V*************************************************/ screen_different () { register int tmp; char buf[80] ; /*msg("screen_different") ;*/ tmp = cmp_screen (last_crt) ; /* striaght off compare */ /* if tmp == -1 then there is more then one difference if it is 0 then none if tmp is some number that is the position of the single difference */ if (tmp ! » 0) /* any difference found? */ { if (video_cols ■== 80 && fv.ig80.flag && qual80()) /* IGNORE_80? */ tmp ■= scrdifig(tmp, 160, fv.ig80.max, fv.ig80.min, fv.ig80.xmax, fv.ig80.xmin); else if (video_cols = 40 && fv.ig40.flag && qual40()) /* IGNORE_40? */ tmp = scrdifig(tmp, 80, fv.ig40.max, fv.ig40.min, fv.ig40.xmax, fv.ig40.xmin) ; move_screen(las _crt) ; /* new reference */ if (fv.typein_pos_flag && (tmp > 0)) /* process typein pos? */ { last_scr_char[last_scr__in] = last_crt[tmp] ; last_scr_pos[last_scr_in++] = tmp; if (last_scr_in — 10) last_scr_in = 0; } ) return (tmp = -1 ? 1 ; 0) ; ) /*************************************************/ /* scrdifig - do a screen compare ignore some regions */ /*************************************************/ scrdifig (register int tmp, int colsw, int max, int min, int xmax, int xmin) { register int ndiff = -1; int hold_diff = 0; if (tmp ■== -l)tmp = cmp_screenl (tmp, last_crt) ; do { if ((tmp > max) ; ; (tmp < min) : : (tmp % colsw < xmin) : ; (tmp % colsw > xmax)) if (++ndiff) return (-1) ; else hold_diff - tmp; tmp += 2; tmp - cmp_screenl(tmp, last_crt) ; } while (tmp) ; return (hold_diff) ; } /*************************************************/ /* qual 80 & 40 - if a qualifying sting is provided check for it */ /*************************************************/ qual80() { if (fv.ig80.flag == 1) return (1) ; return (crtcmp (fv.igδO.qualpos, fv.igβo.string, fv.ig80.qlen)) ; ) qual40() { if (fv.ig40.flag == 1) return (1) ; return (crtcmp (fv.ig40.qualpos, fv.ig40.string, fv.ig40.qlen)) ; } /*************************************************/ /* ring buffer management Most of the memory used by the resident portion of the recorder is contained within the recorder's ring buffer. Several pointers are maintained within this structure. The first pointer is the begin of the next write to disk. This is the data at risk. The system begins the write to the disk file at this mark. The next is of course the next write into buf pointer. This pointer is the last valid information in the buffer and the end of the write to disk area. The next pointer points at the last type character buffer in the ring. */ /************************************************* do_ring_write() { register int file, i; /*msg("do_ring_write") ; */ lost_key = 0; in_write_ring = TRUE; if (chk_exist_flag && (file - open_file (file_name)) !=• 0) { if (chk_exist_ lag ■— «0' :: chk_exist_flag ■*■*•■= 'o') /* overwrite */ { close_file(file); file = creat_file (file_name) ; ) else if (chk_exist__flag =~- 'A' :: chk_exist_flag — 'a' seek_file (file, 39); /* append */ else { close_file(file) ; chk_exist_flag = 2; in_write_ring = FALSE; return; ) } else if (single_flag)file = creat_file(file_name) ; else if ((file ■ open_file(file_name) ) != 0)seek_eof(file) ; else file = creat_file(file_name) ; chk_exist_flag - 0; if (begin_ring_write J= end_ring_write) write_it(file) ; if (FinFlag) { store_ascii (major_buf_nvιm++) ; ringstore (0) ; ringstore (OxlE) ; ringstore (3) ; for (i = 0; i< 33; i+t)ringstorec (0); ringstorec(3) ; for (i = 0; i <33; i++) ringstorec (0) ; end_ring_write = write_ring_ptr; write_it (file) ; FinFlag = single_flag = HotFlag = did_lastw_flag = O; if (over_run) { show_text ("Buffer overrun, stopping record... ", 34); over_run = 0; } else show_text ("Recording complete...", 26); ) if (FinFlag | | single_flag) FinFlag - single_flag = HotFlag = 0; write_ring_flag = FALSE; close_file (file) ; if (lost_key_)blip(); in_write_ring = FALSE; /*msg("end_ring_write") ;*/ } /★♦A**********************************************/ /* write_it - does the write to the file and adjusts ring pointers */ /*************************************************/ write_it(int file) { if (begin_ring_write < end_ring_write) write_file(file, begin_ring_write, end_ring_write begin_ring_write) ; else { write_file(file, begin_ ing_write, end_ring - begin_ring_write) ; write_file(file, begin_ring, end_ring_write - begin_ring) ; } begin__ring__write = end_ring_write; numn_ring_writes = 0; ) /*************************************************/ /* KB_interrupt - This routine fields the keyboard interrupt after the resident rom bios routine is called during a clock. The scan character is sitting on the stack as the keyboard character */ /*************************************************/ void KB_interrupt() { register struct res_key_table_s *rkeytab = &fv.res_key_tab[0] ; register char ascii; unsigned int typein_code; unsigned char scan_code; while(next_char_in I = next_char_out) { typein_code « *next_char_out++; if (next_char_out = &typed_chars[99]) next_char_out = &typed_chars[0] ; ascii - typein_code & OxFF; scan_code - typein_code » 8; while (rkeytab->scan_code && (rkeytab->scan_code ! = scan_code)) rkeytab++; if (rkeytab->scan_code && (rkeytab->ascii_code === ascii)) { if (rkeytab->ctl_type = IGNORE_KEY) continue; if (rkeytab->ctl_type — FUNCTION_KEY) { if (fv. function_holdoff) waiting_for_record = fv. function_holdoff ; else if (fv.settle_time_min) { waiting_for_record = fv.settle_time_min; waiting_for_settle = 1; } else wai ing_for_record = 1; l st_function_keys - rkeytab; if (fv.typein_pos_flag) last_scr_out = last_scr_in = 0; continue; } > if (fv.typein_pos_ lag) while (last_scr_in I - last_scr_out) record_ke (last_scr_char[last_scr_out]) ; else if (ascii && ((ascii >= ' ') && (ascii <*= «-■)) || (rkeytab->ctl_type — TYPED_KEY) ) ) record_key(ascii) ; ) ) /*************************************************/ /* record_key - record the given character into type in buffer */ /************************************************* record_key (char asc__code) { register unsigned int tmp = last_scr_pos[last_scr_out], sav; /********************** autoexit KLUDGE ************************/ /********************** autoexit KLUDGE ************************/ struct res_key_table_s rkeytab, *savkeytab; if ((last_typein-pos J •= -1 ) &__ fv.typein_pos_flag && fv.typein_auto_keya && ((last_typein_pos < tmp - 2) || (last_typein_pos > tmp + 2)) ) { } { savekeytab = last_ unction_keys; last_function_keys - Srkeytab; last_function_keys->scab_code = 1; last_function_keys-.row - fv.typein_auto_keya; last_function_keys->col = fv.typein_auto_keytab; last_crt[tmp] = last_char_under; sav = fv.typein_auto_keya; fv.typein_auto_keya = 0; if (—last_scr-out < 0) last_scr_out = 9; record_screen(0) ; if (++last_scr_out — 10) last_scr_out == 0; fv.typein_auto_keya = sav; last_crt[tmp] = asc_code; last_typein_pos = - 1; last_function_keys = savekeytab ; /********************** autoexit KLUDGE ************************/ /********************** autoexit KLUDGE ************************/ if (recor ing_typein —0) { /* now setup the type in header */ store_ascii(major_buf_num++) ; typein_sizel = write_ring_ptr; ringstorec(0) ; typein_size2 = write_ring__ptr; ringstorec(0) ; ringstorec(1) ; typein_size = 3; /* set the flag for type in record and proceed as normal*/ recording_typein = 1; last_typein_pos = -1; last_curpos = NULL; ) /* two zeros then the ascii code */ ringstorec (0) ; ringstorec (0) ; ringstorec (asc_code) ; /* now put the current cursor position in */ if (fv.typein_pos_ lag) { tmp •= last_scr_pos[last_scr_out++] ; if (last_scr_out == 10) last_scr_out = 0; ) else { tmp = get_cursor () ; if (video_cols == 80) tmp = (tmp » 8) * 160 + ((tmp & OxFF) *2); else tmp = (tmp » 8) * 80 + ((tmp & OxFF) *2) ; tmp -= 2; } last_typein_pos - tmp; ringstorec (tmp / 100) ; ringstorec (tmp % 100) ; /* finally set the syze bytes in this one's header */ typein_size += 5; *typein_sizel = typein_size / 100; *typein_size2 = typein_size % 100; return; } /*************************************************/ /* do__typein_trailer - finish a typein record This routine is necessary because the 22 byte header really isn't. (22 bytes) turns out there is a ascii number a the record size tacked onto the end of every record in the system. This routine provides this essential data before writing the next record. */ /*************************************************/ do_typein_trailer () { /* finish up the typein buffer with minor buff number then size*/ /********************** autoexit KLUD G E ************************/ /********************** autoexit KLUD G E ************************/ register unsigned int tmp - last_scr_pos[last_scr_out], sav; struct res_key_table_s rkeytab, *savkeytab; if ((last__scr_in != last_scr_out) && fv.typein__pos_ lag && fv.typein_auto_keya) { save - fv.typein_auto_keya; fv.typein_auto-keya = 0; record_key (last_scr_char[last_scr_out]) ; fv.typein_auto_keya = sav; ) /********************** autoexit KLUDGE ************************/ /********************** autoexit KLUDGE ************************/ recording_typein = 0 ringstorec (0x20) ; store_ascii (minor_buf_num++) ; ringstorec (*typein_sizel) ; ringstorec (*typein_size2) ; num_ring_writes++; end_ring_write = write_ring_ptr; l st_typein_pos = -1; ) *************************************************/ /* record_screen - record a screen * This routine is called whenever the recorder wishes to create a screen record in the ring buffer. * */ /*************************************************/ record-screen (int move) { char *hdr_ptr; register unsigned int i; unsigned int j; /*msg ("record_screen") ;*/ if (recording_typein) do_typein_trailer () ; store_ascii (major_buf_num++) ; hdr_ptr = write_ring_ptr; setup_hdr () ; if (move) move_screen (last_crt) ; i - Compress (last_crt, tmp_buf) ; ringstore (tmp_buf, i + 1) ; i += 22; *hdr_ptr - i /100; *(hdr_ptr +1) = i %100; store_ascii (minor_buf_num++) ; ringstorec (*hdr_ptr++) ; ringstorec (*hdr_ptr) ; if (video_cols ■=•= 80) { store_ascii (major_buf_num) ; hdr_ptr = write_ring__ptr; setup_hdr (0) ; i = Compress(&last_crt[2000], tmp_buf) ; ringstore (tmp_buf, i + 1) ; i += 22; *hdr_ptr = i /100; *(hdr_ptr +1) = i $100; store_ascii (minor_buf_num++) ; ringstorec (*hdr_ptr++) ; ringstorec (*hdr_ptr) ; } end_ring_write = write_ring_prt; } /*************************************************/ /* setup_hdr - setup a recorded screen header This routine sets up the 22 byte header for each recorded screen. For now I robbed this from the previous incarnation of the recorder. This was done for security in knowing that it already works. This routine is to be expanded before the finished product to allow for 40 col screens */ /*************************************************/ setup_hdr () { register int tmp, i; /*msg ("setup_hdr") ;*/ for (i = 0; i < 7; i++) ringstorec (0); /* [0] to [6] */ ringstorec (0x83) ; /* pfrom [7] */ /* next handel the function key if there is one */ if (last_function_keys && last_function_keys- >scan_code) { ringstorec(last_function_keys->row) ; /* [8] and [9] */ ringstorec(last_function_keys->col) ; } else { ringstorec (24); /* [8] and [9] */ ringstorec (133) ; } /* set the cursor position */ tmp - get_cursor () ; if (video_cols ■=■= 80) { tmp = (tmp » 8) * 160 + ((tmp & OxFF) * 2) ; i = (tmp / 100 I 0x80; } else { tmp = (tmp » 8) * 80 + ((tmp & OxFF) * 2) ; i = tmp / 100; { ringstorec (i) ; /* [10] and [11] */ ringstorec (tmp % 100) ; ringstorec (0); /* [12] */ for (i = 13; i < 22; i++) ringstorec (0); /* [13] to [21] */ ) /*************************************************/ /* store_ascii - place an ascii number into the ring buffer - 256 - This routine converts the interger argument into 3 ASCII digits then places each of the digits into the ring buffer */ /************************************************/ store_ascii (int i) { ringstorec ((i / 100) + '0'); ringstorec ((i % 100 /lθ) + O»); ringstorec ((i % 10) + «0'); ) /*************************************************/ /* ringstorec - move a single byte into the ring buffer This routine performs an efficient storage of a single byte into the ring buffer adjusting the write pointer as necessary */ /*************************************************/ ringstorec (char c) { register char *tmp = write_ring_ptr; *tmp++ ■= c; if (tmp == end ring) tmp ■= begin_ring; if (over_run |J tmp ■== begin_ring_write) { over_run ■= 1; write_ring_ptr -= end_ring_write; return; ) write_ring_ptr = tmp; } /*************************************************/ /* ringstore - store a bloc of memory into the ring buffer This routine moves a block of memory into the ring buffer adjusting the write ring ptr as necessary. The move is broken up into to me cpy's if necessary */ *************************************************/ ringstore (register unsigned char *cptr, register unsigned int size) { /*msg ("ringstore") ;*/ while (size—_ ringstorec (*cptr++) ; num_ring_writes ++; } /*= := * / /* ************************************************** Routine Compress (pbufin, pbufout, pbufw, psize) ; Purpose Compresses the display in pbufin[0] on. First, the characters and attributes are * separated into bottom and top of buffer (into * pbufout [] and pbufw) , then each group of 3 * or more "like" bytes are changed to "OxFF, * byte, nbr", where nbr is the number of bytes * in the "like" string. If 1 or 2 OxFF's are * encountered in the input, they are changed to a "OxFF, OxFF, nbr" series. * If nbr is > 249, the series is: * "OxFF, byte, nbr/256+249, nbr%256" * * Note 1 The output buffer will be used (all psize * bytes) for working storage before being used * as the output buffer (pbufout []) . * * Note 2 The input buffer may be used as the output * buffer, if desired. * * Note 3 Each buffer must be at least psize in size. * * Parameters none * * Note: Don't change this routine - it works and is very * efficient. * ************************************************** V int Compress (char *src, char *dst) { register int i; char *tmp; /*msg ("Compress'•) ;*/ tmp = do_compress (src, dst, video_cols = 80 ? 1000 : 1000) ; i = tmp - dst; dst = tmp; tmp = do_compress (src + 1, dst, video_cols ■== 80 ? 1000 : 1000) ; i += tmp - dst; return (i -1) ; } char *do_compress (register char *src, register char *dst, int len) { int i, j; unsigned char dup; /*msg ("do_compress") ;*/ for (i - 0; i < len; i++) if (*src == *(src +2) && *src == *(src +4)) { *dst++ = OxFF; /* need to compress */ dup = *dst++ = *src; src += 2; for (j = 1; j < len - i; j++) /* count the dups */ if (dup {= *src) break; else src += 2; if (j < 250) *dst++ = j; /* over 249? */ else { *dst++ - j / 250 + 249; *dst++ = j % 250; ) /* adjust count */ i += j - 1; } else if (*src ! = OxFF) *dst++ = *src, src += 2; /* no compress */ else { *dst++ = OxFF; /* handle OxFF in data stream */ *dst++ = OxFF; *dst++ ■= 1; src += 2; if (*src = OxFF && i < 999) { *dst - l) = 2; src +■= 2; i++; ) } return (dst) ; ) fifdef DEBUG char bufl[4000]; char buf2[4000]; main () { move_screen (bufl) ; Compress (bufl, buf2) ; } fendif /* not sure this will ever work but save it any way { if (last_crt[tmp] != *last_curpos) if (last_crt[tmp -2] == *last_curpos) tmp -=2; else if (last_crt[tmp +2] == ■ *last_curpos) tmp +=2; if (last_crt[tmp] == *last_curpos) { last_curpos++; *last_curpos++ =- tmp / 100; *last_curpos++ = tmp % 100; last_curpos++; last_curpos++; ) } buf[0] = last_crt[tmp]/100 + '0' buf[l] = last_crt[tmp]/10) % 10 + '0'; buf[2] = last_crt[tmp]%10 + O'; buf[3] = • •; buf[4] = » •; buf[5] = tmp /1000 + « 0'; buf[6] = (tmp /100) % 10 + '0'; buf[7] = (tmp /10) % 10 + » 0 « ; buf[8] = tmp % 10 + '0'; buf[9] = 0; show_text (buf, 32) ; */ Title Inthndlr page 44, 132 P R O P R I E T A R Y N O T I C E ; THIS DOCUMENT DESCRIBES A PROPRIETARY SOFTWARE PRODUCT OF TDS ; HEALTHCARE SYSTEMS CORPORATION (TDS) , CONTAINS TRADE ; SECRET AND PROPRIETARY INFORMATION, IS AND SHALL REMAIN AN ; UNPUBLISHED WORK PROTECTED BY COMMON LAW COPYRIGHT, IS LOANED ; FOR LIMITED PURPOSES ONLY, REMAINS THE PROPERTY OF TDS, AND IS ; TO BE RETURNED UPON REQUEST OR COMPLETION OF THE PURPOSE OF THE ; LOAN, WHICHEVER IS SOONER. ACCEPTANCE OR RECEIPT OF THIS ; DOCUMENT BY THE AGENT AND/OR PRINCIPAL TO WHOM IT IS LOANED IS ; AN ACKNOWLEDGEMENT THEREBY THAT IT IS BEING RECEIVED IN ; CONFIDENCE AND THAT IN CONSIDERATION FOR THE LOAN, NEITHER IT ; NOR ANY OF THE INFORMATION IT CONTAINS WILL BE. REPRODUCED, ; USED, OR DISCLOSED, IN WHOLE OR IN PART, BY OR TO ANYONE NOT ; HAVING A NEED-TO-KNOW CONSISTENT WITH THE PURPOSE OF THE LOAN ; OR ANYONE NOT EMPOLOYED BY SAID AGENT AND/OR PRINCIPLE WITHOUT ; THE WRITTEN CONSENT OF TDS. THE PROPRIETARY SOFTWARE PRODUCT ; DESCRIBED BY THIS DOCUMENT SHALL NOT BE USED, REPRODUCED, OR ; DISCLOSED, IN WHOLE OR IN PART, WITHOUT THE WRITTEN CONSENT OF ; TDS. I ; Name: inthndlr.asm I ; Description: Routines to handle the interrupts for the MAGIC ; recorder ; This is the section of assembly code that contains all of the ; entry points for the Magic recorder. The code is modified frqm ; an article in the MS DOS encyclopedia and is modified to perform the recorder. Most of this code setups up the inter¬ rupt structure in the machine so that the following interrupt pass through the recorder for the following reasons: ISR 5 Print Screen Non re-entrent uses stack ISR 8 Hardware interrupt for clock we make our own chain ISR 9 Hardware interrupt for keyboard ISR 10 Video BIOS non-reenternatk uses stack ISR 13 DASD BIOS non-reenternat uses stack ISR 23 Control break from the BIOS ISR 24 DOS critical error may be called during our disk ID ISR 28 DOS idle interrupt lets us work when DOS is waiting on the KB ISR 2F DOS multiplex interrupt we identify ourselves in this chain ISR 21 DOS command request monitors DOS activity Perhaps a little overkill may be in here especially with print ; screen but it gives us an extra margin of safety for working ; with a number of different DOS applications. MultiplexID EQU 08Ah unique INT 2FH ID value TSRStackSize EQU 25Oh resident stack size in bytes KB_FLAG EQU 17h offset of shift-key status flag in KB_TAIL EQU ICh offset of KB tail buffer pointer ROM BIOS keyboard data area for KB FLAG LF EQU OAh TRUE EQU -1 FALSE EQU 0 PAGE RAM-resident routines .MODEL SMALL .CODE extrn _Clock_tick;near ; resident C code extrn _do_ring_write:near resident C code extrn _KB_interrupt:near ; ditto extrn _record_key:near ; ditto .DATA INCLUDE MAGIC.INC extrn _single_flag:byte extrn _FinFlag:byte extrn _write_ring_flag;byte extrn _got_file_name:byte extrn _HotFlag:byte extrn _noshift_xlat:byte extrn _shift_xlat:byte extrn _ctrl)_xlat:byte extrn _capsl_xlat:byte extrn _alt_xlat:bγte extrn _Ticks:word public _inGetText _inGetText db O public _fv _fv res_control_prms_s <> public _typed_chars _typed_chars dw 100 dup (0) public _next_char_in, _next_char_out _next_char_in dw offset _typed_chars _next_char_out dw offset _typed_chars .CODE ; System verification routines read the state of the interrupt routines public VerifyDOSState VerifyDOSState flag is busy registers VerifyDOSState ENDP VerifylntState PROC near ; Returns: carry flag ; set if hardware or ROM ;BIOS unstable push ax ; preserve AX ; Verify hardware interrupt status by interrogating Intel 8259A ; Programmable ; Interrupt Controller mov ax,0000l0llb ; AH = 0 AL = OCW3 for Intel ; 8259A (RR=1, cli out 20h,al request 8259A's in- service register jmp short L10 ; wait a few cycles L10: in al,20h ; AL = hardware inter- ; rupts currently cmp ah,al Lll ; exit if any hardware ; interrupts still being serviced ; Verify status of ROM BIOS interrupt handlers xor al,al ; AL =~ OOH cmp al,cs;InISR5 jc Lll ; exit if currently in INT 05H handler cmp al,cs;lnISR9 Lll ; exit if currently in ; INT 09H handler cmp al,cs;InISRl3 ; set carry flag if ; currently in INT 13H ; handler Lll: pop ax ; restore AX and return L20: ret VeryfiylntState ENDP VerifyTSRState PROC near ; Returns: carry flag set ; if TSR inactive call VerifyDOSState jc L20 ; exit if MS-DOS unstable call VerifylntState ; set carry flag if hard- ; ware or BIOS ret VerifyTSRState ENDP PAGE ; System monitor routines ISR5 PROC far INT 05H handler (ROM BIOS print screen) mc cs:InISR5 ; increment status flag mov cs:InISR21,0 ; decrement status flag popf sti ret ISR21 ENDP ; Timer Tick public ISR8 ISR* PROC far INT 08H handler (timer tick, IRQO) ISR8 ENDP Keyboard interrupt ASSUME cs:_TEXT,ds;_DATA public ISR9 ISR9 PROC far INT 09H handler (keyboard interrupt IRQl)d push ds preserve these registers push es push ax push bx mov ds,cs;TSRDS point to res data mov bx,40h mov es,bx ; ES-> ROM BIOS data area mov bx, ex; [KB FAIL] ; BX -> Key buffer in al,60h ; AL = current scan code pushf ; simulate an INT cli call cs:PrevISR9 ; let previous handler ; execute cli test al,80H ; break? jnz L43 ; yes out of here cmp _inGetText, O ; dumping chars? je nodmp ; no cmp _fv.no_kb_rom_flag,0 ; rom code in use? je nodmp using rom code mov es: [KB_TAIL],bx ; pull back the pointer nodmp: cmp cs:InISR9, 0 if already in this ; handler .. jnz L43 inc cs:InISR9 ; increment status flag sti ; now interrupts are ok ; get shift state mov ah,es:[KB_FLAG] AH - ROM BIOS shift-key flags and ah, JustShifts clear out extraneous info ah = shift state al - scan code cmp _FinFlag,0 ; going off? pushf cli call cs:PrevISRS ; chain to previous INT ; 05H handler dec cs:InISR5 ; decrement status flag iret ISR5 ENDP System 21 intr routines ISR21 PROC far ; INT 21H handler pushf dir21: sti cmp ah, 4Bh ; exec? jne noex ; no popf jmp cs:PrevISR21 ; direct noex; or ah, ah ; zero? jne noz ; no mov ah, 4Ch ; quick change noz: mov cs:InISR21,l ; status flag mov cs:DosCmd, ah ; set command popf pushf call cs:PrevISR21 ; chain to previous INT21 pushf cli cmp cs:doingwrite, jnz nowt push ds mov ds, cs:TSRDS cmp ds:_write_ ing_flag, o ; need a write? pop ds je nowt ; no inc cs: oingwrite sti call TSRwrite dec cs:doingwrite nowt: sti 3ne L42 ; nothing to do cmp _HotFlag,0 ; on? jne got_key ; jump C key routine cmp al, _fv.start_key ; are we starting up? je L40 ; yes, check it out cmp al, _fv.single_rec_key ; single screen? jne L42 ; no chance single screen record possible cmp ah, _fv.single_rec_shifts ; proper ; shifts? jne L42 ; no mov _single_flag, TRUE ; mark it mov _HotFlag, TRUE ; turn it on jmp L42 ; all done ; get shift-key state L40: cmp ah, _fv.start_shifts ; proper shifts? jne L42 ; no ; Set flag when hot key is found mov byte ptr _HόtFlag, TRUE turn it on L42: dec cs:InISR9 ; decrement status flag L42: dec cs:InISR9; decrement status flag L43: pop bx; restore registers and exit pop ax pop es pop ds iret ; this code is executed when the recorder is on and we are recording keystrokes got_key: cmp _single_flag, TRUE; recording a single? je L42; yes ignore key cmp al, _fv.stop_key; maybe done? jne reckey; no cmp ah, _fv.stop_shifts; really the key? ne reckey; no, just record. mov _FinFlag, TRUE; mov _write_ring_flag; TRUE; need a write mov _got_file_name, 0; can file name cli cmp cs; oingwrite, o jne L42 call VerifyTSRState jc L42 inc cs: oingwrite sti call TSRwrite dec cs:doingwrite jmp L42; finished ; we are recording a path of screens and keys store in a ring buffer reckey: mov _last_scan_code, al; store the code away cmp _fv.no_kb_rom_flag,O; doing our own rom xlat? jne romnogood; yes jmp romgood; just go ; due to the IRMA intircacacies redo rom code romnogood:mov ah, es; [KB_FLAG] ; get the shift states and ah, JustShifts; just the shift states test ah, KBAlt; alt shifts? jnz altshift; yes test ah, KBCtl; clt shifts? jnz cltshift; yes lea bx, _capsl_xlat; normal caps lock test ah, KBCaps jz no capsl test ah, KBLeft OR KBRight jz getscan lea bx, _noshift_xlat; no shift ' s jmp getscan nocapsl: lea bx, _noshift_xlat; no shifts crop ah, 0 je getscan lea bx, _shift_xlat; shift? test ah, KBLeft OR KBRight jnz getscan altshift: mov bl, al xor bh, bh xor al, al; bottom is zero mov ah, {bx + _alt_xlat] ; get the code jmp storekring ctlshift: lea bx, _ctrl_xlat; control getscan: xor ah, ah; clear the shift status add bx, ax; offset into the table mov ah, al; scan code on top mov al, [bx] ; xlat in the low test al, 80h; special key? jnz storekring; no, just store cmp al, -1; shift key? je toL42; yes, ignore cmp al, -2; lock key? e tbL42 yes, ignore cmp al, -4; ignore key? je toL42; yes, ignore cmp al, -3; special key? jne spclshft; no, check more xor al, al; no ascii code jmp storekring spclshft: cmp al, -5; shift special key? jne spclctrl; no, check more xor al, al; no ascii code add ah, 19h jmp storekring spclctrl: cmp al, -6; ctrl special key? jne storekring; no check more xor al, al; no ascii code add ah, 23h jmp storekring ro good: mov ax, es;[bx]; get last typed key storekring:mov bx, _next_char_in; get the in pointer mov [bx] , ax; store it add bx, 2; bump pointer cmp bx, offset _typed_chars + 100 jb notcadj; no adjust mov bx, offset _ typed_chars notcadj: mov _next_char_in, bx; save cli cmp cs; oingwrite, o jne toL42 cmp ds:_write_ring_flag, 0 je toL42 • call VerifyTSRState jc toL42 inc cs:doingwrite sti call TSRwrite dec cs:doingwrite tpL42: jmp L42; finished ISR9 ENDP Video Bios ASSUME cs:-TEXT,ds:nothing ISR10 PROC far; INT 10H handler (ROM BIOS video I/O) inc cs:InlSRl0; increment status flag pushf cli call cs:PrevISR10; chain to previous INT 10H handler dec cs:InISR10; decrement status flag iret ISR10 ENDP Disk Bios ISR13 PROC far; INT 13H handler ; (ROM BIOS fixed disk I/O) inc cs:InISR13; increment cs:PrevISR13; chain to ; preserve returned flags cs:InISR13; decrement ; restore flags register ; enable interrupts 2; simulate IRET without ISR1B PROC far; INT 1BH trip (ROM BIOS Ctrl-Break) mov byte ptr cs:TraplB,TRUE iret ISR1B ENDP Ctrl C trap ISR23 PROC far; INT 23H trap (MS-DOS Ctrl-C) mov byte ptr cs: Trap23,TRUE iret ISR23 ENDP DOS critical error during our disk 10 ISR24 PROC far; INT 24H trap (MS-DOS critical error) mov byte ptr cs:Trap24,TRUE xor al,al; AL = OOH (MS-DOS 2.x) : ; ignore the error cmp cs; jorVersion,2 je L50 mov al,3; AL = 03H (MS-DOS 3.x) : ; fail the MS-DOS call in which ; the critical error occurred L50: iret ISR24 ENDP DOS idle ISR28 PROC far; INT 28H handler ; (MS-DOS idle interrupt) Multi plex for ID ISR2F PROC far; INT 2FH handler ; (MS-DOS multiplex interrupt) ; Caller: AH = handler ID ; AL = function number ; Returns for function O: AL = OFFH ; for all other functions: nothing cmp ah,MultiplexID je L70; jump if this handler is requested 3mp cs:PrevISR2F; chain to previous INT 2FH handler L70: test al,al jne Getaddr ; Function 0: get installed state mov al,OFFh; AL = OFFh (this handler is installed) jmp MultiplexIRET ; Functionl; get data area Getaddr: cmp al, 1; return addr? MultiplexIRET ax, offset _fv; structure dx, cs:TSRDS; and data ; return from interrupt ; Auxlnt21—sets ErrorMode while executing INT 21h to force use ; ; of the AuxStack instead of the IOStack. PAGE RAM-resident application Tick PROC near assume ds:DATA, cs:_TEXT ; Set up a safe stack push ds; save previous DS on previous stack mov ds,cs:TSRDS mov cs:PrevSPT,sp; save previous SS:SP mov cs:PrevSST,ss mov ss;cs:TSRDS; SS:SP -> RESIDENT STACK mov sp,cs:TSRSPT push es; preserve reamining registers push ax push bx push ex push dx push si push di push bp mov ax, _next_char_out cmp ax, _next_char_in je tic; no call _KB_interrupt ; go do it tic: call _Clock_tick; tick it pop bp pop di pop si pop dx pop ex pop bx pop ax pop es mov ss,cs;PrevSST; SS:SP -> previous stack mov sp,cs:PrevSPT pop ds; restore previous DS Finally, reset status flag and return ret Tick ENDP TSRwrite PROC near assume ds:_DATA, cs:_TEXT ; Set up a safe stack push ds; save previous DS on previous stack mov ds,cs:TSRDS mov cs:PrevSP,sp; save previous SS:SP L90 : lodsb ; AL = interrupt number ; DS;SI -> byte past interrupt number mov byte ptr cs: [si] ,FALSE; zero the trap flag push ax; preserve AX mov ah,35h; INT 21H function 35H ; (get interrupt vector) int 21h; ES:BX = previous interrupt vector mov cs:[si+1] ,bx;save offset and segment., mov cs; [si+3] ,es;.. of previous handler pop ax; AL = interrupt number mov dx,cs: [si+5] ; DS:DX -> this TRS's trap mov ah,25h; INT 21H function 25H int 21h; (set interrupt vector) add si,7; DS:SI -> next in list loop L90 mov ds,cs:TSRDS - 277 - ; Disable MS-DOS break checking during disk I/O mov ax,3300h; AH == INT 21H function number ; AL = OOH (request current break state) int 21h; DL = current break state mov cs:PrevBreak,dl; preserve current state xor dl,dl; DL = OOH (disable disk I/O break ; checking) mov ax,3301h; AL = OIH (set break state) int 21h Preserve previous extended error information sti call _do_ring_write; do the write ; Restore previous MS-DOS break checking L95: mov dl,cs:PrevBrea ; DL = previous state mov ax,3301h int 21h : Restore previous break and critical error traps mov e ,cs:NTrap mov si,offset StartTrapList push ds; preserve DS L96: lods byte ptr cs:[si]; AL = interrupt number ; ES:SI -> byte past interrupt number Ids dx,cs: [si+1];DS:DX -> previous handler mov ah,25h; INT 21H function 25H int 21h; (set interrupt vector) add si,7; DS:SI -> next in list loop L96 pop ds; restore DS - 278 - bp di si dx ex bx ax es ss,cs:PrevSS; SS:SP -> sp,cs:PrevSP ds; restore previous DS flag and return ?; address of MS-DOS ?; address of MS-DOS (EndlSRList- of installed ISRs 05h; INT number ,* flag ; address of previous offset ISR5 08h offset ISR8 09h offset ISR9 DB lOh public InlSJRIO - 279 - OR KBScroll) XOR Offh : The following data is used by the TSR application: NTrap DW (EndTrapList-StartTrapList) /8; number of traps StartTrapList DB lBh TrapIB DB FALSE PrevISRIB DD DW offset ISR1B - 280 - . DATA public last_Jd__y last_key dw public _last_scan_code last scan code db DBFALSE WORD DB?; minor version number ?; major version number TSRStackSize + 2 dup(?) TSRStackSize + 2 dup(?) PAGE Transient installation routines .CODE buffer_size dw public _InstallTSR _InstallTSR PROC far; At entry: CS:IP -> InstallTSR ;SS:SP -> stack ;DS,ES -> PSP ; Save PSP segment mov ax,seg _DATA mov ds,ax; DS -> RESIDENT_DATA mov ss,TSRDS; SS:SP -> RESIDENT STACK mov sp,TSRSP mov TSRPSP,es; save PSP segment ax,81H; addr of args es ax _transient_init; c code sp, 4 buffer_size, ax ; returns ; Check the MS-DOS version call GetDOSVersion; AH = major version number ; AL = minor version number Verify that this TSR is not already installed Before executing INT 2Fh in MS-DOS versions 2.x, test ; ; ; whether INT 2FH vector is in use. If so, abort if PRINT. ; COM is using it. ; (Thus, in MS-DOS 2.x, if both this program and PRINT.COM ; are used, this program should be made resident before PRINT. COM.) cmp ah,2 ja L101; jump if version 3.0 or later mov ax,352Fh; AH » INT 21H function number ; AL = interrupt number int 21h; ES:BX = INT 2FH vector mov ax,es or ax,bx; jump if current INT 2FH vector.. jnz L100; .. is nonzero push ds mov ax,252Fh; AH = INT 21H function number ; AL = interrupt number mov dx,seg _TEXT mov ds,dx mov dx,offset Multiplex IRET int 21h; point INT 2FH vector to IRET pop ds jmp short L103; jump to install this TSR L100: mov ax,OFF00h; look for PRINT.COM: int 2Fh; if resident, AH « print queue length; ; otherwise, AH is unchanged cmp ah,OFFh; if PRINT.COM is not resident je L101; .. use multiplex interrupt mov al,l call FatalError; abort if PRINT.COM already installed L101: mov ah,Multiplex ID; AH = multiplex interrupt ID value xor al,al; AL = OOH int 2Fh; multiplex interrupt al,al L103; jump if ok to al,OFFh L102; jump if not already al,2 FatalError; already al,3 FatalError; can't install handlers es; preserve PSP segment cx,NISR si,offset StartlSRList L104: mov al,cs: [si] ; AL = interrupt number inc si ; DS:SI -> byte past interrupt number push ax; preserve AX mov ah,35h; INT 21H function 35H int 21h; ES:BX = previous interrupt vector mov cs: [si+1],bx; save offset and segment .. mov cs: [si+3] ,es; .. of previous handler pop ax; AL = interrupt handler push ds; preserve DS mov dx,cs: [si+5] mov bx,seg _TEXT mov ds,bx; DS:DX -> this TSR's handler mov ah,25h; INT 21H function 25H int 21h; (set interrupt vector) pop ds; restore DS add si,7; DS:SI -> next in list loop L104 ; Free the environment pop es; ES = PSP segment push es; preserve PSP segment mov es,es: [2Ch] ; ES = segment of environment mov ah,49h; INT 21H function 49H int 2lh; (free memory block) ; Terminate and stay resident pop ax; AX = PSP segment mov dx,cs; DX = paragraph address of start of ; transient portion (end of resident ; portion) sub dx, ax; DX = size of resident portion add dx, buffer size; then add the ring buffer length mov ax,3100h; AH = INT 21H function number ; AL = OOH (return code) int 21h InstallTSR ENDP GetDOSVersion PROC near; Caller: DS = seg RESIDENT DATA ; ES β psp ; Returns: AH = major version ; AL = minor version mov ah,30h; INT 21h function 30H: ; (get MS-DOS version) int 21h cmp al,2 jb LllO; jump if version 1.x xchg ah,al; AH ~ major version ; AL = minor version mov DOSVersion,ax; save with major version in ; high-order byte ret LllO: mov al,00h FatalError ENDP ;TRANSIENT TEXT ENDS PAGE Transient data segment ;TRANSIENT_DATA SEGMENT word public 'DATA' .DATA MessageTable DW MessageO; MS-DOS version error DWMessagel; PRINT.COM found in MS-DOS 2.x DW Message2; already installed DW Message3; can't install DW Message4; can't find flag MessageO DB CR,LF, 'TSR requires MS- DOS 2.0 or later version* ,CR t LF, '$' Messagel DB CR,LF, "Can"t install TSR: PRINT.COM active' ,CR,LF'$' Message2 DB CR,LF, 'This TSR is already installed' ,CR,LF, '$' Message3 DB CR,LF, 'Can't install this TSR',CR,LF, '$• Message4 DB CR,LF, 'Unable to locate MS-DOS Error Mode flag' ,CR,LF, •$* DBTSRStackSize dup(?) ; debug fragments mov ax,0E07h; AH = INT lOH function number int lOh; emit a beep push ds push ax mov ax, OBδOOH mov ds, ax inc byte ptr ds: [O] mov al,cs;_HotFlag mov byte ptr ds:[2], al mov al, cs:InISR9 mov byte ptr ds:[4], al call FatalError; abort if version 1.x GETDosVersion ENDP FatalError PROC near; Caller: AL = message number ; ES = PSP push ax; save message number on stack mov bx,seg _ DATA mov ds,bx ; Display the requested message mov bx,offset MessageTable xor ah,ah; AX = message number shl ax,l; AX = offset into MessageTable add bx,ax; DS:BX -> address of message mov dx, [bx] ; DS:BX -> message mov ah,09h; INT 21H function 09H (display string) int 21h; display error message pop ax; AL = message number or al,al jz L130; jump if message number is zero (MS-DOS versions 1.x) ; Terminate (MS-DOS 2.x and later) ah,4Ch; INT 21H function 21h; (terminate process far es; push PSP:0000H ax,ax ax ; far return (jump to - 287 - pop ax mov byte ptr ds: [6] , al pop ds END InstallTSR

Claims: 1. A method for detecting and recording signals from an input device operatively connected to a digital computer and output from a target program accessible by the computer, the method comprising the steps of: a) loading recorder means into the computer's memory; b) accessing a format table file with the recorder to get data representing predefined recording characteristics of the target program and configure the recorder to the target program; c) monitoring and interceding in the control of the operations of the computer with the recorder; d) accessing the target program with the digital computer; e) recording to a datafile signals from the input device, the signals representing input to the target program, and a sequence of screens produced by the target program. 2. The method of claim 1, wherein the step of recording is carried out by a recorder not integral with a means for playing back the datafile. 3. The method,Λ claim 1, wherein the step of recording is carried out by a recording emulator. 4 * The method of claim 2, wherein the step of recording is carried out by a recording emulator. 5. The method of claim 1, wherein the step of recording is carried out by an emulator linked to a recorder by an application programming interface. 6. The method of claim 1, wherein the step of recording is carried out with a generic recorder. 7. The method of claim 1, wherein the step of recording is carried out with a generic recorder having a terminate-stay-resident portion. 8. The method of claim 1, wherein the step of recording is carried out optionally by at least two of the group consisting of (a recording emulator, a generic recorder, an emulator linked to a recorder by an application programming interface) . 9. The method of claim 1, wherein the step of recording is carried out optionally by any of the group consisting of (a recording emulator, a generic recorder. an emulator linked to a recorder by an application programming interface). 10. The method of claim 4 further comprising the step of: a) recording in the datafile time from a clock chip in the digital computer. 11. The method as in one of claims 5-7, wherein the step of accessing the format table file is carried out with a configurable format table file. 12. The method as in one of claims 5-7, wherein the step of accessing the format table file is carried out with a configurable format table file configurable as to sample rate, record hold off, function hold off, start record, stop record, record screen, type in position, no keyboard in ROM, and auto exit code. 12. The method of claim 6, wherein the step of recording is carried out with hooks or calls into operating system interrupts. 13. The method of claim 12, wherein the step of recording is carried out by outputting the datafile subsequent to formatting and compressing the data. 14. The method of claim 12, wherein the step of recording includes a configure state having the following steps: a) initializing internal data structures from the function table file in FTF 172; b) setting key interpretation tables; and c) arming a start and stop record key sequence. 15. The method of claim 12, wherein the step of recording includes a target program state in which the target program runs in a regular DOS environment, except during the keyboard interrupts and the hardware clock interrupts. 16. The method of claim 12, wherein the step of recording includes a target program state that, during a keyboard interrupt, switches from the target program state to the keyboard state so that the following actions that may optionally be performed include: transferring control to the generic recorder's keyboard interrupt routine so that the following actions may optionally be performed: (1) nothing, the recorder is not activated; (2) recognize the activation key sequence; (3) store away the character generated from the keyboard; and (4) post an I/O cycle for the clock interrupt. 17. The method of claim 12, wherein the step of recording includes a target program state that, during a clock interrupt, switches from the target program state to the clock state so that the actions that may optionally be performed include: (1) nothing, the recorder is not active; (2) compare the screen to its previous state; (3) store the present screen; and (4) perform a disk I/O. 18. The method of claim 12, wherein the step of accessing with a generic recorder a format table file includes: a) reading data from a specified format table file from a disk accessible by a terminate-stay resident portion of the generic recorder, the format table file including a machine-specific, key definition table; and b) compiling the data in the format table file and inserting the data into memory of a resident portion of the generic recorder. 19. The method of claim 12, wherein the step of recording includes: using the keyboard interrupts and the clock hardware interrupts to trigger a comparison of a present and a previous screens to determine if the generic recorder need take any action. 20. The method of claim 12, wherein the step of recording includes: accessing, via the generic recorder, a .master table for interpreting keystrokes from a host computer's BIOS; interpreting keystrokes by the BIOS maintained within a separate ring buffer within the generic recorder; accessing machine recognition code to identify a host machine type and a host keyboard type for direct key number decoding for turn on and turn off key sequences. 21. The method of claim 12, wherein the step of recording is carried out with: a generic recorder having the following: (1) a monitored interrupt; (2) a screen multiplex interrupt; (3) a clock interrupt; and (4) a keyboard interrupt. 22. The method of claim 10, wherein the step of monitoring and interceding in the control of the operations of the computer includes: replacing, via the recorder generic recorder ter inate-stay-resident portion, at least some of normal interrupts so that whenever the computer's operating system calls the normal interrupts, the resident portion will have an opportunity to examine the normal interrupts before the normal interrupts reach the computer's operating system. 23. The method of claim 12, wherein the step of monitoring and interceding in the control of the operations of the computer includes: taking control of the keyboard and clock interrupts. 24. The method of claim 4, wherein the step of recording includes: receiving the signals in the recording emulator but not acting upon the signals without the recorder first having evaluated the signals. 25. The method of claim 4, wherein the step of recording includes: controlling the recording emulator with hooks or traps. 26. The method of claim 4, wherein the step of recording is carried out with the emulating being subservient to recording. 1 27. The method of claim 5, wherein the step of 2. recording is carried out with a 3270 emulator. 3 4 28. The method of claim 5, wherein the step of 5 recording is carried out with CICS protocol. 6 7 29. The method of claim 21, wherein the 8 recorder takes over at least some of the interrupts. 9 0 30. The method as in one of claims 1-29, 1 wherein the step of recording is carried out while the 2 computer is linked to a host. 3 4 31. The method as in one of claims 1-30, 5 further comprising the steps of : 6 with a CBT system accessible by said computer, 7 reading said datafile and displaying the sequence of 8 screens and the signals on a monitor operatively connected 9 to the digital computer. 0 1 32. The method of claim 31, wherein the steps 2 of reading and displaying further comprise the step of: 3 routing the screens and the signals through 4 playback means. 5 33. The method of claim 32, wherein the step of routing includes: routing the screens and the signals through at least one of the playback means selected from the group consisting of (automatic playback means, manual playback means, instructional playback means, and proficiency playback means) . 34. The method as in one of claims 31-33, further comprising the step of: routing the screens and the signals through modify means. 35. The method of claim 34, wherein the step of routing the screens and the signals through modify means comprises: routing the screens and the signals through basic page means. 36. The method of claim 34-35, wherein the step of routing the screens and the signals through modify means comprises: routing the screens and the signals through typing error means. 37. The method of claim 34-36, wherein the step of routing the screens and the signals through modify means comprises: routing the screens and the signals through selection error means. 38. The method of claim 34-37, wherein the step of routing the screens and the signals through modify means comprises: routing the screens and the signals through color/ASCII means. 39. The method of claim 34-38, wherein the step of routing the screens and the signals through modify means comprises: routing the screens and the signals through screen utility means. 40. The method of claim 36, wherein the step of routing further comprises: routing the screens and the signals through student diskette means. 41. The method of claim 33, wherein the routing the screens and the signals through student diskette means comprises; routing the screens and signals through student registration means, course directory means, and directory update means. 42. An apparatus for detecting and recording signals from an input device operatively connected to a digital computer and output from a target program accessible by the computer, the apparatus comprising the steps of: a) a recorder in the computer's memory; b) a format table file accessable by the recorder to get data representing predefined recording characteristics of the target program and to configure the recorder to the target program; and c) wherein the recorder monitors and intercedes in the control of the operations of the computer while the computer accesses the target program to form a datafile of signals from the input device, the signals representing input to the target program, and a sequence of screens produced by the target program. 43. The apparatus of claim 42, wherein the recorder is not integral with a means for playing back the datafile. 44. The apparatus of claim 42, wherein the recorder is a recording emulator. 45. The apparatus of claim 43, wherein the recorder is a recording emulator.

Inventors: ALVES ROBERT A, CERCHIO GERARD J, MCCARTHY KEVIN G, WILLIAMS PAUL E, ALVES, ROBERT, A., CERCHIO, GERARD, J., MCCARTHY, KEVIN, G., WILLIAMS, PAUL, E.
Applicants: TDS HEALTHCARE SYSTEMS CORP, TDS HEALTHCARE SYSTEMS CORPORATION
Application year: 1990
Main classification: G06F11
IPC codes: G06F11, G09B19