|
Table of Content | |
| 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
|
Table of Content | |
Chapter Ten: Control Structures (Part
6)
27 SEP 1996