; ; TITLE 'ERA ver 1.0 - 10-09-85' ; ; ERA.ASM - REMOTE CP/M ERA TWIT CATCHER PROGRAM ; keeps track of remote users who try to ; erase, rename or save files. Data ; entered in TWIT.SYS file ; ; gets data from LASTCALR file ; ; ; This is really thirteen programs in one. Set the appropriate ; EQU and assemble. ; ; Program saves command line and puts it in file with current user ; data and time. ; ; Requires working clock routines in bye or it returns all 9's ; and if you're running NZCPR (ZCMD) the built in ERA will be used ; if wheel is set (0FFh) else this will be used. ; ; At label DUMP: use your BYE.ins routines to drop then raise DTR. ; ; 10/10/85 ver 1.0 These programs are TWIT catchers. That is, if ; a remote user attempts ERA, REN or DEL ; files or tries to SAVE a memory image, a record ; of every attempt is kept in TWIT.LST. ; ; The record structure is: ; ; program ; REN -> [cmdline arguments] [date] [time] [caller's name] ; ; ; The sysop can later examine the TWIT.LST and see who is ; DELIBERATELY trying to do nasty things and who just made ; mistakes. ; ; I MAY circulate the list of twits to other sysops so that we can ; better protect these systems for the legitimate users. ; ; Larry Clive ; Manhattan Micro Connection RCP/M ; (212) 989-2696 (300-1200-2400) ; New York, New York ; 10-10-85 ; ;======================================================================== ; ; NO EQU 0 YES EQU NOT NO ; ;======================================================================== ; ERA EQU YES ; SET ONLY ONE OF THESE 13 EQU'S REN EQU NO ; then assemble and name the com file to SAVE EQU NO ; match the equ set to yes. GO EQU NO ; JUMP EQU NO ; PIP EQU NO ; DDT EQU NO ; BOOT EQU NO ; COPY EQU NO ; MD EQU NO ; MKDIR EQU NO ; RD EQU NO ; DEL EQU NO ; WHEEL EQU NO ;for these 4, rename your real wheel program WEEL EQU NO ; WHEL EQU NO ; WEAL EQU NO ; ; ;========================================================================= ; ; Define ASCII characters used ; CR EQU 0DH ; Carriage return EOF EQU 1AH ; End of file - ^Z LF EQU 0AH ; Line feed TAB EQU 09H ; Horizontal tab ; ;======================================================================= ; ; Conditional equates - change to suit your system, then assemble ; TWTDRV EQU 'B' ; Drive to place 'TWIT.LST' file TWTUSR EQU 15 ; User area to put 'TWIT.LST' file ; ; OxGate BBS puts the date after the callers name. If you are using ; B3RTC and have an OxGate, then set this ; equate to YES, so the date doesn't appear twice. ; OXGATE EQU NO ; If yes, doesn't read date ; in OxGate's lastcaller file. ; MBBS EQU YES ; MBBS puts date in lastcalr file ; ; LASTDRV EQU 'A' ; Drive to read 'LASTCALR' file from LASTUSR EQU 15 ; User area of 'LASTCALR' file ; SPORT EQU 17h ; Modem satus port ; ;======================================================================= ORG 100H ;======================================================================= ; BEGIN: LHLD 0001H ; Get JMP COLDBOOT DCX H MOV D,M DCX H MOV E,M LXI H,12 ; + LOCOFF DAD D ORI 0FFH ; = WRTLOC address MOV M,A ; Turn the lock on ; CALL ILPRT DB CR,LF ; IF ERA DB 'You are NOT permitted to ERASE files on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'ERA -> ' CMDBUF: DB ' ',0 ENDIF ; IF REN DB 'You are NOT permitted to RENAME files on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'REN -> ' CMDBUF: DB ' ',0 ENDIF ; IF SAVE DB 'You are NOT permitted to SAVE files on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'SAVE -> ' CMDBUF: DB ' ',0 ENDIF ; IF GO DB 'You are NOT permitted to GO on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'GO -> ' CMDBUF: DB ' ',0 ENDIF ; IF JUMP DB 'You are NOT permitted to JUMP on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'JUMP -> ' CMDBUF: DB ' ',0 ENDIF ; IF PIP DB 'You are NOT permitted to use PIP on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'PIP -> ' CMDBUF: DB ' ',0 ENDIF ; IF DDT DB 'You are NOT permitted to use DDT on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'DDT -> ' CMDBUF: DB ' ',0 ENDIF ; IF BOOT DB 'You are NOT permitted to BOOT on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'BOOT -> ' CMDBUF: DB ' ',0 ENDIF ; IF COPY DB 'You are NOT permitted to COPY files on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'COPY -> ' CMDBUF: DB ' ',0 ENDIF ; IF MD DB 'You are NOT permitted to use MD on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'MD -> ' CMDBUF: DB ' ',0 ENDIF ; IF MKDIR DB 'You are NOT permitted to use MKDIR on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'MKDIR-> ' CMDBUF: DB ' ',0 ENDIF ; IF RD DB 'You are NOT permitted to use RD on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'RD -> ' CMDBUF: DB ' ',0 ENDIF ; IF DEL DB 'You are NOT permitted to DELETE files on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'DEL -> ' CMDBUF: DB ' ',0 ENDIF ; IF WHEEL DB 'You are NOT permitted to change the WHEEL on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'WHEEL-> ' CMDBUF: DB ' ',0 ENDIF ; IF WEEL DB 'You are NOT permitted to change the WHEEL on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'WEEL-> ' CMDBUF: DB ' ',0 ENDIF ; IF WHEL DB 'You are NOT permitted to change the WHEEL on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'WHEL-> ' CMDBUF: DB ' ',0 ENDIF ; IF WEAL DB 'You are NOT permitted to change the WHEEL on this system !' DB cr,lf,lf,'If you continue to try, your access will ' DB 'be terminated AND other sysops will be notified ! ' DB cr,lf,lf,0 JMP GETCMD CMDLN: DB 'WEAL-> ' CMDBUF: DB ' ',0 ENDIF ; ; GETCMD: MVI B,20 LXI H,tbuf LXI D,CMDBUF+2 MOVCMD: MOV A,M CPI 32 CC NOCTRL STAX D INX H INX D DCR B MOV A,B ORA A JNZ MOVCMD ;done moving cmdline to buffer ; CALL LOGCALL ;now put data in twit.lst CALL WRTOFF ; reset wrtloc CALL RESET ;reset to A0: CALL DUMP ;drop dtr to dump user off JMP BDOS ; we got a twit nailed... now exit to cpm ; ; ;*********************************************************************** ; ; SUBROUTINES ; ;*********************************************************************** ; ; reset wrtloc ; WRTOFF: LHLD 0001H ; If so, time to reset it DCX H ; Get JMP COLDBOOT addr. MOV D,M DCX H MOV E,M LXI H,12 ; + LOCOFF bytes DAD D ; = WRTLOC address XRA A ; Clear it MOV M,A ; (so ctrl-C/ctrl-K work) RET ; ; ; RESET: MVI A,0 STA 04H RET ; DUMP: MVI A,0 ; Setup to write register 0 OUT SPORT MVI A,18H ; Reset channel OUT SPORT MVI A,4 ; Setup to write register 4 OUT SPORT MVI A,44H ; Set 16x, 1 stop bit, no parity OUT SPORT MVI A,3 ; Setup to write register 3 OUT SPORT MVI A,0C1H ; 8 bits, Rx enable OUT SPORT MVI A,5 ; Setup to write register 5 OUT SPORT MVI A,68H ; DTR off OUT SPORT call delay ; .1 sec delay call delay call delay call delay call delay MVI A,5 ; Setup to write register 5 OUT SPORT MVI A,0E8H ; Turn DTR back on OUT SPORT RET ; DELAY: PUSH B LXI B,19000 ;about .1 seconds DELAY1º DCØ B MOV A,B ORA C JNZ DELAY1 POP B RET ; NOCTRL: MOV A,M ADI 40H RET ; CTYPE: PUSH B ; Save all registers PUSH D PUSH H MVI C,2 MOV E,A ; Character to 'E' in case BDOS (normal) CALL BDOS POP H ; Restore all registers saved by 'CTYPE' POP D POP B RET ; ; ; Inline print of message, terminates with a 0 ; ILPRT: XTHL ; Save HL, get HL=message ; ILPLP: MOV A,M ; Get the character INX H ; To next character ORA A ; End of message? JZ ILPRET ; Yes, return CALL CTYPE ; Type the message JMP ILPLP ; Loop ; ILPRET: XTHL ; Restore HL RET ; Past message ; ;*********************************************************************** ; start of LOGCAL routines ; ; The following allocations are used by the LOGCALL routines ; DEFAULT$DISK: DB 0 ; Disk for open stored here DEFAULT$USER: DB 0 ; User for open stored here FCBCALLER: DB 0,'LASTCALR???' ; Last caller file FCB DB 0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0 CALLERPTR: DW LOGBUF ; FCBLOG: DB 0 ; Log file FCB ; DB 'TWIT ' DB 'LST' DB 0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0 LOGPTR: DW DBUF LOGCNT: DB 0 ; SPACE: DB ' ' ; YYSAV: DB 0 MMSAV: DB 0 DDSAV: DB 0 MNSAV: DB 0 ; ; Main log file routine, adds record to log file ; LOGCALL: MVI A,LASTDRV-'A' STA DEFAULT$DISK MVI A,LASTUSR STA DEFAULT$USER LXI D,FCBCALLER CALL OPENF ; Open LASTCALR file JNZ LOGC1 CALL ILPRT DB CR,LF,'No LASTCALR file found',CR,LF,0 JMP BDOS ; LOGC1: MVI C,SETRRD ; Get random record # LXI D,FCBCALLER ; (for first record in file) CALL BDOS LXI D,DBUF ; Set DMA to DBUF MVI C,SETDMA CALL BDOS LXI D,FCBCALLER ; Read first (& only) record MVI C,RRDM CALL BDOS ; IF NOT MBBS LXI H,DBUF ; Set pointer to beginning of record ENDIF ; If normal, start at beginning ; IF MBBS LXI H,DBUF+11 ; Set pointer to skip log on date ENDIF ; As MBBS puts date in LASTCALR ; SHLD CALLERPTR LXI D,LOGBUF ; Set DMA address to LOGBUF MVI C,SETDMA CALL BDOS XRA A STA FCBLOG+12 STA FCBLOG+32 MVI A,TWTDRV-'A' STA DEFAULT$DISK MVI A,TWTUSR STA DEFAULT$USER LXI D,FCBLOG CALL OPENF ; Open log file JNZ LOGC4 ; If file exists, skip create LXI D,FCBLOG MVI C,MAKE ; Create a new file if needed CALL BDOS INR A JNZ LOGC2 CALL ILPRT DB CR,LF,'No DIR space: LOG',CR,LF,0 JMP BDOS ; LOGC2: MVI C,SETRRD ; Set random record # LXI D,FCBLOG ; (for first record in file) CALL BDOS ; LOGC3: MVI A,EOF STA LOGBUF JMP LOGC4B ; LOGC4: MVI C,CFSIZE ; Get file length LXI D,FCBLOG CALL BDOS ; (end+1) LHLD FCBLOG+33 ; Back up to last record MOV A,L ORA H JZ LOGC3 ; Unless zero length file DCX H SHLD FCBLOG+33 LXI D,FCBLOG MVI C,RRDM ; And read it CALL BDOS ; LOGC4B: CALL RSTLP ; Initialize LOGPTR and LOGCNT ; LOGC6: CALL GETLOG ; Get characters out of last record CPI EOF JNZ LOGC6 ; Until EOF LDA LOGCNT ; Then backup one character DCR A STA LOGCNT LHLD LOGPTR DCX H SHLD LOGPTR ; ; WDRV1: LXI H,CMDLN ; Now the command line arguments MVI B,29 CALL PUTSTR ; LXI H,SPACE MVI B,10 CALL PUTSTR ; CALL GETBDAT ; IF RTC, get current date PUSH B ; (save DD/YY) CALL PNDEC ; Print MM MVI A,'/' ; '/' CALL PUTLOG POP PSW ; Get DD/YY PUSH PSW ; Save YY CALL PNDEC ; Print DD MVI A,'/' ; '/' CALL PUTLOG POP B ; Get YY MOV A,C CALL PNDEC ; Print YY CALL PUTSP ; ' ' CALL GETBTIM ; IF RTC, get current time STA MNSAV ; Save min MOV A,B ; Get current hour CALL PNDEC ; Print hr to file MVI A,':' ; With ':' CALL PUTLOG ; Between HH:MM LDA MNSAV ; Get min CALL PNDEC ; And print min CALL PUTSP ; Print a space ; IF OXGATE XRA A STA CMMACNT ; Clear comma count ENDIF ; CLOOP: CALL GETCALLER ; And the caller CPI EOF JZ QUIT CPI CR ; Do not print 2nd line of 'LASTCALR' JNZ CLOP1 CALL PUTLOG MVI A,LF CALL PUTLOG ; And add a LF JMP QUIT ; CLOP1: CPI ',' ; Do not print the ',' between names JNZ CLOP2 ; IF OXGATE LDA CMMACNT ; Get comma count INR A STA CMMACNT CPI 2 ; If reached second comma, do CRLF exit JZ CLOPX ENDIF ; MVI A,' ' ; Instead send a ' ' CLOP2: CALL PUTLOG JMP CLOOP ; IF OXGATE CLOPX: MVI A,CR ; Cloop exit... do a CRLF and finnish up. CALL PUTLOG MVI A,LF CALL PUTLOG ENDIF ; QUIT: MVI A,EOF ; Put in EOF CALL PUTLOG LDA LOGCNT ; Check count of chars in buffer CPI 1 JNZ QUIT ; Fill last buffer & write it LXI D,FCBCALLER ; Close lastcaller file MVI C,CLOSE CALL BDOS INR A JZ QUIT1 LHLD FCBLOG+33 ; Move pointer back to show DCX H ; Actual file size SHLD FCBLOG+33 LXI D,FCBLOG ; Close log file MVI C,CLOSE CALL BDOS INR A RNZ ; If OK, return now... ; QUIT1: CALL ILPRT DB CR,LF,'Cannot close TWIT file',CR,LF,0 JMP BDOS ;-------------------------------------------------------------- ; logcal support routines ; ; Gets a single byte from DBUF ; GETCALLER: LHLD CALLERPTR MOV A,M INX H SHLD CALLERPTR RET ; ; Gets a single byte from log file ; GETLOG: LDA LOGCNT INR A STA LOGCNT CPI 129 JZ EOLF LHLD LOGPTR MOV A,M INX H SHLD LOGPTR RET ; EOLF: LHLD FCBLOG+33 INX H SHLD FCBLOG+33 LXI H,LOGBUF+1 SHLD LOGPTR MVI A,1 STA LOGCNT MVI A,EOF RET ; ; Open file with FCB pointed to by DE (disk/user passed in DEFAULT$DISK ; and DEFAULT$USER) ; OPENF: PUSH D ; Save FCB address LDA DEFAULT$DISK ; Get disk for file CALL RECDRX ; Log into it LDA DEFAULT$USER ; Get default user CALL RECARE ; Log into it POP D ; Get FCB address MVI C,OPEN ; Open file CALL BDOS CPI 255 ; Not present? RET ; Return to caller ; ; Write character to log file ; PUTLOG: LHLD LOGPTR ; Get pointer MOV M,A ; Put data INX H ; Increment pointer SHLD LOGPTR ; Update pointer MOV B,A ; Save character in B LDA LOGCNT ; Get count INR A ; Increment it STA LOGCNT ; Update count CPI 129 ; Check it RNZ ; If not EOB, return PUSH B ; Save character LXI D,FCBLOG ; Else, write this sector MVI C,WRDM CALL BDOS ORA A ; ADVRCP: LHLD FCBLOG+33 ; Advance record number INX H SHLD FCBLOG+33 CALL RSTLP ; Reset buffer pointers POP PSW ; Get saved character JMP PUTLOG ; Put it in buffer and return ; RSTLP: LXI H,LOGBUF ; Reset pointers SHLD LOGPTR ; And return MVI A,0 STA LOGCNT RET ; ; Print number in decimal format (into log file) IN: HL=binary number ; OUT: nnn=right justified with spaces ; PNDEC3: MOV A,H ; Check high byte ORA A JNZ DECOT ; If on, is at least 3 digits MOV A,L ; Else, check low byte CPI 100 JNC TEN CALL PUTSP ; TEN: CPI 10 JNC DECOT CALL PUTSP JMP DECOT ; ; Puts a single space in log file, saves PSW/HL ; PUTSP: PUSH PSW PUSH H MVI A,' ' CALL PUTLOG POP H POP PSW RET ; ; Print number in decimal format (into log file) ; PNDEC: CPI 10 ; Two column decimal format routine JC ONE ; One or two digits to area number? JMP TWO ; ONE: PUSH PSW MVI A,'0' CALL PUTLOG POP PSW ; TWO: MVI H,0 MOV L,A ; DECOT: PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 ; DECOT2: DAD B INX D JC DECOT2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOT MOV A,E ADI '0' CALL PUTLOG POP H POP D POP B RET ; ; Put string to log file ; PUTSTR: MOV A,M PUSH H PUSH B CALL PUTLOG POP B POP H INX H DCR B JNZ PUTSTR RET ; end of LOGCAL routine ; ; GETBTIM:LHLD 0001H ; Get COLDBOOT address DCX H ; (just before JMP WBOOT) MOV D,M ; And stuff in DE DCX H MOV E,M LXI H,25 ; Add offset to RTCBUF address DAD D ; (in HL) MOV E,M ; Get RTCBUF address INX H ; And MOV D,M ; Stuff in DE XCHG ; Then put in HL (RTCBUF address) ; MOV A,M ; Get hours on system CALL BCDBIN ; Convert BCD value to binary PUSH PSW ; Save hr on stack INX H ; Point to minute MOV A,M ; Get min CALL BCDBIN ; Convert BCD to binary POP B ; Get hr in B (min in A) RET ; And return ; GETBDAT:LHLD 0001H ; Get COLDBOOT addr DCX H ; (just before JMP WBOOT) MOV D,M ; And stuff in DE DCX H MOV E,M LXI H,25 ; Add offset to RTCBUF address DAD D ; (in HL) MOV E,M ; Get RTCBUF address INX H ; And MOV D,M ; Stuff in DE XCHG ; HL now points to RTC buffer ; LXI D,4 ; Offset to YY DAD D ; HL=YY Address MOV A,M ; Get YY CALL BCDBIN ; Make it binary STA YYSAV ; Save YY INX H ; Point to MM MOV A,M ; Get MM CALL BCDBIN ; Convert BCD to binary STA MMSAV ; Save it INX H ; Point to DD MOV A,M ; Get it CALL BCDBIN ; Convert it to binary MOV B,A ; Stuff DD in B LDA YYSAV ; Get YY MOV C,A ; Put YY in C LDA MMSAV ; Get MM in A RET ; And return ; ; Convert BCD value in A to binary in A ; BCDBIN: PUSH PSW ; Save A ANI 0F0H ; Mask high nibble RRC ; Move to low nibble RRC RRC RRC MOV C,A ; And stuff in C (C=A) MVI B,9 ; X10 (*9) ; BCDBL: ADD C ; Add orig value to A DCR B ; Decrement B JNZ BCDBL ; Loop nine times (A+(C*9)=A*10) MOV B,A ; Save result in B POP PSW ; Get original value ANI 0FH ; Mask low nibble ADD B ; +B gives binary value of BCD digit A RET ; Return ; RECDRX: MOV E,A ; Stuff it in E MVI C,SELDSK ; Tell BDOS CALL BDOS ; Do it RET ; Back RECARE: MOV E,A ; Stuff it in E MVI C,SETUSR ; Tell BDOS what we want to do CALL BDOS ; Now do it RET ; ;*********************************************************************** ; ; Temporary storage area ; ;*********************************************************************** ; IF OXGATE CMMACNT:DB 0 ; Comma counter ENDIF ; SAVEHL: DW 0 ; Saves TBUF command line address EOFLG: DB 0 ; 'EOF' flag (1=yes) EOFCTR: DB 0 ; EOF send counter OUTADR: DW LOGBUF RECPTR: DW DBUF RECNBF: DW 0 ; Number of records in the buffer ; ; ; Disk buffer ; ORG ($+127)/128*128 ; DBUF EQU $ ; 16-record disk buffer STACK EQU DBUF-2 ; Save original stack address LOGBUF EQU DBUF+128 ; For use with LOGCAL ; ; ;*********************************************************************** ; ; BDOS equates ; ;*********************************************************************** ; RDCON EQU 1 ; Get character from console WRCON EQU 2 ; Output to console DIRCON EQU 6 ; Direct console output PRINT EQU 9 ; Print string function VERNO EQU 12 ; Get CP/M version number SELDSK EQU 14 ; Select drive OPEN EQU 15 ; 0FFH = not found CLOSE EQU 16 ; " " SRCHF EQU 17 ; " " SRCHN EQU 18 ; " " DELET EQU 19 ; Delete file READ EQU 20 ; 0=OK, 1=EOF WRITE EQU 21 ; 0=OK, 1=ERR, 2=?, 0FFH=no dir. space MAKE EQU 22 ; 0FFH=bad RENAME EQU 23 ; Rename a file CURDRV EQU 25 ; Get current drive SETDMA EQU 26 ; Set DMA SETUSR EQU 32 ; Set user area to receive file RRDM EQU 33 ; Read random WRDM EQU 34 ; Write random CFSIZE EQU 35 ; Compute file size SETRRD EQU 36 ; Set random record GETTIM EQU 105 ; CP/M Plus get date/time BDOS EQU 0005H TBUF EQU 0080H ; Default DMA address FCB EQU 005CH ; System FCB FCBEXT EQU FCB+12 ; File extent FCBRNO EQU FCB+32 ; Record number RANDOM EQU FCB+33 ; Random record field ; END