'===========================================================================
' Subject: ACCESSING COM PORT VIA INT 14      Date: 08-26-96 (11:39)       
'  Author: Robert Fortune                     Code: QB, PDS                
'  Origin: FidoNet QUIK_BAS Echo            Packet: MODEM.ABC
'===========================================================================
'-> Sorry, but that's the way QuickBasic does it. You have to use
'-> OPEN/CLOSE to read/write to files as well as COMPORTS.

'>       Actually, I found this in a help file:

'>INARY%(AX) = &H3C00            ' DOS function to create a file.
'>INARY%                         ' DOS attribute for created file.

'>       It appears that you can use Interrupts to work with files (and
'>probably devices such as commports).  I bet someone here could use the above
'>(or they might not even need it) to create their own OPEN/CLOSE SUBs and
'>other SUBs to work with the files such as writing to, reading from, and
'>getting information like LOF and EOF.

'   Yes you can. Here is some play code I've fiddled around with. It isn't
'   error-proof but it does demonstrate using BIOS Int 14h to access a com
'   port. You would use similar code to access a FOSSIL driver (BNU, X00...)
'   The demo code doesn't do anything but reset\init modem, dial out and
'   then hang up. It does show how to use interrupts to access a com port.

' --------- CUT HERE -------------------- CUT HERE ---------------------------
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM *  BIOSCOM.BAS    8/26/1996                                           *
REM *  Demo QB code to access a serial port via BIOS INT 14h using        *
REM *  QB\PDS.  YOU MUST start QB\PDS with the /L command line switch to  *
REM *  allow QB to call BIOS interrupt 14H as in:   QB BIOSCOM /L         *
REM *                                                                     *
REM *  Maximum baud rate BIOS Int 14H reliably supports is 9600 BPS.      *
REM *  This demo program uses 9600 BPS on COM 2 with NO parity, one Stop  *
REM *  Bit and eight Data Bits. Modify as needed. - RAF                   *
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DEFINT A-Z    ' All untyped variables default to type integer
'$INCLUDE: 'REGTYPE.BI'

DECLARE SUB InitPort (PortNum%, BaudRate%)
DECLARE SUB GetStatus ()
DECLARE SUB Get1Byte (Byte$)
DECLARE SUB GetStr (Text$)
DECLARE SUB Send1Byte (Byte$)
DECLARE SUB SendStr (Text$)
DECLARE FUNCTION Dec2Bin$ (b%) ' Useful function for determining com parms

DIM SHARED Registers AS RegType
DIM SHARED ComPort%, BaudRate%, Parity%, StopBits%, DataBits%

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM *   Example QB code to test BIOS INT 14H to access a serial port      *
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

ComPort% = 1      ' zero-based so 1 is actually com 2 (com 1 would be 0)
BaudRate% = 9600  ' 9600 BPS (max that BIOS INT 14H reliably supports)
Parity% = 16      ' use NO parity        00010000
StopBits% = 0     ' use one Stop bit     00000x00
DataBits% = 3     ' use eight Data bits  00000011

REM * * * * * * * * * * * * MAIN PROGRAM CODE * * * * * * * * * * * * * * *
CR$ = CHR$(13)    ' ASCII code for carriage return\ENTER
ESC$ = CHR$(27)   ' ASCII code the the ESC key
CLS
PRINT
CALL InitPort(ComPort%, BaudRate%)
PRINT
PRINT "Testing BIOS Interrupt 14H in QB\PDS to access a serial port."
PRINT
PRINT "Communications Port:" + STR$(ComPort% + 1)
PRINT "BPS Rate: " + STR$(BaudRate%)
PRINT "Resetting com port "; LTRIM$(STR$(ComPort% + 1)); "...";

ByteStr$ = ""
Text$ = "ATZ" + CR$     ' initialize\reset modem to use modem profile 0
CALL SendStr(Text$)

REM Wait for an OK from the modem that it received our modem
REM reset command.
DO
  CALL GetStr(Text$)
LOOP UNTIL INSTR(Text$, "OK")  ' u might wanna check for error(s) here
PRINT "done!"
PRINT
PRINT

REM Dial a number. Best to dial your own phone number here which will
REM ensure a BUSY signal and not annoy anyone.

LINE INPUT "Enter your telephone number: "; Number$
IF Number$ = "" THEN Number$ = "555-1212"  ' Information please? <g>
PRINT
Text$ = "ATDT" + Number$ + CR$ ' ATDT touch tone line (ATDP for pulse line)
CALL SendStr(Text$)            ' And dial out

REM Wait for a BUSY or CONNECT from the modem to be sure that the modem
REM received our DIAL command properly. In a real world program you would
REM need to check for other conditions like NO CARRIER, etc...
PRINT "Press ESC key to cancel"
PRINT
ByteStr$ = ""
DO
  CALL Get1Byte(Byte$)
  ByteStr$ = ByteStr$ + Byte$
  PRINT Byte$;
  AnyKey$ = INKEY$
  IF AnyKey$ <> "" THEN EXIT DO
LOOP UNTIL INSTR(ByteStr$, "BUSY") OR INSTR(ByteStr$, "CONNECT")
PRINT
PRINT "Hanging up...";
Text$ = "ATH0" + CR$    ' force the modem to hang up
CALL SendStr(Text$)
PRINT "All done Bubba!"
END  ' The End.
REM * * * * * * * * * * * * * THE END * * * * * * * * * * * * * * * * * * *

REM * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Converts a decimal number into a binary string.
REM  Called with:  b% which is an integer variable
REM  Returns:  binary value of integer b%
REM * * * * * * * * * * * * * * * * * * * * * * * * *
FUNCTION Dec2Bin$ (b%) STATIC
Temp$ = ""
H$ = HEX$(b%)
FOR I% = 1 TO LEN(H$)
    Digit% = INSTR("0123456789ABCDEF", MID$(H$, I%, 1)) - 1
    IF Digit% < 0 THEN
       Temp$ = ""
       EXIT FOR
    END IF
    J% = 8
    K% = 4
   DO  ' convert from hexadecimal to binary
       Temp$ = Temp$ + RIGHT$(STR$((Digit% \ J%) MOD 2), 1)
       J% = J% - (J% \ 2)
       K% = K% - 1
       IF K% = 0 THEN EXIT DO
   LOOP WHILE J%
NEXT I%
Dec2Bin$ = Temp$
END FUNCTION
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM   Receives one byte from active port via BIOS INT 14H - Function 2
REM   Called with: AH = 2
REM                DX = serial port 0 to 3 (zero-based)
REM   Returns: AH = Line Status
REM            AL = Byte recieved
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB Get1Byte (Byte$)
   PortFunc% = 2      ' Int 14H Function 2 = Read Char from port
   AL% =  0           ' zero-out, unused with this function
   Registers.AX = AL% + (256 * PortFunc%)
   Registers.DX = ComPort%  ' com port (zero-based, com 1 is zero, etc...)
   CALL INTERRUPT(&H14, Registers, Registers)
   Byte$ = CHR$(Registers.AX AND 255) ' return string of ASCII char rec'd
END SUB

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Reads line and modem status of the specified com port
REM  Called with:  AH = 3  ' function 3 (Get status) of INT 14H
REM                DX = serial port 0 to 3 (zero-based)
REM  Returns:  AH = Line Status
REM            AL = Modem Status
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB GetStatus
    AL% = 0          ' zero out AL register half
    AH% = 3          ' Int 14H Function 3 = Read line and modem status
    Registers.AX = AL% + (256 * AH%)
    Registers.DX = ComPort%  ' zero based active com port number
    CALL INTERRUPT(&H14, Registers, Registers)
    LineStat% = Registers.AX \ 256       ' extract AH from AX register
    ModemStat% = Registers.AX AND 255    ' extract AL from AX register
END SUB

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Gets a string from the active com port one byte at a time using
REM  INT 14H's Get Byte function (2). *See Get1Byte SUB
REM  Example:  CALL GetStr(Text$)
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB GetStr (Text$)
 DO
   CALL Get1Byte(Byte$)
   Text$ = Text$ + Byte$
 LOOP WHILE Byte$ <> CHR$(13)  ' loop till a carriage return is rec'd
END SUB

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Opens and initializes the com port via BIOS INT 14h (Function 0).
REM  Registers set before calling:
REM           AH = INT 14H function we want to invoke ( 0 = initialize port)
REM           AL = Serial port initialization values (Baud, Parity...)
REM           DX = com port number (zero based, 0 is com 1, 1 is com 2...)
REM  Returns:  AH = Line Status
REM            AL = Modem Status
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB InitPort (ComPort%, BaudRate%)
  SELECT CASE BaudRate%    ' max baud via BIOS INT 14H is 9600 BPS
         CASE 9600
              BaudVal% = 128 + 64 + 32  ' (224) = binary 11100000
         CASE 2400
              BaudVal% = 128 + 32       ' (160) = binary 10100000
         CASE 1200
              BaudVal% = 128            ' (128) = binary 10000000
         CASE 300
              BaudVal% = 64             ' (64)  = binary 01000000
  END SELECT
  ComParms% = BaudVal% + Parity% + StopBits% + DataBits%
  PortFunc% = 0 ' Function 0 (of Int 14H) which is init port
  Registers.AX = ComParms% + (256 * PortFunc%)
  Registers.DX = ComPort%  ' active com port to init (zero-based)
  CALL INTERRUPT(&H14, Registers, Registers) ' call the interrupt
END SUB

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Sends one byte to the specified serial port (zero based ComPort) via -
REM  Function 1 of BIOS INT 14H
REM  Called with: AH = 1
REM               AL = ASCII value of the byte to send
REM               DX = Serial port 0 to 3 (zero based; use 0 for port 1...)
REM  Returns:  AH = Line Status
REM            AL = unchanged (the byte that was sent)
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB Send1Byte (Byte$)
    PortFunc% = 1      ' INT 14H Function 1 = write char to port goes into AH
    Byte% = ASC(Byte$) ' ASCII value of byte which goes into AL
    Registers.AX = Byte% + (256 * PortFunc%)
    Registers.DX = ComPort%  ' com port number (zero-based)
    CALL INTERRUPT(&H14, Registers, Registers)
END SUB

REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
REM  Sends a string to the active com port one byte at a time using
REM  INT 14H's Send Byte function (1).
REM  Called with:  Text$ which is the string to send out the com port.
REM  Example:  CALL SendStr(Text$)
REM
REM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SUB SendStr (Text$)
    FOR I% = 1 TO LEN(Text$)
        OneByte$ = MID$(Text$, I%, 1)
        CALL Send1Byte(OneByte$)
    NEXT I%
END SUB
' --------- CUT HERE -------------------- CUT HERE ---------------------------
