void InitScan( void ) { //================== // Initialize the scanner. if( Options & OPT_EXTEND_REAL ) { TokenREA = TO_DBL; ExpREA = 'D'; TokenDBL = TO_EXT; ExpDBL = 'Q'; } else { TokenREA = TO_REA; ExpREA = 'E'; TokenDBL = TO_DBL; ExpDBL = 'D'; } TokenEXT = TO_EXT; ExpEXT = 'Q'; Line = 0; State = SNS; TkCrsr = &TokenBuff[ 0 ]; LexToken.stop = TkCrsr; LexToken.col = Column; LexToken.flags = 0; LexToken.line = 0; SrcRecNum = CurrFile->rec; // at this point, the line has already been read // so just print it with an ISN in front LinePrint(); if( StmtType == STMT_CONT ) { Error( CC_NOT_INITIAL ); } // examine the statement number if there was one ScanNum(); ExtnSw &= ~XS_CONT_20; }
void Scan( void ) { //============== // Collect a token. token_state state2; byte ch; token_state old_state; char *dpt = NULL; byte tab; int len; int hlen; TOKCLASS class; char_class ch_class; char_class wasextch; if( !(LexToken.flags & TK_LAST) ) { wasextch = 0; len = 0; tab = 0; class = 0; old_state = SNS; LexToken.start = LexToken.stop; LexToken.col = Column; LexToken.line = Line; for(;;) { ch = *Cursor; ch_class = CharSetInfo.character_set[ ch ]; wasextch |= ch_class; state2 = StateTable[ State ][ ch_class & C_MASK ]; switch( state2 ) { case SAN : case SLG : if( ch_class & C_LOW ) { // lower case character ch += 'A' - 'a'; } *TkCrsr = ch; TkCrsr++; State = state2; break; case SDB : if( CharSetInfo.character_width( Cursor ) != 2 ) { Error( CC_BAD_CHAR ); } else if( CharSetInfo.is_double_byte_blank( Cursor ) ) { *Cursor = ' '; *( Cursor + 1 ) = ' '; Column--; // compensate for Column++ and Cursor++ Cursor--; // after select } else { State = StateTable[ State ][ C_AL ]; *TkCrsr = ch; TkCrsr++; Cursor++; Column++; *TkCrsr = *Cursor; TkCrsr++; } break; case SNM : case SFT : case SEN : *TkCrsr = ch; TkCrsr++; State = state2; break; case SSG : *TkCrsr = ch; TkCrsr++; // 0..71 is the statement area // 72..79 is the sequence area ++Cursor; if( ++Column >= LastColumn ) { // we've just processed column 72 *Cursor = NULLCHAR; } State = SOP; class = TO_OPR; goto token; case SEX : case SLX : if( LexToken.flags & TK_LENSPEC ) { LexToken.flags &= ~TK_LENSPEC; goto token; } if( ch_class & C_LOW ) { // lower case character ch += 'A' - 'a'; } State = state2; switch( ch ) { case( 'Q' ): class = TokenEXT; break; case( 'D' ): class = TokenDBL; break; case( 'E' ): class = TokenREA; break; } *TkCrsr = ch; TkCrsr++; break; case SML : case SLL : dpt = TkCrsr; old_state = State; *TkCrsr = ch; TkCrsr++; State = state2; break; case SFQ : State = SIQ; break; case SIQ : state2 = SIQ; case SFM : if( ch_class == C_TC ) { tab = 8 - Column % 8; // Column gets incremented normally at bottom of loop Column += tab - 1; memset( TkCrsr, ' ', tab ); TkCrsr += tab; } else { *TkCrsr = ch; TkCrsr++; } State = state2; break; case SOL : case SHX : case SCS : *TkCrsr = NULLCHAR; // for conversion routines TkCrsr++; case SAP : State = state2; break; case SSO : goto token; case SOP : *TkCrsr = ch; TkCrsr++; // 0..71 is the statement area // 72..79 is the sequence area ++Cursor; if( ++Column >= LastColumn ) { // we've just processed column 72 *Cursor = NULLCHAR; } State = SOP; class = TO_OPR; goto token; case SFL : if( old_state == SNS ) { // Consider: i .eq. j LkUpLog(); if( LexToken.log != BAD_LOG ) { // if it's a valid logical operator, // set final state to SFL State = state2; } else if( dpt == NULL ) { dpt = LexToken.start; } } else { // Consider: 1 .eq. 2 State = state2; } goto token; case SHL : if( LexToken.flags & TK_LENSPEC ) { LexToken.flags &= ~TK_LENSPEC; goto token; } *TkCrsr = NULLCHAR; len = atoi( LexToken.start ); LexToken.start = TkCrsr; State = SIH; class = TO_LIT; StmtSw |= SS_HOLLERITH; break; case SIH : if( TkCrsr - LexToken.start >= len ) { TkCrsr = LexToken.start + len; // in case of TABCHAR State = SSO; goto token; } if( ch_class == C_TC ) { tab = 8 - Column % 8; // Column gets incremented normally at bottom of loop Column += tab - 1; memset( TkCrsr, ' ', tab ); TkCrsr += tab; } else { *TkCrsr = ch; TkCrsr++; } break; case SSP : if( State == SNS ) { LexToken.col = Column; } break; case STC : // Column gets incremented normally at bottom of loop Column += 7 - Column % 8; break; case SBC : Error( CC_BAD_CHAR ); break; case SCM : if( !(ExtnSw & XS_EOL_COMMENT) ) { Extension( CC_EOL_COMMENT ); ExtnSw |= XS_EOL_COMMENT; } case SNR : if( LexToken.flags & TK_INCLUDE ) { LexToken.flags |= TK_LAST; goto token; } // 0..71 is the statement area // 72..79 is the sequence area // calculate the number of spaces // that we may require for filling tab = LastColumn - Column; ComRead(); if( StmtType != STMT_CONT ) { LexToken.flags |= TK_LAST; goto token; } if( ( State == SIQ ) || ( State == SIH ) || ( State == SFM ) ) { memset( TkCrsr, ' ', tab ); TkCrsr += tab; } ++Line; ComPrint(); ScanNum(); if( Line >= 20 ) { if( !(ExtnSw & XS_CONT_20) ) { Extension( CC_TOO_MANY_CONT ); ExtnSw |= XS_CONT_20; } if( (TkCrsr-TokenBuff) + (LastColumn-CONT_COL) > TOKLEN ) { TkCrsr = TokenBuff; // so we don't overflow TokenBuff if( !(StmtSw & SS_CONT_ERROR_ISSUED) ) { Error( CC_CONT_OVERFLOW ); StmtSw |= SS_CONT_ERROR_ISSUED; } } } if( State == SNS ) { LexToken.col = Column; } Column--; // to offset ++Cursor and ++Column in code Cursor--; // after } (must be after ScanNum break; // and Error since Error uses Column) } // 0..71 is the statement area // 72..79 is the sequence area ++Cursor; if( ++Column >= LastColumn ) { // we've just processed column 72 *Cursor = NULLCHAR; } } token: LexToken.stop = TkCrsr; state2 = State; // final state for Scan State = SNS; // set to no state for next time switch( state2 ) { case SAN : class = TO_NAM; break; case SNM : class = TO_INT; break; case SFT : class = TokenREA; break; case SOL : class = TO_OCT; break; case SHX : class = TO_HEX; break; case SCS : case SAP : class = TO_LIT; break; case SFL : if( old_state == SNS ) { LexToken.start++; // skip over starting dot // 0..71 is the statement area // 72..79 is the sequence area ++Cursor; if( ++Column >= LastColumn ) { // just processed column 72 *Cursor = NULLCHAR; } class = TO_LGL; } else {
int main(int argc, char **args) { if (argc != 6 && argc != 7) usage(); bool ptr3 = false; if (argc == 7) { ptr3 = strchr(args[6],'3') != 0; } int err = readall(args[1],patches); if (err) { printf("Can't %s patches\n",readall_errtype[err]); usage(); } err = readall(args[2],envelopes); if (err) { printf("Can't %s envelopes\n",readall_errtype[err]); usage(); } err = readall(args[3],sequences); if (err) { printf("Can't %s sequences\n",readall_errtype[err]); usage(); } err = readall(args[4],samples); if (err) { printf("Can't %s samples\n",readall_errtype[err]); usage(); } if (sequences.size() < 2) return 0; sequences_used.resize(sequences.size()); sequence_used.resize(sequences.size()); { int i; for (i=0; args[5][i]; ++i) dir[i] = args[5][i]; for (; i>=0; --i) if (dir[i] == '/' || dir[i] == '\\') break; dir[i+1] = 0; } // get tracks count int count = GetWordLE(&sequences[0])/2; for (int i=0; i<count; ++i) { sprintf(buff,"%s%03d",dir,i); if (_mkdir(buff) != 0 && errno != EEXIST) { printf("Can't create dir \"%03d\"\n",i); return 0; } sprintf(buff,"%s%03d\\%03d.code",dir,i,i); FILE *f_seq = fopen(buff,"wb"); if (!f_seq) { printf("Can't create \"%s\" file\n",buff); return 0; } // check that we can get sequence header offset if (i*2 + 1 >= sequences.size()) { printf("Error: sequence %03d offset over end of file\n",i); return 0; } // get sequence header offset int seq_start = GetWordLE(&sequences[i*2]); if (seq_start >= sequences.size()) { printf("Error: sequence %03d header over end of file\n",i); return 0; } // mark sequence offset bytes as "used" sequences_used[i*2 ] = true; sequences_used[i*2+1] = true; // get channels count int channels = sequences[seq_start]; // calculate header length int header_len = channels*(ptr3?3:2) + 1; // check that whole header contains in file if (seq_start + header_len > sequences.size()) { printf("Error: sequence %03d header over end of file\n",i); return 0; } // mark header offsets as "used" for (int j=0; j<header_len; ++j) sequences_used[seq_start+j] = true; // labels for "assembly" std::vector<int> labels; // where std::vector<int> labelswhere; // where required std::vector<BYTE> notepatch; notepatch.resize(sequences.size()); // reserve labels for channel offsets labels.resize(channels); labelswhere.resize(channels); // mark all bytes not used for current sequence for (int j=0; j<sequences.size(); ++j) sequence_used[j] = 0; // mark all required bytes by sequence // and make labels for (int c=0; c<channels; ++c) { int ch_start; // get channel code offset if (ptr3) ch_start = GetTriple(&sequences[seq_start + c*3 + 1]); else ch_start = GetWordLE(&sequences[seq_start + c*2 + 1]); // set label (was reserved) labels[c] = ch_start; // check that channel start is in file if (ch_start >= sequences.size()) { printf("Error: sequence %03d channel %d starts out of file\n",i,c); return 0; } // stack of addresses to scan std::vector<int> jmp_stack; std::vector<int> jmp_stackp; // patch // put channel start jmp_stack.push_back(ch_start); jmp_stackp.push_back(0); while (!jmp_stack.empty()) { // we have address to scan! int j = jmp_stack.back(); int pp = jmp_stackp.back(); // patch // remove from stack jmp_stack.pop_back(); jmp_stackp.pop_back(); for (;;) { // scanned? if (sequence_used[j]) break; // stop // decode command, save length int len = gems_decode(&sequences[j], buff); if (!len) { // Unknown command detected printf("Warning: unknown command in sequence %03d, in channel %d, at global offset $%X\n", i,c,j); len = 1; } // check command end if (j + len > sequences.size()) { printf("Warning: command start + len more than end of file " "in sequence %03d, in channel %d, at global offset $%X\n", i,c,j); break; } // is it "patch"? if (memcmp(buff,"patch",5) == 0) { // yes, set pp // read patch index from text ScanNum(buff+5,pp); } // is it "goto"? if (memcmp(buff,"goto",4) == 0) { // yes, give label for offset // read offset from text int offs; ScanNum(buff+4,offs); // add label labels.push_back(j+len+(short)offs); // where needed labelswhere.push_back(j); // add offset into scan stack jmp_stack.push_back(j+len+(short)offs); jmp_stackp.push_back(pp); // patch } // is it "if"? if (memcmp(buff,"if",2) == 0) { // yes, give label for offset // find comma before offset char *tmp = strchr(buff,','); // read offset int offs; ScanNum(tmp+1,offs); // add label labels.push_back(j+len+offs); // where needed labelswhere.push_back(j); // add offset into scan stack jmp_stack.push_back(j+len+offs); jmp_stackp.push_back(pp); // patch } // mark command bytes as "being used" for (; len > 0; --len, ++j) { sequences_used[j] = true; // within whole file sequence_used[j] = true; // within current sequence notepatch[j] = pp; } // is it end of sequence? if (memcmp(buff,"eos",3) == 0) break; // stop scan } } } // write section fprintf(f_seq," SECTION HEADER\n"); // write count of channels fprintf(f_seq," dc.b %d\n",channels); // write channels offsets for (int c=0; c<channels; ++c) fprintf(f_seq," dc.t channel_%d\n",c); // write section fprintf(f_seq,"\n SECTION CODE\n"); // indexes of patches, modulations, samples required for current sequence std::vector<int> patches_used; std::vector<int> modulations_used; std::vector<int> samples_used; // scan bytes again, but without jumps // all we need is to get marked commands for (int j=0; j<sequences.size(); ) { for (int z=0; z<labels.size(); ++z) if (labels[z] == j) fprintf(f_seq,"%s_%d:\n",(z<channels)?"channel":"label",z); // Is this byte required by current sequence? if (!sequence_used[j]) { // no, skip forward ++j; continue; } int len = gems_decode(&sequences[j], buff); if (!len) // if it is unknown command { // then write plain byte fprintf(f_seq," dc.b $%02X",(int)sequences[j]); ++j; continue; // resume to next byte } // is it "note"? if (memcmp(buff,"note",4) == 0) { // yes, then if patch is DAC, then use name int pp = notepatch[j]; int poffs = -1; int ptype = -1; // if we can get patch offs, then get it if (pp*2 + 1 < patches.size()) poffs = GetWordLE(&patches[pp*2]); // if we can get patch type, then get it if (poffs != -1 && poffs < patches.size()) ptype = patches[poffs]; // if patch type == DAC if (ptype == GEMSI_DAC) { // scan note id int sid; ScanNum(buff+4,sid); // translate to sample id sid = (sid + 0x30) % 0x60; // look in already found int z; for (z=0; z<samples_used.size(); ++z) if (samples_used[z] == sid) break; // add if not found if (z == samples_used.size()) samples_used.push_back(sid); // replace id with name sprintf(buff+4," sample_%02X",sid); } } // is it "patch"? if (memcmp(buff,"patch",5) == 0) { // yes, use name instead of index // read patch index from text int pn; ScanNum(buff+5,pn); // check that it is not allocated int z; for (z=0; z<patches_used.size(); ++z) if (patches_used[z] == pn) break; // if not exist, then add if (z == patches_used.size()) patches_used.push_back(pn); // replace offset with name sprintf(buff+5," patch_%02X",pn); } // is it "modulation"? if (memcmp(buff,"modulation",10) == 0) { // yes, use name instead of index // read modulation index from text int mn; ScanNum(buff+10,mn); // check that it is not allocated int z; for (z=0; z<modulations_used.size(); ++z) if (modulations_used[z] == mn) break; // if not exist, then add if (z == modulations_used.size()) modulations_used.push_back(mn); // replace offset with name sprintf(buff+10," modulation_%02X",mn); } // is it "goto"? if (memcmp(buff,"goto",4) == 0) { // yes, set label instead of plain offset // find index of label given to this "goto" int z; for (z=0; z<labels.size(); ++z) if (labelswhere[z] == j) break; // check "not found" if (z == labels.size()) { printf("Unexpected Error: label not allocated " "in %03d sequence at global offset $%X",i,j); --z; } // replace offset with label sprintf(buff+4," label_%d",z); } // is it "if"? if (memcmp(buff,"if",2) == 0) { // yes, set label instead of plain offset // find index of label given to this "if" int z; for (z=0; z<labels.size(); ++z) if (labelswhere[z] == j) break; // check "not found" if (z == labels.size()) { printf("Unexpected Error: label not allocated " "in %03d sequence at global offset $%X",i,j); --z; } // find comma before offset char *tmp = strchr(buff,','); // replace offset with label sprintf(tmp+1,"label_%d",z); } // write command text fprintf(f_seq," %s\n",buff); // skip first byte, because it's label is correct ++j; --len; // test for invalid labels for (; len>0; ++j, --len) for (int z=0; z<labels.size(); ++z) if (labels[z] == j) printf("Error: label %d at middle of command " "in sequence %03d, at global offset $%X\n",z,i,j); } fclose(f_seq); // dump used instruments for (int z=0; z<patches_used.size(); ++z) { int pn = patches_used[z]; // check that we can get offset if (pn * 2 + 1 >= patches.size()) { printf("Error: patch $%02X out of bounds of instruments files " "used in sequence %03d\n", pn, i); continue; } // get offset int poffs = GetWordLE(&patches[pn*2]); // check that we can get type if (poffs >= patches.size()) { printf("Error: patch $%02X out of bounds of instruments files " "used in sequence %03d\n", pn, i); continue; } // get patch type int ptype = patches[poffs]; // check patch type if (ptype > 3) { printf("Error: patch $%02X unknown type %d " "used in sequence %03d\n", pn, ptype, i); continue; } // patch dump sizes int psizes[]={0x27,2,7,7}; // check that full patch dump contains in file if (poffs + psizes[ptype] > patches.size()) { printf("Error: patch $%02X info not full, " "used in sequence %03d\n", pn, i); continue; } // create file for instrument info sprintf(buff,"%s%03d\\patch_%02X.ins",dir,i,pn); FILE *ins = fopen(buff,"w"); if (!ins) { printf("Can't create file \"%s\"\n",buff); continue; } // write instrument info switch(ptype) { case GEMSI_FM: case GEMSI_PSG: case GEMSI_NOISE: fprintf(ins,"importraw 'patch_%02X.raw'\n",pn); break; case GEMSI_DAC: // DAC has only one byte param, so keep it in plain text fprintf(ins,"DAC %d\n",(int)patches[poffs+1]); break; } fclose(ins); // if patch type != DAC, write dump. DAC patch contains only 1 param, // so it will be in plain text right in instrument info if (ptype != GEMSI_DAC) { // create dump file sprintf(buff,"%s%03d\\patch_%02X.raw",dir,i,pn); ins = fopen(buff,"wb"); if (!ins) { printf("Can't create file \"%s\"\n",buff); continue; } // write dump int writed = fwrite(&patches[poffs],1,psizes[ptype],ins); // check that it was writen if (writed != psizes[ptype]) printf("Error: can't write \"%s\" file\n",buff); fclose(ins); } } // write dumps of used modulations for (int z=0; z<modulations_used.size(); ++z) { int mn = modulations_used[z]; // check that we can get offset if (mn*2 + 1 > envelopes.size()) { printf("Error: modulation $%02X out of file bounds, " "used in sequence %03d\n",mn,i); continue; } // get offset int moffs = GetWordLE(&envelopes[mn*2]); // check that file contains this offset if (moffs >= envelopes.size()) { printf("Error: modulation $%02X out of file bounds, " "used in sequence %03d\n",mn,i); continue; } // calculate end of envelope int mend; // start from delay at offset moffs + 2 for (mend = moffs + 2; ; mend += 3) { // check that we can test delay if (mend >= envelopes.size()) { printf("Error: modulation $%02X has no end, " "used in sequence %03d\n",mn,i); mend = 0; break; } // test delay if (envelopes[mend] == 0) break; // we found END } // check that we found mend if (!mend) continue; // set end after last delay ( = 0) ++mend; // create mod file sprintf(buff,"%s%03d\\modulation_%02X.mod",dir,i,mn); FILE *mod = fopen(buff,"wb"); if (!mod) { printf("Can't create file \"%s\"\n",buff); continue; } // write dump int writed = fwrite(&envelopes[moffs],1,mend-moffs,mod); // check that it was writen if (writed != mend - moffs) printf("Error: can't write \"%s\" file\n",buff); fclose(mod); } // write dumps of used samples for (int z=0; z<samples_used.size(); ++z) { int sn = samples_used[z]; int sh = sn*12; // check than we can read sample header if (sh + 12 > samples.size()) { printf("Error: sample $%02X header out of file, " "used in %03d sequence\n",sn,i); continue; } int sflags = samples[sh]; int soff = GetTriple(&samples[sh+1]); int sskip = GetWordLE(&samples[sh+4]); int sfirst = GetWordLE(&samples[sh+6]); int sloop = (short)GetWordLE(&samples[sh+8]); int send = GetWordLE(&samples[sh+10]); int s_all = sskip + sfirst + send; if (soff+s_all > samples.size()) { printf("Error: sample $%02X ending is out of file, " "used in sequence %03d",sn,i); continue; } // create file for sample cfg sprintf(buff,"%s%03d\\sample_%02X.sfx",dir,i,sn); FILE *s = fopen(buff,"w"); if (!s) { printf("Can't create file \"%s\"\n",buff); continue; } fprintf(s, "RAW 'sample_%02X.snd'\n" "FLAGS =$%02X\n" "SKIP =$%04X\n" "FIRST =$%04X\n" "LOOP =$%04X\n" "END =$%04X\n", sn, sflags, sskip, sfirst, sloop, send); fclose(s); sprintf(buff,"%s%03d\\sample_%02X.snd",dir,i,sn); s = fopen(buff,"wb"); if (!s) { printf("Can't create file \"%s\"\n",buff); continue; } int writed = fwrite(&samples[soff],1,s_all,s); if (writed != s_all) printf("Can't write file \"%s\"\n",buff); fclose(s); } // write sequence config sprintf(buff,"%s%03d\\%03d.cfg",dir,i,i); f_seq = fopen(buff,"w"); // include code fprintf(f_seq,"code '%03d.code'\n\n",i); // include instruments for (int z=0; z<patches_used.size(); ++z) fprintf(f_seq,"instrument patch_%02X,'patch_%02X.ins'\n",patches_used[z],patches_used[z]); fprintf(f_seq,"\n"); // include modulations for (int z=0; z<modulations_used.size(); ++z) fprintf(f_seq,"modulation modulation_%02X,'modulation_%02X.mod'\n",modulations_used[z],modulations_used[z]); fprintf(f_seq,"\n"); // include samples for (int z=0; z<samples_used.size(); ++z) fprintf(f_seq,"sample sample_%02X,'sample_%02X.sfx'\n",samples_used[z],samples_used[z]); // end of cfg fclose(f_seq); } // create main cfg FILE *cfg = fopen(args[5],"w"); if (!cfg) { printf("Can't create \"%s\"\n",args[5]); return 0; } // include sequences for (int i=0; i<count; ++i) { fprintf(cfg,"sequence '%03d\\%03d.cfg'\n",i,i); } fclose(cfg); bool _mid = true; for (int i=0; i<sequences.size(); ++i) if (sequences_used[i] != _mid) { if (_mid) printf("Sequences not used bytes: $%X",i); else printf("-$%X\n",i); _mid = sequences_used[i]; } if (!_mid) printf("-$%X\n",sequences.size()); return 0; }