M.MMP=0;THIS IS A MULTIPROCESSOR MONITOR! ;STANDALONE VERSION MSX0 IO HANDLER TASK ROUTINES ;CONDITIONED BY "NODOS" IF DOS REALLY NOT THERE AND DUAL MODE ; ; THE EXPECTATION IS THAT I/O HANDLER TASKS WILL MAP TO THE ;SAME SPACE AS THE KERNEL FOR AT LEAST THE FIRST SEVERAL APR'S ;AND WILL NOT MODIFY THEIR APR'S (EXCEPT APR 6 FOR USER BUFFERS) ;SO THAT THE MSX APR HANDLINGS WILL WORK AND THE HANDLER CAN ACCESS ;TABLES IN MX.TBLS DIRECTLY. THE I/O PAGE MUST BE CONNECTED TO ;APR 7. ; .TITLE MXIOHT V002 MSX IO HANDLER TASK .PSECT ; THIS CODE IS SUPPOSED TO HANDLE ALL QTRAN TYPE IO ; OPERATIONS WITHIN THE CONTEXT OF MSX0. IT IS ; RE-ENTRANT AND THUS CUTS DOWN ON THE CORE REQUIREMENTS ; OF MSX0 IO HANDLEING. IT WORKS VERY MUCH LIKE ; QTRAN EXCEPT THAT IS ASSUMES MSX0 IS RUNNING TO ; ISSUE WAITS, ETC. ; DEFINE OUR REGISTERS R0=%0 R1=%1 R2=%2 R3=%3 R4=%4 R5=%5 SP=%6 PC=%7 ;PS=177776 ; WE KEEP TRACK OF QUEUES WITGIN MSX0 BY MEANS OF THE ; DOS LINK-BLOCK PLUS SOME EXTRA WORDS FOR EACH TASK. ; DURING IO/ THE ADDRESS OF THE UNIT TABLE ENTRY ; IS KEPT TRACK OF BY THE SEO ; IS KEPT TRACK OF IN THE DDB (JUST LIKE TRAN).. LNKTCB=10 ; POINTER TO THIS TCB ENTRY LNKQHD=12 ; QUEUE HEADER LNKQTL=14 ; QUEUE TAILE LNKEVA=16 ; EVENT VARIABLE ADDRES LNKNOD=20 ; POINTER TO CURRENT NODE LNKDVR=22 ; DRIVER ADDRESS LNKPSW=24 ; ORIGINAL NEW PSW FROM INT VECT (2 WORDS) TCBST=3 ; OFFSET OF STATE IN TCB TCBEVA=16 ; OFFSET OF EVA IN TCB .GLOBL MX.FSL,MX.SEV $WAIT=0 .IF NDF,NODOS S.XIT=42 S.CDB=50 S.CDQ=52 .ENDC ; .GLOBL MX.TCB,S.RRES ; MX.IOI GENERAL DRIVER INIT ; HANDLER STARTS UP HERE. IT ALSO IS THE LOOP POINT ; FOR QUEUEING OPERATIONS. .GLOBL MX.IOI ; NAME FOR ENTRY .GLOBL MX.IVS ; INTERRUPT VECTOR SAVE ROUTINE MX.IOI: ; ENTRY FOR GENERAL SETUP .IF NDF,NODOS MOV R0,(SP) ; USE PUSHED CELL FOR INIT EMT 6 ; PERFORM THE DOS IIT MOV (R1),LNKPSW(R0) ; SAVE FOR INT RECALL ; NOW SET UP THE INTERUPT VECTOR USING COMMON SUB. JSR PC,MX.IVS ; SAVE AND SWITHC ; NOW FALL THRU FOR MERGE WITH STANDARD CODE .ENDC ; MX.IOH ENTRY TO PERFORM INITIAL WAIT OF HANDLER .GLOBL MX.IOH .IF DF,NODOS IOHTWT: RTS PC ;IF STANDALONE, RETURN WHEN WAITING... .ENDC MX.IOH: .IF NDF,NODOS ;DUAL MODE STANDALONE HAS DRIVERS AS PROCESSES ;AND NOT TASKS... MOV MX.TCB,LNKTCB(R0) ; SET UP TCB POINTER IOHTWT: ; WAIT FOR AN ENTRY INTO OUR QUEUE CLRB @#PS ; ALLOW INTS HERE MOV R0,-(SP) ; MOVE THE TABLE ENTRY ADDR TO STACK ADD #LNKQHD,(SP) ; AND POINT TO HEAER TRAP $WAIT ; AND WAIT FOR SOMEONE TO NEED US ; WHEN WE GET BACK, THERE IS AN ENTRY ON THE QUEUE. TAKE ; IF OFF AND START UP THE ACTION... .ENDC MOVB #340,@#PS ; DISABLE INT IOHTDQ: ; DE-QUEUE LOOP POINT MOV LNKQHD(R0),R1 ; MOVE QUEUE TOP ADR TO R1 BEQ IOHTWT ; NONE, WAIT FOR MORE MOV R1,LNKNOD(R0) ; SAVE FOR DEBUGGING AND RETRY ; REMOVE THIS NOE FROM THE QUEUE MOV (R1),LNKQHD(R0) BNE IOHT1 ; SKIP F NOT LAS ONE CLR LNKQTL(R0) ; REMOVE LAST IF SO IOHT1: CLRB @#PS ; ENABLE INTERRUPTS AGAIN CLR (R1)+ ; CLEAR TCBEVA VALUE MOV (R1)+,LNKEVA(R0) ; SAVE EVENT VAR ADDR MOV (R0),R2 ; MOVE TE DDB ADDR TO R2 MOV R0,(R2)+ ; INSERT ADDRESS OF LNKBLK ;:: NOW CHECK TYPE OF TRANSFERS BIT #6,6(R1) ; TRAN ? BNE TRAN ; YES, DO IT BR SPEC ; ELSE, ASSUME SPECIAL FUNC ;; THE OTHER ROUTINES WILL GET BACK HERE IOHT2: ;---- HERE WE GO FOLKS MOV R0,-(SP) ; SAVE LINK FOR LATER MOV (R0),R0 ; AND GET DDB ADR ;-- HERE WE FUCGE THE STACK TO LOOK LIKE A NORMAL EMT CALL ; TO HANDLE CASES OF IMMEDIATE RETURNS. IF WE RETURN FROM CALL ; TO CDB, THENWE WILL WIPE THE STACK AND WAIT ; IF NOT, WE SHOULDN'T WAIT ANYWAY MOV #340,-(SP) ; FUDGE PSW MOV #IOHT3,-(SP) ; FOR IMMEDIATE RETURN SUB #14,SP ; ROOM FOR 'SAVED REGS' .IF NDF,NODOS MOV @#S.CDB,-(SP) ; MOVE ADDR OF CB JSR PC,@(SP)+ ; OFF TO START FRIVER .IFF JSR PC,S.CDB ;CALL S.CDB DIRECTLY .GLOBL S.CDB,S.CDQ .ENDC ; HERE WE WIPE THE STACK BECAUSE WE HAD NO IMMEDIATE RETURN ADD #20,SP ; THIS DOES IT ; NOW WAIT FOR OUR EVENT (DRIVER DONE). USE LNKNOD FOR EVA MOV (SP)+,R0 ; GET BACK LNK ADR .IF NDF,NODOS MOV LNKNOD(R0),-(SP) ; PUSH ONTO STACK TRAP $WAIT ; AND WAIT FOR DONE .IFF RTS PC ;RETURN TO CALLER. DONE WILL RECALL. .ENDC ; GET BACK WHEN I/O HAS BEEN DONE ; WE NOW RETURN ITEM TO FREE SPACE MOVB #340,@#PS ; PREVENT INTERRUPTS ; FOLLOWING IS IMMEDIATE RETURN LOCATION IOHT3: ;-- FROM CDB RETURN BY IMMEDIATE DRIVERS MOV LNKNOD(R0),R1 ; GET NODES ADDRESS MOV MX.FSL,(R1) ; SET NEXT PTR IN DONE MOV R1,MX.FSL ; AND NOW FIRST PTR BR IOHTDQ ; LOOK FOR ANOTHER ONE HERE ; TRAN AND SPEC SET-UP ; TRAN SETUP HERE TRAN: ; FROM ABOVE CLR (R2)+ ; SPC _0 MOV (R1)+,(R2)+ ; BLOCK _ MOV (R1)+,(R2)+ ; MA_ MOV (R1)+,(R2)+ ; WC_ MOV (R1)+,(R2)+ ; FUNC_ MOV #XFRDUN,(R2)+ ; RET ADR_ MY RTN .IF DF,NODOS MOV (R1)+,2(R2) ;SAVE TCB ADDR OF TASK DOING I/O MOV (R1)+,4(R2) ;SAVE TASK NUMBER TO CALL ON COMPLETION OR 0 .ENDC MOV #10,R1 ; SET TYPE OF ACTION BR IOHT2 ; AND USE COMMON CODE ; SPECIAL FUNCTIONS SET-UP HERE SPEC: ; FROM UP ABOVE MOV 2(R1),(R2)+ ; SPC ADR_ ARG CLR (R2)+ ; BLK_0 CLR (R2)+ ; MA_ 0 CLR (R2)+ ; WC_ 0 MOV 6(R1),(R2)+ ; FUNC_ ARG MOV #SPCDUN,(R2)+ ; RET ADR_ ME .IF DF,NODOS ;STANDALONE DUAL MODE VERSION MOV 10(R1),2(R2) ;COPY TCB ADDR OF TASK DOING I/O MOV 12(R1),4(R2) ;ALSO TASK TO CALL ON DONE .ENDC MOV #12,R1 ; SET OFFSECT ACTION BR IOHT2 ; AND USE COMMON CODE ; XFRDUN AND SPCDUN (RETURNS FROM DRIVER) ; WHEN WE GET BACK, WE ACTIVATE THE DRIVER FOR MORE ; GARBAGE AND THEN LOOK FOR MORE STUFF IN OUR WUEUE ;------- XFRDUN: ; ENTRY FROM DRIVER JSR PC,DUNCOM ; SET UP STUFF IN COMMON MOV 12(R0),R1 ; SET UP RETURN STATUS BMI ERROR ; ERROR IF SET ASL R1 ; SET FOREOM BMI EOM ; SET, SKIP ; NOW TRY TO GIVE SOME + STATUS MOV 10(R0),R2 ; INITAL WC SUB 16(R0),R2 ; FINAL NEG R2 ; AND SET UP + BGT DUNXIT ; IF PLUS, WERE OK MOV #1,R2 ; ELSE, GIVE IT ONE ANYSA BR DUNXIT ; AND MERGE ;------- ERROR: MOV R1,R2 ; SET FOR ERROR GIVING CODE WITH HI BIT ON BIC #40000,R2 ; GUARANTEE EOF BIT IS OFF IF JUST ERROR BR DUNXIT ; AND EXTI EOM: ASR R1 ; LEAVE EOM FLAG IN POSITION BUT W/ERR CODE MOV R1,R2 ; SET FOR EOM BR DUNXIT ; AND EXIT ;------- ; SPEC RETURNS HERE SPCDUN: ; ENTRY FROM DRIVER JSR PC,DUNCOM ; PERFORM STANDARD ACITIO MOV 2(R0),R2 ;GIVE CALLERS ARG AS EVA BR DUNXIT ; AND USE COMMON CODE ; DUNCOM AND DUNXIT- SHARED STUFF BY DUN ENTRIES DUNCOM: ; ENTRY TO DEQ STUFF MOVB #340,@#PS ; PREVENT INT MOV R0,-(SP) ; SAVE IT .IF NDF,NODOS MOV @#S.CDQ,-(SP) ; MOVE ADD TO STACK JSR PC,@(SP)+ ; AND FO IT .IFF JSR PC,S.CDQ ;DEQUEUE DDB AND COMPLETE THAT STUFF... .ENDC CLRB MX.SEV ; FORCE A SCAN MOV (SP)+,R0 ; GET IT BACK GAIN RTS PC ; AND EXIT DUNXIT: ; ENTRY TO RETURN TO INTERRUPTES STUFF MOV #30340,@#PS ; DISABLE INT .IF NDF,NODOS MOV (R0),R0 ; GET LINK ADR MOV R2,@LNKEVA(R0) ; SET IN TO USER EVE MOV R2,@LNKNOD(R0) ; SET IOHT EVENT ALSO .IFF MOV R0,DDBPSV ;SAVE DDB POINTER FOR AFTER REG RESTORE MOV R2,DDBVLU ;SAVE R2 AS BYTE COUNT OR ERROR FLAG IF - .ENDC ; NOW EXIT JSR R5,S.RRES ; RESTORE USER REGS .IF DF,NODOS ;NOW PERFORM USER COMPLETIONS JSR PC,U.RSAV ;SAVE CURRENTLY ACTIVE TASK REGS AND APRS MOV DDBPSV,R0 ;GET DDB ADDRESS JUST DONE WITH MOV R0,R1 ;COPY FOR LATER MOV 20(R0),R0 ;POINT AT TCB ADDR OF TSK DOING I/O MOV @R1,R4 ;GET LINKBLOCK ADDRESS (QUEUE HEADS THERE) MOV LNKEVA(R4),R3 ;SEE IF EV THERE AND WHERE IT IS BEQ 1$ ;IF MISSING FORGET ABOUT REMAP JSR PC,U.LDR ;REMAP USR APRS TO TASK DOING THE I/O MOV DDBVLU,-(SP) ;RETURN XFER FUNCT TO USER. - ==> ERR MTPI @R3 ;PUT INTO USER SPACE 1$: JSR R5,S.RSAV ;RESAVE REGS FOR I/O RESTART IF MORE MOV R4,R0 ;POINT R0 AT THE LINK BLOCK AGAIN...THEN... JSR PC,IOHT3 ;BEGIN THE I/O JSR R5,S.RRES ;GET BACK REGS NOW ;NOW HANDLE COMPLETION TASKS (CALLED DIRECTLY WHEREVER I/O COMPLETES) ; ; THESE WILL BE DONE VIA AN EFFECTIVE QRUN$ CALL IN THE STANDALONE ;VERSION FOR SIMPLICITY. NOTE THAT ANY NEW I/O HAS NOW BEEN BEGUN ;AND THAT THE INTERRUPTED TASK STATE IS SAVED, SO OUR RETURN (VIA ;THE STACKED MX.RTI) CAN EVENTUALLY GET US TO A REASONABLE STATE. ; HOWEVER, SINCE THE SCANNER WILL TRY TO RESAVE APRS POSSIBLY ;ALTERED BY THE REMAP FOR EV SETTING ABOVE, WE WILL CALL U.RRES ;JUST PRIOR TO RETURN TO GET REGS TO AGREE WITH THE MAPPED TASK. ; ;WHERE A NEW TASK IS REQUESTED, THIS IS NOT REALLY NEEDED BECAUSE THE ;EXIT WILL ENSURE ALL IS WELL. THEREFORE WE WILL FAKE UP THE RETURN ON ;RETURN THERE TO SIMPLY GO TO THE POINT OF INTERRUPTION BY NOT FAKING ;A PS,PC PAIR... .IF DF,RQAST ;ONLY RUN TASKS IF THIS IS PRESENT MOV 22(R1),R0 ;GET COMPLETION TASK NUMBER (0 ILLEGAL) BEQ 2$ ;IF NONE, JUST RESTORE AND GO CMP R0,#</> ;LEGAL TASK NUMBER? BHI 2$ ;YES. MUST REQUEST THESE BY NUMBER. ;CALL COMPLETION TASK. ASH #MX.CNT,R0 ;MAKE OFFSET TO MX.TBL FROM TSK NUMBER ADD #MX.TBL,R0 ;MAKE IT AN ADDRESS ;;; N.B. -- ALLOW RE-REQUEST OF SAME TASK, TO PERMIT AUTO-RESUMES. .IFTF ;RQAST...ALWAYS ALLOW INT. SIMULATION .IF DF,INT.SM ;"INTERRUPT SIMULATION" ON COMPLETION? ; ;WHERE A TASK IS REQUESTING ITSELF AFTER I/O DONE, LET THE SECOND ;WORD OF THE I/O STATUS BLOCK, IF NONZERO, CONTAIN A TASK RESUME ADDRESS ;WHICH WILL BE ENTERED AND SHHOULD BE EXITED VIA RTI. IT WILL RTI TO ;THE ORIGINAL TASK. THE TRANSFER WILL NOT BE ABLE TO KEEP THE TASK ;STATUS INVARIANT, BUT THE NEXT TIME THE TASK CAN RUN, IT WILL START ;RUNNING AT THE INTERRUPT LOCATION AND RTI TO ITSELF WHERE IT WAS BEFORE. ;THE STACK DURING THE OPERATION WILL BE OF FORM: ; ; PSW AT I/O DONE ; PC AT I/O DONE ; ; NOTE THIS WILL ONLY BE DONE FOR STANDALONE SYSTEMS. ; ;BITS 11 (OCTAL) IN THE "TCBPR" BYTE INDICATE WE ARE IN A LIBRARY ROUTINE ;THAT HAS BEEN CALLED AS ENCAPSULATED. IN THIS CASE, WE MUST INHIBIT ;INTERRUPTS TO GUARANTEE A TASK CANNOT USE THEM TO GAIN CONTROL WITH ;DIRECTIVE PRIVILEGES OF THE LIBRARY. THEREFORE SKIP GENERATING THE ;INTERRUPT (AND LOSE IT) IF IN A LIBRARY. BITB #11,TCBPR(R0) ;ARE WE IN A LIBRARY? BNE 12$ ;IF SO, FAKE AN INTERRUPT. ALSO NO INTERRUPT ;WHERE ERROR ENTRY IN PROGRESS. ; .IIF NDF,RQAST, MOV 20(R1),R0 ;ENSURE THIS TASK IF NONE CAN BE SPEC'D CMP R0,20(R1) ;THIS TASK REQUESTING ITSELF (FOR RESUMES) BNE 12$ ;NO MOV R0,-(SP) ;YES. GET RESUME ADDRESS HERE IF ANY MOV LNKEVA(R1),R0 ;GET EV ADDRESS MOV 2(R0),R0 ;THEN RESUME ADDRESS BEQ 11$ ;IF NONE, JUST RE-REQUEST MOV R1,-(SP) MOV 2(SP),R1 ;GET TCB ADDRESS TO R1 SUB #4,TCBDSP(R1) ;MOVE DYNAMIC STACK DOWN MOV TCBDPC(R1),-(SP) ;AND INTERRUPTED PC MOV R0,TCBDPC(R1) ;BASH PC OF TASK, LEAVE PS ALONE MOV TCBDSP(R1),R0 ;GET TASK'S SP MTPI @R0 ;FILL IN WITH SAVED PC AT (SP) MOV #174000,-(SP) ;SET USER MODE, PREV USER, PRIO 0 PSW MTPI 2(R0) ;AND PUT THAT AS INTERRUPT PS MOV (SP)+,R1 MOV (SP)+,R0 BR 2$ ;JUST DO NORMAL SCHEDULING NOW ;NOTE WE DO NOT START THE TASK SINCE IT IS NOT NECESSARILY RUNNING NOW ;AND THE INTERRUPT IS AN EXTENSION OF "TASK RUNNING" STATUS ONLY. IT IS ;DONE THIS WAY TO PERMIT INTERRUPTS TO BE SIMULATED BUT NOT SCREW UP ;THE SCHEDULER OR COMPLICATE LIFE TOO MUCH. 11$: MOV (SP)+,R0 12$: ; .ENDC .IFT ;RQAST ; JMP MX.RQS ;GO TO THE NEW TASK...OLD STATUS SAVE ASSUMED .ENDC ;RQAST 2$: JSR PC,U.RRES ;NO NEW TASK REQUEST...RESTORE MAPPING AND ;FINISH UP .ENDC JMP MX.RTI ; AND OFF TO SCANNER (WE HOPE) .IF DF,NODOS DDBVLU: .WORD 0 ;BYTE COUNT OR ERROR FLAG RETURNED IN USER EV DDBPSV: .WORD 0 ;DDB ADDRESS OF COMPLETION .ENDC ;MONITOR QUEUE & DEQUEUE DRIVER ROUTINES: ; FOR DEVICE DRIVER SERVICES ;A) CHECK DRIVER BUSY: ; ENTERED VIA PC ; IF DRIVER IDLE, DESPATCHES TO REQUIRED SERVICE ; ROUTINE HAVING SET UP CALLER RETURN ; IF DRIVER BUSY, SETS CURRENT CALL INTO QUEUE ; BY PRIORITY & RETURNS TO CALLER ; EXPECTS THE FOLLOWING REGISTER CONTENTS:- ; A) R0:- DDB ADDRESS ; B) R1:- POSITION OF ROUTINE ; REQUIRED IN DRIVER TABLE RELATIVE TO ; DRIVER START ADDRESS ; OPERATES AT CALL PRIORITY EXCEPT WHEN ; INTERRUPTS MIGHT INVALIDATE RESULT R0=%0 R1=%1 R2=%2 R3=%3 R4=%4 R5=%5 SP=%6 PC=%7 .GLOBL S.CDB,S.CDQ ;CHECK DRIVER BUSY & DESPATCH IF IDLE: S.CDB: CLR R5 ;SET UP STATUS REGISTER ADDR. MOV -(R5),-(SP) ;SAVE PRIORITY (ONLY Z BIT SET) S.DVFA: MOV R0,R2 ;GET DRIVER ADDRESS MOV -(R2),R3 CLRB -(R2) ;CLEAR QUEUE SWITCH BISB #340,@R5 ;RAISE PRIORITY FOR CHECK S.EQGO: MOV @R3,R4 ;IS DRIVER BUSY? BNE S.DBSY ;YES - R4 = DDB USING IT MOV R0,@R3 ;NO - CLAIM IT FOR THIS CALL MOVB (SP)+,@R5 ;SAFE NOW! DROP PRIORITY ADD R3,R1 ;BUILD POINTER TO DVR ENTRY BISB @R1,R4 ;(R4 CLEAR HERE) ADD R4,R3 ;GET ENTRY ADDRESS MOV R3,PC ;GO TO DRIVER ;DRIVER BUSY: S.DBSY: MOVB R1,@R2 ;SAVE IN DDB FOR LATER: MOVB @SP,-(R2) ;DRIVER POINTER & PRIORITY S.GETQ: ADD #-6,R4 ;GET Q LINK IN ADDRESSED DDB MOV R4,R3 ;HOLD POINTER TO IT MOV @R4,R4 ;WHILE CHECK IF OTHERS IN Q BEQ S.NTRQ CMPB -4(R4),@R2 ;IF SO CHECK PRIORITY OF NEXT BHIS S.GETQ ;IF HIGHER, TRY AGAIN S.NTRQ: MOV R4,-(R2) ;OTHERWISE THIS ONE GOES IN MOV R0,@R3 ;ADJUST LINKAGE ACCORDINGLY S.CXIT: MOVB (SP)+,@R5 ;RESTORE PRIORITY RTS PC ;RETURN TO CALLER UNTIL FREE ; ; (*) THE DRIVER MAY POSSIBLY FINISH DURING Q SEARCH! ;B) DRIVER HAS FINISHED - CHECK IF OTHERS WAITING: ; ENTERED VIA PC ; IF NO QUEUE, RETURNS TO CALLER ; OTHERWISE CLAIMS DRIVER FOR NEXT CALL IN LINE ; USING S.CDB TO SET UP DRIVER OPERATION ; & THEN RETURNS TO CALLER ; EXPECTS R0 TO CONTAIN ADDRESS OF DDB JUST SERVICED ; OPERATES WITH SAME PRIORITY CONSTRAINTS AS S.CDB ;CHECK IF OTHERS WAITING: S.CDQ: CLR R5 ;SET UP STATUS REGISTER ADDRESS MOV 22(SP),-(SP) ;GET CALL PRIORITY BIC #177437,@SP MOV @SP,-(R5) ;DROP PRIORITY FOR MOMENT MOV -(R0),R3 ;GET DRIVER ADDRESS BISB #340,@R5 ;RAISE PRIORITY TO 7 TST -(R0) ;BUMP POINTER (******) CLR @R3 ;FREE DRIVER MOV -(R0),R2 ;CHECK IF Q WAITING BEQ S.CXIT ;NONE SO GO OUT VIA S.CDB EXIT CLR @R0 ;CLEAR Q LINK FROM THIS DDB MOV R2,R0 ;GET ADDRESS OF NEXT IN Q MOVB -3(R0),R1 ;RESTORE DVR ENTRY PTR BR S.EQGO ;USE S.CDB TO SET UP ACTION .END