FreeCalypso > hg > freecalypso-tools
view ringtools/fc-e1gen.c @ 894:7ade15d4e0cb
fc-buzplay: BU refactoring, first step
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 03 Apr 2022 08:11:05 +0000 |
parents | 2133c475f5bd |
children |
line wrap: on
line source
/* * This program is an experimental compiler for TI's Melody E1 format * based on the description given in the L1M_AS001_1.pdf document * found in the Peek/FGW drop. */ #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <strings.h> #define MAX_FIELDS 17 char *infname, *outfname; FILE *inf, *outf; char linebuf[512], *fields[MAX_FIELDS+1]; int lineno, nfields; int global_osc_set; int start_time, osc_mask; u_short osc_words[8][4]; get_input_line() { char *cp; int n; if (!fgets(linebuf, sizeof linebuf, inf)) { fprintf(stderr, "%s: unexpected EOF\n", infname); exit(1); } cp = index(linebuf, '\n'); if (!cp) { fprintf(stderr, "%s line %d: too long or missing newline\n", infname, lineno); exit(1); } *cp = '\0'; /* parse it into fields */ cp = linebuf; n = 0; for (;;) { while (isspace(*cp)) cp++; if (*cp == '\0' || *cp == '#') break; if (n >= MAX_FIELDS) { fprintf(stderr, "%s line %d: too many fields\n", infname, lineno); exit(1); } fields[n++] = cp; while (*cp && !isspace(*cp)) cp++; if (*cp) *cp++ = '\0'; } fields[n] = 0; nfields = n; } input_number(str, min, max) char *str; { char *endp; long val; val = strtol(str, &endp, 10); if (*endp) { fprintf(stderr, "%s line %d: \"%s\" is not a valid decimal number\n", infname, lineno, str); exit(1); } if (val < min || val > max) { fprintf(stderr, "%s line %d: number %ld is out of range\n", infname, lineno, val); exit(1); } return val; } input_paren_number(str, min, max) char *str; { char *cp; if (str[0] != '(') { badparen: fprintf(stderr, "%s line %d: bad parenthesized argument \"%s\"\n", infname, lineno, str); exit(1); } cp = index(str, ')'); if (!cp || cp[1]) goto badparen; *cp = '\0'; return input_number(str + 1, min, max); } handle_time_line() { if (nfields != 2) { fprintf(stderr, "%s line %d: time header takes 1 argument\n", infname, lineno); exit(1); } start_time = input_number(fields[1], 1, 255); } check_req_field(n) { if (n >= nfields) { fprintf(stderr, "%s line %d: too few fields\n", infname, lineno); exit(1); } } process_osc_line() { int p = 1; int oscn, osc_bit; u_short word0, word1, word2, word3; int amp, freq, length; int tremT0, tremFreq; int sustain, t1, t2, t3, t5; int bit0; check_req_field(p); oscn = input_number(fields[p], 0, 7); p++; osc_bit = 1 << oscn; if (osc_mask & osc_bit) { fprintf(stderr, "%s line %d: osc %d defined more than once\n", infname, lineno, oscn); exit(1); } osc_mask |= osc_bit; /* basic part */ check_req_field(p); if (fields[p][0] == '(') { bit0 = input_paren_number(fields[p], 0, 1); p++; check_req_field(p); } else bit0 = 0; if (!strcmp(fields[p], "df")) { p++; check_req_field(p); freq = input_number(fields[p], -8192, 8191) & 0x3FFF; p++; check_req_field(p); amp = input_number(fields[p], 0, 1023); p++; word0 = freq << 2 | 2 | bit0; word1 = amp << 6; } else { word0 = bit0; if (!strcmp(fields[p], "sq1")) { p++; word0 |= 4; check_req_field(p); } if (!strcmp(fields[p], "sq2")) { p++; word0 |= 8; check_req_field(p); } amp = input_number(fields[p], 0, 63); p++; check_req_field(p); freq = input_number(fields[p], 0, 63); p++; word0 |= (freq << 10) | (amp << 4); check_req_field(p); length = input_number(fields[p], 0, 1023); p++; word1 = length << 6; } /* optional 3rd word */ if (p < nfields && !strcmp(fields[p], "trem")) { p++; word1 |= 0x10; check_req_field(p); tremT0 = input_number(fields[p], 0, 7); p++; check_req_field(p); tremFreq = input_number(fields[p], -16, 15) & 31; p++; word2 = (tremFreq << 11) | (tremT0 << 8); } /* optional 4th word */ if (p < nfields && !strcmp(fields[p], "env")) { p++; word1 |= 0x20; check_req_field(p); sustain = input_number(fields[p], 0, 15); p++; check_req_field(p); t1 = input_number(fields[p], 0, 7); p++; check_req_field(p); t2 = input_number(fields[p], 0, 7); p++; check_req_field(p); t3 = input_number(fields[p], 0, 7); p++; check_req_field(p); t5 = input_number(fields[p], 0, 7); p++; word3 = (t1 << 13) | (t2 << 10) | (t3 << 7) | (t5 << 4) | sustain; } if (p != nfields) { fprintf(stderr, "%s line %d: unexpected extra fields\n", infname, lineno); } osc_words[oscn][0] = word0; osc_words[oscn][1] = word1; osc_words[oscn][2] = word2; osc_words[oscn][3] = word3; } read_osc_lines() { osc_mask = 0; for (;;) { get_input_line(); if (!nfields) break; if (!strcmp(fields[0], "osc")) { process_osc_line(); continue; } fprintf(stderr, "%s line %d: osc line expected\n", infname, lineno); exit(1); } } emit_16bit_word(word) u_short word; { putc(word & 0xFF, outf); putc(word >> 8, outf); } emit_record() { int oscn, osc_bit; putc(start_time, outf); putc(osc_mask, outf); for (oscn = 0; oscn < 8; oscn++) { osc_bit = 1 << oscn; if (!(osc_mask & osc_bit)) continue; emit_16bit_word(osc_words[oscn][0]); emit_16bit_word(osc_words[oscn][1]); if (osc_words[oscn][1] & 0x10) emit_16bit_word(osc_words[oscn][2]); if (osc_words[oscn][1] & 0x20) emit_16bit_word(osc_words[oscn][3]); } } handle_global_osc_set() { int p; int oscn, osc_bit; int dummy_time_byte; do get_input_line(); while (!nfields); if (strcmp(fields[0], "osc-set")) { fprintf(stderr, "%s line %d: osc-set line expected\n", infname, lineno); exit(1); } if (nfields < 2) { emptyerr: fprintf(stderr, "%s line %d: osc-set must be non-empty\n", infname, lineno); exit(1); } p = 1; if (fields[p][0] == '(') { dummy_time_byte = input_paren_number(fields[p], 0, 255); p++; if (nfields < 3) goto emptyerr; } else dummy_time_byte = 0; for (; p < nfields; p++) { oscn = input_number(fields[p], 0, 7); osc_bit = 1 << oscn; global_osc_set |= osc_bit; } putc(dummy_time_byte, outf); putc(global_osc_set, outf); } main(argc, argv) char **argv; { if (argc != 3) { fprintf(stderr, "usage: %s src-file e1-bin-file\n", argv[0]); exit(1); } if (strcmp(argv[1], "-")) { infname = argv[1]; inf = fopen(infname, "r"); if (!inf) { perror(infname); exit(1); } } else { infname = "stdin"; inf = stdin; } outfname = argv[2]; outf = fopen(outfname, "w"); if (!outf) { perror(outfname); exit(1); } handle_global_osc_set(); /* main loop */ for (;;) { do get_input_line(); while (!nfields); if (!strcmp(fields[0], "time")) { handle_time_line(); read_osc_lines(); emit_record(); } else if (!strcmp(fields[0], "end")) { emit_16bit_word(0); exit(0); } else { fprintf(stderr, "%s line %d: expected time or end\n", infname, lineno); exit(1); } } }