; ;VIDEO MONITOR - 80 BY BARRY A. WATZMAN 4/26/79 ;1/05/80 VERSION - USES INTERRUPTS AND SOME Z80 CODE ; ;*********************************************** ; ;DISK CONTROLLER EQUATES ; DCOM EQU 0F8H DSKST EQU DCOM TRACK EQU DCOM+1 SECTP EQU DCOM+2 DDATA EQU DCOM+3 WAIT EQU DCOM+4 ; ;CONSOLE AND NON-DISK I/O EQUATES ; ;PORT A: SERIAL IS TTY FOR DIABLO 1620 ; PARALLEL IS LPT: UNIVERSAL PARALLEL OUTPUT (ONLY) PORT ; ;PORT B: SERIAL IS SP2: ASSIGNABLE TO ANYTHING ; PARALLEL IS CRT: KEYBOARD ; PORTA EQU 60H ;SIO = TTY: PIO: = LPT: PORTB EQU 30H ;SIO = SP2: PIO: = CRT: ; ;VIDEO DRIVER EQUATES ; VIO EQU 0F000H LINLEN EQU 80 LPP EQU 24 PGLEN EQU LPP*LINLEN ; ;VIDEO DRIVER AND MONITOR RAM AREAS IN UNUSED 128 ;BYTES OF THE VIDEO REFRESH RAM MEMORY ; ORG VIO VIDBF DS PGLEN ;REFRESH RAM AREA ROW DS 1 ;CURSOR ROW (0-23) COLMN DS 1 ;CURSOR COLUMN (0-79) POLFLG DS 1 ;INVERSE VIDEO FLAG INVST DS 1 ;INVERTED COPY OF STATUS MODE BYTES INSFLG DS 1 ;INSERT MODE TOGGLE ESCFLG DS 1 ;ESCAPE FLAG ESC2CH DS 1 ;STORAGE FOR 2ND CHAR OF ESC SEQ ACTLTB DS 2 ;ADDR OF ALT CTL CHAR TABLE AESCTB DS 2 ;ADDR OF ALT ESC CHAR TABLE DS 2 ;WAS ALT CMD TABLE ADDR FOR MONITOR ASCPTR DS 2 ;ADDR FOR ASCII POINTER IN DUMP STRT EQU ASCPTR ;STARTING ADDR FOR MEMORY TESTS CURADR DS 2 ;ABSOLUTE CURSOR ADDRESS PROTFL DS 1 ;0FFH HERE ==> BIT 80H=PROTECT CURCHR DS 1 ;STORE CHAR UNDER CURSOR NEWCHR DS 1 ;STORE CHAR TO BE DISPLAYED EXSTAT DS 1 ;COPY OF STATUS W/EXTRA BITS TABTBL DS 10 ;TAB STORAGE (BIT MAPPED) FMTDF DS 5 ;FOR COMPATABILITY W/VIO PFFLG DS 1 ;PROT FLD ENCOUNTERED FLAG FOR TAB FNCT FMTDF2 DS 3 ;FOR COMPATABILITY IORAM DS 3 ;ADDR OF RAM FOR I/O CMDS ASCFLD DS 16 ;ASCII AREA DS 68 STATS DS 1 ;HARDWARE COMMAND BYTE ; ;PROGRAM ENTRY POINTS ; ; ORG 8000H ;FOR DEBUG ; MONITOR:JMP MONTR ;MONITOR ENTRY POINT JMP WMSTRT ;MONITOR WARM START DSPLY: JMP CRTOUT ;CHARACTER DISPLAY ENTRY JMP INITL LXI SP,STATS-1 CALL INITL LF80F: CALL GTCHR JMP LF80F ; ;INITIALIZE NON-DISK I/O DEVICES ; INITL: DI ;NO INT. FOR NOW MVI A,1 ;RESET AND INT. ACK. OFF OUT PORTA+2 ;ON DEVICE A OUT PORTB+2 ;AND B MVI A,0 ;NO INT. ENABLED OUT PORTA+3 ;ON DEVICE A MVI A,04H ;ENABLE SENS (KBD) OUT PORTB+3 ;ON DEVICE B MVI A,88H ;1200 BAUD 1 STOP BIT OUT PORTA ;FOR DIABLO MVI A,0C0H ;9600 BAUD 1 STOP BIT OUT PORTB ;FOR 2ND CONSOLE/RDR-PCH PUSH H ;SAVE H,L ; ;NOW DO VIDEO INITIALIZATION ; XRA A LXI H,ROW ;POINT TO START OF VAR. STORAGE MVI B,(ASCFLD+16-ROW) ;LENGTH OF STORAGE AREA INIT1: MOV M,A ;CLEAR STORAGE INX H ;POINT TO NEXT DCR B ;DECR. COUNT JNZ INIT1 ;MORE IF NOT DONE LXI H,VIDBF ;GET 1ST CHAR ADDRESS SHLD CURADR ;SAVE ABSOLUTE CURSOR ADDR CALL CLEAR ;CLEAR SCREEN ; ;FOLLOWING IS FOR VIO SOFTWARE COMPATABILITY ; LXI H,80 SHLD FMTDF MVI A,23 STA FMTDF+2 LXI H,1920 SHLD FMTDF+3 LXI H,VIO+7FFH SHLD FMTDF2+1 ; LXI D,DISP3 ;RETURN ADDR FOR ALL COMMANDS PUSH D ;PUT IT ON STACK TOP MVI A,8 ;ASCII TEXT MODE, POS VID, 80X24 INIT2: STA EXSTAT ;SOFTWARE'S STATUS BYTE STA STATS ;HARDWARE'S STATUS BYTE ANI 0CH ;MASK MODE BITS XRI 0CH ;INVERT THEM STA INVST ;AND SAVE ; ;COME HERE AFTER PROCESSING AN ESCAPE SEQ. ; ESCEX: XRA A ;GET A ZERO STA ESCFLG ;CLEAR ESCAPE OUTSTANDING FLAG RET ;DONE ; ;CHARACTER OUTPUT ENTRY POINT ; CRTOUT: PUSH H ;SAVE H,L LHLD CURADR ;GET ABS. CURSOR ADDR LDA CURCHR ;GET CHAR UNDER CURSOR MOV M,A ;PUT IT BACK LXI H,NEWCHR ;POINT AT CURRENT CHAR MOV M,C ;SAVE NEW CHAR MOV A,C ;GET IT BACK CPI 1BH ;ESCAPE ? JZ ESCCH ;YES - PROCESS ESCAPE LDA ESCFLG ;NO - PREV ESC OUTSTANDING ? ORA A ;SET FLAGS JNZ ESCCH ;YES - PROCESS ESCAPE MOV A,C ;GET CHAR AGAIN CPI 7FH ;DELETE ? JZ CTLCH ;YES - PROCESS LDA INVST ;GET INVERTED STATUS ORA A ;ZERO - E.G. USING ALL 256 CHARS JZ DISP ;YES - HANDLE SEPARATELY MOV A,C ;GET CHAR AGAIN CPI 0FFH ;PAD CHARACTER ? JZ DISP3 ;SKIP PROCESSING ANI 7FH ;MASK PARITY MOV M,A ;SAVE IT THAT WAY CPI 20H ;SEE IF CNTL CHAR JM CTLCH ;WE DON'T DISPLAY CNTL CHARS ; ;PUT CHAR ON SCREEN ; DISP: LHLD CURADR ;GET CUR ADDR LDA INSFLG ;GET INSERT MODE SWITCH ORA A ;SET FLAGS JZ DISP1 ;IF NOT USING INSERT PUSH H ;SAVE CURADR ON STACK CALL SP2END ;COMPUTE SPACES TO END OF FIELD DCX B ;DCR BY 1 DAD B ;ADD TO H,L = END ADDR MOV D,H ;DUPE H,L IN D,E MOV E,L DCX D ;DCR D,E BY 1 XCHG MOV A,C ORA B JZ DISP5 DB 0EDH,0B8H ;Z80: LDDR DISP5: POP H ;RESTOR CURSOR ADDR DISP1: LDA POLFLG ;GET CURSOR ON-OFF FLAG ANI 80H ;MASK REV. VIDEO BIT MOV B,A ;SAVE IN B LDA NEWCHR ;GET CHAR ORA B ;TURN CURSOR ON (MAYBE) MOV M,A ;STORE CHAR CALL RIGHT ;MOVE CURSOR RIGHT 1 POSITION DISP2: CALL CALCAD ;CALC ABS CUR ADDR FROM ROW, COL DISP3: LHLD CURADR ;GET CURSOR ADDRESS MOV A,M ;GET CHAR STA CURCHR ;SAVE IT ORI 80H ;TURN CURSOR ON MOV M,A ;PUT IT BACK LDA INVST ;GET STATUS BYTE ORA A ;SET FLAGS JNZ DISP4 MVI M,7FH ;IN GRAPHICS MODE, DEL IS CURSOR DISP4: POP H ;RESTORE H,L (ONLY REG. SAVED) RET ;AND GO HOME ; ;ROUTINE TO MOVE CURSOR RIGHT ; RIGHT: CALL BMPCUR ;ADV. COL. NO. CZ LINFD1 ;UPDATE ROW ON OVERFLOW LDA PROTFL ;GET PROTECT ON/OFF FLAG XCHG ;NEW CUR ADDR TO H,L ANA M ;SEE IF THIS CHAR PROT. JM RIGHT ;CUR CAN'T STOP ON PROT. FIELD RET ;DONE, CUR ON UPPROT. FIELD ; ;ROUTINE TO DO LINE-FEED (CURSOR DOWN) ; LINFD: LXI H,ROW ;POINT TO CURRENT ROW LINFD1: INR M ;INCREMENT IT MVI A,LPP-1 ;MAX ALLOWED ROW CMP M ;OVERFLOW ? RP ;NO-DONE DCR M ;OVERFLOW - DCR ROW LDA EXSTAT ;GET EXTENDED STATUS BYTE ANI 8CH ;MASK MODE & SCROLL/PG BITS JM LINFD2 ;JUMP IF IN PAGE MODE CPI 0CH ;SEE IF IN GRAPHICS MODE JNZ SCRLUP ;SCROLL IF IN SCROLL MODE LINFD2: XRA A ;GET A ZERO MOV M,A ;MAKE IT THE NEW ROW INX H ;POINT TO COLUMN MOV M,A ;GET IT RET ;RETURN W/COLUMN IN ACC ; ;COME HERE TO SCROLL SCREEN UP 1 LINE ; SCRLUP: LXI B,PGLEN-LINLEN ;GET NO. CHRS TO MOVE LXI H,VIDBF+LINLEN ;H,L = 1ST CHAR TO MOVE LXI D,VIDBF ;GET ADDR OF VIDEO BUFFER (DEST) DB 0EDH,0B0H ;Z80: LDIR JMP DELIN1 ;CLEAR LAST LINE AND RETURN ; ;PROCESS CONTROL CHARS -- H,L=POINTER TO CURRENT CHARACTER ; CTLCH: XCHG ;CHAR POINTER TO D,E LHLD ACTLTB ;GET ADDR OF ALT. TABLE MOV A,H ;GET HIGH BYTE ORA L ;SET FLAGS JZ CTLCH1 ;SKIP NEXT IF USING STD. TABLE CALL TBSCH ;ELSE DO SEARCH OF ALT. TABLE JNZ CTLCH3 ;AND DISPATCH IF MATCH FOUND CTLCH1: LDA NEWCHR ;GET CURRENT CHARACTER CTLCH2: LXI H,CTLTAB ;POINT TO STD. TABLE CALL TBSCH ;SEARCH IT JNZ CTLCH3 ;IF MATCH FOUND MOV A,B ;GET CHAR BACK CPI 'G'-40H ;BELL ? JZ DISP JMP DISP3 ;OTHER, DON'T DISPLAY CTLCH3: LXI D,DISP2 ;GET RET POINT FOR CNTL CHR ROUTINES PUSH D ;AND PUT IT ON STACK TOP LXI D,COLMN ;LET D,E POINT TO CURSOR COLUMN PCHL ;AND DISPATCH TO ROUTINE ; ;COME HERE TO PROCESS ESCAPE SEQUENCE, BOTH ON ;ESC. ITSELF & SUBSEQUENT CHARS. H,L POINTS A NEWCHR. ; ESCCH: LXI D,DISP2 ;GET RETURN ADDR FOR WHEN DONE PUSH D ;PUSH IT ON STACK XCHG ;SAVE CURRENT CHAR ADDR IN D,E LXI H,ESCFLG ;POINT TO ESCAPE FLAG MOV A,M ;GET IT INR M ;INCREMENT IT IN MEMORY ONLY ORA A ;SET FLAGS RZ ;NO ESC WAS OUTSTANDING, NOW IS DCR A ;ESC WAS OUTSTANDING, DCR OLD FLAG INX H ;POINT TO 2ND CHAR OF ESC. SEQ LDAX D ;GET CHAR UNDER CURSOR JNZ ESCCH2 ;SKIP NXT IF ON 3RD OR 4TH CHAR (DCA) MOV M,A ;ALL OTHERS, SAVE 2ND ESC. SEQ CHAR ESCCH2: LHLD AESCTB ;POINT TO ALT. ESC. TABLE MOV A,H ;GET HIGH ADDRESS BYTE ORA L ;SET FLAGS LDAX D ;GET ESC. SEQ 2ND CHAR JZ ESCCH3 ;USE STD. TABLE CALL TBSCH ;ELSE SEARCH ALT. TABLE JNZ ESCCH4 ;EXEC. ROUTINE IF MATCH FOUND ESCCH3: LDA ESC2CH ;ELSE RESTORE 2ND CHAR OF ESC. SEQ ANI 0DFH ;MASK OUT 20H BIT LXI H,ESCTB ;POINT TO ESCAPE TABLE CALL TBSCH ;SEARCH IT JZ ESCEX ;NO MATCH FOUND, CLR ESC. FLAG ESCCH4: LDA EXSTAT ;GET EXTENDED STATUS BYTE LXI D,INIT2 ;RET ADDR TO UPDATE STATUS BYTES PCHL ;EXECUTE ESCAPE SUBROUTINE ; ;ROUTINES TO MOVE CURSOR UP/LEFT ONE POSITION ; UP: DCX D ;POINT AT ROW INSTEAD OF COL LEFT: LDAX D ;GET COLUMN (ROW) ORA A ;SET FLAGS RZ ;CAN'T BE LESS THAN ZERO, DONE DCR A ;ELSE DECREMENT STAX D ;AND PUT BACK RET ;THEN RETURN ; ;ROUTINE TO DO A CARRIAGE RETURN ; CRET: XRA A ;GET A ZERO STA INSFLG ;CLEAR INSERT MODE STAX D ;STORE ZERO AS COLUMN RET ; ;ROUTINES TO TOGGLE THE PROTECT AND INSERT MODES ; PROT: LXI H,PROTFL ;POINT AT PROTECT ON/OFF FLAG JMP TOGLE ;COMPLEMENT IT & RETURN INTOG: LXI H,INSFLG ;POINT AT INSERT MODE FLAG TOGLE: MOV A,M ;GET IT CMA ;COMPLEMENT IT MOV M,A ;PUT IT BACK RET ;DONE ; ;ROUTINE TO CLEAR SCREEN ; CLEAR: LXI D,PGLEN ;NO CHARS/SCREEN LXI H,VIDBF ;POINT TO START OF BUFFER CLEAR1: LDA PROTFL ;GET PROTECT FLAG ANI 80H ;MASK HIGH ORDER BIT ANA M ;AND WITH CHAR JM CLEAR2 ;DON'T CLEAR PROTECTED MVI M,' ' ;ELSE REPLACE WITH SPACE CLEAR2: INX H ;INCREMENT POINTER DCX D ;DECR. COUNT OF CHARS TO CLEAR MOV A,D ;SEE IF COUNT IS 0 ORA E ; JNZ CLEAR1 ;NO - DO MORE,ELSE HOME CURSOR ; ;ROUTINE TO HOME CURSOR ; HOME: LXI H,0 ;MAKE H,L ZERO SHLD ROW ;SAVE 0 AS BOTH ROW & COL RET ;DONE ; ;ROUTINE TO ERASE TO END OF LINE OR FIELD ; EREOF: CALL SP2END ;GET COUNT OF CHRS TO CLEAR IN C EREOF1: LDA PROTFL ;GET PROTECT FLAG ORA A ;ZERO ? JZ EREOF2 ;YES - CLEAR TO END OF LINE MOV A,M ;NO - GET CHAR ORA A ;SET FLAGS JM EREOF3 ;DON'T CLEAR IF PROTECTED EREOF2: MVI M,' ' ;CLEAR OUT A CHAR EREOF3: INX H ;INCREMENT POINTER DCR C ;DECR. COUNT JNZ EREOF1 ;CONTINUE IF NOT DONE RET ;ELSE RETURN ; ;CONTROL-V (TOGGLE INVERSE VIDEO) ROUTINE ; CNTLV: LXI H,POLFLG ;POINT TO INVERSE VIDEO FLAG JMP TOGLE ;TOGGLE IT & RET ; ;ROUTINE TO DELETE A CHAR & CLOSE UP LINE ; CHDEL: CALL SP2END ;SPACES TO END OF FIELD IN C LHLD CURADR ;GET CURRENT CURSOR ADDR MOV D,H ;DUPLICATE IN D,E MOV E,L ; INX H ;POINT TO NEXT CHAR DB 0EDH,0B0H ;Z80: LDIR MVI A,' ' ;GET SPACE DCX D ;POINT TO LAST POS. IN FIELD STAX D ;PUT SPACE THERE RET ;DONE ; ;ROUTINE TO COMPUTE NO. OF SPACES TO END OF LINE OR ;START OF NEXT PROTECTED FIELD & LEAVE ANS. IN REG. C ; SP2END: LDA PROTFL ;GET PROTECT STATUS ANI 80H ;MASK 80H BIT MOV D,A ;SAVE IN D LHLD CURADR ;GET CURSOR ADDRESS PUSH H ;SAVE ON STACK LDA COLMN ;GET CURRENT COLUMN MOV E,A ;SAVE IN E. LXI B,0 ;MAKE B,C ZERO SP2END1:MVI A,LINLEN ;GET MAX ALLOWED COLUMN SUB E ;SUBTRACT CUR COLUMN INR E ;INR CUR COLUMN INX H ;INR CURSOR ADDR INR C ;INR COUNT OF SPACES TO CLR DCR A ;DCR COUNT SPACES LEFT THIS LINE JZ SP2END2 ;DONE IF WENT TO END OF LINE MOV A,M ;ELSE GET CHAR AT CUR POSITION ANA D ;SEE IF PROTECTED JP SP2END1 ;CONTINUE IF NOT PROTECTED SP2END2:POP H ;RESTORE CUR CURSOR ADDR TO H,L RET ;DONE ; ;COMMAND TABLE SEARCH ROUTINE FOR SINGLE CHAR COMMANDS ; TBSCH: MOV B,A ;SAVE COMMAND TO LOOK UP IN B TBSCH1: MOV A,M ;GET TABLE ENTRY LXI D,ROW ;LET D POINT TO ROW,COL ORA A ;SET FLAGS RZ ;END OF TABLE, RETN W/ZERO SET CMP B ;ELSE CHECK FOR MATCH JNZ TBSCH2 ;AND INR. PAST ADDR. IF NO MATCH INX H ;MATCH - POINT AT ADDR MOV E,M ;GET LOW ADDR IN E INX H ;POINT TO HIGH MOV D,M ;GET IT IN D XCHG ;XCHG - ADDR TO H,L ORA A ;SET FLAGS NON-ZERO RET ;AND RETURN TBSCH2: INX H ;POINT TO LOW ADDR INX H ;POINT TO HIGH ADDR INX H ;POINT TO NEXT TABLE ENTRY JMP TBSCH1 ;AND TEST IT ; ;ROUTINE TO DELETE A LINE ; DELIN: CALL INLIN1 ;PUT CUR AT COL. 0 OF CUR LINE DAD D ;H,L POINTS TO 1ST CHAR NEXT LINE MOV A,B ORA C JZ DELIN1 DB 0EDH,0B0H ;Z80: LDIR DELIN1: LXI B,LINLEN ;LINE SIZE TO B,C XCHG JMP FLWSP ;FILL WITH SPACES ; ;ROUTINE TO INSERT A LINE ; INLIN: CALL INLIN1 ;B,C = NO CHRS AFTER CURSOR - 1 LINE PUSH H ;MAX COL SIZE DAD D ;ADD LINE LEN TO CURSOR DAD B ;ADD IN NO CHARS AFTR CUR - 1 LINE = END SCRN XCHG ;TO D,E (END OF SCREEN) DAD B ;H,L=CUR, B,C=NO CHR AFTER - 1 LINE DCX H ;DCR BY 1 FOR EA CHAR DCX D MOV A,B ORA C JZ INLIN5 DB 0EDH,0B8H ;Z80: LDDR INLIN5: POP B ;MAX COL SIZE INX H ;POINT TO 1ST CHAR OF "NEW" LINE FLWSP: MVI M,20H ;BLANK OUT CHAR INX H ;INR POINTER DCR C ;DCR COUNT JNZ FLWSP ;DO TILL DONE RET ; INLIN1: XRA A ;GET A ZERO STAX D ;NEW COLUMN CALL CALCAD ;UPDATE ABS CURR ADDR PUSH H ;SAVE ON STACK XCHG ;AND IN D,E LXI H,VIDBF+PGLEN ;GET END OF SCRN ADDR MOV A,D ;NEGATE CURSOR CMA MOV D,A MOV A,E CMA MOV E,A INX D DAD D ;HL = NO CHARS AFTER CURSOR PUSH H ;SAVE ON STACK POP B ;RESTORE BC (NO CHARS AFTER CUR) POP H ;CURSOR IN HL XCHG ;DE = CUR LXI H,LINLEN MOV A,L ;TO A INLIN2: DCX B ;DCR B, NO CHRS DCR A ;CONTINUE TILL B,C REDUCED BY 1 LINE JNZ INLIN2 RET ; ;EXTENDED TEXT MODE (UPPER CHAR SET) ; EXTXT: ANI 0F3H ;CLEAR MODE BITS ORI 04H ;SET TO EXTENDED TEXT XCHG PCHL ; ;GRAPHICS MODE (ALL 256 BYTES AVAIL) ; GRAPH: ORI 0CH ;SET GRAPHICS BITS XCHG PCHL ; ;ASCII TEXT MODE (LOWER CHAR SET) ; TXT: ANI 0F3H ;CLEAR MODE BITS ORI 08H ;SET TO TEXT MODE XCHG PCHL ; ;TOGGLE SCROLL/PAGE MODE ; SCTOG: XRI 80H ;INVERT SCROLL BIT XCHG PCHL ; ;TOGGLE INVERSE VIDEO BIT ; INVRT: XRI 10H ;INVERT POLARITY BIT XCHG PCHL ; ;CLEAR ALL TABS ; CATAB: LXI H,TABTBL ;POINT TO 80 BIT TAB TABLE MVI B,0AH ;10 BYTES OF 8 TAB STOPS EA. XRA A ;GET ZERO C8TABS: MOV M,A ;CLEAR 8 TABS INX H ;POINT TO NEXT BLOCK OF 8 TABS DCR B ;DCR COUNT JNZ C8TABS ;CONTINUE TILL DONE JMP ESCEX ;THEN CLR ESC FLAG & GO HOME ; ;SET OR CLEAR TAB STOPS ; SCTAB: CALL SCTAB2 ;GET TAB BYTE, BIT NO. & BYTE COUNT XRI 80H ;CHANGE 80H BIT SCTAB1: RRC DCR B ;DCR COUNT OF BIT OFFSET JNZ SCTAB1 ;KEEP ROTATING IF NOT DONE STAX D ;PUT BYTE BACK JMP ESCEX ;CLEAR ESC. FLAG & GO HOME ; SCTAB2: LDA COLMN ;GET CURRENT COLUMN TO H MOV H,A ;TO H INR H ;INITIALIZATION FOR FOLLOWING ROUTINE LXI D,TABTBL ;POINT TO TAB TABLE SCTAB3: MVI C,8 ;NO TAB STOPS PER BYTE IN TABLE SCTAB4: DCR H ;POINT TO PREV. COLUMN JZ SCTAB5 ;JUMP IF COL. EQ. ZERO DCR C ;ELSE DCR. BIT COUNT JNZ SCTAB4 ;AND REPEAT IF NOT ZERO INX D ;BIT COUNT = 0, GET NEXT BYTE JMP SCTAB3 ;GO UNTIL COL. EQ. ZERO SCTAB5: LDAX D ;GET TAB TABLE BYTE MOV B,C ;SAVE BIT COUNT SCTAB6: RLC DCR C ;DCR BIT COUNT JNZ SCTAB6 ;DO UNTIL BIT COUNT EQ. ZERO RET ; ;TAB TO NEXT TAB STOP OR UNPROTECTED FIELD ; TAB: XRA A ;GET A ZERO STA PFFLG ;CLEAR PROT. FIELD ENCOUNTERED FLAG TAB1: CALL BMPCUR ;ADVANCE 1 POSITION JNZ TAB2 ;JUMP IF NOT ON LAST COL. OF LINE INR M ;ELSE INR. ROW CMP M ;ROW = MAX. ALLOWED ROW ? JM LINFD2 ;YES, OFF SCREEN, DO HOME TAB2: LDA PROTFL ;GET PROTECT FIELD ON-OFF FLAG XCHG ANA M ;SET SIGN FLAG MOV A,M ;GET CHAR UNDER CURSOR LXI D,PFFLG ;POINT TO PROTECTED FIELD ENCOUNTERED FLAG JP TAB3 ;IF CURRENT POSITION NOT PROTECTED STAX D ;ELSE SET PROT. FIELD ENCOUNTERED FLAG JMP TAB1 ;AND TRY NEXT COL. TAB3: LDAX D ;SEE IF PROT FIELD PREV. ENCOUNTERED ORA A RM ;YES, NOW ON 1ST UNPROT AFTER PROTECTED FIELD CALL SCTAB2 ;SEE IF TAB STOP AT CUR. POSITION ORA A ;SET FLAGS RM ;DONE IF TAB STOP SET JMP TAB1 ;TAB STOP NOT SET & STILL IN UNPROT FIELD ; ;UPDATE ABSOLUTE CURSOR ADDR ; CALCAD: LXI D,LINLEN ;MAX ALLOWED COL. LHLD ROW ;CUR ROW, COLUMN MOV C,H ;DUPE IN B,C MOV B,L LXI H,VIDBF ;POINT TO DISPLAY BUFFER INR B ;INITIALIZE LOOP CALCAD1:DCR B ;FOR EA. ROW AFTER FIRST JZ CALCAD2 DAD D ;ADD NO. OF COLS/ROW TO SCREEN JMP CALCAD1 ;UNTIL DONE CALCAD2:DAD B ;THEN ADD CURRENT COL. NO SHLD CURADR ;AND SAVE RESULT = NEW CURSOR ADDR RET ; ;BUMP CURSOR POSITION BY 1 ; BMPCUR: LHLD CURADR ;GET CUR ADDR INX H ;INCREMENT IT SHLD CURADR ;PUT IT BACK XCHG LXI H,COLMN ;POINT TO CUR. COL. NO INR M ;INCREMENT IT MVI A,LINLEN ;GET MAX ALLOWED COL. SUB M ;COMPARE TO NEW COLUMN RNZ MOV M,A ;GONE OFF LINE, MAKE COL=0 DCX H ;POINT TO CURRENT ROW MVI A,LPP-1 ;MAX ALLOWED ROW RET ; ;DIRECT CURSOR ADDRESSING ; DCA: LXI H,ESCFLG ;ESCAPE SEQ. FLAG LXI D,ROW ;CURSOR ROW LDA NEWCHR ;CUR. CHAR SUI 20H MOV B,A ;SAVE CURRENT CHAR MOV A,M ;GET ESC. FLAG SUI 3 ;3RD CHAR OF ESC. SEQ ? RM ;NO, 1ST OR 2ND JNZ DCA2 ;IF 4TH CHAR OF ESC SEQ MVI A,LPP-1 ;MAX ALLOWED ROW DCA1: STAX D ;SAVE AS CURRENT ROW CMP B ;SEE IF SAME AS STRED CHAR RM ;DONE IF STORED CHAR > MAX. ROW MOV A,B ;ELSE GET STORED CHAR STAX D ;MAKE IT CUR ROW RET ;DONE ; DCA2: MVI M,0 ;CLEAR ESC FLAG MVI A,LINLEN ;MAX ALLOWED COL INX D ;POINT TO CUR COL DCR A ;DCR MAX ALLOWED COL JMP DCA1 ;UPDATE COL ; ;CONTROL CODE TABLE FOR VIDEO DISPLAY ; CTLTAB: DB 0DH DW CRET DB 0AH DW LINFD DB 'K'-40H DW UP DB 'L'-40H DW RIGHT DB 'H'-40H DW LEFT DB '^'-40H DW HOME DB 'Z'-40H DW CLEAR DB 'U'-40H DW EREOF DB 'V'-40H DW CNTLV ;ENTER INVERSE VIDEO MODE DB 'I'-40H DW TAB DB 7FH DW CHDEL DB 'T'-40H DW INTOG DB 'D'-40H DW DELIN DB 'E'-40H DW INLIN DB 'P'-40H DW PROT DB 0 ; ;ESCAPE SEQUENCE TABLE FOR VIDEO DISPLAY ; ESCTB: DB '='-20H DW DCA ;DIRECT CURSOR ADDR DB 'I' DW SCTAB DB 'I'-40H DW CATAB DB 'T' DW TXT DB 'E' DW EXTXT DB 'G' DW GRAPH DB 'S' DW SCTOG DB 'V' DW INVRT DB 0 ; ;AVAILABLE SPACE ; DS 128H ; ;MONITOR PROGRAM STARTS HERE ; MONTR: LXI SP,STATS CALL INITL ;INITIALIZE VIO & TU-ART LXI H,SGNON ;SIGNON MESSAGE CALL PMSG ;PRINT IT WMSTRT: DI ;TURN OFF INT. LXI SP,STATS CALL CRLF ;DO CR/LF MVI C,'?' ;PROMPT CALL CRTOUT CALL GTCHR ;WAIT FOR INPUT LXI D,WMSTRT ;PUSH RETURN ADDR ON STACK PUSH D LXI H,LFFC0 ;AND NOW TRY STD. TABLE CALL TBSCH RZ ;NO MATCH MVI B,1 DOIT: PCHL ;GO TO ROUTINE ; ;SIGN-ON MESSAGE & COPYRIGHT NOTICE ; SGNON: DB 'VM-80 By Barry A. Watzman',0 ; ;BOOT COMMAND - DISK BOOTSTRAP FOR TARBELL DISK ; BOOT: XRA A ;SEL DISK 0 OUT WAIT MVI A,43H ;LOW SPEED STEP IN COMMAND OUT DCOM ;IN CASE ON TRACK -1 IN WAIT ;WAIT FOR DONE MVI A,43H ;AGAIN, FOR SAFETY OUT DCOM IN WAIT MVI A,3 ;LOW SPEED 1771 HOME COMMAND OUT DCOM IN WAIT XRA A ;GET A ZERO MOV L,A MOV H,A PUSH H ;0 IS BOOT ENTRY POINT INR A ;A=1 OUT SECTP MVI A,8CH ;READ SECTOR COMMAND OUT DCOM RLOOP: IN WAIT ORA A ;SET FLAGS JP RDONE ;INTRQ IN DDATA MOV M,A INX H JMP RLOOP RDONE: IN DSKST ;READ DISK STATUS ORA A ; RZ ;00 IS ON STACK MOV C,A ;SAVE STATUS CALL CRLF ; MOV A,C ;GET STATUS BACK CALL HXSPOT ;PRINT IN HEX POP H ;TAKE BOOT ADDR OFF STACK RET ; ;GO COMMAND - JUMPS TO MEMORY ADDRESS WITH MONITOR RETURN ON STACK ; EXEC: CALL GETADR ;GET ARGUMENT ADDR IN H,L CPI 0AH ;SEE IF CAR. RET WAS LAST RNZ PCHL ;GO TO IT ; ;EXAMINE AND SUBSTITUTE MEMORY ; ENTR: CALL GETADR ;GET ADDR IN H,L CPI 0AH ;SEE IF CAR. RET. LAST RNZ ENTR1: CALL CRLF ;START ON NEW LINE ENTR2: CALL OUTADR ;OUTPUT ADDR IN H,L TO SCREEN MOV A,M ;GET CURRENT VALUE MOV E,A ;SAVE IN E CALL HXSPOT ;DUMP IT FOLLOWED BY A SPACE XCHG CALL GETAD1 ;GET HEX VALUE IN H,L XCHG MOV M,E ;STORE NEW VALUE IN MEMORY DCX H ;POINT TO PREV. LOCATION CPI '-' ;'-' TYPED ? JZ ENTR1 ;YES, DO PREV. LOCATION CPI 0AH ;CR/LF TYPED ? RNZ ;NO, EXIT ROUTINE INX H ;POINT TO ORIG. LOCATION INX H ;POINT TO NEXT LOCATION JMP ENTR2 ;DO IT ; ;ESCAPE COMMAND - USED ONLY TO SEND ESC. SEQ. TO VIO FROM KBD ; ESCAP: CALL GTCHR CPI '=' RNZ CALL GTCHR JMP GTCHR ; ;DUMP COMMAND ; DUMP: CALL LFD20 ;START, END, DIFF IN B,C XCHG ;STARTING ADDR NOW IN D,E LXI H,15 ;MAKE IT MULTIPLE OF 16 DAD B ; MOV A,L ; ANI 0F0H ; MOV C,A ;THERE MOV B,H DUMP1: CALL NLCHK ;DO CR/LF LXI H,ASCFLD ;POINT TO ASCII AREA SHLD ASCPTR ;SAVE IT MVI L,16 ;80 COLS, USE 16 BYTES/LINE XCHG CALL OUTADR ;WRITE ADDR FOLLOWED BY SPACE XCHG DUMP2: LDAX D ;GET BYTE PUSH H ;SAVE LINE COUNT IN L LHLD ASCPTR MOV M,A ;SAVE ASCII CHAR INX H ;INX ASCII POINTER SHLD ASCPTR ;PUT BACK POINTER CALL HXSPOT ;DUMP BYTE FOLLOWED BY SPACE INX D ;INR POINTER DCX B ;DCR COUNT POP H ;RESTORE COUNTER DCR L ;DCR BYTES THIS LINE JNZ DUMP2 ;MORE IF NOT DONE ; ;PRINT THE BUILT UP ASCII ; ASOUT: PUSH D ;SAVE H,L LXI H,ASCFLD ;POINT TO THE ASCII AREA MVI E,16 ;COUNT CALL PSPACE ;PRINT A SPACE ASOU1: MOV A,M ;GET ASCII CPI 20H ;SEE IF CNTL CHAR JC NOPRT CPI 7FH ;SEE IF DELETE JNC NOPRT CPI 5FH ;SEE IF UNDERLINE JNZ OKYDOK ;PRINT IT NOPRT: MVI A,'.' ;PRINT A PERIOD OKYDOK CALL CHROT ;PRINT THE CHAR INX H DCR E JNZ ASOU1 POP D DUMP3: MOV A,B ;TEST FOR DONE ORA C RZ ;DONE JMP DUMP1 ;THEN DO NEXT PAGE ; ;FILL MEMORY WITH A CONSTANT ; FILL: CALL LFDD7 MOV A,E MOV M,A DCX B MOV D,H MOV E,L INX D FILL1: MOV A,C ORA B RZ DB 0EDH,0B0H ;Z80: LDIR RET ; ;TEST MEMORY BETWEEN TWO ADDRESSES ; MEMCK: CALL LFF3A ;GET START & END IN D,E & H,L CPI 0AH ;WAS LAST CHAR CAR. RET.? RNZ ;ERROR IF NOT CALL CPHLDE ;SUBTRACT END ADDR. FROM START RNC ;IF START > END SHLD STRT ;SAVE AS STARTING ADDR XRA A ;START WITH ZERO MOV C,A ;PATTERN IN C WRO: MOV M,C ;PUT PATTERN IN LOC CALL CPHLDE ;SEE IF DONE INX H ;POINT TO NEXT LOC. JC WRO ;NOT DONE TRST: LHLD STRT ;GET BEGINNING ADDR RCW: MOV A,M ;GET CHAR THERE CMP C ;SAME AS PATTERN ? JZ GOOD ;YES, O.K. CALL OUTADR ;NO, BAD, PRINT ADDR MOV A,C ;WHAT SHOULD HAVE BEEN THERE CALL HXSPOT ;PRINT IT MOV A,M ;GET WHAT WAS THERE CALL HXDGOT ;PRINT IT CALL NLCHK GOOD: INR M ;INCREMENT DATA CALL CPHLDE ;TEST FOR DONE INX H ;INR POINTER JC RCW ;NOT DONE INR C ;DONE WITH PASS, INR PATTERN MOV A,C ;SHOW NEW PATTERN STA VIDBF JNZ TRST ;NEXT PASS ; ;SO FAR, SO GOOD, START THE WORM TEST ; MVI A,0C3H ;JUMP INST ; STA 038H ;RST 7 LXI H,WORM7 SHLD 38H+1 ;TRAP ADDR CALL ; LHLD STRT ;GET STARTING ADDR (WORM DEST) LXI D,3BH ;LOWEST ALLOWED LOCATION FOR WORM CALL CPHLDE ;COMPARE JC WORM1 ;USE 3B AS STARTING ADDR XCHG ;IN D,E WORM1: LXI H,WORM ;POINT TO WORM LXI B,CPHLDE-WORM ;WORM LENGTH PUSH D ;STARTING ADDR OF WORM DB 0EDH,0B0H ;Z80: LDIR LXI B,WRMDTA ;B,C HAS WORM DATA ADDR POP H ;RESTORE WORM ADDR INX H ;START AT WORM + 1 PCHL ;TO THE WORM ! ; ;ROUTINE TO MOVE THE WORM ; WORM2: POP D ;GET ADDR OF WORM+1 MOV H,D ;DUPLICATE MOV L,E ;IN H,L DCX H ;LAST ADDR OF WORM LXI B,CPHLDE-WORM ;WORM LENGTH DB 0EDH,0B8H ;Z80: LDDR LXI B,WRMDTA ;RESTORE BC XCHG ;RESTORE WORM ADDR TO H,L INX H INX H ;H,L = START OF WORM PCHL ;RETURN TO WORM ; ;REPORT WORM ADDR ; WORM5: INR A ;CHECK FOR ERROR JNZ ERRA ;ERROR TYPE A IF NOT 0FFH ORA B ;SET FLAGS JNZ ERRB ;ERROR TYPE B IF NOT ZERO POP H ;RECOVER ADDR ORA L ;ONLY WANT TO REPORT XX00 ADDRS. WORM5A: PUSH PSW CZ OUTADR ;ADDRESS OUTPUT POP PSW CZ NLCHK INX H ;ADJUST ADDRESS INX H ; INX H ; PCHL ;RETURN ; ;TRAP - SYSTEM CRASHED ; WORM7: MVI A,'T' ;TRAP INDICATOR CALL CHROT CALL PSPACE ;FOLLOWED BY SPACE POP H ;RECOVER ADDR DCX H ;ADJUST ADDR CALL OUTADR ;ADDRESS OUTPUT CALL NLCHK INX H ;ADJUST ADDR PCHL ;RETURN ; ;WORM ERROR REPORTING ; ERRA: MVI B,'A' ;TYPE A ERROR JMP ERR ;FINISH UP ERRB: MVI B,'B' ;TYPE B ERROR ERR: MOV C,A ;SAVE ERROR DATA MVI A,'#' ;ERROR SYMBOL CALL CHROT ;PRINT IT MOV A,B ;GET ERROR TYPE CALL CHROT ;PRINT IT CALL PSPACE ;PRINT SPACE MOV A,C ;GET BAD DATA CALL HXSPOT ;PRINT POP H ;SET UP XRA A ;MAKE ACC. ZERO JMP WORM5A ;FINISH UP ; ;THE WORM ITSELF ; WORM: RST 7 ;TRAP AT TAIL LDAX B ;MOVE DATA TO A; START OF WORM PUSH PSW ;SAVE DATA ON STACK MVI A,0FFH ;GET 2ND BYTE TO TEST POP B ;1ST BYTE NOW IN B CALL WORM5 ;REPORT ADDR RESTART:RST 7 ;TRAP RST 7 ;TRAP RST 7 ;TRAP WRMDTA: NOP ;REPORT ADDR RETURNS HERE CALL WORM2 ;MOVE WORM ; ;COMPARE H,L TO D,E ; CPHLDE: MOV A,H SUB D RNZ MOV A,L SBB E RET ; ;DIRECT I/O TO/FROM PORTS ; INPUT: DCR B OUTPT: CALL LFF3A MOV A,B RLC RLC RLC XRI 08H ORI 0D3H MOV D,L LXI H,IORAM MOV M,A PUSH H INX H MOV M,D INX H MVI M,0C9H LXI H,LFD87 XTHL MOV A,B ORA A MOV A,E PCHL LFD87: RNZ JMP HXSPOT ; ;COMPARE TWO REGIONS OF MEMORY ; MEMCP: CALL LFDD7 LFD96: LDAX D CMP M INX H INX D JZ LFDA5 CALL LFF8D XCHG CALL LFF90 XCHG CALL CTLS LFDA5: DCX B MOV A,B ORA C RZ JMP LFD96 ; ;SEARCH MEMORY FOR A 16-BIT VALUE ; SERCH: CALL LFDD7 PUSH H LXI H,0FFFFH CPI 0AH CNZ GETADR XTHL LFDBE: MOV A,M XTHL ANA H CMP D XTHL INX H JNZ LFDCF MOV A,M XTHL ANA L CMP E XTHL CZ LFF85 LFDCF: DCX B MOV A,B ORA C JNZ LFDBE POP B RET ; LFDD7: CALL LFD20 JMP LFF4A ; ;MOVE MEMORY FROM START UNTIL END TO DESTINATION ;OPERATES FROM LOW MEMORY TO HIGH MEMORY ; MOVEM: CALL LFDD7 JMP FILL1 ; ;CHARACTER INPUT ROUTINE ; GTCHR: IN PORTB ANI 20H JZ GTCHR ;NOT READY IN PORTB+3 ;CLEAR INTERRUPT IN PORTB+4 ;GET DATA ANI 7FH CPI 'C'-40H JZ WMSTRT CPI 0DH JZ CRLF JMP CHROT ; ;GET TWO 16-BIT VALUES IN D,E & H,L & SUBTRACT LEAVE ANS. IN B,C ; LFD20: CALL LFF3A ;GET 1ST NO. IN H,L, 2ND NO. IN D,E PUSH PSW ;SAVE LAST CHAR TYPED MOV A,E SUB L MOV C,A MOV A,D SBB H MOV B,A INX B ;WANT INCLUSIVE RANGE POP PSW RET ; ;OUTPUT H,L AS ASCII HEX FOLLOWED BY SPACE ; OUTADR: MOV A,H ;MOV H TO A CALL HXDGOT ;OUTPUT AS HEX ASCII MOV A,L ;MOVE L TO A & FALL THROUGH ; ;PRINT ACC. CONTENTS IN ASCII HEX FOLLOWED BY SPACE ; HXSPOT: CALL HXDGOT ;MOVE H TO A PSPACE: MVI A,' ' ;PRINT A SPACE ; ;CHARACTER OUTPUT ROUTINE - SAVES ALL REGS ; CHROT: PUSH D PUSH B PUSH PSW MOV C,A CALL CRTOUT POP PSW POP B POP D RET ; ;GET TWO ADDRESSES IN D,E AND H,L DELIMITED BY , OR SPACE ;IF ONLY ONE ADDR GIVEN, IT IS IN BOTH H,L & D,E ;ELSE, 1ST ADDR IN H,L, 2ND IN D,E ; LFF3A: CALL GETADR MOV D,H MOV E,L CPI 0AH RZ CPI ',' JZ LFF4A CPI ' ' JNZ WMSTRT LFF4A: XCHG CALL GETADR XCHG RET ; ;CARRIAGE RETURN/LINFEED ROUTINE ; CRLF: MVI A,0DH CALL CHROT MVI A,0AH JMP CHROT ; ;ROUTINE TO GET AN ADDR & CONVERT TO BINARY IN H,L ; GETADR: LXI H,0 GETAD1: CALL GTCHR PUSH PSW SUI 30H JC GETAD2 CPI 0AH JC LFF6A SUI 7 CPI 0AH JNC LFF6A GETAD2: POP PSW RET ; ;ADD A HEX DIGIT TO H,L & GET ANOTHER CHARACTER ; LFF6A: DAD H DAD H DAD H DAD H ADD L MOV L,A POP PSW JMP GETAD1 ; LFF8D: CALL CRLF LFF90: DCX H CALL OUTADR MOV A,M CALL HXSPOT INX H RET ; LFF85: CALL LFF8D MOV A,M ; HXDGOT: PUSH PSW RRC RRC RRC RRC CALL LFFA7 POP PSW ; LFFA7: ANI 0FH ADI 90H DAA ACI 40H DAA JMP CHROT ; ;SEND CR/LF AND SEE IF CNTL-S TYPED -- WAIT IF SO ; NLCHK: CALL CRLF ; ;CONTROL-S WAIT ROUTINE ; CTLS: IN PORTB ;READ PORT B STATUS ANI 20H ;MASK INT. PENDING BIT RZ CALL GTCHR CPI 'S'-40H JNZ WMSTRT JMP GTCHR ;WAIT FOR ANY CHAR & RET ; PMSG: MOV A,M ORA A RZ MOV C,A CALL CRTOUT INX H JMP PMSG ; ;MONITOR COMMAND TABLE ; LFFC0: DB 'B' DW BOOT DB 'D' DW DUMP DB 'E' DW ENTR DB 'F' DW FILL DB 'G' DW EXEC DB 'I' DW INPUT DB 'M' DW MOVEM DB 'O' DW OUTPT DB 'R' DW RESTART DB 'S' DW SERCH DB 'T' DW MEMCK DB 'V' DW MEMCP DB 1BH ;1BH = ESCAPE DW ESCAP DB 0 ;END OF TABLE DB 'VIO' ;REQD FOR SOFTWARE COMPATABILITY END MONITOR