'Example of using the word functions that are in the
'WORD.BAS file to manipulate the I/O ports.
'
'By:  George Spafford Copyright 1993
'
'No additional libraries required
'
'v1.0  05/02/93
'
'**************************************************

'If you use these routines, please have the courtesy
'of registering the tutorial.

'Make sure that you have all of your variables typed
'correctly before you use one of the following
'routines.  Use integers where integers are identified (%).
'Use long integers where long integers are identified (&).

'Int2Long& is a function that accepts a signed
'integer and returns an unsigned long integer. Since
'BASIC uses the high bit to toggle the sign of an
'integer, you must convert it to a long integer before
'you perform any bit operations on it.

DECLARE FUNCTION Int2Long& (IntNumber%)

'BASIC does NOT have a built in facility to PEEK
'words!  Tell the PeekWord% function where to look,
'and it will read in a word at that segment and offset.

DECLARE FUNCTION PeekWord% (Segment%, Offset%)

'As with Peeks, BASIC does not support a POKE function
'for words.  This next subroutine adds that capability.
'Tell the subroutine the segment, offset and word that
'you wish it to POKE in and it will do so.

DECLARE SUB PokeWord (Segment%, Offset%, Word%)

'As you code more and more programs, try to make your code
'as modular as possible.  By creating objects (OOP systems),
'you can significantly increase your code reusability.
'Try to only create the wheel once!  Some of the demos would
'benefit greatly by judicial use of subroutines.  In order to
'have top-down readability, I did not modularize the code.
'Just a FYI   -- George

DEFINT A-Z
Title$ = "S3 Demo of WORD functions and I/O ports           Copyright George Spafford 1993"

Main:
    CLS
    PRINT Title$
    LOCATE 3, 30: PRINT "To enter hexadecimal numbers, prefix your answer"
    LOCATE 4, 30: PRINT "with &H.  For example, &H400 represents 400 Hex."
    LOCATE 3, 1
    GOSUB ReadPorts
    PRINT "Menu     Port   Address"
    PRINT "----     ----   -------"
    PRINT " 1.      COM1:  "; HEX$(COM1); " Hex"    'HEX$ formats numeric
    PRINT " 2.      COM2:  "; HEX$(COM2)            'input to a hexadecimal
    PRINT " 3.      COM3:  "; HEX$(COM3)            'string representation
    PRINT " 4.      COM4:  "; HEX$(COM4)
    PRINT " 5.      LPT1:  "; HEX$(LPT1)
    PRINT " 6.      LPT2:  "; HEX$(LPT2)
    PRINT " 7.      LPT3:  "; HEX$(LPT3)
    PRINT " 8.      LPT4:  "; HEX$(LPT4)
    PRINT "======================="
    PRINT
    PRINT "Please select an option:"
    PRINT
    INPUT "(D)elete   (E)dit   (S)wap   (Q)uit:  ", Ans$
    Ans$ = LTRIM$(RTRIM$(UCASE$(Ans$)))             'trim spaces and make the
                                                    'answer upper case.
    IF Ans$ = "" THEN PRINT : PRINT : END           'if answer is NULL then end

    'Just a note regarding efficiency when conditions are involved:
    'If you have more than a couple of repetitive tasks to test, use
    'the SELECT CASE method.  IF THEN statements require converting and
    'testing a condition each time.  A select only has to convert the test
    'variable once and then evaluate it against all of the answers.
    'Taking this approach one step further, if you can use integer varaibles,
    'then the test is even faster.  In the instance below, speed is not a
    'factor, readability is.

    SELECT CASE Ans$
           CASE "D"
                PRINT
                INPUT "Delete which port:  ", Del$
                Del$ = LTRIM$(RTRIM$(Del$))
                IF Del$ <> "" THEN
                   Value = VAL(Del$)
                   GOSUB Choice
                   IF Offset <> 0 THEN
                      CALL PokeWord(0, Offset, 0)
                   END IF
                END IF
           CASE "E"
                PRINT
                INPUT "Edit which port:  ", Edit$
                Edit$ = LTRIM$(RTRIM$(Edit$))
                IF Edit$ <> "" THEN
                   PRINT
                   Value = VAL(Edit$)
                   GOSUB Choice
                   IF Offset <> 0 THEN
                      INPUT "New value:  ", NewValue
                      CALL PokeWord(0, Offset, NewValue)
                   END IF
                END IF
           CASE "S"
                PRINT
                INPUT "Swap port one:  ", From$
                Value = VAL(From$)
                GOSUB Choice
                FromAddr = Addr
                FromOffset = Offset
                INPUT "Swap port two:  ", To$
                Value = VAL(To$)
                GOSUB Choice
                ToAddr = Addr
                ToOffset = Offset
                IF ToOffset <> 0 AND FromOffset <> 0 THEN
                   CALL PokeWord(0, ToOffset, FromAddr)
                   CALL PokeWord(0, FromOffset, ToAddr)
                END IF
           CASE "Q"
                PRINT : PRINT : END
    END SELECT
    GOTO Main
    END

Choice:
    Addr = 0
    SELECT CASE Value
           CASE 1
                Addr = COM1
                Offset = COM1off
           CASE 2
                Addr = COM2
                Offset = COM2off
           CASE 3
                Addr = COM3
                Offset = COM3off
           CASE 4
                Addr = COM4
                Offset = COM4off
           CASE 5
                Addr = LPT1
                Offset = LPT1off
           CASE 6
                Addr = LPT2
                Offset = LPT2off
           CASE 7
                Addr = LPT3
                Offset = LPT3off
           CASE 8
                Addr = LPT4
                Offset = LPT4off
           CASE ELSE
                Addr = 0
                Offset = 0
    END SELECT


ReadPorts:
    'The BIOS data area starts at 0000:0400.  The first
    '16 bytes of it concerns the I/O ports.  If the port
    'is enabled, there will be a word entry that describes
    'the I/O address where data should be sent when that device
    'is used.  If the word is 0, then the device has not been
    'recognized.  If a value is forced, then the system will
    'recognize that port whether it truly exists or not.
    'For example, some systems do not automatically scan
    'for COM3 and COM4 at POST (Power On Self-Test) time.  You
    'must enter the address manually for that port if you
    'need to use it in those instances.

    COM1off = &H400                     'Save Offset
    COM1 = PeekWord%(0, COM1off)        'Get I/O address used
    COM2off = &H402
    COM2 = PeekWord%(0, COM2off)
    COM3off = &H404
    COM3 = PeekWord%(0, COM3off)
    COM4off = &H406
    COM4 = PeekWord%(0, COM4off)
    LPT1off = &H408
    LPT1 = PeekWord%(0, LPT1off)
    LPT2off = &H40A
    LPT2 = PeekWord%(0, LPT2off)
    LPT3off = &H40C
    LPT3 = PeekWord%(0, LPT3off)
    LPT4off = &H40E
    LPT4 = PeekWord%(0, LPT4off)
    RETURN

DEFSNG A-Z
FUNCTION Int2Long& (IntNumber%)
    'If the integer is negative, we need to add 65536
    'to the number (which is 2 * integer limit) to get
    'our new long integer.

    IF IntNumber% < 0 THEN
       Int2Long& = 65536 + IntNumber%
    ELSE
       Int2Long& = IntNumber%
    END IF
END FUNCTION

FUNCTION PeekWord% (Segment%, Offset%)
    DEF SEG = Segment%              'set segment
    High = PEEK(Offset% + 1)        'High byte is at offset + 1
    Low = PEEK(Offset%)             'Low byte is at offset  
    PeekWord% = (High * 256) + Low  'Compute the word value
    DEF SEG                         'return to default segment
END FUNCTION

SUB PokeWord (Segment%, Offset%, Word%)
    DEF SEG = Segment%          'Set segment
    High = (Word% \ 256)        'perform integer division
                                'to compute the high byte
    Low = Word% - (High * 256)  'compute the low byte
    POKE (Offset% + 1), High    'POKE in the high byte
    POKE Offset%, Low           'POKE in the low byte
    DEF SEG                     'return to the default segment
END SUB

