aylet-0.5/0040711000175000001440000000000010351151134011212 5ustar rususersaylet-0.5/COPYING0100600000175000001440000004307605657456152012301 0ustar rususers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. aylet-0.5/Makefile0100600000175000001440000000423310351150744012657 0ustar rususers# makefile for aylet # You need an ANSI C compiler. gcc is probably best. # CC=gcc # Compiler options (and sound driver) to use. This one is for Linux, # and other OSS-using systems. # CFLAGS=-O -Wall -DDRIVER_OSS # for OpenBSD, uncomment this: #CFLAGS=-O -Wall -DDRIVER_OPENBSD # Set how to link the curses lib - this is for Linux. # (Most non-Linux systems will probably want `-lcurses' instead.) # CURSES_LIB=-lncurses # dest for make install # PREFIX=/usr/local BINDIR=$(PREFIX)/bin XBINDIR=$(BINDIR) MANDIR=$(PREFIX)/man/man1 # if you want the X version to be installed in the usual X executables # directory, uncomment this (altering if necessary): # #XBINDIR=/usr/X11R6/bin # you shouldn't need to edit the rest #----------------------------------------------------------------- # this looks wrong, but *ops.c are actually #include'd by z80.c OBJS=main.o sound.o ui.o z80.o drv-oss.o drv-obsd.o XOBJS=main.o sound.o uigtk.o z80.o drv-oss.o drv-obsd.o all: aylet xaylet aylet: $(OBJS) $(CC) -o aylet $(OBJS) $(CURSES_LIB) xaylet: $(XOBJS) $(CC) -o xaylet $(XOBJS) `gtk-config --libs` uigtk.o: uigtk.c $(CC) $(CFLAGS) `gtk-config --cflags` -c uigtk.c -o uigtk.o installdirs: /bin/sh ./mkinstalldirs $(BINDIR) $(XBINDIR) $(MANDIR) install: installdirs if [ -f aylet ]; then install -m 755 aylet $(BINDIR); fi if [ -f xaylet ]; then install -m 755 xaylet $(XBINDIR); fi install -m 644 aylet.1 $(MANDIR) ln -sf $(MANDIR)/aylet.1 $(MANDIR)/xaylet.1 uninstall: $(RM) $(BINDIR)/aylet $(XBINDIR)/xaylet $(RM) $(MANDIR)/aylet.1* $(MANDIR)/xaylet.1* clean: $(RM) *.o *~ aylet xaylet # dependencies cbops.o: cbops.c drv-obsd.o: drv-obsd.c drv-oss.o: drv-oss.c edops.o: edops.c main.o: main.c main.h sound.h ui.h z80.h sound.o: sound.c main.h z80.h sound.h driver.h ui.o: ui.c main.h ui.h uigtk.o: uigtk.c main.h ui.h button1.xpm button2.xpm button3.xpm \ button4.xpm button5.xpm z80.o: z80.c main.h z80.h z80ops.c cbops.c edops.c z80ops.o: z80ops.c cbops.c edops.c VERS=0.5 tgz: ../aylet-$(VERS).tar.gz ../aylet-$(VERS).tar.gz: clean $(RM) ../aylet-$(VERS) @cd ..;ln -s aylet aylet-$(VERS) cd ..;tar zchvf aylet-$(VERS).tar.gz aylet-$(VERS) @cd ..;$(RM) aylet-$(VERS) aylet-0.5/main.c0100600000175000001440000004554010351150611012306 0ustar rususers/* aylet 0.4, a .AY music file player. * Copyright (C) 2001-2005 Russell Marks and Ian Collier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "main.h" #include "sound.h" #include "ui.h" #include "z80.h" #define AYLET_VER "0.5" #define FRAME_STATES_48 (3500000/50) #define FRAME_STATES_128 (3546900/50) #define FRAME_STATES_CPC (4000000/50) /* see main.h */ struct aydata_tag aydata; struct time_tag tunetime; char *progname; /* start stopping (errr) after this many sec, 0=never. * This is too short to allow some things to finish (Agent X 2 * springs to mind), but *so* many tunes repeat after less than 2 mins * that I think this is a sensible default. */ int stopafter=3*60; int fadetime=10; /* fadeout time *after* that in sec, 0=none */ static int done_fade=0; /* the memory is a flat all-RAM 64k */ unsigned char mem[64*1024]; unsigned long tstates=0,tsmax=FRAME_STATES_128; int ay_current_reg=0; int silent_max=4*50; /* max frames of silence before skipping */ int highspeed=0; int playing=1; int paused=0; int want_quit=0; int use_ui=1; int play_to_stdout=0; int list_only=0; char **ay_filenames=NULL; /* for ptrs to filenames */ int ay_num_files=0; int ay_file=0; int ay_track=0; /* for prev-track - when we skip back a file, this flag * indicates that we want to start at the last track. */ int go_to_last=0; /* -1 to run as speccy, allowing only speccy ports; * 0 to run as speccy, and allow speccy and CPC ports (initial value); * 1 to run as CPC, allowing only CPC ports. */ int do_cpc=0; /* 0 if playing "normal" (or with curses/GTK UI) * # of track otherwise * track number passed through additional parameter (-t) */ int play_one_track_only=0; unsigned int in(int h,int l) { /* presumably nothing? XXX */ return(255); } unsigned int out(int h,int l,int a) { static int cpc_f4=0; /* unlike a real speccy, it seems we should only emulate exact port * number matches, rather than using bitmasks. */ if(do_cpc<1) switch(l) { case 0xfd: switch(h) { case 0xff: do_cpc=-1; write_reg: ay_current_reg=(a&15); break; case 0xbf: do_cpc=-1; write_dat: sound_ay_write(ay_current_reg,a,tstates); break; default: /* ok, since we do at least have low byte=FDh, * do bitmask for top byte to allow for * crappy .ay conversions. But don't disable * CPC autodetect, just in case. */ if((h&0xc0)==0xc0) goto write_reg; if((h&0xc0)==0x80) goto write_dat; } break; case 0xfe: do_cpc=-1; sound_beeper(a&0x10); break; } if(do_cpc>-1) switch(h) { case 0xf6: switch(a&0xc0) { case 0x80: /* write */ sound_ay_write(ay_current_reg,cpc_f4,tstates); break; case 0xc0: /* select */ ay_current_reg=(cpc_f4&15); break; } break; case 0xf4: cpc_f4=a; if(!do_cpc) { /* restart as a more CPC-ish emulation */ do_cpc=1; sound_ay_reset_cpc(); tsmax=FRAME_STATES_CPC; if(tstates>tsmax) tstates-=tsmax; } break; } return(0); /* additional t-states */ } void mem_init(int track) { static unsigned char intz[]= { 0xf3, /* di */ 0xcd,0,0, /* call init */ 0xed,0x5e, /* loop: im 2 */ 0xfb, /* ei */ 0x76, /* halt */ 0x18,0xfa /* jr loop */ }; static unsigned char intnz[]= { 0xf3, /* di */ 0xcd,0,0, /* call init */ 0xed,0x56, /* loop: im 1 */ 0xfb, /* ei */ 0x76, /* halt */ 0xcd,0,0, /* call interrupt */ 0x18,0xf7 /* jr loop */ }; int init,ay_1st_block,ourinit,interrupt; unsigned char *ptr; int addr,len,ofs; #define GETWORD(x) (((*(x))<<8)|(*(x+1))) init=GETWORD(aydata.tracks[track].data_stacketc+2); interrupt=GETWORD(aydata.tracks[track].data_stacketc+4); ay_1st_block=GETWORD(aydata.tracks[track].data_memblocks); memset(mem+0x0000,0xc9,0x0100); memset(mem+0x0100,0xff,0x3f00); memset(mem+0x4000,0x00,0xc000); mem[0x38]=0xfb; /* ei */ /* call first AY block if no init */ ourinit=(init?init:ay_1st_block); if(!interrupt) memcpy(mem,intz,sizeof(intz)); else { memcpy(mem,intnz,sizeof(intnz)); mem[ 9]=interrupt%256; mem[10]=interrupt/256; } mem[2]=ourinit%256; mem[3]=ourinit/256; /* now put the memory blocks in place */ ptr=aydata.tracks[track].data_memblocks; while((addr=GETWORD(ptr))!=0) { len=GETWORD(ptr+2); ofs=GETWORD(ptr+4); if(ofs>=0x8000) ofs=-0x10000+ofs; /* range check */ if(ptr-4-aydata.filedata+ofs>=aydata.filelen || ptr-4-aydata.filedata+ofs<0) { ptr+=6; continue; } /* fix any broken length */ if(ptr+4+ofs+len>=aydata.filedata+aydata.filelen) len=aydata.filedata+aydata.filelen-(ptr+4+ofs); if(addr+len>0x10000) len=0x10000-addr; memcpy(mem+addr,ptr+4+ofs,len); ptr+=6; } } /* do action requested by UI code. returns zero if we need to * exit the current track. */ int action_callback(enum cb_action_tag action) { switch(action) { case cb_quit: /* we have to pretend to be playing to get out... :-) */ playing=1; paused=0; want_quit=1; return(0); /* exit track */ case cb_highspeed: highspeed=!highspeed; ui_change_notify(); break; case cb_prev_file: do_prev_file: if(ay_file<=0) { go_to_last=0; /* wouldn't need that now */ goto do_stop; } ay_file--; goto do_move; case cb_next_file: do_next_file: if(ay_file>=ay_num_files-1) goto do_stop; ay_file++; goto do_move; case cb_prev_track: if(ay_track<=0) { go_to_last=1; goto do_prev_file; } ay_track--; goto do_move; case cb_next_track: if(ay_track>=aydata.num_tracks-1) goto do_next_file; ay_track++; do_move: paused=0; ui_change_notify(); return(0); case cb_play: playing=1; if(paused) paused=0; ui_change_notify(); break; case cb_pause: if(playing) paused=!paused,ui_change_notify(); break; case cb_stop: do_stop: if(playing) { playing=paused=0; ui_change_notify(); return(0); } break; case cb_restart: ui_change_notify(); return(0); case cb_dec_stopafter: if(!(stopafter%30)) stopafter-=30; else stopafter-=stopafter%30; if(stopafter<0) stopafter=10*60; ui_change_notify(); break; case cb_inc_stopafter: if(!(stopafter%30)) stopafter+=30; else stopafter+=(30-stopafter%30); if(stopafter>10*60) stopafter=0; ui_change_notify(); break; case cb_dec_fadetime: fadetime--; if(fadetime<1) fadetime=20; ui_change_notify(); break; case cb_inc_fadetime: fadetime++; if(fadetime>20) fadetime=1; ui_change_notify(); break; case cb_dec_vol: case cb_inc_vol: /* XXX NYI */ break; default: /* for cb_none */ break; } return(1); } /* rets zero if we want to exit the emulation (i.e. exit track) */ int do_interrupt(void) { static int count=0; static int silent_for=0; count++; if(count>=4) count=0; if(!playing || paused) usleep(20000); else { /* check for fade needed */ if(!done_fade && stopafter && tunetime.min*60+tunetime.sec>=stopafter) { done_fade=1; sound_start_fade(fadetime); } /* incr time */ tunetime.subsecframes++; if(tunetime.subsecframes>=50) { tunetime.subsecframes=0; tunetime.sec++; if(tunetime.sec>=60) { tunetime.sec=0; tunetime.min++; } } /* play frame, and stop if it's been silent for a while */ if(!sound_frame(count==0 || !highspeed)) silent_for++; else silent_for=0; if(silent_for>=silent_max) { silent_for=0; ui_change_notify(); /* do next track, or file, or just stop */ /* if play_one_track_only is set, then finish now */ if (play_one_track_only) { want_quit=1; return(0); } ay_track++; if(ay_track>=aydata.num_tracks) { ay_track=0; ay_file++; if(ay_file>=ay_num_files) { /* return to first file/track (except for non-UI, * to save any pointless reload), and stop. */ if(use_ui) ay_file=ay_track=0; else ay_file--,ay_track=aydata.num_tracks-1; playing=0; } } return(0); } } return(ui_frame()); } int read_ay_file(char *filename) { FILE *in; unsigned char *data,*buf,*ptr,*ptr2; int data_alloc=16384,buf_alloc=16384,data_ofs=0; int data_len; int ret,tmp,f; /* given the loopy format, it's much easier to deal with in memory. * But I'm avoiding mmap() in case I want to tweak this to run from * a pipe at some point. */ if((buf=malloc(buf_alloc))==NULL) return(0); if((data=malloc(data_alloc))==NULL) { free(buf); return(0); } if((in=fopen(filename,"rb"))==NULL) { free(buf); free(data); return(0); } while((ret=fread(buf,1,buf_alloc,in))>0) { if(data_ofs+ret>=data_alloc) { unsigned char *oldptr=data; data_alloc+=buf_alloc; if((data=realloc(data,data_alloc))==NULL) { fclose(in); free(oldptr); free(buf); return(0); } } memcpy(data+data_ofs,buf,ret); data_ofs+=ret; } free(buf); if(ferror(in)) { fclose(in); free(data); return(0); } fclose(in); data_len=data_ofs; if(memcmp(data,"ZXAYEMUL",8)!=0) { free(data); return(0); } /* for the rest, we don't parse that much; just make copies of the * offset `pointers' as real pointers, and save all the `top-level' * stuff. */ aydata.tracks=NULL; #define READWORD(x) (x)=256*(*ptr++); (x)|=*ptr++ #define READWORDPTR(x) READWORD(tmp); \ if(tmp>=0x8000) tmp=-0x10000+tmp; \ if(ptr-data-2+tmp>=data_len || ptr-data-2+tmp<0) \ { \ free(data); \ if(aydata.tracks) free(aydata.tracks); \ return(0); \ } \ (x)=ptr-2+tmp #define CHECK_ASCIIZ(x) \ if(!memchr((x),0,data+data_len-(x))) \ { \ free(data); \ if(aydata.tracks) free(aydata.tracks); \ return(0); \ } ptr=data+8; aydata.filever=*ptr++; aydata.playerver=*ptr++; ptr+=2; /* skip `custom player' stuff */ READWORDPTR(aydata.authorstr); CHECK_ASCIIZ(aydata.authorstr); READWORDPTR(aydata.miscstr); CHECK_ASCIIZ(aydata.miscstr); aydata.num_tracks=1+*ptr++; aydata.first_track=*ptr++; /* skip to track info */ READWORDPTR(ptr2); ptr=ptr2; if((aydata.tracks=malloc(aydata.num_tracks*sizeof(struct ay_track_tag)))==NULL) { free(data); return(0); } for(f=0;fdata_len-4) { free(aydata.tracks); free(data); return(0); } ptr=aydata.tracks[f].data+10; READWORDPTR(aydata.tracks[f].data_stacketc); READWORDPTR(aydata.tracks[f].data_memblocks); ptr=aydata.tracks[f].data+4; READWORD(aydata.tracks[f].fadestart); READWORD(aydata.tracks[f].fadelen); } /* ok then, that's as much parsing as we do here. */ aydata.filedata=data; aydata.filelen=data_len; return(1); } static void tunetime_reset(void) { tunetime.min=tunetime.sec=tunetime.subsecframes=0; done_fade=0; } /* read a file and play it. sound_init() should already have been called, * and sound device should still be open when we exit. */ void mainloop(void) { int oldfile=-1; aydata.filedata=NULL; aydata.tracks=NULL; /* this is kind of a weird multi-level event loop (if * you consider do_interrupt()); it's difficult to do it very * differently without turning the Z80 emulation inside-out. */ while(!want_quit) { /* load a new file if we need to */ if(ay_file!=oldfile) { if(aydata.tracks) free(aydata.tracks); if(aydata.filedata) free(aydata.filedata); if(!read_ay_file(ay_filenames[ay_file])) { ui_end(); if(sound_enabled) sound_end(); fprintf(stderr,"%s: reading `%s' failed.\n", progname,ay_filenames[ay_file]); exit(1); } if(!play_one_track_only) ay_track=0; else { ay_track=play_one_track_only-1; if(ay_track>=aydata.num_tracks) { ui_end(); if(sound_enabled) sound_end(); fprintf(stderr,"%s: `%s' has only %d track%s.\n", progname,ay_filenames[ay_file], aydata.num_tracks,(aydata.num_tracks==1)?"":"s"); exit(1); } } if(go_to_last) { go_to_last=0; ay_track=aydata.num_tracks-1; } } oldfile=ay_file; /* only do the whole emulation thing if we're actually playing... */ if(playing) { /* re-enable sound after stopping, if needed */ if(!sound_enabled && !sound_init()) { ui_end(); fprintf(stderr,"%s: couldn't reopen sound device.\n",progname); exit(1); } ay_current_reg=0; sound_ay_reset(); mem_init(ay_track); tunetime_reset(); tsmax=FRAME_STATES_128; do_cpc=0; z80loop(aydata.tracks[ay_track].data, aydata.tracks[ay_track].data_stacketc); } /* if stopped, close sound device */ if(sound_enabled && !playing) sound_end(); /* do reset now, so any paused/stopped status time makes sense */ tunetime_reset(); while((!playing || paused) && ay_file==oldfile) do_interrupt(); } free(aydata.tracks); free(aydata.filedata); } void usage_help(void) { printf("%s " AYLET_VER " - copyright (C) 2001-2005 Russell Marks and Ian Collier.\n\n",progname); printf("usage: %s [-BhlmnNsS] [-A stopafter] [-F fadetime] [-t tracknum]\n" "\t\tfile [file2 ...]\n", progname); puts("\n" " -A set stop-after time in seconds (the time at which tracks\n" " start fading out), default 180 seconds (3 minutes).\n" "\n" " -B use ABC stereo (default ACB).\n" "\n" " -e force 8-bit output even if 16-bit playback is possible.\n" "\n" " -F set fade-out time in seconds (the time tracks take to\n" " fade out), default 10 seconds.\n" "\n" " -h give this help.\n" "\n" " -l list contents of the files, rather than playing them.\n" "\n" " -m use mono rather than the default stereo.\n" "\n" " -n use a simple batch-playing tty style, rather than the\n" " usual interface. (Only works in the curses version.)\n" "\n" " -N use narrower AY stereo separation.\n" "\n" " -s output sample data to stdout rather than playing it;\n" " implies `-n'. The sample is 44.1kHz 16-bit stereo using\n" " the machine's native byte ordering (or mono if using `-m',\n" " and 8-bit if using `-e').\n" "\n" " -S use fake pseudo-stereo for the beeper.\n" "\n" " -t play specified track, then quit.\n"); } void parseoptions(int argc,char *argv[]) { int done=0; opterr=0; do switch(getopt(argc,argv,"A:BeF:hlmnNsSt:")) { case 'A': /* stopafter */ stopafter=atoi(optarg); if(stopafter<0) stopafter=0; if(stopafter>10*60) stopafter=10*60; break; case 'B': /* ABC stereo (not ACB) */ sound_stereo_ay_abc=1; break; case 'e': /* eight-bit-only */ sixteenbit=0; break; case 'F': /* fadetime */ fadetime=atoi(optarg); if(fadetime<1) fadetime=1; if(fadetime>20) fadetime=20; break; case 'h': usage_help(); exit(0); case 'l': /* list tracks etc. rather than playing */ list_only=1; break; case 'm': /* mono */ sound_stereo=0; break; case 'N': /* narrow stereo separation */ sound_stereo_ay_narrow=1; break; case 'n': /* no UI (effective in non-X ver only) */ use_ui=0; break; case 'S': /* pseudostereo (for beeper) */ sound_stereo_beeper=1; break; case 's': /* play to stdout */ use_ui=0; /* implied */ play_to_stdout=1; break; case 't': if (!(play_one_track_only=atoi(optarg))) { fprintf(stderr, "%s: error parsing an argument.\n This should be " "a numerical value betwen 1 and num_tracks.\n", progname); exit(1); } break; case '?': switch(optopt) { case 'A': fprintf(stderr,"%s: " "the `-A' option requires a stop-after time (in seconds).\n", progname); break; case 'F': fprintf(stderr,"%s: " "the `-F' option requires a fade-out time (in seconds).\n", progname); break; case 't': fprintf(stderr,"%s: the -t option requires a track number to " "play.\n",progname); break; default: fprintf(stderr,"%s: " "option `%c' not recognised.\n", progname,optopt); } exit(1); case -1: done=1; } while(!done); if(optind>=argc) /* if no filenames given... */ { fprintf(stderr,"%s: you must specify the file(s) to %s.\n", progname,list_only?"list":"play"); exit(1); } ay_filenames=argv+optind; ay_num_files=argc-optind; } void do_list(void) { char *ptr; int f,g; aydata.filedata=NULL; aydata.tracks=NULL; for(f=0;f #include #include #include #include #include "main.h" #include "z80.h" #include "sound.h" #include "driver.h" /* configuration */ int soundfd=-1; /* file descriptor for the sound device */ int sixteenbit=1; /* use sixteen-bit audio? */ int sound_enabled=0; int sound_freq=44100; int sound_stereo=1; /* true for stereo *output sample* (only) */ int sound_stereo_beeper=0; /* beeper pseudo-stereo */ int sound_stereo_ay=1; /* AY stereo separation */ int sound_stereo_ay_abc=0; /* (AY stereo) true for ABC stereo, else ACB */ int sound_stereo_ay_narrow=0; /* (AY stereo) true for narrow AY st. sep. */ #define AY_CLOCK 1773400 #define AY_CLOCK_CPC 1000000 /* assume all three tone channels together match the beeper volume. * (XXX maybe not - that makes beeper stuff annoyingly loud) * Must be <=127 for all channels; 40+(28*3) = 124. * (Now scaled up for 16-bit.) */ #define AMPL_BEEPER (40*256) #define AMPL_AY_TONE (28*256) /* three of these */ /* full range of beeper volume */ #define VOL_BEEPER (AMPL_BEEPER*2) /* max. number of sub-frame AY port writes allowed; * given the number of port writes theoretically possible in a * 50th I think this should be plenty. */ #define AY_CHANGE_MAX 8000 static int sound_framesiz; static unsigned int ay_tone_levels[16]; static signed short *sound_buf; static int sound_oldpos,sound_fillpos,sound_oldval,sound_oldval_orig; /* foo_subcycles are fixed-point with low 16 bits as fractional part. * The other bits count as the chip does. */ static unsigned int ay_tone_tick[3],ay_tone_high[3],ay_noise_tick; static unsigned int ay_tone_subcycles,ay_env_subcycles; static unsigned int ay_env_internal_tick,ay_env_tick; static unsigned int ay_tick_incr; static unsigned int ay_tone_period[3],ay_noise_period,ay_env_period; static int beeper_last_subpos=0; /* AY registers */ /* we have 16 so we can fake an 8910 if needed (XXX any point?) */ static unsigned char sound_ay_registers[16]; struct ay_change_tag { unsigned long tstates; unsigned short ofs; unsigned char reg,val; }; static struct ay_change_tag ay_change[AY_CHANGE_MAX]; static int ay_change_count; static int fading=0,fadetotal; static int sfadetime; #define STEREO_BUF_SIZE 1024 static int pstereobuf[STEREO_BUF_SIZE]; static int pstereobufsiz,pstereopos; static int psgap=250; static int rstereobuf_l[STEREO_BUF_SIZE],rstereobuf_r[STEREO_BUF_SIZE]; static int rstereopos,rchan1pos,rchan2pos,rchan3pos; void sound_ay_init(void) { /* AY output doesn't match the claimed levels; these levels are based * on the measurements posted to comp.sys.sinclair in Dec 2001 by * Matthew Westcott, adjusted as I described in a followup to his post, * then scaled to 0..0xffff. */ static int levels[16]= { 0x0000, 0x0385, 0x053D, 0x0770, 0x0AD7, 0x0FD5, 0x15B0, 0x230C, 0x2B4C, 0x43C1, 0x5A4B, 0x732F, 0x9204, 0xAFF1, 0xD921, 0xFFFF }; int f; /* scale the values down to fit */ for(f=0;f<16;f++) ay_tone_levels[f]=(levels[f]*AMPL_AY_TONE+0x8000)/0xffff; ay_noise_tick=ay_noise_period=0; ay_env_internal_tick=ay_env_tick=ay_env_period=0; ay_tone_subcycles=ay_env_subcycles=0; for(f=0;f<3;f++) ay_tone_tick[f]=ay_tone_high[f]=0,ay_tone_period[f]=1; #define CLOCK_RESET(clock) ay_tick_incr=(int)(65536.*clock/sound_freq) CLOCK_RESET(AY_CLOCK); ay_change_count=0; } int sound_init(void) { int f; if(!driver_init(&sound_freq,&sound_stereo)) return(0); /* important to override these if not using stereo */ if(!sound_stereo) { sound_stereo_ay=0; sound_stereo_beeper=0; } sound_enabled=1; sound_framesiz=sound_freq/50; if((sound_buf=malloc(sizeof(signed short)*sound_framesiz*(sound_stereo+1)))==NULL) { sound_end(); return(0); } sound_oldval=sound_oldval_orig=0; sound_oldpos=-1; sound_fillpos=0; sound_ay_init(); if(sound_stereo_beeper) { for(f=0;f AMPL_BEEPER) bl= AMPL_BEEPER; if(br> AMPL_BEEPER) br= AMPL_BEEPER; *out=bl; out[1]=br; pstereobuf[pstereopos]=c; pstereopos++; if(pstereopos>=pstereobufsiz) pstereopos=0; } /* not great having this as a macro to inline it, but it's only * a fairly short routine, and it saves messing about. * (XXX ummm, possibly not so true any more :-)) */ #define AY_GET_SUBVAL(chan) (level*2*ay_tone_tick[chan]/tone_count) #define AY_DO_TONE(var,chan) \ is_low=0; \ if(is_on) \ { \ (var)=0; \ if(level) \ { \ if(ay_tone_high[chan]) \ (var)= (level); \ else \ (var)=-(level),is_low=1; \ } \ } \ \ ay_tone_tick[chan]+=tone_count; \ count=0; \ while(ay_tone_tick[chan]>=ay_tone_period[chan]) \ { \ count++; \ ay_tone_tick[chan]-=ay_tone_period[chan]; \ ay_tone_high[chan]=!ay_tone_high[chan]; \ \ /* has to be here, unfortunately... */ \ if(is_on && count==1 && level && ay_tone_tick[chan]1) \ (var)=-(level) /* add val, correctly delayed on either left or right buffer, * to add the AY stereo positioning. This doesn't actually put * anything directly in soundbuf, though. */ #define GEN_STEREO(pos,val) \ if((pos)<0) \ { \ rstereobuf_l[rstereopos]+=(val); \ rstereobuf_r[(rstereopos-pos)%STEREO_BUF_SIZE]+=(val); \ } \ else \ { \ rstereobuf_l[(rstereopos+pos)%STEREO_BUF_SIZE]+=(val); \ rstereobuf_r[rstereopos]+=(val); \ } /* bitmasks for envelope */ #define AY_ENV_CONT 8 #define AY_ENV_ATTACK 4 #define AY_ENV_ALT 2 #define AY_ENV_HOLD 1 static void sound_ay_overlay(void) { static int rng=1; static int noise_toggle=0; static int env_first=1,env_rev=0,env_counter=15; int tone_level[3]; int mixer,envshape; int f,g,level,count; signed short *ptr; struct ay_change_tag *change_ptr=ay_change; int changes_left=ay_change_count; int reg,r; int is_low,is_on; int chan1,chan2,chan3; int frametime=tsmax*50; unsigned int tone_count,noise_count; /* convert change times to sample offsets */ for(f=0;f=change_ptr->ofs) { sound_ay_registers[reg=change_ptr->reg]=change_ptr->val; change_ptr++; changes_left--; /* fix things as needed for some register changes */ switch(reg) { case 0: case 1: case 2: case 3: case 4: case 5: r=reg>>1; /* a zero-len period is the same as 1 */ ay_tone_period[r]=(sound_ay_registers[reg&~1]| (sound_ay_registers[reg|1]&15)<<8); if(!ay_tone_period[r]) ay_tone_period[r]++; /* important to get this right, otherwise e.g. Ghouls 'n' Ghosts * has really scratchy, horrible-sounding vibrato. */ if(ay_tone_tick[r]>=ay_tone_period[r]*2) ay_tone_tick[r]%=ay_tone_period[r]*2; break; case 6: ay_noise_tick=0; ay_noise_period=(sound_ay_registers[reg]&31); break; case 11: case 12: /* this one *isn't* fixed-point */ ay_env_period=sound_ay_registers[11]|(sound_ay_registers[12]<<8); break; case 13: ay_env_internal_tick=ay_env_tick=ay_env_subcycles=0; env_first=1; env_rev=0; env_counter=(sound_ay_registers[13]&AY_ENV_ATTACK)?0:15; break; } } /* the tone level if no enveloping is being used */ for(g=0;g<3;g++) tone_level[g]=ay_tone_levels[sound_ay_registers[8+g]&15]; /* envelope */ envshape=sound_ay_registers[13]; level=ay_tone_levels[env_counter]; for(g=0;g<3;g++) if(sound_ay_registers[8+g]&16) tone_level[g]=level; /* envelope output counter gets incr'd every 16 AY cycles. * Has to be a while, as this is sub-output-sample res. */ ay_env_subcycles+=ay_tick_incr; noise_count=0; while(ay_env_subcycles>=(16<<16)) { ay_env_subcycles-=(16<<16); noise_count++; ay_env_tick++; while(ay_env_tick>=ay_env_period) { ay_env_tick-=ay_env_period; /* do a 1/16th-of-period incr/decr if needed */ if(env_first || ((envshape&AY_ENV_CONT) && !(envshape&AY_ENV_HOLD))) { if(env_rev) env_counter-=(envshape&AY_ENV_ATTACK)?1:-1; else env_counter+=(envshape&AY_ENV_ATTACK)?1:-1; if(env_counter<0) env_counter=0; if(env_counter>15) env_counter=15; } ay_env_internal_tick++; while(ay_env_internal_tick>=16) { ay_env_internal_tick-=16; /* end of cycle */ if(!(envshape&AY_ENV_CONT)) env_counter=0; else { if(envshape&AY_ENV_HOLD) { if(env_first && (envshape&AY_ENV_ALT)) env_counter=(env_counter?0:15); } else { /* non-hold */ if(envshape&AY_ENV_ALT) env_rev=!env_rev; else env_counter=(envshape&AY_ENV_ATTACK)?0:15; } } env_first=0; } /* don't keep trying if period is zero */ if(!ay_env_period) break; } } /* generate tone+noise... or neither. * (if no tone/noise is selected, the chip just shoves the * level out unmodified. This is used by some sample-playing * stuff.) */ chan1=tone_level[0]; chan2=tone_level[1]; chan3=tone_level[2]; mixer=sound_ay_registers[7]; ay_tone_subcycles+=ay_tick_incr; tone_count=ay_tone_subcycles>>(3+16); ay_tone_subcycles&=(8<<16)-1; level=chan1; is_on=!(mixer&1); AY_DO_TONE(chan1,0); if((mixer&0x08)==0 && noise_toggle) chan1=0; level=chan2; is_on=!(mixer&2); AY_DO_TONE(chan2,1); if((mixer&0x10)==0 && noise_toggle) chan2=0; level=chan3; is_on=!(mixer&4); AY_DO_TONE(chan3,2); if((mixer&0x20)==0 && noise_toggle) chan3=0; /* write the sample(s) */ if(!sound_stereo) { /* mono */ (*ptr++)+=chan1+chan2+chan3; } else { if(!sound_stereo_ay) { /* stereo output, but mono AY sound; still, * incr separately in case of beeper pseudostereo. */ (*ptr++)+=chan1+chan2+chan3; (*ptr++)+=chan1+chan2+chan3; } else { /* stereo with ACB AY positioning. * Here we use real stereo positions for the channels. * Just because, y'know, it's cool and stuff. No, really. :-) * This is a little tricky, as it works by delaying sounds * on the left or right channels to model the delay you get * in the real world when sounds originate at different places. */ GEN_STEREO(rchan1pos,chan1); GEN_STEREO(rchan2pos,chan2); GEN_STEREO(rchan3pos,chan3); (*ptr++)+=rstereobuf_l[rstereopos]; (*ptr++)+=rstereobuf_r[rstereopos]; rstereobuf_l[rstereopos]=rstereobuf_r[rstereopos]=0; rstereopos++; if(rstereopos>=STEREO_BUF_SIZE) rstereopos=0; } } /* update noise RNG/filter */ ay_noise_tick+=noise_count; while(ay_noise_tick>=ay_noise_period) { ay_noise_tick-=ay_noise_period; if((rng&1)^((rng&2)?1:0)) noise_toggle=!noise_toggle; /* rng is 17-bit shift reg, bit 0 is output. * input is bit 0 xor bit 2. */ rng|=((rng&1)^((rng&4)?1:0))?0x20000:0; rng>>=1; /* don't keep trying if period is zero */ if(!ay_noise_period) break; } } } /* don't make the change immediately; record it for later, * to be made by sound_frame() (via sound_ay_overlay()). */ void sound_ay_write(int reg,int val,unsigned long tstates) { if(!sound_enabled) return; if(reg>=15) return; if(ay_change_count>4)/(fadetotal>>4); if(sound_stereo) { ptr++; *ptr=(*ptr)*(sfadetime>>4)/(fadetotal>>4); } } } } if(really_play) driver_frame(sound_buf,fulllen); sound_oldpos=-1; sound_fillpos=0; ay_change_count=0; return(!silent); } /* don't do a real frame, just play silence to keep things sane. */ void sound_frame_blank(void) { static int first=1; static signed short buf[2048]; /* should be plenty */ int fulllen=sound_framesiz*(sound_stereo+1); if(first) { first=0; memset(buf,0,sizeof(buf)); } /* just in case it's *not* plenty... :-) */ if(sizeof(buf)=0) { /* fill gap from previous position */ ptr=sound_buf+(sound_stereo?sound_fillpos*2:sound_fillpos); for(f=sound_fillpos;f #include #include #include #include "main.h" #include "ui.h" /* button pixmaps */ #include "button1.xpm" #include "button2.xpm" #include "button3.xpm" #include "button4.xpm" #include "button5.xpm" static GtkWidget *window,*vbox,*detailstbl,*detaillabel[5]; static GtkWidget *label_for_status,*label_for_time; static GtkWidget *highspeed_widget; static GtkWidget *label_for_stopafter,*label_for_fadetime; static int used_ui=0,need_update=1; static int retval=1; static void action(enum cb_action_tag a) { /* only set retval if we get a zero return, i.e. we need to * stop current track. */ if(!action_callback(a)) retval=0; } static void cb_button_prev_track(void) { action(cb_prev_track); } static void cb_button_next_track(void) { action(cb_next_track); } static void cb_button_play(void) { action(cb_play); } static void cb_button_pause(void) { action(cb_pause); } static void cb_button_stop(void) { action(cb_stop); } static void cb_button_restart(void) { action(cb_restart); } static void cb_button_prev_file(void) { action(cb_prev_file); } static void cb_button_next_file(void) { action(cb_next_file); } static void cb_toggle_highspeed(void) { action(cb_highspeed); } static void cb_doquit(void) { window=NULL; action(cb_quit); } /* shouldn't really be needing this, but I had trouble * with a few bits and pieces. */ gint keypress(GtkWidget *widget,GdkEventKey *event) { switch(event->keyval) { case GDK_BackSpace: case GDK_Delete: action(cb_prev_file); break; case GDK_space: action(cb_next_file); break; case GDK_q: case GDK_Escape: cb_doquit(); break; case GDK_s: case GDK_S: action((event->state&GDK_SHIFT_MASK)?cb_dec_stopafter:cb_inc_stopafter); break; case GDK_f: case GDK_F: action((event->state&GDK_SHIFT_MASK)?cb_dec_fadetime:cb_inc_fadetime); break; default: return(FALSE); /* don't stop event if not handled */ } /* if we handled it, stop anything else getting the event. */ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),"key_press_event"); return(TRUE); } static void ui_initwin(void) { GtkWidget *statustbl,*buttontbl; GtkWidget *sep,*label,*hbox,*button,*ebox; GtkWidget *pixmap; GdkBitmap *bitmap,*mask; int tbl_row,width; window=gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(window),"destroy", GTK_SIGNAL_FUNC(cb_doquit),NULL); gtk_signal_connect(GTK_OBJECT(window),"key_press_event", GTK_SIGNAL_FUNC(keypress),NULL); gtk_widget_set_events(window,GDK_KEY_PRESS_MASK); gtk_window_set_title(GTK_WINDOW(window),"xaylet"); gtk_window_set_policy(GTK_WINDOW(window),FALSE,TRUE,FALSE); /* XXX? */ /* main vbox */ vbox=gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(window),vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox),0); gtk_widget_show(vbox); /* add file details table */ /* 6 columns used to get the proportions nice - left one is for * description (e.g. "Filename:"), right N for value. */ detailstbl=gtk_table_new(5,width=6,TRUE); gtk_box_pack_start(GTK_BOX(vbox),detailstbl,TRUE,TRUE,0); gtk_table_set_col_spacings(GTK_TABLE(detailstbl),10); gtk_table_set_row_spacings(GTK_TABLE(detailstbl),2); gtk_container_set_border_width(GTK_CONTAINER(detailstbl),5); gtk_widget_show(detailstbl); /* couldn't get gtk_label_set_justify() to work (perhaps this is * only relevant to multi-line labels?), but gtk_misc_set_alignment() is ok. */ #define DO_TBL_LEFT(table,row,start,name) \ label=gtk_label_new(name); \ gtk_misc_set_alignment(GTK_MISC(label),1.,0.5); \ gtk_table_attach_defaults(GTK_TABLE(table),label, \ (start),(start)+1, (row),(row)+1); \ gtk_widget_show(label) #define DO_TBL_RIGHT(table,row,start,end,name) \ ebox=gtk_event_box_new(); \ label=gtk_label_new(name); \ gtk_misc_set_alignment(GTK_MISC(label),0.,0.5); \ gtk_container_add(GTK_CONTAINER(ebox),label); \ gtk_widget_set_usize(label,5,5); \ gtk_table_attach_defaults(GTK_TABLE(table),ebox, \ (start),(end), (row),(row)+1); \ gtk_widget_show(label),gtk_widget_show(ebox) tbl_row=0; DO_TBL_LEFT(detailstbl,tbl_row,0,"File:"); DO_TBL_RIGHT(detailstbl,tbl_row,1,width,""); detaillabel[tbl_row]=label; tbl_row++; DO_TBL_LEFT(detailstbl,tbl_row,0,"Misc:"); DO_TBL_RIGHT(detailstbl,tbl_row,1,width,""); detaillabel[tbl_row]=label; tbl_row++; DO_TBL_LEFT(detailstbl,tbl_row,0,"Author:"); DO_TBL_RIGHT(detailstbl,tbl_row,1,width,""); detaillabel[tbl_row]=label; tbl_row++; DO_TBL_LEFT(detailstbl,tbl_row,0,"Tracks:"); DO_TBL_RIGHT(detailstbl,tbl_row,1,width,""); detaillabel[tbl_row]=label; tbl_row++; DO_TBL_LEFT(detailstbl,tbl_row,0,"Playing:"); DO_TBL_RIGHT(detailstbl,tbl_row,1,width,""); detaillabel[tbl_row]=label; sep=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vbox),sep,TRUE,TRUE,0); gtk_widget_show(sep); statustbl=gtk_table_new(2,6,TRUE); gtk_box_pack_start(GTK_BOX(vbox),statustbl,TRUE,TRUE,0); gtk_table_set_col_spacings(GTK_TABLE(statustbl),10); gtk_table_set_row_spacings(GTK_TABLE(statustbl),2); gtk_container_set_border_width(GTK_CONTAINER(statustbl),5); gtk_widget_show(statustbl); DO_TBL_LEFT(statustbl,0, 0,"Status:"); DO_TBL_RIGHT(statustbl,0, 1,2,""); label_for_status=label; DO_TBL_LEFT(statustbl,1, 0,"Time:"); DO_TBL_RIGHT(statustbl,1, 1,2,""); label_for_time=label; highspeed_widget=gtk_check_button_new_with_label("High-speed"); /* don't allow focus, looks too weird and we have shortcut */ GTK_WIDGET_UNSET_FLAGS(highspeed_widget,GTK_CAN_FOCUS); gtk_table_attach_defaults(GTK_TABLE(statustbl),highspeed_widget, 2,4, 0,2); gtk_signal_connect(GTK_OBJECT(highspeed_widget),"clicked", GTK_SIGNAL_FUNC(cb_toggle_highspeed),NULL); gtk_widget_add_accelerator(highspeed_widget,"clicked", gtk_accel_group_get_default(), GDK_h,0,0); gtk_widget_show(highspeed_widget); /* XXX these should really be spin buttons */ DO_TBL_LEFT(statustbl,0, 4,"Stop after:"); DO_TBL_RIGHT(statustbl,0, 5,6,""); label_for_stopafter=label; DO_TBL_LEFT(statustbl,1, 4,"Fade time:"); DO_TBL_RIGHT(statustbl,1, 5,6,""); label_for_fadetime=label; sep=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vbox),sep,TRUE,TRUE,0); gtk_widget_show(sep); buttontbl=gtk_table_new(2,4,TRUE); gtk_box_pack_start(GTK_BOX(vbox),buttontbl,TRUE,TRUE,0); gtk_table_set_col_spacings(GTK_TABLE(buttontbl),5); gtk_table_set_row_spacings(GTK_TABLE(buttontbl),5); gtk_container_set_border_width(GTK_CONTAINER(buttontbl),5); gtk_widget_show(buttontbl); hbox=gtk_hbox_new(TRUE,5); gtk_table_attach_defaults(GTK_TABLE(buttontbl),hbox, 0,4, 0,1); gtk_container_set_border_width(GTK_CONTAINER(hbox),0); gtk_widget_show(hbox); /* XXX this seems to be needed here, but is it ok? */ gtk_widget_realize(window); #define ADD_PIXMAP_TO_BUTTON(button,xpm) \ bitmap=gdk_pixmap_create_from_xpm_d(window->window,&mask, \ &(window->style->black), \ (xpm)); \ pixmap=gtk_pixmap_new(bitmap,mask); \ gtk_container_add(GTK_CONTAINER(button),pixmap); \ gtk_widget_show(pixmap) button=gtk_button_new(); ADD_PIXMAP_TO_BUTTON(button,button1); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_prev_track),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_z,0,0); /* since I use Space, there's no point having these focusable... */ GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new(); ADD_PIXMAP_TO_BUTTON(button,button2); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_play),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_x,0,0); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new(); ADD_PIXMAP_TO_BUTTON(button,button3); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_pause),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_c,0,0); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new(); ADD_PIXMAP_TO_BUTTON(button,button4); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_stop),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_v,0,0); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new(); ADD_PIXMAP_TO_BUTTON(button,button5); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_next_track),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_b,0,0); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new_with_label("Restart"); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_restart),NULL); gtk_widget_add_accelerator(button,"clicked",gtk_accel_group_get_default(), GDK_r,0,0); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); /* second row, these go straight on the table */ /* backspace/delete/space are dealt with by keypress() */ button=gtk_button_new_with_label("Prev File"); gtk_table_attach_defaults(GTK_TABLE(buttontbl),button, 1,2, 1,2); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_prev_file),NULL); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); button=gtk_button_new_with_label("Next File"); gtk_table_attach_defaults(GTK_TABLE(buttontbl),button, 2,3, 1,2); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(cb_button_next_file),NULL); GTK_WIDGET_UNSET_FLAGS(button,GTK_CAN_FOCUS); gtk_widget_show(button); gtk_widget_show(window); } static void ui_draw_status(char *filename,char *misc, char *author,int track,char *playingstr) { static gchar buf[256]; char *ptr=strrchr(filename,'/'); int tbl_row=0; if(!window) return; gtk_label_set_text(GTK_LABEL(detaillabel[tbl_row]),ptr?ptr+1:filename); tbl_row++; gtk_label_set_text(GTK_LABEL(detaillabel[tbl_row]),misc); tbl_row++; gtk_label_set_text(GTK_LABEL(detaillabel[tbl_row]),author); tbl_row++; g_snprintf(buf,sizeof(buf),"%d",aydata.num_tracks); gtk_label_set_text(GTK_LABEL(detaillabel[tbl_row]),buf); tbl_row++; g_snprintf(buf,sizeof(buf),"%d - %s",track,playingstr); gtk_label_set_text(GTK_LABEL(detaillabel[tbl_row]),buf); gtk_label_set_text(GTK_LABEL(label_for_status), paused?"paused ":(playing?"playing":"stopped")); if(!stopafter) gtk_label_set_text(GTK_LABEL(label_for_stopafter),"--:--"); else { g_snprintf(buf,sizeof(buf),"%2d:%02d",stopafter/60,stopafter%60); gtk_label_set_text(GTK_LABEL(label_for_stopafter),buf); } g_snprintf(buf,sizeof(buf),"%2d sec",fadetime); gtk_label_set_text(GTK_LABEL(label_for_fadetime),buf); } static void ui_draw_status_timeonly(void) { static char buf[32]; if(!window) return; g_snprintf(buf,sizeof(buf),"%2d:%02d",tunetime.min,tunetime.sec); gtk_label_set_text(GTK_LABEL(label_for_time),buf); } /* called per 1/50th, this deals with the usual events. * returns zero if we want to stop the current track. */ int ui_frame(void) { if(!window) return(0); retval=1; if(need_update) { need_update=0; ui_draw_status(ay_filenames[ay_file],aydata.miscstr,aydata.authorstr, ay_track+1,aydata.tracks[ay_track].namestr); } /* update time display */ ui_draw_status_timeonly(); /* this should be the only place retval gets modified by actions */ while(gtk_events_pending()) gtk_main_iteration(); return(retval); } /* called if playback status has changed without us knowing. */ void ui_change_notify(void) { need_update=1; } void ui_init(int argc,char **argv) { used_ui=1; if(!use_ui) { fprintf(stderr,"xaylet: warning: " "non-UI mode not supported by GTK+ version, use `aylet'.\n"); use_ui=1; } gtk_init(&argc,&argv); ui_initwin(); } void ui_end(void) { if(!used_ui) return; /* XXX can't call gtk_exit(), ui_end() is meant to return! * Oh well, it seems to be coping without... :-/ */ } aylet-0.5/TODO0100600000175000001440000000125410330152546011706 0ustar rususersProvide some way of using fadestart/fadelen info from those files that provide it. (Does currently read the info, but it's ignored.) Really want runtime modification/loading/saving of playlist in at least the GTK+ version. It's a bit of a pain though, so it might be a while in coming. The silent-time-count isn't reset for new tracks. Rarely noticeable, but if you jump from one currently-silent track to another which remains silent, it'll skip the new track after less time than usual. The button bitmaps in xaylet are hardcoded to use black. This is bound to cause problems with dark GTK+ themes. Should see if there's some way I can get them to use the button-text colour. aylet-0.5/cbops.c0100600000175000001440000001267306700506723012505 0ustar rususers/* Emulations of the CB operations of the Z80 instruction set. * Copyright (C) 1994 Ian Collier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define var_t unsigned char t #define rlc(x) (x=(x<<1)|(x>>7),rflags(x,x&1)) #define rrc(x) do{var_t=x&1;x=(x>>1)|(t<<7);rflags(x,t);}while(0) #define rl(x) do{var_t=x>>7;x=(x<<1)|(f&1);rflags(x,t);}while(0) #define rr(x) do{var_t=x&1;x=(x>>1)|(f<<7);rflags(x,t);}while(0) #define sla(x) do{var_t=x>>7;x<<=1;rflags(x,t);}while(0) #define sra(x) do{var_t=x&1;x=((signed char)x)>>1;rflags(x,t);}while(0) #define sll(x) do{var_t=x>>7;x=(x<<1)|1;rflags(x,t);}while(0) #define srl(x) do{var_t=x&1;x>>=1;rflags(x,t);}while(0) #define rflags(x,c) (f=(c)|(x&0xa8)|((!x)<<6)|parity(x)) #define bit(n,x) (f=(f&1)|((x&(1<>3)&7; switch(op&0xc7){ case 0x40: bit(n,b); break; case 0x41: bit(n,c); break; case 0x42: bit(n,d); break; case 0x43: bit(n,e); break; case 0x44: bit(n,h); break; case 0x45: bit(n,l); break; case 0x46: tstates+=4;val=fetch(addr);bit(n,val);store(addr,val);break; case 0x47: bit(n,a); break; case 0x80: res(n,b); break; case 0x81: res(n,c); break; case 0x82: res(n,d); break; case 0x83: res(n,e); break; case 0x84: res(n,h); break; case 0x85: res(n,l); break; case 0x86: tstates+=4;val=fetch(addr);res(n,val);store(addr,val);break; case 0x87: res(n,a); break; case 0xc0: set(n,b); break; case 0xc1: set(n,c); break; case 0xc2: set(n,d); break; case 0xc3: set(n,e); break; case 0xc4: set(n,h); break; case 0xc5: set(n,l); break; case 0xc6: tstates+=4;val=fetch(addr);set(n,val);store(addr,val);break; case 0xc7: set(n,a); break; } } if(ixoriy)switch(reg){ case 0:b=val; break; case 1:c=val; break; case 2:d=val; break; case 3:e=val; break; case 4:h=val; break; case 5:l=val; break; case 7:a=val; break; } } #undef var_t #undef rlc #undef rrc #undef rl #undef rr #undef sla #undef sra #undef sll #undef srl #undef rflags #undef bit #undef set #undef res aylet-0.5/aylet.10100600000175000001440000001566510127355404012433 0ustar rususers.\" -*- nroff -*- .\" .\" aylet 0.4, a .AY music file player. .\" Copyright (C) 2001-2004 Russell Marks and Ian Collier. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. .\" .\" .\" aylet.1 - man page .\" .TH aylet 1 "1st October, 2004" "Version 0.4" "Music Software" .\" .\"------------------------------------------------------------------ .\" .SH NAME aylet, xaylet \- play Spectrum `.ay' music files .\" .\"------------------------------------------------------------------ .\" .SH SYNOPSIS .PD 0 .BR aylet / xaylet .RB [ -BehlmnNsS ] .RB [ -A .IR stopafter ] .RB [ -F .IR fadetime ] .RB [ -t .IR tracknum ] .RI [ file1 .RI [ file2 .cc @ ... ]] @cc . .P .PD 1 .\" .\"------------------------------------------------------------------ .\" .SH DESCRIPTION aylet plays music files in the `.ay' format. These files are essentially wrappers around bits of Z80 code which play music on the Sinclair ZX Spectrum 128's sound hardware - the beeper, or the AY-3-8912 sound chip, or both. Files using the Amstrad CPC ports are also supported. `aylet' has a curses-based interface, and `xaylet' has an X-based one. .\" .\"------------------------------------------------------------------ .\" .SH OPTIONS .TP .B -A set stop-after time in seconds (this is the time at which tracks start fading out), or 0 to disable fading. The default is 180 seconds (3 minutes). .TP .B -B use `ABC' stereo positions, rather than the default `ACB'. ACB effectively pushes channels A and B to the left and right (using a stereo delay), with C remaining central; ABC puts A and C to the left/right instead, leaving B central. .TP .B -e force 8-bit playback, even if 16-bit is available. .TP .B -F set fade-out time in seconds (the time tracks take to fade out). The default is 10 seconds. .TP .B -h give terse usage help. .TP .B -l list the contents of files (track names etc.) rather than playing them. .TP .B -m use mono rather than the default stereo playback. .TP .B -n rather than the usual interface, use a simple batch-playing tty style, a bit like mpg123. (Only works in the curses version.) .TP .B -N use `narrow' stereo separation (so the channels sound closer together) rather than the default `wide' separation. .TP .B -s output sample data to stdout rather than playing it; implies `-n'. The sample is 44.1kHz 16-bit stereo (or mono if using `-m'). Don't be surprised if the sample takes a little while to generate, as it still has to emulate the Z80. .TP .B -S apply a pseudo-stereo effect to the beeper output. Works well in some cases, but can make things sound a bit shaky (or rather, more so than usual :-)). .TP .B -t play only the specified track, then exit. (Strictly speaking, it starts playing the given track, and exits when any track fades out.) Mainly useful when playing a single file non-interactively. .TP .IR file1 " etc." the file(s) to play. Currently these can only be specified on the command-line. .\" .\"------------------------------------------------------------------ .\" .SH "BEHIND THE SCENES" The way aylet works may seem a little strange if you're not familiar with how the `.ay' format goes about things, so here's some background on how it all works. .PP As mentioned above, the files basically contain chunks of machine code to run on an emulated Z80 with certain sound hardware. Since you're just running a program, you can't really fast-forward or rewind as such, but you can move between tracks specified in the file. (Some files only have one, but others have several.) .PP The whole just-running-a-program thing leads to two other problems, too. You can't tell where a track ends, other than to see when the sound stops; what aylet does is to exit a track after 4 seconds of total silence. And a track may never end at all; aylet fades it out after 3 minutes by default. (Yes, 3 minutes be may too soon to allow some tracks to `finish', but you have to strike the balance somewhere, and it's only a default. Use `-A' or press `s' to change it.) .\" .\"------------------------------------------------------------------ .\" .SH KEYS Both the curses-based and X-based interfaces use these keys (some of which are based on those used by XMMS): .TP .IR q " or " Esc exit the program. .TP .I z previous track. .TP .I x play. .TP .I c pause. .TP .I v stop. .TP .I b next track. .TP .I r restart track. .TP .IR Backspace " or " Delete previous file. .TP .I Space next file. .TP .I s increase stop-after time. .TP .I S decrease stop-after time. Setting it to zero disables fading completely. .TP .I f increase fade-out time. .TP .I F decrease fade-out time. .TP .I h enable/disable high-speed mode, playing four times as fast when enabled (in a way), as a sort of substitute for a real fast-forward control. .PP In xaylet, you can also use the matching buttons for most of the above. .PP When using `aylet -n', the interactivity is scaled down dramatically, leaving just the one control: .TP .I Ctrl-C if the current track has been playing for less than half a second, exit the program; otherwise, skip to the next track. .PP Note that the key used to do this is really whatever you have set up to generate a SIGINT signal, but it's generally Ctrl-C. .\" .\"------------------------------------------------------------------ .\" .SH EXAMPLES To play all .ay files in the current directory: .PP aylet *.ay .br or .br xaylet *.ay .PP To convert ysfollin.ay to a mono sample in .wav format (requires `sox'): .PP aylet -sm ysfollin.ay |sox -r 44100 -t sw - tune.wav .\" .\"------------------------------------------------------------------ .\" .SH BUGS The AY is probably too loud relative to the beeper. This may be fixed in the next version, though it's more likely to be an option to choose between the current more reasonable balance and the real thing. :-) .PP Several options which it could reasonably allow to be changed at runtime (e.g. AY stereo, beeper stereo, ACB vs. ABC) can't be at the moment. .PP The X version is currently a bit too much like the curses one, at the expense of GUIness in some respects. .\" .\"------------------------------------------------------------------ .\" .SH SEE ALSO .IR fuse "(1)" .\" .\"------------------------------------------------------------------ .\" .SH AUTHOR Russell Marks (rus@svgalib.org). .PP The Z80 emulation was written by Ian Collier, for xz80. .PP Chris Cox wrote the OpenBSD sound code. .PP Matan Ziv-Av was partly responsible for the OSS sound code. aylet-0.5/README0100600000175000001440000000372310351150573012102 0ustar rususersaylet 0.5 - a player for Spectrum `.ay' music files. Copyright (C) 2001-2005 Russell Marks. Z80 emulation copyright (C) 1994 Ian Collier. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Roadmap ------- The COPYING file contains a copy of the GPL described above. NEWS and ChangeLog describe differences between versions. Description ----------- aylet plays music files in the `.ay' format. These files are essentially wrappers around bits of Z80 code which play music on the Sinclair ZX Spectrum 128's sound hardware - either the beeper, or (eponymously) the AY-3-8912 sound chip. Files using the Amstrad CPC ports are also supported. `aylet' has a curses-based interface, and `xaylet' has an X-based one. One source of `.ay' files playable with aylet/xaylet is "Project AY" on the World of Spectrum website: http://www.worldofspectrum.org/ Installation ------------ You'll need an ANSI C compiler. `gcc' is fine - so should most other compilers be, these days. If you're running Linux, the Makefile should be ok; if not, you might want to check it. After doing that (or not :-)), you should do `make' (or `make aylet' if you don't have GTK+ installed), then (as root) `make install'. Once it's installed, don't forget to read the man page for further documentation. Contacting me ------------- You can email me at rus@svgalib.org. Share and enjoy! -Rus. aylet-0.5/edops.c0100600000175000001440000002052206166477215012512 0ustar rususers/* Emulations of the ED operations of the Z80 instruction set. * Copyright (C) 1994 Ian Collier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define input(var) { unsigned short u;\ var=u=in(b,c);\ tstates+=u>>8;\ f=(f&1)|(var&0xa8)|((!var)<<6)|parity(var);\ } #define sbchl(x) { unsigned short z=(x);\ unsigned long t=(hl-z-cy)&0x1ffff;\ f=((t>>8)&0xa8)|(t>>16)|2|\ (((hl&0xfff)<(z&0xfff)+cy)<<4)|\ (((hl^z)&(hl^t)&0x8000)>>13)|\ ((!(t&0xffff))<<6)|2;\ l=t;\ h=t>>8;\ } #define adchl(x) { unsigned short z=(x);\ unsigned long t=hl+z+cy;\ f=((t>>8)&0xa8)|(t>>16)|\ (((hl&0xfff)+(z&0xfff)+cy>0xfff)<<4)|\ (((~hl^z)&(hl^t)&0x8000)>>13)|\ ((!(t&0xffff))<<6)|2;\ l=t;\ h=t>>8;\ } #define neg (a=-a,\ f=(a&0xa8)|((!a)<<6)|(((a&15)>0)<<4)|((a==128)<<2)|2|(a>0)) { unsigned char op=fetch(pc); pc++; radjust++; switch(op){ instr(0x40,8); input(b); endinstr; instr(0x41,8); tstates+=out(b,c,b); endinstr; instr(0x42,11); sbchl(bc); endinstr; instr(0x43,16); {unsigned short addr=fetch2(pc); pc+=2; store2b(addr,b,c); } endinstr; instr(0x44,4); neg; endinstr; instr(0x45,4); iff1=iff2; ret; endinstr; instr(0x46,4); im=0; endinstr; instr(0x47,5); i=a; endinstr; instr(0x48,8); input(c); endinstr; instr(0x49,8); tstates+=out(b,c,c); endinstr; instr(0x4a,11); adchl(bc); endinstr; instr(0x4b,16); {unsigned short addr=fetch2(pc); pc+=2; c=fetch(addr); b=fetch(addr+1); } endinstr; instr(0x4c,4); neg; endinstr; instr(0x4d,4); ret; endinstr; instr(0x4e,4); im=1; endinstr; instr(0x4f,5); r=a; radjust=r; endinstr; instr(0x50,8); input(d); endinstr; instr(0x51,8); tstates+=out(b,c,d); endinstr; instr(0x52,11); sbchl(de); endinstr; instr(0x53,16); {unsigned short addr=fetch2(pc); pc+=2; store2b(addr,d,e); } endinstr; instr(0x54,4); neg; endinstr; instr(0x55,4); ret; endinstr; instr(0x56,4); im=2; endinstr; instr(0x57,5); a=i; f=(f&1)|(a&0xa8)|((!a)<<6)|(iff2<<2); endinstr; instr(0x58,8); input(e); endinstr; instr(0x59,8); tstates+=out(b,c,e); endinstr; instr(0x5a,11); adchl(de); endinstr; instr(0x5b,16); {unsigned short addr=fetch2(pc); pc+=2; e=fetch(addr); d=fetch(addr+1); } endinstr; instr(0x5c,4); neg; endinstr; instr(0x5d,4); ret; endinstr; instr(0x5e,4); im=3; endinstr; instr(0x5f,5); r=(r&0x80)|(radjust&0x7f); a=r; f=(f&1)|(a&0xa8)|((!a)<<6)|(iff2<<2); endinstr; instr(0x60,8); input(h); endinstr; instr(0x61,8); tstates+=out(b,c,h); endinstr; instr(0x62,11); sbchl(hl); endinstr; instr(0x63,16); {unsigned short addr=fetch2(pc); pc+=2; store2b(addr,h,l); } endinstr; instr(0x64,4); neg; endinstr; instr(0x65,4); ret; endinstr; instr(0x66,4); im=0; endinstr; instr(0x67,14); {unsigned char t=fetch(hl); unsigned char u=(a<<4)|(t>>4); a=(a&0xf0)|(t&0x0f); store(hl,u); f=(f&1)|(a&0xa8)|((!a)<<6)|parity(a); } endinstr; instr(0x68,8); input(l); endinstr; instr(0x69,8); tstates+=out(b,c,l); endinstr; instr(0x6a,11); adchl(hl); endinstr; instr(0x6b,16); {unsigned short addr=fetch2(pc); pc+=2; l=fetch(addr); h=fetch(addr+1); } endinstr; instr(0x6c,4); neg; endinstr; instr(0x6d,4); ret; endinstr; instr(0x6e,4); im=1; endinstr; instr(0x6f,5); {unsigned char t=fetch(hl); unsigned char u=(a&0x0f)|(t<<4); a=(a&0xf0)|(t>>4); store(hl,u); f=(f&1)|(a&0xa8)|((!a)<<6)|parity(a); } endinstr; instr(0x70,8); {unsigned char x;input(x);} endinstr; instr(0x71,8); tstates+=out(b,c,0); endinstr; instr(0x72,11); sbchl(sp); endinstr; instr(0x73,16); {unsigned short addr=fetch2(pc); pc+=2; store2(addr,sp); } endinstr; instr(0x74,4); neg; endinstr; instr(0x75,4); ret; endinstr; instr(0x76,4); im=2; endinstr; instr(0x78,8); input(a); endinstr; instr(0x79,8); tstates+=out(b,c,a); endinstr; instr(0x7a,11); adchl(sp); endinstr; instr(0x7b,16); {unsigned short addr=fetch2(pc); pc+=2; sp=fetch2(addr); } endinstr; instr(0x7c,4); neg; endinstr; instr(0x7d,4); ret; endinstr; instr(0x7e,4); im=3; endinstr; instr(0xa0,12); {unsigned char x=fetch(hl); store(de,x); if(!++l)h++; if(!++e)d++; if(!c--)b--; f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); } endinstr; instr(0xa1,12); {unsigned char carry=cy; cpa(fetch(hl)); if(!++l)h++; if(!c--)b--; f=(f&0xfa)|carry|(((b|c)>0)<<2); } endinstr; instr(0xa2,12); {unsigned short t=in(b,c); store(hl,t); tstates+=t>>8; if(!++l)h++; b--; f=(b&0xa8)|((b>0)<<6)|2|((parity(b)^c)&4); } endinstr; instr(0xa3,12); /* I can't determine the correct flags outcome for the block OUT instructions. Spec says that the carry flag is left unchanged and N is set to 1, but that doesn't seem to be the case... */ {unsigned char x=fetch(hl); tstates+=out(b,c,x); if(!++l)h++; b--; f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); } endinstr; instr(0xa8,12); {unsigned char x=fetch(hl); store(de,x); if(!l--)h--; if(!e--)d--; if(!c--)b--; f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); } endinstr; instr(0xa9,12); {unsigned char carry=cy; cpa(fetch(hl)); if(!l--)h--; if(!c--)b--; f=(f&0xfa)|carry|(((b|c)>0)<<2); } endinstr; instr(0xaa,12); {unsigned short t=in(b,c); store(hl,t); tstates+=t>>8; if(!l--)h--; b--; f=(b&0xa8)|((b>0)<<6)|2|((parity(b)^c^4)&4); } endinstr; instr(0xab,12); {unsigned char x=fetch(hl); tstates+=out(b,c,x); if(!l--)h--; b--; f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); } endinstr; /* Note: the Z80 implements "*R" as "*" followed by JR -2. No reason to change this... */ instr(0xb0,12); {unsigned char x=fetch(hl); store(de,x); if(!++l)h++; if(!++e)d++; if(!c--)b--; f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); if(b|c)pc-=2,tstates+=5; } endinstr; instr(0xb1,12); {unsigned char carry=cy; cpa(fetch(hl)); if(!++l)h++; if(!c--)b--; f=(f&0xfa)|carry|(((b|c)>0)<<2); if((f&0x44)==4)pc-=2,tstates+=5; } endinstr; instr(0xb2,12); {unsigned short t=in(b,c); store(hl,t); tstates+=t>>8; if(!++l)h++; b--; f=(b&0xa8)|((b>0)<<6)|2|((parity(b)^c)&4); if(b)pc-=2,tstates+=5; } endinstr; instr(0xb3,12); {unsigned char x=fetch(hl); tstates+=out(b,c,x); if(!++l)h++; b--; f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); if(b)pc-=2,tstates+=5; } endinstr; instr(0xb8,12); {unsigned char x=fetch(hl); store(de,x); if(!l--)h--; if(!e--)d--; if(!c--)b--; f=(f&0xc1)|(x&0x28)|(((b|c)>0)<<2); if(b|c)pc-=2,tstates+=5; } endinstr; instr(0xb9,12); {unsigned char carry=cy; cpa(fetch(hl)); if(!l--)h--; if(!c--)b--; f=(f&0xfa)|carry|(((b|c)>0)<<2); if((f&0x44)==4)pc-=2,tstates+=5; } endinstr; instr(0xba,12); {unsigned short t=in(b,c); store(hl,t); tstates+=t>>8; if(!l--)h--; b--; f=(b&0xa8)|((b>0)<<6)|2|((parity(b)^c^4)&4); if(b)pc-=2,tstates+=5; } endinstr; instr(0xbb,12); {unsigned char x=fetch(hl); tstates+=out(b,c,x); if(!l--)h--; b--; f=(f&1)|0x12|(b&0xa8)|((b==0)<<6); if(b)pc-=2,tstates+=5; } endinstr; default: tstates+=4; }} aylet-0.5/ChangeLog0100600000175000001440000002472610351150504012774 0ustar rususers2005-12-18 Russell Marks * Version 0.5. * sound.c (sound_start_fade): remembered why fadetime was declared as static - it was a different variable. As such, version 0.4 got released with a zero fade-out time. :-( Fixed again by renaming sound.c's fadetime to sfadetime, which should hopefully avoid future confusion... 2005-07-14 Russell Marks * Version 0.4. * sound.c: `fadetime' was declared as static when it shouldn't have been. Thanks to Daniel Baumann for pointing this out. 2004-05-15 Russell Marks * main.c (action_callback): fixed unhelpful interactive stop-after-setting behaviour when started with stop-after set to something not a multiple of 30 seconds; now the first interactive change will set it to the nearest multiple in the specified direction. Thanks to Bartlomiej Ochman for this. 2004-05-01 Russell Marks * main.c: added `-t' option, to play only a given track (actually slightly different, see the man page). Thanks to Bartlomiej Ochman for this. 2004-03-26 Russell Marks * Added 16-bit support. Now defaults to this when possible. New option `-e' forces 8-bit playback (even that is improved, due to the 16-bit mixing now done). Thanks to Stuart Brady for inspiring this change. 2003-11-30 Russell Marks * aylet.1: fixed typo. 2003-11-27 Russell Marks * ui.c (ui_frame): fixed minor bug when checking value of KEY_DC at compile time. Unlikely to affect anything in practice. 2002-09-14 Russell Marks * Version 0.3. * main.c (out): added partial port-bitmask to allow for certain less-than-ideal .ay conversions. (This is only done when the low byte is FDh, and even then doesn't disable CPC autodetect, so it shouldn't break CPC tunes.) Thanks to Vít Hotárek for reporting the files that (previously) didn't work. 2002-06-24 Russell Marks * z80.c (z80loop): fixed silly typo which meant that L and L' weren't set correctly when starting up the Z80. (Though curiously, this bug didn't seem to break any .ay files.) Thanks to Patrik Rak for spotting this. 2002-05-25 Russell Marks * sound.c (sound_frame): previously, when a track stopped and happened to do so leaving high/low level `silence' (e.g. a few AY tracks, and all beeper tracks after the recent beeper-fade removal), if this change happened during a fade the fading level would screw up the silence detection and give (with default settings) up to ten seconds of extra `silence'. Fixed that by detecting silence before fade filter is applied, and overriding the not-silent result if the track has been completely faded out. 2002-05-05 Russell Marks * sound.c (sound_frame): removed beeper fading, which wasn't actually necessary and was causing problems with some tracks, most noticeably in Trantor. The rest position is still central for AY-only and CPC tracks, though, so the change shouldn't affect those. * uigtk.c (DO_TBL_RIGHT): long file details (e.g. track name) no longer expand the window to fit, but are clipped. You can still manually resize the window to see the rest of the text, if you like. 2002-03-16 Russell Marks * sound.c (sound_ay_overlay): now emulates tone counters at all times, rather than just doing it when producing a tone and attempting to compensate later. I'm not sure if this makes any practical difference, but it's a more accurate emulation. 2002-02-26 Russell Marks * aylet.1: added explanation of what ACB/ABC actually mean, rather than just pulling the terminology out of thin air. :-) 2001-12-21 Russell Marks * sound.c (sound_ay_init): finally added decent AY levels. Thanks to Matthew Westcott for the measurements these were based on. The change from the previous values isn't all that big to be honest, but we should at least get a faithful `blip' now. :-) 2001-12-11 Russell Marks * Version 0.2. * sound.c (sound_ay_init): the calculated AY volume levels used previously were wrong, even though they were based on the levels claimed by official documentation. (!) The new precalculated levels should be a lot more accurate, but don't yet reproduce a curious `blip' in the AY DACs. (I may use values as measured from a speccy in future, in order to do this.) 2001-11-13 Russell Marks * drv-oss.c: the OSS driver has now moved here. * drv-obsd.c: OpenBSD sound driver. Thanks to Chris Cox for this, and separating out the OSS code. 2001-11-11 Russell Marks * main.c (out): whoops, no *additional* t-states for OUT... this explains why some beeper tunes with very tight loops over OUT instructions (e.g. Heavy on the Magick) were slow before. (out): ok, we need to disable CPC detection when *any* speccy port is written to. * sound.c (sound_ay_overlay): fixed a strange clicking problem; this was probably most noticeable in Defenders of the Earth, especially the drum solo bit, but there were several other cases. I've been struggling with this one for maybe a week now (bleah), but I think I've finally got it. :-) (sound_ay_overlay): ok, that fix wasn't quite right; changed tone_tick workings so it should work properly now. 2001-11-09 Russell Marks * sound.c (sound_ay_overlay): fix for high-frequency noise. 2001-11-04 Russell Marks * sound.c (sound_ay_overlay): rewrote envelope emulation, the old one couldn't be made to cope with high-speed envelopes (as used in some demos, e.g. Binary Love track 2). Also fixes accidental (?) zero-period envelope use with FFh volume (e.g. Afterburner). 2001-11-03 Russell Marks * Changed field label from "Title" to "Misc" throughout. Some files use it for Title, some Copyright, some both. So "Misc" is about the only reasonable label. * main.c (out): added support for CPC tunes. Unfortunately these have to be auto-detected (!) so it starts off emulating the usual cut-down speccy, then switches to a cut-down CPC if those ports are used, slowing down the AY and speeding up the Z80. Also, if the (speccy) beeper is used (which could theoretically clash with the CPC stuff) and it's not in the CPC mode, the CPC ports are disabled for the current track. 2001-10-25 Russell Marks * sound.c: beeper tones were *also* upside-down, not just AY - fixed. * aylet.1: again, it's 44.1kHz, not 44 (see below). * main.c (usage_help): oops... `-s' outputs a stereo sample by default, not mono. Also, it's 44.1kHz not 44. 2001-10-21 Russell Marks * Version 0.1. * sound.c (sound_frame): correctly detect silences at a non-zero level - some .ay files have tracks which end like this, most likely due to the original author's misunderstanding/ignoring an AY chip oddity (the whole `turning the channel tone and noise off in the mixer doesn't disable it, you have to zero the volume' thing). Understandable, really, it's not an *audible* problem... 2001-10-17 Russell Marks * sound.c: tweaked beeper/AY volume balance. I decided not to make the beeper the same volume as all 3 AY channels together, it was just annoyingly loud like that. But I did try to give beeper and AY tunes more comparable levels, so the beeper is a bit louder now. Agent X 2 still sounds odd, but I think I may just have to add an option for the proper balance at some point, because it'd be a crazy default for a player given how loud the beeper really is. (sound_ay_overlay): fixed behaviour when neither tone nor noise is selected on a channel, meaning that e.g. Chase HQ's samples work. (sound_ay_overlay): AY tones were upside-down :-), fixed. 2001-10-16 Russell Marks * sound.c: added `-N' option for narrower AY channel stereo positioning. (sound_beeper): fix for slight spikes on ordinary beeper tones. 2001-10-15 Russell Marks * sound.c: improved ACB stereo, and made it the default. (Use `-m' to force mono playback.) Also added a pseudo-stereo option for the beeper sound; not enabled by default (use `-S') as it can make things sound a bit shaky and rough in some cases. * main.c: default stopafter reduced from 5 minutes to 3. I think this is probably a decent default, even if it is too short for some things (e.g. Agent X 2). * sound.c (sound_ay_overlay): removed tone/noise counter wraparound (inherited from z81), which wasn't necessary and screwed up (e.g.) Marauder and Cybernoid 2. 2001-10-14 Russell Marks * main.c: added `-s', a play-to-stdout option. (do_interrupt): fixed bug in non-UI mode, where it could have been left on a non-existent file. (do_list): added `-l', to list the contents of files rather than playing them. * ui.c (ui_frame): fixed backspace/del, which I'd managed to break. 2001-10-13 Russell Marks * main.c (do_interrupt): now returns to first file/track (rather than sticking on the last one) as well as stopping when it's got no more to play. * ui.c: added non-UI `UI'. :-) It's a bit like using mpg123. If you press ^C (well, send SIGINT) in the first half-second of a track the program exits, otherwise it starts the next track. * main.c (action_callback): prev/next track now cross files. * uigtk.c: a GTK+ UI. Probably matches the curses one a bit too closely, making things like tweaking stopafter/fadetime rather clunky, but I think it'll do for now. 2001-10-10 Russell Marks * sound.c (sound_ay_overlay): whoops, get that frequency right - not on a ZX81 any more... :-) * main.c (mainloop): now supports prev/next file. Pretty hairy, but it's working. The playlist comes from the command-line at the moment; that behaviour's probably going to stick, but I might add an alternative way of specifying a playlist eventually. 2001-10-09 Russell Marks * sound.c (sound_ay_reset): fixed, previously didn't always reset due to possible non-zero tstates when calling. * First essentially working version, based on nc100em and z81. (z81's sound emulation being based on the one I wrote for Fuse.) aylet-0.5/main.h0100600000175000001440000000242507371054321012317 0ustar rususers/* aylet 0.1, a .AY music file player. * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence. * * main.h */ enum cb_action_tag { cb_none, cb_quit, cb_highspeed, cb_prev_file, cb_next_file, cb_prev_track, cb_next_track, cb_play, cb_pause, cb_stop, cb_restart, cb_dec_stopafter, cb_inc_stopafter, cb_dec_fadetime, cb_inc_fadetime, cb_dec_vol, cb_inc_vol }; extern int action_callback(enum cb_action_tag action); extern int do_interrupt(void); extern unsigned int in(int h,int l); extern unsigned int out(int h,int l,int a); extern unsigned char mem[]; extern unsigned long tstates,tsmax; extern int highspeed,playing,paused,want_quit; extern int stopafter,fadetime; extern int use_ui,play_to_stdout; extern char **ay_filenames; extern int ay_file,ay_num_files; extern int ay_track; struct ay_track_tag { unsigned char *namestr,*data; unsigned char *data_stacketc,*data_memblocks; int fadestart,fadelen; }; struct aydata_tag { unsigned char *filedata; int filelen; struct ay_track_tag *tracks; int filever,playerver; unsigned char *authorstr,*miscstr; int num_tracks; int first_track; }; struct time_tag { int min,sec,subsecframes; }; extern struct aydata_tag aydata; extern struct time_tag tunetime; aylet-0.5/sound.h0100600000175000001440000000140007374233260012517 0ustar rususers/* aylet 0.2, a .AY music file player. * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence. * * sound.h */ extern int sound_enabled; extern int sound_freq; extern int sound_stereo; extern int sound_stereo_beeper; extern int sound_stereo_ay; extern int sound_stereo_ay_abc; extern int sound_stereo_ay_narrow; extern int soundfd; extern int sixteenbit; extern int play_to_stdout; extern int sound_init(void); extern void sound_end(void); extern int sound_frame(int really_play); extern void sound_frame_blank(void); extern void sound_start_fade(int fadetime_in_sec); extern void sound_ay_write(int reg,int val,unsigned long tstates); extern void sound_ay_reset(void); extern void sound_ay_reset_cpc(void); extern void sound_beeper(int on); aylet-0.5/z80.c0100600000175000001440000000660307540654667012032 0ustar rususers/* Emulation of the Z80 CPU with hooks into the other parts of aylet. * Copyright (C) 1994 Ian Collier. aylet changes (C) 2001-2002 Russell Marks. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "main.h" #include "z80.h" #define parity(a) (partable[a]) unsigned char partable[256]={ 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4 }; void z80loop(unsigned char *data,unsigned char *stacketc) { unsigned char a, f, b, c, d, e, h, l; unsigned char r, a1, f1, b1, c1, d1, e1, h1, l1, i, iff1, iff2, im; unsigned short pc; unsigned short ix, iy, sp; unsigned int radjust; unsigned int ixoriy, new_ixoriy; unsigned int intsample; unsigned char op; int interrupted=0; a=f=b=c=d=e=h=l=a1=f1=b1=c1=d1=e1=h1=l1=i=r=iff1=iff2=im=0; ixoriy=new_ixoriy=0; ix=iy=sp=pc=0; tstates=0; radjust=0; a=a1=b=b1=d=d1=h=h1=data[8]; f=f1=c=c1=e=e1=l=l1=data[9]; ix=iy=hl; sp=stacketc[0]*256+stacketc[1]; while(1) { if(tstates>=tsmax) { do { if(!do_interrupt()) { /* they want to exit */ return; } } while(playing && paused); /* when it's paused, we'd better wait here */ tstates-=tsmax; interrupted=1; } ixoriy=new_ixoriy; new_ixoriy=0; intsample=1; op=fetch(pc); pc++; radjust++; switch(op) { #include "z80ops.c" } if(interrupted && intsample && iff1) { interrupted=0; if(fetch(pc)==0x76)pc++; iff1=iff2=0; tstates+=5; /* accompanied by an input from the data bus */ switch(im) { case 0: /* IM 0 */ case 1: /* undocumented */ case 2: /* IM 1 */ /* there is little to distinguish between these cases */ tstates+=7; /* perhaps */ push2(pc); pc=0x38; break; case 3: /* IM 2 */ tstates+=13; /* perhaps */ { int addr=fetch2((i<<8)|0xff); push2(pc); pc=addr; } } } } } aylet-0.5/z80.h0100600000175000001440000000313007360677271012023 0ustar rususers/* Miscellaneous definitions for xz80, copyright (C) 1994 Ian Collier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define Z80_quit 1 #define Z80_NMI 2 #define Z80_reset 3 #define Z80_load 4 #define Z80_save 5 #define Z80_log 6 extern void z80loop(unsigned char *data,unsigned char *stacketc); #define fetch(x) (mem[x]) #define fetch2(x) ((fetch(((x)+1)&0xffff)<<8)|fetch(x)) #define store(x,y) do {\ mem[x]=(y); \ } while(0) #define store2b(x,hi,lo) do {\ mem[x]=lo; \ mem[(x+1)&0xffff]=hi; \ } while(0) #define store2(x,y) store2b(x,(y)>>8,(y)&255) #ifdef __GNUC__ static void inline storefunc(unsigned short ad,unsigned char b){ store(ad,b); } #undef store #define store(x,y) storefunc(x,y) static void inline store2func(unsigned short ad,unsigned char b1,unsigned char b2){ store2b(ad,b1,b2); } #undef store2b #define store2b(x,hi,lo) store2func(x,hi,lo) #endif #define bc ((b<<8)|c) #define de ((d<<8)|e) #define hl ((h<<8)|l) aylet-0.5/mkinstalldirs0100600000175000001440000000133407016274623014030 0ustar rususers#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain # $Id: mkinstalldirs,v 1.10 1996/05/03 07:37:52 friedman Exp $ errstatus=0 for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" 1>&2 mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here aylet-0.5/ui.c0100600000175000001440000001455607761451667012034 0ustar rususers/* aylet 0.2, a .AY music file player. * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence. * * ui.c - curses UI code (and no-UI UI :-)). */ #include #include #include #include #include #include "main.h" #include "ui.h" static int frame_x=1,frame_y=5; /* frame_y is adjusted */ /* XXX should adjust frame_x when >80 cols, too... */ static int used_curses_ui=0,need_update=1,need_nexttrack=0; static void clearscreen(void) { wclear(stdscr); move(0,0); refresh(); } static void draw_frame(void) { int y=frame_y; move(frame_y,frame_x); addstr(",--------------------------------------"); addstr("--------------------------------------."); move(frame_y+12,frame_x); addstr("`--------------------------------------"); addstr("--------------------------------------'"); move(frame_y+6,frame_x+1); addstr("--------------------------------------"); addstr("--------------------------------------"); move(frame_y+9,frame_x+1); addstr("--------------------------------------"); addstr("--------------------------------------"); for(y=frame_y+1;y=1) { if(len>maxlen) len=maxlen; memcpy(buf,str,len); } return(buf); } static void draw_status(char *filename,char *misc, char *author,int track,char *playingstr) { char *ptr=strrchr(filename,'/'); mvaddstr(frame_y+1,frame_x+11,fitwidth(ptr?ptr+1:filename,0)); mvaddstr(frame_y+2,frame_x+11,fitwidth(misc,0)); mvaddstr(frame_y+3,frame_x+11,fitwidth(author,0)); mvprintw(frame_y+4,frame_x+11,"%3d",aydata.num_tracks); mvprintw(frame_y+5,frame_x+11,"%3d - %s",track,fitwidth(playingstr,6)); mvaddstr(frame_y+7,frame_x+11,paused?"paused ":(playing?"playing":"stopped")); mvaddstr(frame_y+7,frame_x+44,highspeed?"on ":"off"); move(frame_y+7,frame_x+69); if(!stopafter) addstr("--:--"); else printw("%2d:%02d",stopafter/60,stopafter%60); mvprintw(frame_y+8,frame_x+68,"%2d sec",fadetime); move(0,0); refresh(); } static void draw_status_timeonly(void) { mvprintw(frame_y+8,frame_x+11,"%2d:%02d ",tunetime.min,tunetime.sec); move(0,0); refresh(); } static int non_ui_frame(void) { static int oldfile=-1; static int oldtrack=-1; if(need_update) { need_update=0; if(ay_file!=oldfile) { char *filename=ay_filenames[ay_file]; char *ptr=strrchr(filename,'/'); fprintf(stderr, "\n File: %s\n Misc: %s\n Author: %s\n Tracks: %3d\n", ptr?ptr+1:filename,aydata.miscstr,aydata.authorstr, aydata.num_tracks); } if(ay_file!=oldfile || ay_track!=oldtrack) { oldtrack=ay_track; fprintf(stderr, "Playing: %3d - %s\n", ay_track+1,aydata.tracks[ay_track].namestr); } oldfile=ay_file; } /* quit if we stop (i.e. when we've played all tracks) */ if(!playing && !paused) return(action_callback(cb_quit)); if(need_nexttrack) { need_nexttrack=0; return(action_callback(cb_next_track)); } return(1); } /* called per 1/50th, this deals with the usual events. * returns zero if we want to stop the current track. */ int ui_frame(void) { enum cb_action_tag action; if(!use_ui) return(non_ui_frame()); if(need_update) { need_update=0; draw_frame(); draw_status(ay_filenames[ay_file],aydata.miscstr,aydata.authorstr, ay_track+1,aydata.tracks[ay_track].namestr); } /* update time display */ draw_status_timeonly(); /* read keys */ action=cb_none; switch(getch()) { case 27: case 'q': action=cb_quit; break; case 'h': action=cb_highspeed; break; #if KEY_BACKSPACE!=8 case 8: #endif #if KEY_DC!=127 case 127: #endif case KEY_BACKSPACE: case KEY_DC: action=cb_prev_file; break; case ' ': action=cb_next_file; break; case 'z': action=cb_prev_track; break; case 'b': action=cb_next_track; break; case 'x': action=cb_play; break; case 'c': action=cb_pause; break; case 'v': action=cb_stop; break; case 'r': action=cb_restart; break; case 'S': action=cb_dec_stopafter; break; case 's': action=cb_inc_stopafter; break; case 'F': action=cb_dec_fadetime; break; case 'f': action=cb_inc_fadetime; break; } if(action==cb_none) return(1); return(action_callback(action)); } /* called if playback status has changed without us knowing. */ void ui_change_notify(void) { need_update=1; } static void non_ui_ctrlc(int foo) { if(tunetime.min==0 && tunetime.sec==0 && tunetime.subsecframes<25) playing=0; /* quit */ else need_nexttrack=1; } static void non_ui_init(void) { struct sigaction sa; sa.sa_flags=SA_RESTART; sa.sa_handler=non_ui_ctrlc; sigaction(SIGINT,&sa,NULL); need_update=1; } void ui_init(int argc,char **argv) { if(!use_ui) { non_ui_init(); return; } used_curses_ui=1; initscr(); cbreak(); noecho(); curs_set(0); /* remove cursor (if possible) */ keypad(stdscr,TRUE); nodelay(stdscr,1); /* XXX do I need to enable SIGWINCH handling in ncurses myself? */ /* check term size */ if(COLS<80 || LINES<13) { ui_end(); fprintf(stderr,"aylet: " "sorry, need a terminal with at least 80 cols and 13 lines.\n"); exit(1); } frame_y=(LINES-13)/2; clearscreen(); } void ui_end(void) { if(!use_ui || !used_curses_ui) return; clearscreen(); move(LINES-1,0); refresh(); nodelay(stdscr,0); echo(); nocbreak(); endwin(); putchar('\n'); /* in case of error exit, since stderr will be used immediately after... */ fflush(stdout); } aylet-0.5/NEWS0100600000175000001440000000630410351151125011711 0ustar rususers* Changes between versions -*- outline -*- This file (NEWS) documents the significant changes between all versions of aylet, with the newest changes first. * Changes in aylet 0.5 Fixed a stupid bug where the fadeout time defaulted to zero, so by default all tracks lasting longer than 3 minutes were cut off abruptly. * Changes in aylet 0.4 Added 16-bit support. Now defaults to this when possible. New option `-e' forces 8-bit playback (even that is improved, due to the 16-bit mixing now done). Thanks to Stuart Brady for inspiring this change. Added `-t' option, to play only a given track (actually slightly different, see the man page). Thanks to Bartlomiej Ochman for this. Fixed unhelpful interactive stop-after-setting behaviour when started with stop-after set to something not a multiple of 30 seconds; now the first interactive change will set it to the nearest multiple in the specified direction. Thanks to Bartlomiej Ochman for this too. Fixed a compilation error with newer versions of gcc (the code was wrong before, but wasn't complained about). Thanks to Daniel Baumann for pointing this one out. * Changes in aylet 0.3 Finally uses accurate AY levels. Thanks to Matthew Westcott for the measurements these were based on. Removed beeper fading, which wasn't actually necessary and was causing problems with some tracks, most noticeably in Trantor. The rest position is still central for AY-only and CPC tracks, though, so the change shouldn't affect those. Added partial port-bitmask to allow for certain less-than-ideal .ay conversions. Thanks to Vít Hotárek for helping find this one. Fixed silly typo which meant that L and L' weren't set correctly when starting up the Z80. (Though curiously, this bug didn't seem to break any .ay files.) Thanks to Patrik Rak for spotting this. Previously, when a track stopped and happened to do so leaving high/low level `silence' (e.g. a few AY tracks and, given the beeper-fade removal, all beeper tracks), if this change happened during a fade the fading level would screw up the silence detection and give (with default settings) up to ten seconds of extra `silence'. Now fixed. In xaylet, long file details (e.g. track name) no longer expand the window to fit, but are clipped. You can still manually resize the window to see the rest of the text, if you like. * Changes in aylet 0.2 OUT instructions previously took too long, making some beeper tunes (e.g. Heavy on the Magick) sound terribly slow - fixed. New AY volume levels, which should more closely reflect actual AY output. Added support for CPC files. A native sound driver for OpenBSD. Thanks to Chris Cox for this. Fixed most clicking problems. There are still a few, but it's doing much better than before. Rewrote envelope emulation, the old one couldn't be made to cope with high-speed envelopes (as used in some demos). Also fixes presumably-accidental zero-period envelope use with `negative' volume (e.g. Afterburner). Fixed high-frequency noise. Beeper tones inverted, so they're now the right way up. :-) Changed field label from "Title" to "Misc" throughout. Some files use it for Title, some Copyright, some both. So "Misc" is about the only reasonable label. * Changes in aylet 0.1 Everything. :-) aylet-0.5/ui.h0100600000175000001440000000045307362153015012007 0ustar rususers/* aylet 0.1, a .AY music file player. * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence. * * ui.h */ extern int ui_frame(void); extern void ui_change_notify(void); extern void ui_reset_notify(void); extern void ui_init(int argc,char **argv); extern void ui_end(void); aylet-0.5/drv-oss.c0100600000175000001440000000415510031141702012750 0ustar rususers/* aylet 0.4, a .AY music file player. * Copyright (C) 2001-2004 Russell Marks and Ian Collier. See main.c for licence. * * drv-oss.c - OSS sound code, partly due to Matan Ziv-Av. */ #ifdef DRIVER_OSS #include #include #include #include #include #include "sound.h" #include "driver.h" /* using (4) 256 byte frags for 8kHz, scale up for higher */ #define BASE_SOUND_FRAG_PWR 8 /* returns non-zero on success, and adjusts freq/stereo args to reflect * what we've actually got. */ int driver_init(int *freqptr,int *stereoptr) { int frag,tmp; if(play_to_stdout) { soundfd=1; return(1); } if((soundfd=open("/dev/dsp",O_WRONLY))<0) return(0); tmp=AFMT_S16_NE; if(!sixteenbit || ioctl(soundfd,SNDCTL_DSP_SETFMT,&tmp)==-1) { /* try 8-bit - may be a 8-bit-only device */ tmp=AFMT_U8; if((ioctl(soundfd,SNDCTL_DSP_SETFMT,&tmp))==-1) { close(soundfd); return(0); } sixteenbit=0; } tmp=(*stereoptr)?1:0; if(ioctl(soundfd,SNDCTL_DSP_STEREO,&tmp)<0) { /* if it failed, make sure the opposite is ok */ tmp=(*stereoptr)?0:1; if(ioctl(soundfd,SNDCTL_DSP_STEREO,&tmp)<0) { close(soundfd); return(0); } *stereoptr=tmp; } frag=(0x40000|BASE_SOUND_FRAG_PWR); if(ioctl(soundfd,SNDCTL_DSP_SPEED,freqptr)<0) { close(soundfd); return(0); } if(*freqptr>8250) frag++; if(*freqptr>16500) frag++; if(*freqptr>33000) frag++; if(*stereoptr) frag++; if(sixteenbit) frag++; if(ioctl(soundfd,SNDCTL_DSP_SETFRAGMENT,&frag)<0) { close(soundfd); return(0); } return(1); /* success */ } void driver_end(void) { if(soundfd>1) close(soundfd); } void driver_frame(signed short *data,int len) { static unsigned char buf8[4096]; unsigned char *data8=(unsigned char *)data; int ret=0,ofs=0; len<<=1; /* now in bytes */ if(!sixteenbit) { signed short *src; unsigned char *dst; int f; src=data; dst=buf8; len>>=1; for(f=0;f0) ofs+=ret,len-=ret; } } #endif /* DRIVER_OSS */ aylet-0.5/z80ops.c0100600000175000001440000004505607360467462012554 0ustar rususers/* Emulations of the Z80 CPU instruction set - part of xz80. * Copyright (C) 1994 Ian Collier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define instr(opcode,cycles) case opcode: {tstates+=cycles #define HLinstr(opcode,cycles,morecycles) \ case opcode: {unsigned short addr; \ tstates+=cycles; \ if(ixoriy==0)addr=hl; \ else tstates+=morecycles, \ addr=(ixoriy==1?ix:iy)+ \ (signed char)fetch(pc),\ pc++ #define endinstr }; break #define cy (f&1) #define xh (ixoriy==0?h:ixoriy==1?(ix>>8):(iy>>8)) #define xl (ixoriy==0?l:ixoriy==1?(ix&0xff):(iy&0xff)) #define setxh(x) (ixoriy==0?(h=(x)):ixoriy==1?(ix=(ix&0xff)|((x)<<8)):\ (iy=(iy&0xff)|((x)<<8))) #define setxl(x) (ixoriy==0?(l=(x)):ixoriy==1?(ix=(ix&0xff00)|(x)):\ (iy=(iy&0xff00)|(x))) #define inc(var) /* 8-bit increment */ ( var++,\ f=(f&1)|(var&0xa8)|\ ((!(var&15))<<4)|((!var)<<6)|\ ((var==128)<<2)\ ) #define dec(var) /* 8-bit decrement */ ( f=(f&1)|((!(var&15))<<4)|2,\ --var,\ f|=(var&0xa8)|((var==127)<<2)|\ ((!var)<<6)\ ) #define swap(x,y) {unsigned char t=x; x=y; y=t;} #define addhl(hi,lo) /* 16-bit add */ if(!ixoriy){\ unsigned short t;\ l=t=l+(lo);\ f=(f&0xc4)|(((t>>=8)+(h&0x0f)+((hi)&0x0f)>15)<<4);\ h=t+=h+(hi);\ f|=(h&0x28)|(t>>8);\ }\ else do{unsigned long t=(ixoriy==1?ix:iy);\ f=(f&0xc4)|(((t&0xfff)+((hi<<8)|lo)>0xfff)<<4);\ t+=(hi<<8)|lo;\ if(ixoriy==1)ix=t; else iy=t;\ f|=((t>>8)&0x28)|(t>>16);\ } while(0) #define adda(x,c) /* 8-bit add */ do{unsigned short y;\ unsigned char z=(x);\ y=a+z+(c);\ f=(y&0xa8)|(y>>8)|(((a&0x0f)+(z&0x0f)+(c)>15)<<4)|\ (((~a^z)&0x80&(y^a))>>5);\ f|=(!(a=y))<<6;\ } while(0) #define suba(x,c) /* 8-bit subtract */ do{unsigned short y;\ unsigned char z=(x);\ y=(a-z-(c))&0x1ff;\ f=(y&0xa8)|(y>>8)|(((a&0x0f)<(z&0x0f)+(c))<<4)|\ (((a^z)&0x80&(y^a))>>5)|2;\ f|=(!(a=y))<<6;\ } while(0) #define cpa(x) /* 8-bit compare */ do{unsigned short y;\ unsigned char z=(x);\ y=(a-z)&0x1ff;\ f=(y&0xa8)|(y>>8)|(((a&0x0f)<(z&0x0f))<<4)|\ (((a^z)&0x80&(y^a))>>5)|2|((!y)<<6);\ } while(0) #define anda(x) /* logical and */ do{\ a&=(x);\ f=(a&0xa8)|((!a)<<6)|0x10|parity(a);\ } while(0) #define xora(x) /* logical xor */ do{\ a^=(x);\ f=(a&0xa8)|((!a)<<6)|parity(a);\ } while(0) #define ora(x) /* logical or */ do{\ a|=(x);\ f=(a&0xa8)|((!a)<<6)|parity(a);\ } while(0) #define jr /* execute relative jump */ do{int j=(signed char)fetch(pc);\ pc+=j+1;\ tstates+=5;\ } while(0) #define jp /* execute jump */ (pc=fetch2(pc)) #define call /* execute call */ do{\ tstates+=7;\ push2(pc+2);\ jp;\ } while(0) #define ret /* execute return */ do{\ tstates+=6;\ pop2(pc);\ } while(0) #define pop2(var) /* pop 16-bit register */ (var=fetch2(sp),sp+=2) #define pop1(v1,v2) /* pop register pair */ (v2=fetch(sp),\ v1=fetch(sp+1),sp+=2) #define push2(val) /* push 16-bit register */ do{sp-=2;store2(sp,(val));}\ while(0) #define push1(v1,v2) /* push register pair */ do{sp-=2;\ store2b(sp,v1,v2);\ }while(0) instr(0,4); /* nop */ endinstr; instr(1,10); c=fetch(pc),pc++; b=fetch(pc),pc++; endinstr; instr(2,7); store(bc,a); endinstr; instr(3,6); if(!++c)b++; endinstr; instr(4,4); inc(b); endinstr; instr(5,4); dec(b); endinstr; instr(6,7); b=fetch(pc),pc++; endinstr; instr(7,4); a=(a<<1)|(a>>7); f=(f&0xc4)|(a&0x29); endinstr; instr(8,4); swap(a,a1); swap(f,f1); endinstr; instr(9,11); addhl(b,c); endinstr; instr(10,7); a=fetch(bc); endinstr; instr(11,6); if(!c--)b--; endinstr; instr(12,4); inc(c); endinstr; instr(13,4); dec(c); endinstr; instr(14,4); c=fetch(pc),pc++; endinstr; instr(15,4); f=(f&0xc4)|(a&1); a=(a>>1)|(a<<7); f|=a&0x28; endinstr; instr(16,8); if(!--b)pc++; else jr; endinstr; instr(17,10); e=fetch(pc),pc++; d=fetch(pc),pc++; endinstr; instr(18,7); store(de,a); endinstr; instr(19,6); if(!++e)d++; endinstr; instr(20,4); inc(d); endinstr; instr(21,4); dec(d); endinstr; instr(22,7); d=fetch(pc),pc++; endinstr; instr(23,4); {int t=a>>7; a=(a<<1)|(f&1); f=(f&0xc4)|(a&0x28)|t; } endinstr; instr(24,7); jr; endinstr; instr(25,11); addhl(d,e); endinstr; instr(26,7); a=fetch(de); endinstr; instr(27,6); if(!e--)d--; endinstr; instr(28,4); inc(e); endinstr; instr(29,4); dec(e); endinstr; instr(30,4); e=fetch(pc),pc++; endinstr; instr(31,4); {int t=a&1; a=(a>>1)|(f<<7); f=(f&0xc4)|(a&0x28)|t; } endinstr; instr(32,7); if(f&0x40)pc++; else jr; endinstr; instr(33,10); if(!ixoriy){ l=fetch(pc),pc++; h=fetch(pc),pc++; } else { if(ixoriy==1)ix=fetch2(pc); else iy=fetch2(pc); pc+=2; } endinstr; instr(34,16); {unsigned short addr=fetch2(pc); pc+=2; if(!ixoriy)store2b(addr,h,l); else if(ixoriy==1)store2(addr,ix); else store2(addr,iy); } endinstr; instr(35,6); if(!ixoriy){if(!++l)h++;} else if(ixoriy==1)ix++; else iy++; endinstr; instr(36,4); if(ixoriy==0)inc(h); else{unsigned char t; t=(ixoriy==1?ix:iy)>>8; inc(t); if(ixoriy==1)ix=(ix&0xff)|(t<<8); else iy=(iy&0xff)|(t<<8); } endinstr; instr(37,4); if(ixoriy==0)dec(h); else{unsigned char t; t=(ixoriy==1?ix:iy)>>8; dec(t); if(ixoriy==1)ix=(ix&0xff)|(t<<8); else iy=(iy&0xff)|(t<<8); } endinstr; instr(38,7); setxh(fetch(pc)); pc++; endinstr; instr(39,4); { unsigned char incr=0, carry=cy; if((f&0x10) || (a&0x0f)>9) incr=6; if((f&1) || (a>>4)>9) incr|=0x60; if(f&2)suba(incr,0); else { if(a>0x90 && (a&15)>9)incr|=0x60; adda(incr,0); } f=((f|carry)&0xfb)|parity(a); } endinstr; instr(40,7); if(f&0x40)jr; else pc++; endinstr; instr(41,11); if(!ixoriy)addhl(h,l); else if(ixoriy==1)addhl((ix>>8),(ix&0xff)); else addhl((iy>>8),(iy&0xff)); endinstr; instr(42,16); {unsigned short addr=fetch2(pc); pc+=2; if(!ixoriy){ l=fetch(addr); h=fetch(addr+1); } else if(ixoriy==1)ix=fetch2(addr); else iy=fetch2(addr); } endinstr; instr(43,6); if(!ixoriy){if(!l--)h--;} else if(ixoriy==1)ix--; else iy--; endinstr; instr(44,4); if(!ixoriy)inc(l); else {unsigned char t; t=(ixoriy==1?ix:iy); inc(t); if(ixoriy==1)ix=(ix&0xff00)|t; else iy=(iy&0xff00)|t; } endinstr; instr(45,4); if(!ixoriy)dec(l); else {unsigned char t; t=(ixoriy==1?ix:iy); dec(t); if(ixoriy==1)ix=(ix&0xff00)|t; else iy=(iy&0xff00)|t; } endinstr; instr(46,4); setxl(fetch(pc)); pc++; endinstr; instr(47,4); a=~a; f=(f&0xc5)|(a&0x28)|0x12; endinstr; instr(48,7); if(f&1)pc++; else jr; endinstr; instr(49,10); sp=fetch2(pc); pc+=2; endinstr; instr(50,13); {unsigned short addr=fetch2(pc); pc+=2; store(addr,a); } endinstr; instr(51,6); sp++; endinstr; HLinstr(52,11,8); {unsigned char t=fetch(addr); inc(t); store(addr,t); } endinstr; HLinstr(53,11,8); {unsigned char t=fetch(addr); dec(t); store(addr,t); } endinstr; HLinstr(54,10,5); store(addr,fetch(pc)); pc++; endinstr; instr(55,4); f=(f&0xc4)|1|(a&0x28); endinstr; instr(56,7); if(f&1)jr; else pc++; endinstr; instr(57,11); addhl((sp>>8),(sp&0xff)); endinstr; instr(58,13); {unsigned short addr=fetch2(pc); pc+=2; a=fetch(addr); } endinstr; instr(59,6); sp--; endinstr; instr(60,4); inc(a); endinstr; instr(61,4); dec(a); endinstr; instr(62,4); a=fetch(pc),pc++; endinstr; instr(63,4); f=(f&0xc4)|(cy^1)|(cy<<4)|(a&0x28); endinstr; instr(0x40,4); /* ld b,b */ endinstr; instr(0x41,4); b=c; endinstr; instr(0x42,4); b=d; endinstr; instr(0x43,4); b=e; endinstr; instr(0x44,4); b=xh; endinstr; instr(0x45,4); b=xl; endinstr; HLinstr(0x46,7,8); b=fetch(addr); endinstr; instr(0x47,4); b=a; endinstr; instr(0x48,4); c=b; endinstr; instr(0x49,4); /* ld c,c */ endinstr; instr(0x4a,4); c=d; endinstr; instr(0x4b,4); c=e; endinstr; instr(0x4c,4); c=xh; endinstr; instr(0x4d,4); c=xl; endinstr; HLinstr(0x4e,7,8); c=fetch(addr); endinstr; instr(0x4f,4); c=a; endinstr; instr(0x50,4); d=b; endinstr; instr(0x51,4); d=c; endinstr; instr(0x52,4); /* ld d,d */ endinstr; instr(0x53,4); d=e; endinstr; instr(0x54,4); d=xh; endinstr; instr(0x55,4); d=xl; endinstr; HLinstr(0x56,7,8); d=fetch(addr); endinstr; instr(0x57,4); d=a; endinstr; instr(0x58,4); e=b; endinstr; instr(0x59,4); e=c; endinstr; instr(0x5a,4); e=d; endinstr; instr(0x5b,4); /* ld e,e */ endinstr; instr(0x5c,4); e=xh; endinstr; instr(0x5d,4); e=xl; endinstr; HLinstr(0x5e,7,8); e=fetch(addr); endinstr; instr(0x5f,4); e=a; endinstr; instr(0x60,4); setxh(b); endinstr; instr(0x61,4); setxh(c); endinstr; instr(0x62,4); setxh(d); endinstr; instr(0x63,4); setxh(e); endinstr; instr(0x64,4); /* ld h,h */ endinstr; instr(0x65,4); setxh(xl); endinstr; HLinstr(0x66,7,8); h=fetch(addr); endinstr; instr(0x67,4); setxh(a); endinstr; instr(0x68,4); setxl(b); endinstr; instr(0x69,4); setxl(c); endinstr; instr(0x6a,4); setxl(d); endinstr; instr(0x6b,4); setxl(e); endinstr; instr(0x6c,4); setxl(xh); endinstr; instr(0x6d,4); /* ld l,l */ endinstr; HLinstr(0x6e,7,8); l=fetch(addr); endinstr; instr(0x6f,4); setxl(a); endinstr; HLinstr(0x70,7,8); store(addr,b); endinstr; HLinstr(0x71,7,8); store(addr,c); endinstr; HLinstr(0x72,7,8); store(addr,d); endinstr; HLinstr(0x73,7,8); store(addr,e); endinstr; HLinstr(0x74,7,8); store(addr,h); endinstr; HLinstr(0x75,7,8); store(addr,l); endinstr; instr(0x76,4); pc--; /* keep nopping until int */ endinstr; HLinstr(0x77,7,8); store(addr,a); endinstr; instr(0x78,4); a=b; endinstr; instr(0x79,4); a=c; endinstr; instr(0x7a,4); a=d; endinstr; instr(0x7b,4); a=e; endinstr; instr(0x7c,4); a=xh; endinstr; instr(0x7d,4); a=xl; endinstr; HLinstr(0x7e,7,8); a=fetch(addr); endinstr; instr(0x7f,4); /* ld a,a */ endinstr; instr(0x80,4); adda(b,0); endinstr; instr(0x81,4); adda(c,0); endinstr; instr(0x82,4); adda(d,0); endinstr; instr(0x83,4); adda(e,0); endinstr; instr(0x84,4); adda(xh,0); endinstr; instr(0x85,4); adda(xl,0); endinstr; HLinstr(0x86,7,8); adda(fetch(addr),0); endinstr; instr(0x87,4); adda(a,0); endinstr; instr(0x88,4); adda(b,cy); endinstr; instr(0x89,4); adda(c,cy); endinstr; instr(0x8a,4); adda(d,cy); endinstr; instr(0x8b,4); adda(e,cy); endinstr; instr(0x8c,4); adda(xh,cy); endinstr; instr(0x8d,4); adda(xl,cy); endinstr; HLinstr(0x8e,7,8); adda(fetch(addr),cy); endinstr; instr(0x8f,4); adda(a,cy); endinstr; instr(0x90,4); suba(b,0); endinstr; instr(0x91,4); suba(c,0); endinstr; instr(0x92,4); suba(d,0); endinstr; instr(0x93,4); suba(e,0); endinstr; instr(0x94,4); suba(xh,0); endinstr; instr(0x95,4); suba(xl,0); endinstr; HLinstr(0x96,7,8); suba(fetch(addr),0); endinstr; instr(0x97,4); suba(a,0); endinstr; instr(0x98,4); suba(b,cy); endinstr; instr(0x99,4); suba(c,cy); endinstr; instr(0x9a,4); suba(d,cy); endinstr; instr(0x9b,4); suba(e,cy); endinstr; instr(0x9c,4); suba(xh,cy); endinstr; instr(0x9d,4); suba(xl,cy); endinstr; HLinstr(0x9e,7,8); suba(fetch(addr),cy); endinstr; instr(0x9f,4); suba(a,cy); endinstr; instr(0xa0,4); anda(b); endinstr; instr(0xa1,4); anda(c); endinstr; instr(0xa2,4); anda(d); endinstr; instr(0xa3,4); anda(e); endinstr; instr(0xa4,4); anda(xh); endinstr; instr(0xa5,4); anda(xl); endinstr; HLinstr(0xa6,7,8); anda(fetch(addr)); endinstr; instr(0xa7,4); anda(a); endinstr; instr(0xa8,4); xora(b); endinstr; instr(0xa9,4); xora(c); endinstr; instr(0xaa,4); xora(d); endinstr; instr(0xab,4); xora(e); endinstr; instr(0xac,4); xora(xh); endinstr; instr(0xad,4); xora(xl); endinstr; HLinstr(0xae,7,8); xora(fetch(addr)); endinstr; instr(0xaf,4); xora(a); endinstr; instr(0xb0,4); ora(b); endinstr; instr(0xb1,4); ora(c); endinstr; instr(0xb2,4); ora(d); endinstr; instr(0xb3,4); ora(e); endinstr; instr(0xb4,4); ora(xh); endinstr; instr(0xb5,4); ora(xl); endinstr; HLinstr(0xb6,7,8); ora(fetch(addr)); endinstr; instr(0xb7,4); ora(a); endinstr; instr(0xb8,4); cpa(b); endinstr; instr(0xb9,4); cpa(c); endinstr; instr(0xba,4); cpa(d); endinstr; instr(0xbb,4); cpa(e); endinstr; instr(0xbc,4); cpa(xh); endinstr; instr(0xbd,4); cpa(xl); endinstr; HLinstr(0xbe,7,8); cpa(fetch(addr)); endinstr; instr(0xbf,4); cpa(a); endinstr; instr(0xc0,5); if(!(f&0x40))ret; endinstr; instr(0xc1,10); pop1(b,c); endinstr; instr(0xc2,10); if(!(f&0x40))jp; else pc+=2; endinstr; instr(0xc3,10); jp; endinstr; instr(0xc4,10); if(!(f&0x40))call; else pc+=2; endinstr; instr(0xc5,11); push1(b,c); endinstr; instr(0xc6,7); adda(fetch(pc),0); pc++; endinstr; instr(0xc7,11); push2(pc); pc=0; endinstr; instr(0xc8,5); if(f&0x40)ret; endinstr; instr(0xc9,4); ret; endinstr; instr(0xca,10); if(f&0x40)jp; else pc+=2; endinstr; instr(0xcb,4); #include "cbops.c" endinstr; instr(0xcc,10); if(f&0x40)call; else pc+=2; endinstr; instr(0xcd,10); call; endinstr; instr(0xce,7); adda(fetch(pc),cy); pc++; endinstr; instr(0xcf,11); push2(pc); pc=8; endinstr; instr(0xd0,5); if(!cy)ret; endinstr; instr(0xd1,10); pop1(d,e); endinstr; instr(0xd2,10); if(!cy)jp; else pc+=2; endinstr; instr(0xd3,11); tstates+=out(a,fetch(pc),a); pc++; endinstr; instr(0xd4,10); if(!cy)call; else pc+=2; endinstr; instr(0xd5,11); push1(d,e); endinstr; instr(0xd6,7); suba(fetch(pc),0); pc++; endinstr; instr(0xd7,11); push2(pc); pc=16; endinstr; instr(0xd8,5); if(cy)ret; endinstr; instr(0xd9,4); swap(b,b1); swap(c,c1); swap(d,d1); swap(e,e1); swap(h,h1); swap(l,l1); endinstr; instr(0xda,10); if(cy)jp; else pc+=2; endinstr; instr(0xdb,11); {unsigned short t; a=t=in(a,fetch(pc)); tstates+=t>>8; pc++; } endinstr; instr(0xdc,10); if(cy)call; else pc+=2; endinstr; instr(0xdd,4); new_ixoriy=1; intsample=0; endinstr; instr(0xde,7); suba(fetch(pc),cy); pc++; endinstr; instr(0xdf,11); push2(pc); pc=24; endinstr; instr(0xe0,5); if(!(f&4))ret; endinstr; instr(0xe1,10); if(!ixoriy)pop1(h,l); else if(ixoriy==1)pop2(ix); else pop2(iy); endinstr; instr(0xe2,10); if(!(f&4))jp; else pc+=2; endinstr; instr(0xe3,19); if(!ixoriy){ unsigned short t=fetch2(sp); store2b(sp,h,l); l=t; h=t>>8; } else if(ixoriy==1){ unsigned short t=fetch2(sp); store2(sp,ix); ix=t; } else{ unsigned short t=fetch2(sp); store2(sp,iy); iy=t; } endinstr; instr(0xe4,10); if(!(f&4))call; else pc+=2; endinstr; instr(0xe5,11); if(!ixoriy)push1(h,l); else if(ixoriy==1)push2(ix); else push2(iy); endinstr; instr(0xe6,7); anda(fetch(pc)); pc++; endinstr; instr(0xe7,11); push2(pc); pc=32; endinstr; instr(0xe8,5); if(f&4)ret; endinstr; instr(0xe9,4); pc=!ixoriy?hl:ixoriy==1?ix:iy; endinstr; instr(0xea,10); if(f&4)jp; else pc+=2; endinstr; instr(0xeb,4); swap(h,d); swap(e,l); endinstr; instr(0xec,10); if(f&4)call; else pc+=2; endinstr; instr(0xed,4); #include"edops.c" endinstr; instr(0xee,7); xora(fetch(pc)); pc++; endinstr; instr(0xef,11); push2(pc); pc=40; endinstr; instr(0xf0,5); if(!(f&0x80))ret; endinstr; instr(0xf1,10); pop1(a,f); endinstr; instr(0xf2,10); if(!(f&0x80))jp; else pc+=2; endinstr; instr(0xf3,4); iff1=iff2=0; intsample=0; endinstr; instr(0xf4,10); if(!(f&0x80))call; else pc+=2; endinstr; instr(0xf5,11); push1(a,f); endinstr; instr(0xf6,7); ora(fetch(pc)); pc++; endinstr; instr(0xf7,11); push2(pc); pc=48; endinstr; instr(0xf8,5); if(f&0x80)ret; endinstr; instr(0xf9,6); sp=!ixoriy?hl:ixoriy==1?ix:iy; endinstr; instr(0xfa,10); if(f&0x80)jp; else pc+=2; endinstr; instr(0xfb,4); iff1=iff2=1; intsample=0; endinstr; instr(0xfc,10); if(f&0x80)call; else pc+=2; endinstr; instr(0xfd,4); new_ixoriy=2; intsample=0; endinstr; instr(0xfe,7); cpa(fetch(pc)); pc++; endinstr; instr(0xff,11); push2(pc); pc=56; endinstr; aylet-0.5/button1.xpm0100600000175000001440000000050607362064061013344 0ustar rususers/* XPM */ static char *button1[] = { /* width height ncolors chars_per_pixel */ "14 10 2 1", /* colors */ " c black", ". c None", /* pixels */ " .......... ", " ........ ", " ...... ", " .... ", " .. ", " .... ", " ...... ", " ........ ", " .......... ", ".............." }; aylet-0.5/button2.xpm0100600000175000001440000000043607362064071013350 0ustar rususers/* XPM */ static char *button2[] = { /* width height ncolors chars_per_pixel */ "10 10 2 1", /* colors */ " c black", ". c None", /* pixels */ " ........", " ......", " ....", " ..", " ", " ..", " ....", " ......", " ........", ".........." }; aylet-0.5/button3.xpm0100600000175000001440000000043607362064104013346 0ustar rususers/* XPM */ static char *button3[] = { /* width height ncolors chars_per_pixel */ "10 10 2 1", /* colors */ " c black", ". c None", /* pixels */ " .... ", " .... ", " .... ", " .... ", " .... ", " .... ", " .... ", " .... ", " .... ", ".........." }; aylet-0.5/button4.xpm0100600000175000001440000000043607362064130013346 0ustar rususers/* XPM */ static char *button4[] = { /* width height ncolors chars_per_pixel */ "10 10 2 1", /* colors */ " c black", ". c None", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " ", " ", ".........." }; aylet-0.5/button5.xpm0100600000175000001440000000050607362064142013350 0ustar rususers/* XPM */ static char *button5[] = { /* width height ncolors chars_per_pixel */ "14 10 2 1", /* colors */ " c black", ". c None", /* pixels */ " .......... ", " ........ ", " ...... ", " .... ", " .. ", " .... ", " ...... ", " ........ ", " .......... ", ".............." }; aylet-0.5/driver.h0100600000175000001440000000047210031132570012654 0ustar rususers/* aylet 0.2, a .AY music file player. * Copyright (C) 2001 Russell Marks and Ian Collier. See main.c for licence. * * driver.h - function prototypes for audio drivers. */ extern int driver_init(int *freqptr,int *stereoptr); extern void driver_end(void); extern void driver_frame(signed short *data,int len); aylet-0.5/drv-obsd.c0100600000175000001440000000272210031135564013102 0ustar rususers/* aylet 0.4, a .AY music file player. * Copyright (C) 2001-2004 Russell Marks and Ian Collier. See main.c for licence. * * drv_obsd.c - (Rough) native audio driver for OpenBSD (by Chris Cox). */ #ifdef DRIVER_OPENBSD #include #include #include #include #include #include #include "sound.h" #include "driver.h" int driver_init (int *freqptr, int *stereoptr) { audio_info_t ainfo; if (play_to_stdout) { soundfd = 1; return 1; } if ((soundfd = open ("/dev/sound", O_WRONLY, 0)) < 0) return 0; AUDIO_INITINFO (&ainfo); /* TODO: hackish. */ ainfo.play.encoding = AUDIO_ENCODING_SLINEAR; ainfo.play.channels = (*stereoptr) ? 2 : 1; ainfo.play.precision = (sixteenbit ? 16 : 8); ainfo.play.sample_rate = (*freqptr); if ((ioctl (soundfd, AUDIO_SETINFO, &ainfo)) < 0) { sixteenbit = 0; return 0; } else sixteenbit = 1; return 1; } void driver_end (void) { if (soundfd > 1) close (soundfd); } void driver_frame(signed short *data,int len) { static unsigned char buf8[4096]; unsigned char *data8=(unsigned char *)data; int ret=0,ofs=0; len<<=1; /* now in bytes */ if(!sixteenbit) { signed short *src; unsigned char *dst; int f; src=data; dst=buf8; len>>=1; for(f=0;f0) ofs+=ret,len-=ret; } } #endif /* DRIVER_OPENBSD */