REM ************************************************************************** REM * INSTALL: write control codes to a file. Digital Research, Inc. 1982 * REM *------------------------------------------------------------------------* REM * This program is for your use. You may alter it in anyway that you need.* REM * You may package this program with your application program, the end * REM * users can then install their terminal for your product. If TERMS.DM is * REM * too large for your distribution disk, you may carefully remove any * REM * terminals that you want. To do this, make sure that no extra blank * REM * lines appear in the file, nor that you alter any lines that you don't * REM * remove. * REM *========================================================================* REM * This program has three parts: * REM * 1) The user interface, that gets the file name, and calls the other two* REM * routines. You will probably want to alter this to fit your needs. * REM * 2) The terminal file reader. This routine opens TERMS.DM and builds a * REM * data structure out of the terminal information. The only thing that * REM * you might need to change is the terminal file name. * REM * 3) The terminal information lister. This lists the manufacturer and * REM * model names, so that a code may be selected. Note that the listing * REM * will fit on a 52 column screen. * REM *========================================================================* REM * TERMS.DM: this is the terminal code file. You may use it with this pro-* REM * gram, but do not remove the Digital Research notice. You may add more * REM * header lines, but you will need to add more READ's in CRTRM. * REM *------------------------------------------------------------------------* REM * THE CODE BELOW IS COMMENTED ON A LINE BY LINE BASIS. READ THE COMMENTS * REM * BEFORE ATTEMPTING CHANGES. THE CODE IS NOT ALWAYS OBVIOUS. 11/15/82 * REM ************************************************************************** STRING TABC rem tabs are removed from codes. INTEGER TRUE,FALSE, \ rem logical operands, for conditionals. MAXCOD,MAXMOD,MAXMANS rem overflow bounds for data structure. TRUE = -1 : FALSE = 0 rem set booleans. TABC = CHR$(9) rem set TAB character constant. MAXMANS = 200 rem up to 200 manufacturers MAXMOD = 300 rem up to 300 terminal models. MAXCOD = 200 rem up to 200 individual codes. DIM MANU$(MAXMANS),MANU%(MAXMANS)rem if translating to another language DIM MODEL$(MAXMOD),MODEL%(MAXMOD)rem these could be current-size+20. DIM CODE$(MAXCOD) rem (CB80 uses dynamic strings). REM ========================================================================== REM THE FUNCTIONS USED THROUGHOUT THIS PROGRAM ARE DEFINED HERE. REM ========================================================================== rem ------------------------------------------------------------------ rem | HEXNUM: converts a value between 0-15 into a character bet. 0-Fh | rem | NUMHEX: converts a char. bet 0-Fh into an integer bet 0-15 dec. | rem ------------------------------------------------------------------ DEF HEXNUM(VALU%) STRING HEXNUM IF VALU% < 10 \ rem value better be bet. 0-15 dec. THEN HEXNUM = CHR$(VALU% + 48) \ ELSE HEXNUM = CHR$(VALU% + 55) FEND DEF NUMHEX(HEX$) INTEGER NUMHEX IF ASC(HEX$) > 57 \ rem better be bet. 0-Fh. THEN NUMHEX = ASC(HEX$) - 55 \ ELSE NUMHEX = ASC(HEX$) - 48 FEND rem ---------------------------------------------------- rem | CLRSCR: send out a specified number of CRLF's. | rem | TABS: return number of spaces specified in string. | rem ---------------------------------------------------- DEF CLRSCR(NUM.LIN%) FOR X% = 1 TO NUM.LIN% PRINT NEXT FEND DEF TABS(SPACE%) STRING TABS rem maximum of 35 spaces. TABS = LEFT$(" ",SPACE%) FEND rem --------------------------------------------------------------- rem | SCAN: find the number of occurences of one string in another. | rem --------------------------------------------------------------- DEF SCAN(BASE$,PATRN$) INTEGER SCAN COUNT% = -1 : POS% = 1 WHILE POS% POS% = MATCH(PATRN$,BASE$,POS%) IF POS% THEN POS% = POS% + 1 COUNT% = COUNT% + 1 WEND SCAN = COUNT% FEND rem ------------------------------------------------------------------------ rem | VALIDF: checks file name for CP/M validity. Also shifts to upper case. | rem ------------------------------------------------------------------------ DEF VALIDF(FNAME$) STRING VALIDF rem returns null-string if invalid. WHILE LEFT$(FNAME$,1) = " " FNAME$ = RIGHT$(FNAME$,LEN(FNAME$)-1) WEND rem deblank name on left. IF FNAME$ = "" \ rem null is not allowed. THEN GOTO BADN FOR X% = 1 TO LEN(FNAME$)rem control chars are not allowed. IF ASC(MID$(FNAME$,X%,1)) < 32 \ THEN GOTO BADN NEXT TMP% = MATCH(":",FNAME$,1)rem a drive specifier is allowed. IF TMP% > 2 OR TMP% = 1 \ rem must be in 2nd column. THEN GOTO BADN TMP% = MATCH(".",FNAME$,1)rem a '.' seperator is allowed. IF TMP% <> 0 AND TMP% < (LEN(FNAME$) - 3) \ THEN GOTO BADN rem must be followed by max. 3 chars. IF TMP% = 0 \ rem normalize file name. THEN FNAME$ = FNAME$ + ". " \ ELSE FNAME$ = FNAME$ + TABS(3-(LEN(FNAME$)-TMP%)) IF SCAN(FNAME$,".") > 1 \ THEN GOTO BADN rem only one seperator allowed. IF SCAN(FNAME$,":") > 1 \ THEN GOTO BADN rem only one drive spec. allowed. VALIDF = UCASE$(FNAME$) rem shift to upper case. RETURN BADN: VALIDF = "" rem error return. FEND rem ------------------------------------------------------------------- rem | YES: asks yes/no question specified, and is true if answered Yes. | rem ------------------------------------------------------------------- DEF YES(OUT$) rem OUT$ is question to ask. INTEGER YES PRINT OUT$; : TEST% = INKEY IF UCASE$(CHR$(TEST%)) = "Y" \rem defaults to no. THEN YES = TRUE : PRINT "Y" \ ELSE YES = FALSE : PRINT "N" FEND rem =================================================================== rem | START: main program. Reads TERMS.DM first, then gets file name to | rem | write code to. Shows scrollable list of terms, and gets | rem | code to write. Verifies selection, and writes code. | rem =================================================================== START: PRINT "---------------------------------------------------" PRINT "INSTALL - write a DM80 control code to selected" PRINT " file. 1982 Digital Research, Inc." PRINT "---------------------------------------------------" GOSUB CRTRM rem read in control codes. FTRY: PRINT INPUT "Enter file to write control code to: ";LINE FILE$ IF FILE$ = "" THEN STOP rem ^C and null exit program. FILE$ = VALIDF(FILE$) rem validate under CP/M. IF FILE$ = "" THEN \ rem error return from VALIDF. PRINT "bad file name, retry." : GOTO FTRY IF END #2 THEN GETSEL rem find out if file exists. OPEN FILE$ AS 2 rem if it does, tell them. IF NOT YES("File exists, overwrite(Y/N)? ") THEN \ CLOSE 2 : GOTO FTRY DEL% = TRUE rem will delete when ready to write. GETSEL: GOSUB LIST rem show scrollable list. CALL CLRSCR(24) rem clear screen of list. PRINT "Manufacturer is ";MID$(MANU$(SELMAN%),5,25) PRINT "Model name is ";MODEL$(SELMOD%) TMP$ = CODE$(MODEL%(SELMOD%)) rem give vital stats on selection. PRINT "Control code is:" WHILE TMP$ <> "" rem make it readable size, for 52 col. PRINT LEFT$(TMP$,52) rem screens and larger. TMP$ = MID$(TMP$,53,LEN(TMP$)) WEND PRINT IF NOT YES("Write code(Y/N)? ") THEN \verify selection. IF YES("Retry(Y/N)? ") \rem if not happy, want to make new sel? THEN GOTO GETSEL \ ELSE STOP rem no, so quit. IF DEL% THEN DELETE 2 rem if want to write it, delete old. IF END #3 THEN ERR.FO CREATE FILE$ AS 3 rem make new copy of file. PRINT USING "&";#3;CODE$(MODEL%(SELMOD%)) CLOSE 3 rem write it, and close file. PRINT "Code written to ";FILE$ rem tell them and stop. STOP rem ========================================================================= rem | CRTRD: open terminal file, and read in. Note data structure: | rem | MANU$ = manufacturer name. MANU% = pointer to last model in group. | rem | MODEL$ = model name. MODEL% = pointer to control code for model. | rem | CODE$ = control code deblanked. | rem | Note: 0th manufacturer is faked, so that real first group starts with | rem | model number 1. | rem | FILE: the terminal file TERMS.DM has two parts, the Display Manager | rem | section, and the user supported section. DM80SET will change the | rem | user supported section only. | rem | The DM80 part has models bundled; ie. all models with the same code are| rem | listed on the same line, seperated by a semi-colon. Each manufacturer is| rem | given a letter/number code. The letter is the first letter in the Manu- | rem | facturer's name. All models of the same manufacturer are tied together | rem | by this code. The control code is on the lines after the manufacturer/ | rem | model line. The control code may take from one to 6 lines, and is always| rem | followed by a blank line. The preceding blanks/tabs are removed. | rem | The user supported terminals are essentially the same, except that the | rem | models are never bundled, and the manufacturer's code is two numbers. | rem | EXAMPLE: | rem | (Header - 4 lines) | rem | A1: MANUFACTURER # 1 ,MODEL 1;MODEL 2;MODEL 3 | rem | /CONTROL_CODE_______________________________ | rem | ___________________________________________ | rem | ____END_OF_CONTROL_CODE | rem | | rem | A1: MANUFACTURER # 1 ,MODEL 4 | rem | . | rem | . | rem | . | rem | | rem | You will note that all 4 models of manufacturer 1 will be grouped tog- | rem | ether. The comma before the model is the seperator for Manu./Model. | rem | With user supported terms, the word UNDEFINED means that no terminal | rem | has been assigned to that manufacturer number. | rem ========================================================================= CRTRM: IF END # 1 THEN ERR.NT rem open terminal code list. OPEN "TERMS.DM" AS 1 rem only DM80SERT should write to it. READ #1; LINE HEAD$ rem header and copyright notice. READ #1; LINE HEAD$ rem file integrity notice. READ #1; LINE HEAD$ rem blank line, or more header info. READ #1; LINE HEAD$ rem table headings. MANU$(0) = "FAKE MANUAFACTURER" rem create a fake group ending at 1 MANU%(0) = 0 rem for manufacturer model grouping CURMOD% = 1 rem list of models in groups. 1st at 1. CURCOD% = 1 rem list of codes, pointed to by mods. IF END # 1 THEN ENDR.1 rem read manufacturer, model, code. FOR CNT% = 1 TO MAXMANS rem theoretical maximum codes. READ #1; MANU$(CNT%) rem must always check integrity of file IF LEN(MANU$(CNT%)) < 6 \em whenever possible. THEN GOTO ERR.BT IF MATCH("!#: ",MANU$(CNT%),1) <> 1 \manufacturer number code: THEN GOTO USR.1 rem ie. A2: for ADDS's terms. IF MID$(MANU$(CNT%),1,2) = MID$(MANU$(CNT%-1),1,2) \ THEN CNT% = CNT% - 1 rem still same manufacturer. READ #1; TMP$ rem must unbundle models w/same code. IF LEN(TMP$) = 0 \ rem check valid model name. THEN GOTO ERR.BT MTCH% = TRUE : TMP$ = TMP$ + ";" WHILE MTCH% rem unbundle models, seperated by ';'s. MTCH% = MATCH(";",TMP$,1) MODEL$(CURMOD%) = LEFT$(TMP$,MTCH%-1) MODEL%(CURMOD%) = CURCOD% rem they all use same c code. CURMOD% = CURMOD% + 1 IF CURMOD% > MAXMOD \model name overflow. THEN GOTO ERR.TF TMP$ = MID$(TMP$,MTCH%+1,LEN(TMP$)) IF LEN(TMP$) = 0 \m unbundling done. THEN MTCH% = FALSE WEND MANU%(CNT%) = CURMOD% - 1 rem last model in manufacturer group. GOSUB GET.COD rem gets control code, and concat.s it. NEXT CNT% rem when EOF, will goto ENDR.1. GOTO ERR.TF rem overflow of manufacturers. GET.COD:CODE$(CURCOD%) = "" rem build code up 250 chars. rem ****note: blank line indicates the end of the control code. rem **** As an extra precaution, the size of the code is tested. READ #1; LINE TMP$ rem read code in parts. WHILE LEFT$(TMP$,1) = TABC \ rem in case blank line isn't null, OR LEFT$(TMP$,1) = " " rem codes must start with tab/blank. WHILE LEFT$(TMP$,1) = " " OR \deblank the code of preceding LEFT$(TMP$,1) = TABC rem spaces and tabs. TMP$ = RIGHT$(TMP$,LEN(TMP$)-1) WEND CODE$(CURCOD%) = CODE$(CURCOD%) + TMP$ IF LEN(CODE$(CURCOD%)) > 250 \concatenate each line, till done. THEN GOTO ERR.BC rem if code too large, prob. bad file. READ #1; TMP$ rem read lines till empty line found. WEND CURCOD% = CURCOD% + 1 IF CURCOD% > MAXCOD \ rem code overflow THEN GOTO ERR.TF RETURN rem got code, so return. rem -------------------------------------------------------------------- rem | USR.1: since user supported terms treated differently, handle here | rem -------------------------------------------------------------------- USR.1: IF LEFT$(MANU$(CNT%),5) <> "=====" \the ='s means user supps follow. THEN GOTO ERR.BT rem if not users, then bad file integ. READ #1; LINE HEAD$ rem if user supps, read out headers. READ #1; LINE HEAD$ READ #1; LINE HEAD$ USER% = CNT% FOR CNT% = USER% TO MAXMANS rem user supps are never bundled. READ #1; MANU$(CNT%) IF MID$(MANU$(CNT%),5,9) = "UNDEFINED" THEN \ MANU%(CNT%) = MANU%(CNT%-1) : \ GOTO NXT1 rem if undefined, then no model/code. MANU%(CNT%) = CURMOD% rem no previous manu testing done here. READ #1; MODEL$(CURMOD%) MODEL%(CURMOD%) = CURCOD% CURMOD% = CURMOD% + 1 IF CURMOD% > MAXMOD \ THEN GOTO ERR.TF GOSUB GET.COD rem removes extra CRLF's and blanks. NXT1: NEXT GOTO ERR.TF rem manufacturer overflow. ENDR.1: ENDL% = CNT% rem EOF encountered. this is OK. RETURN rem set end of list pointer, return. rem ========================================================================= rem | LIST: show all terminals and models in a scrollable list. | rem | Listed output format: | rem | | rem | Manufacturer: MANUFACTURER NAME | rem | -#- --model-- -#- --model-- | rem | A11 MODEL NAME A12 MODEL NAME | rem | A13 MODEL NAME | rem | | rem | Manufacturer: etc. | rem | (note that this fits in 52 columns.) | rem | The commands used are: | rem | ^Z = scroll down to next page. ^W = scroll up to previous page. | rem | ESC = abort program. BS, ^S = remove previous char(not control chars.) | rem | Letter/Number = part of model code number. Note that third digit is | rem | a hexidecimal value, so up to 16 models can be shown. | rem | Once a number is selected, it is verified. If OK, then the model num- | rem | ber, manufacturer number, and code is returned via SELMAN% & SELMOD% | rem ========================================================================= LIST: LAST% = 0 : FLAG% = 0 rem keeps track of page size. FOR CNT% = 1 TO MAXMANS rem uses ENDL% for terminator. IF LAST% < 22 \ rem fill page with data. THEN GOTO CONT1 CHOOSE: LAST% = 0 : NUM$ = "" rem get input. NUM$ is selection. PRINT "(^Z scrolls down, ^W scrolls up, ESC quits)" RETR: PRINT "Enter ^Z, ^W, ESC or Terminal #: ";NUM$; CON.IN: SEL% = INKEY rem note how this reused during input. IF SEL% = 27 THEN GOTO ESC1 rem escape key, abort. IF SEL% = 8 OR SEL% = 19 \m BS, delete backwards up to prompt. THEN IF NUM$ = "" THEN GOTO CON.IN \ ELSE NUM$ = LEFT$(NUM$,LEN(NUM$)-1) : \ PRINT CHR$(8);" ";CHR$(8); : \ GOTO CON.IN rem get more input. IF SEL% = 26 THEN \ rem ^Z scrolls down. IF CNT% >= ENDL% THEN GOTO CON.IN : \if at end, ignore. ELSE : PRINT : \ FLAG% = 1 : GOTO CONT1 IF SEL% <> 23 THEN GOTO VALID rem ^W scrolls up. IF NOT FLAG% \ THEN GOTO CON.IN PRINT rem note that this is approximate. LAST% = MANU%(CNT%) TMP% = LAST% - 40 rem if at top, still redraws. WHILE LAST% > TMP% rem the data structure does not make CNT% = CNT% - 1 rem this easy to figure out. IF CNT% < 1 THEN \don't go through roof. CNT% = 1 : \ FLAG% = 0 : GOTO CONT2 LAST% = LAST% - 3 - (MANU%(CNT%) \ - MANU%(CNT%-1) + 1) / 2 WEND rem back up by calculating backwards. IF LAST% > TMP% \rem split draw doesn't happen down. THEN CNT% = CNT% + 1 CONT2: LAST% = 0 rem redraw. GOTO CONT1 VALID: SEL$ = UCASE$(CHR$(SEL%))rem check if valid manu. number. IF LEN(NUM$) >= 3 \ rem can't be over 3 chars. THEN SEL$ = " " rem this caused by not found case. IF NOT (MATCH("!",SEL$,1) OR MATCH("#",SEL$,1)) THEN \ PRINT TABS(5-LEN(NUM$+SEL$));"BAD ENTRY";CHR$(13); : \ GOTO RETR rem must be number or letter. NUM$ = NUM$ + SEL$ rem build input number. PRINT SEL$; rem if ok, show it. IF LEN(NUM$) < 3 THEN GOTO CON.IN GOTO FIND rem got num, now find code. CONT1: IF CNT% >= ENDL% THEN \ rem still displaying terms. PRINT "END OF LIST" : \ CALL CLRSCR(21-LAST%) : \ GOTO CHOOSE rem end of list is special case. IF MID$(MANU$(CNT%),5,9) = "UNDEFINED" \ THEN GOTO NXT rem don't try to show undefined terms. TMP% = (MANU%(CNT%) - MANU%(CNT%-1) + 1)/2 + LAST% + 3 IF TMP% > 22 \ rem don't split manufacturers models. THEN CALL CLRSCR(22 - LAST%) : \ GOTO CHOOSE rem just wait till next page. PRINT "Manufacturer: ";MID$(MANU$(CNT%),5,25) PRINT "-#- --model-- -#- --model--" LEFT% = TRUE rem for column alignment. FOR CNT2% = MANU%(CNT%-1)+1 TO MANU%(CNT%) PRINT LEFT$(MANU$(CNT%),2); rem give codes for select. PRINT HEXNUM(CNT2%-MANU%(CNT%-1)); PRINT " ";LEFT$(MODEL$(CNT2%),20); IF LEFT% \ rem just keep alternating alignments. THEN PRINT TAB(28); : \ LEFT% = FALSE \ ELSE PRINT : LEFT% = TRUE : \ LAST% = LAST% + 1 NEXT rem incr. LAST% only after two shown. IF LEFT% = FALSE THEN \ rem reset to next line if split. PRINT : LAST% = LAST% + 1 PRINT rem blank line after each manufacturer. LAST% = LAST% + 3 rem 3 for manu-name,header, blank line. NXT: NEXT rem should never drop out of here. GOTO LIST rem if does, do a full wrap. ESC1: PRINT : PRINT "ABORTED" rem they pressed ESC. STOP FIND: FOR CNT2% = 1 TO ENDL% rem they entered 3 chars, now find code IF LEFT$(NUM$,2) = LEFT$(MANU$(CNT2%),2) \ THEN GOTO FOUND rem for it. Note that they might have NEXT rem selected one far from those shown. NOGOOD: PRINT " NOT FOUND";CHR$(13); : \rem if not verified, allow retry. GOTO RETR rem note that still on prompt line. FOUND: CNT3% = NUMHEX(RIGHT$(NUM$,1)) + MANU%(CNT2%-1) IF CNT3% > MANU%(CNT2%) OR CNT3% = MANU%(CNT2%-1) \ THEN GOTO NOGOOD rem can't be 0, or larger than those SELMAN% = CNT2% rem found. This allows G,H,I,etc. SELMOD% = CNT3% rem this is the selection values. RETURN rem ======================================== rem | ERROR ROUTINES CALLED FROM CODE ABOVE. | REM ======================================== ERR.NT: PRINT "ERROR: file TERMS.DM not found." STOP ERR.TF: PRINT "ERROR: overflow in reading TERMS.DM." STOP ERR.BT: PRINT "ERROR: damaged control code file - TERMS.DM" PRINT " (use backup copy from master disk)" STOP ERR.BC: PRINT "ERROR: control code overflow - TERMS.DM" PRINT " (file probably damaged, use backup)" STOP ERR.FO: PRINT "ERROR: can't create file - ";FILE$ PRINT " (probably disk full)" STOP