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); }