The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Ten (Part 5)

Table of Content

Chapter Eleven  

CHAPTER TEN:
CONTROL STRUCTURES (Part 6)
10.11 - Sample Program
10.11 Sample Program

This chapter's sample program is a simple moon lander game. While the simulation isn't terribly realistic, this program does demonstrate the use and optimization of several different control structures including loops, if..then..else statements, and so on.

; Simple "Moon Lander" game.
;
; Randall Hyde
; 2/8/96
;
; This program is an example of a trivial little "moon lander"
; game that simulates a Lunar Module setting down on the Moon's
; surface.  At time T=0 the spacecraft's velocity is 1000 ft/sec
; downward, the craft has 1000 units of fuel, and the craft is
; 10,000 ft above the moon's surface.  The pilot (user) can 
; specify how much fuel to burn at each second.
;
; Note that all calculations are approximate since everything is
; done with integer arithmetic.


; Some important constants

InitialVelocity =       1000
InitialDistance =       10000
InitialFuel     =       250
MaxFuelBurn     =       25

MoonsGravity    =       5               ;Approx 5 ft/sec/sec
AccPerUnitFuel  =       -5              ;-5 ft/sec/sec for each fuel unit.


                .xlist
                include         stdlib.a
                includelib      stdlib.lib
                .list

                .386                    ;Comment out these two statements
                option  segment:use16   ; if you are not using an 80386.

dseg            segment para public 'data'

; Current distance from the Moon's Surface:

CurDist         word    InitialDistance

; Current Velocity:

CurVel          word    InitialVelocity

; Total fuel left to burn:

FuelLeft        word    InitialFuel


; Amount of Fuel to use on current burn.

Fuel            word    ?

; Distance travelled in the last second.

Dist            word    ?

dseg            ends

cseg            segment para public 'code'
                assume  cs:cseg, ds:dseg

; GETI- Reads an integer variable from the user and returns its
;       its value in the AX register.  If the user entered garbage,
;       this code will make the user re-enter the value.

geti            textequ <call _geti>
_geti           proc
                push    es
                push    di
                push    bx

; Read a string of characters from the user.
;
; Note that there are two (nested) loops here.  The outer loop
; (GetILp) repeats the getsm operation as long as the user 
; keeps entering an invalid number.  The innermost loop (ChkDigits)
; checks the individual characters in the input string to make
; sure they are all decimal digits.

GetILp:         getsm

; Check to see if this string contains any non-digit characters:
;
; while (([bx] >= '0') and ([bx] <= '9')  bx := bx + 1;
;
; Note the sneaky way of turning the while loop into a
; repeat..until loop.

                mov     bx, di          ;Pointer to start of string.
                dec     bx
ChkDigits:      inc     bx
                mov     al, es:[bx]     ;Fetch next character.
                IsDigit                 ;See if it's a decimal digit.
                je      ChkDigits       ;Repeat if it is.

                cmp     al, 0           ;At end of string?
                je      GotNumber

; Okay, we just ran into a non-digit character.  Complain and make
; the user reenter the value.

                free                    ;Free space malloc'd by getsm.
                print
                byte    cr,lf
                byte    "Illegal unsigned integer value, "
                byte    "please reenter.",cr,lf
                byte    "(no spaces, non-digit chars, etc.):",0
                jmp     GetILp


; Okay, ES:DI is pointing at something resembling a number.  Convert
; it to an integer.

GotNumber:      atoi
                free                    ;Free space malloc'd by getsm.

                pop     bx
                pop     di
                pop     es
                ret
_geti           endp





; InitGame-     Initializes global variables this game uses.

InitGame        proc
                mov     CurVel, InitialVelocity
                mov     CurDist, InitialDistance
                mov     FuelLeft, InitialFuel
                mov     Dist, 0
                ret
InitGame        endp


; DispStatus-   Displays important information for each
;               cycle of the game (a cycle is one second).

DispStatus      proc
                printf
                byte    cr,lf
                byte    "Distance from surface: %5d",cr,lf
                byte    "Current velocity:      %5d",cr,lf
                byte    "Fuel left:             %5d",cr,lf
                byte    lf
                byte    "Dist travelled in the last second: %d",cr,lf
                byte    lf,0
                dword   CurDist, CurVel, FuelLeft, Dist
                ret
DispStatus      endp


; GetFuel-      Reads an integer value representing the amount of fuel
;               to burn from the user and checks to see if this value
;               is reasonable.  A reasonable value must:
;
;               * Be an actual number (GETI handles this).
;               * Be greater than or equal to zero (no burning
;                 negative amounts of fuel, GETI handles this).
;               * Be less than MaxFuelBurn (any more than this and
;                 you have an explosion, not a burn).
;               * Be less than the fuel left in the Lunar Module.

GetFuel         proc
                push    ax

; Loop..endloop structure that reads an integer input and terminates
; if the input is reasonable.  It prints a message an repeats if
; the input is not reasonable.
;
; loop
;       get fuel;
;       if (fuel < MaxFuelBurn) then break;
;       print error message.
; endloop
;
; if (fuel > FuelLeft) then
;
;       fuel = fuelleft;
;       print appropriate message.
;
; endif

GetFuelLp:      print
                byte    "Enter amount of fuel to burn: ",0
                geti
                cmp     ax, MaxFuelBurn
                jbe     GoodFuel

                print
                byte    "The amount you've specified exceeds the "
                byte    "engine rating,", cr, lf
                byte    "please enter a smaller value",cr,lf,lf,0
                jmp     GetFuelLp

GoodFuel:       mov     Fuel, ax
                cmp     ax, FuelLeft
                jbe     HasEnough
                printf
                byte    "There are only %d units of fuel left.",cr,lf
                byte    "The Lunar module will burn this rather than %d"
                byte    cr,lf,0
                dword   FuelLeft, Fuel

                mov     ax, FuelLeft
                mov     Fuel, ax

HasEnough:      mov     ax, FuelLeft
                sub     ax, Fuel
                mov     FuelLeft, ax
                pop     ax
                ret
GetFuel         endp



; ComputeStatus-
;
;       This routine computes the new velocity and new distance based on the
;       current distance, current velocity, fuel burnt, and the moon's
;       gravity.  This routine is called for every "second" of flight time.
;
; note:
;
;       Distance Travelled = Acc*T*T/2 + Vel*T  (note: T=1, so it goes away).
;       Acc = MoonsGravity + Fuel * AccPerUnitFuel
;
;       New Velocity = Acc*T + Prev Velocity
;
;       This code should really average these values over the one second
;       time period, but the simulation is so crude anyway, there's no
;       need to really bother.

ComputeStatus   proc
                push    ax
                push    bx
                push    dx

; First, compute the acceleration value based on the fuel burnt
; during this second (Acc = Moon's Gravity + Fuel * AccPerUnitFuel).

                mov     ax, Fuel                ;Compute
                mov     dx, AccPerUnitFuel      ; Fuel*AccPerUnitFuel
                imul    dx

                add     ax, MoonsGravity        ;Add in Moon's gravity.
                mov     bx, ax                  ;Save Acc value.

; Now compute the new velocity (V=AT+V)

                add     ax, CurVel              ;Compute new velocity
                mov     CurVel, ax

; Next, compute the distance travelled (D = 1/2 * A * T^2 + VT +D)

                sar     bx, 1                   ;Acc/2
                add     ax, bx                  ;Acc/2 + V (T=1!)
                mov     Dist, ax                ;Distance Travelled.
                neg     ax
                add     CurDist, ax             ;New distance.
                
                pop     dx
                pop     bx
                pop     ax
                ret
ComputeStatus   endp


; GetYorN-      Reads a yes or no answer from the user (Y, y, N, or n).
;               Returns the character read in the al register (Y or N,
;               converted to upper case if necessary).

GetYorN         proc
                getc
                ToUpper
                cmp     al, 'Y'
                je      GotIt
                cmp     al, 'N'
                jne     GetYorN
GotIt:          ret
GetYorN         endp

                


Main            proc
                mov     ax, dseg
                mov     ds, ax
                mov     es, ax
                meminit

MoonLoop:       print
                byte    cr,lf,lf
                byte    "Welcome to the moon lander game.",cr,lf,lf
                byte    "You must manuever your craft so that you touch"
                byte    "down at less than 10 ft/sec",cr,lf
                byte    "for a soft landing.",cr,lf,lf,0

                call    InitGame

; The following loop repeats while the distance to the surface is greater
; than zero.

WhileStillUp:   mov     ax, CurDist
                cmp     ax, 0
                jle     Landed

                call    DispStatus
                call    GetFuel
                call    ComputeStatus
                jmp     WhileStillUp

Landed:         cmp     CurVel, 10
                jle     SoftLanding

                printf
                byte    "Your current velocity is %d.",cr,lf
                byte    "That was just a little too fast.  However, as a "
                byte    "consolation prize,",cr,lf
                byte    "we will name the new crater you just created "
                byte    "after you.",cr,lf,0
                dword   CurVel

                jmp     TryAgain

SoftLanding:    printf
                byte    "Congrats!  You landed the Lunar Module safely at "
                byte    "%d ft/sec.",cr,lf
                byte    "You have %d units of fuel left.",cr,lf
                byte    "Good job!",cr,lf,0
                dword   CurVel, FuelLeft

TryAgain:       print
                byte    "Do you want to try again (Y/N)? ",0
                call    GetYorN
                cmp     al, 'Y'
                je      MoonLoop

                print
                byte    cr,lf
                byte    "Thanks for playing!  Come back to the moon again sometime"
                byte    cr,lf,lf,0
        

Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack   ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       byte    16 dup (?)
zzzzzzseg       ends
                end     Main

Chapter Ten (Part 5)

Table of Content

Chapter Eleven  

Chapter Ten: Control Structures (Part 6)
27 SEP 1996