; DLSDriverA.asm -- Alto DLS driver, assembly-language portion ; Last modified May 2, 1982 3:17 PM by Taft ; Last modified November 16, 1983 10:20 AM by Diebert .bext dlsOnVector .bext lvUserFinishProc .bext dlsSavedUserFinish .bext lvSwatContextProc .bext dlsSavedSwatContext .bextz inputRBCB .bextz outputRBCB .bextz baseIGCB .bextz lbTable .bext Block .bext dlsInputOverflows .bext dlsErrorInterrupts .ent SendXon .ent TurnOnDLS .ent TurnOffDLS .ent DLSUserFinishProc .ent DLSSwatContextProc .ent DLSInputInterrupt .ent DLSOutputInterrupt .ent DLSErrorInterrupt .ent StartDLSOutput .ent DLSInputEmpty .ent DLSInputIdle .ent DLSOutputEmpty .ent DLSOutputFull .ent AndMemory .ent OrMemory .ent WaitForBitTimes .ent InitRingBuffer .ent ResetRingBuffer .ent ReadRingBuffer .ent WriteRingBuffer .ent ReadAC1 .srel ReadAC1: .ReadAC1 TurnOnDLS: .TurnOnDLS SendXon: .SendXon TurnOffDLS: .TurnOffDLS DLSUserFinishProc: .DLSUserFinishProc DLSSwatContextProc: .DLSSwatContextProc DLSInputInterrupt: .DLSInputInterrupt DLSOutputInterrupt: .DLSOutputInterrupt DLSErrorInterrupt: .DLSErrorInterrupt StartDLSOutput: .StartDLSOutput DLSInputEmpty: .DLSInputEmpty DLSInputIdle: .DLSInputIdle DLSOutputEmpty: .DLSOutputEmpty DLSOutputFull: .DLSOutputFull AndMemory: .AndMemory OrMemory: .OrMemory WaitForBitTimes: .WaitForBitTimes InitRingBuffer: .InitRingBuffer ResetRingBuffer: .ResetRingBuffer ReadRingBuffer: .ReadRingBuffer WriteRingBuffer: .WriteRingBuffer .nrel ; Special emulator instructions dlson = 61016 ; Load pointers and turn on DLS dlsoff = 61015 ; Turn off DLS qch = 61022 ; Queue character for output on line gcrb = 61017 ; Get character from DLS ring buffer andm = 61007 ; And to memory @ac1 = @ac1&ac0 orm = 61023 ; Or to memory @ac1 = @ac1%ac0 setblv = 61025 ; Set Boot Locus Vector to value in ac0 rrb = 61026 ; Read Ring Buffer wrb = 61027 ; Write Ring Buffer ; Offsets in structure DLB, copied from DLSDriver.decl DLB.line = 0 ; Line number DLB.outActive=11. ; B15=1 if output is active DLB.flowControl=12. ; non-zero if flow control is enabled DLB.flowControlAction=13. ; B15=1 if output is active DLB.oRBD=14. ; Output ring buffer descriptor DLB.iRBD=18. ; Input ring buffer descriptor DLB.iRBD.length = 19. ; Input buffer length DLB.oLCB=22. ; Output line control block DLB.oLCB.line=23. ; Word containing line number in right byte DLB.iLCB=26. ; Input line control block DLB.iLCB.interval=29. ; Word containing interval in right byte ; Hardware definitions, from DLSDriver.decl IGCB.link = 0 IGCB.typeLine = 1 IGCB.idle = 2 IGCB.interval = 3 IGCB.active = 4 ; RingBufferDescriptor, used by RingBytes and by microcode. RBD.begin = 0 ; pointer to beginning of ring buffer RBD.length = 1 ; length of ring buffer in bytes RBD.read = 2 ; index of last byte read RBD.write = 3 ; index of last byte written ; ReadAC1() .ReadAC1: mov 1 0 jmp 1 3 ; TurnOnDLS() ; Called both from DLS initialization and when leaving Swat .TurnOnDLS: lda 0 @lvdlsOnVector dlson jmp 1 3 lvdlsOnVector: dlsOnVector ; TurnOffDLS() ; Called when entering Swat .TurnOffDLS: dlsoff jmp 1 3 ; DLSUserFinishProc(OsFinishCode) ; Called to clean up during a finish .DLSUserFinishProc: sta 3 1 2 dlsoff ; Shut off the DLS adczl 0 0 ; Set boot locus vector to 177776 setblv ; (start all tasks except emulator in rom) subzr 0 0 ; Silent boot sio lda 3 @lvlvSwatContextProc ; Get ptr to SwatContextProc lda 0 @lvdlsSavedSwatContext ; Restore old SwatContextProc sta 0 0 3 lda 3 @lvlvUserFinishProc ; Get ptr to UserFinishProc lda 0 @lvdlsSavedUserFinish ; Restore old UserFinishProc sta 0 0 3 lda 3 1 2 jmp 1 3 lvlvSwatContextProc: lvSwatContextProc lvdlsSavedSwatContext: dlsSavedSwatContext lvlvUserFinishProc: lvUserFinishProc lvdlsSavedUserFinish: dlsSavedUserFinish ; DLSSwatContextProc(direction) ; Routine called when entering and leaving Swat ; Direction is 0 when entering and -1 when leaving .DLSSwatContextProc: snz 0 0 ; Entering or leaving? jmp .TurnOffDLS ; Entering Swat, turn off DLS jmp .TurnOnDLS ; Leaving Swat, turn on DLS ; DLSInputInterrupt() c377: 377 .DLSInputInterrupt: sta 3 1 2 ; Save return iint1: lda 0 inputRBCB ; Input ring buffer control block gcrb ; Get item from buffer (char,,line) jmp iint99 ; No more movs 0 1 ; Get char in rh lda 3 c377 and 3 0 ; Mask line number (line# in AC0) and 3 1 ; Mask char (char in AC1) lda 3 lbTable ; Get base of DLB table add 0 3 ; Get pointer to line's DLB lda 3 0 3 ; AC3 contains addr of DLB lda 0 DLB.flowControl 3 ; Offset to flowControl word mov 0 0 szr jmp iint7 ; In flowControl mode ; This is non flow controlled path! lda 0 .DLB.iRBD ; Compute pointer to input RBD add 3 0 wrb ; Write char into ring buffer iint3: isz @lvdlsInputOverflows ; Full, count input buffer overflows jmp iint1 ; Ok, done, look for more input jmp iint1 ; This is flow controlled path! iint7: lda 0 cXoff ; Check for Xoff subs 1 0 movzl 0 0 szr jmp iint5 ; not Xoff ; Char is Xoff lda 0 fcStop lda 1 .DLB.flowControlAction ; set up offset for action bits add 3 1 ; AC1 ← adr of Action orm ; Set stop bit lda 1 cXoff ; put the Xoff back in the buffer jmp iint12 iint17: lda 0 fcResetStopTimerGoing ; Xon lda 1 .DLB.flowControlAction add 3 1 andm ; reset stop and timer Going lda 1 cXon jmp iint12 fcResetStopTimerGoing: 157775 .DLB.flowControlAction: DLB.flowControlAction .DLB.iRBD: DLB.iRBD iint5: lda 0 cXon ; Check for Xon subs 1 0 movzl 0 0 snr jmp iint17 ; is Xon iint12: lda 0 .DLB.iRBD ; Compute pointer to input RBD add 3 0 wrb ; Write char into ring buffer jmp iint3 ; Overflow lda 1 .minBuffLeft ; Check to see how much space is left adcl# 0 1 szc ; skip if not enough room jmp iint1 ; See if more to do lda 1 DLB.flowControlAction 3 ; put action word in AC1 movr 1 1 szc ; check for XoffSent. jmp iint1 ; has been sent ; This point we don't have room in buffer so force Xoff to be sent lda 0 DLB.outActive 3 ; put outActive in AC0 movr# 0 0 snc jmp iint15 ; Not Active ; Active set send in action lda 0 fcSendXoff lda 1 .DLB.flowControlAction ; set up offset for action bits add 3 1 ; AC1 ← adr of Action orm ; Set stop bit jmp iint1 ; see if more to do. iint15: ; not active so send char lda 0 fcXoffSent lda 1 .DLB.flowControlAction ; set up offset for action bits add 3 1 ; AC1 ← adr of Action orm ; Set XoffSent bit lda 1 cXoff isz DLB.outActive 3 ; dlb>>DLB.outActive=true lda 0 .DLB.oLCB.line ; Get offset of output LCB add 3 0 ; Make pointer for microcode qch ; Queue character for output jmp iint1 ; go see if more to do. iint99: lda 3 1 2 jmp 1 3 lvdlsInputOverflows: dlsInputOverflows lvdlsErrorInterrupts: dlsErrorInterrupts ; DLSErrorInterrupt() .DLSErrorInterrupt: isz @lvdlsErrorInterrupts ; Just count them jmp 1 3 jmp 1 3 oint99: lda 3 1 2 jmp 1 3 ; DLSOutputInterrupt() .DLSOutputInterrupt: sta 3 1 2 ; Save return oint1: lda 0 outputRBCB ; Output ring buffer control block gcrb ; Get item from buffer (ptr to OLCB.line) jmp oint99 ; No more (exit) mov 0 3 ; Save ptr to DLB.oLCB.line dir lda 0 DLB.flowControl-DLB.oLCB.line 3 ; AC0 ← flowControl mov 0 0 szr jmp oint5 ; flowControl enabled oint7: lda 0 .oRBDmoLCB.line ; Compute pointer to output RBD add 3 0 rrb ; Get char from ring buffer jmp oint2 ; Empty, go clear outActive flag eir mov 3 0 ; Ptr to DLB.oLCB.line qch ; Queue character for output jmp oint1 ; Look for more work to do oint5: ; At this point we have flow control enabled and we are ; ready to send a char to this line. lda 0 DLB.flowControlAction-DLB.oLCB.line 3 ; AC0 ← flowControlAction movl 0 0 szc ; check to see if sendXoff is on. skip if off jmp oint10 ; sendXoff is on! movl 0 0 szc ; check to see if sendXon is on. skip if off jmp oint15 ; sendXon is on! movl 0 0 snc ; skip if Stop flag on jmp oint7 ; go ahead. ; Here if output buffer now empty oint2: dsz DLB.outActive-DLB.oLCB.line 3 ; dlb>>DLB.outActive=false nop eir jmp oint1 ; Look for more work to do oint10: ; here we are. we need to send an Xoff lda 1 .flowControlActionmoLCB.line ; set Xoff sent add 3 1 lda 0 fcXoffSent orm ; this turns on XoffSent lda 0 fcResetSendXoff andm eir lda 1 cXoff mov 3 0 ; Ptr to DLB.oLCB.line qch ; Queue character for output jmp oint1 ; Look for more work to do oint15: ; here we are. we need to send an Xon lda 1 .flowControlActionmoLCB.line ; reset Xoff sent add 3 1 lda 0 fcResetSendXonXoffSent andm eir lda 1 cXon mov 3 0 ; Ptr to DLB.oLCB.line qch ; Queue character for output jmp oint1 ; Look for more work to do c360: 360 cXoff: 223 cXon: 21 fcResetXoffSent: 177776 fcStop: 20000 fcResetStop: 157777 fcXoffSent: 1 fcSendXoff: 100000 fcResetSendXoff: 77777 fcSendXon: 40000 fcResetSendXonXoffSent: 137776 .minBuffLeft: 6 .DLB.flowControl: DLB.flowControl .DLB.oRBD: DLB.oRBD .DLB.oLCB.line: DLB.oLCB.line .oRBDmoLCB.line: DLB.oRBD-DLB.oLCB.line .flowControlActionmoLCB.line: DLB.flowControlAction-DLB.oLCB.line ; StartDLSOutput(dlb) .StartDLSOutput: dir sta 3 1 2 ; Save return mov 0 3 ; Save line data block ptr lda 0 DLB.flowControl 3 ; AC0 ← flowControl mov 0 0 szr jmp start5 ; flowControl active start2: lda 0 .DLB.oRBD ; Compute pointer to output RBD add 3 0 rrb ; Get char from ring buffer jmp start1 ; Empty, go clear outActive flag lda 0 DLB.outActive 3 movr# 0 0 snc isz DLB.outActive 3 ; dlb>>DLB.outActive=true lda 0 .DLB.oLCB.line ; Get offset of output LCB add 3 0 ; Make pointer for microcode qch ; Queue character for output start99: eir lda 3 1 2 ; Recover return jmp 1 3 ; Here if output buffer now empty start1: lda 0 DLB.outActive 3 movr# 0 0 szc dsz DLB.outActive 3 ; dlb>>DLB.outActive=false jmp start99 jmp start99 start5: lda 0 DLB.flowControlAction 3 ; AC0 ← action code movl 0 0 ; shift SendXof into carry movl 0 0 ; shift SendXon into carry movl 0 0 szc ; skip if flag off. jmp start99 ; stop bit is set. jmp start2 ; stop bit not set. exit: lda 3 1 2 ; Recover return jmp 1 3 ; SendXon(dlb) .SendXon: dir sta 3 1 2 ; Save return mov 0 3 ; Save line data block ptr lda 0 DLB.outActive 3 movr# 0 0 szc jmp SendXon10 isz DLB.outActive 3 ; dlb>>DLB.outActive=true lda 1 ..DLB.flowControlAction add 3 1 lda 0 fcResetXoffSent andm lda 0 .DLB.oLCB.line ; Get offset of output LCB add 3 0 ; Make pointer for microcode lda 1 cXon qch ; Queue character for output jmp start99 SendXon10: lda 1 ..DLB.flowControlAction add 3 1 lda 0 fcSendXon orm jmp start99 ..DLB.flowControlAction: DLB.flowControlAction ; DLSInputEmpty(dlb) ; Predicate for line input buffer empty .DLSInputEmpty: sta 3 1 2 mov 0 3 ; DLB lda 0 DLB.iRBD+RBD.read 3 lda 1 DLB.iRBD+RBD.write 3 jmp AC0eqAC1 ; read = write means empty ; DLSInputIdle(dlb) ; Predicate for input line idle (no character currently being received) .DLSInputIdle: sta 3 1 2 mov 0 3 lda 1 DLB.line 3 lda 0 c360 ; this is now a byte value in the low byte! and 1 0 ; first line of group lda 3 baseIGCB add 0 3 ; address of IGCB lda 0 IGCB.idle 3 cycle 0 ; left-justify idle bit for this line movl# 0 0 szc mkminusone 0 0 skp ; idle mkzero 0 0 ; not idle lda 3 1 2 jmp 1 3 ; DLSOutputEmpty(dlb) ; Predicate for line output buffer empty .DLSOutputEmpty: sta 3 1 2 mov 0 3 ; DLB lda 0 DLB.oRBD+RBD.read 3 lda 1 DLB.oRBD+RBD.write 3 jmp AC0eqAC1 ; read = write means empty ; DLSOutputFull(dlb) ; Predicate for line output buffer full .DLSOutputFull: sta 3 1 2 mov 0 3 ; DLB lda 1 DLB.oRBD+RBD.write 3 inc 1 1 ; write+1 lda 0 DLB.oRBD+RBD.length 3 sne 0 1 mkzero 1 1 ; wrap around lda 0 DLB.oRBD+RBD.read 3 ; read = (write+1) mod length means full ; Here to return true iff AC0 = AC1 AC0eqAC1: se 0 1 mkzero 0 0 skp ; AC0 # AC1 mkminusone 0 0 ; AC0 = AC1 lda 3 1 2 jmp 1 3 ; Misc utility procedures ; AndMemory(mask, adr) .AndMemory: andm jmp 1 3 ; OrMemory(mask, adr) .OrMemory: orm jmp 1 3 ; WaitForBitTimes(dlb, bitTimes) ; Dismiss for the specified number of bit times at this line's data rate. ; Note: bitTimes * ticks/bit had better not be greater than 512. .WaitForBitTimes: sta 3 2 2 ; use nonstandard pc place so Block won't clobber it mov 2 3 mov 0 2 ; dlb lda 0 DLB.iLCB.interval 2 lda 2 x377 and 0 2 ; dlb>>DLB.iLCB.interval inc 2 2 ; normalize (it was actually interval-1) mkzero 0 0 mul ; [ac0, ac1] ← bitTimes*interval mov 3 2 mov 1 0 ; just the low result cycle 6 ; left-justify 10-bit value to match RCLK mov 0 3 rclk ; returns low 10 bits of time left-justified in ac1 add 3 1 ; ending time sta 1 3 2 WaitBitTimeLoop: jsrii lvBlock 0 rclk lda 0 3 2 sgt 1 0 ; reached ending time? jmp WaitBitTimeLoop ; no, keep waiting lda 3 2 2 jmp 1 3 lvBlock: Block x377: 377 ; DLS partial reimplementation of Ring Buffer package (byte version) ; InitRingBuffer(rbd, buffer, length) ; Initialize ring buffer descriptor using specified buffer and length (words). .InitRingBuffer: sta 3 1 2 mov 0 3 ; rbd sta 1 RBD.begin 3 ; begin lda 0 3 2 ; length movzl 0 0 ; convert words to bytes sta 0 RBD.length 3 mkzero 0 0 sta 0 RBD.read 3 ; reset read pointer sta 0 RBD.write 3 ; reset write pointer jmp exit ; ResetRingBuffer(rbd) ; Delete the contents of the ring buffer .ResetRingBuffer: sta 3 1 2 mov 0 3 ; rbd dir ; no races lda 0 RBD.write 3 ; get write pointer sta 0 RBD.read 3 ; update read pointer eir lda 3 1 2 jmp 1 3 ; ReadRingBuffer(rbd) = next char from ring buffer, or -1 if empty .ReadRingBuffer: rrb mkminusone 1 1 ; empty mov 1 0 jmp 1 3 ; WriteRingBuffer(rbd, char) ; Write Char into ring buffer ; Return true if succeeded, false if failed (buffer full) ; Note: this routine depends on the left half of Char being zero .WriteRingBuffer: wrb mkzero 0 0 skp ; full mkminusone 0 0 ; not full jmp 1 3 .end