Tuesday, August 6, 2024

Miscellaneous comments on porting 1130 Fortran to Assembler

CHARACTER CODES IN FORTRAN VERSUS NATIVE

The IBM 1130 has disparate encodings for characters depending on the peripheral from which it comes or is written to. This eliminated the need for the device controller to convert from the native format of the device into some common lingua franca but it imposed a requirement on programmers to convert back and forth between the encodings. On the IBM 360, by comparison, each controller converts to EBCDIC thus the programmer is unconcerned with native device formats. 

In Fortran, however, the programmer only deals with EBCDIC. This means that internal to Fortran run time software, every device's data is converted to and from EBCDIC. Thus, when the user pushes the Space bar on the keyboard, delivering x0000 as the value, the logic converts it to x4000 an EBCDIC space. When the user wants to print a space on the typewriter they use x4000 but it is converted to x2100 for transmission to the 1053 printer. 

The consequence of this when porting is that you will find statements like:

                              IF (INP - IE)3,4,5    

This will check the input against a constant IE which will be the EBCDIC value. When coding in Assembler, the keyboard provides Hollerith codes, so the value of IE must be converted to the Hollerith value. Again we need to convert when the code types out a character with a statement like

                            WRITE (TYP,1)IE

                        1  FORMAT(A1)

This will grab the word storing variable IE and send it to the typewriter but the Format string takes the upper 8 bits as an EBCDIC value and converts to the 1053 code before sending to the typewriter. My code has to take the value of the character for IE, but in the typewriter encoding, then put that in the first 8 bits of a word and send it to the typewriter. 

There were also variables loaded with values such as:

                            IBLANK = 16448

                            IUL = 27968

                           IBAR = 20288

                           IX =-6336

These are 16 bit signed integer values that produce a word with two EBCDIC character codes. In this case, the lower eight bits is always 40 (a space) and all we care is about the upper eight bits. The four words above become x4040, x6D40, x4F40 and xE740 respectively. We turn this into x2100, xBE00, xC600 and xE700 respectively to drive the typewriter. 

CONVERTING MATH FORMULAS TO 1130 ARITHMETIC INSTRUCTIONS

The statements in 1130 Fortran are represented in decimal and cover both floating point and integer data types. The 1130 hardware does not understand floating point at all, only integer. Even for programs like TicTacToe that use only integer arithmetic, one has to consider the intent of the Fortran statement and at the same time the 1130 hardware capabilities. 

A statement like this in Fortran seems cumbersome:

                          J = I - (I/2)*2

Integer division drops any fractional part. Thus when the value 57 is divided by 2 in integer, the answer is 28 not 28.5 as would be true in a floating point calculation. When the 28 is multiplied by 2, the result is now 56. The statement above subtracts 56 from 57. If the value were 30, dividing by 2 gives 15, multiply by 2 gives 30, so that 30-30 produces 0. In other words, this expression produces a 0 if I is even and 1 if I is odd. 

On the IBM 1130, the arithmetic unit has conditions that you can test in a branch instruction, one of which is EVEN. Therefore no multiplying, dividing or subtracting is needed to check whether a number is even or odd. 

OLD STYLE FORTRAN SYNTAX UNFAMILIAR TO THOSE THAT KNOW MODERN FORTRAN

IBM 1130 Fortran expresses conditional statements (IF) with this syntax:

                        IF (value)1,2,3

This means that if the value is negative, this branches to statement 1. If the value is zero, it branches to 2. When positive, it branches to 3. One does not have statements such as IF (condition) 4 to branch to statement 4 when the condition is true, otherwise just fall through to the next statement in the program. 

2 comments:

  1. just for fun I coded up your 7-11 game in Python. I imagine the structure matches the shape of your assembly version.
    '''
    7-11 game, as described by Carl Claunch who made it for the IBM 1130 demo set
    '''

    import random as R

    STAKE = 100 # player's stake, start with $100
    TOP_STAKE = 5000 # stake at which the game ends with a win
    BUSTED = 0 # I don't want to allow negative stakes
    MAX_BET = 1000 # largest bet (arbitrary)
    MIN_BET = 1

    def roll() -> tuple[int,int] :
    return (R.randint(1,6),R.randint(1,6))

    def get_bet() -> int:
    global STAKE, MAX_BET, MIN_BET
    '''
    loop until valid bet received
    Prompt for bet amount 1..max
    validate and break if valid
    return 0 to mean end
    return valid bet
    '''
    upper_bet = min(MAX_BET,STAKE)
    while True:
    print()
    inp = input(f"Your wager, from ${MIN_BET} to ${upper_bet} (0 to end) $")
    inp = int(inp) if inp.isdecimal() else -1 # reject null or nondigit
    if (inp == 0) or (inp >= MIN_BET and inp <= upper_bet):
    return inp
    print("Sorry, not a valid bet.")
    return bet

    def round(bet: int) -> int :
    '''
    One round of betting, returning +bet on a win, -bet on a loss
    - do the first roll
    - end round with loss if 2 or 12
    - end round with win if 7 or 11
    - note the point
    - loop until some conclusion
    - perform a roll on demand
    - report roll
    - end with win if == point
    - end with loss if 7
    '''
    def roll_and_show() -> int :
    '''
    roll two dies, display, and return their sum
    '''
    the_dice = roll()
    the_roll = sum(the_dice)
    print(f"And the dice come up {the_dice[0]} and {the_dice[1]} for {the_roll}")
    return the_roll
    '''
    First roll
    '''
    point = roll_and_show()
    if point == 2 or point == 12 :
    print("Sorry.")
    return -bet
    if point == 7 or point == 11 :
    print("Nice one!")
    return bet
    while True:
    print(f"Your point is {point}")
    print()
    the_roll = roll_and_show()
    if the_roll == point :
    print("Great!")
    return bet
    if the_roll == 7:
    print("Oh, too bad.")
    return -bet

    def game():
    global STAKE, TOP_STAKE
    '''
    Run the game
    repeat until an end is reached:
    report current stake
    if stake < 0 report bust, end
    if stake > TOP_STAKE, report win, end
    get the bet
    if bet == 0, thanks byeee
    run a round, returning +/- bet
    increment the stake by bet
    '''
    while True:
    print(f"Your present stake: ${STAKE}")
    if STAKE <= BUSTED :
    print("Sorry, you're busted.")
    return
    if STAKE > TOP_STAKE :
    print("And that's a win, cash out and go have a drink!")
    return
    bet = get_bet()
    if bet == 0 : # user wants to quit
    return
    STAKE += round(bet)

    if __name__ == "__main__" :
    game()

    ReplyDelete
  2. It required 350 lines of assembler, but also leveraged routines in the demo monitor which together comprise about another 375 lines. Sadly no Python back in the day.

    ReplyDelete