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