************************************************
*
*  SUBROUTINES
*
*
*  ^SYSOPEN - \OPEN ^SYSGLOBL AND UNPROTECT IT.
*
SYSOPEN LXI D,SYSNAME  LOCATION OF NAME "SYSGLOBL".
 LXI H,0  STATIC BUFFERING FOR OPEN.
 CALL SYS  TRY TO OPEN FILE.
 DB OPEOP
 CALL PTDER
 STA FILENUM  STORE FILE #.
*
 LXI D,GLFCB  GET THE ^FCB BASE TO ZAP ^SYSGLOBL' S ATTRIBS.
 CALL SYSGET
 LXI D,FCBLEN  MOVE TO THE PROPER ^FCB.
 LDA FILENUM  GET NUMBER OF \F\C\BS TO SKIP.
 INR A  FUDGE IT.
*
OPENLOOP DCR A  A=0 MEANS DONE SKIPPING.
 JZ ZAPATRB
 DAD D  MOVE ANOTHER ^FCB FORWARD.
 JMP OPENLOOP
*
ZAPATRB LXI D,ATRBPOS  MOVE TO ATTRIBUTES IN ^FCB.
 DAD D
 MVI M,0  ^ZAP!!!
 RET
*
SYSNAME ASC "SYSGLOBL/"  FILE NAME FOR OPEN.
UNIT DS 1  CONFIGURE UNIT GETS STORED DIRECTLY INTO NAME.
 DB 0
*
*
*  SYSCLOSE - \TRY TO CLOSE ^SYSGLOBL
*
SYSCLOSE LDA FILENUM  GET FILE # TO CLOSE.
 CPI 0FFH  IF FILE IS NOT OPEN
 RZ .  THEN DON'T DO ANYTHING.
*
 CALL SYS  DO THE CLOSE.
 DB CLOOP
 JMP OHNO  NO ERROR SHOULD OCCUR, BUT...
 MVI A,0FFH  INDICATE FILE WAS CLOSED.
 STA FILENUM
 RET .  IF FILE WAS CLOSED, THAT'S IT.
*
OHNO LDA FILENUM  TRY TO WRITE OUT NEW DATA
 MVI D,1  BY SPACING 0 BYTES.
 LXI B,0
 CALL SYS
 DB SPAOP
 JMP ONCEMORE  STILL IN TROUBLE.
*
ONCEMORE LDA FILENUM  MAYBE IT WILL CLOSE NOW.
 CALL SYS
 DB CLOOP
 JMP GIVEUP  IF NOT NOW, NEVER.
 MVI A,0FFH  INDICATE FILE WAS CLOSED.
 STA FILENUM
 RET
*
GIVEUP CALL CRLF
 LXI D,GIVEMESG  TELL USER THAT CLOSE WAS NO GO.
 CALL MESG
 RET
*
GIVEMESG ASC "Can't close SYSGLOBL; changes may not"
 ASC " have been made."
 DB 0
*
*
*  INPUT - \READS CONSOLE INPUT UNTIL CR
*   
INPUT LXI D,BUFFER  WHERE TO READ DATA INTO.
 MVI C,10  MAXIMUM CHARACTER READ COUNT.
  MVI A,' '  PRINT A BLANK FIRST TO SEPARATE
  CALL CONOUT
*   
INLOOP CALL CONIN  GET A CHAR.
 ANI 7FH  REMOVE PARITY BIT
 JZ RETURN  AND TEST FOR QUIT.
 CPI CR  CHECK FOR INPUT DONE.
 JZ INDONE
 CPI RUBOUT  SEE IF USER WANTS TO DELETE A CHAR.
 JZ BACKUP
 DCR C  CHECK COUNT.
 JZ TOOMANY  IF MORE THAN 10 THEN TOO MANY CHARS.
 STAX D  CHAR WAS OK SO SAVE IT.
 INX D
 CALL CONOUT  ECHO IT.
 JMP INLOOP  GO BACK FOR MORE.
*   
BACKUP MVI A,10  IF NO INPUT IN BUFFER
 CMP C
 JZ INLOOP  THEN DON'T DO ANYTHING; GO GET ANOTHER CHAR.
 INR C  BACKUP THE COUNT
 DCX D  AND THE BUFFER POINTER.
 MVI A,BACKCHAR  AND THE CONSOLE.
 CALL CONOUT
 JMP INLOOP  GO GET MORE.
*   
TOOMANY LXI D,OVERFMESG  PRINT " Input too long".
 CALL MESG
 STC .  INDICATE ERROR.
 RET
*   
OVERFMESG ASC " Input too long"
 DB 0
*
INDONE XRA A  STORE 0 AT END OF INPUT; DON'T ECHO ^CR.
 STAX D
 INX D  CONINUE ZERO FILLING BUFFER
 DCR C  CHECK COUNT.
 JNZ INDONE  LOOP UNTIL BUFFER IS ZERO FILLED.
*
 LDA BUFFER  CHECK IF ANY CHARACTERS WERE INPUT.
 ORA A
 RET .  ZERO FLAG IS SET PROPERLY, CARRY IS CLEAR.
*
*
*  MAP -  UPSHIFTS ALL LOWER CASE LETTERS IN BUFFER.
*
MAP LXI D,BUFFER  WHERE CHARS ARE.
 DCX D  FUDGE ADDRESS.
*
MAPLOOP INX D  MOVE TO NEXT CHAR
 LDAX D  AND GET IT.
 ORA A  ARE WE DONE?
 RZ .  LOOKS THAT WAY.
 CPI 'a'
 JC MAPLOOP  CHAR < 'a'.
 CPI 'z'+1
 JNC MAPLOOP  CHAR > 'z'.
 ADI 'A'-'a'  MAKE UPPER CASE.
 STAX D
 JMP MAPLOOP
*
*
*  READ - \GET DATA FROM ^SYSGLOBL.
*   
READ CALL REWIND  REWIND ^SYSGLOBL.
 CALL SPACE  SPACE TO DATA WANTED; COUNT IN BC.
 MOV B,D  MOVE NUMBER OF BYTES WANTED TO BC.
 MOV C,E
 LXI D,BUFFER  READ DATA IN HERE.
 LDA FILENUM
 CALL SYS
 DB RBLOP
 CALL PTDER
 XRA A  PUT A 0 AT END OF BUFFER.
 STAX D
 RET
*
*
*  WRITE - \PUT DATA IN ^SYSGLOBL
*   
WRITE CALL REWIND  GO TO BEGINNING OF ^SYSGLOBL.
 CALL SPACE  MOVE TO WHERE DATA IS TO BE WRITTEN.
 MOV B,D  PUT # OF BYTES TO WRITE INTO BC.
 MOV C,E
 LXI D,BUFFER  WHERE THE DATA IS.
 LDA FILENUM
 CALL SYS
 DB WBLOP
 CALL PTDER
 RET
*
*
*  REWIND
*
REWIND PUSH D  SAVE DE
 MVI D,0  REWIND OP FOR SPACE.
 LDA FILENUM
 CALL SYS
 DB SPAOP
 CALL PTDER
 POP D  RESTORE DE.
 RET
*   
*
*  SPACE
*
SPACE PUSH D  PRESERVE DE
 MVI D,1  SPACE FORWARD THE AMOUNT IN BC.
 LDA FILENUM
 CALL SYS
 DB SPAOP
 CALL PTDER
 POP D  RESTORE DE.
 RET
*
*
*
*  MESG - PRINT A MESSAGE FOLLOWED BY A CR AND LF
*   
MESG CALL PRINT  PRINT THE MESSAGE DELIMITED BY 0.
 CALL CRLF  GO TO NEXT LINE.
 RET
*
*
*  PRINT - \SEND MESSAGE TO CONSOLE.
*
PRINT LDAX D  ADDRESS OF STRING IS IN DE.
 ORA A  CHECK FOR DELIMETER.
 RZ .  IF ZERO BYTE THEN DONE.
 CALL CONOUT  PRINT A CHAR.
 INX D  MOVE TO NEXT CHAR.
 JMP PRINT  LOOP UNTIL DONE.
*
*
*  CRLF
*
CRLF MVI A,CR  FIRST SEND A CR TO THE CONSOLE.
 CALL CONOUT
 MVI A,LF  THEN A LF.
 CALL CONOUT
 RET
*
*
*  ^ZERO - \ZEROES OUT PSCAN BUFFER; MEANS NO PASSWORD
*
ZERO MVI C,20  LENGTH OF BUFFER.
 XRA A  MAKE THE ZERO TO ZAP WITH.
 LXI D,PBUFF  WHERE THE ZEROES GO.
*
ZAPBUFF STAX D  ^ZAP!!!
 INX D  MOVE TO NEXT BYTE.
 DCR C  COUNT
 JNZ ZAPBUFF  UNTIL 0
 RET .  THEN RETURN.
*
*
*
*  HEXTOASCII
*
HEXTOASCII LHLD BUFFER  ASSUME NUMBER IS VALID IN BUFFER.
 MOV B,H  CONVERT HIGH BYTE.
 CALL ASC2
 MOV A,D  TAKE FROM DE AND STORE BACK IN BUFFER.
 STA BUFFER
 MOV A,E
 STA BUFFER+1
 MOV B,L  CONVERT LOW BYTE.
 CALL ASC2
 MOV A,D  PUT IN BUFFER ALSO.
 STA BUFFER+2
 MOV A,E
 STA BUFFER+3
 XRA A  FOLLOW WITH A ZERO
 STA BUFFER+4
 RET .  ALL DONE
*
*   
ASC2 MVI A,0F0H  MASK OFF LOW DIGIT.
 ANA B
 RRC .  SHIFT HIGH DIGIT DOWN TO LOW HALF OF BYTE.
 RRC
 RRC
 RRC
 CALL ASKEE  MAKE IT ASCII.
 MOV D,A  SAVE RESULT.
 MVI A,0FH  NOW SO LOW DIGIT.  MASK HIGH DIGIT OFF.
 ANA B
 CALL ASKEE  ASCII-IZE.
 MOV E,A  SAVE.
 RET
*
*
ASKEE CPI 0AH  FIND WHETHER DIGIT IS LETTER OR NUMBER.
 JNC LETTER
 ORI 30H  MAKE A NUMBER DIGIT ASCII.
 RET
*
LETTER ADI 'A'-0AH  MAKE A LETTER DIGIT ASCII.
 RET
*
*   
*
*  ASCIITOHEX
*
ASCIITOHEX LXI H,0  INITIALIZE SUM.
 LXI B,BUFFER  BC POINTS TO CURRENT INPUT DIGIT
*
ASCLOOP LDAX B  GET A DIGIT.
 ORA A  TEST FOR NUMBER DONE.
 JZ ASCDONE  YEP.
 CALL DEASCII  TURN CHARACTER INTO A NUMBER.
 RC .  CARRY RETURN MEANS ERROR.
 DAD H  ROTATE SUM LEFT 4 BITS.
 RC .  CARRY MEANS OVERFLOW OCCURED AND ERROR.
 DAD H
 RC
 DAD H
 RC 
 DAD H
 RC
 ORA L  MAKE A NEW LOW BYTE OF SUM
 MOV L,A  AND REPLACE IT
 INX B  GO TO NEXT CHARACTER IN NUMBER
 JMP ASCLOOP  AND GO BACK AND PROCESS IT.
*
ASCDONE SHLD BUFFER  PUT RESULT BACK IN BUFFER.
 XRA A  FOLLOW WITH 0 TO BE SAFE (ALSO RESET CARRY).
 STA BUFFER+2
 RET
*   
DEASCII SUI '0'  REMOVE ASCII BIAS AND CHECK FOR < '0'.
 RC .  BAD DIGIT.  CARRY SIGNALS ERROR.
 CPI 0AH  CHECK IF IT IS A LETTER DIGIT.
 CMC .  SO THAT CHECK CAN RETURN WITH CARRY CORRECT.
 RNC .  IT WAS A NUMBER DIGIT AND SO IS NOW OK.
 SUI 7  SUBTRACT OFFSET OF LETTERS FROM NUMBERS
 CPI 0FH+1  CHECK FOR DIGIT > F
 CMC .  GET CARRY RIGHT.
 RET .  IF CARRY SET THEN ILLEGAL DIGIT.
*
*
*
*  ASCDECTOHEX
*
ASCDECTOHEX LXI H,BUFFER  WHERE THE NUMBER IS.
 LXI D,PBUFF  SCRATCH BUFFER FOR PSCAN.
 MVI A,PSCN   CONVERT #, POINTED TO BY HL OF BASE B, INTO DE.
 MVI B,10  IT IS A DECIMAL NUMBER.
 CALL PSCAN  CONVERT!
 RC .  IF ERROR RETURN WITH CARRY SET.
 XCHG .  STORE NEW VALUE IN BUFFER.
 SHLD BUFFER
 RET .  WE KNOW CARRY IS NOT SET.
*   
*  Subroutine written by Mike Sherman
*   
*  DOUT writes the contents of HL to the COFILE as a decimal
*  number between -32768 and 32767 inclusive.  Leading zeroes
*  and + signs are not printed.
*       Calls DSUB to subtract DE from HL.
*       Calls PUTCHR for output to the COFILE.
*       All registers and flags are returned unchanged.
*   
DOUT PUSH PSW
 PUSH B
 PUSH D
 PUSH H
 MVI C,0  Flag means "don't print zeroes" initially.
 MOV A,H  Find out if we have a negative number.
 ORA A
 JP DOUT0  Nope.
 CMA  Set HL=-HL (two's complement)
 MOV H,A
 MOV A,L
 CMA
 MOV L,A
 INX H
 MVI A,'-'
 CALL PUTCHR  Print the minus sign.
*
DOUT0 LXI D,10000  First the 10000's digit gets printed.
 CALL DOUT1
 LXI D,1000  Then the 1000's.
 CALL DOUT1
 LXI D,100  The 100's.
 CALL DOUT1
 LXI D,10
 CALL DOUT1
 MOV A,L  Get the one's digit.
 ADI 48  Make it ascii.
 CALL PUTCHR  Print it.
 POP H
 POP D
 POP B
 POP PSW
 RET
*   
*                             Subroutine written by Mike Sherman
*   
*  This subroutine subtracts DE from HL as many times as it
*  will go before HL becomes negative.  Then DE is added back
*  on, and the number of times it went is printed unless it
*  was zero.  If DE is set to powers of any base<=10 then this
*  routine can be used to print a hex number in that base.
*  HL is left as it was just before it went negative.
*       A,B and HL are the only registers affected.
*   
DOUT1 MVI B,0  Count # of subtractions performed.
*
DOUT2 CALL DSUB  HL=HL-DE
 MOV A,H
 ORA A
 JM DOUT3  We're done if HL was negative.
 INR B
 JMP DOUT2
*
DOUT3 DAD D  Fix HL.
 MOV A,B  Get the digit.
 ORA C  If A is zero and we haven't printed anything else,
 RZ .  then don't print this zero.
 MOV A,B  Get the digit back again.
 ADI 48  Make it ascii.
 INR C  Remember that we printed something.
 CALL PUTCHR
 RET
*   
*                             Subroutine written by Mike Sherman
*   
*  DSUB is just like the DAD instruction except that it
*  subtracts DE from HL instead of adding it.  Only HL
*  is changed.  Flags are returned as they were.
*
DSUB PUSH PSW
 PUSH D
 MOV A,D  First set D=-D (two's complement).
 CMA
 MOV D,A
 MOV A,E
 CMA
 MOV E,A
 INX D
 DAD D  Add HL to -D.
 POP D
 POP PSW
 RET
*   
*   
*
*  PUTCHR
*   
PUTCHR PUSH PSW  DON'T CHANGE REGISTERS.
 PUSH B
 CALL CONOUT  PRINT CHAR TO CONSOLE.
 POP B  RESTORE REGISTERS.
 POP PSW
 RET
*   
*   
*   
*   
*  PTDER - \HANDLES PTDOS ERRORS
*   
PTDER STA ERMCD  STORE ERROR # FOR ERROR UTILITY.
 POP H  GET RETURN ADDRESS.
 DCX H  MOVE BACK TO COMMAND CODE.
 DCX H
 DCX H
 DCX H
 MOV A,M  GET ^CALL ^SYS OP.
 STA COMCD  STORE FOR ERROR UTILITY.
 PUSH H  PUT UPDATED RETURN ADDRESS BACK (NEVER RETURNED TO).
*
FINISH CALL SYSCLOSE  CLOSE ^SYSGLOBL.
 CALL TRAPRESET  PUT ERROR TRAPS BACK.
*
 IF DEBUG  THEN PRINT "CALLED FROM <ADDRESS>"
 POP H  RETURN ADDRESS.
 DCX H  MOVE TO ADDRESS OF CALL.
 DCX H
 DCX H
 MVI A,3  UTILITY OP.
 ENDF
*
 IF NOTDEBUG  PRINT "CALLED FROM CONFIG".
 LXI H,CONFIG  POINTER TO MESSAGE "CONFIG"
 MVI A,2  UTILITY OP; CALLED FROM AND HL -> STRING.
 ENDF
*
 CALL UTIL  EXPLAIN ERROR AND RETURN TO SYSTEM.
 DB UXOP
 JMP ABORT  ERROR RETURN (CAN'T HAPPEN).
COMCD DB -1  COMMAND OP
ERMCD DB -1  ERROR #
ABORT CALL SYS  NORMAL RETURN (CAN'T HAPPEN EITHER).
 DB ABTOP
*
CONFIG ASC "CONFIGR"
 DB 0
*
*
PSCER MOV A,E  TEST TYPE OF PSCAN ERROR.
 ORA A  CHECK FOR 0.
 JZ ARGERROR  IF FIELD ERROR FROM PSCAN...
 STA ERMCD  SAVE ERROR # FOR ERROR UTILITY.
 JMP FINISH  GO PROCESS ERROR.
*   
*
ARGERROR LXI D,AERRMESG  ARGUMENT ERROR MESSAGE.
 JMP RSET  PRINT IT AND RETURN.
AERRMESG ASC "Argument error"
 DB 0
*
BADPASSW LXI D,WRONGMESG  PASSWORDS DON'T MATCH.
 JMP RSET
WRONGMESG ASC "Incorrect password"
 DB 0
*
*
RSET CALL MESG  PRINT ERROR MESSAGE.
 CALL SYSCLOSE  CLOSE ^SYSGLOBL.
 CALL TRAPRESET  PUT ERROR TRAPS BACK.
 CALL SYS  RETURN TO SYSTEM, RESETTING.
 DB RESOP
*
*
ERROR LXI D,ERRMESG  PRINT "ERROR"AND RETURN
 CALL MESG
 RET
ERRMESG ASC " Error"
 DB 0
*   
*
*
*
TRAPSAVE LXI D,GLERS  SAVE SOFT ERROR TRAP
 CALL SYSGET  GET TRAP FROM GLOBAL AREA INTO HL
 SHLD STRAP
 LXI D,GLERM  SAVE MEDIUM ERROR TRAP
 CALL SYSGET
 SHLD MTRAP
 RET
*
TRAPINIT LXI D,GLERS  INITIALIZE SOFT ERROR TRAP
 LXI H,-1  TO RETURN TO CALLER
 CALL SYSPUT
 LXI D,GLERM  INIT MEDIUM ERROR TRAP
 LXI H,-1  TO RETURN
 CALL SYSPUT
 RET
*
TRAPRESET LXI D,GLERS  RESTORE SOFT ERROR TRAP
 LHLD STRAP
 CALL SYSPUT
 LXI D,GLERM  RESTORE MEDIUM ERROR TRAP
 LHLD MTRAP
 CALL SYSPUT
 RET
*
*
SYSGET LHLD SYSGLO  GET 16-BIT VALUE FROM GLOBAL AREA
 DAD D  OFFSET IS IN DE
 MOV A,M  GET LOW BYTE
 INX H
 MOV H,M  GET HIGH BYTE
 MOV L,A  VALUE ENDS UP IN HL
 RET
*
SYSPUT PUSH H  PUT 16-BIT VALUE INTO GLOBAL AREA
 LHLD SYSGLO
 DAD D
 POP D
 MOV M,E
 INX H
 MOV M,D
 RET
*
*   
*
*
*  STORAGE AREA
*
FILENUM DB 0FFH  ^SYSGLOBL FILE NUMBER (WHEN OPEN).
 DS 100  STACK AREA
STKEND EQU $  END OF STACK (TO INITIALIZE STACK POINTER).
PBUFF DS 20  BUFFER FOR USE BY PSCAN.
BUFFER DW 0  HOLDS INPUT FROM USER AND DATA FROM ^SYSGLOBL.
 DW 0
 DW 0
 DW 0
 DW 0
 DW 0
MTRAP DS 2  HOLDS OLD VALUE OF MEDIUM ERROR TRAP.
STRAP DS 2         "            SOFT    "     .
MESGADDR DS 2  ADDRESS OF WHO I AM MESSAGE FOR SWITCHES.
SPACOUNT DS 2  LOCATION OF SWITCH IN SYSGLOBL.
*
*
 END

