7 All routines dealing with the manipulation of UNITRK(tm) streams
\r
10 All systems - all compilers
\r
17 #define BUFPAGE 128 /* smallest unibuffer size */
\r
20 /* unibuffer is increased by BUFPAGE
\r
21 bytes when unipc reaches unimax-TRESHOLD */
\r
26 Ok.. I'll try to explain the new internal module format.. so here it goes:
\r
29 The UNITRK(tm) Format:
\r
30 ======================
\r
32 A UNITRK stream is an array of bytes representing a single track
\r
33 of a pattern. It's made up of 'repeat/length' bytes, opcodes and
\r
34 operands (sort of a assembly language):
\r
37 [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
\r
39 |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
\r
42 The rep/len byte contains the number of bytes in the current row,
\r
43 _including_ the length byte itself (So the LENGTH byte of row 0 in the
\r
44 previous example would have a value of 5). This makes it easy to search
\r
45 through a stream for a particular row. A track is concluded by a 0-value
\r
48 The upper 3 bits of the rep/len byte contain the number of times -1 this
\r
49 row is repeated for this track. (so a value of 7 means this row is repeated
\r
52 Opcodes can range from 1 to 255 but currently only opcodes 1 to 19 are
\r
53 being used. Each opcode can have a different number of operands. You can
\r
54 find the number of operands to a particular opcode by using the opcode
\r
55 as an index into the 'unioperands' table.
\r
61 UWORD unioperands[256]={
\r
64 1, /* UNI_INSTRUMENT */
\r
65 1, /* UNI_PTEFFECT0 */
\r
66 1, /* UNI_PTEFFECT1 */
\r
67 1, /* UNI_PTEFFECT2 */
\r
68 1, /* UNI_PTEFFECT3 */
\r
69 1, /* UNI_PTEFFECT4 */
\r
70 1, /* UNI_PTEFFECT5 */
\r
71 1, /* UNI_PTEFFECT6 */
\r
72 1, /* UNI_PTEFFECT7 */
\r
73 1, /* UNI_PTEFFECT8 */
\r
74 1, /* UNI_PTEFFECT9 */
\r
75 1, /* UNI_PTEFFECTA */
\r
76 1, /* UNI_PTEFFECTB */
\r
77 1, /* UNI_PTEFFECTC */
\r
78 1, /* UNI_PTEFFECTD */
\r
79 1, /* UNI_PTEFFECTE */
\r
80 1, /* UNI_PTEFFECTF */
\r
81 1, /* UNI_S3MEFFECTA */
\r
82 1, /* UNI_S3MEFFECTD */
\r
83 1, /* UNI_S3MEFFECTE */
\r
84 1, /* UNI_S3MEFFECTF */
\r
85 1, /* UNI_S3MEFFECTI */
\r
86 1, /* UNI_S3MEFFECTQ */
\r
87 1, /* UNI_S3MEFFECTT */
\r
88 1, /* UNI_XMEFFECTA */
\r
89 1, /* UNI_XMEFFECTG */
\r
90 1, /* UNI_XMEFFECTH */
\r
91 1 /* UNI_XMEFFECTP */
\r
95 /***************************************************************************
\r
96 >>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<<
\r
97 ***************************************************************************/
\r
100 static UBYTE *rowstart; /* startadress of a row */
\r
101 static UBYTE *rowend; /* endaddress of a row (exclusive) */
\r
102 static UBYTE *rowpc; /* current unimod(tm) programcounter */
\r
105 void UniSetRow(UBYTE *t)
\r
109 rowend=rowstart+(*(rowpc++)&0x1f);
\r
113 UBYTE UniGetByte(void)
\r
115 return (rowpc<rowend) ? *(rowpc++) : 0;
\r
119 void UniSkipOpcode(UBYTE op)
\r
121 UWORD t=unioperands[op];
\r
122 while(t--) UniGetByte();
\r
126 UBYTE *UniFindRow(UBYTE *t,UWORD row)
\r
128 Finds the address of row number 'row' in the UniMod(tm) stream 't'
\r
130 returns NULL if the row can't be found.
\r
137 c=*t; /* get rep/len byte */
\r
139 if(!c) return NULL; /* zero ? -> end of track.. */
\r
141 l=(c>>5)+1; /* extract repeat value */
\r
143 if(l>row) break; /* reached wanted row? -> return pointer */
\r
145 row-=l; /* havn't reached row yet.. update row */
\r
146 t+=c&0x1f; /* point t to the next row */
\r
154 /***************************************************************************
\r
155 >>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<<
\r
156 ***************************************************************************/
\r
159 static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
\r
160 static UWORD unimax; /* maximum number of bytes to be written to this buffer */
\r
162 static UWORD unipc; /* index in the buffer where next opcode will be written */
\r
163 static UWORD unitt; /* holds index of the rep/len byte of a row */
\r
164 static UWORD lastp; /* holds index to the previous row (needed for compressing) */
\r
167 void UniReset(void)
\r
169 Resets index-pointers to create a new track.
\r
172 unitt=0; /* reset index to rep/len byte */
\r
173 unipc=1; /* first opcode will be written to index 1 */
\r
174 lastp=0; /* no previous row yet */
\r
175 unibuf[0]=0; /* clear rep/len byte */
\r
179 void UniWrite(UBYTE data)
\r
181 Appends one byte of data to the current row of a track.
\r
184 /* write byte to current position and update */
\r
186 unibuf[unipc++]=data;
\r
188 /* Check if we've reached the end of the buffer */
\r
190 if(unipc>(unimax-TRESHOLD)){
\r
194 /* We've reached the end of the buffer, so expand
\r
195 the buffer by BUFPAGE bytes */
\r
197 newbuf=(UBYTE *)realloc(unibuf,unimax+BUFPAGE);
\r
199 /* Check if realloc succeeded */
\r
206 /* realloc failed, so decrease unipc so we won't write beyond
\r
207 the end of the buffer.. I don't report the out-of-memory
\r
208 here; the UniDup() will fail anyway so that's where the
\r
209 loader sees that something went wrong */
\r
217 void UniInstrument(UBYTE ins)
\r
219 Appends UNI_INSTRUMENT opcode to the unitrk stream.
\r
222 UniWrite(UNI_INSTRUMENT);
\r
227 void UniNote(UBYTE note)
\r
229 Appends UNI_NOTE opcode to the unitrk stream.
\r
232 UniWrite(UNI_NOTE);
\r
237 void UniPTEffect(UBYTE eff,UBYTE dat)
\r
239 Appends UNI_PTEFFECTX opcode to the unitrk stream.
\r
242 if(eff!=0 || dat!=0){ /* don't write empty effect */
\r
243 UniWrite(UNI_PTEFFECT0+eff);
\r
249 BOOL MyCmp(UBYTE *a,UBYTE *b,UWORD l)
\r
254 if(*(a++)!=*(b++)) return 0;
\r
260 void UniNewline(void)
\r
262 Closes the current row of a unitrk stream (updates the rep/len byte)
\r
263 and sets pointers to start a new row.
\r
268 n=(unibuf[lastp]>>5)+1; /* repeat of previous row */
\r
269 l=(unibuf[lastp]&0x1f); /* length of previous row */
\r
271 len=unipc-unitt; /* length of current row */
\r
273 /* Now, check if the previous and the current row are identical..
\r
274 when they are, just increase the repeat field of the previous row */
\r
276 if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)){
\r
277 unibuf[lastp]+=0x20;
\r
281 /* current and previous row aren't equal.. so just update the pointers */
\r
291 UBYTE *UniDup(void)
\r
293 Terminates the current unitrk stream and returns a pointer
\r
294 to a copy of the stream.
\r
301 if((d=(UBYTE *)malloc(unipc))==NULL){
\r
302 myerr=ERROR_ALLOC_STRUCT;
\r
305 memcpy(d,unibuf,unipc);
\r
311 UWORD TrkLen(UBYTE *t)
\r
313 Determines the length (in rows) of a unitrk stream 't'
\r
333 if(!(unibuf=(UBYTE *)malloc(unimax))){
\r
334 myerr=ERROR_ALLOC_STRUCT;
\r
341 void UniCleanup(void)
\r
343 if(unibuf!=NULL) free(unibuf);
\r