; ; NOTE: Due to the length of the filenames of the various support files ; updates provided by the original author will have a filetype ; that consists of 3 digits (0-9) ... these digits represent the ; version (first digit) and the revision (last two digits) numbers ; of the file. It is suggested that others follow this course... ; This file must be RENAMED to EMXMNT.MAC ... it is intended that ; this file be assembled with M80 - Simon Ewins ; ; ; --> 1.34 -- Added a message list routine that is compatable with ; version 3.13 of EMX.... The new message entry system in ; v3.13 of EMX is character-based, rather than the old ; line-based routines of earlier versions. This meant the ; actual 'typing' of the message to the console in EMXMNT ; would not work anymore.... With the new list routine we ; arrive at version 2.34. - Simon Ewins ; ; --> 1.33 -- Added a 'new and improved' sort routine that allows ; sorting on any of the user record fields and allows as- ; cending or descending sort ordering. It is important to ; note that this is an in-memory sort... this means that ; you will be able to work with CCPaddress-EMXMNTsize/100 ; records... in a 50k CP/M system this is about 389 re- ; cords.... a disk based sort will be written as soon as ; I get the time.... ; ; -- Fixed a possible bug in the message maintenance routines. a ; Possibility existed that the K)ill function would not ; always do as it was told... ; - Simon Ewins ; ;--> 1.32 -- Added limited sorting by number times on system... will ; expand further in the near future but this should take ; care of some initial requests. ; - Simon Ewins ; ;--> 1.31 -- Initial source-code release ; - Simon Ewins ; ;----------------------------------------------------------------------- ; ; EMXMNT is the file maintainence routines for the .EMX files used with ; the EMX remote e-mail series of programs. ; ; This file is for version 3.xx of EMX programs or later. ; ; Version 1.0 had some built-in routines and the file structure was ex- ; panded from 64 bytes to 100 bytes in v1.1 but v1.2 is the version that ; is compatible with the rbbs series of programs as far as file structure ; is concerned. ; ;NOTE: This program does NOT log to the .EMX user area. This serves as ; an extra level of security since if some user actually gets your ; password right he will bomb out to BYE as soon as this program ; tries to open the INDEX file. The only way to run this program ; is to have logged into the system file user area before running ; it. If your system is set up correctly, then you will be the ; only one who can log to the system file user area. ; ;----------------------------------------------------------------------- ; .Z80 ASEG ORG 100H ; ; JP START ; ; VER: DB 1 VERR: DB 34 ; ; Include header info ; INCLUDE EMXHDR.MAC ; ; And subroutines ; INCLUDE GETTIM.MAC ; ;----------------------------------------------------------------------- ; ; Start of program ; START: LD HL,0 ; Set up local stack ADD HL,SP LD (CCPSTK),HL ; Saving old one LD SP,STACK ; CALL ENDEMX ; Must call to get end of program in HL LD A,0FFH LD (HL),A ; Set flag for message area INC HL LD (MSG),HL ; Store msg pointer LD DE,MSGBUF ; Size of message buffer must match that used ; In EMX.MAC ADD HL,DE ; Offset to array pointer LD (MSGARR),HL ; Set msg array pointer LD A,0C3H ; Patch error exit to avoid chain to BYE LD HL,EXIT LD (ERROR),A ; Load error exit with jump to... LD (ERROR+1),HL ; Address of soft return CALL PRINT DB CR,LF,LF DB 'EMX Maintenance - Version ',0 LD HL,(VER) ; H=revision, L=version CALL PVER LD A,(PW) ; See if password wanted OR A JP Z,GOTPASS ; If first character of password=0 then skip ; CALL PRINT DB CR,LF DB 'Password: ',0 LD B,10 LD C,0 LD D,0 LD A,1 ; Set no echo CALL INPUT CP 0 JP Z,0 ; Nothing entered so quit LD DE,PW ; Match input to password LD B,10 ; Number bytes to search CALL MATCH CP 0 ; Match? JP NZ,0 ; No, so quit ; GOTPASS:LD HL,INDEX CALL OPEN LD HL,NDXLEN ; Record size LD (RRSZ),HL ; Place in parameter block LD HL,0 ; Record number CALL GET LD HL,IDATEF LD DE,IDATE LD BC,NDXLEN LDIR ; Move index to memory ; SYS1: CALL PRINT DB CR,LF,LF DB '0: Quit.',CR,LF DB '1: Maintain users file. 2: Maintain message file.',CR,LF DB '3: Hard-copy log on/off. 4: Pack .emx files.',CR,LF DB '5: Sort users file.',CR,LF DB LF,'Select: ',0 ; SYSWT: CALL GETCH JR Z,SYSWT CP '0' JR C,SYSWT CP '9'+1 JR NC,SYSWT CALL ECHO CP '0' JP Z,EXIT CP '1' JP Z,MUSR CP '2' JP Z,MMSG CP '3' JP Z,MHLG CP '4' JP Z,PACK CP '5' JP Z,SORT JP SYSWT ; EXIT: CALL PRINT DB CR,LF,0 ; LD HL,(CCPSTK) LD SP,HL RET ; Return to ccp stack ;..... ; ; This is the array that is used by SORT. All values are handled dynam- ; icly based on system size and size of file being sorted... as of this ; time this is an in-memory sort, a disk based sort is somewhat more ; difficult to carry out with the present structure but it is hoped that ; this will soon be possible... the selection of the sort key will fill ; in SRTOFF and SRTKLN as required, a secondary sort key should two ele- ; ments be equal is not yet available but as with a disk based sort it ; is hoped that this too will be along shortly... ; SRTARR: DW 0 ; Start address of array to sort SRTREC: DW 0 ; Number of elements to sort SRTLEN: DB SYSLEN ; Length of each record SRTOFF: DB 0 ; Offset from start of record to sort key SRTKLN: DB 0 ; Length of sort key SRTWRK: DW 0 ; Address of work area (sortbuffer-syslen) ; OVRFLW: CALL PRINT DB CR,LF,LF,BEL DB 'TPA overflowed...',CR,LF DB 'TPA is only large enough for ',0 LD HL,(RRNO) CALL PB2ASC CALL PRINT DB ' USERS.EMX records....',CR,LF,BEL,0 JP SYS1 ; SORT: CALL PRINT DB CR,LF,LF DB 'This routine will erase USERS.BAK if it is present.' DB CR,LF DB 'Continue? ',0 ; SRT0: CALL GETCH ; Erase any .bak files present JR Z,SRT0 CALL CAPS CP 'Y' JR Z,SRT1 CP 'N' JP Z,SYS1 JR SRT0 ; SRT1: CALL ECHO CALL PRINT DB CR,LF,LF,0 LD HL,USERS2 ; "USERS.BAK" CALL BFCB LD DE,FCB ; Kill USERS.BAK if there LD C,13H CALL BDOS ; CALL PRINT DB 'Renaming USERS.EMX to USERS.BAK.....',0 LD DE,RENU1 LD C,17H CALL BDOS CP 0FFH JP Z,PERR ; CALL PRINT DB CR,LF,LF DB '1: Name. 2: Location. 3: Password. 4: Signons.' DB CR,LF DB '5: Last on. 6: Access. 7: Mail flag. 8: Init. du.' DB CR,LF DB '9: Nulls. A: Baudrate. B: Uploads. C: Downloads.' DB CR,LF DB 'D: Usr map. E: Drv map. F: Terminal.' DB CR,LF,LF DB 'Enter code (x:) of field to sort on (0 to quit): ',0 ; SWT: CALL GETCH JR Z,SWT CALL CAPS CP '0' JP Z,SYS1 ; Quit JR C,SWT ; If less then ignore CALL ECHO ; Show selection CP '1' JR Z,S1 CP '2' JR Z,S2 CP '3' JR Z,S3 CP '4' JR Z,S4 CP '5' JR Z,S5 CP '6' JR Z,S6 CP '7' JR Z,S7 CP '8' JR Z,S8 CP '9' JR Z,S9 CP 'A' JR Z,SA CP 'B' JR Z,SB CP 'C' JR Z,SC CP 'D' JR Z,SD CP 'E' JR Z,SE CP 'F' JR Z,SF ; JR SWT ; S1: LD A,0 ; Offset from start of record LD B,30 ; Length of field JR SETDON ; S2: LD A,30 LD B,20 JR SETDON ; S3: LD A,50 LD B,10 JR SETDON ; S4: LD A,60 LD B,2 JR SETDON ; S5: LD A,62 LD B,3 JR SETDON ; S6: LD A,65 LD B,1 JR SETDON ; S7: LD A,66 LD B,1 JR SETDON ; S8: LD A,67 LD B,1 JR SETDON ; S9: LD A,68 LD B,1 JR SETDON ; SA: LD A,69 LD B,1 JR SETDON ; SB: LD A,70 LD B,2 JR SETDON ; SC: LD A,72 LD B,2 JR SETDON ; SD: LD A,74 LD B,2 JR SETDON ; SE: LD A,76 LD B,2 JR SETDON ; SF: LD A,78 LD B,1 ; SETDON: LD (SRTOFF),A ; Store offset from start of record LD A,B LD (SRTKLN),A ; And length of sort key ; ADSET: CALL PRINT DB CR,LF,LF DB 'Ascending or Desecending order (A/D) ? ',0 ; ADWT: CALL GETCH JR Z,ADWT CALL CAPS CP 'A' JR C,ADWT CALL ECHO CP 'A' JR Z,SETA CP 'D' JR NZ,ADSET LD A,30H ; Code for JR NC,?? JR SETAD ; SETA: LD A,38H ; Code for JR C,?? ; SETAD: LD (NOTEQU),A ; Modify code appropriately CALL PRINT DB CR,LF,LF,0 LD HL,USERS2 CALL OPEN LD HL,SYSLEN LD (RRSZ),HL LD DE,(MSGARR) ; Point to buffer LD (SRTWRK),DE ; Save workarea address for sort ADD HL,DE ; Need syslen bytes for workarea EX DE,HL ; Move start of buffer to de LD (SRTARR),DE ; Save buffer address for sort LD HL,(IUSRC) ; Start at last record INC HL ; Up to base 1 LD (SRTREC),HL ; Save actual number of records to sort DEC HL ; Back to base 0 ; RDLOOP: PUSH DE ; Save buffer pointer PUSH HL CALL PRINT DB CR,'Reading record # ',0 POP HL PUSH HL CALL PB2ASC CALL PRINT DB ' ',0 POP HL CALL GET LD HL,RNDBUF ; From buffer POP DE ; To memory LD BC,SYSLEN ; This many LDIR ; Move ... LD HL,(RRNO) ; Get record number DEC HL ; Point to next record LD A,H CP 0FFH ; At end? JR Z,LODDON ; Yes, done loading PUSH HL ; Save for tpa check PUSH DE LD HL,(1) LD L,0 LD A,H SUB 17H ; Ccp - one page LD H,A EX DE,HL ; Ccp address in de, memory buffer in hl AND A ; Clear carry SBC HL,DE ; See if still room JP NC,OVRFLW ; Overflow error POP DE ; Get memory pointer back POP HL ; And next record number JR RDLOOP ; Go get another record ; LODDON: CALL CLOSE CALL PRINT DB CR,LF DB 'Sorting.....',0 LD IX,SRTARR ; Point to sort array LD C,(IX+2) ; Get number of records into BC LD B,(IX+3) CALL SORT1 ; Sort it CALL PRINT DB CR,LF,0 ; ; Write the records back to the .EMX file ; LD HL,USRWRK ; Now create USERS.EMX CALL OPEN2 ; Buffer 2 = .EMX file (to be updated) LD HL,SYSLEN LD (RRSZ2),HL ; Set record length LD BC,(IUSRC) ; Set up max records to write INC BC ; Up to base 1 PUSH BC ; Preserve counter LD HL,0FFFFH ; Set up for record number for .emx file LD (RRNO2),HL ; Save it LD HL,(SRTARR) ; Start of sorted records PUSH HL ; Set for initial pop ; WRTLOP: LD DE,RNDBUF ; Destination=disk buffer POP HL ; Get source data from memory LD BC,SYSLEN ; Number bytes to move LDIR ; Move it PUSH HL ; Save pointer to next record LD HL,(RRNO2) ; Bump last written to get ... INC HL ; Next to write PUSH HL CALL PRINT DB CR,'Writing record # ',0 POP HL PUSH HL CALL PB2ASC POP HL CALL PUT2 ; Write record POP HL ; Remove one level off stack POP BC ; Get counter back DEC BC PUSH BC ; Save counter PUSH HL ; Save address in memory buffer again LD A,B OR C ; At end yet? JR NZ,WRTLOP ; More to do... CALL CLOSE2 ; Close users.emx file CALL PRINT DB CR,LF,LF DB 'Done. Please verify new file before destroying USERS.BAK.' DB CR,LF,BEL,0 JP SYS1 ;..... ; ; Routine to sort based on data at SRTARR -> SRTWRK, Will sort based on ; any substring (key) of any length to 255. Records may be any length.. ; NOTE: Enter ONLY at SORT1: with IX->SRTAR and BC=# records ; LOOP1: EXX PUSH HL EXX POP BC ; SORT1: LD HL,1 ; Check to see if we are done OR A SBC HL,BC RET NC ; Done if # records <= 1 SRL B ; Divide # records by 2 (one byte at a time) RR C PUSH BC EXX POP HL ; Hl' has current gap EXX LD L,(IX+2) LD H,(IX+3) ; Hl=# records OR A SBC HL,BC ; # records - gap PUSH HL EXX POP DE ; De' has the difference EXX ; LOOP2: EX AF,AF' ;test swap(ped) flag RES 0,A ; Bit 0 is flag EX AF,AF' ;restore af LD C,(IX+0) LD B,(IX+1) ; Bc->first record PUSH BC ; Save it LD HL,1 PUSH HL EXX POP BC ; BC' has lower compare record # PUSH HL EXX POP DE ; De has current gap ADD HL,DE ; Find upper record number EX DE,HL ; Move it to DE LD C,(IX+4) ; C=rec length DEC DE ; DE= upper rec # - 1 LD HL,0 ; DE*C into HL ; MUL1: SRL C JR NC,MUL2 ADD HL,DE ; MUL2: JR Z,MUL9 EX DE,HL ADD HL,HL EX DE,HL JR MUL1 ; MUL9: LD E,(IX+0) ; HL=number bytes from base LD D,(IX+1) ; DE=memory base ADD HL,DE ; HL->upper record POP DE ; DE=lower record PUSH DE ; Save to stack PUSH HL ; Save upper pointer to stack as well JR LOOP3 ; JMP2: POP HL POP HL ; Clear stack EX AF,AF' ;test swap PUSH AF EX AF,AF' POP AF BIT 0,A ; Made any swaps? JR Z,LOOP1 ; No swaps JR LOOP2 ; Yes have made swap ; LOOP3: LD C,(IX+5) LD B,0 ; BC=compare offset ADD HL,BC ; Point to key EX DE,HL ADD HL,BC ; Point to key (string 2) EX DE,HL ; De & hl now set for compare LD B,(IX+6) ; Length of key ; ; The test for a 2 byte record length is required due to the fact that ; word values are stored lsb/msb and a test as for a string of greater ; length will yield incorrect results... ; LD A,B ; Get length of key and ... CP 2 ; If it is two then treat it as a word value JR Z,SPCHK ; Otherwise check 1 then 2 then ... n ; CPLOOP: LD A,(DE) ; Lower record byte CP (HL) ; Compare to upper record byte JR Z,NXCHAR ; If same then check next byte as well JR NOTEQU ; Not equal so see if < or > ; SPCHK: PUSH DE POP IY ; Lower address in iy LD A,(IY) LD E,A LD A,(IY+1) LD D,A ; De=value of lower record PUSH HL POP IY ; Upper address in iy LD A,(IY) LD L,A LD A,(IY+1) LD H,A ; Hl=value of upper EX DE,HL ; So that if upper is greater cy will be set AND A ; Clear carry SBC HL,DE JR Z,NOSWAP ; Equal so no need to switch records JR NOTEQU ; Process as less or greater than ; NXCHAR: INC HL ; Point to next bytes INC DE DJNZ CPLOOP ; Do length of key ; ; If strings are equal this is the spot to do secondary key check ... ; routine not yet implemented... ; JR NOSWAP ; Keys are equal ; ; The next byte is modify via selection of ascending or descending sort ; order selection..... (carry set if upper is greater) ; NOTEQU: JR C,NOSWAP ; Ascending=C, descending=NC JR SWAP ; Re-order the 2 records... ; NOSWAP: EXX PUSH DE PUSH BC EXX POP DE POP HL ; DE=lower, HL=upper records OR A SBC HL,DE ; See if same JR Z,JMP2 ; If not then more to do... ; INC DE ; Add one to lower PUSH DE EXX POP BC ; Save it in BC' EXX LD B,0 LD C,(IX+4) ; BC=record length POP DE ; Get upper record pointer POP HL ; And lower ADD HL,BC ; Point to next record PUSH HL ; Save to stack EX DE,HL ADD HL,BC ; Point to next upper PUSH HL ; Back onto stack JR LOOP3 ; Do it all again... ; SWAP: POP HL ; Get upper pointer PUSH HL ; Still need it so save to stack LD E,(IX+7) LD D,(IX+8) ; DE=work area LD C,(IX+4) LD B,0 ; BC=# bytes to move PUSH BC ; Save for next move LDIR ; Move upper record to workarea POP BC ; Get back # bytes to move POP DE ; Pointer to upper record POP HL ; Pointer to lower record PUSH HL ; Save all as still needed PUSH DE PUSH BC LDIR ; Move lower to upper POP BC ; Get length POP HL POP DE ; DE=lower, HL=upper PUSH DE PUSH HL LD L,(IX+7) LD H,(IX+8) ; Hl points to workarea LDIR ; Move from workarea to lower record EX AF,AF' SET 0,A ; Show we swapped one EX AF,AF' JR NOSWAP ; Done so loop through again ; ACCTMP: DB 0 MF1: DB 'ON',0 MF0: DB 'OFF',0 ; MUSR: CALL PRINT DB CR,LF,LF DB 'User file last maintained: ',0 LD IX,MUSRT ; Point at user mnt time CALL PTIME CALL PRINT DB ' on ',0 LD IX,MUSRD ; Point to user mnt date CALL PDATE CALL PRINT DB CR,LF DB 'CAUTION: Take note of old data BEFORE changing....',0 LD HL,USERS CALL OPEN LD HL,USRLEN ; Record length LD (RRSZ),HL LD HL,(IUSRC) ; Number of records ; PMLP: CALL GET ; PMLP0: CALL PRINT DB CR,LF,LF DB ':: Record number.........: ',0 LD HL,(RRNO) CALL PB2ASC ; CALL PRINT DB CR,LF DB '1: Name..................: ',0 LD HL,UNAMEF LD B,30 CALL PRINTL ; CALL PRINT DB CR,LF DB '2: From..................: ',0 LD HL,CITPRVF LD B,20 CALL PRINTL ; CALL PRINT DB CR,LF DB '3: Password..............: ',0 LD HL,PSWORDF LD B,10 CALL PRINTL ; CALL PRINT DB CR,LF DB '4: Access................: ',0 LD H,0 LD A,(ACCESSF) LD (ACCTMP),A ; Save temporarily LD L,A CALL PB2ASC ; CALL PRINT DB CR,LF DB '5: Signons...............: ',0 LD HL,(TMESONF) CALL PB2ASC ; CALL PRINT DB CR,LF DB '6: Last on...............: ',0 LD IX,LASTONF CALL PDATE ; CALL PRINT DB CR,LF DB '7: Initial du............: ',0 LD A,(INITARF) PUSH AF AND 00001111B ; Get drive ADD A,41H ; Make letter CALL ECHO ; Print drive POP AF AND 11110000B ; Get user area RRCA RRCA RRCA RRCA ; Move to low 4 bits LD L,A LD H,0 CALL PB2ASC ; Show userarea LD A,':' CALL ECHO ; CALL PRINT DB CR,LF DB '8: Mail flag.............: ',0 LD A,(MAILF) OR A ; 0=no mail waiting JR Z,NML LD HL,MF1 JR ML ; NML: LD HL,MF0 ; ML: CALL PRINTM CALL PRINT DB CR,LF DB '9: # of nulls............: ',0 LD A,(NNULLF) LD L,A LD H,0 CALL PB2ASC ; CALL PRINT DB CR,LF DB 'A: Last baudrate code....: ',0 LD A,(BDCDEF) LD L,A LD H,0 CALL PB2ASC ; CALL PRINT DB CR,LF DB 'B: # of uploads..........: ',0 LD HL,(UPLDSF) CALL PB2ASC ; CALL PRINT DB CR,LF DB 'C: # of downloads........: ',0 LD HL,(DNLDSF) CALL PB2ASC ; CALL PRINT DB CR,LF DB 'D: Userarea map..........: ',0 LD HL,(USRMPF) CALL PMAP ; CALL PRINT DB CR,LF DB 'E: Drive map.............: ',0 LD HL,(DRVMPF) CALL PMAP ; CALL PRINT DB CR,LF DB 'F: Terminal identity code: ',0 LD A,(TRMCDEF) LD L,A LD H,0 CALL PB2ASC ; CALL PRINT DB CR,LF,LF DB 'Enter code (x:) of field to alter or:' DB CR,LF DB '= (next record), - (previous record), ' DB '/ (goto username), \ (quit)',CR,LF DB '--> Select? ',0 ; UWT: CALL GETCH JR Z,UWT CALL CAPS CP '=' JP Z,MBPREC ; Move to next record CP '\' JP Z,MUDON ; Quit to main menu CP '-' JP Z,BACKU ; Move to previous record CP '/' JP Z,GOTO ; Set record to start and find user CP '1' JP Z,UWT3 ; Change user's name CP '2' JP Z,UWT4 ; Change city, province/state CP '3' JP Z,UWT2 ; Change password CP '4' JP Z,UWT1 ; Change access level CP '5' JP Z,TMO ; Change # times on CP '6' JP Z,LSTO ; Change last date on CP '7' JP Z,IDU ; Change initial drive/user CP '8' JP Z,MWF ; Change mail waiting flag CP '9' JP Z,NLS ; Change number of nulls CP 'A' JP Z,LBD ; Change last baud-rate CP 'B' JP Z,UPL ; Change # uploads CP 'C' JP Z,DNL ; Change downloads CP 'D' JP Z,UMP ; Change user map CP 'E' JP Z,DMP ; Change drive map CP 'F' JP Z,TCD ; Change terminal code JP UWT ; PMAP: PUSH HL ; Save for later LD C,H CALL P8BITS ; Do msb (bit 16=user 15) POP HL LD C,L CALL P8BITS ; And lsb (bit 0=user 0) RET ; P8BITS: LD B,8 ; Set counter ; P8B1: LD A,'0' BIT 7,C ; Test JR Z,P8B2 INC A ; Make ascii '1' P8B2: CALL ECHO SLA C ; Move next bit into place DJNZ P8B1 ; Keep going RET ; Done if 0 ; CMAP: CALL C8BITS PUSH AF ; Save result (for h later) CALL C8BITS LD L,A POP AF LD H,A RET ; HL=binary ; C8BITS: LD B,8 LD C,0 ; C8B1: SLA C LD A,(HL) ; Get character INC HL SUB 30H OR C ; Merge results LD C,A ; Save result in c DJNZ C8B1 ; Not done RET ; UMP: CALL PRINT DB CR,LF DB '- userarea map (hex) --> FEDCBA9876543210' DB CR,LF DB 'Enter map for userareas: ',0 LD A,0 LD D,A LD C,A LD B,16 CALL INPUT OR A JP Z,MBPREC CALL CMAP ; Convert ascii (bin) to binary in hl LD (USRMPF),HL ; Save it JP UWRT ; DMP: CALL PRINT DB CR,LF DB '- drive map --------> PONMLKJIHGFEDCBA' DB CR,LF DB 'Enter map for drives: ',0 LD A,0 LD D,A LD C,A LD B,16 CALL INPUT OR A JP Z,MBPREC CALL CMAP LD (DRVMPF),HL JP UWRT ; NLS: CALL PRINT DB CR,LF DB 'Enter number of nulls (0-50): ',0 LD B,2 LD A,0 LD D,0 LD C,0 CALL INPUT OR A JP Z,MBPREC LD (CNVRT0+1),A ; Set number of bytes to convert PUSH HL POP IX ; Pointer to string in ix CALL CNVRT0 LD A,2 LD (CNVRT0+1),A PUSH HL ; Save result LD DE,51 AND A SBC HL,DE POP HL ; Recover result in case okay JR NC,NLS ; 255 max LD A,L LD (NNULLF),A JP UWRT ; LBD: CALL PRINT DB CR,LF DB 'Enter baud-code (0-9): ',0 ; LBDWT: CALL GETCH JR Z,LBDWT CP CR JP Z,MBPREC ; escapes CP '0' JR C,LBDWT CP '9'+1 JR NC,LBDWT CALL ECHO SUB 30H ; Make it binary LD (BDCDEF),A JP UWRT ; UPL: CALL PRINT DB CR,LF DB 'Enter number of uploads: ',0 LD B,5 LD A,0 LD D,0 LD C,0 CALL INPUT OR A JP Z,MBPREC LD (CNVRT0+1),A ; Set number of bytes to convert PUSH HL POP IX ; Pointer to string in ix CALL CNVRT0 LD A,2 LD (CNVRT0+1),A LD (UPLDSF),HL JP UWRT ; DNL: CALL PRINT DB CR,LF DB 'Enter number of downloads: ',0 LD B,5 LD A,0 LD D,0 LD C,0 CALL INPUT OR A JP Z,MBPREC LD (CNVRT0+1),A ; Set number of bytes to convert PUSH HL POP IX ; Pointer to string in ix CALL CNVRT0 LD A,2 LD (CNVRT0+1),A LD (DNLDSF),HL JP UWRT ; TCD: CALL PRINT DB CR,LF DB 'Enter terminal code (0-255): ',0 LD B,3 LD A,0 LD D,0 LD C,0 CALL INPUT OR A JP Z,MBPREC LD (CNVRT0+1),A ; Set number of bytes to convert PUSH HL POP IX ; Pointer to string in ix CALL CNVRT0 LD A,2 LD (CNVRT0+1),A PUSH HL ; Save result LD DE,256 AND A SBC HL,DE POP HL ; In case okay, recover result JR NC,TCD ; 255 max LD A,L LD (TRMCDEF),A ; Store it JP UWRT ; GSTRT: LD HL,(IUSRC) ; Get start of search (end of file) JP PMLP ; GEND: LD HL,0 ; Get end of search (start of file) JP PMLP ; STARTM: DB 'START',0 ENDM: DB 'END',0 ; GOTO: CALL PRINT DB CR,LF DB 'Enter first and last names (or START or END): ',0 LD B,30 LD C,20H LD D,0 LD A,D CALL INPUT OR A JP Z,MBPREC LD DE,STARTM ; See if 'start' LD HL,INBUF LD B,6 CALL MATCH JP Z,GSTRT LD DE,ENDM LD HL,INBUF LD B,4 CALL MATCH OR A JP Z,GEND ; CALL PRINT DB CR,LF DB 'Searching...',0 LD HL,(RRNO) ; Get current record number and... LD (TRECNO),HL ; Save it in case user not found LD HL,(IUSRC) ; Reset to end of file ; GLOOP: CALL GET ; Get record LD DE,UNAMEF ; Point to user name LD HL,INBUF ; Point to name wanted LD B,30 ; Number characters to match CALL MATCH ; See if same OR A ; A=0=same JP Z,PMLP0 ; Got him! LD HL,(RRNO) DEC HL LD A,H CP 0FFH ; More? JR NZ,GLOOP ; Yes CALL PRINT DB CR,BEL DB 'Not found... check your spelling(?)',0 LD HL,(TRECNO) JP PMLP ; Go back to last record displayed ; TRECNO: DW 0 ; UWT2: CALL PRINT DB CR,LF DB 'New password? ',0 LD B,10 LD C,20H LD D,0 XOR A CALL INPUT OR A JP Z,MBPREC LD DE,PSWORDF LD BC,10 LDIR ; Move new password to buffer JP UWRT ; And write record ; UWT3: CALL PRINT DB CR,LF DB 'New name? ',0 LD B,30 LD C,20H LD D,0 XOR A CALL INPUT OR A JP Z,MBPREC LD DE,UNAMEF LD BC,30 LDIR ; Move name to buffer JP UWRT ; And write record ; UWT4: CALL PRINT DB CR,LF DB 'New city, province/state? ',0 LD B,20 LD C,20H LD D,0 XOR A CALL INPUT OR A JP Z,MBPREC LD DE,CITPRVF LD BC,20 LDIR ; Move location to buffer JP UWRT ; And write record ; IDU: CALL PRINT DB CR,LF DB 'Drive letter? ',0 ; IDU1: CALL GETCH JR Z,IDU1 CP CR JP Z,MBPREC CALL CAPS CALL ECHO CP 'A' JP C,IDU CP 'P'+1 JP NC,IDU ; SUB 41H PUSH AF ; Save drive as number 0-f CALL PRINT DB ' -> User area? ',0 LD D,0 LD B,2 LD C,0 LD A,C CALL INPUT CP 2 ; How many characters? JR Z,IDU2 CP 1 JR Z,IDU2 POP AF ; Fix stack back up JP IDU ; Illegal entry - do it again ; IDU2: LD (CNVRT0+1),A ; Set number characters to convert PUSH HL POP IX ; Pointer to ix CALL CNVRT0 ; Make string binary number LD A,2 LD (CNVRT0+1),A ; Restore code LD A,L ; Get binary number RLCA RLCA RLCA RLCA ; Move to upper 4 bits PUSH AF POP BC ; User in upper 4 bits of b POP AF ; Get drive back AND 00001111B ADD A,B ; Merge drive/user LD (INITARF),A JP UWRT ; MWF: CALL PRINT DB CR,LF,LF DB 'Mail-waiting flag toggled.',0 LD A,(MAILF) OR A JR Z,MFSET XOR A LD (MAILF),A JP UWRT ; MFSET: LD A,1 LD (MAILF),A JP UWRT ; TMO: CALL PRINT DB CR,LF DB 'Number times on? ',0 LD C,0 LD D,0 LD B,5 LD A,C CALL INPUT OR A JP Z,MBPREC LD (CNVRT0+1),A ; Set number of bytes to convert PUSH HL POP IX CALL CNVRT0 LD A,2 LD (CNVRT0+1),A LD (TMESONF),HL ; Store new times on JP UWRT ; LSTO: CALL PRINT DB CR,LF DB 'Enter last date on as mm/dd/yy: ',0 LD B,8 LD D,0 LD C,0 LD A,C CALL INPUT CP 8 JP C,LSTERR LD BC,8 LD DE,DATE LDIR CALL CDATE ; Make string binary LD HL,BDATE LD DE,LASTONF LD BC,3 LDIR JP UWRT ; LSTERR: CALL PRINT DB BEL,' <-- MUST be 8 characters.',0 LD HL,(RRNO) JP PMLP ; UWT1: CALL PRINT DB CR,LF DB 'New level? ',0 ; UWT1A: CALL GETCH JR Z,UWT1A CP CR JP Z,MBPREC CP '0' JR C,UWT1 CP '9'+1 JR NC,UWT1 CALL ECHO SUB 30H PUSH AF LD B,A LD A,(ACCTMP) CP B JR Z,MBPREC ; No change JR C,UP ; Increased POP AF ; Get back new level PUSH AF ; And save it again CP 0 ; Now deleted? JR Z,U2 ; Yes JR UMTND ; UP: LD A,(ACCTMP) CP 0 ; Was it deleted before? JR NZ,UMTND ; No ; U1: LD HL,(IUSER) ; Increase # users by one INC HL LD (IUSER),HL JR UMTND ; U2: LD HL,(IUSER) ; Lower # users by one DEC HL LD (IUSER),HL ; UMTND: POP AF ; Get back access level LD (ACCESSF),A ; UWRT: LD HL,(RRNO) CALL PUT LD HL,(RRNO) JP PMLP ; MBPREC: LD HL,(RRNO) ; Get record DEC HL ; And drop it by one LD A,H CP 0FFH ; See if any more JP NZ,PMLP ; Yes ; MUDON: CALL CLOSE ; Close CALL PRINT DB CR,LF,LF DB 'User maintenance completed.',0 CALL GETTIM CALL CDATE CALL CTIME ; Binary date & time set LD HL,BTIME LD DE,MUSRT LD BC,3 LDIR ; Set time LD HL,BDATE LD DE,MUSRD LD BC,3 LDIR ; Set date LD HL,INDEX CALL OPEN ; HLGDON: LD HL,IDATE LD DE,IDATEF LD BC,NDXLEN LDIR LD HL,NDXLEN LD (RRSZ),HL LD HL,0 CALL PUT ; Update index CALL CLOSE JP SYS1 ; BACKU: LD DE,(IUSRC) ; BACK: INC DE ; Set for compare LD HL,(RRNO) INC HL AND A ; Clear carry SBC HL,DE JP NC,MBPREC ; Hl>de so forget it LD HL,(RRNO) INC HL JP PMLP ; ENDF: DW 2 ; Temp rec no. storage DELE: DW 2 ; Address of start of delete array ; MMSG: CALL PRINT DB CR,LF DB 'Message file last maintained: ',0 LD IX,MMSGT ; Point at user mnt time CALL PTIME CALL PRINT DB ' on ',0 LD IX,MMSGD ; Point to user mnt date CALL PDATE CALL PRINT DB CR,LF,0 ; READMP: LD DE,(MSGARR) ; Point to messages table PUSH DE CALL PRINT DB CR,LF,LF DB 'Please wait, loading...',0 LD HL,MSGINDEX CALL OPEN LD HL,MNDXLEN ; Record length LD (RRSZ),HL LD HL,(IMNDX) ; Start record ; MRDLP: CALL GET LD A,(MNUMF) CP 0 ; Message deleted? JP NZ,MRDLP3 LD A,(MNUMF+1) CP 0 ; Maybe JP Z,BMPREC ; Yes ; MRDLP3: LD HL,MNUMF ; No POP DE ; Get current pointer LD BC,MTABLEN LDIR ; Save message info LD HL,(RRNO) ; Get record number in msgindex LD A,L LD (DE),A LD A,H INC DE LD (DE),A INC DE ; Record number is last two bytes of each entry PUSH DE ; New pointer so save and ... ; BMPREC: LD HL,(RRNO) ; Get record DEC HL ; And drop it by one LD A,H CP 0FFH ; See if any more JP NZ,MRDLP ; Yes CALL CLOSE POP IY ; Get end of array XOR A ; Mark end of array LD (IY),A LD (IY+1),A ; PMSGS: LD IY,(MSGARR) ; Point to table LD HL,MESSAGES CALL OPEN ; Open messages file LD HL,MSGLEN LD (RRSZ),HL ; Set record length ; PMLOP1: LD A,(IY) CP 0 JP NZ,PM01 LD A,(IY+1) CP 0 JP Z,NOMOR ; PM01: CALL PRINT DB CR,LF,LF,LF DB 'Message number ',0 LD A,(IY) LD L,A INC IY LD A,(IY) LD H,A INC IY ; Points to date CALL PB2ASC CALL PRINT DB ', dated ',0 PUSH IY POP IX CALL PDATE INC IY INC IY INC IY ; Points to number records CALL PRINT DB '. ',0 LD A,(IY) LD B,A ; Number of lines (records) to b PUSH BC ; Save it as bc INC IY ; Points to starting record number LD A,(IY) ; Get starting record number LD L,A ; Into hl INC IY LD A,(IY) LD H,A LD (RRNO),HL ; And random param block INC IY ; Now points to subject CALL PRINT DB ' Subject: ',0 PUSH IY POP HL LD B,26 CALL PRINTL PUSH IY POP HL LD DE,26 ; Get passed subject ADD HL,DE ; And point to record number in msgindex INC HL INC HL ; Point to next data PUSH HL POP IY ; Updated and into IY LD DE,(MSG) ; Point to message buffer POP BC ; Recover line count ; PMLOP2: PUSH BC ; Save counter PUSH DE ; And pointer to buffer location LD HL,(RRNO) CALL GET LD HL,RNDBUF LD BC,MSGLEN POP DE ; Get buffer address LDIR LD HL,(RRNO) INC HL ; Next line (record) LD (RRNO),HL POP BC ; Get counter DJNZ PMLOP2 CALL LIST ; And print it to console CALL PRINT DB CR,LF,0 CALL PRINT DB '= (next), - (previous), \ (quit), ! (kill) ? ',0 ; PWAIT: CALL GETCH JP Z,PWAIT CALL CAPS CP '\' JP Z,DONEM CP '=' JP Z,PMLOP1 CP CR JP Z,PMLOP1 CP '-' CALL Z,BACKM CP '!' JP NZ,PWAIT ; ; Kill routine ; CALL CLOSE ; Close messages file while we kill LD HL,MSGINDEX ; By opening msgindex CALL OPEN LD HL,SYSLEN LD (RRSZ),HL PUSH IY ; Getting pointer to msgindex record # POP DE ; Which is the last 2 bytes of each entry DEC DE DEC DE ; And CALL FILLHL ; (after placing (de) in hl LD (RRNO),HL ; Reading it CALL GET LD HL,0 ; Clearing record number LD (MNUMF),HL LD HL,(RRNO) ; (restore record number) CALL PUT ; And writing record back CALL CLOSE ; Done kill so now open messages file LD HL,MESSAGES CALL OPEN LD HL,MSGLEN LD (RRSZ),HL JP PMLOP1 ; And going to check for next message data ; LIST: CALL PRINT DB CR,LF,LF,0 XOR A LD (COLUMN),A ; Set column counter LD HL,(MSG) ; Point to buffer ; LSTLOP: LD A,(HL) OR A RET Z ; 0=end CP 1 ; 1 is used as a pad char for first 2 records JR Z,LSTSKP ; And to allow easy conversion of old messages CP CR ; Carriage return? CALL Z,LSTCR CP ' ' ; Space? CALL Z,LSTWRP ; See if wrap time CALL ECHO ; Echo character CALL GETCH ; See if we want to pause / restart CALL NZ,LSTPSE ; Yes INC HL LD A,(COLUMN) ; Update current column INC A CP 80 ; At end of line? JR C,LST0 ; Nope XOR A ; Yep so reset ; LST0: LD (COLUMN),A JR LSTLOP ; LSTSKP: INC HL ; Point at next character JR LSTLOP ; And check it out ; LSTWRP: LD A,(COLUMN) CP 64 ; <<<=== wrap point at linelength-15 LD A,' ' ; In case RET C ; Less so return XOR A LD (COLUMN),A ; Reset column count LD A,CR CALL ECHO ; Do carriage return LD A,LF ; Get linefeed RET ; And return to print it ; LSTCR: CALL ECHO ; Do XOR A LD (COLUMN),A ; Reset counter LD A,LF ; Return to print linefeed RET ; LSTPSE: CALL GETCH ; Wait for another character JR Z,LSTPSE ; Wait... RET ; Back to the farm... ; COLUMN: DB 0 ; Counter byte ; NOMOR: CALL PRINT DB CR,LF,LF DB 'Finished.',0 ; DONEM: CALL CLOSE ; DONEM0: CALL PRINT DB CR,LF,LF DB 'Renumber messages? ',0 ; RNMWT: CALL GETCH JR Z,RNMWT CALL CAPS CP 'A' JR C,RNMWT CALL ECHO CP 'Y' JP Z,RENUM CP 'N' JP Z,NORNM JR DONEM0 ; RENUM: CALL PRINT DB CR,LF,LF DB 'Renumbering messages....',0 LD HL,MSGINDEX CALL OPEN LD HL,MNDXLEN LD (RRSZ),HL LD HL,(IMNDX) LD (ENDF),HL LD HL,0 LD IX,1 ; Starting number ; RNMLP: CALL GET LD A,(MNUMF) CP 0 ; Deleted? JP NZ,RNM1 ; No LD A,(MNUMF+1) CP 0 ; Maybe JP Z,RNMBMP ; Yes ; RNM1: LD (MNUMF),IX INC IX ; Next number LD HL,(RRNO) CALL PUT ; RNMBMP: LD HL,(RRNO) INC HL LD DE,(ENDF) INC DE AND A SBC HL,DE JP NC,RNMDN ; Finished LD HL,(RRNO) INC HL JP RNMLP ; Keep going ; RNMDN: CALL CLOSE INC IX LD (IMNXT),IX ; NORNM: CALL GETTIM ; Get current time and date CALL CTIME CALL CDATE ; Time and date to binary LD DE,MMSGT ; Point to time in index LD HL,BTIME ; Point to binary time LD BC,3 LDIR ; Move LD DE,MMSGD ; Point todate in index LD HL,BDATE ; And binary LD BC,3 LDIR ; Move it LD HL,IDATE LD DE,IDATEF LD BC,NDXLEN LDIR ; Move index to buffer LD HL,INDEX CALL OPEN LD HL,NDXLEN LD (RRSZ),HL LD HL,0 CALL PUT ; Update index JP SYS1 ; BACKM: PUSH IY POP HL LD DE,36 AND A ; Clear carry SBC HL,DE ; Points to current message SBC HL,DE ; Points to previous PUSH HL ; Save in case ok LD DE,(MSGARR) ; See if passed start of array AND A ; Clear carry SBC HL,DE ; Ok? JP NC,BACKM1 ; HL>=DE = ok LD HL,(MSGARR) ; Point to start POP IY ; Clear stack PUSH HL ; Save new pointer ; BACKM1: POP IY ; Get new pointer JP PMLOP1 ; MHLG: CALL PRINT DB CR,LF,LF DB 'Hard-log now ',0 LD A,(HRDLOG) CP 1 JP NZ,LGOFF CALL PRINT DB 'ON.',0 JP MHLG1 ; LGOFF: CALL PRINT DB 'OFF.',0 ; MHLG1: CALL PRINT DB CR,LF,LF DB 'Enter Q to quit, N for on, F for off: ',0 ; MHWT: CALL GETCH JP Z,MHWT CALL CAPS CP 'F' JP Z,OFF CP 'N' JP Z,ON CP 'Q' JP Z,HLGDON JP MHWT ; OFF: CALL ECHO XOR A JR NFDON ; ON: CALL ECHO LD A,1 ; NFDON: LD (HRDLOG),A JP MHLG ; PACK: CALL PRINT DB CR,LF,LF,BEL DB 'These routines MAY take up to 10 minutes to complete.' DB CR,LF,LF DB 'This routine will erase any of:' DB CR,LF DB 'USERS.BAK, MESSAGES.BAK or MSGINDEX.BAK that are present.' DB CR,LF DB 'Continue? ',0 ; PACK0: CALL GETCH ; Erase any .bak files present JR Z,PACK0 CALL CAPS CP 'Y' JR Z,PACK1 CP 'N' JP Z,SYS1 JR PACK0 ; PACK1: CALL ECHO CALL PRINT DB CR,LF,LF,0 LD HL,USERS2 CALL BFCB LD DE,FCB LD C,13H CALL BDOS LD HL,MSGIN2 CALL BFCB LD DE,FCB LD C,13H CALL BDOS LD HL,MESSA2 CALL BFCB LD DE,FCB LD C,13H CALL BDOS CALL PRINT DB 'Renaming USERS.EMX to USERS.BAK',0 LD DE,RENU1 LD C,17H CALL BDOS CP 0FFH JP Z,PERR CALL PRINT DB CR,LF DB 'Renaming MESSAGES.EMX to MESSAGES.BAK',0 LD DE,RENM1 LD C,17H CALL BDOS CP 0FFH JP Z,PERR CALL PRINT DB CR,LF DB 'Renaming MSGINDEX.EMX to MSGINDEX.BAK',0 LD DE,RENX1 LD C,17H CALL BDOS CP 0FFH JP Z,PERR CALL PRINT DB CR,LF,LF DB 'Transferring USERS.BAK to USERS.EMX ...........',0 LD HL,USERS2 CALL OPEN LD HL,USRWRK CALL OPEN2 LD HL,USRLEN ; Get length of each record LD (RRSZ),HL ; Into source param block LD (RRSZ2),HL ; And dest param block LD IX,0 ; Clear counter for new records LD HL,(IUSRC) ; Get number of records ; PUSLP: CALL GET ; Get user record LD A,(ACCESSF) ; See if active CP 0 JP Z,PUBMP ; Inactive so don't write new record PUSH IX POP HL ; Get record number to write INC IX ; Point to next record CALL PUT2 ; Write new record ; PUBMP: LD HL,(RRNO) ; Get last record read DEC HL ; Down by one LD A,H ; See if at end yet CP 0FFH JP Z,PUDON ; Yes, so phase 1 done LD (RRNO),HL ; No, so update record number JP PUSLP ; And process next one ; PUDON: DEC IX ; Set to number of records in new file LD (IUSRC),IX ; Place it in index CALL CLOSE ; Close both files CALL CLOSE2 CALL PRINT DB ' DONE.',CR,LF DB 'Transferring MSGINDEX.BAK to MSGINDEX.EMX .....',0 LD HL,MSGIN2 CALL OPEN LD HL,MSGWRK CALL OPEN2 LD HL,MNDXLEN ; Get length of each record LD (RRSZ),HL ; Into source param block LD (RRSZ2),HL ; And dest param block LD IX,0 ; Clear counter for new records LD HL,(IMNDX) ; Get number of records ; PMILP: CALL GET ; Get message index record LD A,(MNUMF) ; See if active CP 0 JP Z,PMIBMP ; Inactive so don't write new record PUSH IX POP HL ; Get record number to write INC IX ; Point to next record CALL PUT2 ; Write new record ; PMIBMP: LD HL,(RRNO) ; Get last record read DEC HL ; Down by one LD A,H ; See if at end yet CP 0FFH JP Z,PMIDON ; Yes, so phase 1 done LD (RRNO),HL ; No, so update record number JP PMILP ; And process next one ; PMIDON: DEC IX ; Set to number of records in new file LD (IMNDX),IX ; Place it in index CALL CLOSE ; Close both files CALL CLOSE2 CALL PRINT DB ' DONE.',CR,LF DB 'Transferring MESSAGES.BAK to MESSAGES.EMX .....',0 LD IX,0 ; Clear counter LD HL,(IMNDX) ; Get number of records LD (CURREC),HL ; Set current record LD HL,MESWRK CALL OPEN2 ; Open new messages file (stays open) LD HL,MSGLEN LD (RRSZ2),HL ; PMSLOP: LD HL,MSGWRK CALL OPEN ; Open new index LD HL,MNDXLEN ; Set size LD (RRSZ),HL LD HL,(CURREC) ; Get this record number CALL GET LD A,(MBLKF) LD (BLOCKS),A ; Number of blocks LD HL,(MSTRF) LD (STRT),HL ; Starting at this record number LD (MSTRF),IX ; New start record LD HL,(CURREC) CALL PUT ; Save it CALL CLOSE ; Done with index for now LD HL,MESSA2 CALL OPEN LD HL,MSGLEN LD (RRSZ),HL LD A,(BLOCKS) LD B,A ; Set counter to number of blocks ; PMSLP1: PUSH BC ; Save counter LD HL,(STRT) ; Get record number to read CALL GET ; Get block LD HL,(STRT) ; Get record number and.... INC HL ; Bump it then.... LD (STRT),HL ; Save it PUSH IX ; New record number POP HL ; To hl INC IX ; Point to next record to write CALL PUT2 ; Write record POP BC ; Restore counter DJNZ PMSLP1 ; Do til done with this message CALL CLOSE ; Finished with old messages file for now LD HL,(CURREC) ; See if at end of new index yet DEC HL LD A,H CP 0FFH JP Z,PMSDON ; Yes, so quit LD (CURREC),HL ; No so update record pointer and... JP PMSLOP ; Do it all again ; PMSDON: LD (IMRNM),IX ; Set next message record number to index CALL CLOSE CALL CLOSE2 ; Close files CALL PRINT DB ' DONE.',CR,LF,LF DB 'Updating INDEX.EMX .....',0 ; ; Update INDEX file and save old INDEX to record #2 ; LD HL,INDEX CALL OPEN LD HL,NDXLEN LD (RRSZ),HL LD HL,0 ; Get old index CALL GET LD HL,1 ; Move old info to record #2 for safety CALL PUT LD HL,IDATE LD DE,IDATEF LD BC,NDXLEN LDIR ; Move new index info to buffer LD HL,0 CALL PUT CALL CLOSE CALL PRINT DB CR,LF,LF DB 'Old INDEX info now in second record of ' DB 'INDEX.EMX for safety.' DB CR,LF,LF,0 JP SYS1 ;..... ; PUSR: JP SYS1 PMSG: JP SYS1 ;..... ; PERR: CALL PRINT DB ' -- Fatal error....',0 JP EXIT ;.... ; BLOCKS: DB 0 STRT: DW 0 CURREC: DW 0 USERS2: DB 'USERS.BAK',0 MSGIN2: DB 'MSGINDEX.BAK',0 MESSA2: DB 'MESSAGES.BAK',0 USRWRK: DB 'USERS.EMX',0 MSGWRK: DB 'MSGINDEX.EMX',0 MESWRK: DB 'MESSAGES.EMX',0 RENU1: DB 0,'USERS EMX',0,0,0,0 DB 0,'USERS BAK',0,0,0,0 RENM1: DB 0,'MESSAGESEMX',0,0,0,0 DB 0,'MESSAGESBAK',0,0,0,0 RENX1: DB 0,'MSGINDEXEMX',0,0,0,0 DB 0,'MSGINDEXBAK',0,0,0,0 ; ; Stack area ; BSTACK EQU $ ; Bottom of stack for sort routines ; DS 512 ; Big stack for sort routines ; STACK: DW 0H CCPSTK: DW 0H ; Stack storage ; MSG: DW 0 ; Holding area for messages being viewed MSGARR: DW 0 ; Array starts at address placed here ; a call to ENDEMX in EMXSUBS.REL at ; start: END