/* interrupt.c */ /* set up a CIA interrupt to play the music */ /* $Author: Espie $ * $Date: 91/05/20 22:44:37 $ * $Revision: 1.32 $ * $Log: interrupt.c,v $ * Revision 1.32 91/05/20 22:44:37 Espie * *** empty log message *** * * Revision 1.31 91/05/16 15:05:08 Espie * Modified far stuff. * * Revision 1.30 91/05/12 19:55:31 Espie * correct handling of resume. * * Revision 1.29 91/05/12 15:59:08 Espie * Suppressed some unnecessary assignments... More cleanup in order. * * Revision 1.28 91/05/09 17:36:26 Espie * Support for non standard speed modes. * * Revision 1.27 91/05/08 15:51:18 Espie * Added set_volume command for slider. * * Revision 1.26 91/05/07 12:13:06 Espie * *** empty log message *** * * Revision 1.25 91/05/06 23:38:32 Espie * Changed some includes ??? * * Revision 1.24 91/05/06 15:14:46 Espie * Speed is now entirely the responsibility of the player itself. * * Revision 1.23 91/05/05 19:05:38 Espie * Moved most of the player stuff which should be private * to the player itself. * * Revision 1.22 91/05/05 15:38:21 Espie * Play is now private/public... * * Revision 1.21 91/05/02 23:26:38 Espie * Almost tested, reliable. Needs some cleanup. * * Revision 1.20 91/05/02 11:19:22 Espie * Added some more tests... Not incredibly reliable. * * Revision 1.19 91/05/02 01:29:22 Espie * Completely new interface, much safer. * The hardware part is now completely isolated from the software part. * * Revision 1.18 91/04/30 00:35:35 Espie * Stable version III. * * Revision 1.17 91/04/30 00:24:28 Espie * Modified launch_play() slightly: now resets the speed. * * Revision 1.16 91/04/29 15:07:26 Espie * Cleaned-up, now allocates cia timer on the fly. * Important: always ask for audio first, because the system * avoids deadlock in that case. * * Revision 1.15 91/04/29 02:21:39 Espie * Suppressed ``critical sections''. * * Revision 1.14 91/04/28 20:34:43 Espie * Added fine speed control, definitely needs some cleanup now. * * Revision 1.13 91/04/27 20:48:26 Espie * New dual speed tempo. * * Revision 1.12 91/04/27 16:44:17 Espie * Slight changes. * * Revision 1.11 91/04/27 04:00:39 Espie * Little as changed. * * Revision 1.10 91/04/26 16:30:36 Espie * Now the interrupt routines times itself, so there * are new fields to set up correctly. * * Revision 1.9 91/04/24 15:25:35 Espie * Minor changes ?? * * Revision 1.8 91/04/23 21:28:33 Espie * New interrupt settings: since the player knows when not to play anything, * it is no longer necessary to give a valid song to establish the interrupt. * Also, the memory for interrupt and play structure is now dynamically allocated * as public memory, which seems more reasonable. * * Revision 1.7 91/04/21 20:05:16 Espie * Oversampling. * * Revision 1.6 91/04/21 12:11:40 Espie * Stable version, known as bunch II. * Also features ``right'' log description. * * Revision 1.5 91/04/20 18:14:04 Espie * Added symbolic constants everywhere. * * Revision 1.4 91/04/19 13:21:27 Espie * New interrupt setup, still needs to dynamically allocate * interrupt structure, plus some symbolic constants. * * Revision 1.3 91/04/19 02:19:17 Espie * Still many things to do. Concept of a ``play'' structure working. * Timing ok. * * Revision 1.2 91/04/18 20:24:09 Espie * Complete interrupt driver, speed is correct (small bug in play.c), * setup is correct. Take care that you can't change is_Data after * you've added the server. * * Revision 1.1 91/04/18 16:38:05 Espie * Initial revision * */ #include #include #include #include #include #include #include #include #include #include #include "song.h" #include "proto.h" #include "player.h" #include "public_play.h" #include "int_play.h" extern struct ExecBase *SysBase; #ifdef LATTICE #define FAR_SUPPORT __far #else #error #endif extern volatile struct CIA FAR_SUPPORT ciab; struct BattMemBase *BattMemBase; LOCAL CLEAN int_clean; /* this is the logical interface structure with the interrupt * you send things to the interrupt through its data structure (play). * If you specify it, the interrupt will signal you when something * interesting happens (specifiy a task/signal mask...) * * In that case, you can check the play oflags to know what triggered * the signal. Don't forget to reset it yourself if you want to know * what's going on next time. Also, don't forget that the interrupt * won't stop for you, i.e., while you're processing that signal, * the interrupt might send other signals to you. * As a general rule, unless you disable/enable, don't expect any * field in the play structure to stay constant. That's generally not * a problem. */ LOCAL struct play *play; LOCAL struct pub_play *public; LOCAL struct priv_play *private; LOCAL struct Interrupt *timerinterrupt; LOCAL ULONG timerbase; LOCAL ULONG current_tempo = 256, current_effect = 256; LOCAL BOOL available = FALSE, running = FALSE; LOCAL void latch_tempo(void) { Disable(); public->timebase = timerbase*current_tempo/128; public->effectbase = timerbase*current_effect/128; private->tempo_change = TRUE; Enable(); } /* country dependency: the frequency of the timers is different * when you change countries. The precise values are listed * in the hardware manual. */ LOCAL void set_timerbase(void) { switch(SysBase->PowerSupplyFrequency) { case 50: timerbase = 709379; break; case 60: timerbase = 715909; break; default: break; } } LOCAL void timer_on(void) { *private->control |= CIACRAF_START; } LOCAL void timer_off(void) { *private->control &= ~CIACRAF_START; } /* if you want to modify play yourself, don't forget to Disable()/Enable() * all the changes, so that the interrupt routine will always see something * coherent. Don't forget to make it fast */ /* sine table for vibrato command */ BYTE sine_table[32] = {0,25,49,71,90,106,117,125,127,125,117,106,90, 71,49,25,0,-25,-49,-71,-90,-106,-117,-125,-127, -125,-117,-106,-90,-71,-49,-25}; LOCAL void init_structures() { int i; play = AllocMem(sizeof(struct play), MEMF_PUBLIC | MEMF_CLEAR); if (play) ToClean2(FreeMem, play, sizeof(struct play)); else mayPanic("Could not allocate data structure for replay"); public = &(play->public); private = &(play->private); for (i = 0; i < 32; i++) play->sine_table[i] = sine_table[i]; private->sine_table = play->sine_table; for (i = 0; i < NUMBER_TUNING; i++) { play->period_table[i] = play->periods + NUMBER_NOTES * i; } init_periods(play->period_table); private->period_table = play->period_table; for (i = 0; i < NUMBER_TRACKS; i++) private->track[i] = play->tracks + i; private->channel_mask = play->channel_mask; private->setup = play->setup; private->state = init_player; /* don't need that */ /* public->volume = 256; */ public->command = STOP; /* done by the MEMF_CLEAR, but keep as comments that this is a good idea. public->resume = FALSE; public->info = NULL; */ timerinterrupt = AllocMem(sizeof(struct Interrupt), MEMF_PUBLIC|MEMF_CLEAR); if (timerinterrupt) ToClean2(FreeMem, timerinterrupt, sizeof(struct Interrupt)); else mayPanic("Could not allocate interrupt structure"); timerinterrupt->is_Node.ln_Type = NT_INTERRUPT; /* timerinterrupt->is_Node.ln_Pri = 0; */ timerinterrupt->is_Node.ln_Name = "player"; timerinterrupt->is_Data = play; timerinterrupt->is_Code = &do_play; } LOCAL void open_cia(void) { BattMemBase = OpenResource("ciab.resource"); if (!BattMemBase) mayPanic("Could not open ciab resource"); } LOCAL void init_interrupt(void) { int signal; set_timerbase(); init_structures(); open_cia(); signal = AllocSignal(-1); if (signal == -1) mayPanic("No signals available"); else ToClean(FreeSignal, signal); public->signal = 1<task = FindTask(0L); } /* the install timer now puts everything back in the * state it should be */ LOCAL void install_timer(void) { int old; int_clean = AllocClean(NIL); old = AddICRVector(BattMemBase, CIAICRB_TB, timerinterrupt); if (!old) { ToClean3L(int_clean, RemICRVector, BattMemBase, CIAICRB_TB, timerinterrupt); private->control = &(ciab.ciacrb); private->latchlo = &(ciab.ciatblo); private->latchhi = &(ciab.ciatbhi); } else { old = AddICRVector(BattMemBase, CIAICRB_TA, timerinterrupt); if (!old) { ToClean3L(int_clean, RemICRVector, BattMemBase, CIAICRB_TA, timerinterrupt); private->control = &(ciab.ciacra); private->latchlo = &(ciab.ciatalo); private->latchhi = &(ciab.ciatahi); } else mayPanic("Sorry, no timer available"); } ToClean0L(int_clean, timer_off); /* keep the alarm running */ *private->control &= CIACRBF_ALARM; latch_tempo(); } struct pub_play *obtain_player() { init_interrupt(); return public; } void start_timer(void) { if (!available) { install_timer(); timer_on(); available = TRUE; } } void stop_timer(void) { if (available) { timer_off(); CleanUp(int_clean); public->resume = TRUE; available = FALSE; } } /* ``standard'' frequency is 50Hz. * tempo is in 256th. */ void set_tempo(int tempo, int effect) { current_tempo = tempo; current_effect = effect; if (available) latch_tempo(); } void set_volume(int new_value) { public->volume = new_value; } void set_mode(int mode) { public->mode = mode; } void setup_song(struct song *s) { Disable(); public->command = STOP; public->pattern = 0; public->position = 0; public->info = s->info; public->sample = s->samples; public->resume = FALSE; Enable(); } void launch_play(int patt) { if (public->info) { Disable(); public->command = NEWPOS; public->pattern = patt; public->position = 0; Enable(); } }