Leica DOS rules, BABY!

Apparently, 25 years ago, I ruled DOS... Looking through some of the assembly language code for my FORTRAN code- found

SIDT PWORD DS:[ESI]

I have a vague recollection of spending a week diving into the low levels of the 80386. "Store Interrupt Descriptor Table Entry Register". The 80386 manual states that it is not for use by application programs, only for the OS. I loved Phar Lap DOS. It let you do physical to virtual memory mapping yourself, closest that I've seen to VAX/VMS. Map, unmap, and remap physical to virtual addresses while the code was running.

I'm sure WIN8 will let me do that to.... Until I learn it, will stick with PHAR8.
 
Until the past few years, since 2007 anyway, I kept up with tables and parsers that could flop code from a current compiler version to previous, or from one O/S to another, since I had a huge dread of walking through code to find the glitches. Some of the interactive debuggers are good, but I keep my reliance on them to bare minimum. In 1991 while I was working on a parser that could convert ANSI BASIC to ANSI C about 98 percent complete with known fixups, my boss tried to get me to moonlight on a project to convert Cobol code to 'C' - he really thought it could be done! And if I could have taken that on as a daytime job I would have tried it! I started that job in 1988 in SE Tennessee as a mild-mannered database programmer and left 4 years later as a cowboy programmer - anything goes. It took years to recover and get reorganized. But rather than develop assembler code for BASIC programs with their major weaknesses (i.e. parsing), I found Ethan Winer and bought his packages, and got him to make certain improvements so I could more easily convert code with fewer exceptions. I've examined some HP Fortran from the early to mid '80s, and I think I could write a parser to move it to 'C', but the opportunities to sell that to companies that are sitting on a bunch of Fortran code are probably long past, besides which the outfits that are still using 'C' (everybody?) are using C++, so forget that. I think it goes without saying that Microsoft et al have been doing all they can to get developers to use mostly Microsoft code modules (DLLs) to keep everything in the family. If the executives who run the larger corporations knew how easy it is to build code that's transportable as compared to Microsoft-specific, i.e. how little extra it would cost them, they'd jump on it. But try knocking on those guys' doors and they just send you to IT.
 
I've managed to make a successful career using a skill set that is 20 years obsolete; writing compact code that runs fast. 30 years ago- it was for vector supercomputers where 100MFlops took every trick in the book and knowing the internals of the CPU. VAX/VMS- know all the internals of the computer, peripherals and OS. The console lights dimmed on the Vax 11/750 when it was running my code- processing data from a digital imager. 386 computers- more of the same. These days: embedded processing applications where a device is waiting for a response, and the code has to be fast. I've written some machine code parsers for my own emulators, written in FORTRAN. If you know how to use them- FORTRAN character processing is pretty good. I made a lot of use of multi-dimensional arrays, sized at runtime.
 
Yesterday I hit a "MotherBug" in my code, all that was left was a locked keyboard and register dump on the screen.

I was a wimp, and used a library call instead of doing it myself. Forgot rule 2,

2. The system library is a challenge to all those using the computer to write
their own faster and better routines or to bow to the superior strength and
skill of a true master.


Words to live by.

Found some code from 1986 that did the function under real-mode DOS, just converted it to protected mode. Worked on the first try.

I have always commented code, never know when you are going to need it.

The code from 1986. This is now the oldest code that I've re-used in my life.

TITLE EXEC: RM FORTRAN CALLABLE EXEC DOS FUNCTION.
NAME EXEC
PAGE 60,132
; THIS IS AN RM FORTRAN CALLABLE FUNCTION.
; STATUS= EXEC( PROGRM, COMAND)
; THIS SUBROUTINE LOADS AND EXECUTES A PROGRAM.
; BVS, 1986.
; PROGRAM: LOGICAL* 1 "ASCIZ" CHARACTER STRING WITH THE NAME OF THE FILE
; TO LOAD AND EXECUTE.
; COMAND: LOGICAL* 1 "ASCIZ" CHARACTER STRING WITH THE NAME COMMAND STRING
; INPUT TO THE PROGRAM.
PROGRM EQU 0 ; OFFSET INTO ARGUMENT LIST OF ADDRESS OF PORT.
COMAND EQU 4 ; OFFSET INTO ARGUMENT LIST OF ADDRESS OF VALUE.
LD EQU MOV ; DEFINE OPCODE "LD" TO BE THE SAME AS "MOV".
DATA SEGMENT
; 14 BYTES NEEDED FOR RM TRACEBACK.
DB 'EXEC ' ; 8 BYTES FOR ROUTINE NAME.
SP_SAVE DW ? ; 2 BYTES FOR STACKPOINTER SAVE AREA.
DD EXCBGN ; 4 BYTE ADDRESS FOR BEGINNING OF CODE.
DD 0 ; DEBUG POINTER, FOR T OPTION.
; LOCAL DATA DECLARATIONS MAY GO HERE.
EXCNTR LABEL FAR ; LABEL OF EXEC CONTROL BLOCK.
DW 0 ; SEG ENVRMT ; SEGMENT ADDRESS OF ENVIRONMENT STRING.
; VALUE OF ZERO COPIES ENVIRONMENT FROM CALLING PROGRAM.
DD 0 ; SEGMENTED ADDRESS OF COMMAND LINE. THIS
; GETS THE ADDRESS OF "COMAND".
DD 0 ; ADDRESS OF DEFAULT FCB 1.
DD 0 ; ADDRESS OF DEFAULT FCB 2.
;
DATA ENDS
ENVRMT SEGMENT PARA ; ENVIRONMENT STRINGS FOR EXEC. PARAGRAPH ALIGNED.
DB 'NAME=comspec',0 ; NAME OF COMMAND PROCESSOR.
DB 0 ; TERMINATE ENVIRONMENT.
ENVRMT ENDS
; ENVIRONMENT SEGMENT.
CODE SEGMENT
EXCBGN PROC FAR
ASSUME CS:CODE,DS:DATA,ES:NOTHING,SS:NOTHING
PUBLIC EXEC ; DECLARE EXEC AS PUBLIC, I.E. EXERNAL.
SS_SAVE DW ? ; STACK SEGMENT SAVE AREA.
SP_SAV2 DW ? ; STACK POINTER SAVE AREA.
DW SEG DATA ; NEEDED FOR T OPTION.
EXEC LABEL FAR
PUSH DS ; SAVE DATA SEGMENT.
MOV AX,DATA ; LOAD DATA SEGMENT WITH LOCAL DATA.
MOV DS,AX
MOV SP_SAVE,SP ; SAVE THE STACK POINTER VALUE ON ENTRY.
; ES:BX POINT TO THE ARGUMENT LIST. THERE ARE TWO ARGUMENTS.
; THE LIST IS A SET OF DOUBLE WORD ADDRESSES OF THE ARGUMENTS.
; BODY OF ROUTINE.
;
; FETCH ARGUMENTS.
; LOAD ADDRESS OF COMAND INTO EXEC CONTROL BLOCK.
MOV AX,WORD PTR ES:[BX+COMAND] ; GET SEGMENT OFFSET.
MOV WORD PTR DS:[EXCNTR+2],AX
MOV AX,WORD PTR ES:[BX+COMAND+2] ; GET SEGMENT NUMBER.
MOV WORD PTR DS:[EXCNTR+4],AX
PUSH DS ; PUSH DS AS EXEC FUNCTION DESTROYS IT.
; PUSH ALL WANTED REGISTERS. THIS FUNCTION CALL BLOWS AWAY EVERYTHING
; BUT CS:IP!
; SAVE SS AND SP INTO A LOCATION ADDRESSED BY CS:IP.
MOV AX,SS ; SAVE STACK SEGMENT.
MOV WORD PTR CS:[SS_SAVE],AX
MOV CS:[SP_SAV2],SP ; SAVE STACK POINTER.
; LOAD DS:DX WITH ADDRESS OF PROGRM. NOTE THAT DS IS NOW GONE.
LDS DX,DWORD PTR ES:[BX+PROGRM] ; LOAD DS:DX WITH ADDRESS OF PROGRM.
; NOW LOAD ES:BX WITH THE ADDRESS OF THE EXCNTR BLOCK.
MOV BX,OFFSET EXCNTR ; GET OFFSET OF EXCNTR.
MOV AX,SEG EXCNTR ; GET SEGMENT NUMBER.
MOV ES,AX
; LOAD AL WITH FUNCTION CODE TO LOAD AND RUN THE PROGRAM.
XOR AL,AL ; VALUE OF ZERO LOADS AND RUNS THE PROGRAM.
; LOAD AH WITH EXEC FUNCTION CODE.
MOV AH,04BH ; HEX 4B "EXEC" FUNCTION CODE.
; FIRE.
INT 33 ; INTERRUPT 33 (DECIMAL) FIRES THE SHOT.
; SAVE THE RETURN STATUS IN A REGISTER. EVERYTHING ELSE IS BLOWN AWAY.
MOV DX,AX ; USE DX TO SAVE STATUS.
; RED ALERT! THE SP IS BLOWN AWAY!!!
MOV AX,WORD PTR CS:[SS_SAVE] ; RESTORE STACK SEGMENT.
MOV SS,AX
MOV SP,WORD PTR CS:[SP_SAV2] ; RESTORE THE STACK POINTER.
POP DS ; POP DS.
CLD ; CLEAR THE DIRECTION FLAG.
;
; END ROUTINE
MOV SP,SP_SAVE ; RESTORE THE STACK.
POP DS
MOV AX,DX ; RESTORE THE RETURN CODE.
RET
EXCBGN ENDP
CODE ENDS
;
STACK SEGMENT WORD STACK 'STACK'
STAKSZ EQU 0
IF STAKSZ GT 0
DB STAKSZ DUP (?)
ENDIF
STACK ENDS
END


This code is not in 32-bit protected mode, and the 32-bit code is easier to write than this one. The OS actually does not destroy all of the registers anymore. This 16-bit version used the Code segment for data storage.
 
Yes- but for your job, I'm guessing the highest degree of trust is required- both in the software and in the person.

I wrote a piece of code that caused the computer to catch on fire.
 
It does, if you willingly put in some sort of backdoor and it's discovered, you'll never work in this branch of employment again.
We've had some accidental cases were players discovered ways to beat the machines, but as soon as they were discovered they were removed with updates. I'm sure some people walked away with a big amount of money before we plugged the holes though ;)


The only things I've done to DOS was create the most memory-friendly and quickest start-up environments for games back in the 90s. Creating a sort of multi-boot floppy, everything was controlled by batchfiles.
We mainly needed it to play games over a coax-wired Network....then someone salvaged a hub from his employer...
We'd drag our PCs to a friend's house and played games all weekend.
 
Back in the late 70s, ATM machines were coming online. One "glitch": a user transferred money from their savings to their checking, the money ended up in the account of the person that used the machine before you to check their savings balance.

I did make 9cents; discovered that a transfer would deposit the exact amount typed, but truncated the cents of the amount withdrawn. I didn't need to do it a million times, just once- to know the error was there.

I have not used an ATM machine since 1979.
 
PAGE 56,132
TITLE COMIO: USE COM1 PORT FOR INTERRUPT DRIVEN I/O.
NAME COM1IO
.386C ; ENABLE USE OF 32BIT INSTRUCTIONS; MUST DECLARE SEGMENTS AS "USE16" FOR REAL MODE.
; Microsoft C version 1.52 CALLABLE ROUTINES TO BRING IN BYTE DATA ON THE "AT" compatible SERIAL PORT
; SUBROUTINE RST8259
;
REGBASE EQU 3F8H ; REGISTER BASE AS SET ON COM1.
; DEFINE THE REGISTERS.
; DATA REGISTER, WHEN B7 OF LINE CONTROL= 0.
DATABF EQU REGBASE ; DATA BUFFER: B7:B0
;
; INTERRUPT ENABLE REGISTER WHEN B7 OF LINE CONTROL= 0.
INTNBL EQU REGBASE+ 1 ; INTERRUPT ENABLE REGISTER.
; INTERRUPT ENABLE MASKS.
DTRCVD EQU 00000001B ; INTERRUPT ON DATA RECEIVED.
SNDMTY EQU 00000010B ; INTERRUPT ON DATA TRANSMIT EMPTY.
RCVERR EQU 00000100B ; INTERRUPT ON DATA RECEIVE ERROR.
MDMCHG EQU 00001000B ; INTERRUPT ON MODEM REGISTER CHANGE.
;
; INTERRUPT ID REGISTER.
INTRID EQU REGBASE+ 2 ; INTERRUPT ID.
INTPND EQU 00000001B ; INTERRUPT PENDING MASK, BIT 0 SET WHEN
; INTERRUPT IS PENDING.
; INTERRUPT ID TAGS: INT ID IS STORED IN BITS 2:1 OF INTRID.
IDMASK EQU 00000110B
MDMINT EQU 00000000B ; MODEM CAUSED INTERRUPT.
SNDINT EQU 00000010B ; TRANSMIT REGISTER EMPTY.
RCVINT EQU 00000100B ; RECEIVED DATA.
ERRINT EQU 00000110B ; ERROR CAUSED INTERRUPT.
;
; LINE CONTROL REGISTER.
LNCNTL EQU REGBASE+ 3 ; LINE CONTROL REGISTER.
; BIT LEVEL DEFINITIONS.
BITLEN5 EQU 00000000B ; 5 BIT WORDS.
BITLEN6 EQU 00000001B ; 6 BIT WORDS.
BITLEN7 EQU 00000010B ; 7 BIT WORDS.
BITLEN8 EQU 00000011B ; 8 BIT WORDS.
STOP1BT EQU 00000000B ; ONE STOP BIT.
STOP2BT EQU 00000100B ; TWO STOP BITS.
PARITY EQU 00001000B ; PARITY
EVNPRTY EQU 00010000B ; EVEN PARITY
ODDPRTY EQU 00000000B ; ODD PARITY
STKPRTY EQU 00100000B ; STICK PARITY
SETBRK EQU 01000000B ; SET BREAK.
SETBAUD EQU 10000000B ; ENABLES SETTING OF BAUD RATE.
;
; MODEM CONTROL REGISTER.
MDCNTL EQU REGBASE+ 4 ; MODEM CONTROL REGISTER.
; MODEM CONTROL BITS.
DTR EQU 00000001B ; DATA TERMINAL READY
RTS EQU 00000010B ; REQUEST TO SEND.
OUT1 EQU 00000100B ; SPARE 1.
OUT2 EQU 00001000B ; Interrupt Mask: MUST BE SET TO ALLOW INTERRUPTS.
LOOPBK EQU 00010000B ; LOOP BACK.
;
; LINE STATUS REGISTER.
LNSTAT EQU REGBASE+ 5 ; LINE STATUS.
; DEFINE BIT MASKS FOR LINE STATUS.
DTARCV EQU 00000001B ; DATA RECEIVED.
RCVOFL EQU 00000010B ; RECEIVE OVERRUN.
PRTYERR EQU 00000100B ; PARITY ERROR.
FRAMERR EQU 00001000B ; FRAMING ERROR.
BRKDTC EQU 00010000B ; BREAK DETECT.
TXEMTY EQU 00100000B ; TRANSMIT BUFFER EMPTY.
TXSHMT EQU 01000000B ; TRANSMIT SHIFT REGISTER EMPTY.
TIMEOUT EQU 10000000B ; TIME OUT.
;
; MODEM STATUS REGISTER.
MDSTAT EQU REGBASE+ 6 ; MODEM STATUS.
;
; BAUD RATE DIVISOR REGISTERS WHEN B7 OF LINE CONTROL= 1.
BAUDLO EQU REGBASE ; LOW BYTE OF BAUD RATE DEVISOR.
BAUDHI EQU REGBASE+ 1 ; HIGH BYTE OF REGBASE.
; COMMON BAUD RATE DIVISORS TO CONVERT 119000 CYCLES TO BAUD RATE CLOCK.
B110H EQU 04H ; 110 BAUD HI BYTE.
B110L EQU 17H ; 110 BAUD LOW BYTE.
B300H EQU 01H ; 300 BAUD HIGH.
B300L EQU 80H ; 300 BAUD LOW.
B1200H EQU 00H ; 1200 BAUD HIGH
B1200L EQU 60H ; 1200 BAUD LOW.
B1800H EQU 00H ; 1800 BAUD HIGH
B1800L EQU 40H ; 1800 BAUD LOW.
B2400H EQU 00H ; 2400 BAUD HIGH
B2400L EQU 30H ; 2400 BAUD LOW.
B3600H EQU 00H ; 3600 BAUD HIGH
B3600L EQU 20H ; 3600 BAUD LOW.
B4800H EQU 00H ; 4800 BAUD HIGH
B4800L EQU 18H ; 4800 BAUD LOW.
B9600H EQU 00H ; 9600 BAUD HIGH
B9600L EQU 0CH ; 9600 BAUD LOW.
B19200H EQU 00H ; 19200 BAUD HIGH
B19200L EQU 06H ; 19200 BAUD LOW.
B38400H EQU 00H ; 38400 BAUD HIGH
B38400L EQU 03H ; 38400 BAUD LOW.
B57600H EQU 00H ; 57600 BAUD HIGH
B57600L EQU 02H ; 57600 BAUD LOW.
B115200H EQU 00H ; 115200 BAUD HIGH
B115200L EQU 01H ; 115200 BAUD LOW.
;
;
;
; MISCELLANEOUS.
; DEFINE INTERRUPT MASK REGISTER.
INTCTL EQU 021H ; INTERRUPT IRQ0: IRQ7
LOWBIT EQU 1 ; LOW BIT.
READY EQU 0FFH ; DATA READY SIGNAL.
NOTRDY EQU 0 ; DATA NOT READY.
CLEAR EQU 0 ; CLEAR PORT.
NORMAL EQU 0H ; NORMAL COMPLETION.
INTRST EQU 020H ; INTERRUPT RESET COMMAND.
INTREG EQU 020H ; INTERRUPT PRIORITY REGISTER.
INTMSK EQU 021H ; INT MASK REGISTER.
SLVRST EQU 0A0H ; SLAVE 8259 REGISTER.
SLVMSK EQU 0A1H ; SLAVE 8259 MASK REGISTER.
LINEFD EQU 0AH ; LINE FEED VALUE.
CRETRN EQU 0DH ; CARRIAGE RETURN.
PUBLIC _CMCOM1Q
PUBLIC _COM1LOADED
;+++ BEGIN / CMCOM1Q/
CMCOM1Q SEGMENT PAGE COMMON USE16 'DATA'
QUESIZ EQU 128
EMPTY EQU 07FFFH ; QUEUE EMPTY VALUE
NOTMTY EQU 02FFH ; QUEUE NOT EMPTY VALUE.
QUFULL EQU 01FFH ; QUEUE OVERFLOW VALUE.
QUNTFL EQU 03FFH ; QUEUE NOT FULL.
QUEMIN EQU 128 ; MINIMUM SPACE ON QUEUE BEFORE PAUSING.
;
_CMCOM1Q LABEL FAR ; Microsoft C will use this as the external structure CMCOM1Q
RCVQUE DB QUESIZ DUP (?) ; DATA QUEUE.
HEAD DW 0 ; CURRENT HEAD OF QUEUE.
; HEAD IS INCREMENTED AFTER EACH QUEUE WRITE.
; IF NEW HEAD = TAIL, OVERFLOW OCCURRED.
; QUEMTY IS SET TO NOTMTY.
TAIL DW 0 ; CURRENT TAIL OF QUEUE.
; 1ST, QUEMTY IS CHECKED. IF NOTMTY, DATA IS
; READ AND TAIL IS INCREMENTED AFTER THE READ.
; IF NEW TAIL= HEAD, QUEUE IS EMPTY.
OVRFLO DW QUNTFL ; QUEUE OVERFLOW INDICATOR.
QUEMTY DW EMPTY ; QUEUE EMPTY.
QSPACE DW QUESIZ ; SPACE LEFT ON QUEUE.
QSTOP DW EMPTY ; QUEUE PAUSED INDICATOR.
DTAERR DW 0 ; DATA RECEIVER ERROR!
MDMERR DW 0 ; MODEM ERROR
UNKINT DW 0 ; UNKNOWN ERROR
_COM1LOADED LABEL FAR
DB 0 ; SET WHEN HANDLER IS LOADED.

CMCOM1Q ENDS
;--- END / CMCOM1Q/
;
PUBLIC _CMWRT1Q
;+++ BEGIN / CMWRT1Q/
CMWRT1Q SEGMENT PAGE COMMON USE16 'DATA'
WRTQSIZ EQU 1024
WRTEMPTY EQU 07FFFH ; QUEUE EMPTY VALUE
WRTNOTMTY EQU 02FFH ; QUEUE NOT EMPTY VALUE.
WRTQFULL EQU 01FFH ; QUEUE OVERFLOW VALUE.
WRTQNTFL EQU 03FFH ; QUEUE NOT FULL.
;
_CMWRT1Q LABEL FAR ; Microsoft C will use this as the external structure CMWRT1Q
WRTQUE DB WRTQSIZ DUP (021H); DATA QUEUE.
WRTHEAD DW 0 ; CURRENT HEAD OF QUEUE.
; HEAD IS INCREMENTED AFTER EACH QUEUE WRITE.
; IF NEW HEAD = TAIL, OVERFLOW OCCURRED.
; QUEMTY IS SET TO NOTMTY.
WRTTAIL DW 0 ; CURRENT TAIL OF QUEUE.
; 1ST, QUEMTY IS CHECKED. IF NOTMTY, DATA IS
; READ AND TAIL IS INCREMENTED AFTER THE READ.
; IF NEW TAIL= HEAD, QUEUE IS EMPTY.
WRTOVRFLO DW WRTQNTFL ; QUEUE OVERFLOW INDICATOR.
WRTQSPACE DW WRTQSIZ ; SPACE LEFT ON QUEUE.
WRTQSTOP DW WRTEMPTY ; QUEUE PAUSED INDICATOR.
TXBUSY DW WRTEMPTY ; TRANSMIT INDICATOR.
CMWRT1Q ENDS
;--- END / CMWRT1Q/

CODE SEGMENT USE16
SUBTTL COM1HANDLER
; INTERRUPT HANDLER FOR COM1 PORT.
INTBGN PROC FAR
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
PUBLIC _COM1HNDL ; DECLARE AS PUBLIC, I.E. EXERNAL.
OLDINTC DD ? ; HOLDER FOR INTERRUPT C ADDRESS.
_COM1HNDL LABEL FAR ; Serial Port IO HANDLER.
PUSH AX ; SAVE AX.
PUSH BX ; SAVE BX.
PUSH CX ; SAVE CX.
PUSH DX ; SAVE DX.
PUSH DI ; SAVE DI.
PUSH DS ; SAVE DS.
MOV AX,CMCOM1Q ; POINT TO COM1 COMMON BLOCK.
MOV DS,AX
; GET INTERRUPT TYPE.
MOV DX,INTRID ; GET PORT ADDRESS.
IN AL,DX ; GET THE VALUE.
MOV BX,AX ; COPY THE VALUE FOR LATER USE.
AND AL,ERRINT ; DID AN ERROR CAUSE THE INTERRUPT?
CMP AL,ERRINT
JNE NOERR
MOV DS:[DTAERR],NOTMTY ; SET ERROR INDICATOR.
MOV DX,LNSTAT ; GET LINE STATUS REGISTER.
IN AL,DX ; READING LINE STATUS RESETS INTERRUPT.
NOERR LABEL NEAR
; JMP INTFNS ; FINISH UP THIS INTERRUPT.
MOV AX,BX ; GET STORED VALUE.
AND AL,RCVINT
CMP AL,RCVINT ; TEST FOR DATA RECEIVED.
JNE NOREAD ; WAS IT A READ?
; GET DATA REGISTER.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; COPY IT.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
IN AL,DX ; INPUT DATA.
MOV DI,DS:[HEAD] ; GET HEAD OF QUEUE.
MOV BYTE PTR DS:[DI+RCVQUE],AL ; STORE THE BYTE.
MOV DS:[QUEMTY],NOTMTY ; SET QUEUE Not EMPTY
; RESET LINE CONTROL TO ORIGINAL VALUE.
MOV DX,LNCNTL
MOV AL,AH ; GET ORIGINAL VALUE.
OUT DX,AL ; RESTORE IT.
INC DI ; POINT TO NEXT QUEUE POSITION.
CMP DI,QUESIZ ; OVER THE END?
JNE HDRINC
XOR DI,DI ; WRAP HEAD POINTER BACK TO ZERO.
HDRINC LABEL NEAR
MOV DS:[HEAD],DI ; STORE THE INCREMENTED HEADER.
MOV DX,DS:[TAIL] ; GET TAIL POINTER.
CMP DI,DX ; IS NEW HEAD = TAIL?
JNE NOVFLO ; IF NOT EQUAL, IS OK.
MOV DS:[OVRFLO],QUFULL ; THE QUEUE IS FULL: SET FLAG
NOVFLO LABEL NEAR ; FINISHED.
; HOW MUCH SPACE IS LEFT?
MOV AX,DS:[QSPACE] ; GET QSPACE INDICATOR.
DEC AX ; WE JUST USED UP ONE BYTE.
MOV DS:[QSPACE],AX ; STORE THE NEW VALUE.
CMP AX,QUEMIN ; IS THERE ENOUGH SPACE LEFT ON THE QUEUE?
JA NOPAUS ; YES, THINGS ARE FINE
NOP ; A CONTROL S could be sent here to SLOW THINGS DOWN.
NOPAUS LABEL NEAR
; JMP INTFNS ; FINISH UP THIS INTERRUPT
;
NOREAD LABEL NEAR ; NOT A READ.
; CHECK FOR MULTIPLE CONDITIONS THAT COULD CAUSE THE INTERRUPT.
MOV AX,BX ; RESTORE IIR VALUE.
AND AL,SNDINT
CMP AL,SNDINT ; DID THE TRANSMIT BUFFER EMPTY CAUSE THE INTERRUPT?
JNE NOSEND
MOV AX,CMWRT1Q ; POINT TO COM1 WRITE COMMON BLOCK.
MOV DS,AX
; HOW MUCH SPACE IS LEFT?
MOV AX,DS:[WRTQSPACE] ; GET QSPACE INDICATOR.
CMP AX,WRTQSIZ ; IS IT EMPTY?
JNE WRTDTA
; ALL SPACE IS AVAILABLE, THE QUEUE IS EMPTY.
MOV DX,MDCNTL ; CLEAR THE RTS (REQUEST TO SEND) BIT.
MOV AL,OUT2 OR DTR ; ENABLE DATA TERMINAL READY
OUT DX,AL
JMP NOSEND ; WE ARE DONE.
WRTDTA LABEL NEAR
INC AX ; WE WILL SEND ONE BYTE.
MOV DS:[WRTQSPACE],AX ; STORE THE NEW VALUE.
; WRITE TO THE DATA REGISTER.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; COPY IT.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SET TO ADDRESS THE DATA BUFFERS.
MOV DI,DS:[WRTTAIL] ; GET TAIL OF WRITE QUEUE.
MOV AL,BYTE PTR DS:[DI+WRTQUE]; GET THE BYTE TO TRANSMIT.
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
OUT DX,AL ; OUTPUT DATA.
; RESET LINE CONTROL TO ORIGINAL VALUE.
MOV DX,LNCNTL
MOV AL,AH ; GET ORIGINAL VALUE.
OUT DX,AL ; RESTORE IT.
INC DI ; POINT TO NEXT TAIL QUEUE POSITION.
CMP DI,WRTQSIZ ; OVER THE END?
JNE WRTINC
XOR DI,DI ; WRAP TAIL POINTER BACK TO ZERO.
WRTINC LABEL NEAR
MOV DS:[WRTTAIL],DI ; STORE THE INCREMENTED HEADER.
; READING THE INTERRUPT IDENTIFICATION REGISTER CLEARS THIS INTERRUPT.
; JMP INTFNS ; FINISH UP THIS INTERRUPT
NOSEND LABEL NEAR
MOV AX,BX ; RESTORE VALUE.
AND AL,MDMINT ; HOW ABOUT A MODEM ERROR?
CMP AL,MDMINT
JNE INTERR ; TEST THAT THE INTERRUPT WAS PROCESSED.
MOV AX,CMCOM1Q
MOV DS,AX
XOR CX,CX
MOV DX,MDSTAT ; READ THE MODEM STATUS REGISTER
IN AL,DX
INC DS:[MDMERR]
JMP INTFNS ; WE KNOW WE PROCESSED THE INTERRUPT
INTERR LABEL NEAR ; UNKNOWN INTERRUPT?
MOV AX,CMCOM1Q
MOV DS,AX
ADD DS:[UNKINT],CX
INTFNS LABEL NEAR
POP DS
POP DI ; RESTORE DI.
POP DX ; RESTORE DX.
POP CX ; RESTORE CX.
POP BX ; RESTORE BX.
MOV AL,INTRST ; RESTORE
OUT INTREG,AL ; INTERRUPTS.
POP AX ; RESTORE REGISTERS.
; JMP DWORD PTR CS:[OLDINTC] ; DAISY-CHAIN INTERRUPTS.
IRET ; DO NOT DAISY-CHAIN INTERRUPTS.
INTBGN ENDP
;
INITCD PROC FAR
PUBLIC _COM1INT
_COM1INT LABEL FAR ; INITIALIZE COM1 INTERRUPT HANDLER.
PUSH AX
PUSH DX
PUSH DS
; MAKE SURE THAT THE RX BUFFER IS EMPTY.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SET TO ADDRESS THE DATA BUFFER.
RXCLEAR LABEL NEAR
MOV DX,LNSTAT
IN AL,DX ; GET THE VALUE.
AND AL,DTARCV ; CHECK THE DATA RECEIVED BIT.
JZ RXEMPTY ; IS IT CLEAR?
; SOMETHING IS IN THERE!
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
IN AL,DX ; INPUT DATA, THIS SHOULD CLEAR THE DATA RECEIVED BIT.
JMP RXCLEAR ; REPEAT UNTIL EMPTY.
RXEMPTY LABEL NEAR
XOR AX,AX ; SET DS TO 0000.
MOV DS,AX
CLI ; DISABLE INTERRUPTS
; SET UP MODEM AND LINE CONTROL.
MOV DX,MDCNTL
MOV AL,OUT2 OR DTR ; ENABLE INTERRUPTS (OUT2) AND DATA TERMINAL READY
OUT DX,AL
MOV AX,WORD PTR DS:[030H] ; GET OFFSET
MOV WORD PTR CS:[OLDINTC],AX ; STORE IN CODE SEGMENT.
MOV AX,WORD PTR DS:[032H] ; GET SEGMENT
MOV WORD PTR CS:[OLDINTC+2],AX
MOV WORD PTR DS:[030H],OFFSET _COM1HNDL ; LOAD OFFSET OF INT HANDLER
MOV WORD PTR DS:[032H],SEG _COM1HNDL ; LOAD SEGMENT OF INT HANDLER
; ENABLE INTERRUPTS ON THE SERIAL PORT FOR DATA RECEIVED.
STI ; ENABLE INTERRUPTS.
; INITIALIZE QUEUE POINTERS AND STATUS.
MOV AX,CMCOM1Q ; LOAD UP THE COM2 RECEIVE COMMON BLOCK.
MOV DS,AX
XOR DX,DX ; SET AX TO ZERO
MOV WORD PTR DS:[HEAD],DX ; CURRENT HEAD OF QUEUE.
MOV WORD PTR DS:[TAIL],DX ; CURRENT TAIL OF QUEUE.
MOV WORD PTR DS:[OVRFLO],QUNTFL ; QUEUE OVERFLOW INDICATOR.
MOV WORD PTR DS:[QUEMTY],EMPTY ; QUEUE EMPTY INDICATOR.
MOV WORD PTR DS:[QSPACE],QUESIZ ; SPACE LEFT ON QUEUE.
MOV WORD PTR DS:[QSTOP],EMPTY ; QUEUE PAUSED INDICATOR.
MOV WORD PTR DS:[DTAERR],EMPTY ; RECEIVE ERROR INDICATOR.
MOV BYTE PTR DS:[_COM1LOADED],0FFH ; SET INDICATOR THAT HANDLER IS LOADED.
; AND INIT THE WRITE QUEUE.
MOV AX,CMWRT1Q ; LOAD UP THE COM2 RECEIVE COMMON BLOCK.
MOV DS,AX
MOV WORD PTR DS:[WRTHEAD],DX ; CURRENT HEAD OF QUEUE.
MOV WORD PTR DS:[WRTTAIL],DX ; CURRENT TAIL OF QUEUE.
MOV WORD PTR DS:[WRTOVRFLO],WRTQNTFL ; QUEUE OVERFLOW INDICATOR.
MOV WORD PTR DS:[WRTQSPACE],WRTQSIZ ; SPACE LEFT ON QUEUE.
MOV WORD PTR DS:[WRTQSTOP],WRTEMPTY ; QUEUE PAUSED INDICATOR.
MOV WORD PTR DS:[TXBUSY],WRTEMPTY ; TRANSMIT INDICATOR.
MOV AL,MDMCHG OR RCVERR OR SNDMTY OR DTRCVD ; OUTPUT INTERRUPT ENABLE.
MOV DX,INTNBL ; TO THE INTERRUPT ENABLE PORT.
OUT DX,AL
XOR AL,AL ; CLEAR AL.
OUT INTMSK,AL ; SET MASK REGISTER TO 0.
MOV AL,INTRST ; RESTORE
OUT INTREG,AL ; INTERRUPTS.
POP DS
POP DX
POP AX
RET
INITCD ENDP
RSTCD PROC FAR
PUBLIC _COM1RST
_COM1RST LABEL FAR ; RESET INTERRUPTS.
PUSH DX
PUSH AX
PUSH DS ; SAVE DS
PUSH ES
XOR AX,AX ; SET ES TO 0000.
MOV ES,AX
CLI ; DISABLE INTERRUPTS.
MOV AX,WORD PTR CS:[OLDINTC] ; RESTORE INTERRUPT VECTOR
MOV WORD PTR ES:[030H],AX ; OFFSET.
MOV AX,WORD PTR CS:[OLDINTC+2] ; RESTORE ORIGINAL INTERRUPT
MOV WORD PTR ES:[032H],AX ; SEGMENT.
; DISABLE INTERRUPTS ON THE SERIAL PORT.
XOR AL,AL ; OUTPUT NO INTERRUPTS ENABLED.
MOV DX,INTNBL ; TO THE INTERRUPT ENABLE PORT.
OUT DX,AL
STI ; RESTORE INTERRUPTS.
; RESET QUEUE TO ORIGINAL STATE.
MOV AX,CMCOM1Q ; POINT TO COM COMMON BLOCK.
MOV DS,AX
XOR AX,AX ; SET TAIL AND HEAD TO ZERO.
MOV DS:[HEAD],AX ; CURRENT HEAD OF QUEUE.
MOV DS:[TAIL],AX
; SET OVERFLOW TO QUEUE-NOT-FULL.
MOV DS:[OVRFLO],QUNTFL
; SET QUEUE EMPTY TO EMPTY.
MOV DS:[QUEMTY],EMPTY
; SET SPACE LEFT ON QUEUE TO FULL SIZE.
MOV DS:[QSPACE],QUESIZ
; RESET PAUSE FLAG.
MOV DS:[QSTOP],EMPTY
MOV BYTE PTR DS:[_COM1LOADED],0 ; SET INDICATOR THAT HANDLER IS UNLOADED.
POP ES
POP DS
POP AX
POP DX
RET
RSTCD ENDP
SUBTTL INPUT/ OUTPUT SUBROUTINES.
COM1SUBS PROC FAR
PUBLIC _RDCOM1
_RDCOM1 LABEL FAR
; LOAD THE NEXT VALUE OF THE COM QUEUE.
PUSH DS ; SAVE DATA SEGMENT.
MOV AX,CMCOM1Q ; SET DS TO CMCOM1Q SEGMENT.
MOV DS,AX
; CHECK QUEUE EMPTY BUFFER.
MOV AX,DS:[QUEMTY] ; GET QUEMTY.
CMP AX,EMPTY
JNE GETVAL
POP DS ; QUEUE IS EMPTY, RETURN.
RET
GETVAL LABEL NEAR
PUSH SI ; Save Register
MOV SI,DS:[TAIL] ; GET TAIL.
MOV AL,BYTE PTR DS:[SI+RCVQUE] ; GET VALUE POINTED TO BY TAIL.
XOR AH,AH
INC SI ; INCREMENT TAIL.
CMP SI,QUESIZ ; OVER THE END?
JNE TALINC
XOR SI,SI ; WRAP TAIL POINTER BACK TO ZERO.
TALINC LABEL NEAR
MOV DS:[TAIL],SI ; STORE NEW TAIL.
CLI ; DISABLE INTERRUPTS WHILE CHECKING HEAD AND QSPACE.
CMP SI,DS:[HEAD] ; IS TAIL=HEAD?
JNE MORDTA
MOV DS:[QUEMTY],EMPTY ; QUEUE IS EMPTY.
MORDTA LABEL NEAR
; INCREMENT QSPACE.
INC DS:[QSPACE] ; QSPACE = QSPACE+ 1
; DO WE NEED TO SEND A CONTROL Q?
MOV SI,DS:[QSTOP] ; GET QUEUE STOPPED FLAG.
CMP SI,NOTMTY ; IS THE FLAG SET?
JNE NOTSTP
MOV DS:[QSTOP],EMPTY ; RESET FLAG.
; SEND OUT CONTROL Q at this point if ^S and ^Q signalling is in use..
NOTSTP LABEL NEAR
; CHECK FOR OVERFLOW.
MOV SI,QUFULL
CMP SI,DS:[OVRFLO]
JNE QUECHK
MOV AX,QUFULL ; OVERFLOW OCCURRED.
QUECHK LABEL NEAR
; VALUE IS RETURNED IN AX.
STI ; ENABLE INTERRUPTS.
POP SI
POP DS
RET ; RETURN
CHARS EQU 6 ; Offset of Length
STRING EQU 8 ; OFFSET OF STRING.
PUBLIC _WRCOM1Q
_WRCOM1Q LABEL FAR
; WRITE A STRING TO THE COM1 QUEUE AND SEND USING INTERRUPTS.
PUSH BP
MOV BP,SP
PUSH DS ; PUSH DS.
PUSH ES
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV CX,WORD PTR SS:[BP+CHARS] ; GET NUMBER OF BYTES.
LES SI,DWORD PTR SS:[BP+STRING] ; GET ADDRESS OF BUFFER.
MOV AX,CMWRT1Q ; GET COM1 TRANSMIT COMMON BLOCK.
MOV DS,AX
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY AND READY TO SEND
OUT DX,AL
; IS THE QUEUE EMPTY?
CLI ; DISABLE INTERRUPTS WHILE CHECKING QUEUE SIZE.
MOV DI,DS:[WRTQSPACE] ; GET WRITE QUEUE INDICATOR.
CMP DI,WRTQSIZ ; IS IT EMPTY?
JNE QUEDTA
; THE TRANSMIT BUFFER IS EMPTY AND MUST BE "PRIMED" BY SENDING OUT THE FIRST BYTE.
; CHECK THAT THE PHYSICAL TX BUFFER IS EMPTY.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST3 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST3 ; LOOP UNITL NON-ZERO.
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY AND REQUEST TO SEND
OUT DX,AL
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; SAVE ORIGINAL VALUE.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SEND IT.
MOV AL,ES:[SI] ; GET THE FIRST BYTE TO SEND.
INC SI ; POINT TO THE NEXT BYTE.
MOV DX,DATABF ; SEND THE FIRST BYTE TO THE DATA PORT.
OUT DX,AL
MOV AL,AH ; RESTORE LINE CONTROL.
MOV DX,LNCNTL
OUT DX,AL ; BACK IT GOES.
DEC CX ; DECREMENT THE COUNT.
MOV AX,DI ; IF NEXT BRANCH OCCURS, ALL SPACE IS AVAILABLE.
CMP CX,0 ; DID WE ONLY HAVE TO SEND ONE BYTE?
STI ; ENABLE INTERRUPTS.
JE TXDONE ; WAHOO!
QUEDTA LABEL NEAR
STI ; ENABLE INTERRUPTS
NOP
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CLI ; DISABLE INTERRUPTS TO UPDATE QSPACE.
CMP AX,0
JE QUEDTA ; WAIT FOR SOME SPACE TO CLEAR UP.
DEC AX ; UPDATE AVAILABLE QUEUE SPACE, PREVENT INTERRUPT HANDLER FROM
MOV DS:[WRTQSPACE],AX ; MODIFYING IT AT THE SAME TIME
STI ; ENABLE INTERRUPTS.
MOV DI,DS:[WRTHEAD] ; GET THE HEAD OF THE QUEUE.
MOV AL,ES:[SI] ; GET NEXT BYTE TO QUEUE UP.
INC SI ; POINT TO NEXT BYTE
MOV DS:[DI+WRTQUE],AL ; PUT IT ON THE QUEUE.
INC DI ; POINT TO NEXT SPOT IN QUEUE
CMP DI,WRTQSIZ
JNE HEADINC
XOR DI,DI ; RESET IT TO ZERO.
HEADINC LABEL NEAR
MOV DS:[WRTHEAD],DI
LOOP QUEDTA
MOV AX,DS:[WRTQSPACE]
TXDONE LABEL NEAR
POP DI
POP SI
POP DX
POP CX
POP ES
POP DS
POP BP
RET ; RETURN
;
PUBLIC _WRCOM1P
_WRCOM1P LABEL FAR
; WRITE A STRING TO THE COM PORT USING POLLING
PUSH BP
MOV BP,SP
PUSH DS ; PUSH DS.
PUSH ES
PUSH CX
PUSH DX
PUSH SI
; LOAD DX WITH ADDRESS OF HANDLE.
MOV CX,WORD PTR SS:[BP+CHARS] ; GET NUMBER OF BYTES.
LES SI,DWORD PTR SS:[BP+STRING] ; GET ADDRESS OF BUFFER.
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
MTYDTA LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY BEFORE WE CAN DO A POLLED OUTPUT.
JNE MTYDTA ; WAIT FOR THE QUEUE TO EMPTY.
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY
OUT DX,AL
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
PUSH AX ; SAVE ORIGINAL VALUE.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SEND IT.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST2 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST2 ; LOOP UNITL NON-ZERO.
SEND2 LABEL NEAR
MOV DX,DATABF ; SEND DATA.
MOV AL,BYTE PTR ES:[SI] ; GET NEXT BYTE.
OUT DX,AL
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXFNS2 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXFNS2 ; LOOP UNITL NON-ZERO.
INC SI ; POINT TO NEXT BYTE.
LOOP SEND2
MOV DX,MDCNTL
MOV AL,OUT2 OR DTR ; ENABLE DATA TERMINAL READY
MOV DX,LNCNTL ; SET TO LINE CONTROL.
POP AX ; GET ORIGINAL VALUES BACK.
OUT DX,AL ; Send it out THE VALUE.
POP SI
POP DX
POP CX
POP ES
POP DS
POP BP
MOV AX,NORMAL
RET ; RETURN
;
PUBLIC _COM1RDY
_COM1RDY LABEL FAR
; RETURN CONDITION OF QUEMTY
PUSH DS ; SAVE DATA SEGMENT.
MOV AX,CMCOM1Q ; SET DS TO CMCOM1Q SEGMENT.
MOV DS,AX
MOV AX,DS:[QUEMTY] ; GET VALUE OF QUEMTY .
POP DS ; RESTORE DATA SEGMENT.
RET ; RETURN
;
PUBLIC _WRQ1FLUSH
_WRQ1FLUSH LABEL FAR
; EMPTY THE COM PORT WRITE QUEUE.
PUSH DS ; PUSH DS.
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
FLUSHQ LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY.
JNE FLUSHQ ; WAIT FOR THE QUEUE TO EMPTY.
POP DS
RET ; RETURN

PUBLIC _SETBAUD1
_SETBAUD1 LABEL FAR
BAUDRATE EQU 6
PUSH BP ; SAVE BP
MOV BP,SP ; GET BAUD RATE FROM ARGUMENT LIST
PUSH DS ; PUSH DS.
PUSH DX
PUSH CX
PUSH BX
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
MOV CX,WORD PTR SS:[BP+BAUDRATE] ; GET BAUD RATE FROM THE ARGUMENT LIST. VALUES GO UP TO 11520.
FLUSHQ2 LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY.
JNE FLUSHQ2 ; WAIT FOR THE QUEUE TO EMPTY.
; CHECK THAT THE PHYSICAL TX BUFFER IS EMPTY.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST4 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST4 ; LOOP UNITL NON-ZERO.
; GET BAUD RATE DIVISORS.
CMP BX,11520 ; use 11520 for 115200 baud.
JNE CHK57600
MOV BX,B115200L ; BAUD RATE DIVISOR FOR 115200.
JMP SETRATE
CHK57600 LABEL NEAR
CMP CX,57600 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK38400
MOV BX,B57600L ; BAUD RATE DIVISOR FOR 57600.
JMP SETRATE
CHK38400 LABEL NEAR
CMP CX,38400 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK19200
MOV BX,B38400L ; BAUD RATE DIVISOR FOR 38400.
JMP SETRATE
CHK19200 LABEL NEAR
CMP CX,19200 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK9600
MOV BX,B19200L ; BAUD RATE DIVISOR FOR 19200.
JMP SETRATE
CHK9600 LABEL NEAR
MOV BX,B9600L ; BAUD RATE DIVISOR FOR 9600.
SETRATE LABEL NEAR
; SET NEW BAUD RATE.
MOV DX,LNCNTL ; ADDRESS BAUD RATE REGISTERS.
MOV AL,SETBAUD ; SET LINE CONTROL TO MAP BAUD RATE REGISTERS.
OUT DX,AL ; SET TO BAUD GENERATOR.
MOV DX,BAUDLO ; GET LOW BYTE OF BAUD RATE.
MOV AL,BL ; SET TO 9600 BAUD.
OUT DX,AL
INC DX ; SET TO BAUD RATE HIGH.
XOR AL,AL ; SET HIGH BYTE
OUT DX,AL
; SET TO 8 BITS NO PARITY, 1 STOP BIT.
MOV DX,LNCNTL ; ADDRESS LINE CONTROL.
MOV AL,BITLEN8 OR STOP1BT
OUT DX,AL
; SET UP MODEM AND LINE CONTROL.
MOV DX,MDCNTL
; MOV AL,0BH ; ENABLE DATA TERMINAL READY
MOV AL,OUT2 OR DTR ; ENABLE INTERRUPTS AND DATA TERMINAL READY
OUT DX,AL
POP BX
POP CX
POP DX
POP DS
POP BP
RET
COM1SUBS ENDP
RSTPIC PROC FAR
PUBLIC _RST8259
_RST8259 LABEL FAR ; RESET THE 8259 PROGRAMMABLE
; INTERRUPT CONTROLLERS.
CLI ; DISABLE INTERRUPTS.
; THIS CODE RE-INITIALIZES THE 8259 MASTER.
MOV AL,11H ; ICW1: EDGE, MASTER, ICW4
OUT INTREG,AL ; SEND IT
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,08H ; ICW2: INT VECTORS 8-F.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
MOV AL,04H ; ICW3: MASTER, SLAVE ON IR2.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,01H ; ICW4: MASTER, 8086 MODE.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT

; THIS CODE RE-INITIALIZES THE 8259 SLAVE.
MOV AL,11H ; ICW1: EDGE MODE, ICW4
OUT SLVRST,AL ; SEND IT
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,070H ; ICW2: INT VECTORS 70H-7FH.
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
MOV AL,02H ; ICW3: SLAVE ID 2 (010).
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,01H ; ICW4: 8086 MODE.
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT

STI ; RESTORE INTERRUPTS.
RET
RSTPIC ENDP
CODE ENDS
;
STACK SEGMENT USE16 WORD STACK 'STACK'
STAKSZ EQU 16
IF STAKSZ
DB STAKSZ DUP (?)
ENDIF
STACK ENDS
END
 
Wowee! As I read this I was thinking how most hackers today get their subroutines or included 'objects' off the Web from common pools. But this raw code is unusual in today's world I think, unless there's a secret conclave of hooded code masters somewhere churning out real code while the would-be hackers just copy and paste. I imagine as well that deep in the bowels of our brotherly intel services someone is looking at your code and saying "Whaaa - we better watch this guy".
 
Someone on the Dos Ain't Dead forum wanted to know how to do interrupt driven TX for a serial port... And this was too big to post there, so I linked back.

DOS ain't dead - uart interrupt tx rx in Dos32 bit open watcom



Knowing that software engineers like rangefinder cameras...

This code took some work: my copy of the IBM Technical Ref manual has hand-written corrections in it. They "unreserved" a bit that needs to be set for the interrupts to come through. One of the bits "became" an line that would mask off the incoming interrupts. Figured that out sometime in the 1980s and had to disassemble a terminal emulator to find the value that they wrote to the register.
 
I found an assembly language driver that I wrote in 1994, but could not find any code written that actually used it.

I just debugged it.

This is now officially the longest time that it's ever taken me to debug a program... 20 years.

The Symptom of the Bug: The computer turned itself off. This also ups the ante for my "The only error message a user needs is a blank screen and a locked keyboard". I've mellowed. I liked getting the HEX dump, led right to the error in the code.
 
I found an assembly language driver that I wrote in 1994, but could not find any code written that actually used it. I just debugged it. This is now officially the longest time that it's ever taken me to debug a program... 20 years. The Symptom of the Bug: The computer turned itself off. This also ups the ante for my "The only error message a user needs is a blank screen and a locked keyboard". I've mellowed. I liked getting the HEX dump, led right to the error in the code.

I haven't coded in assembler in the formal sense, but I've always maintained a folder I named 'MASM', to recompile DOS assembly routines needed for BASIC environments, especially in the 1980's. The biggest downfall of BASIC was that BASIC would always allocate new temporary memory space (chewing up time) to do operations like Chr$(x), to generate a byte from a 0-255 value, and Asc(Mid$(x$, n, n)) to get the numeric value of one byte in a string. Replacing those 2 routines with fast assembly code that would use the stack only to return a value without allocating memory - made parsing possible, although much slower than 'C'.

Then I wrote an ASCII byte viewer, showing 2000 literal characters on a 25x80 screen, with the first 32 characters substituted with viewable highlighted characters. Making that viewer configurable for width (i.e. VIEW filename 50, to see 50 wide by 25 high) made possible some quick analysis of fixed-length-record 'binary' data files. The downside is Windows, which shows different characters than DOS above ASCII 127.

02/20/1990 05:21 AM 39,255 BIND.EXE
02/13/1990 08:59 AM 49,661 CVPACK.EXE
02/20/1990 05:12 PM 46,277 EXEHDR.EXE
02/20/1990 05:22 AM 41,487 EXP.EXE
01/31/1991 03:20 AM 20,122 H2INC.ERR
04/03/1991 08:38 AM 137,016 H2INC.EXE
01/24/1991 02:18 PM 67,193 HELPMAKE.EXE
02/20/1990 05:09 PM 34,727 IMPLIB.EXE
02/26/1991 02:16 PM 55,248 LIB.EXE
03/08/1991 12:07 PM 144,159 LINK.EXE
06/14/1990 08:36 AM 58,123 PWBRMAKE.EXE
02/20/1990 05:22 AM 33,337 RM.EXE
02/20/1990 05:23 AM 42,659 UNDEL.EXE
11/24/2009 05:42 AM <DIR> INCLUDE
 
That's pretty much why I stick with FORTRAN and Assembly for time-critical stuff, never have to worry about the compiler doing maintenance while running. I'll also use "C", but never let it allocate memory on the fly.

What is amazing - the kind of response time that the software gives running on a 2.4GHz machine. Without Windows slowing it down.

The above code- used to control .... wait for it ... pinky to lip... LAZUHHHRRRzzzz...
 
While I have the utmost respect for anyone who knows (or knew) all of that DOS stuff (I used to do okay, but that was then and this is now) I highly doubt I would have the patience to do anything more than change directories in DOS these days.

Command line is so 20th century.
 
While I have the utmost respect for anyone who knows (or knew) all of that DOS stuff (I used to do okay, but that was then and this is now) I highly doubt I would have the patience to do anything more than change directories in DOS these days. Command line is so 20th century.

Actually the command line in Windows (i.e. DOS) or probably under UNIX and its variants including OSX, is the only place where you can do automation (i.e. batch files).

If you try coding to replace batch files, you get into nightmares of UI coding and registry and all manner of kludge to find things. Building Internet objects was supposed to be easy (it can be), but it's still a messy business and full of bugs, not to mention vastly insufficient error processing.

My DOS utility system is a far better Object Oriented system than anything that's been written in C++ or the other semi-AI systems. Basically, anything (objects) can call anything else (objects), passing whatever parameters are needed. There is no early binding (another nightmare) and no need to worry about blown stacks from failing to close objects.

The proof of the pudding is in the eating - a one-hour demo of this never fails to leave people gasping (I'll leave that one to your imagination).
 
While I have the utmost respect for anyone who knows (or knew) all of that DOS stuff (I used to do okay, but that was then and this is now) I highly doubt I would have the patience to do anything more than change directories in DOS these days.

Command line is so 20th century.

Anybody watch Stargate Atlantis?

The crew makes it aboard an alien spaceship, which is heavily damaged. The Chief Scientist working at a damaged console loudly proclaims "It's a good thing I know DOS!" as he works to bring life support online.

I use the "EXEC" function a lot from within FORTRAN for automation of processes.

Right now- I'm in deep, chasing stalled interrupt handlers caused by hardware "not quite following the spec". Good thing I have a pair of Oscopes at home...

20+ years ago I told one of my friends that I "Tronned Out" working on code, and that's why I had not called in a while. He entered it into some geek-computer dictionary as a term for working intensely on software. I'm somewhere between "Tronned out" and "Mind-Meld with the Computer". I did get the computer to quit turning off whenever it hit the bug... Better than catching on fire. That's only happened to me twice.

"I am Tanru, I am the Other... Great Damage... Darkness..." and

"Dave, my mind Dave" I told my Boss 30+ years ago that if anyone offered my a job as system programmer on a HAL 9000 I would grab it. Stupid 21st century. No flying cars, and no HAL 9000's. at least I can use my 1930s lenses on a digital camera. And you don't even need liquid nitrogen for them anymore. I found my DOS code used to debug the parallel interface on the digital imager that we built in 1983.
 
So I did some quick hacks to a program written to batch process my DCS200 raw files, cut out most of the code that processed the images and just used it to change one byte in the EXIF data of the Nikon Df ".nef" files.

Lightroom 4 now thinks the .NEF is for a Nikon D4, which has the same sensor and file format.


PROGRAM CVTDFNEF
IMPLICIT NONE
CHARACTER* 80 FILE
INTEGER* 2 COLUMS, ROWS
C+++ BEGIN / CMLUNM/
INTEGER* 2 LUINPT, LUOUT, LULIST
COMMON/ CMLUNM/ LUINPT, LUIN, LULIST
C--- END / CMLUNM/
C+++ BEGIN DOS I/O FUNCTION CODES.
C FILE I/O PARAMETERS AND CODES.
INTEGER* 2 CRTNRM, CRTRD, CRTHDN, CRTSYS
INTEGER* 2 OPNRD, OPNWRT, OPNRDW
INTEGER* 2 FRMBGN, FRMCRN, FRMEND
INTEGER* 2 BADFIL, NORMAL
PARAMETER ( CRTNRM= 0, CRTRD= 1, CRTHDN= 2, CRTSYS= 3)
PARAMETER ( OPNRD= 0, OPNWRT= 1, OPNRDW= 2)
PARAMETER ( FRMBGN= 0, FRMCRN= 1, FRMEND= 2)
PARAMETER ( BADFIL= 0, NORMAL= -1)
C--- END DOS I/O FUNCTION CODES.
C LOCAL STUFF.
CHARACTER* 60 PATH
INTEGER* 2 I, J, K, LENGTH
INTEGER* 2 STATUS, BYTES, PATHLN
CHARACTER* 80 STRING, FLNAME
INTEGER* 4 OFFSET, POSITN
LOGICAL* 1 HEADER( 0: 511)
CHARACTER* 1 SCANCHAR( 0: 511), ASCIIF, ASCII4
EQUIVALENCE ( HEADER, SCANCHAR)
C EXTERNALS.
INTEGER* 4 LOC
INTEGER* 2 FILOPN, FILCRT, FILCLS, READF, WRITEF, READS, WRITES
INTEGER* 4 MVFLPT
INTEGER* 4 UNSIGN
INTEGER* 2 STRLEN, IEOR, IAND, IOR, FILENG, EXCUTE
CHARACTER* 1 UPCASE
INTEGER* 2 CHANGE
CHANGE= Z'0183'
WRITE( *, *) 'PATH TO NEF FILES: '
READ( *, 200) PATH
200 FORMAT( A)
PATHLN= STRLEN( PATH)
IF( PATHLN .GT. 0) THEN
IF( PATH( PATHLN: PATHLN) .NE. '\') THEN
PATHLN= PATHLN+ 1
PATH( PATHLN: PATHLN)= '\'
END IF
WRITE( STRING, 400) PATH( 1: PATHLN)
400 FORMAT( 'DIR/B ', A, '*.NEF>TEMPNEF.DAT')
ELSE
STRING= 'DIR/B *.NEF>TEMPNEF.DAT'
END IF
I= STRLEN( STRING)
STATUS= EXCUTE( STRING( 1: I))
LULIST= 1
OPEN( UNIT= LULIST, FILE= 'TEMPNEF.DAT', STATUS= 'OLD')
1010 CONTINUE
READ( LULIST, 300, END= 1000) FLNAME
300 FORMAT( A)
LENGTH= STRLEN( FLNAME)
C OPEN THE INPUT FILE.
IF( PATHLN .GT. 0) THEN
STRING= PATH( 1: PATHLN)// FLNAME( 1: LENGTH)
WRITE( *, *) STRING
ELSE
STRING= FLNAME( 1: LENGTH)
END IF
LENGTH= STRLEN( STRING)
WRITE( *, *) STRING( 1: LENGTH)
LUINPT= FILOPN( STRING( 1: LENGTH), OPNRDW)
OFFSET= 0
BYTES= 512
POSITN= MVFLPT( LUINPT, FRMBGN, OFFSET)
STATUS= READF( LUINPT, HEADER, BYTES)
IF( SCANCHAR( CHANGE) .EQ. 'f') THEN
SCANCHAR( CHANGE)= '4'
WRITE( *, *) ' CHANGED VALUE'
POSITN= MVFLPT( LUINPT, FRMBGN, OFFSET)
STATUS= WRITEF( LUINPT, HEADER, BYTES)
ELSE
WRITE( *, *) ' DID NOT CHANGE VALUE'
WRITE( *, *)( SCANCHAR( I), I= Z'0170', Z'0187')
END IF
STATUS= FILCLS( LUINPT)
GO TO 1010
1000 CONTINUE
STOP 'NORMAL TERMINATION'
END

INTEGER* 2 FUNCTION EXCUTE( CMNDLN)
CHARACTER CMNDLN* ( *)
C DECLARE EXTERNALS.
EXTERNAL EXEC
INTEGER* 2 LEN, EXEC
C DECLARE LOCAL STORAGE.
INTEGER* 2 LENGTH
LOGICAL* 1 CMDINT( 15)
LOGICAL* 1 COMAND( 80), ZERO, CRETRN, BYTE( 2)
INTEGER* 2 I, BGBYTE
CHARACTER* 8 FORMAT
SAVE BYTE, BGBYTE
EQUIVALENCE ( BGBYTE, BYTE( 1))
DATA ZERO/ Z'00'/, CRETRN/ Z'0D'/
C SET COMMAND INTERPRETER PATH TO 'C:\COMMAND.COM'. ZERO TERMINATE.
DATA CMDINT/ 1HC, 1H:, 1H\, 1HC, 1HO, 1HM, 1HM, 1HA, 1HN, 1HD,
1 1H., 1HC, 1HO, 1HM, .FALSE./
C SET COMAND(2): COMAND( 4) TO '/C '.
DATA COMAND( 2)/ 1H/ /, COMAND( 3)/ 1HC/, COMAND( 4)/ 1H /
C USE THE EXEC MACRO SUBROUTINE TO EXECUTE THE COMMAND INTERPRETER.
C PASS CMNDLN BUILT INTO THE COMMAND LINE STRING FOR THE COMMAND INTERPRETER.
C INSERT THE COMMAND LINE INTO COMAND.
LENGTH= LEN( CMNDLN)
C SET FIRST BYTE OF COMAND TO LENGTH.
BGBYTE= LENGTH+ 3
COMAND( 1)= BYTE( 1)
C CREATE A FORMAT STATEMENT TO READ THE COMMAND LINE.
WRITE( FORMAT, 300) LENGTH
300 FORMAT( '( ', I2, 'A1)')
C READ THE COMMAND LINE.
READ( CMNDLN, FORMAT)( COMAND( I), I= 5, LENGTH+ 4)
C TERMINATE COMAND WITH A CARRIAGE RETURN.
COMAND( LENGTH+ 5)= CRETRN
C EXECUTE THE COMMAND AND SAVE THE STATUS.
EXCUTE= EXEC( CMDINT, COMAND)
RETURN
END

I wonder how many other Df owners used FORTRAN and DOS to process their NEF files.
 

Similar threads

Back
Top