RDROYA01@ULKYVX.BITNET.UUCP (07/12/86)
* assembly source for background commands in CP/M-68k
*****************************************************************************
* UEASM.S Assembly language support routines for CP/M-68K MicroEMACS *
*****************************************************************************
* (c) 1986 Robert Royar
* This file may be used for any purposes as long as this copyright notice is
* retained. I make no warranties as to this program's fitness for any purpose.
*
* Revisions: 10-July-1986
*
* The errors with DDT and buffer lossage were corrected by storing and
* resetting the DMA and default disks on each switch to/from background.
*
* A CTRL-Y command has been added to "warm boot" the background system
* without switching to the foreground.
*
* Some near branches were re-worked to avoid short address overflow. (This
* code is getting too large.)
*
* Implementation notes:
* To make this work, you will need to patch _initexc in the CPM.SYS so that
* the bdos does not reset traps #0 & #1 on warm boot, or you will need to
* reset these traps each time through the wboot procedure in this source.
* You will need to find a few addresses on your system that I have equated
* below. To give you an idea, my system begins at $400 and ends at $8c00.
* The TPA continues from there up. Most systems are located at the top of
* memory. You can use CPMLIB (a relocated version of it examined in DDT) to
* find offsets for _main (ccpmain in this code), defdma, ssp, and _log_dsk.
* If you are going to patch _initexc to work the way DRI says it should (i.e.
* will not init traps #0 & #1), then you will need to list it or trace through
* it. Somewhere in there (offset = $4738) is a "moveq.l #$20,d1" change this
* to "moveq.l #$22,d1", and each time the bdos warm boots it will skip the two
* RSX trap vectors on initialization. _initexc is at offset $4718 from the
* beginning of the CPM.REL file. BTW _initexc is called immediately before
* a call to bios "warm boot."
*
* Synopsis:
* A technique for implementing sys() calls from inside a C program, similar
* to the <ESC>$ command in GNU_VAX_EMACS.
* 1) call initv() once at start of program.
* 2) store highest memory value to use later.
* 3) call through trap #1 (_ccp routine).
* 4) reset trap #3 handler to capture warm boot, connin, conout, and
* set DMA functions.
* 5) on exit from background programs, loop to ccp (background).
* 6) on ^Y exit from background prog with a BDOS reset and bra to ccp.
* 7) on ^C reset tpa, trap #3, and return to suspended program.
* 8) on <ESC> reset tpa, trap #3, suspend background prog and return to
* foreground program.
*
* Comment field conventions
* * signifies code that runs in U mode
* ** signifies code that runs in S mode
* *** signifies S code with interrupts disabled
* * *** signifies U code with interrupts disabled
* External references:
* append - a bios routine to send bytes to my I/O server,
* not likely necessary on most systems.
* extract- a bios routine to get bytes from my I/O server,
* not likely necessary on most systems.
* _outchar-a 'C' routine to negotiate allocation with
* MicroEmacs.
*
ssp: equ $5a62 * address of SSP at pload
* Use DDT to find this
ccpmain: equ $234a * cpmlib _main on HSC board (not _ccp)
* Offset = ($234a - $400) Absolute address = $BDOS + OFFSET
log_dsk: equ $5abe * address of log_dsk tbl on HSC system
defdma: equ $66e4 * bios address of current dma pointer
* Discover this by examining the code for bios function 12.
* Use this funtion for all bios I/O (AUX:, LST:, CON:) to avoid any
* infinite loops when calling the MicroEmacs insert routines while in
* background. Example ttgetc() becomes bios(3); ttputc(c) becomes
* bios(4,(long)c)
.globl _bios
_bios: move.w 4(sp),d0 * argv[0]
move.l 6(sp),d1 * argv[1]
move.l 10(sp),d2 * argv[2]
trap #0 * this program knows where the real
rts * bios trap vector is
* Call this as soon as the program begins functioning, before calling
* any CON:, AUX:, or LST: I/O.
.globl _initv
_initv: move.l $8c,$80 * set up trap #0 from trap #3
move.l #_ccp,$84 * set up trap #1
move.w #$0,tpab * request current values
jsr settpa
move.w #0,inbg * init background flag
move.w #0,f_tim * init first time flag
move.l high,hmem * save original high memory
move.w #62,d0 * set supervisor state to get SSP value
trap #2 **
move.l #sstack,a0 ** address for new system stack
move.l #ssp,a1 ** default SSP
* This routine may not be necessary because in most cases the SSP at
* load is the same as the default value.
setstk: move.l -(a1),-(a0) ** copy current SSP contents to bg_ssp
cmpa.l a7,a1
bhi setstk ** bra if a1 is higher than a7 (never?)
move.l a0,_ssp ** default stack pointer for background
move.w #0,sr ** back to user state
rts *
* access to ccp through trap #1
_ccp: movem.l d0-d7/a1-a7,regs1 ** save return environment
move.l usp,a0 ** save USP for later return
move.l a0,_usp
move.l #bios_tr,$8c ** set temporary trap #3 handler
move.l #$20000,high ** set temporary high memory
move.w #$1,tpab ** temporary set parameter
bsr settpa ** use temp. in case of crash
move.w #$19,d0 ** get current disk
trap #2
move.w d0,fodsk ** foreground logged disk
move.l defdma,fodma ** save current dma pointer
bsr popdsk ** pops background disk environment
cmp.w #$0,inbg ** has a background task been suspended?
beq ccp ** not in background, jump around
jmp restore ** yes, then return to it
ccp: move.l _ssp,a7 ** set default ssp
jsr ccpmain ** address of _ccpmain on HSC board
bra ccp ** loop forever (until ^C)
fix: move.l $80,$8c ** reset the original trap #3 handler
move.w #$3,tpab ** reset tpa permanently
move.l hmem,high ** back to original value
bsr settpa
bsr reset ** reset disks
move.w #$ff,f_tim ** set f_tim to NO
movem.l regs1,d0-d7/a1-a7 ** reset old environment
move.l _usp,a0 ** replace user stack pointer
move.l a0,usp
rte
* Bios trap handler takes over when in background. Passes through all
* commands except conin and warm start. Captures these to enable return
* to suspended program. Trapping warm start allows the bdos to reinitialize
* everything as usual and return on end to background ccp. This routine
* assumes the patch to CPM _initexc has been made so that traps #0 and #1
* are left alone by the system, as should be. This routine is borrowed from
* the system bios in that it always disables interrupts. Most background
* tasks, except during program execution, operate with interrupts disabled.
* Could this be a problem on some systems?
.globl _outchar
bios_tr:
move.w #$2700,sr *** disable interrupts
cmp.w #4,d0 *** console out request?
beq conout *** put char in buffer
cmp.w #3,d0 *** was it a conin request?
beq conin *** check for return to editor
cmp.w #1,d0 *** was it a warm boot?
beq wboot *** return to calling program
cmp.w #12,d0 *** setdma?
bne out *** branch around
move.l d1,bgdma ***
out: trap #0 *** no, process the request
rte *** return
conin: trap #0 *** get the character
cmp #3,d0 *** was it a ^C?
beq fix *** yes, go back to calling program
cmp #$7f,d0 *** delete?
beq bsp *** change to backspace
cmp #2,d0 *** left arrow?
beq bsp *** change to backspace
cmp #$1b,d0 *** suspend background task command?
beq suspend *** save this environment
cmp #$19,d0 *** ^Y interrupt Cancel reset
beq cancel
rte *** send char through
bsp: move.w #8,d0 *** put ^H in d0
rte *** return
* Each running (suspended) program needs its own stacks to avoid interference.
* But the editor must use its own stack to allocate text in the [Command]
* buffer. That's the reason for the stack manipulation in this routine.
conout: link a6,#-6 ***
move d1,-2(a6) ***
move.w #$0700,sr * *** set user mode without interrupts
move.l a7,ra7u * *** save user (sp)
move.l _usp,a7 * *** replace old usp
move -2(a6),(a7) * *** put char on stack
jsr _outchar * *** goto 'C' code to handle allocation
move.l ra7u,a7 * *** replace background USP
move.l d0,rd0 * *** save return from _outchar
move.w #62,d0 * *** back to superviser state
trap #2 ***
move.l rd0,d0 *** replace _outchar's returned value
cmp #$ff,d0 *** -1 means allocation error
beq fix *** if so cleanup and leave
unlk a6 ***
rte ***
suspend: *** save all environment variables
movem.l d1-a7,bgregs
move.l usp,a0
move.l a0,bgusp *** save current USP value
move.w #$ffff,inbg *** set switch
bsr savdsk *** save current disk setup
bra fix *** return to editor
restore: *** return to suspended task
move.w #$2700,sr *** restore SR to trap #3 state
bsr popdsk *** restore disk setup
move.l bgusp,a0 *** restore all environment variables
move.l a0,usp *** restore background USP
movem.l bgregs,d1-a7
clr.w inbg *** set up for next time through
bra bsp *** return to suspended program
savdsk: move.w #$19,d0 *** save current disk value
trap #2
move.w d0,bgdsk
rts
cancel: move.w #0,d0 *** warm boot used to clear between
trap #2 *** background functions
rte
popdsk: cmp.w #$0,f_tim *** is this the first time through?
beq first_t *** yes, no environ to pop
move.w #$0e,d0 *** restore default drive code
move.w bgdsk,d1
trap #2
move.w #$1a,d0 *** use the same dma as when opened
move.l bgdma,d1
trap #2
first_t:
rts
wboot: move.w #$0,log_dsk *** address of disk select word
moveq.l #$d,d0 *** warm boot request to z80
bsr append *** HSC dependent routines to deal with
bsr extract *** I/O server
move.w #$1,tpab *** temporarily reset tpa to protect editor
bsr settpa ***
bra ccp *** remain in background
* Return disk system and buffers to original state
reset: bsr savdsk ** save current disk state
move.w #$1a,d0 ** reset dma
move.l fodma,d1
trap #2
move.w #$0e,d0 ** return disks to original login
move.w fodsk,d1
trap #2
move.w #$25,d0 ** reset disks A-C to R/W
move.w #$3,d1
trap #2
rts
* Sets tpa limits using values in tpab. Use to set TPA before beginning
* a background task. On return from task call the function again to
* set the high TPA back to the temporary value. This way a crash during
* a background task will not leave the memory permanently truncated. On
* exit from background, restore original value stored in hmem. The
* calling function stores the TPAB parameter at tpab before calling this
* function. The tpab should be 1 to set the memory temporarily or 3 for
* permanent setting.
settpa: move.w #$3f,d0
move.l #tpab,d1
trap #2
rts
.even
.bss
inbg: ds.w 1 * 0 = no bg task running
f_tim: ds.w 1 * 0 = first time through background
tpab: ds.w 1 * parameter for tpa set
low: ds.l 1 * storage for low mem value
high: ds.l 1 * storage for high mem value
.even
hmem: ds.l 1 * permanent highmem
rd0: ds.l 1
ra7u: ds.l 1 * storage for register user a7 in conout
regs1: ds.l 15 * register storage for _ccp and fix
_usp: ds.l 1 * value of USP for _ccp and fix
_ssp: ds.l 1 * pointer to stack location in background
bgregs: ds.l 15 * register storage for suspended task two
bgusp: ds.l 1 * value of USP for for suspended task
fodma: ds.l 1 * dma *address in foreground (Editor)
bgdma: ds.l 1 * dma *address in background
fodsk: ds.w 1 * foreground (Editor) logged disk
bgdsk: ds.w 1 * background logged disk
.even
ds.l 300 * space for stack probably overkill
sstack:=*
.end
------------------------------ cut - here ------------------------------------
/* 'C' code for MicroEmacs interface. Note calling conventions are version
* 28, not 35 or 36
*/
main(argc,argv)
register int argc;
register char **argv;
{
/* usual declarations */
initv(); /* init trap handlers #0 & #1, and set-up the
* background system.
*/
}
/* Bound to AGRAVE CTRL-C on my system */
case 0x03: /* start a ccp subjob */
if ((f=onlywind(NULL, TRUE)) != TRUE)
return(f);
if ((f=combuf()) != TRUE)/* open [Command] window */
return(f);
/* combuf() is a new function not in original
* source. Depends on a new addline() also
*/
if ((f=prevwind(NULL, TRUE)) != TRUE)
return(f);
setmark(NULL,TRUE);
gotoeob(NULL,TRUE);
update();
asm("trap #1"); /* was 4 */
return(prevwind(NULL,TRUE));
/* Handles the [Command] buffer line allocation */
outchar(c)
register int c;
{
switch(c)
{
case '\r':
return(NULL);
case '\n': /* newline */
c = newline(NULL,TRUE);
break;
case CTRL|'X': /* delete line */
c = mdeleln(NULL,TRUE);
break;
case '\b': /* back delete */
c = backdel(NULL, TRUE);
break;
default: /* simple insert */
c = linsert(1, c);
}
if(c==NULL)
c = 0xff;
else
{
update();
c=NULL;
}
return(c);
}