This is gck_lib.c in view mode; [Download] [Up]
/* ******************************************************************** */ /* * GCK * */ /* * A Circuit Simulation Program * */ /* * by Tanju Cataltepe * */ /* * (c) Copyright 1989 * */ /* ******************************************************************** */ /* (c) Copyright 1989, Tanju Cataltepe */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <time.h> #ifdef Macintosh #include <console.h> #include <Files.h> #else #include <sys/types.h> #include <sys/stat.h> #endif #include <math.h> #include <float.h> #include "gck.h" #include "gck_vars.h" #include "gck_protos.h" #include "gck_errors.h" void *c_malloc(n) long n; { void *ptr; if (n == 0) errorReport(ERROR_C_MALLOC_SIZE_ZERO,0,ERR_VOID,0); ptr = calloc(n,1); if (ptr == NULL) errorReport(ERROR_C_MALLOC_VOID,0,ERR_VOID,0); return (ptr); } int P_eof(f) FILE *f; { register int ch; if (feof(f)) return 1; if (f == stdin) return 0; ch = getc(f); if (ch == EOF) return 1; ungetc(ch, f); return 0; } int P_eoln(f) FILE *f; { register int ch; ch = getc(f); if (ch == EOF) return 1; ungetc(ch, f); return (ch == '\n'); } double gck_random() { return((double)((double)rand() / (double)(RAND_MAX >> 1)) - 1.0); } void initializeGlobals() { long i; rfile.depth = 0; for (i = 0; i < MAX_FILE_DEPTH; i++) rfile.filePtr[i] = NULL; strcpy(rfile.filename,"\0"); for (i = 0; i < MAX_SUBCKT_DEPTH; i++) strcpy(sd[i],"\0"); ID_nindex_size = DYNAMIC_INITIAL_SIZE; ID_nindex = (long *)c_malloc(sizeof(long) * (ID_nindex_size + 1)); nodeNames.max_nodes = DYNAMIC_INITIAL_SIZE; nodeNames.node_size = 1; nodeNames.node = (nodeNameStruct *) c_malloc(sizeof(nodeNameStruct) * DYNAMIC_INITIAL_SIZE); strcpy(nodeNames.node[0].name,"0"); for (i = 1; i < DYNAMIC_INITIAL_SIZE; i++) nodeNames.node[i].name[0] = NULL_CHAR; symbolNames.max_symbols = DYNAMIC_INITIAL_SIZE; symbolNames.symbol_size = 0; commandNames.max_symbols = DYNAMIC_INITIAL_SIZE; commandNames.symbol_size = 0; symbolNames.symbol = (symbolNameStruct *) c_malloc(sizeof(symbolNameStruct) * DYNAMIC_INITIAL_SIZE); commandNames.symbol = (symbolNameStruct *) c_malloc(sizeof(symbolNameStruct) * DYNAMIC_INITIAL_SIZE); for (i = 0; i < DYNAMIC_INITIAL_SIZE; i++) { symbolNames.symbol[i].name[0] = NULL_CHAR; commandNames.symbol[i].name[0] = NULL_CHAR; } up_to_date = TRUE; cmdline_symbols = FALSE; sample_card_found = FALSE; #ifdef Macintosh object_stamp = (long) 0; #else object_stamp = (time_t) 0; #endif flag_dialog = DIALOG_NORMAL; flag_compiler = COMPILER_FORCE_COMPILATION; deltaT = 1.0; periodT = 0.0; samplesOn = 1; The_Time = 0.0; lastTime = 0.0; GND = (double *)c_malloc(sizeof(double)); *GND = 0.0; sampleTicks = (clockRec *)c_malloc(sizeof(clockRec)); NoOfTicks = 1; TicksRegistered = FALSE; presentTick = 1; NoOfVars = 0; baseOfBranch = NULL; baseOfClock = NULL; baseOfNode = NULL; baseOfNonlin = NULL; baseOfDigital = NULL; baseOfLogic = NULL; baseOfTable = NULL; baseOfModel = NULL; baseOfSource = NULL; baseOfPrint = NULL; baseOfFFT = NULL; baseOfMFFT = NULL; baseOfStatus = NULL; baseOfSubckt = NULL; twoTerminal = (1L << ((long)resistor)) | (1L << ((long)capacitor)) | (1L << ((long)inductor)) | (1L << ((long)switch_)) | (1L << ((long)vs)) | (1L << ((long)cs)); fourTerminal = (1L << ((long)coupledInductor)) | (1L << ((long)opamp)) | (1L << ((long)quantizer)) | (1L << ((long)comparator)); fourTerminal |= (1L << ((long)vcvs)) | (1L << ((long)vccs)) | (1L << ((long)adder)); nonlinears = (1L << ((long)opamp)) | (1L << ((long)quantizer)) | (1L << ((long)comparator)); globalControl = (1L << ((long)switch_)) | (1L << ((long)opamp)) | (1L << ((long)quantizer)) | (1L << ((long)subckt)) | (1L << ((long)delay)) | (1L << ((long)vs)) | (1L << ((long)cs)) | (1L << ((long)l_general)); digitals = (1L << ((long)delay)) | (1L << ((long)adder)) | (1L << ((long)multiplier)); logicals = 1L << ((long)l_general); print_to_file = FALSE; strcpy(prn_filename,"\0"); fft_to_file = FALSE; strcpy(fft_filename,"\0"); fft_window = WINDOW_RECTANGULAR; fft_must_reorder = TRUE; /* FFT vs. DFT */ shortPrint = TRUE; timePrint = TRUE; runDiag = FALSE; binaryArray[0] = 1; for (i = 1; i <= BINARY_LEN; i++) binaryArray[i] = binaryArray[i - 1] * 2; terminate_on_eof = FALSE; terminate = FALSE; } void MakeValueList(base, n) flex *base; long n; { MakeSymbolList(base, n); base->dim = n; base->UU.c = c_malloc(sizeof(double) * n); } void MakeIDList(base, n) flex *base; long n; { MakeSymbolList(base, n); base->dim = n; base->UU.c = c_malloc(sizeof(long) * n); } long GetID(np, n) flex np; long n; { return (np.UU.n[n - 1]); } double GetValue(vp, n) flex vp; long n; { return (vp.UU.x[n - 1]); } /* this function is never called.... */ void PutToID(p, n, d) flex *p; long n, d; { p->UU.n[n - 1] = d; } void PutToValue(p, n, x) flex *p; long n; double x; { p->UU.x[n - 1] = x; } long CloneID(src, res) flex src, *res; { long i; res->dim = src.dim; res->UU.c = c_malloc(sizeof(long) * src.dim); for (i = 0; i < src.dim; i++) res->UU.n[i] = src.UU.n[i]; return (res->dim); } void GetBranchByID(there, n_ID) branchRec **there; long n_ID; { /*Finds the branch with ID number n_ID in the linked list*/ *there = baseOfBranch; while (*there != NULL && (*there)->ID.UU.n[0] != n_ID) *there = (*there)->next; } void GetBranchByName(there, bname) branchRec **there; char *bname; { /*Finds the branch with name bname in the linked list*/ boolean found; *there = baseOfBranch; found = FALSE; while (*there != NULL && !found) { if (strcmp((*there)->name, bname) == 0) found = TRUE; else *there = (*there)->next; } if (!found) errorReport(ERROR_UNRESOLVED_BRANCH, bname, ERR_VOID, 0); } void newBranch(b, no_ID, no_val) branchRec **b; long no_ID, no_val; { *b = (branchRec *)c_malloc(sizeof(branchRec)); (*b)->next = NULL; MakeIDList(&(*b)->ID, no_ID); MakeValueList(&(*b)->value, no_val); } void GetModelByName(there, mname) modelRec **there; char *mname; { /*Finds the model with name mname in the linked list*/ boolean found; *there = baseOfModel; found = FALSE; while (*there != NULL && !found) { if (strcmp((*there)->name, mname) == 0) found = TRUE; else *there = (*there)->next; } if (!found) errorReport(ERROR_UNRESOLVED_MODEL, mname, ERR_VOID, 0); } void GetClockByName(there, pname) clockRec **there; char *pname; { /*Finds the clock waveform named pname in the linked list*/ boolean found; *there = baseOfClock; found = FALSE; while (*there != NULL && !found) { if (strcmp((*there)->name, pname) == 0) found = TRUE; else *there = (*there)->next; } if (!found) errorReport(ERROR_UNRESOLVED_CLOCK, pname, ERR_VOID, 0); } void GetSubcktByName(there, sname) subcktRec **there; char *sname; { /*Finds the subcircuit names sname in the linked list*/ boolean found; *there = baseOfSubckt; found = FALSE; while (*there != NULL && !found) { if (strcmp((*there)->name, sname) == 0) found = TRUE; else *there = (*there)->next; } if (!found) errorReport(ERROR_UNRESOLVED_SUBCKT, sname, ERR_VOID, 0); } void GetFromSystem(there, n) systemRec **there; long n; { /*Finds the system record for the nth clock tick*/ long i; *there = baseOfSystem; for (i = 2; i <= n; i++) *there = (*there)->next; } void InitializeSystem() { systemRec *tmp; long matrix_loop; long i, FORLIM; baseOfSystem = NULL; FORLIM = NoOfTicks; for (i = 1; i <= FORLIM; i++) { tmp = (systemRec *)c_malloc(sizeof(systemRec)); tmp->next = baseOfSystem; /* No longer dependent upon VAR limitation ... 1 Mar 92 RdH */ /* Initialize new tmpSystem records */ tmp->N = (double **) c_malloc(NoOfVars * sizeof (double *)); tmp->B = (double **) c_malloc(NoOfVars * sizeof (double *)); for (matrix_loop = 0; matrix_loop < NoOfVars; matrix_loop++) { tmp->N[matrix_loop] = (double *) c_malloc(NoOfVars * sizeof (double)); tmp->B[matrix_loop] = (double *) c_malloc(NoOfVars * sizeof (double)); } baseOfSystem = tmp; } } /* branchID returns a new branch number for every time it's called. */ long branchID() { NoOfVars++; /* error checking eliminated ... no such limit exists */ return NoOfVars; } /************ Reading Utilities *************************/ void readCard(f, l) FILE **f; char *l; { char ch; char dead_char; /* RdH */ long i, j; boolean null_encountered; int wordnum; int aa; int found; /* used to ignore blanks preceeding 1st char */ i = 1; found = FALSE; while (!P_eoln(*f)) { ch = getc(*f); if (ch == '\n') ch = SPACE_CHAR; if (ch == TAB_CHAR) ch = SPACE_CHAR; if (found == TRUE) { l[i - 1] = ch; i++; } else if ((ch != SPACE_CHAR) && (ch != TAB_CHAR)) { found = TRUE; l[i-1] = ch; i++; } } fscanf(*f, "%*[^\n]"); dead_char = getc(*f); l[i-1] = '\0'; /* The following junk performs "getwords" on the entire card... the function getword may now perform a single lookup... */ rcard.FIRSTCHAR = toupper(l[0]); for (j = 0; j < 80; j++) rcard.Card[j] = NULL_CHAR; strcpy(rcard.Card,l); /* get ALL words! */ for (wordnum = 0; wordnum < MAX_WORDS; wordnum++) for (aa = 0; aa < MAX_WORD_LENGTH; aa++) { rcard.Word[wordnum][aa] = NULL_CHAR; rcard.WORD[wordnum][aa] = NULL_CHAR; } for (j = 0; j < 80; j++) rcard.CARD[j] = l[j] = toupper(rcard.Card[j]); if (&(rcard.Card[0]) != NULL_CHAR) { wordnum = 0; i = 0; aa = 0; null_encountered = FALSE; do { while ((rcard.Card[i] != NULL_CHAR) && (rcard.Card[i] != '=') && (rcard.Card[i] != SPACE_CHAR) && (rcard.Card[i] != TAB_CHAR) && (i < 80) && (wordnum < MAX_WORDS)) { if (rcard.Card[i] == NULL_CHAR) null_encountered = TRUE; rcard.Word[wordnum][aa] = rcard.Card[i]; rcard.WORD[wordnum][aa] = rcard.CARD[i]; aa++; i++; } rcard.Word[wordnum][aa] = NULL_CHAR; rcard.WORD[wordnum][aa] = NULL_CHAR; wordnum++; i++; aa = 0; while ((rcard.Card[i] != NULL_CHAR) && ((rcard.Card[i] == '=') || (rcard.Card[i] == SPACE_CHAR) || (rcard.Card[i] == TAB_CHAR)) && (i < 80) && (wordnum < MAX_WORDS)) { if (rcard.Card[i] == NULL_CHAR) null_encountered = TRUE; rcard.Word[wordnum][i] = rcard.Card[i]; rcard.WORD[wordnum][i] = rcard.CARD[i]; i++; } } while ((i < 80) && (wordnum < MAX_WORDS) && (null_encountered == FALSE)); } for (i = 0; i < MAX_WORDS; i++) if ((rcard.Word[i][0] == NULL_CHAR) || (rcard.Word[i][0] == ';') || (rcard.Word[i][0] == '#')) break; rcard.word_count = i; } char *GetWord(Result,n) char *Result; long n; { return strcpy(Result,rcard.Word[n-1]); } char *GetWORD(Result,n) char *Result; long n; { return strcpy(Result,rcard.WORD[n-1]); } void readNNode(b, l, n) branchRec **b; char *l; long n; { aWord tmp; long i; GetWORD(tmp,1L); for (i = 0; i < NAME_LEN; i++) (*b)->name[i] = tmp[i]; (*b)->ID.UU.n[0] = branchID(); for (i = 2; i <= n + 1; i++) { GetWORD(tmp,i); /* assigns a position for the given node RdH */ (*b)->ID.UU.n[i - 1] = nodeID(tmp); } } /* Reads 2 more nodes from card */ void read2Node(b, l) branchRec **b; char *l; { readNNode(b, l, 2L); } void read4Node(b, l) branchRec **b; char *l; { readNNode(b, l, 4L); } void read2NodeName(b, l) branchRec **b; char *l; { /*NAME1 N+ N- NAME2*/ aWord tmp; long i; read2Node(b, l); GetWORD(tmp,4L); strcpy((*b)->cname,tmp); (*b)->next = baseOfBranch; baseOfBranch = *b; } void read4NodeName(b, l) branchRec **b; char *l; { /*NAME1 N+ N- NC+ NC- NAME2*/ aWord tmp; long i; read4Node(b, l); GetWORD(tmp,6L); for (i = 0; i < NAME_LEN; i++) (*b)->cname[i] = tmp[i]; (*b)->next = baseOfBranch; baseOfBranch = *b; } void read2NodeValue(b, l) branchRec **b; char *l; { /*NAME N+ N- VALUE*/ aWord tmp; read2Node(b, l); GetWORD(tmp,4L); if (isalpha(tmp[0])) (*b)->value.UU.x[0] = parseSymbol(b,0,4L); else (*b)->value.UU.x[0] = gck_atod(tmp); (*b)->next = baseOfBranch; baseOfBranch = *b; } void read4NodeValue(b, l) branchRec **b; char *l; { /*NAME N+ N- NC+ NC- VALUE*/ aWord tmp; read4Node(b, l); GetWORD(tmp,6L); if (isalpha(tmp[0])) (*b)->value.UU.x[0] = parseSymbol(b,0,6L); else (*b)->value.UU.x[0] = gck_atod(tmp); (*b)->next = baseOfBranch; baseOfBranch = *b; } /************ Specific Components ***********************/ /********** Clock Specifications ************/ void readLibrary() { aWord tmp; GetWord(tmp,2L); if (rfile.depth == (MAX_FILE_DEPTH - 1)) errorReport(ERROR_LIBRARY_DEPTH,0,ERR_VOID,0); if ((rfile.filePtr[++rfile.depth] = fopen(tmp,"r")) == NULL) errorReport(ERROR_LIBRARY_NOT_FOUND,tmp,ERR_VOID,0); if (object_stamp < FileAge(tmp)) up_to_date = FALSE; } void readClock(l) char *l; { /*.CLOCK NAME 11100001*/ clockRec *b; aWord tmp; long i, j, FORLIM; b = (clockRec *)c_malloc(sizeof(clockRec)); GetWORD(tmp,2L); strcpy(b->name,tmp); GetWORD(tmp,3L); i = 0; do { i++; } while (tmp[i - 1] != NULL_CHAR); /* was SPACE */ i--; if ((TicksRegistered == TRUE) && (NoOfTicks != i)) errorReport(ERROR_INCONSISTENT_CLOCKS,0,ERR_VOID,0); if (TicksRegistered == FALSE) { TicksRegistered = TRUE; NoOfTicks = i; } MakeValueList(&b->value, NoOfTicks); FORLIM = NoOfTicks; for (j = 0; j < FORLIM; j++) { if (tmp[j] == '1') b->value.UU.x[j] = 1.0; else b->value.UU.x[j] = 0.0; } b->next = baseOfClock; baseOfClock = b; } /*********** Node Specifications ******************/ void readNode(l) char *l; { /*.NODE <NODE_NUMBER> LOW = <VSAT_LOW> HIGH = <VSAT_HIGH> SR = <SLEW_RATE>*/ nodeRec *b; aWord tmp; long i; aWord TEMP; b = (nodeRec *)c_malloc(sizeof(nodeRec)); GetWORD(tmp,2L); b->ID = nodeID(tmp); b->flo = FALSE; b->fhi = FALSE; b->fsr = FALSE; i = 3; GetWORD(tmp,i); while (tmp[0] != NULL_CHAR) { /* was SPACE_CHAR RdH */ if (strncmp(tmp,"LOW",3) == 0) { i++; b->lo = gck_atod(GetWORD(TEMP,i)); b->flo = TRUE; } else if (strncmp(tmp,"HIGH",4) == 0) { i++; b->hi = gck_atod(GetWORD(TEMP,i)); b->fhi = TRUE; } else if (strncmp(tmp,"SR",2) == 0) { i++; b->sr = gck_atod(GetWORD(TEMP,i)); b->fsr = TRUE; } i++; GetWORD(tmp,i); } b->next = baseOfNode; baseOfNode = b; } void SetUpNodes() { nodeRec *tmpNode; tmpNode = baseOfNode; while (tmpNode != NULL) { tmpNode->me = &vv_x[tmpNode->ID]; /* tmpNode->me = var_vector_x[tmpNode->ID]; */ tmpNode = tmpNode->next; } } void RunNodes() { nodeRec *tmpN; double d; tmpN = baseOfNode; while (tmpN != NULL) { if (tmpN->flo) { if (*tmpN->me < tmpN->lo) *tmpN->me = tmpN->lo; } if (tmpN->fhi) { if (*tmpN->me > tmpN->hi) *tmpN->me = tmpN->hi; } if (tmpN->fsr) { d = *tmpN->me - tmpN->oldme; if (fabs(d) > tmpN->sr) { if (d > 0) *tmpN->me = tmpN->oldme + tmpN->sr; else *tmpN->me = tmpN->oldme - tmpN->sr; } tmpN->oldme = *tmpN->me; } tmpN = tmpN->next; } } /********** Simulation Time ***************/ void readTimeStep(l) char *l; { /*.STEP <VALUE>*/ aWord symbol; aWord value; GetWORD(symbol,2L); if (isalpha(symbol[0])) { GetWORD(value,3L); createSymbol(symbol,value); deltaT_sv.is_symbol = TRUE; strcpy(deltaT_sv.sName,symbol); if (strlen(value) == 0) deltaT = 0.0; else deltaT = gck_atod(value); } else { deltaT_sv.is_symbol = FALSE; deltaT_sv.sName[0] = NULL_CHAR; deltaT = gck_atod(GetWORD(symbol,2L)); } } void readClockPeriod(l) char *l; { /*.PERIOD <VALUE>*/ aWord symbol; aWord value; GetWORD(symbol,2L); if (isalpha(symbol[0])) { GetWORD(value,3L); createSymbol(symbol,value); periodT_sv.is_symbol = TRUE; strcpy(periodT_sv.sName,symbol); if (strlen(value) == 0) periodT = 0.0; else periodT = gck_atod(value); } else { periodT_sv.is_symbol = FALSE; periodT_sv.sName[0] = NULL_CHAR; periodT = gck_atod(GetWORD(symbol,2L)); } } void readTime(l) char *l; { /*.TIME <SIMULATION_TIME>*/ aWord symbol; aWord value; GetWORD(symbol,2L); if (isalpha(symbol[0])) { GetWORD(value,3L); createSymbol(symbol,value); lastTime_sv.is_symbol = TRUE; strcpy(lastTime_sv.sName,symbol); if (strlen(value) == 0) lastTime = 0.0; else lastTime = gck_atod(value); } else { lastTime_sv.is_symbol = FALSE; lastTime_sv.sName[0] = NULL_CHAR; lastTime = gck_atod(GetWORD(symbol,2L)); } } void readSample(l) char *l; { /*.SAMPLE 00100*/ aWord tmp; long i, j, FORLIM; sample_card_found = TRUE; GetWORD(tmp,2L); i = 0; do { i++; } while (tmp[i - 1] != NULL_CHAR); /* was SPACE_CHAR */ i--; if ((TicksRegistered == TRUE) && (NoOfTicks != i)) errorReport(ERROR_INCONSISTENT_CLOCKS,0,ERR_VOID,0); if (TicksRegistered == FALSE) { TicksRegistered = TRUE; NoOfTicks = i; } MakeValueList(&sampleTicks->value, NoOfTicks); FORLIM = NoOfTicks; samplesOn = 0; for (j = 0; j < FORLIM; j++) { if (tmp[j] == '1') { sampleTicks->value.UU.x[j] = 1.0; samplesOn++; } else sampleTicks->value.UU.x[j] = 0.0; } } void SetUpStubSample() { long j; if (!sample_card_found) { MakeValueList(&sampleTicks->value, NoOfTicks); for (j = 0; j < NoOfTicks; j++) { sampleTicks->value.UU.x[j] = 1.0; } } } void readPrint(l) char *l; { /*.PRINT V(n1, n2) I(name) ...*/ printRec *tmpP, *lastP; aWord tmpW; nameString tmpN; long i, j, k; long counter; i = 2; GetWORD(tmpW,i); while (tmpW[0] != NULL_CHAR) { switch (tmpW[0]) { case '>': /* new option */ print_to_file = TRUE; if (tmpW[1] == NULL_CHAR) { i++; GetWord(prn_filename,i); } else { GetWord(tmpW,i); strcpy(prn_filename,&tmpW[1]); } break; case 'V': tmpP = (printRec *)c_malloc(sizeof(printRec)); tmpP->outType = branchVoltage; strcpy(tmpN,"\0"); j = 3; while ((tmpW[j - 1] != ',') && (tmpW[j - 1] != ')')) { tmpN[j - 3] = tmpW[j - 1]; j++; } tmpN[j - 3] = NULL_CHAR; tmpP->n1 = nodeRecordFromList(tmpN,REQUEST_NODE); if (tmpW[j - 1] == ')') /* Remembering Ayse! */ tmpP->n2 = nodeRecordFromList("0",REQUEST_NODE); else { strcpy(tmpN,"\0"); k = j + 1; while (tmpW[k - 1] != ')') { tmpN[k - j - 1] = tmpW[k - 1]; k++; } tmpN[k - j - 1] = NULL_CHAR; tmpP->n2 = nodeRecordFromList(tmpN,REQUEST_NODE); } if (baseOfPrint == NULL) { baseOfPrint = tmpP; lastP = tmpP; } else { lastP->next = tmpP; lastP = tmpP; } break; case 'I': tmpP = (printRec *)c_malloc(sizeof(printRec)); tmpP->outType = branchCurrent; strcpy(tmpN,"\0"); j = 3; while (tmpW[j - 1] != ')') { tmpN[j - 3] = tmpW[j - 1]; j++; } strcpy(tmpP->name,tmpN); if (baseOfPrint == NULL) { baseOfPrint = tmpP; lastP = tmpP; } else { lastP->next = tmpP; lastP = tmpP; } break; default: errorReport(ERROR_BAD_PRINTSPEC, progName, ERR_VOID, 0); break; } i++; GetWORD(tmpW,i); } } void SetUpPrint() { printRec *tmp; branchRec *b; tmp = baseOfPrint; while (tmp != NULL) { switch (tmp->outType) { case branchVoltage: if (tmp->n1 == 0) tmp->vo1 = GND; else tmp->vo1 = &vv_x[ID_nindex[tmp->n1]]; if (tmp->n2 == 0) tmp->vo2 = GND; else tmp->vo2 = &vv_x[ID_nindex[tmp->n2]]; break; case branchCurrent: GetBranchByName(&b, tmp->name); tmp->vo1 = &vv_x[b->ID.UU.n[0]]; tmp->vo2 = GND; break; } tmp = tmp->next; } if (print_to_file) { print_file = fopen(prn_filename,"w"); if (print_file == NULL) errorReport(ERROR_FILE_PRINT_CANNOT_OPEN,prn_filename,ERR_VOID,0); } else { print_file = stdout; } } void RunPrint() { printRec *tmp; float shTmp; if (baseOfPrint == NULL) return; tmp = baseOfPrint; if (shortPrint) { if (timePrint) { shTmp = The_Time; fprintf(print_file,"% .5E%c", shTmp, TAB_CHAR); } } else { if (timePrint) fprintf(print_file,"% .5E%c", The_Time, TAB_CHAR); } while (tmp != NULL) { if (shortPrint) { shTmp = *tmp->vo1 - *tmp->vo2; fprintf(print_file,"% .5E", shTmp); } else fprintf(print_file,"% .5E", *tmp->vo1 - *tmp->vo2); if (tmp->next != NULL) fprintf(print_file,"%c",TAB_CHAR); else fprintf(print_file,"\n"); tmp = tmp->next; } } /*********** Model Specifications ********************/ void readModelSection(f, firstLine) FILE **f; char *firstLine; { /*.MODEL MODEL_NAME VAL1 VAL2 ... .. .END*/ card l; aWord tmp; nameString tmpName; modelRec *tmpModel; flex tmpV; long i; long symbol_count, position, op_amp_stub; /* RdH */ long model_size; model_size = 10; strcpy(l,firstLine); GetWORD(tmp,2L); strcpy(tmpName,tmp); tmpModel = baseOfModel; while (tmpModel != NULL) { if (strcmp(tmpModel->name, tmpName) == 0) errorReport(ERROR_DUPLICATE_MODEL, tmpName, ERR_VOID, 0); else tmpModel = tmpModel->next; } if (tmpModel != NULL) return; tmpModel = (modelRec *)c_malloc(sizeof(modelRec)); tmpModel->next = baseOfModel; MakeSymbolList(&tmpV,2); /* symbols only work for op amps */ baseOfModel = tmpModel; strcpy(baseOfModel->name,tmpName); /*start reading the data section*/ readCard(f, l); GetWORD(tmp,1L); /* this next line limits ADC size ... Grrr */ tmpV.UU.c = c_malloc(sizeof(double) * model_size); tmpV.dim = 0; op_amp_stub = 0; while (strncmp(tmp,".END",4) != 0) { position = 1; symbol_count = 0; while (tmp[0] != NULL_CHAR) { tmpV.dim++; if (tmpV.dim == model_size) { model_size += 10L; tmpV.UU.c = realloc(tmpV.UU.c,sizeof(double) * model_size); if (tmpV.UU.c == NULL) errorReport(ERROR_REALLOC_MODEL,0,ERR_VOID,0); } GetWORD(tmp,position); if ((isalpha(tmp[0])) && (op_amp_stub < 2)) { symbol_count++; tmpV.UU.x[tmpV.dim - 1] = parseSymbolModel(&tmpModel,tmpV.dim - 1,position++); } else tmpV.UU.x[tmpV.dim - 1] = gck_atod(tmp); position++; op_amp_stub++; GetWORD(tmp,position); /* stub, but necessary */ } readCard(f, l); GetWORD(tmp,1L); } baseOfModel->value.dim = tmpV.dim; baseOfModel->value.UU.c = c_malloc(sizeof(double) * tmpV.dim); for (i = 0; i < tmpV.dim; i++) baseOfModel->value.UU.x[i] = tmpV.UU.x[i]; if (tmpV.UU.c != NULL) free(tmpV.UU.c); } /********** Subcircuit *********/ void readXCard(l) char *l; { /*XYYYYYYY N1 N2 ... SUBCKT_NAME*/ branchRec *b; aWord tmp; long i, nc; aWord TEMP; b = (branchRec *)c_malloc(sizeof(branchRec)); GetWORD(tmp,1L); strcpy(b->name,tmp); b->element = subckt; nc = rcard.word_count - 2; /* number of nodes to pass to subckt */ MakeIDList(&b->ID, nc); for (i = 1; i <= nc; i++) b->ID.UU.n[i - 1] = nodeID(GetWORD(TEMP,i + 1)); GetWORD(tmp,nc + 2); strcpy(b->cname,tmp); b->next = baseOfBranch; baseOfBranch = b; } void substituteSubckt(b) branchRec **b; { long i, k, bn, newn; branchRec *tmp, *tmpC; subcktRec *s; long depth; char the_name[32]; depth = (*b)->depth + 1; strcpy(the_name,(*b)->name); GetSubcktByName(&s, (*b)->cname); if (s == NULL) errorReport(ERROR_UNRESOLVED_SUBCKT, (*b)->cname, ERR_VOID, 0); tmp = s->base; /*branch(es) to be copied*/ while (tmp != NULL) { if (tmp->element == subckt) { if (strcmp(tmp->cname,s->name) == 0) errorReport(ERROR_RECURSIVE_SUBCKT, s->name, ERR_VOID, 0); } tmpC = (branchRec *)c_malloc(sizeof(branchRec)); tmpC->element = tmp->element; /*obtain unique names by concanating to the 1st 8 characters of the subckt branch name the 1st 8 characters of the calling branch name (i.e. XYYY)*/ for (i = 0; i < NAME_HAF; i++) tmpC->name[i] = tmp->name[i]; for (i = NAME_HAF + 1; i <= NAME_LEN; i++) tmpC->name[i - 1] = (*b)->name[i - 5]; if (((1L << ((long)tmpC->element)) & globalControl) != 0) { for (i = 0; i < NAME_LEN; i++) tmpC->cname[i] = tmp->cname[i]; } else { for (i = 0; i < NAME_HAF; i++) tmpC->cname[i] = tmp->cname[i]; for (i = NAME_HAF + 1; i <= NAME_LEN; i++) tmpC->cname[i - 1] = (*b)->name[i - 5]; } /*no need to replicate the value list*/ tmpC->value = tmp->value; /*adjust the IDs*/ k = CloneID(tmp->ID, &tmpC->ID); for (i = 0; i < k; i++) { bn = GetID(tmpC->ID, i + 1); if (bn != 0) { if (bn <= s->NConn) /*external nodes*/ tmpC->ID.UU.n[i] = (*b)->ID.UU.n[bn - 1]; else { newn = NoOfVars + bn - s->NConn; tmpC->ID.UU.n[i] = newn; } } } /* RdH March 24, 1992 */ tmpC->depth = depth; strcpy(tmpC->cktname,the_name); tmpC->next = (*b)->next; (*b)->next = tmpC; tmp = tmp->next; } NoOfVars += s->NVar - s->NConn; } /************** Capacitor **************/ void MakeCapacitor(b) branchRec **b; { newBranch(b, 3L, 1L); (*b)->element = capacitor; } void readCapacitor(l) char *l; { /*CXXXX N+ N- VALUE*/ branchRec *b; MakeCapacitor(&b); read2NodeValue(&b, l); } /************** Inductor **************/ void MakeInductor(b) branchRec **b; { newBranch(b, 3L, 1L); (*b)->element = inductor; } void readInductor(l) char *l; { /*LXXXX N+ N- VALUE*/ branchRec *b; MakeInductor(&b); read2NodeValue(&b, l); } /************** Resistor **************/ void MakeResistor(b) branchRec **b; { newBranch(b, 3L, 1L); (*b)->element = resistor; } void readResistor(l) char *l; { /*RXXXX N+ N- VALUE*/ branchRec *b; MakeResistor(&b); read2NodeValue(&b, l); } /************** Sources **************/ void readSource(l) char *l; { /* vars help FILE implementation */ char tmpfn[64]; int nn; /* RdH December 18 1991 v0.2.0 changes reflected here */ /*VXXXX N+ N- SIN <amp> <freq> | DC <amp> | STDIN | FILE <filename> | RND <lim> | PULSE <unpulsed_amp> <pulsed_amp> <period> <delay> <rise_time> <duration> <fall_time> | IMPULSE <amplitude> | COMB <amp> <start_freq> <delta_freq> <no_of_freq> */ /*IXXXX N+ N- <source_specification> */ branchRec *b; aWord tmp, tmpd; long i; aWord TEMP; aWord T2; long position; long loop; long symbol_count; newBranch(&b, 3L, 1L); /* was 0L on 3rd parameter...3 Mar 92 RdH */ read2Node(&b, l); GetWORD(tmp,4L); if (strncmp(tmp,"SIN",3) == 0) { MakeValueList(&b->value, 3L); position = 5; symbol_count = 0; for (loop = 0; loop < 3; loop++) { GetWORD(T2,position); if ((loop == 2) && (T2[0] == NULL_CHAR)) b->value.UU.x[2] = 0.0; else if (isalpha(T2[0])) { symbol_count++; b->value.UU.x[loop] = parseSymbol(&b,loop,position++); } else b->value.UU.x[loop] = gck_atod(T2); position++; if ((loop == 2) && (T2[0] != NULL_CHAR)) symbol_count++; /* not really, just error checking! */ } strcpy(b->cname,"SIN"); if (rcard.word_count < (6 + symbol_count)) errorReport(ERROR_SIN_CARD_BROKEN,0,ERR_VOID,0); } else if (strncmp(tmp,"COMB",4) == 0) { MakeValueList(&b->value, 4L); position = 5; symbol_count = 0; for (loop = 0; loop < 4; loop++) { GetWORD(T2,position); if (isalpha(T2[0])) { symbol_count++; b->value.UU.x[loop] = parseSymbol(&b,loop,position++); } else b->value.UU.x[loop] = gck_atod(T2); position++; } strcpy(b->cname,"COMB"); if (rcard.word_count < (8 + symbol_count)) errorReport(ERROR_COMB_CARD_BROKEN,0,ERR_VOID,0); } else if (strncmp(tmp,"DC",2) == 0) { MakeValueList(&b->value, 1L); GetWORD(T2,5L); if (isalpha(T2[0])) b->value.UU.x[0] = parseSymbol(&b,0,5L); else b->value.UU.x[0] = gck_atod(T2); strcpy(b->cname,"DC"); } else if (strncmp(tmp,"IMPULSE",7) == 0) { MakeValueList(&b->value, 1L); GetWORD(T2,5L); if (isalpha(T2[0])) b->value.UU.x[0] = parseSymbol(&b,0,5L); else b->value.UU.x[0] = gck_atod(T2); strcpy(b->cname,"IMPULSE"); } else if (strncmp(tmp,"RND",3) == 0) { MakeValueList(&b->value, 1L); GetWORD(T2,5L); if (isalpha(T2[0])) b->value.UU.x[0] = parseSymbol(&b,0,5L); else b->value.UU.x[0] = gck_atod(T2); strcpy(b->cname,"RND"); } else if (strncmp(tmp,"STDIN",5) == 0) strcpy(b->cname,"STDIN"); else if (strncmp(tmp,"FILE",4) == 0) { MakeValueList(&b->value, 32L); strcpy(tmpfn,GetWord(TEMP,5L)); strcpy(&(b->value.UU.w[0]),tmpfn); strcpy(b->cname,"FILE"); } else if (strncmp(tmp, "PULSE",5) == 0) { MakeValueList(&b->value, 7L); position = 5; symbol_count = 0; for (loop = 0; loop < 7; loop++) { GetWORD(T2,position); if (isalpha(T2[0])) { symbol_count++; b->value.UU.x[loop] = parseSymbol(&b,loop,position++); } else b->value.UU.x[loop] = gck_atod(T2); position++; } strcpy(b->cname,"PULSE"); if (rcard.word_count < (11 + symbol_count)) errorReport(ERROR_PULSE_CARD_BROKEN,0,ERR_VOID,0); } else errorReport(ERROR_UNRESOLVED_SOURCE, progName, ERR_VOID, 0); GetWORD(tmp,1L); if (tmp[0] == 'v' || tmp[0] == 'V') b->element = vs; else if (tmp[0] == 'i' || tmp[0] == 'I') b->element = cs; b->next = baseOfBranch; baseOfBranch = b; } void CreateSourceList() { /* This function is completely new, in hopes that it will assist in shattering the MaX_VARS requirement ... 1 Mar 92 RdH */ /* Now that the number of Vars is known, create an array for them */ long i, FORLIM; FORLIM = NoOfVars; vv_s = (double *) c_malloc((NoOfVars + 1) * sizeof(double)); /* limits should include "NoOfVars + 1"-th element */ for (i = 0; i <= FORLIM; i++) { /* Init not really necessary */ vv_s[i] = 0.0; } } void SetUpSources() { branchRec *b; sourceRec *tmpS; long i, dummy; sourceRec *WITH; b = baseOfBranch; while (b != NULL) { if (((1L << ((long)b->element)) & ((1L << ((long)vs)) | (1L << ((long)cs)))) != 0) { tmpS = (sourceRec *)c_malloc(sizeof(sourceRec)); tmpS->next = baseOfSource; baseOfSource = tmpS; if (!strncmp(b->cname, "SIN", sizeof(nameString))) { baseOfSource->signal = sinusoid; if (b->value.sv[0].is_symbol) baseOfSource->VV.U0.amp = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.U0.amp = b->value.UU.x[0]; if (b->value.sv[1].is_symbol) baseOfSource->VV.U0.freq = TWOPI * getSymbolValueByName(b->value.sv[1].sName); else baseOfSource->VV.U0.freq = TWOPI * b->value.UU.x[1]; if (b->value.sv[2].is_symbol) baseOfSource->VV.U0.dly = getSymbolValueByName(b->value.sv[2].sName); else baseOfSource->VV.U0.dly = b->value.UU.x[2]; } else if (!strncmp(b->cname, "COMB", sizeof(nameString))) { baseOfSource->signal = comb; if (b->value.sv[0].is_symbol) baseOfSource->VV.U5.amp0 = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.U5.amp0 = b->value.UU.x[0]; if (b->value.sv[1].is_symbol) baseOfSource->VV.U5.freq0 = TWOPI * getSymbolValueByName(b->value.sv[1].sName); else baseOfSource->VV.U5.freq0 = TWOPI * b->value.UU.x[1]; if (b->value.sv[2].is_symbol) baseOfSource->VV.U5.deltaF = TWOPI * getSymbolValueByName(b->value.sv[2].sName); else baseOfSource->VV.U5.deltaF = TWOPI * b->value.UU.x[2]; if (b->value.sv[3].is_symbol) baseOfSource->VV.U5.noFreq = (long) floor(getSymbolValueByName(b->value.sv[3].sName) + 0.5); else baseOfSource->VV.U5.noFreq = (long) floor(b->value.UU.x[3] + 0.5); } else if (!strncmp(b->cname, "DC", sizeof(nameString))) { baseOfSource->signal = dc; if (b->value.sv[0].is_symbol) baseOfSource->VV.A = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.A = b->value.UU.x[0]; } else if (!strncmp(b->cname, "IMPULSE", sizeof(nameString))) { baseOfSource->signal = impulse; if (b->value.sv[0].is_symbol) baseOfSource->VV.A = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.A = b->value.UU.x[0]; } else if (!strncmp(b->cname, "STDIN", sizeof(nameString))) { baseOfSource->signal = fromStdin; terminate_on_eof = TRUE; } else if (!strncmp(b->cname, "FILE", sizeof(nameString))) { baseOfSource->signal = fromFile; terminate_on_eof = TRUE; baseOfSource->VV.U7.FilePtr = fopen(&(b->value.UU.w[0]),"r"); if (baseOfSource->VV.U7.FilePtr == 0) errorReport(ERROR_INPUT_FILE_NOT_FOUND,&(b->value.UU.w[0]),ERR_VOID,0); } else if (!strncmp(b->cname, "RND", sizeof(nameString))) { baseOfSource->signal = rndm; if (b->value.sv[0].is_symbol) baseOfSource->VV.L = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.L = b->value.UU.x[0]; } else if (!strncmp(b->cname, "PULSE", sizeof(nameString))) { baseOfSource->signal = pulse; if (b->value.sv[0].is_symbol) baseOfSource->VV.U3.unpls = getSymbolValueByName(b->value.sv[0].sName); else baseOfSource->VV.U3.unpls = b->value.UU.x[0]; if (b->value.sv[1].is_symbol) baseOfSource->VV.U3.pls = getSymbolValueByName(b->value.sv[1].sName); else baseOfSource->VV.U3.pls = b->value.UU.x[1]; if (b->value.sv[2].is_symbol) baseOfSource->VV.U3.T = getSymbolValueByName(b->value.sv[2].sName); else baseOfSource->VV.U3.T = b->value.UU.x[2]; if (b->value.sv[3].is_symbol) baseOfSource->VV.U3.td = getSymbolValueByName(b->value.sv[3].sName); else baseOfSource->VV.U3.td = b->value.UU.x[3]; if (b->value.sv[4].is_symbol) baseOfSource->VV.U3.tr = getSymbolValueByName(b->value.sv[4].sName); else baseOfSource->VV.U3.tr = b->value.UU.x[4]; if (b->value.sv[5].is_symbol) baseOfSource->VV.U3.tp = getSymbolValueByName(b->value.sv[5].sName); else baseOfSource->VV.U3.tp = b->value.UU.x[5]; if (b->value.sv[6].is_symbol) baseOfSource->VV.U3.tf = getSymbolValueByName(b->value.sv[6].sName); else baseOfSource->VV.U3.tf = b->value.UU.x[6]; WITH = baseOfSource; if (WITH->VV.U3.tr != 0) WITH->VV.U3.mr = (WITH->VV.U3.pls - WITH->VV.U3.unpls) / WITH->VV.U3.tr; if (WITH->VV.U3.tf != 0) /* was mf ... since Pascal ... RdH */ WITH->VV.U3.mf = (WITH->VV.U3.unpls - WITH->VV.U3.pls) / WITH->VV.U3.tf; } else errorReport(ERROR_UNRESOLVED_SOURCE, progName, ERR_VOID, 0); baseOfSource->branch = b; i = b->ID.UU.n[0]; baseOfSource->value = &vv_s[i]; } b = b->next; } /* RdH Modified the following: the line: dummy := seed(wallclock); was changed to dummy := 0; the current compiler / system does not recognize neither the seed function, or the "wallclock" variable..... I must look into this more. */ dummy = 0; } void RunSources() { char dead_char; /* RdH */ sourceRec *tmpS; double tmp; long i, FORLIM; double t; tmpS = baseOfSource; while (tmpS != NULL) { switch (tmpS->signal) { case sinusoid: *tmpS->value = tmpS->VV.U0.amp * sin(tmpS->VV.U0.freq * (The_Time - tmpS->VV.U0.dly)); break; case comb: tmp = sin(tmpS->VV.U5.freq0 * The_Time); FORLIM = tmpS->VV.U5.noFreq; for (i = 2; i <= FORLIM; i++) tmp += sin((tmpS->VV.U5.freq0 + i * tmpS->VV.U5.deltaF) * The_Time); *tmpS->value = tmpS->VV.U5.amp0 * tmp; break; case dc: *tmpS->value = tmpS->VV.A; break; case impulse: if (The_Time == 0) *tmpS->value = tmpS->VV.A; else *tmpS->value = 0.0; break; case fromStdin: if (!P_eof(stdin)) { scanf("%lg%*[^\n]", tmpS->value); dead_char = getchar(); } if (P_eof(stdin)) terminate = TRUE; break; case fromFile: if (!feof(tmpS->VV.U7.FilePtr)) { fscanf(tmpS->VV.U7.FilePtr,"%lg%*[^\n]", tmpS->value); } if (feof(tmpS->VV.U7.FilePtr)) terminate = TRUE; break; case pulse: t = The_Time - floor(The_Time / tmpS->VV.U3.T) * tmpS->VV.U3.T; if (t < tmpS->VV.U3.td) *tmpS->value = tmpS->VV.U3.unpls; else if (t >= tmpS->VV.U3.td + tmpS->VV.U3.tr + tmpS->VV.U3.tp + tmpS->VV.U3.tf) *tmpS->value = tmpS->VV.U3.unpls; else if (t >= tmpS->VV.U3.td + tmpS->VV.U3.tr && t < tmpS->VV.U3.td + tmpS->VV.U3.tr + tmpS->VV.U3.tp) *tmpS->value = tmpS->VV.U3.pls; else if (t >= tmpS->VV.U3.td && t < tmpS->VV.U3.td + tmpS->VV.U3.tr) *tmpS->value = (t - tmpS->VV.U3.td) * tmpS->VV.U3.mr + tmpS->VV.U3.unpls; else if (t >= tmpS->VV.U3.td + tmpS->VV.U3.tr + tmpS->VV.U3.tp && t < tmpS->VV.U3.td + tmpS->VV.U3.tr + tmpS->VV.U3.tp + tmpS->VV.U3.tf) *tmpS->value = (t - tmpS->VV.U3.td - tmpS->VV.U3.tr - tmpS->VV.U3.tp) * tmpS->VV.U3.mf + tmpS->VV.U3.pls; break; case rndm: *tmpS->value = gck_random() * tmpS->VV.L; break; } tmpS = tmpS->next; } } /************** VCVS **************/ void MakeVCVS(b) branchRec **b; { newBranch(b, 5L, 1L); (*b)->element = vcvs; } void readVCVS(l) char *l; { /*EXXXX N+ N- NC+ NC- VALUE*/ branchRec *b; MakeVCVS(&b); read4NodeValue(&b, l); } /************** VCCS **************/ void MakeVCCS(b) branchRec **b; { newBranch(b, 5L, 1L); (*b)->element = vccs; } void readVCCS(l) char *l; { /*GXXXX N+ N- NC+ NC- VALUE*/ branchRec *b; MakeVCCS(&b); read4NodeValue(&b, l); } /************** CCVS **************/ void MakeCCVS(b) branchRec **b; { newBranch(b, 4L, 1L); (*b)->element = ccvs; /* OH CRAP ? A BUG ? RdH Was "vccs" */ } void readCCVS(l) char *l; { /*HYYY N+ N- CONTROL_NAME VALUE*/ aWord tmp; branchRec *b; MakeCCVS(&b); read2NodeName(&b, l); GetWORD(tmp,5L); if (isalpha(tmp[0])) b->value.UU.x[0] = parseSymbol(&b,0,5L); else b->value.UU.x[0] = gck_atod(tmp); } /************** CCCS **************/ void MakeCCCS(b) branchRec **b; { newBranch(b, 4L, 1L); (*b)->element = cccs; } void readCCCS(l) char *l; { /*FYYY N+ N- CONTROL_NAME VALUE*/ aWord tmp; branchRec *b; MakeCCCS(&b); read2NodeName(&b, l); GetWORD(tmp,5L); if (isalpha(tmp[0])) b->value.UU.x[0] = parseSymbol(&b,0,5L); else b->value.UU.x[0] = gck_atod(tmp); } /************** Switch **************/ void MakeSwitch(b) branchRec **b; { /* value points to a clock sequence */ newBranch(b, 3L, 1L); /* was 0L on 3rd parameter...3 Mar 92 RdH */ (*b)->element = switch_; } void readSwitch(l) char *l; { /*SXXXX N+ N- CLOCK_NAME*/ branchRec *b; MakeSwitch(&b); read2NodeName(&b, l); } void SetUpClock(b) branchRec **b; { clockRec *clk; GetClockByName(&clk, (*b)->cname); (*b)->value = clk->value; } void SetUpModel(b) branchRec **b; { modelRec *mdl; GetModelByName(&mdl, (*b)->cname); (*b)->value = mdl->value; } /************** Opamp **************/ void MakeOpamp(b) branchRec **b; { newBranch(b, 5L, 1L); /* was 0L on 3rd parameter...3 Mar 92 RdH */ /* value_1 = gain, value_2 = slew rate a special case of vcvs */ (*b)->element = opamp; } void readOpamp(l) char *l; { /*OXXXX N+ N- NC+ NC- MODEL_NAME*/ branchRec *b; MakeOpamp(&b); read4NodeName(&b, l); } void RunOpamp(op) nonlinRec **op; { double tmp1, tmp2, sr; sr = (*op)->branch->value.UU.x[1] * deltaT; tmp1 = *(*op)->out1 - *GND - (*op)->xdata1; tmp2 = *(*op)->out2 - *GND - (*op)->xdata2; *(*op)->src = 0.0; if (fabs(tmp1) > sr) { if (tmp1 > 0) *(*op)->out1 = (*op)->xdata1 + sr; else *(*op)->out1 = (*op)->xdata1 - sr; } if (fabs(tmp2) > sr) { if (tmp2 > 0) *(*op)->out2 = (*op)->xdata2 + sr; else *(*op)->out2 = (*op)->xdata2 - sr; } (*op)->xdata1 = *(*op)->out1; (*op)->xdata2 = *(*op)->out2; } /************** Quantizer **************/ void MakeQuantizer(b) branchRec **b; { /* upper threshold and output level */ newBranch(b, 5L, 1L); /* was 0L on 3rd parameter...3 Mar 92 RdH */ (*b)->element = quantizer; } void readQuantizer(l) char *l; { /*QXXXX N+ N- NC+ NC- MODEL_NAME*/ branchRec *b; MakeQuantizer(&b); read4NodeName(&b, l); } void RunQuantizer(qt) nonlinRec **qt; { double tmpIn; boolean done; long i; tmpIn = *(*qt)->in1 - *(*qt)->in2; i = 1; done = FALSE; while (!done && i <= (*qt)->branch->value.dim) { if (tmpIn >= (*qt)->branch->value.UU.x[i - 1]) done = TRUE; i++; if (i > (*qt)->branch->value.dim) errorReport(ERROR_QUANTIZER, (*qt)->branch->name, ERR_VOID, 0); if (done) *(*qt)->src = (*qt)->branch->value.UU.x[i - 1]; } } /************* General Nonlinear Stuff ****************/ void MakeNonlinList() { /*goes through the completed branch list to find nonlinear elements*/ nonlinRec *tmpNonlin; branchRec *tmpBranch; modelRec *tmpModel; tmpBranch = baseOfBranch; while (tmpBranch != NULL) { switch (tmpBranch->element) { case opamp: if (tmpBranch->value.dim > 1) { tmpNonlin = (nonlinRec *)c_malloc(sizeof(nonlinRec)); GetModelByName(&tmpModel, tmpBranch->cname); tmpBranch->value.dim = tmpModel->value.dim; /* op amp gain */ if (tmpModel->value.sv[0].is_symbol) tmpBranch->value.UU.x[0] = getSymbolValueByName(tmpModel->value.sv[0].sName); else tmpBranch->value.UU.x[0] = tmpModel->value.UU.x[0]; /* slew rate */ if (tmpModel->value.sv[0].is_symbol) tmpBranch->value.UU.x[1] = getSymbolValueByName(tmpModel->value.sv[1].sName); else tmpBranch->value.UU.x[1] = tmpModel->value.UU.x[1]; tmpNonlin->branch = tmpBranch; tmpNonlin->next = baseOfNonlin; baseOfNonlin = tmpNonlin; } break; case quantizer: tmpNonlin = (nonlinRec *)c_malloc(sizeof(nonlinRec)); GetModelByName(&tmpModel, tmpBranch->cname); tmpBranch->value.dim = tmpModel->value.dim; /* might have to fix ... RdH */ tmpBranch->value.UU.x = tmpModel->value.UU.x; tmpNonlin->branch = tmpBranch; tmpNonlin->next = baseOfNonlin; baseOfNonlin = tmpNonlin; /* var_vector_s[tmpBranch->ID.UU.n[0]] = (double *)c_malloc(sizeof(double)); */ /* call is redundant now ... RdH 1 Mar 92 */ break; } tmpBranch = tmpBranch->next; } } void SetUpNonlins() { nonlinRec *tmpNon; long bn, n1, n2, nc1, nc2; tmpNon = baseOfNonlin; while (tmpNon != NULL) { bn = tmpNon->branch->ID.UU.n[0]; n1 = tmpNon->branch->ID.UU.n[1]; n2 = tmpNon->branch->ID.UU.n[2]; nc1 = tmpNon->branch->ID.UU.n[3]; nc2 = tmpNon->branch->ID.UU.n[4]; tmpNon->src = &vv_s[bn]; tmpNon->out1 = &vv_x[n1]; tmpNon->out2 = &vv_x[n2]; tmpNon->in1 = &vv_x[nc1]; tmpNon->in2 = &vv_x[nc2]; tmpNon = tmpNon->next; } } void RunNonlins() { nonlinRec *tmpN; tmpN = baseOfNonlin; while (tmpN != NULL) { switch (tmpN->branch->element) { case opamp: RunOpamp(&tmpN); break; case quantizer: RunQuantizer(&tmpN); break; } tmpN = tmpN->next; } } /********** Digital Stuff **********/ /************** Delay **************/ void MakeDelay(b) branchRec **b; { newBranch(b, 5L, 1L); (*b)->element = delay; } void readDelay(l) char *l; { /*@DXXXX N_OUT N_IN NO_OF_TICKS_OF_DELAY*/ branchRec *b; long nr; aWord tmpW; MakeDelay(&b); read2NodeValue(&b, l); b->ID.UU.n[3] = b->ID.UU.n[2]; b->ID.UU.n[2] = 0; b->ID.UU.n[4] = 0; b->value.UU.x[0] = 0.0; GetWORD(tmpW,4L); if (isalpha(tmpW[0])) { nr = (long) parseSymbol(&b,0,4L); } else nr = atol(tmpW); if (nr <= 0) errorReport(ERROR_DELAY_ZERO, b->name, ERR_VOID, 0); b->value.dim = nr; b->value.UU.c = c_malloc(sizeof(double) * nr); } void SetUpDelay(dl) digitalRec **dl; { long bn, nc1; long new_size; long counter; /* get new dimension from symbol */ if ((*dl)->branch->value.sv[0].is_symbol) { new_size = (long) (*dl)->branch->value.sv[0].delay; /* reallocate size */ if (new_size != (*dl)->branch->value.dim) { (*dl)->branch->value.UU.c = realloc((*dl)->branch->value.UU.c,sizeof(double) * new_size); if ((*dl)->branch->value.UU.c == NULL) errorReport(ERROR_REALLOC_DELAY,0,ERR_VOID,0); /* causes realloc to "calloc" new region */ for (counter = (*dl)->branch->value.dim; counter < new_size; counter++) (*dl)->branch->value.UU.x[counter] = (double) 0.0; (*dl)->branch->value.dim = new_size; } } bn = (*dl)->branch->ID.UU.n[0]; nc1 = (*dl)->branch->ID.UU.n[3]; (*dl)->out1 = &vv_s[bn]; if (nc1 == 0) (*dl)->in1 = GND; else (*dl)->in1 = &vv_x[nc1]; } void RunDelay(dl) digitalRec **dl; { long i; for (i = (*dl)->branch->value.dim; i >= 2; i--) (*dl)->branch->value.UU.x[i - 1] = (*dl)->branch->value.UU.x[i - 2]; (*dl)->branch->value.UU.x[0] = *(*dl)->in1; *(*dl)->out1 = (*dl)->branch->value.UU.x[(*dl)->branch->value.dim - 1]; } /************** Adder **************/ void MakeAdder(b) branchRec **b; { newBranch(b, 5L, 2L); (*b)->element = adder; } void readAdder(l) char *l; { /*@AXXXX N_OUT N_IN1 N_IN2 VAL_1 VAL_2*/ branchRec *b; aWord tmp; long i; aWord TEMP,T2; long position, symbol_count, loop; MakeAdder(&b); GetWORD(tmp,1L); strcpy(b->name,tmp); b->ID.UU.n[0] = branchID(); b->ID.UU.n[1] = nodeID(GetWORD(TEMP,2L)); b->ID.UU.n[2] = 0; b->ID.UU.n[3] = nodeID(GetWORD(TEMP,3L)); b->ID.UU.n[4] = nodeID(GetWORD(TEMP,4L)); position = 5; symbol_count = 0; for (loop = 0; loop < 2; loop++) { GetWORD(T2,position); if (isalpha(T2[0])) { symbol_count++; b->value.UU.x[loop] = parseSymbol(&b,loop,position++); } else b->value.UU.x[loop] = gck_atod(T2); position++; } if (rcard.word_count < (6 + symbol_count)) errorReport(ERROR_ADDER_CARD_BROKEN,0,ERR_VOID,0); b->next = baseOfBranch; baseOfBranch = b; } /************** Multiplier **************/ void MakeMult(b) branchRec **b; { newBranch(b, 5L, 2L); (*b)->element = multiplier; } void readMult(l) char *l; { /*@MXXXX N_OUT N_IN1 N_IN2*/ branchRec *b; aWord tmp; long i; aWord TEMP; MakeMult(&b); GetWORD(tmp,1L); strcpy(b->name,tmp); b->ID.UU.n[0] = branchID(); b->ID.UU.n[1] = nodeID(GetWORD(TEMP,2L)); b->ID.UU.n[2] = 0; b->ID.UU.n[3] = nodeID(GetWORD(TEMP,3L)); b->ID.UU.n[4] = nodeID(GetWORD(TEMP,4L)); b->next = baseOfBranch; baseOfBranch = b; } void SetUpMult(mlt) digitalRec **mlt; { long bn, nc1, nc2; bn = (*mlt)->branch->ID.UU.n[0]; nc1 = (*mlt)->branch->ID.UU.n[3]; nc2 = (*mlt)->branch->ID.UU.n[4]; /* var_vector_s[bn] = (double *)c_malloc(sizeof(double)); */ /* (*mlt)->out1 = var_vector_s[bn]; */ (*mlt)->out1 = &vv_s[bn]; if (nc1 == 0) (*mlt)->in1 = GND; else /* (*mlt)->in1 = var_vector_x[nc1]; */ (*mlt)->in1 = &vv_x[nc1]; if (nc2 == 0) (*mlt)->in2 = GND; else /* (*mlt)->in2 = var_vector_x[nc2]; */ (*mlt)->in2 = &vv_x[nc2]; } void RunMult(mlt) digitalRec **mlt; { *(*mlt)->out1 = *(*mlt)->in1 * *(*mlt)->in2; } void readDigital(l) char *l; { aWord tmp; GetWORD(tmp,1L); switch (tmp[1]) { case 'D': readDelay(l); break; case 'A': readAdder(l); break; case 'M': readMult(l); break; default: errorReport(ERROR_DIGITAL_UNKNOWN, progName, ERR_VOID, 0); break; } } void MakeDigitalList() { digitalRec *tmpDigital; branchRec *tmpBranch; tmpBranch = baseOfBranch; while (tmpBranch != NULL) { if (((1L << ((long)tmpBranch->element)) & digitals) != 0) { tmpDigital = (digitalRec *)c_malloc(sizeof(digitalRec)); tmpDigital->branch = tmpBranch; tmpDigital->next = baseOfDigital; baseOfDigital = tmpDigital; } tmpBranch = tmpBranch->next; } } void SetUpDigital() { digitalRec *tmpDigital; tmpDigital = baseOfDigital; while (tmpDigital != NULL) { switch (tmpDigital->branch->element) { case delay: SetUpDelay(&tmpDigital); break; case multiplier: SetUpMult(&tmpDigital); break; } tmpDigital = tmpDigital->next; } } void RunDigital() { digitalRec *tmpN; tmpN = baseOfDigital; while (tmpN != NULL) { switch (tmpN->branch->element) { case delay: RunDelay(&tmpN); break; case multiplier: RunMult(&tmpN); break; } tmpN = tmpN->next; } } /***********************************************************/ /************* LOGIC ELEMENTS ******************************/ void readLogical(l) char *l; { /*$YYYY n_out n_0 n_1 .. n_M <table_name> <0|1> <v_th> <v_low> <v_high>*/ long i, bn, n_out, nn; iArray n_in; branchRec *b; aWord tmp, TEMP; long FORLIM; long position; long symbol_count; /* figure out where the '1' or '0' is. */ /* minimum position for this is position 5. An inverter */ for (position = 6; position < rcard.word_count; position++) { if ((strcmp(rcard.WORD[position-1],"1") == 0) || (strcmp(rcard.WORD[position-1],"0") == 0) || (strcmp(rcard.WORD[position-1],"H") == 0) || (strcmp(rcard.WORD[position-1],"L") == 0)) break; } if (position >= rcard.word_count) errorReport(ERROR_LOGIC_CARD_BROKEN,0,ERR_VOID,0); GetWORD(tmp,1L); b = (branchRec *)c_malloc(sizeof(branchRec)); strcpy(b->name,tmp); b->element = l_general; bn = branchID(); n_out = nodeID(GetWORD(TEMP,2L)); nn = 1; GetWORD(tmp,nn + 2); while (nn <= (position - 4)) { n_in[nn - 1] = nodeID(tmp); nn++; GetWORD(tmp,nn + 2); } strcpy(b->cname,tmp); /*bn, n_out, GND, nn-1 input nodes -> nn+2 nodes*/ MakeIDList(&b->ID, nn + 2); b->ID.UU.n[0] = bn; b->ID.UU.n[1] = n_out; b->ID.UU.n[2] = 0; FORLIM = b->ID.dim; for (i = 4; i <= FORLIM; i++) b->ID.UU.n[i - 1] = n_in[i - 4]; MakeValueList(&b->value, 4L); GetWORD(TEMP,nn + 3); if ((TEMP[0] == 'H') || (TEMP[0] == '1')) b->value.UU.x[0] = 1.0; else b->value.UU.x[0] = 0.0; symbol_count = 0; for (i = 2; i <= 4; i++) { GetWORD(TEMP,nn + i + 2 + symbol_count); if (isalpha(TEMP[0])) b->value.UU.x[i - 1] = parseSymbol(&b,i - 1,nn + i + 2 + symbol_count++); else b->value.UU.x[i - 1] = gck_atod(GetWORD(TEMP,nn + i + 2 + symbol_count)); } b->next = baseOfBranch; baseOfBranch = b; } void readTable(f, firstLine) FILE **f; char *firstLine; { /*.TABLE <table_name> 01..11 11..01 . . .END*/ card l; aWord tmp; nameString tmpName; tableRec *tmpTable; iArray tmpEntry; long i, j, r, FORLIM; strcpy(l,firstLine); GetWORD(tmp,2L); strcpy(tmpName,tmp); tmpTable = baseOfTable; while (tmpTable != NULL) { if (strcmp(tmpTable->name, tmpName) == 0) errorReport(ERROR_DUPLICATE_TABLE, tmpName, ERR_VOID, 0); else tmpTable = tmpTable->next; } tmpTable = NULL; tmpTable = (tableRec *)c_malloc(sizeof(tableRec)); tmpTable->next = baseOfTable; baseOfTable = tmpTable; strcpy(baseOfTable->name,tmpName); readCard(f, l); GetWORD(tmp,1L); i = 0; while (strncmp(tmp,".END",4) != 0) { i++; j = 1; r = 0; while (tmp[j - 1] != NULL_CHAR) { if (tmp[j - 1] == '1') r += binaryArray[j - 1]; j++; } tmpEntry[i - 1] = r; readCard(f, l); GetWORD(tmp,1L); } baseOfTable->data.dim = i; baseOfTable->data.UU.c = c_malloc(sizeof(long) * i); FORLIM = baseOfTable->data.dim; for (j = 0; j < FORLIM; j++) baseOfTable->data.UU.n[j] = tmpEntry[j]; } void GetTableByName(there, mname) tableRec **there; char *mname; { /*Finds the table with name mname in the linked list*/ boolean found; *there = baseOfTable; found = FALSE; while (*there != NULL && !found) { if (strcmp((*there)->name, mname) == 0) found = TRUE; else *there = (*there)->next; } if (!found) errorReport(ERROR_UNRESOLVED_TABLE, mname, ERR_VOID, 0); } void MakeLogicalList() { logicRec *tmpLogical; branchRec *tmpBranch; tableRec *tmpTable; long bn; long counter; tmpBranch = baseOfBranch; while (tmpBranch != NULL) { if (((1L << ((long)tmpBranch->element)) & logicals) != 0) { tmpLogical = (logicRec *)c_malloc(sizeof(logicRec)); tmpLogical->branch = tmpBranch; bn = GetID(tmpLogical->branch->ID, 1L); /*if (var_vector_s[bn] == NULL) var_vector_s[bn] = (double *)c_malloc(sizeof(double)); */ /* changed dramatically */ if (bn > NoOfVars) errorReport(ERROR_LOGICAL_LIST_COMPILER_ERROR,0,ERR_VOID,0); tmpLogical->out = &vv_s[bn]; GetTableByName(&tmpTable, tmpBranch->cname); tmpLogical->truthTable = tmpTable->data; if (tmpBranch->value.UU.x[0] != 0) tmpLogical->default_out = TRUE; else tmpLogical->default_out = FALSE; /* update symbols */ for (counter = 1; counter <= 3; counter++) if (tmpBranch->value.sv[counter].is_symbol) tmpBranch->value.UU.x[counter] = getSymbolValueByName(tmpBranch->value.sv[counter].sName); tmpLogical->next = baseOfLogic; baseOfLogic = tmpLogical; } tmpBranch = tmpBranch->next; } } void RunLogical() { logicRec *tmpL; long i, inSig; double vth, vout; boolean done; long FORLIM; tmpL = baseOfLogic; while (tmpL != NULL) { inSig = 0; vth = tmpL->branch->value.UU.x[1]; FORLIM = tmpL->branch->ID.dim; for (i = 4; i <= FORLIM; i++) { /* if (*var_vector_x[tmpL->branch->ID.UU.n[i - 1]] > vth) */ if (vv_x[tmpL->branch->ID.UU.n[i - 1]] > vth) inSig += binaryArray[i - 4]; } if (tmpL->default_out) vout = tmpL->branch->value.UU.x[2]; else vout = tmpL->branch->value.UU.x[3]; i = 1; done = FALSE; while (i <= tmpL->truthTable.dim && !done) { if (inSig == tmpL->truthTable.UU.n[i - 1]) { done = TRUE; if (tmpL->default_out) vout = tmpL->branch->value.UU.x[3]; else vout = tmpL->branch->value.UU.x[2]; } i++; } *tmpL->out = vout; tmpL = tmpL->next; } } /************* Building of the system matrices ******************/ void SetUpCircuit() { branchRec *b; dsymbol_base(); b = baseOfBranch; while (b != NULL) { if (b->element == subckt) { substituteSubckt(&b); } expandSymbols(&b); b = b->next; } b = baseOfBranch; while (b != NULL) { switch (b->element) { case switch_: SetUpClock(&b); break; case opamp: case quantizer: SetUpModel(&b); break; } b = b->next; } /* update symbols for step, period, time */ if (deltaT_sv.is_symbol) deltaT = getSymbolValueByName(deltaT_sv.sName); if (periodT_sv.is_symbol) periodT = getSymbolValueByName(periodT_sv.sName); if (lastTime_sv.is_symbol) lastTime = getSymbolValueByName(lastTime_sv.sName); if (periodT > 0) deltaT = periodT / NoOfTicks; } char *componentType_NAMES[] = { "RESISTOR", "CAPACITOR", "INDUCTOR", "SWITCH", "VS", "CS", "COUPLEDINDUCTOR", "VCSW", "VCVS", "VCCS", "CCVS", "CCCS", "OPAMP", "COMPARATOR", "QUANTIZER", "SUBCKT", "DELAY", "ADDER", "MULTIPLIER", "L_GENERAL" } ; /* RdH Prototype for Diagnostic Changed, due to new memory management * system for M, N, and B....It appears this is just debug code anyways. */ void Diagnostic() /* double (*M)[], (*N)[], (*B)[]; */ { long i, j; /* RdH ... Due to case insensitivity, the variable b: branchPtr is replaced with bptr...removes collisions! */ branchRec *bptr; float v; long FORLIM, FORLIM1; fprintf(stderr,"M = \n"); FORLIM = NoOfVars; for (i = 0; i < FORLIM; i++) { FORLIM1 = NoOfVars; for (j = 0; j < FORLIM1; j++) { v = M[i][j]; if (v != (double) 0.0) /* RdH */ fprintf(stderr,"% .5E%c", v, TAB_CHAR); } fprintf(stderr,"\n"); } fprintf(stderr,"\nN = \n"); FORLIM = NoOfVars; for (i = 0; i < FORLIM; i++) { FORLIM1 = NoOfVars; for (j = 0; j < FORLIM1; j++) { v = N[i][j]; if (v != (double) 0.0) /* RdH */ fprintf(stderr,"% .5E%c", v, TAB_CHAR); } fprintf(stderr,"\n"); } fprintf(stderr,"\nB = \n"); FORLIM = NoOfVars; for (i = 0; i < FORLIM; i++) { FORLIM1 = NoOfVars; for (j = 0; j < FORLIM1; j++) { v = B[i][j]; if (v != (double) 0.0) /* RdH */ fprintf(stderr,"% .5E%c", v, TAB_CHAR); } fprintf(stderr,"\n"); } bptr = baseOfBranch; while (bptr != NULL) { fprintf(stderr,"Branch : %.8s\n", bptr->name); fprintf(stderr," Type : %s\n", componentType_NAMES[(long)bptr->element]); fprintf(stderr," Branch no: %12ld\n", GetID(bptr->ID, 1L)); fprintf(stderr," Pos. node: %12ld\n", GetID(bptr->ID, 2L)); fprintf(stderr," Neg. node: %12ld\n", GetID(bptr->ID, 3L)); bptr = bptr->next; } } void SetUpVariables() { long i, FORLIM; FORLIM = NoOfVars; vv_x = (double *) c_malloc((NoOfVars + 1) * sizeof(double)); for (i = 1; i <= FORLIM; i++) { vv_x[i] = 0.0; } vv_x[0] = *GND; } void RunSystem(ps) systemRec **ps; { long i, j; /* sysVector y; 1 Mar 92 RdH */ double *y; double N_i_j, B_i_j; long FORLIM, FORLIM1; y = (double *) c_malloc(NoOfVars * sizeof(double)); FORLIM = NoOfVars; for (i = 0; i < FORLIM; i++) { y[i] = 0.0; FORLIM1 = NoOfVars; for (j = 0; j < FORLIM1; j++) { N_i_j = (*ps)->N[i][j]; if (N_i_j != 0) y[i] += N_i_j * vv_x[j + 1]; } FORLIM1 = NoOfVars; for (j = 0; j < FORLIM1; j++) { B_i_j = (*ps)->B[i][j]; if (B_i_j != 0) y[i] += B_i_j * vv_s[j + 1]; } } FORLIM = NoOfVars; for (i = 1; i <= FORLIM; i++) vv_x[i] = y[i - 1]; free(y); /* RdH 1 Mar 92 */ } /***** Reading the input file *****/ void readInputFile() { card l; aWord tmp; strcpy(tmp,rfile.filename); strcat(tmp,".gck"); object_stamp = FileAge(tmp); if (object_stamp == 0) up_to_date = FALSE; if (FileAge(rfile.filename) > object_stamp) up_to_date = FALSE; do { do { readCard(&(rfile.filePtr[rfile.depth]), l); GetWORD(tmp,1L); switch (tmp[0]) { case 'C': readCapacitor(l); break; case 'L': readInductor(l); break; case 'R': readResistor(l); break; case 'O': readOpamp(l); break; case 'Q': readQuantizer(l); break; case 'S': readSwitch(l); break; case 'V': case 'I': readSource(l); break; case 'E': readVCVS(l); break; case 'G': readVCCS(l); break; case 'H': readCCVS(l); break; case 'F': readCCCS(l); break; case 'X': readXCard(l); break; case '@': readDigital(l); break; case '$': readLogical(l); break; case '.': if ((strncmp(tmp,".LIB",4) == 0) || (strncmp(tmp,".INC",4) == 0)) readLibrary(); else if ((strncmp(tmp,".DEF",4) == 0) || (strncmp(tmp,".SYM",4) == 0)) readSymbolDef(); else if (strncmp(tmp,".CLOCK",6) == 0) readClock(l); else if (strncmp(tmp,".NODE",5) == 0) readNode(l); else if (strncmp(tmp,".MODEL",6) == 0) readModelSection(&(rfile.filePtr[rfile.depth]), l); else if (strncmp(tmp,".SUBCKT",7) == 0) openSubckt(l); else if (strncmp(tmp,".ENDSUB",7) == 0) closeSubckt(l); else if (strncmp(tmp,".PRINT",6) == 0) readPrint(l); else if (strncmp(tmp,".LPRINT",7) == 0) { shortPrint = FALSE; readPrint(l); } else if (strncmp(tmp,".NPRINT",7) == 0) { shortPrint = TRUE; timePrint = FALSE; readPrint(l); } else if (strncmp(tmp,".NLPRINT",8) == 0) { shortPrint = FALSE; timePrint = FALSE; readPrint(l); } else if (strncmp(tmp,".FFT",4) == 0) { readFFT(); } else if (strncmp(tmp,".TIME",5) == 0) readTime(l); else if (strncmp(tmp,".SAMPLE",7) == 0) readSample(l); else if (strncmp(tmp,".STEP",5) == 0) readTimeStep(l); else if (strncmp(tmp,".PERIOD",7) == 0) readClockPeriod(l); else if (strncmp(tmp,".TABLE",6) == 0) readTable(&(rfile.filePtr[rfile.depth]), l); else if (strncmp(tmp,".DIAGNOSE",9) == 0) { runDiag = TRUE; } break; case '*': case '#': /* blank case */ break; } } while (!P_eof(rfile.filePtr[rfile.depth])); fclose(rfile.filePtr[rfile.depth]); if (rfile.depth >= 0) rfile.depth--; } while (rfile.depth >= 0); } void exit_now() { /* place handlers for closing all possibly open files */ exit(EXIT_SUCCESS); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.