📄 proj5_2.asm
字号:
; or west, if possible.
IsNS: mov ch, East ;See if we can move East
call CanMove
jnc NotEast
mov ch, West ;See if we can move West
call CanMove
jnc DoEast
call RandNum ;Get a random direction
and ax, 1b ;Make it East or West
or al, 10b
ret
DoEast: mov ax, East
ret
DoWest: mov ax, West
ret
NotEast: mov ch, West
call CanMove
jc DoWest
; Gee, we can't switch to a perpendicular direction, see if we can
; turn around.
TryReverse: mov ch, cl
xor ch, 1
call CanMove
jc ReverseDir
; If we can't turn around (likely), then keep going in the same direction.
mov ah, 0
mov al, cl ;Stay in same direction.
ret
; Otherwise reverse direction down here.
ReverseDir: mov ah, 0
mov al, cl
xor al, 1
ret
SetDir endp
; Stuck- This function checks to see if a demon is stuck and cannot
; move in any direction. It returns true if the demon is
; stuck and needs to be killed.
Stuck proc near
mov ch, North
call CanMove
jc NotStuck
mov ch, South
call CanMove
jc NotStuck
mov ch, East
call CanMove
jc NotStuck
mov ch, West
call CanMove
NotStuck: ret
Stuck endp
; NextDemon- Searches through the demon list to find the next available
; active demon. Return a pointer to this guy in es:di.
NextDemon proc near
push ax
NDLoop: inc DemonIndex ;Move on to next demon,
and DemonIndex, ModDemons ; MOD MaxDemons.
mov al, size pcb ;Compute index into
mul DemonIndex ; DemonList.
mov di, ax ;See if the demon at this
add di, offset DemonList ; offset is active.
cmp byp [di].pcb.NextProc, 0
je NDLoop
mov ax, ds
mov es, ax
pop ax
ret
NextDemon endp
; Dig- This is the demon process.
; It moves the demon one position (if possible) in its current
; direction. After moving one position forward, there is
; a 25% chance that this guy will change its direction; there
; is a 25% chance this demon will spawn a child process to
; dig off in a perpendicular direction.
Dig proc near
; See if the current demon is stuck. If the demon is stuck, then we've
; go to remove it from the demon list. If it is not stuck, then have it
; continue digging. If it is stuck and this is the last active demon,
; then return control to the main program.
call Stuck
jc NotStuck
; Okay, kill the current demon.
; Note: this will never kill the last demon because we have the timer
; process running. The timer process is the one that always stops
; the program.
dec DemonCnt
; Since the count is not zero, there must be more demons in the demon
; list. Free the stack space associated with the current demon and
; then search out the next active demon and have at it.
MoreDemons: mov al, size pcb
mul DemonIndex
mov bx, ax
; Free the stack space associated with this process. Note this code is
; naughty. It assumes the stack is allocated with the Standard Library
; malloc routine that always produces a base address of 8.
mov es, DemonList[bx].regss
mov di, 8 ;Cheating!
free
; Mark the demon entry for this guy as unused.
mov byp DemonList[bx].NextProc, 0 ;Mark as unused.
; Okay, locate the next active demon in the list.
FndNxtDmn: call NextDemon
cocall ;Never returns
; If the demon is not stuck, then continue digging away.
NotStuck: mov ch, cl
call CanMove
jnc DontMove
; If we can move, then adjust the demon's coordinates appropriately:
cmp cl, South
jb MoveNorth
je MoveSouth
cmp cl, East
jne MoveWest
; Moving East:
inc dl
jmp MoveDone
MoveWest: dec dl
jmp MoveDone
MoveNorth: dec dh
jmp MoveDone
MoveSouth: inc dh
; Okay, store a NoWall value at this entry in the maze and output a NoWall
; character to the screen (if writing data to the screen).
MoveDone: MazeAdrs
mov bx, ax
mov Maze[bx], NoWall
ifdef ToScreen
ScrnAdrs
mov bx, ax
push es
mov ax, ScreenSeg
mov es, ax
mov word ptr es:[bx], NoWallChar
pop es
endif
; Before leaving, see if this demon shouldn't change direction.
DontMove: call RandNum
and al, 11b ;25% chance result is zero.
jne NoChangeDir
call SetDir
mov cl, al
NoChangeDir:
; Also, see if this demon should spawn a child process
call RandNum
and al, 11b ;Give it a 25% chance.
jne NoSpawn
; Okay, see if it's possible to spawn a new process at this point:
call CanStart
jnc NoSpawn
; See if we've already got MaxDemons active:
cmp DemonCnt, MaxDemons
jae NoSpawn
inc DemonCnt ;Add another demon.
; Okay, create a new demon and add him to the list.
push dx ;Save cur demon info.
push cx
; Locate a free slot for this demon
lea si, DemonList- size pcb
FindSlot: add si, size pcb
cmp byp [si].pcb.NextProc, 0
jne FindSlot
; Allocate some stack space for the new demon.
mov cx, 256 ;256 byte stack.
malloc
; Set up the stack pointer for this guy:
add di, 248 ;Point stack at end.
mov [si].pcb.regss, es
mov [si].pcb.regsp, di
; Set up the execution address for this guy:
mov [si].pcb.regcs, cs
mov [si].pcb.regip, offset Dig
; Initial coordinates and direction for this guy:
mov [si].pcb.regdx, dx
; Select a direction for this guy.
pop cx ;Retrieve direction.
push cx
call SetDir
mov ah, 0
mov [si].pcb.regcx, ax
; Set up other misc junk:
mov [si].pcb.regds, seg dseg
sti
pushf
pop [si].pcb.regflags
mov byp [si].pcb.NextProc, 1 ;Mark active.
; Restore current process' parameters
pop cx ;Restore current demon.
pop dx
NoSpawn:
; Okay, with all of the above done, it's time to pass control on to a new
; digger. The following cocall passes control to the next digger in the
; DemonList.
GetNextDmn: call NextDemon
; Okay, we've got a pointer to the next demon in the list (might be the
; same demon if there's only one), pass control to that demon.
cocall
jmp Dig
Dig endp
; TimerDemon- This demon introduces a 1/18th second delay between
; each cycle in the demon list. This slows down the
; maze generation so you can see the maze being built
; (which makes the program more interesting to watch).
TimerDemon proc near
push es
push ax
mov ax, 40h ;BIOS variable area
mov es, ax
mov ax, es:[6Ch] ;BIOS timer location
Wait4Change: cmp ax, es:[6Ch] ;BIOS changes this every
je Wait4Change ; 1/18th second.
cmp DemonCnt, 1
je QuitProgram
pop es
pop ax
call NextDemon
cocall
jmp TimerDemon
QuitProgram: cocall MainPCB ;Quit the program
TimerDemon endp
; What good is a maze generator program if it cannot solve the mazes it
; creates? SolveMaze finds the solution (if any) for this maze. It marks
; the solution path and the paths it tried, but failed on.
;
; function solvemaze(x,y:integer):boolean
sm_X textequ <[bp+6]>
sm_Y textequ <[bp+4]>
SolveMaze proc near
push bp
mov bp, sp
; See if we've just solved the maze:
cmp byte ptr sm_X, EndX
jne NotSolved
cmp byte ptr sm_Y, EndY
jne NotSolved
mov ax, 1 ;Return true.
pop bp
ret 4
; See if moving to this spot was an illegal move. There will be
; a NoWall value at this cell in the maze if the move is legal.
NotSolved: mov dl, sm_X
mov dh, sm_Y
MazeAdrs
mov bx, ax
cmp Maze[bx], NoWall
je MoveOK
mov ax, 0 ;Return failure
pop bp
ret 4
; Well, it is possible to move to this point, so place an appropriate
; value on the screen and keep searching for the solution.
MoveOK: mov Maze[bx], Visited
ifdef ToScreen
push es ;Write a "VisitChar"
ScrnAdrs ; character to the
mov bx, ax ; screen at this X,Y
mov ax, ScreenSeg ; position.
mov es, ax
mov word ptr es:[bx], VisitChar
pop es
endif
; Recusively call SolveMaze until we get a solution. Just call SolveMaze
; for the four possible directions (up, down, left, right) we could go.
; Since we've left "Visited" values in the Maze, we will not accidentally
; search back through the path we've already travelled. Furthermore, if
; we cannot go in one of the four directions, SolveMaze will catch this
; immediately upon entry (see the code at the start of this routine).
mov ax, sm_X ;Try the path at location
dec ax ; (X-1, Y)
push ax
push sm_Y
call SolveMaze
test ax, ax ;Solution?
jne Solved
push sm_X ;Try the path at location
mov ax, sm_Y ; (X, Y-1)
dec ax
push ax
call SolveMaze
test ax, ax ;Solution?
jne Solved
mov ax, sm_X ;Try the path at location
inc ax ; (X+1, Y)
push ax
push sm_Y
call SolveMaze
test ax, ax ;Solution?
jne Solved
push sm_X ;Try the path at location
mov ax, sm_Y ; (X, Y+1)
inc ax
push ax
call SolveMaze
test ax, ax ;Solution?
jne Solved
pop bp
ret 4
Solved:
ifdef ToScreen ;Draw return path.
push es
mov dl, sm_X
mov dh, sm_Y
ScrnAdrs
mov bx, ax
mov ax, ScreenSeg
mov es, ax
mov word ptr es:[bx], PathChar
pop es
mov ax, 1 ;Return true
endif
pop bp
ret 4
SolveMaze endp
; Here's the main program that drives the whole thing:
Main proc
mov ax, dseg
mov ds, ax
mov es, ax
meminit
call Init ;Initialize maze stuff.
lesi MainPCB ;Initialize coroutine
coinit ; package.
; Create the first demon.
; Set up the stack pointer for this guy:
mov cx, 256
malloc
add di, 248
mov DemonList.regsp, di
mov DemonList.regss, es
; Set up the execution address for this guy:
mov DemonList.regcs, cs
mov DemonList.regip, offset Dig
; Initial coordinates and direction for this guy:
mov cx, East ;Start off going east.
mov dh, StartY
mov dl, StartX
mov DemonList.regcx, cx
mov DemonList.regdx, dx
; Set up other misc junk:
mov DemonList.regds, seg dseg
sti
pushf
pop DemonList.regflags
mov byp DemonList.NextProc, 1 ;Demon is "active".
inc DemonCnt
mov DemonIndex, 0
; Set up the Timer demon:
mov DemonList.regsp+(size pcb), offset EndTimerStk
mov DemonList.regss+(size pcb), ss
; Set up the execution address for this guy:
mov DemonList.regcs+(size pcb), cs
mov DemonList.regip+(size pcb), offset TimerDemon
; Set up other misc junk:
mov DemonList.regds+(size pcb), seg dseg
sti
pushf
pop DemonList.regflags+(size pcb)
mov byp DemonList.NextProc+(size pcb), 1
inc DemonCnt
; Start the ball rolling.
mov ax, ds
mov es, ax
lea di, DemonList
cocall
; Wait for the user to press a key before solving the maze:
getc
mov ax, StartX
push ax
mov ax, StartY
push ax
call SolveMaze
; Wait for another keystroke before quitting:
getc
mov ax, 3 ;Clear screen and reset video mode.
int 10h
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
; Stack for the timer demon we create (we'll allocate the other
; stacks dynamically).
TimerStk byte 256 dup (?)
EndTimerStk word ?
; Main program's stack:
stk byte 512 dup (?)
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes db 16 dup (?)
zzzzzzseg ends
end Main
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -