!PR1
The Real Story about DOS and BRUN.........Bob Sander-Cederlof

I was wrong.  Some of you were kind enough to point it out.  John Butterill sent a letter, and others called (sorry, names forgotten).  I said, in the January 1986 AAL, that the reason BRUNning programs from inside Applesoft programs often did not work was the fact that DOS used a JMP rather than a JSR to call your program.

The truth is that DOS does call your program with a JMP, but there is still a return address on the stack.  The BRUN command processor itself was called with a JSR, in a way.  At $A17A there is a JSR $A180.  The routine at $A180 jumps to the BRUN processor.  So when your program finishes it will return to $A17D, right after the JSR $A180.  From there it goes to $9F83.

At $9F83, DOS will finally exit from doing the BRUN command.  If MON C is on, the carriage return from the end of the BRUN command will be echoed at this time.  This can put you into a loop, however, because the BRUN command re-installed the DOS hooks in the input and output vectors.  When the DOS hooks are installed, any character input or output will enter DOS first.  Since we are still, in effect, inside DOS, because of the BRUN, we get into a loop.  DOS is not re-entrant, as John Butterill put it.  The BRUN command processor does a JSR $A851, which re-installs the DOS hooks.  If your program tries to do any character I/O through calls to $FDED (COUT) or $FD0C (RDKEY), and you start up your program by BRUNning it from inside an Applesoft program, you will get DOS into a loop.  Or, even if your program does not do any I/O, if MONC is on DOS can still get into a loop.

I still think the easiest way to avoid this problem is to avoid using BRUN inside Applesoft programs.  Use BLOAD and CALL instead.  But sometimes you may want to use BRUN, because you do not know in advance where the CALL address would be.  One way to allow I/O inside your own program even though it is to be BRUN from inside an Applesoft program is to disconnect or bypass the hooks.  You could output characters by JSR $FDF0, for example.  But that would always go to the screen, and you may have a printer or an 80-column card or a modem hooked in, so that isn't a real solution.  Another way is to dis-install the DOS hooks, by doing a JSR $9EE0 or the equivalent.  The code at $9EE0 does this:

               LDX #3
       .1      LDA $AA53,X
               STA $36,X
               DEX
               BPL .1
               RTS

This unhooks DOS, but leaves any other I/O devices you have connected hooked in.  After doing this step, your program can freely call COUT or RDKEY without DOS even knowing about it.  You might also want to store a zero at $AA5E, to turn off MONC.  Your program can terminate then by a JMP $3EA, which will restore the DOS hooks.

An alternative that seems to work is to save and restore the location where DOS saves the entering stack pointer.  This is the culprit which causes the crippling loop.  At $9FB6, just before returning to whoever entered DOS, the stack pointer gets reset to the value it had when DOS was entered.  If you enter DOS while you are still in DOS, the first value is replaced with the second.  Then the final return point is lost, and it is loop-city.  Your program can save and restore $AA59, where the stack pointer is kept:

       YOUR.PROGRAM
               LDA $AA59       save DOS stack pointer
               PHA
               LDA #0          turn off MON C
               STA $AA5E

       ...do all your stuff, including I/O

               PLA
               STA $AA59
               RTS

This method has the advantage that your program can issue its own DOS commands by printing them, the way you would from Applesoft.  For example, the following program will work when BRUN from inside Applesoft.
!lm+5

       .OR $1000
       .TF B.SHOW OFF
DEMONSTRATE
       LDA $AA59
       PHA
       LDY #0          issue DOS CATALOG command
.1     LDA MSG,Y
       JSR $FDED
       INY
       CPY #MSGSZ
       BCC .1
       LDA #0
       STA $AA5E    "NOMON C"
       PLA
       STA $AA59
       RTS
MSG    .HS 8D.84
       .AS -/CATALOG/
       .HS 8D
MSGSZ  .EQ *-MSG

100 PRINT CHR$(4)"MONC"
110 PRINT CHR$(4)"BRUN B.SHOW OFF"
120 PRINT "FINISHED"

!lm-5

However, that program will not work correctly if you just type "BRUN B.SHOW OFF" from the command mode.  You will get a syntax error after the catalog displays, because the catalog command is left in the input buffer incorrectly.  Oh well!
