/* Julian Stacey's Portable Monitor #else extern char Cmode; /* CPM C80 unbuffered io flag */ extern int CtlB; /* address of a procedure to be excuted on a Control B interrupt */ #endif CPM /*----------------------------------------------------------------------*/ /* Programming entities to aid portability */ #define s32bit long #define x32bit long /* u32bit CPM C80 compiler doesn't provide this */ #define u16bit unsigned #define x16bit int #define s16bit int /* u8bit unused */ #define x8bit char /* s8bit unused */ #define u8more unsigned #define x8more char #define s8more int /*----------------------------------------------------------------------*/ #ifdef CPM #define pointer long /* should be 'char *', but CPM C80 only gives 16 bits for that */ #else #define pointer char * #endif #ifdef CPM int *pc80w; long *pc80l, *pc81l; /* Pointers for intermediate evaluation solely for use by CPM C80 * C to 8080 compiler, as CPM C80 fails to convert longs to * [pointers to ints or longs] properly, * note this only needs to be here whilst developing the monitor, * on the 8080, when monitor is running on a 32bit machine, theyre redundant */ #endif CPM #define BEL 7 /*----------------------------------------------------------------------*/ /* Flags to define mode of operation of the monitor */ u8more size; /* Entity Type */ #define STRING 0 #define BYTE 1 #define WORD 2 #define LONG 3 u8more mod_access; /* Access Modes */ #define READ 1 #define WRITE 2 #define MODIFY READ | WRITE x8more verbose ; /* if flag is off monitor gives no prompts (useful for remote control of monitor by another program */ x8more ascii ; /* if set & also in byte read mode, prints text as well as hex, else prints just hex */ x8more intrupt ; /* used to indicate when keyboard has interrupted a sequence. */ x8more dump ; /* if 1 write to disc (& possibly screen), if 0 write to screen */ x8more load ; /* if 1 [read from disc & display on screen] if 0 read from keyboard */ x8more supres; /* used to suppress multiple CR LFs coming in from keyboard */ /*----------------------------------------------------------------------*/ /* Current address monitor is looking at. * 'relative' & 'base' are the masters, 'total' is * a derived dependant, these pointers are used to * specify beginning of block operations, as well as * discrete operations */ long relative ; /* define b_ent for use everywhere for typecasting, depending if we are using CPM 8080 or MSDOS 8086 */ #ifdef MSDOS #define b_ent pointer #endif MSDOS #ifndef MSDOS #define b_ent long #endif MSDOS b_ent base; pointer total; pointer stop; /* end of source block & scratch pointer */ pointer dest ; /* destination block & scratch pointer */ pointer mem_p ; /* yet another scratch pointer */ /* temporary variable used within individual cases, & to ensure CPM C80 deals with corect sizes */ union cludge { char s1; unsigned s2; long s4; } data ; u8more cksum ; /* Checksum for intel loader format*/ #ifndef CPM FILE *chan; /* used by object io */ #else int chan ; #endif x8more hexcom; /*----------------------------------------------------------------------*/ #define FILE_LN 15 /* Max length name is a:monitor_.obj + null */ char filnam[FILE_LN]; /* filename for object IO (in RAM) */ char initnam[] = "DUMP.OBJ"; /* initial filename (in EPROM) */ char *name_p ; /* pointer to current filename */ char menu[] = "(For menu type \'?\')"; /*----------------------------------------------------------------------*/ #ifdef CPM /* Called by CPM C80 environment when interrupt has been noticed (can only be noticed by calling CtlCk periodically */ interupt() { putchar(BEL); intrupt = 1; } x8more intj() { CtlCk(); return(intrupt); } #else /* JJLATER hack this to signals */ interupt() { putchar(BEL); intrupt = 1; } x8more intj() { return(0); } #endif /*----------------------------------------------------------------------*/ x8more isletter(x) char x; { if ((x >= 0x20) && (x <= 0x7E)) return(1) ; else return(0); } /* Put a character to screen after seeing if a control B keyboard interrupt has occured */ putjch(ch_x) char ch_x; { #ifdef CPM CtlCk(); /* call 'interupt' if one has occured */ #else CPM /* JJLATER add something */ #endif CPM ch_x &= 0x7F ; /* make sure I put out no parity, also * make it a litle bit hard to modify the * copyright notice if the recipient only has a binary */ #define BS 8 #define HT 9 #define NL 0xA #define CR 0xD #define NULL 0 if (isletter(ch_x) || ch_x == BEL || ch_x == BS || ch_x == HT || ch_x == NL || ch_x == CR || ch_x == NULL ) /* mask out chars that will cause cursor addressing screen * disturbances, but allow nulls, as they may be used to * implement time delays */ { if (!dump) putchar(ch_x); else if (verbose) putchar(ch_x); } if (dump) putc(ch_x, chan); } /*----------------------------------------------------------------------*/ /* Error messages defined so they are consistently the same, & can be recognised by a remote control program */ error(x) char *x; { putjch(BEL); prints( "\nERROR : ") ; prints(x); } /*----------------------------------------------------------------------*/ /* Ensure same spacing on screen for reads & writes */ #define prompt prints(" ? ") #define space prints(" ") /*----------------------------------------------------------------------*/ /*The output procedures in this section could be defined by macros, but not using CPM C80, as the pre-processor refuses parameters */ /* Output Routines */ put1h(x) char x; { if (x < 10) putjch(x + '0'); else putjch(x -10 + 'A'); } put2h(x) x8bit x; { cksum += x ; put1h((x >>4 ) & 0xF) ; put1h(x & 0xF); } put4h(x) x16bit x; { put2h((x >> 8) & 0xFF ); put2h(x & 0xFF) ; } put8h(x) x32bit x; { put4h((x >> 16) & 0xFFFF ); put4h(x & 0xFFFF); } putadr(x) x32bit x; { put2h(x >> 16 & 0xFF ); put4h(x & 0xFFFF); } out1h(x) char x; { putjch(' '); put1h(x); } out2h(x) x8bit x; { putjch(' '); put2h(x); } out4h(x) x16bit x; { putjch(' '); put4h(x); } out8h(x) x32bit x; { putjch(' '); put8h(x); } outadr(x) x32bit x; { putjch(' '); putadr(x); } /*----------------------------------------------------------------------*/ #define ADDER total = relative + base ; /*----------------------------------------------------------------------*/ /* Set 'total' to be consistent with 'relative' & 'base' masters, then display*/ setloc(lf) x8more lf; /* line feed flag */ { ADDER outloc((x8more)lf) ; } /* Display current location addresses */ outloc(lf) x8more lf; { if (lf) vputjch('\n'); if (base != (b_ent)0) { vprints( "B:" ); putadr((x32bit)base); vprints( "+R:" ); } putadr((x32bit)relative); if (base != (b_ent)0) { vprints( "=T:" ); putadr((x32bit)total) ; } putjch(' '); } put_byte(x) x8bit x; { put2h(x); put_letr(x); } put_letr(x) x8bit x; { putjch(' '); if ( ascii && isletter(x)) putjch(x) ; else putjch(' '); } vputjch(x) char x; { if (verbose) putjch(x); } /*-------------------------------------------------------------------------*/ /* String procedures */ vprints(x) char *x; { if (verbose) prints(x); } jgets(name,len) /* Puts a null terminated string into array *'name', where array is of max. size (inc null) 'len') */ char *name ; x8more len; { s8more c ; char *limit ; limit = name + len -1 ; #ifdef CPM #define EOF -1 #endif /* End of file (CPM C80 defines this as -1) */ while ( name < limit && (c = getjch()) != '\n' && c != EOF) { *name = (char)c ; name++ ; } *name = '\0'; } prints(x) char *x; { while((*x) && !intrupt) { /* if necessary for VDU if (*x == '\n') putjch('\r'); */ putjch(*x); x++; } } /*----------------------------------------------------------------------*/ /* Input Routines */ /* Gets a charater from disc or keyboard */ s8more getjjch() { s8more x ; x = ( load ? getc(chan) : #ifdef CPM getchar() #endif CPM #ifdef MSDOS getche() #endif MSDOS #ifdef UNIX getchar() /* something else needed */ #endif UNIX ) ; if ( x != EOF) { x &= 0x7F ; /* strip parity bit */ if (load && verbose) putchar((char)x); /* show whats coming from disc */ } return(x); } /* Strips multiple CR LF NULL strings to a single LF, to deal with CPM and nasty terminals.*/ s8more getjch() { s8more x; if (supres) /* ignore 'CR's etc */ { do x = getjjch() ; while (x == '\r' || x == '\n' || x == '\0'); supres = 0; /* allow next time to return a CR */ } else { /* may get a 'CR' etc */ x = getjjch() ; if ( x == '\r' || x == '\n' || x == '\0') { supres = 1 ; x = '\n'; } } return(x); } /* Gets one hex from keyboard returns -1 if a 'white space' character or comma, Assumptions : ASCII */ s8more geth() { s8more data ; hexcom = 0; for(;;) { data = getjch() ; if (data >= '0' && data <= '9') return(data - '0'); if (data >= 'A' && data <= 'F') return(data - 'A' + 10 ); if (data >= 'a' && data <= 'f') return(data - 'a' + 10 ); if (data == ' ' || data == '\t' || data == ',' || data == '\n' || data == EOF ) return((s8more)-1); if (data == '+' || data == '-' || data == '.') { hexcom = data; vprints("\b \n"); return((s8more)-1); } error( "\nHex characters only please.\n" ); } } /* The hex input routines below allow number correction by allowing continuous input up to a 'white space' - excess most significant nibbles fall of the left end */ x8bit get2h() /* Get a byte containing 2 hex chars */ { int temp1, temp2; while (( temp1 = geth()) == -1); while (( temp2 = geth() ) != -1 ) temp1 = (temp1 << 4) | temp2 ; return((x8bit)temp1); } u16bit get4h() { int temp; u16bit result; while ((result = geth()) == -1); while ( ( temp = geth() ) != -1 ) result = (result << 4) | temp ; return(result); } x32bit get8h() { int temp; s32bit result; while (( result = geth()) == -1 ) ; while ( ( temp = geth() ) != -1 ) result = (result << 4) | temp ; return(result); } /* 'getadr' jgets an address, it is separate from 'get8h' for 2 reasons : 1 in case fixed format input is one day required. 2 Current NS16K family adresses are 24 bits (= 6 hex) */ #ifdef CPM x32bit #else pointer #endif getadr() { x32bit temp; temp = get8h(); return( #ifndef CPM (pointer) #endif (0xFFFFFF & temp)); } /*----------------------------------------------------------------------*/ /* Gets 2 hex chars for object loader, no more, no less, no terminating white space required */ u8more take2h() { x8bit temp; temp = geth() << 4 ; temp |= geth() ; cksum += temp ; return(temp); } /*----------------------------------------------------------------------*/ /* Display [current, previous, or next] : [byte, word, long, or string] */ present(disp) s8more disp ; /* Displacement of next entity to be accessed with case '.' , '+' , or '-' */ { unsigned count ; x8bit tmp; if ( size == BYTE) { relative += disp; setloc(0); /* 'outloc' would be a bit faster, but less safe */ if (mod_access & READ) { space; put_byte(*(char *)total); } if (mod_access & WRITE) { prompt; *(char *)total = get2h(); } } else if ( size == WORD) { relative += 2 * disp; setloc(0); #ifdef CPM pc80w = (int *)total; #endif if (mod_access & READ) { space; #ifdef CPM out4h(*pc80w); #else out4h(*(int *)total); #endif } if (mod_access & WRITE) { prompt; #ifdef CPM *pc80w = get4h(); #else *(int *)total = get4h(); #endif } } else if ( size == LONG ) { relative += 4 * disp; setloc(0); #ifdef CPM pc80l = (long *)total; #endif if (mod_access & READ) { space; #ifdef CPM out8h(*pc80l) ; #else out8h(*(long *)total); #endif } if (mod_access & WRITE) { prompt; #ifdef CPM *pc80l = get8h(); #else *(long *)total = get8h(); #endif } } else /* STRING */ { relative += disp; if (mod_access & READ) { setloc(0); count = 300; /* limit printout to give a feel of where you are, without printing kilobytes (in case the string you are printing is a source file ! */ space; while ((*(char *)total) && count-- && !( intrupt)) { putjch(*(char *)total); total += 1; } } if (mod_access & WRITE) { setloc(0); prompt; while ((tmp = getjch()) != '\n' && tmp != EOF) { *(char *)total = tmp; total += 1 ; } /* note no null appended to string */ } } if (!(mod_access & WRITE)) vputjch('\n'); } /*----------------------------------------------------------------------*/ /* Procedures used by the ram block validate case */ tstrfrsh() /* test dram refresh (see separate note in hardware document) */ { /* #define CHAIN_LN 10000 u8bit jumpchain[CHAIN_LN]; pointer ptr; for (ptr = jumpchain, ; ; ) ; */ } int initblk(x) /* Initialise Block to x (normally 0 or 0xFF), Returns: 1=success, 0=fail */ x8bit x; { for (relative = dest; relative <= stop; relative++) { ADDER *(x8bit *)total = x; } tstrfrsh(); for (relative = dest; relative <= stop; relative++) { ADDER if (*(x8bit *)total != x) { checkerr(x); return(0); } } return(1); } x8bit rolb(x) /* rotate byte 1 bit to left */ x8bit x; { if ( x & 0x80 ) return((x << 1 ) | 1); else return(x<<1); } x8bit rorb(x) /* rotate byte 1 bit to right */ x8bit x; {if (x&1) return( ( x >> 1 ) | 0x80); else return( ( x >> 1 ) & 0x7F); } checkerr(x) x8bit x; { error("\nData lost at "); outloc((x8more)0); prints("Data is "); put2h(*(x8bit *)total); prints(", but should be "); put2h(x); putjch('\n'); } checblok(dir,bits) /* check block for correctly set rotating 1s or 0s (return 1 if ok, 0 if not) */ s8more dir; { x8bit data; tstrfrsh(); if (dir == 1) /* increment through block */ { for (relative = dest, data = bits; relative <= stop; relative++, data = rolb(data)) { ADDER if ( *(x8bit *)total != data ) { checkerr(data); return(0); } } } else /* decrement through block */ { for (relative = stop, data = bits; relative >= dest; relative--, data = rorb(data)) { ADDER if ( *(x8bit *)total != data ) { checkerr(data); return(0); } } } return(1); } /* Prints error message for ram validation case. For when main case validate code has detected either : incompletely decoded address common with a previous address in the ram block under test, OR interference between the block & this monitor's ram workspace */ shorterr(dir,initial) s8more dir; x8bit initial; { error("Possible incomplete decoding with a "); if (dir == 1) prints("lower"); else prints("higher"); prints(" address"); checkerr(initial); } /*----------------------------------------------------------------------*/ /* Set disc to r/w (if you have changed discs while the monitor is running cpm will have made it read only, so make it now read/write. Side effect: logged disc becomes drive A/0 */ disc_rw() { #ifndef CPM /* #endif #asm PUSH B PUSH D PUSH H MVI C,0DH CALL 5 POP H POP D POP B #endasm #ifndef CPM */ #endif } /*----------------------------------------------------------------------*/ s8more out_open() { ADDER disc_rw(); if ((chan = fopen(name_p, "w")) == 0) { error("Cant open ") ;prints(name_p); return(0); } dump = 1; return(1); } s8more in_open() { if (relative ==0 && base == (b_ent)0) { error("Probable overwrite of monitor - so aborted"); return(0); } ADDER if ((chan = fopen(name_p,"r")) == 0) { error("Cant open ") ; prints(name_p); return(0); } load = 1; return(1); } sort() { if (stop < total) { /* dont chage mem_p to another temporary variable, as 'main' kows it is set */ mem_p = total ; total = stop ; stop = mem_p ; } } upto() { prints("Up to, "); } status() /* Print Monitor Status */ { vprints("Status: "); setloc(0); if (mod_access & READ ) prints("Read, "); if (mod_access & WRITE ) prints("Write, "); if (( size) == BYTE ) prints("Byte"); if (( size) == WORD ) prints("Word"); if (( size) == LONG ) prints("Long"); if (( size) == STRING ) prints("Text"); prints(", Hex"); if ( ascii ) prints("+Ascii,"); else prints(" Only,"); if ( verbose ) prints(" Verbose,"); else prints(" Quiet,") ; prints(" \'"); prints(name_p); prints("\'\n"); } /*----------------------------------------------------------------------*/ main() { s8more command; /* command character */ int (*procptr)(); /* procedure pointer */ x8more count1,count2; /* general count variable */ x8more forever; /* loop forever flag */ s8more validir; /* validate case: values 1, 0, -1, represent whether incrementing, or decrementing through block, or finished */ #define FINDSTR 30 char findstr[FINDSTR]; x8bit bits1,bits2,bits3; /* ram validate case: bit patterns of least significant byte in the block */ x8more init0F; /* ram validate case: 0 or FF block initialise data */ s8more chint; /* gen. purpose char declared as int so can get EOF */ u16bit load_tmp; /* used by load object case */ x8more eof_flg; /* used by load object case, 0 until an EOF record encountered, 1 after */ colds: /* cold start point */ /* initialise target micro port here */ #ifdef CPM Cmode = 0; /* set single char IO (unbuffered mode) */ CtlB = (int)interupt; /* load the CPM C80 interupt vector with the address of my routine */ #endif #ifdef UNIX /* add something */ #endif /* ifdef MSDOS nothing necessary */ printf("This program is being ported, even though it compiles, it is not ready for use.\n"); prints(sccsID); #define DEFENSE 0x80 putjch('C'+DEFENSE); putjch('o'+DEFENSE); putjch('p'+DEFENSE); putjch('y'+DEFENSE); putjch('r'+DEFENSE); putjch('i'+DEFENSE); putjch('g'+DEFENSE); putjch('h'+DEFENSE); putjch('t'+DEFENSE); putjch(' '+DEFENSE); putjch('J'+DEFENSE); putjch('a'+DEFENSE); putjch('n'+DEFENSE); putjch('.'+DEFENSE); putjch(' '+DEFENSE); putjch('8'+DEFENSE); putjch('4'+DEFENSE); putjch(' '+DEFENSE); putjch('J'+DEFENSE); putjch('u'+DEFENSE); putjch('l'+DEFENSE); putjch('i'+DEFENSE); putjch('a'+DEFENSE); putjch('n'+DEFENSE); putjch(' '+DEFENSE); putjch('S'+DEFENSE); putjch('t'+DEFENSE); putjch('a'+DEFENSE); putjch('c'+DEFENSE); putjch('e'+DEFENSE); putjch('y'+DEFENSE); putjch('.'+DEFENSE); prints(menu); relative = 0; #ifdef DEBUG /* { */ #define BASE 0x4000 /* This so that whilst debuging the monitor in ram under CPM you dont inadvertently stamp over the binary*/ base = (b_ent)BASE ; #endif DEBUG /* } */ size = BYTE ; mod_access = READ ; verbose = 1; ascii = 1; supres = 0; name_p = &initnam[0] ; /*----------------------------------------------------------------------*/ warms: /* Warm Start Point */ hexcom = 0; putchar('\n'); for (;;) /* Main Command Loop Of Monitor */ { if ((hexcom) && ( command == '+' || command == '.' || command == '-' )) { /* user wants to chain write, he has previously typed a '+', '-', or '.' */ command = hexcom ; hexcom = 0 ; } else { intrupt = 0; /* ensure prompt string will appear */ /* vputjch('\n'); only needed if keyboard doesnt have local echo */ vprints( "> " ); command=getjch(); vprints("\b\b\b \b\b\b"); } intrupt = 0 ; /* flush any interrupts */ if (command <= 'Z' && command >= 'A') command += 'a' - 'A' ; switch(command) { case EOF:error("End of file read.\n"); break ; case'\n':/* NULL :ignore carriage returns & line feeds */ break ; case'0':/* Set monitor back to initial mode */ relative = 0 ; #ifdef DEBUG /* { */ base = (b_ent)BASE ; #endif DEBUG /* } */ size = BYTE ; mod_access = READ ; verbose = 1; ascii = 1; name_p = &initnam[0] ; /* no break */ case's':status(); break; case'r':vprints("Read mode." );vputjch('\n'); mod_access = READ ; break; case'w':vprints( "Write mode." );vputjch('\n'); mod_access = WRITE ; break; case'm':vprints( "Modify mode (= read then write)." ); vputjch('\n') ; mod_access = MODIFY ; break; case'\"':vprints( "Text mode." );vputjch('\n'); size = STRING ; break; case'1':vprints("Byte mode." ); vputjch('\n') ; size = BYTE ; break; case'2':vprints("Word mode." ); vputjch('\n') ; size = WORD ; break; case'4':vprints("Long mode." ); vputjch('\n') ; size = LONG ; break; case'v':prints( "Verbose mode." ); vputjch('\n') ; verbose = 1; break; case'q':vprints( "Quiet mode." ); vputjch('\n') ; /* Tell user before you stop talking to them */ verbose = 0; break; case'\'':vprints("Ascii+hex mode."); vputjch('\n'); ascii = 1; break; case'h':vprints("Hex only mode." ); vputjch('\n') ; ascii = 0; break; case'a':vprints("Relative address = ? "); relative = getadr(); setloc(0); break ; case'b':vprints( "Base address = ? "); base=(b_ent)getadr(); setloc(0); break; case'n':vprints("File name = ? "); jgets(&filnam[0],FILE_LN) ; name_p = &filnam[0] ; break ; case'e':prints( "Exiting Monitor.\n" ); exit(); /* This will do a return to a CP/M monitor or Unix shell if called from there (probably whilst testing this program */ break; case'g':vprints("Goto where ? " ); relative = getadr(); prints("\nCAN\'T GOTO "); /* C wont allow a changeable goto (exept by using machine dependant self modifying code). */ setloc(0); break ; case'x':prints( "Call where ? " ); relative = getadr(); vprints( "Calling"); setloc(0); procptr = total; (*procptr)(); break; case'.':/* Access at same address, (useful if you have just changed modes, or wish to repeatedly access a port */ present(0) ; break; case'+':/* advance & display */ present(1); break; case'-':/* back & display */ present(-1); break; case'!':prints( "SWI set at " ); setloc(0); #define BREAKPOINT 0xF2 /* set as appropriate for machine & remember to type cast for the correct size */ /* 0xF2 for NS16K */ *(char *)total = BREAKPOINT; break; /* Processor status */ case'p':error( "Processor Status Not Implemented\n"); /* use #ASM, do a SWI (Software Interrupt), then do a stack unwind */ break; case'd':vprints( "Display"); /* a block of text or string */ ADDER if (size != STRING) { mem_p = relative ; prints(": LENGTH ? "); stop = getadr() ; /* length of display from current position * to lower address. This is a cludge * based on the fact I known * 'getadr' can actually return negatives, * even though it is defined as a pointer * JJLATER is this true for MSDOS & UNIX */ if (size == WORD) stop = (pointer)((long)stop * 2) ; if (size == LONG) stop = (pointer)((long)stop * 4) ; stop = stop + (long)total - 1 ; sort(); count1 = 0 ; /* count of items on line */ while ((total <= stop) && !( intrupt)) { if (!count1) outloc((x8more)1) ; if (size == BYTE) { #define DISP_LEN 0x10 count1 = DISP_LEN; dest = total; while (count1) { if (!((base != (b_ent)0) && (ascii) && ((count1%4) || (count1 == DISP_LEN) ))) putjch(' '); if (total <= stop) { put2h(*(char *)total); total += 1 ; relative += 1; /* needs to do this else address display is inconsistent */ } else /* space out to start position for ascii */ vprints(" "); count1--; } /* now print ascii equivalent of the previous hex */ if ( ascii) { putjch(' '); total = dest ; count1 = DISP_LEN ; while(count1 && (total <= stop)) { if (isletter(*(char *)total)) putjch(*(char *)total); else putjch('.'); total += 1 ; count1-- ; /* if (count1 == 8) putjch(' '); */ } } } else if ( size == WORD) { if (!count1) count1 = 0x8 ; #ifdef CPM pc80w = (int *)total ; out4h(*pc80w); #else out4h(*(int *)total); #endif total += 2 ; relative += 2; count1--; } else if ( size == LONG ) { if (!count1) count1 = 0x4; #ifdef CPM pc80l = total; out8h(*pc80l) ; #else out8h(*(pointer)total) ; #endif CPM total += 4 ; relative += 4; count1--; } } relative = mem_p ; } else /* (size == STRING) */ { /* monitor ends at null after displaying */ while (!intrupt && (chint = *(char *)total) ) { if (isletter((char)chint)) putjch((char)chint); total += 1; relative += 1; } vprints("\nSTART WAS"); /* where string ends */ outadr(mem_p); } vputjch('\n') ; break; case')':prints( "Dump Text\n" ); if (!out_open()) goto warms; while (!intrupt) { chint = *(char *)total; if (chint) vputjch(chint); else break; total += 1 ; } #define CPM_EOF 0x1A putjch(CPM_EOF); /* CPM end of text file marker */ fclose(chan) ; dump = 0; vputjch('\n'); break; case'(':prints( "Loading Text\n" ); if (!in_open()) goto warms; while(!intrupt && (chint = getjch() ) != EOF) { *(char *)total = chint; total++ ; } *(char *)total = '\0'; fclose(chan); load = 0; vputjch('\n'); break; case'>':prints( "Dump Object : Stop Address ? " ); dest = relative ;/* save 'relative'; note 'relative' is used as well as 'total' otherwise addresses printed & written to disc would be wrong */ if (relative > stop ) { dest = stop ; stop = relative ; relative = dest ; } stop = getadr(); if (!out_open()) goto warms; count1 = 0 ; /* count of binary data bytes */ while ((relative <= stop) && !intrupt) { if (count1 == 0) { putjch(':'); cksum = 0 ; #define OBJ_OUT 0xf /* max spec allows is 0x20 */ if (stop - relative < OBJ_OUT) put2h((x8bit)(stop - relative + 1 ) & 0xFF) ; else put2h((x8bit)OBJ_OUT); put4h((u16bit)relative); /* Address of start of record (spec is 16 bit address) */ put2h((x8bit)0); /* record type : data*/ } ADDER put2h(*(char *)total ); count1 += 1 ; if ((count1 == OBJ_OUT) || (relative == stop)) { cksum = ~cksum + 1 ; put2h(cksum & 0xFF); putjch('\r'); putjch('\n'); count1 =0; } relative += 1 ; } /* append an EOF record */ putjch(':'); cksum = 0; put2h(0); put4h((int)base); put2h(1); /* record type = data */ cksum = ~cksum + 1 ; put2h(cksum) ; putjch('\r'); putjch('\n'); putjch(CPM_EOF); fclose(chan) ; dump = 0; relative = dest; /* restore 'relative' */ break; case'<':prints( "Loading Object\n" ); /* note 'relative' & 'total' are both incremented for same reason as dump object case */ /* exit by giving a non data record type ( != 0 ). * slight bug : if a checksum error occurs, * data record is written to ram regardless, * checksum error is not detected until end of line * Note 'relative' is used as well as 'total' * otherwise addresses printed & written to disc * would be wrong */ dest = relative ; eof_flg = 0; if (!in_open()) goto warms; while(!eof_flg && !intrupt) { /* get beginning of record delimiter */ while((chint = getjch() ) != ':') if ( chint == EOF ) { prints("End of file."); load = 0; fclose(chan); goto warms; } cksum = 0 ; count1 = take2h(); /* get data byte count */ #define OBJ_IN 0x20 /* max allowed by mostek definition */ if(count1 > OBJ_IN && count1 != 0) { error("Bad Count"); break; } /* get record start address */ relative = take2h() ; relative <<= 8 ; data.s4 = take2h() ; relative |= data.s4 ; ADDER if ((load_tmp = take2h()) == 1) eof_flg = 1 ; /* load ram with data */ while (count1--) { *(char *)total = take2h(); total += 1 ; } /* calculate checksum from data */ data.s1 = ~cksum +1 ; /* compare with checksum from record */ if ( data.s1 != take2h()) { error("Bad Checksum"); break; } } /* have we received an EOF record ? */ if (!eof_flg) error("No EOF Record."); else { vprints("\nTHE EOF ADDRESS WAS "); putadr((x32bit)load_tmp); putjch('\n'); /* note cannot output this number earlier as it would corrupt checksum */ } fclose(chan); load = 0; relative = dest ; break; case'c':vprints("Copy: "); ADDER if (size != STRING) upto() ; prints("Destination = ? "); if (size != STRING) /* get top of source block */ { stop = getadr(); stop = stop + (long)base; } dest = getadr() ; /* get base of destination */ dest = dest + (long)base; /* now process */ if ((size == WORD) && ((stop - total) % 2 ) || (size == LONG) && ((stop - total) % 4)) { error( "Length quantisation" ); break ; } if (size == STRING ) { /* set 'stop' so we can treat it as a block */ stop = total; while (*(char *)stop++) ; /* note the null will be copied */ } else if (size == WORD) stop += 1 ; /* note we will be copying the byte above 'stop' as well. */ else if (size == LONG) stop += 3 ; /* ditto 3 bytes */ sort() ; /* Now do the copy, note this has not been written to copy through zero */ if (dest < total) { do { *(char *)dest = *(char *)total; dest += 1 ; total += 1 ; } while ( total <= stop ) ; } else { dest += stop - total ; do { *(char *)dest = *(char *)stop ; stop-- ; dest-- ; } while ( stop >= total ) ; } break; case'f':vprints("Fill : "); upto() ; prints("Data ? " ); ADDER if (size != STRING) { stop = getadr(); stop = stop + (long)base ; } else { stop = relative + base ; while (*(char *)stop++) ; stop -- ; /* dont include nul */ } if (( size == BYTE) || ( size== STRING )) { data.s1 = get2h(); while(( total <= stop) && !intj()) { *(char *)total = data.s1; total += 1; } } else if ( size == WORD) /* Note the byte beyond 'stop' may be affected*/ { data.s2 = get4h(); while(( total <= stop) && !intj()) { *(int *)total = data.s2; total += 2; } } else if (size == LONG ) { /* Note up to 3 bytes beyond 'stop' may be affected */ data.s4 = get8h(); while(( total <= stop) && !intj()) { #ifdef CPM pc80l = (long *)total; *pc80l = data.s4 ; #else *(long *)total = data.s4 ; #endif CPM total += 4; } } break; case'/':prints( "Search : Data ? " ); ADDER dest = total ; /* to detect unsuccesful searches */ if ( size == BYTE) { data.s1 = get2h(); do { if (*(char *)total == data.s1) break; total += 1; #define TOP 0x1000000 /* NS 16000 is 24 bit address, not 32 so cut short the search */ if (total == TOP) total = 0; } while ((total != dest) && !intj()); } else if ( size == WORD) { data.s2 = get4h(); do { if (*(int *)total == data.s2) break; total += 1; if (total == TOP) total = 0; } while ((total != dest) && !intj()); } else if ( size == LONG) { data.s4 = get8h(); do { #ifdef CPM pc80l = (long *)total ; if (*pc80l == data.s4) break; #else if (*(long *)total == data.s4) break; #endif CPM total += 1; if (total== TOP) total = 0; } while ((total != dest) && !intj()); } else /* String */ { jgets(findstr,FINDSTR); prints(" LOOKING FOR \'"); prints(findstr); putjch('\''); /* total == dest */ do { /* JJ is this an error */ if( *(char *)total == *findstr) { for (mem_p = findstr + 1 , stop = total + 1; *(char *)stop == *(char *)mem_p && *(char *)stop != '\0' ;) { stop++ ; if (stop == TOP) stop = 0; mem_p++ ; } if (*(char *)mem_p == '\0' ) break ; } total++ ; if (total == TOP) total = 0; } while ( total != dest && !intj()) ; } if (total == dest) error("Not found.\n"); else relative = total - base ; outloc((x8more)0); vputjch('\n'); break; case't':vprints("Test RAM: up to ? "); /* Algorithm enhanced from an article by * Edward J Milner, NASA * Lewis Research Center, * Published Electronic Design News * October 13th 1983. * Synopsis of article : * slide a 1 thru succesive incrementing bytes, * ( having initialised block to 0 ) * 1 dec 0 * 1 inc FF * 1 dec FF * repeat all previous 8 times, shifting start * bit one bit each time, * repeat all with a sliding 0 instead of a 1. * Thus the first test is : Sliding one, Zero initial, * Incrementing addresses, rotate a bit to the left * through succesive bytes in the block, * first testing each byte to see if * a previous cycle of the loop (to * access a lower address) has also erroneously * set the contents of the current address */ dest = relative; stop = getadr(); /* insert code here to generate data for 'tstrfrsh' */ prints("Number of laps (0=infinite) ? "); count1 = get2h(); forever = (count1 != 0) ? 0 : 1 ; while (count1 || forever) { count1--; /* First a sliding 1 test, then a sliding 0 */ for (bits1=1; ; bits1 = ~1) { /*For each of the 8 bits within a byte*/ for (bits2 = bits1, count2 = 0; count2 < 8 ; bits2 = rolb(bits2), count2++) { /* first initialise block low, then high */ for (init0F = 0; ; init0F = 0xFF) { /* Sweep block first incrementing addresses, then decrementing */ for (validir = 1; ; validir = -1) { if (verbose) { putchar('\n'); putadr((x32bit) (dest+(long)base)); putchar(':'); putadr((x32bit) (stop+(long)base)); if (forever) prints(" FOREVER"); else { prints(" LAP="); put2h(count1); } prints(", sliding "); if (bits1 == 1) putchar('1'); else putchar('0'); prints(", bit "); putchar('0'+count2); prints(", block start "); if(!init0F) prints("00"); else prints("FF"); prints(", sweep "); if (validir == 1) putchar('+'); else putchar('-'); } /* initialise block */ if (!initblk(init0F) ) goto warms; /* set bit pattern in block */ for ( bits3 = bits2, relative = dest; relative <= stop; bits3 = rolb(bits3), relative++) { if (intj()) goto warms; ADDER if (*(x8bit *)total != init0F) { shorterr(validir ,init0F); goto warms; } *(x8bit *)total = bits3 ; } /* check bit pattern in block */ if (validir == 1) { if (!checblok(1,bits2)) goto warms; } else { if (!checblok(-1, rorb(bits3))) goto warms; } if (validir != 1) break ; } if (init0F != 0) break ; } } if (bits1 != 1) break ; } } relative = dest ; putchar('\n'); break; case'?':prints( "Set Address : A Relative B Base / Search"); prints("\nSet R/W : R Read W Write M Modify=R+W"); prints("\nSet Size : 1 Byte 2 Word 4 Double \" Text"); prints("\nSet Mode : \' Ascii H Hex V Verbose Q Quiet"); prints("\nEntity Access: + Next . Same - Previous ! SWI Set"); prints("\nBlock Access : D Display C Copy F Fill T Test"); prints("\nFile Access : N Name > Dump Obj < Load Obj ) Dump Text ( Load Text"); prints("\nStatus Print : S Monitor P Processor"); prints("\nControl : E Exit ^B Interrupt 0 Restart X Execute G Goto\n"); status(); break; default:error("BAD COMMAND, TRY \'?\'\n"); break; } } }