Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site ucbvax.BERKELEY.EDU Path: utzoo!decvax!ittatc!dcdwest!sdcsvax!ucbvax!ULKYVX.BITNET!RDROYA01 From: RDROYA01@ULKYVX.BITNET (Robert Royar) Newsgroups: mod.computers.68k Subject: Source code for the background routines (long = 273 lines) Message-ID: <8607091809.AA13815@ucbjade.Berkeley.Edu> Date: Wed, 9-Jul-86 15:06:00 EDT Article-I.D.: ucbjade.8607091809.AA13815 Posted: Wed Jul 9 15:06:00 1986 Date-Received: Thu, 10-Jul-86 21:00:48 EDT Sender: mwm@ucbvax.BERKELEY.EDU Organization: University of Louisville Lines: 273 Approved: info-68k@ucbvax.berkeley.edu * Below is the code for the background utility I have been writing about. * I would appreciate someone's assmebling this code and integrating it into * her own package to see whether it works for others. The simple move to * background operation, I have been using for three months without bugs. I * posted that earlier. This version, with its stack manipulations is new * and has been tested for a week. I cannot guarantee it. At the end of the * assembly code is the 'C' routine that interfaces to uEmacs, but any routine * to output characters should work. Also, this could be a stand alone program * loaded in high memory. If so add some lines of code to the conin routine to * watch for an attention char that will begin the process, or patch your bios * conin routine to call trap #1, after this is installed, whenever the * attention character is typed. * Known Gotcha's: * 1) If a file is open in one process when you switch to the other which also * opens a file, the original DMA address is lost. On return, your original * file will have an incorrect buffer. Solution: write a getdma routine * to save the foreground DMA pointer and reset it on return. * 2) If you have an open file in the foreground and when you go to background, * you change the logged disk drive, when you return to foreground, the * program cannot find the open file. It should save the current disk value * on entry, and restore it on return. Then it would also have to save the * background logged disk, and reset it when switching back to the background * task. I'm too lazy right now. * 3) This problem occurs frequently on my system with or without this * background stuff and is probably a problem with my system. If you * are logged into one drive (say C:) and load ddt (only ddt) from another * (say B:) with a file to load on the command line (i.e. B:DDT FOO.BAR), ddt * goes to lunch, rather the drive does. However, if you load ddt without * the filename on the command line and use the 'e' command, everthing works * fine. Also, if ddt is on a RAMDRIVE, it will load successfully with the * filename on the command line. * a technique for implementing sys() calls from inside a C program. * 1) call initv() once at start-up. * 2) oldhigh_add = hmem * 3) trap #1 (_ccp routine) * 4) reset trap #3 handler to capture warm boot and connin * 5) on exit from background programs, loop to ccp (background) * 6) on ^C reset tpa, trap #3, and return to suspended program * 7) on suspend background task and return to foreground * 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 ssp: = $5a62 * address of SSP at pload ccpmain:= $234a * cpmlib _main on HSC board (not _ccp) log_dsk:= $5abe * address of log_dsk tbl on HSC system .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 bsr settpa 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 cmp.w #$0,inbg ** has a background task been suspended? bne 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 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 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 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 *** 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 jsr settpa *** bra ccp *** remain in background * These two routines should probably save and restore the current DMA address * and current logged disk for each process, then restore them on exit. 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 bra fix *** return to editor restore: *** return to suspended task move.w #$2700,sr *** restore SR to trap #3 state 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 reset: 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: movem.l d0-d7/a0-a6,regs2 * not presently necessary move.w #$3f,d0 move.l #tpab,d1 trap #2 movem.l regs2,d0-d7/a0-a6 rts .even .data inbg: dc.w 0 * 0 = no bg task running * $FFFF = bg task active .even .bss 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 regs2: ds.l 15 * register storage for settpa _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 .even ds.l 300 * space for stack probably overkill sstack:=* .end ----------------- cut -- here ------------------------------------------------- /* Some sample 'C' code */ /* minimum initiation. This assumes you have patched _initexc in CPM.SYS * to work as DRI claims it does, i.e. leaves traps 0 & 1 alone on warm * boot. */ main() { initv(); } /* 'C' code fragment to call installed program */ /* All functions are part of uEmacs */ case 0x03: /* start a ccp subjob */ if ((f=onlywind(NULL, TRUE)) != TRUE) return(f); if ((f=combuf()) != TRUE)/* open [Command] window */ return(f); if ((f=prevwind(NULL, TRUE)) != TRUE) return(f); setmark(NULL,TRUE); gotoeob(NULL,TRUE); update(); asm("trap #1"); return(prevwind(NULL,TRUE)); /* this routine is only necessary for uEmacs interface; otherwise could * be in the assembly code. */ 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; /* failed functions return 0, but conout expects -1 for failure */ else { update(); c=NULL; } return(c); }