BASIC compiler/interpreter for PIC32MX/MZ-80K (suspended)
Rev. | 265 |
---|---|
サイズ | 15,811 バイト |
日時 | 2018-07-30 09:16:08 |
作者 | kmorimatsu |
ログメッセージ | Revise help. Support 15700 Hz WAVE file. |
/*
This file is provided under the LGPL license ver 2.1.
Written by Katsumi.
http://hp.vector.co.jp/authors/VA016157/
kmorimatsu@users.sourceforge.jp
*/
#include <xc.h>
#include "compiler.h"
#include "api.h"
/*
Timer3: 1/32 prescaler, toggle mode : 894886.25 Hz
PR3=2047 <-> 437 Hz
*/
/*
c: 1722
B: 1825
A#: 1933
A: 2048
G#: 2170
G: 2299
F#: 2435
F: 2580
E: 2734
D#: 2896
D: 3067
C#: 3251
C: 3444
Cb: 3650
*/
const static int g_keys[]={
1933,1722,3251,2896,2580,2435,2170,// 0 7# C# A#m
1933,1825,3251,2896,2580,2435,2170,// 7 6# F# D#m
1933,1825,3251,2896,2734,2435,2170,// 14 5# B G#m
2048,1825,3251,2896,2734,2435,2170,// 21 4# E C#m
2048,1825,3251,3067,2734,2435,2170,// 28 3# A F#m
2048,1825,3251,3067,2734,2435,2299,// 35 2# D Bm
2048,1825,3444,3067,2734,2435,2299,// 42 1# G Em
2048,1825,3444,3067,2734,2580,2299,// 49 0 C Am
2048,1933,3444,3067,2734,2580,2299,// 56 1b F Dm
2048,1933,3444,3067,2896,2580,2299,// 63 2b Bb Gm
2170,1933,3444,3067,2896,2580,2299,// 70 3b Eb Cm
2170,1933,3444,3251,2896,2580,2299,// 77 4b Ab Fm
2170,1933,3444,3251,2896,2580,2435,// 84 5b Db Bbm
2170,1933,3650,3251,2896,2580,2435,// 91 6b Gb Ebm
2170,1933,3650,3251,2896,2734,2435 // 98 7b Cb Abm
};
/*
2^(1/12) ~= 69433/(2^16)
1/(2^(1/12) ~= 1933/(2^11)
*/
#define toneFlat(x) ((((unsigned long)(x))*69433)>>16)
#define toneSharp(x) ((((unsigned long)(x))*1933)>>11)
/* local global vars */
static int* g_tones;
static int g_qvalue;
static int g_lvalue;
static int g_mpoint;
static char* g_mstr;
static int g_mspos;
static unsigned short g_music[32];
static unsigned short g_sound[32];
static unsigned short g_musiclen[32];
static unsigned char g_soundlen[32];
static int g_musicstart;
static int g_musicend;
static int g_musicwait;
static int g_soundstart;
static int g_soundend;
static int g_soundwait;
static int g_soundrepeat;
static char g_sound_mode=0;
static FSFILE* g_fhandle=0;
static char* g_wavtable=0;
#define SOUND_MODE_MUSIC 1
#define SOUND_MODE_WAVE 2
#define start_dma() T4CONSET=0x8000; DCH2CONSET=0x00000080; g_sound_mode=SOUND_MODE_WAVE
#define stop_dma() T4CONCLR=0x8000; DCH2CONCLR=0x00000080; g_sound_mode=SOUND_MODE_MUSIC
int waveRemaining(int mode){
if (!g_fhandle) return 0;
switch(mode){
case 1: // current position (header excluded)
return g_fhandle->seek-0x2c;
break;
case 2: // file size (header excluded)
return g_fhandle->size-0x2c;
break;
case 0: // remaining
default:
return g_fhandle->size-g_fhandle->seek;
break;
}
}
int musicRemaining(int flagsLR){
// flagsLR is ignored
return (g_musicend-g_musicstart)&31;
}
#pragma interrupt musicint IPL3SOFT vector 1
void musicint(){
unsigned int i;
static unsigned short wavtable_pos;
// This function is called every 1/60 sec.
IFS0bits.CS0IF=0;
switch(g_sound_mode){
case SOUND_MODE_MUSIC:
if (g_soundstart!=g_soundend){
// Start timer & OC4
i=g_sound[g_soundstart];
if (i<0xffff) {
T3CONSET=0x8000;
PR3=i;
if (i<TMR3) TMR3=0;
} else {
T3CONCLR=0x8000;
}
if ((--g_soundwait)<=0) {
g_soundstart++;
if (g_soundstart==g_soundend || 31<g_soundstart) {
g_soundstart=0;
g_soundrepeat--;
if (0<g_soundrepeat) {
g_soundwait=g_soundlen[g_soundstart];
} else {
g_soundend=g_soundrepeat=g_soundwait=0;
}
} else {
g_soundwait=g_soundlen[g_soundstart];
}
}
// Shift music data even though without output.
if (g_musicstart!=g_musicend) {
if ((--g_musicwait)<=0) {
g_musicstart++;
g_musicstart&=31;
g_musicwait=g_musiclen[g_musicstart];
}
}
} else if (g_musicstart!=g_musicend) {
// Start timer & OC4
i=g_music[g_musicstart];
if (i<0xffff) {
T3CONSET=0x8000;
PR3=i;
if (i<TMR3) TMR3=0;
} else {
T3CONCLR=0x8000;
}
if ((--g_musicwait)<=0) {
g_musicstart++;
g_musicstart&=31;
g_musicwait=g_musiclen[g_musicstart];
}
} else {
// Stop timer
T3CONCLR=0x8000;
}
break;
case SOUND_MODE_WAVE:
// Initialize parameters
if (!T4CONbits.ON){
start_dma();
wavtable_pos=0;
}
wavtable_pos=262-wavtable_pos;
// Read from file
if (0 == FSfread((void*)&g_wavtable[wavtable_pos],1,262,g_fhandle)) {
// End of file.
stop_dma();
FSfclose(g_fhandle);
g_fhandle=0;
g_sound_mode=SOUND_MODE_MUSIC;
stop_music();
break;
}
break;
default:
break;
}
}
int musicGetNum(){
int i, ret;
char b;
// Skip non number character
for(i=0;(b=g_mstr[g_mspos+i])<'0' && '9'<g_mstr[g_mspos+i];i++);
// Determine the number
ret=0;
while('0'<=b && b<='9'){
ret*=10;
ret+=b-'0';
i++;
b=g_mstr[g_mspos+i];
}
g_mspos+=i;
return ret;
}
void stop_music(){
// Initialize normal music mode.
// Use Timer3 and OC4 for sound.
RPB13R=5; //Use RPB13 for OC4
OC4R=0;
OC4CON=0x000b; // Timer3, toggle mode
OC4CONSET=0x8000;// Start OC4
T3CON=0x0050; // Prescaller: 1:32 (1.8 MHz), not yet started
// Software interrupt every 1/60 sec (triggered by Timer5)
IPC0bits.CS0IP=3;
IPC0bits.CS0IS=0;
IFS0bits.CS0IF=0;
IEC0bits.CS0IE=1;
// Initializations for music/sound.
g_musicstart=g_musicend=g_musicwait=g_soundstart=g_soundend=g_soundwait=g_soundrepeat=0;
g_sound_mode=SOUND_MODE_MUSIC;
// Initialize DMA (see also init_dma_music())
// Stop Timer4
T4CON=0x0000; // Not start yet
// Enable DMA, stop DMA2
DMACONSET=0x8000;
DCH2CONCLR=0x0080;
// Close handle if open.
if (g_fhandle) FSfclose(g_fhandle);
g_fhandle=0;
}
void init_music(){
// Initilize music system
stop_music();
// Initializations for music/sound.
g_qvalue=160; // Q: 1/4=90
g_lvalue=20; // L: 1/8
g_tones=(int*)&(g_keys[49]); // C major
}
void musicSetL(){
// Set length of a character.
// Syntax: L:n/m, where n and m are numbers.
int n,m;
n=musicGetNum();
g_mspos++;
m=musicGetNum();
g_lvalue=g_qvalue*n/m;
}
void musicSetQ(){
int i;
// Syntax: Q:1/4=n, where n is number.
// Skip "1/4="
for(i=0;g_mstr[g_mspos+i]!='=';i++);
g_mspos+=i+1;
i=musicGetNum();
if (i<48) { g_qvalue=320; /* 1/4=45 */ }
else if (i<53) { g_qvalue=288; /* 1/4=50 */ }
else if (i<60) { g_qvalue=256; /* 1/4=56 */ }
else if (i<70) { g_qvalue=224; /* 1/4=64 */ }
else if (i<83) { g_qvalue=192; /* 1/4=75 */ }
else if (i<102) { g_qvalue=160; /* 1/4=90 */ }
else if (i<132) { g_qvalue=128; /* 1/4=113 */ }
else if (i<188) { g_qvalue=96; /* 1/4=150 */ }
else { g_qvalue=64; /* 1/4=225 */ }
g_lvalue=g_qvalue>>3;
}
void musicSetK(){
// Syntax: K:xxx
if (!strncmp((char*)&(g_mstr[g_mspos]),"A#m",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[0]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"D#m",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[7]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"G#m",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[14]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"C#m",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[21]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"F#m",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[28]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Bbm",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[84]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Ebm",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[91]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Abm",3)) {
g_mspos+=3;
g_tones=(int*)&(g_keys[98]);
return;
}
if (!strncmp((char*)&(g_mstr[g_mspos]),"C#",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[0]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"F#",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[7]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Bm",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[35]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Em",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[42]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Am",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[49]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Dm",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[56]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Gm",2) || !strncmp((char*)&(g_mstr[g_mspos]),"Bb",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[63]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Cm",2) || !strncmp((char*)&(g_mstr[g_mspos]),"Eb",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[70]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Fm",2) || !strncmp((char*)&(g_mstr[g_mspos]),"Ab",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[77]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Db",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[84]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Gb",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[91]);
return;
} else if (!strncmp((char*)&(g_mstr[g_mspos]),"Cb",2)) {
g_mspos+=2;
g_tones=(int*)&(g_keys[98]);
return;
}
switch(g_mstr[g_mspos]){
case 'B':
g_mspos++;
g_tones=(int*)&(g_keys[14]);
return;
case 'E':
g_mspos++;
g_tones=(int*)&(g_keys[21]);
return;
case 'A':
g_mspos++;
g_tones=(int*)&(g_keys[28]);
return;
case 'D':
g_mspos++;
g_tones=(int*)&(g_keys[35]);
return;
case 'G':
g_mspos++;
g_tones=(int*)&(g_keys[42]);
return;
case 'C':
g_mspos++;
g_tones=(int*)&(g_keys[49]);
return;
case 'F':
g_mspos++;
g_tones=(int*)&(g_keys[56]);
return;
default:
err_music(g_mstr);
break;
}
}
void musicSetM(){
// Currently do nothing
musicGetNum();
musicGetNum();
}
void set_sound(unsigned long* data, int flagsLR){
// flagsLR is ignored
int sound;
int len;
int pos;
int datalen;
IEC0bits.T4IE=0; // Stop interruption, first.
// Initialize
g_soundrepeat=g_soundstart=g_soundend=0;
pos=0;
do {
while(data[1]!=0x00000020) data++; // Seek DATA statement
datalen=(data[0]&0x00007FFF)-1; // Use bgezal statement containing data length.
data+=2;
while(0<datalen){
datalen--;
len=data[0]>>16;
sound=data[0]&0x0000FFFF;
data++;
if (len) {
// Shift tone if graphic is used
if (g_use_graphic) {
// MUL_15 instead of MUL_16 for CPU clock.
// sound=sound*15/16
sound-=(sound+7)>>4;
}
g_sound[pos]=sound-1;
g_soundlen[pos]=len;
pos++;
if (32<pos) {
err_music("Sound data too long.");
return;
}
} else {
g_soundrepeat=sound;
break;
}
}
} while(len);
g_soundend=pos;
g_soundwait=g_soundlen[0];
IEC0bits.T4IE=1; // Restart interrupt.
}
void set_music(char* str, int flagsLR){
// flagsLR is ignored
char b;
unsigned long tone,tonenatural;
int len;
g_mstr=str;
g_mspos=0;
while(0<(b=g_mstr[g_mspos])){
if (g_mstr[g_mspos+1]==':') {
// Set property
g_mspos+=2;
switch(b){
case 'L':
musicSetL();
break;
case 'Q':
musicSetQ();
break;
case 'K':
musicSetK();
break;
case 'M':
musicSetM();
break;
default:
err_music(str);
break;
}
} else if ('A'<=b && b<='G' || 'a'<=b && b<='g' || b=='z') {
g_mspos++;
if (b=='z') {
tone=0;
} else if (b<='G') {
tone=g_tones[b-'A'];
tonenatural=g_keys[b-'A'+49];
} else {
tone=g_tones[b-'a']>>1;
tonenatural=g_keys[b-'a'+49]>>1;
}
// Check "'"s
while(g_mstr[g_mspos]=='\''){
g_mspos++;
tone>>=1;
}
// Check ","s
while(g_mstr[g_mspos]==','){
g_mspos++;
tone<<=1;
tonenatural<<=1;
}
// Check "^","=","_"
switch(g_mstr[g_mspos]){
case '^':
g_mspos++;
tone=toneSharp(tone);
break;
case '_':
g_mspos++;
tone=toneFlat(tone);
break;
case '=':
g_mspos++;
tone=tonenatural;
break;
default:
break;
}
// Check number for length
b=g_mstr[g_mspos];
if ('0'<=b && b<='9') {
len=g_lvalue*musicGetNum();
} else {
len=g_lvalue;
}
if (g_mstr[g_mspos]=='/') {
g_mspos++;
len=len/musicGetNum();
}
// Shift tone if graphic is used
if (g_use_graphic) {
// MUL_15 instead of MUL_16 for CPU clock.
// tone=tone*15/16
tone-=(tone+7)>>4;
}
// Update music value array
IEC0bits.T4IE=0; // Stop interruption, first.
if (g_musicstart==g_musicend) {
g_musicwait=len;
}
g_music[g_musicend]=(tone-1)&0x0000FFFF;
g_musiclen[g_musicend]=len;
g_musicend++;
g_musicend&=31;
IEC0bits.T4IE=1; // Restart interruption.
} else {
err_music(str);
}
// Go to next character
while(0<g_mstr[g_mspos] && g_mstr[g_mspos]<=0x20 || g_mstr[g_mspos]=='|') g_mspos++;
}
}
/*
PLAYWAVE routines follow
*/
int checkChars(char* str1, char* str2, int num){
int i;
for(i=0;i<num;i++){
if (str1[i]!=str2[i]) return 1;
}
return 0;
}
void init_dma_music(){
// Timer4 for 15700 Hz
T4CON=0x0000; // Not start yet
if (g_use_graphic) {
PR4=3405-1;
} else {
PR4=3632-1;
}
TMR4=PR4-1;
// Timer3 for PWM
TMR3=0;
PR3=0x100;
T3CON=0x8000;
// OC4 setting
RPB13R=5; //Use RPB13 for OC4
OC4RS=0x80;
OC4CON=0x000e;
OC4CONSET=0x8000;
//DMA2 settings for OC4
DMACONSET=0x8000;
DCH2CON=0x00000012; // CHBUSY=0, CHCHNS=0, CHEN=0, CHAED=0, CHCHN=0, CHAEN=1, CHEDET=0, CHPRI=b10
DCH2ECON=0x1310; // CHAIRQ=0, CHSIRQ=19, CFORCE=0, CABRT=0, PATEN=0, SIRQEN=1, AIRQEN=0
// CHSIRQ=19: Timer4 interrupt
DCH2SSA=((unsigned int)&(g_wavtable[0]))&0x1fffffff;
DCH2DSA=0x1F803620; // OC4RS
DCH2SSIZ=524;
DCH2DSIZ=1;
DCH2CSIZ=1;
DCH2INTCLR=0x00FF00FF;
DCH2CONSET=0x00000080;
}
void play_wave(char* filename, int start){
int i;
// Stop the previous play
stop_music();
// Exit function if null filename
if (filename[0]==0x00) {
return;
}
// Alocate 524 byte buffer if not assigned
if (g_var_size[ALLOC_WAVE_BLOCK]==0) {
g_wavtable=(char*)alloc_memory(524/4,ALLOC_WAVE_BLOCK);
}
// Open file
if (g_fhandle) FSfclose(g_fhandle);
g_fhandle=FSfopen(filename,"r");
if (!g_fhandle) err_file();
// Read header and check if monaural 8 bit 16000 Hz.
if (0x2c != FSfread((void*)&g_wavtable[0],1,0x2c,g_fhandle)) err_file();
i=0;
i+=checkChars((char*)&g_wavtable[0],"RIFF",4); // Check RIFF
i+=checkChars((char*)&g_wavtable[8],"WAVEfmt ",8); // Check WAVE and fmt
i+=checkChars((char*)&g_wavtable[16],"\x10\x00\x00\x00\x01\x00",6); // Check if liear PCM
if (!checkChars((char*)&g_wavtable[22],"\x01\x00\x80\x3e\x00\x00\x80\x3e\x00\x00\x01\x00",12)) {
// Monaural 16000 Hz
} else if (!checkChars((char*)&g_wavtable[22],"\x01\x00\x54\x3d\x00\x00\x54\x3d\x00\x00\x01\x00",12)) {
// Monaural 15700 Hz
} else {
i=1;
}
i+=checkChars((char*)&g_wavtable[34],"\x08\x00\x64\x61\x74\x61",6); // Check bit # and data
if (i) err_wave();
// Support defined start position here to skip file pointer here.
FSfseek(g_fhandle, start, SEEK_CUR);
// Read first 262 bytes.
if (262 != FSfread((void*)&g_wavtable[0],1,262,g_fhandle)) err_file();
// Initialize DMA
init_dma_music();
g_sound_mode=SOUND_MODE_WAVE;
}