Manually changing SP to prevent stack overflow

I'm still working on my game project (a text-based dungeon crawl in the tradition of several DOS-based BBS door games), although it's coming along further. I ran into a design problem, though, and I'd like some input on my stop-gap solution.

With my current approach the stack will eventually overflow if the user dies and either starts a new game or loads from a previous save several times in a single program execution. My tentative fix is to save SP right before the main loop that calls all other branching functions, and in the event the player restarts I restore that SP value and jump back to the beginning of that main loop. Here are some code snippets, TASM syntax:

[code] public SP_STATE, GAME_RESET
SP_STATE dw 0 ;Stack Pointer state stored for game resetting
; to keep stack from overflowing.

GAME_RESET dw DrawMenu ;Offset of DrawMenu for resetting game.

public TextDungeon
extrn ReadByte:proc, ClearScreen:proc, WriteString:proc
extrn SendCRLF:proc, GotoXY:proc, WriteChar:proc
extrn RandomMonster:proc, ResetChar:proc

TextDungeon proc
push ax
push bx
push dx
mov [SP_STATE],sp ;Save SP to SP_STATE for later reset

call ClearScreen
xor dx,dx
call GotoXY ;Put cursor at 0,0 (top left)
mov dx,[CurrentMenu]
call WriteString ;Draw menu and prompt
lea dx,[MenuPrompt]
call WriteString

And here is the current SP resetting procedure:

public RenewGame
extrn ClearScreen:proc, ChangeName:proc, TextDungeon:proc
extrn ResetChar:proc
extrn SP_STATE:word, GAME_RESET:word

RenewGame proc
call ClearScreen
call ResetChar
call ChangeName
mov ax,[GAME_RESET]
mov sp,[SP_STATE]
jmp ax ;Jump to TextDungeon DrawMenu label.

mov ax,4C99h ;Should never be reached but gives error
int 21h ; and exits if it happens...

RenewGame endp[/code]

I've read repeatedly that SP should never be modified directly unless you know exactly what you're doing, given that a system interrupt can occur in the middle of the process and bring the whole thing to an ugly, grinding halt. I've done my best to ensure that the IP change happens immediately after the SP change, which seems to me like it should minimize the chance of a problem down to the smallest it can get.

Is this a good way to do things or should I suck it up and fix my short-sighted design?


  • Where you need to worry about interruptions is when you are in the middle of doing something non-atomic, like an entire sub-procedure, or certain "complicated" CPU instructions like BTR. The CLI/STI combination or LOCK prefix will usually (but not always) handle those situations.

    A simple MOV SP,xx is atomic, so there is no possibility of an interruption in the middle of it. The only thing you really need to worry about is that in YOUR program, there is nothing left on the old stack that you may need to "copy" to the new stack. The amount of time between changing SP and changing CS (or even which one you do first) really doesn't matter, either.

    The only thing I would add is that if you are changing the entire stack (both SS and SP at the same time), the CPU will automatically treat the combination of a MOV SS,xx followed immediately by a MOV SP,xx as atomic. You must be absolutely sure, though, that the MOV SS,xx is first and the MOV SP,xx follows IMMEDIATELY with no other instructions (not even a NOP) between them.
  • [color=Blue]The better question would be why stack overflows? I wrote a lot of ASM code for PCs since 86 (and there was some big production programs), and there is always a way to allocate memory instead of putting it all on stack. Also, there are ways to solve problems without recursion.[/color]
  • The recursion is actually a design oversight on my part. I don't currently have a way of returning back out of all the nested procedure calls when the player dies and is prompted to either start a new game or load a previous save. As such the stack would keep hanging on to all the pushed data from each previous play through, and eventually (granted it could take a while) the stack would overflow. I haven't actually tested it out since I'm currently writing the save/load routines, but I know it's an eventuality.

    All I'm doing is trying to dump the old stack data that's no longer needed *without* having to add all the code necessary to return all the way back up. It's a kludge, I know, but I'm trying to avoid a lot of rewriting or a complete redesign before I've even finished the first complete working draft.
  • Thanks for the information :) That's what I was hoping. So as long as nothing ugly and complicated is in danger of being interrupted it shouldn't be a serious problem.

    Fortunately I don't need to change the stack segment in this program.
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!