C - check memory size
D - boot from floppy disk
E - examine memory
H - boot from hard disk (ie: CDS)
M - modify memory
W - boot from winchester disk
It seems there may be a way to get UniBug to call your own typed-in programs after all. The key to this is that it executes each of its commands as a subroutine; the return-address is pushed onto the stack, and re-loaded when the routine ends. So, the trick is to use the 'M' command to modify that return address on the stack. As soon as that 'M' command is finished, control will pass to that address - your program!
The stack pointer is initialized to $BF40 before calling any command subroutines. Of course, UniBug's 'M' routine accepts physical addresses, so also you need to know that physical page 01 was mapped into the logical memory page at $Bxxx, so logical address $BF40 is really the same as physical address $01F40. So, the return address that we need to modify should be at physical addresses $01F3E and $01F3F. As a sanity check, if your UniBug is the same version as mine, that return address on the stack before you modify it should be $F935. This is probably also the address to which your program should jump when it is finished.
In case you have a different version of UniBug, the trick is to find the address that the stack pointer is initialized to, and also the "JSR [1,x]" instruction that calls command subroutines. The stack pointer has to be initialized soon after a reset, so you can start disassembling at the reset vector. In my UniBug, this LDS is the seventh instruction executed after a reset, and it lives at address $f808. The machine code for that JSR is three bytes: $ad $98 $01 . A quick search of my UniBug shows that these bytes occur in this order only in this one place - at address $f932. I would expect most versions of UniBug to have this, although it might move around a little bit. Note that the address to look for on the stack as a sanity check will be the address of the instruction after the JSR.
There is also the little matter of where to put your program. UniBug does not map RAM into all logical addresses, so you need to know which pages it has mapped in. Since it needs a stack at page $Bxxx, we can use that. Of course, you might want to be careful not to run over UniBug's stack, and your program is free to map in more pages if it needs them.
As an example, here is a little program that I entered and ran. Everything on the right (in italics, browser-willing) is my comment, and should not be typed-in.
>M 01000 this page is mapped in at $B000 - 01000 FF 8e ldx #$b010 - 01001 FF b0 - 01002 FF 10 - 01003 FF bd jsr $bdfd - output string - 01004 FF fd - 01005 FF e8 - 01006 FF 7e jmp $f935 - the original return address - 01007 FF f9 - 01008 FF 35 - 01009 FF (hit return here) >M 01010 this is logical address $B010, remember - 01010 FF 0d the string to display: - 01011 FF 0d CR,CR,"Hi!",CR,CR - 01012 FF 48 - 01013 FF 69 - 01014 FF 21 - 01015 FF 0d - 01016 FF 0d - 01017 FF 03 - 01018 FF >M 01f3e modify the return address on the stack - 01f3e F9 b0 was: $f935 - 01f3f 35 00 is now: $b000 - 01f40 FF when I hit return here, it returns to MY program! Hi! Muahahaha!