diff options
Diffstat (limited to 'scaf')
-rw-r--r-- | scaf/COPYING | 340 | ||||
-rw-r--r-- | scaf/Makefile | 22 | ||||
-rw-r--r-- | scaf/Makefile.config | 34 | ||||
-rw-r--r-- | scaf/README | 88 | ||||
-rw-r--r-- | scaf/README.scaf | 98 | ||||
-rw-r--r-- | scaf/include/Makefile | 6 | ||||
-rw-r--r-- | scaf/include/pdp_ca.h | 71 | ||||
-rw-r--r-- | scaf/modules/Makefile | 21 | ||||
-rw-r--r-- | scaf/modules/carules.scaf | 117 | ||||
-rw-r--r-- | scaf/pdp/Makefile | 13 | ||||
-rw-r--r-- | scaf/pdp/pdp_ca.c | 725 | ||||
-rw-r--r-- | scaf/pdp/pdp_ca_system.c | 228 | ||||
-rw-r--r-- | scaf/system/Makefile | 21 | ||||
-rw-r--r-- | scaf/system/kernel.scaf | 130 | ||||
-rw-r--r-- | scaf/system/optim.rules | 74 | ||||
-rw-r--r-- | scaf/system/scaf_feeder.s | 49 | ||||
-rw-r--r-- | scaf/system/scaf_feeder_test.c | 30 | ||||
-rwxr-xr-x | scaf/system/scafc.pl | 269 | ||||
-rw-r--r-- | scaf/system/scafmacro.s | 487 | ||||
-rw-r--r-- | scaf/test/test_pdp_ca.pd | 132 | ||||
-rw-r--r-- | scaf/test/test_pdp_ca2.pd | 154 |
21 files changed, 3109 insertions, 0 deletions
diff --git a/scaf/COPYING b/scaf/COPYING new file mode 100644 index 0000000..7f87ef8 --- /dev/null +++ b/scaf/COPYING @@ -0,0 +1,340 @@ + + 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 PDP.LICENSE, 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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. + + <signature of Ty Coon>, 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. diff --git a/scaf/Makefile b/scaf/Makefile new file mode 100644 index 0000000..2ac26a5 --- /dev/null +++ b/scaf/Makefile @@ -0,0 +1,22 @@ +include Makefile.config + +all: pdp_scaf.pd_linux + +pdp_scaf_all: + make -C include + make -C system + make -C modules + make -C pdp + +clean: + rm -f *~ + rm -f pdp_scaf.pd_linux + make -C include clean + make -C system clean + make -C modules clean + make -C pdp clean + + +pdp_scaf.pd_linux: pdp_scaf_all + rm -f pdp_scaf.pd_linux + gcc -export_dynamic -shared -o pdp_scaf.pd_linux pdp/*.o system/scaf_feeder.o $(PDP_CA_LIBS) diff --git a/scaf/Makefile.config b/scaf/Makefile.config new file mode 100644 index 0000000..7106a9f --- /dev/null +++ b/scaf/Makefile.config @@ -0,0 +1,34 @@ +# pd's directory +PD_DIR = /home/tom/pd/distro/pd +PDP_DIR = /home/tom/pd/packet + +#PD_DIR = /usr/local/pd +#PDP_DIR = /usr/local/pdp + +SCAF_DIR = $(PDP_DIR)/scaf + + + + +# build flags + +PDP_CA_INCLUDE = -I$(PD_DIR)/src -I$(PDP_DIR)/include -I$(SCAF_DIR)/include +PDP_CA_LIBS = -ldl +PDP_CA_AFLAGS = +#--gstabs +PDP_CA_CFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -ffast-math \ + -Wall -W -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch -g +# -Wshadow + +# compiler and assembler +CC = gcc-3.2 +#CC = gcc +AS = as + +# build rules + +.c.o: + $(CC) $(PDP_CA_CFLAGS) $(PDP_CA_INCLUDE) -o $*.o -c $*.c +.s.o: + $(AS) -o $*.o $*.s $(PDP_CA_AFLAGS) diff --git a/scaf/README b/scaf/README new file mode 100644 index 0000000..60ee4dd --- /dev/null +++ b/scaf/README @@ -0,0 +1,88 @@ +PDP_SCAF for pdp v0.7 +Cellular Automata modules for PDP + +Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + +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. + +The GNU Public Licence can be found in the file COPYING + + +------------------------------------------------------------------ + +This is a pdp extension lib that contains modules for cellular +automata built on a (very) minimal forth-like virtual system +(scaf - simple cellular automaton forth) to define update rules. a +compiler is included to produce scafo object code that can be +dynamically loaded into the pdp_ca module. so it is possible to +add/change rules without restarting pd (note however you need to close +all lib files before the dynamic loader reloads the lib). see +scaf/README for details. + + +pdp_ca2image and pdp_image2ca are included for conversion between +CA packets and image packets. (pdp_ca2image produces greyscale +images) + +Have a look at the patches in test/ for some crude docs. The file +README.scaf contains some more info on the internals. + + +Requirements: + +* pd +* pdp +* linux +* perl for the forth compiler +* an intel/amd processor that supports MMX + + +Building: + +Edit Makefile.config to reflect your system settings. For now this +should be the pd dir, the pdp dir and the pdp_scaf dir. + +type "make" in the top directory. Remember to type "make clean all" +after editing Makefile.config + + +Using: + +add "-lib <SCAF_DIR>/pdp_scaf" to the pd command line after the +"-lib <PDP_DIR>/pdp" part. + + + +launch pd with the options -lib $PDP_DIR/pdp -path $PDP_DIR/abstractions + +Directory structure: + +include/ header files +pdp/ pdp external code +system/ forth system code +test/ some test patches (cryptic doc) +modules/ ca rule libraries + + + +Please let me know if you discover a bug or think something doesn't work +right. Code, documentation or example patches are more than welcome of +course. + +Have Fun, + +Tom + +last modified: 2003/01/12 diff --git a/scaf/README.scaf b/scaf/README.scaf new file mode 100644 index 0000000..0035899 --- /dev/null +++ b/scaf/README.scaf @@ -0,0 +1,98 @@ +SCAF - simple cellular automaton forth + +scaf is a virtual machine / forth environment for binary arithmic +tailored to 2D 1 cell neighbourhood cellular automata. + +scaf is a compiled language. programs run inside a "feeder" +(sort of operating system if you want) + +the feeder is responsable for loading/storing CA cells +from/to memory. data in memory is organized as a scanline +encoded toroidial bitplane (lsb = left). to simplify the feeder +and the stack machine, the top left corner of the rectangular grid +of pixels will shift down every processing step. this enables +to keep a cell neighbourhood in a couple of registers. + +the stack machine has the following architecture: +CA stack: (%esi), TOS: %mm0 (32x2 cells. lsb = top left) +CA horizon: (%edi) (64x4 cells. (%edi) = top row. lsb = left) + +scratch register: %mm1, %mm2 +bitmask register: %mm3 = 0xffffffffffffffff + +4 bit counter: %mm4-%mm7 + +the stack size / organization is not known to the stack machine. +it can be thought of as operating on a 3x3 cell neightbourhood. +the only purpose of the forth program is to determine the CA local update rule. + +the machine is supposed to be very minimal. no looping control. +no adressing modes. no conditional code. so recursion is not allowed +(no way to stop it) there are 9 words to load the cell neigbourhood +on the stack. the rest is just logic and stack manips. + +the counter can be used for counting neighbourhood cells, like in the +game of life. the zero test and sign bit can be used for comparisons. +there are kernel words for loading constants into the counter register, +and for communication between stack and register. + +the horizon is limited to 3x3, however it can be easily extended to +32x3. extending it further than that would require a redesign of the +forth + feeder. + + +HOW TO CREATE NEW CA RULES + +edit scaf/modules/carules.scaf or create your own source lib and add +the name to the scaf/modules/Makefile. type make in scaf/modules +to compile. if you get error messages from the assembler saying things +like + + Error: no such instruction: `xxx' + + or + Error: invalid character '_' in mnemonic + +this means there are undefined words in your source file. since not +all characters are allowed in an asm file, the offending characters are +converted to _SOMETHINGELSE_ + +if you are stuck somewhere, just look at the output of scaf.pl on +your .scaf file to determine where the problem is. + +words that can be accessed from inside pdp_ca have to start with the +prefix rule_ and have to leave a single item on the data stack (the return +value) other rules can have all the stack effect you want, but for safety +reasons they can't be accessed from within pdp_ca. + + + +FORTH SYSTEM CODE + +the forth system is made up of the following files: + +kernel.scaf: a collection of forth kernel words +scafmacro.s: a set of asm macro definitions used in kernel.scaf +optim.rules: some substitution rules to eliminate redundant + stack manipulations + +scaf.pl: the compiler + +scaf.pl is run like this: + +scaf.pl -Isystemdir source.scaf + +if the -I switch is left out, the current directory is searched +for the system files. the compiler produces an assembler source +that includes scafmacro.s on standard output. + +the code it produces is relatively fast. it only uses and/or/xor +and shift mmx instructions. it's not optimal use of silicon but +it's pretty fast given what's possible. the feeder routine could +be improved though. + +porting to another platform would require a rewrite of scafmacro.s +the rest can be reused. if the target machine has 64 bit registers +(or if you can emulate this one using more registers) porting is +relatively easy. for larger registers a small change needs to +be made to the feeder routine in pdp_ca.c diff --git a/scaf/include/Makefile b/scaf/include/Makefile new file mode 100644 index 0000000..1aba02c --- /dev/null +++ b/scaf/include/Makefile @@ -0,0 +1,6 @@ +current: + + +clean: + rm -f *~ + diff --git a/scaf/include/pdp_ca.h b/scaf/include/pdp_ca.h new file mode 100644 index 0000000..acaeea7 --- /dev/null +++ b/scaf/include/pdp_ca.h @@ -0,0 +1,71 @@ +/* + * Cellular Automata Extension Module for pdp - Main header file + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * 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. + * + */ + +#ifndef PDP_CA_H +#define PDP_CA_H + +#include "pdp.h" + + + +/* 2D CA DATA PACKET */ +typedef struct +{ + unsigned int encoding; /* CA data format */ + unsigned int width; /* CA width (in 1 bit cells) */ + unsigned int height; /* CA height (in 1 bit cells) */ + unsigned int offset; /* bit offset of upper left corner */ + +} t_ca; + +/* CA encodings */ +#define PDP_CA_STANDARD 1 /* rectangular CA */ + +/* pdp data packet types */ +#define PDP_CA 2 /* 1bit toroidial shifted scanline encoded cellular automaton */ + + +/* all symbols are C-style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* some utility methods for CA */ +int pdp_type_isvalid_ca(int packet); +int pdp_type_ca2grey(int packet); +int pdp_type_grey2ca(int packet, short int threshold); + +/* returns a pointer to the ca subheader given the pdp header */ +t_ca *pdp_type_ca_info(t_pdp *x); + +/* mmx feeder routine */ +unsigned long long scaf_feeder(void *tos, void *reg, void (*ca_rule)(void), void *env); + + + + + +#ifdef __cplusplus +} +#endif + +#endif //PDP_CA_H diff --git a/scaf/modules/Makefile b/scaf/modules/Makefile new file mode 100644 index 0000000..7bb0dc9 --- /dev/null +++ b/scaf/modules/Makefile @@ -0,0 +1,21 @@ +OBJ = carules.scafo + +SCAFDIR = ../system + +.SUFFIXES: .scaf +.SUFFIXES: .scafo + +.scaf.o: + $(SCAFDIR)/scafc.pl -I$(SCAFDIR) $*.scaf | as -o $*.o + +.o.scafo: + gcc -export_dynamic -shared -o $*.scafo $*.o + rm $*.o + strip --strip-unneeded $*.scafo + +all: $(OBJ) + +clean: + rm -f *.scafo + rm -f *~ + diff --git a/scaf/modules/carules.scaf b/scaf/modules/carules.scaf new file mode 100644 index 0000000..32aef4a --- /dev/null +++ b/scaf/modules/carules.scaf @@ -0,0 +1,117 @@ +( Pure Data Packet - ca rules library. ) +( Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> ) +( ) +( 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. ) + + + +( this is the standard ca rules library ) +( a rule that is accessible from the ouside should start with "rule_" ) +( and has to return exactly one item on the stack (this is checked in pdp_ca) ) + + +( a word is a sequence of non whitespace characters (\S+) ) +( words are separated by whitespace (\s+) ) +( so "word ;" is not the same as "word;" ) + +( all words between the "(" word and the ")" word are ignored ) + +( ":" starts a definition, the next word is the name of the new word ) +( newline ends a definition ) + +( no more than one definition per line ) +( no more than one line per definition ) + +( ";" returns to calling word ) +( if no ";" is encountered the next defined word is executed ) +( this is to have multiple entry points ) +( multiple exit points don't make sense since there are no conditional words ) + + + +: +(4) ++++ ; ( 4 bit add TOS - carry on TOS ) +: +(3) +++ ; ( 3 bit add TOS - carry on TOS ) +: +(2) ++ ; ( 2 bit add TOS - carry on TOS ) + +: +top @-+ +(4) drop @0+ +(4) drop @++ +(4) ; ( add top row to reg - carry on TOS ) +: +mid @-0 +(4) drop @00 +(4) drop @+0 +(4) ; ( add mid row to reg - carry on TOS ) +: +bot @-- +(4) drop @0- +(4) drop @+- +(4) ; ( add bot row to reg - carry on TOS ) +: +all +top drop +mid drop +bot ; ( add all cells to reg - carry on TOS ) + +: +mid-1 @-0 +(4) drop @+0 +(4) ; ( add mid row except center element to reg - carry on TOS ) +: +all-1 +top drop +mid-1 drop +bot ; ( add all cells expet middle one to reg - carry on TOS ) + + +: countall a-0 +all drop ; ( count all cells - no stack effect ) +: countall-1 a-0 +all-1 drop ; ( count all cells except middle one - no stack effect ) + +: +topbot @0+ +(3) drop @0- +(3) ; +: +leftright @+0 +(3) drop @-0 +(3) ; +: +star +topbot drop +leftright ; +: countstar a-0 +star drop ; + +: =2or3? @a2 not @a1 and ; ( sum equal to 2 or 3? only checks 3 bits ) +: =3? =2or3? @a0 and ; ( sum equal to 3 ? ) +: =2? =2or3? @a0 not and ; ( sum equal to 2 ? ) +: =4? @a2 @a1 not and @a0 not and ; ( sum equal to 4 ? ) + + + +( some test rules ) + +( : rule_one 1 ; ) +( : rule_zero 0 ; ) +( : rule_id @00 ; ) + + +: rule_shiftleft @+0 ; +: rule_shifttop @0- ; +: rule_shiftbot @0+ ; +: rule_shifttopright @-- ; +: rule_strobe @00 not ; + +( game of life ) + +: rule_gameoflife countall-1 =2? @00 and =3? or ; + +( wolfram's rule 110) + +: rule_w110 @00 @+0 and not @-0 @00 @+0 or or and ; + + +( some other rules ) + +: rule_w110mod @0+ @+0 and not @-+ @0+ @++ or or and ; + +: rule_w110mod2 @0+ @+0 and not @-+ @0+ @+0 or or and ; +: rule_w110mod3 @0+ @++ and not @-+ @0+ @++ or or and @-0 @00 @+0 or or and ; + +: rule_golmod countall-1 =3? @00 and =2? or ; +: rule_golmod2 countall-1 =2? @0+ and =3? or ; +: rule_golmod3 countall-1 =2? @++ and =3? or ; +: rule_golmod4 countall-1 =2? @++ @-- or and =3? or ; +: rule_golmod5 countall-1 =2? @++ @-- or and =3? @+- and or ; +: rule_golmod6 countall-1 =2? @++ @-- or and =3? @+- and or @0+ or ; +: rule_golmod7 countall-1 =2? @++ @-- or and =3? @+- and or @0+ or @00 and ; + +( ca's with a short settling time ) + +: rule_block countstar =4? not =2? and @00 or ; +: rule_noiseedges countstar =4? =3? or not =2? and @00 or @++ xor ; +: rule_noiseplanes countstar =4? =3? or not =2? and @00 or @++ xor @-- xor ; + + +( : rule_noiseplanes countstar =4? =3? or not =2? and @00 or @++ xor @-- xor ; ) + diff --git a/scaf/pdp/Makefile b/scaf/pdp/Makefile new file mode 100644 index 0000000..73c8892 --- /dev/null +++ b/scaf/pdp/Makefile @@ -0,0 +1,13 @@ +current: all_modules + +include ../Makefile.config + +OBJECTS = pdp_ca.o pdp_ca_system.o + + +all_modules: $(OBJECTS) + +clean: + rm -f *~ + rm -f *.o + diff --git a/scaf/pdp/pdp_ca.c b/scaf/pdp/pdp_ca.c new file mode 100644 index 0000000..391b235 --- /dev/null +++ b/scaf/pdp/pdp_ca.c @@ -0,0 +1,725 @@ +/* + * Pure Data Packet module for cellular automata + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * 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 "pdp_ca.h" +#include <dlfcn.h> +#include <stdio.h> + +t_class *pdp_ca_class; // a cellular automaton processor: single input - single output +//t_class *pdp_ca2_class; // double input - single output +t_class *pdp_ca2image_class; // converter from ca -> grey/yv12 +t_class *pdp_image2ca_class; // converter from grey/yv12 -> ca + + +// *********************** CA CLASS STUFF ********************* + + + +#define PDP_CA_STACKSIZE 256 + +typedef struct pdp_ca_data_struct +{ + unsigned int env[2*4]; + unsigned int reg[2*4]; + unsigned int stack[2*PDP_CA_STACKSIZE]; + short int random_seed[4]; +} t_pdp_ca_data; + +typedef struct pdp_ca_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + int x_queue_id; + + /* double buffering data packets */ + int x_packet0; + int x_packet1; + + /* some data on the ca_routine */ + void (*x_ca_routine)(void); + void *x_ca_libhandle; + char *x_ca_rulenames; + int x_ca_nbrules; + char ** x_ca_rulename; + + /* nb of iterations */ + int x_iterations; + + + + /* aligned vector data */ + t_pdp_ca_data *x_data; + + /* output packet type */ + t_symbol *x_packet_type; + +} t_pdp_ca; + + + +/* process from packet0 -> packet1 */ +static void pdp_ca_process_ca(t_pdp_ca *x) +{ + t_pdp *header0 = pdp_packet_header(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + unsigned int *data0 = (unsigned int *)pdp_packet_data (x->x_packet0); + unsigned int *data1 = (unsigned int *)pdp_packet_data (x->x_packet1); + + + int width = pdp_type_ca_info(header0)->width; + int height = pdp_type_ca_info(header0)->height; + int i,j; + + /* load TOS in middle of buffer to limit the effect of stack errors */ + unsigned int *tos = &x->x_data->stack[2*(PDP_CA_STACKSIZE/2)]; + unsigned int *env = &x->x_data->env[0]; + unsigned int *reg = &x->x_data->reg[0]; + void *ca_routine = x->x_ca_routine; + unsigned int rtos; + + int offset = pdp_type_ca_info(header0)->offset; + int xoffset = offset % width; + int yoffset = offset / width; + + /* double word width: number of unsigned ints per row */ + int dwwidth = width >> 5; + + unsigned long long result = 0; + + /* exit if there isn't a valid routine */ + if(!ca_routine) return; + + //post("pdp_ca: PRE offset: %d, xoffset: %d, yoffset: %d", offset, xoffset, yoffset); + + /* calculate new offset: lines shift up, rows shift left by 16 cells */ + xoffset = (xoffset + width - 16) % width; + yoffset = (yoffset + height - 1) % height; + + offset = yoffset * width + xoffset; + + //post("pdp_ca: PST offset: %d, xoffset: %d, yoffset: %d", offset, xoffset, yoffset); + + + pdp_type_ca_info(header1)->offset = offset; + + + for(j=0; j<dwwidth*(height - 2); j+=(dwwidth<<1)){ + for(i=0; i < (dwwidth-1) ; i+=1){ + env[0] = data0[i + j]; + env[1] = data0[i + j + 1]; + env[2] = data0[i + j + dwwidth]; + env[3] = data0[i + j + dwwidth + 1]; + env[4] = data0[i + j + (dwwidth<<1)]; + env[5] = data0[i + j + (dwwidth<<1) + 1]; + env[6] = data0[i + j + (dwwidth<<1) + dwwidth]; + env[7] = data0[i + j + (dwwidth<<1) + dwwidth + 1]; + result = scaf_feeder(tos, reg, ca_routine, env); + data1[i + j] = result & 0xffffffff; + data1[i + j + dwwidth] = result >> 32; + } + // i == dwwidth-1 + + env[0] = data0[i + j]; + env[1] = data0[j]; + env[2] = data0[i + j + dwwidth]; + env[3] = data0[j + dwwidth]; + env[4] = data0[i + j + (dwwidth<<1)]; + env[5] = data0[j + (dwwidth<<1)]; + env[6] = data0[i + j + (dwwidth<<1) + dwwidth]; + env[7] = data0[j + (dwwidth<<1) + dwwidth]; + result = scaf_feeder(tos, reg, ca_routine, env); + data1[i + j] = result & 0xffffffff; + data1[i + j + dwwidth] = result >> 32; + } + + // j == dwwidth*(height - 2) + for(i=0; i < (dwwidth-1) ; i+=1){ + env[0] = data0[i + j]; + env[1] = data0[i + j + 1]; + env[2] = data0[i + j + dwwidth]; + env[3] = data0[i + j + dwwidth + 1]; + env[4] = data0[i]; + env[5] = data0[i + 1]; + env[6] = data0[i + dwwidth]; + env[7] = data0[i + dwwidth + 1]; + result = scaf_feeder(tos, reg, ca_routine, env); + data1[i + j] = result & 0xffffffff; + data1[i + j + dwwidth] = result >> 32; + } + // j == dwwidth*(height - 2) + // i == dwwidth-1 + env[0] = data0[i + j]; + env[1] = data0[j]; + env[2] = data0[i + j + dwwidth]; + env[3] = data0[j + dwwidth]; + env[4] = data0[i]; + env[5] = data0[0]; + env[6] = data0[i + dwwidth]; + env[7] = data0[dwwidth]; + result = scaf_feeder(tos, reg, ca_routine, env); + data1[i + j] = result & 0xffffffff; + data1[i + j + dwwidth] = result >> 32; + + + + /* check data stack pointer */ + rtos = (unsigned int)tos; + + if (env[0] != rtos){ + if (env[0] > rtos) post("pdp_ca: ERROR: stack underflow detected in ca routine"); + if (env[0] < rtos) post("pdp_ca: ERROR: ca routine returned more than one item"); + x->x_ca_routine = 0; + post("pdp_ca: rule disabled"); + + } + + return; +} + + +static void pdp_ca_swappackets(t_pdp_ca *x) +{ + /* swap packets */ + int packet = x->x_packet1; + x->x_packet1 = x->x_packet0; + x->x_packet0 = packet; +} + + + + + +/* tick advance CA one timestep */ +static void pdp_ca_bang_thread(t_pdp_ca *x) +{ + int encoding; + int packet; + int i; + + /* invariant: the two packets are allways valid and compatible + so a bang is allways possible. this means that in the pdp an + invalid packet needs to be converted to a valid one */ + + for(i=0; i < x->x_iterations; i++){ + + /* process form packet0 -> packet1 and propagate */ + pdp_ca_process_ca(x); + + /* swap */ + pdp_ca_swappackets(x); + + } + +} + +static void pdp_ca_sendpacket(t_pdp_ca *x) +{ + /* output the packet */ + outlet_pdp(x->x_outlet0, x->x_packet0); +} + +static void pdp_ca_bang(t_pdp_ca *x) +{ + /* we don't use input packets for testing dropping here + but check the queue_id to see if processing is + still going on */ + + if (-1 == x->x_queue_id){ + pdp_queue_add(x, pdp_ca_bang_thread, pdp_ca_sendpacket, &x->x_queue_id); + } + + else{ + pdp_control_notify_drop(-1); + } +} + + +/* this method stores the packet into x->x_packet0 (the packet + to be processed) if it is valid. x->x_packet1 is not compatible + it is regenerated so that it is + + in short, when this routine returns both packets are valid + and compatible. +*/ + + +static void pdp_ca_copy_rw_if_valid(t_pdp_ca *x, int packet) +{ + t_pdp *header = pdp_packet_header(packet); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + + + int grabpacket; + int convertedpacket; + + /* check if header is valid */ + if (!header) return; + + if (PDP_CA != header->type) return; + if (PDP_CA_STANDARD != pdp_type_ca_info(header)->encoding) return; + + + /* packet is a ca, register it */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_rw(packet); + + + /* make sure we have the right header */ + header = pdp_packet_header(x->x_packet0); + + + /* make sure that the other packet is compatible */ + if ((pdp_type_ca_info(header1)->width != pdp_type_ca_info(header)->width) || + (pdp_type_ca_info(header1)->height != pdp_type_ca_info(header)->height)) { + + /* if not, throw away and clone the new one */ + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + } + + +}; + +/* hot packet inlet */ +static void pdp_ca_input_0(t_pdp_ca *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_rw")){ + pdp_ca_copy_rw_if_valid(x, (int)f); + } + else if (s == gensym("process")){ + pdp_ca_bang(x); + } + + +} + +/* cold packet inlet */ +static void pdp_ca_input_1(t_pdp_ca *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_rw")) + { + pdp_ca_copy_rw_if_valid(x, (int)f); + } + +} + + +static void pdp_ca_rule_string(t_pdp_ca *x, char *c) +{ + char tmp[256]; + void (*prev_routine)(void); + + /* save previous routine ptr */ + prev_routine = x->x_ca_routine; + + /* check if we can find string */ + sprintf(tmp, "rule_%s", c); + if (!(x->x_ca_routine = dlsym(x->x_ca_libhandle, tmp))){ + post("pdp_ca: can't fine ca rule %s (symbol: %s)", c, tmp); + x->x_ca_routine = x->x_ca_routine; + return; + } + + /* all seems ok */ + //post("pdp_ca: using ca rule %s", c); + +} +static void pdp_ca_rule(t_pdp_ca *x, t_symbol *s) +{ + /* make sure lib is loaded */ + if (!x->x_ca_libhandle) return; + + /* set rule by name */ + pdp_ca_rule_string(x, s->s_name); +} + +static void pdp_ca_rule_index(t_pdp_ca *x, t_float f) +{ + int i = (int)f; + + /* make sure lib is loaded */ + if (!x->x_ca_libhandle) return; + + /* check index */ + if (i<0) return; + if (i>=x->x_ca_nbrules) return; + + /* set rule by index */ + pdp_ca_rule_string(x, x->x_ca_rulename[i]); + +} + + +static void pdp_ca_close(t_pdp_ca *x) +{ + if (x->x_ca_libhandle){ + dlclose(x->x_ca_libhandle); + x->x_ca_libhandle = 0; + x->x_ca_routine = 0; + if (x->x_ca_rulename){ + free (x->x_ca_rulename); + x->x_ca_rulename = 0; + } + + + } +} + + +static void pdp_ca_printrules(t_pdp_ca *x) +{ + int i; + + if (!(x->x_ca_libhandle)) return; + post("pdp_ca: found %d rules: ", x->x_ca_nbrules); + for(i=0;i<x->x_ca_nbrules; i++) post("%3d: %s ", i, x->x_ca_rulename[i]); + + +} + +static void pdp_ca_open(t_pdp_ca *x, t_symbol *s) +{ + + char *c; + int words; + + /* close current lib, if one */ + pdp_ca_close(x); + + /* try to open new lib */ + if (!(x->x_ca_libhandle = dlopen(s->s_name, RTLD_NOW))){ + post("pdp_ca: can't open ca library %s, %s", s->s_name, dlerror()); + x->x_ca_libhandle = 0; + } + + /* scan for valid rules */ + if (!(x->x_ca_rulenames = (char *)dlsym(x->x_ca_libhandle, "rulenames"))){ + post("pdp_ca: ERROR: %s does not contain a name table. closing.", s->s_name); + pdp_ca_close(x); + return; + } + + /* count rules */ + words = 0; + for(c = (char *)x->x_ca_rulenames; *c;){ + words++; + while(*c++); + } + x->x_ca_nbrules = words; + x->x_ca_rulename = (char **)malloc(sizeof(char *) * words); + + /* build name array */ + words = 0; + for(c = (char *)x->x_ca_rulenames; *c;){ + x->x_ca_rulename[words] = c; + words++; + while(*c++); + } + + /* ok, we're done */ + post("pdp_ca: opened rule library %s", s->s_name ,x->x_ca_nbrules); + + /* print rule names */ + //pdp_ca_printrules(x); + + + +} + +/* init the current packet with random noise */ +static void pdp_ca_rand(t_pdp_ca *x){ + + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *) pdp_packet_data(x->x_packet0); + int i; + + int nbshortints = (pdp_type_ca_info(header)->width >> 4) * pdp_type_ca_info(header)->height; + + for(i=0; i<nbshortints; i++) + data[i] = random(); + +} + + +static void pdp_ca_newca(t_pdp_ca *x, t_float width, t_float height) +{ + int w = (int)width; + int h = (int)height; + int bytesize; + t_pdp *header; + + /* ensure with = multiple of 64 */ + w &= 0xffffffc0; + + /* ensure height = multiple of 4 */ + w &= 0xfffffffc; + + w = (w<64) ? 64 : w; + h = (h<4) ? 4 : h; + + bytesize = (w>>3) * h; + + /* delete old packets */ + pdp_packet_mark_unused(x->x_packet0); + pdp_packet_mark_unused(x->x_packet1); + + + /* create new packets */ + x->x_packet0 = pdp_packet_new(PDP_CA, bytesize); + header = pdp_packet_header(x->x_packet0); + pdp_type_ca_info(header)->encoding = PDP_CA_STANDARD; + pdp_type_ca_info(header)->width = w; + pdp_type_ca_info(header)->height = h; + pdp_type_ca_info(header)->offset = 0; + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + + + /* fill with a test pattern */ + if(0) + { + unsigned int *d; + int i, s; + + s = (w * h) >> 5; + + /* fill the first packet with 01 */ + d = (unsigned int *)pdp_packet_data(x->x_packet0); + for (i=0; i<s; i++){ + d[i] = i; + } + + /* fill the second packet with 10 */ + d = (unsigned int *)pdp_packet_data(x->x_packet1); + for (i=0; i<s; i++){ + d[i] = i^-1; + } + + + + } + + + /* fill with random noise */ + pdp_ca_rand(x); + +} + + +static void pdp_ca_iterations(t_pdp_ca *x, t_float f) +{ + int i = (int)f; + + if (i < 0) i = 0; + + x->x_iterations = i; +} + +static void pdp_ca_free(t_pdp_ca *x) +{ + pdp_packet_mark_unused(x->x_packet0); + pdp_packet_mark_unused(x->x_packet1); + pdp_ca_close(x); + free(x->x_data); +} + + + +void *pdp_ca_new(void) +{ + t_pdp_ca *x = (t_pdp_ca *)pd_new(pdp_ca_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("iterations")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + x->x_data = (t_pdp_ca_data *)malloc(sizeof(t_pdp_ca_data)); + x->x_ca_routine = 0; + x->x_ca_libhandle = 0; + x->x_ca_rulename = 0; + + pdp_ca_newca(x, 64, 64); + pdp_ca_iterations(x, 1); + + x->x_packet_type = gensym("grey"); + + return (void *)x; +} + + +// *********************** CA CONVERTER CLASSES STUFF ********************* +// TODO: move this to a separate file later together with other converters (part of system?) + +#define PDP_CA2IMAGE 1 +#define PDP_IMAGE2CA 2 + +typedef struct pdp_ca_conv_struct +{ + t_object x_obj; + t_float x_f; + + + int x_threshold; + + int x_packet; + + t_outlet *x_outlet0; + + /* solve identity crisis */ + int x_whoami; + + /* output packet type */ + /* only greyscale for now */ + t_symbol *x_packet_type; + +} t_pdp_ca_conv; + +/* hot packet inlet */ +static void pdp_ca_conv_input_0(t_pdp_ca_conv *x, t_symbol *s, t_floatarg f) +{ + int packet = -1; + + if (s == gensym("register_ro")){ + pdp_packet_mark_unused(x->x_packet); + x->x_packet = pdp_packet_copy_ro((int)f); + return; + } + else if (s == gensym("process")){ + switch(x->x_whoami){ + case PDP_CA2IMAGE: + packet = pdp_type_ca2grey(x->x_packet); + break; + case PDP_IMAGE2CA: + packet = pdp_type_grey2ca(x->x_packet, x->x_threshold); + break; + } + + /* throw away the original packet */ + pdp_packet_mark_unused(x->x_packet); + x->x_packet = -1; + + /* unregister the freshly created packet */ + pdp_packet_mark_unused(packet); + + /* output if valid */ + if (-1 != packet) outlet_pdp(x->x_outlet0, packet); + } + + +} + +void pdp_ca_conv_free(t_pdp_ca_conv *x) +{ + pdp_packet_mark_unused(x->x_packet); +} + + +void pdp_image2ca_threshold(t_pdp_ca_conv *x, t_float f) +{ + f *= 0x8000; + + if (f < -0x7fff) f = -0x7fff; + if (f > 0x7fff) f = 0x7fff; + + x->x_threshold = (short int)f; +} + +void *pdp_ca2image_new(void) +{ + t_pdp_ca_conv *x = (t_pdp_ca_conv *)pd_new(pdp_ca2image_class); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet_type = gensym("grey"); + x->x_packet = -1; + x->x_whoami = PDP_CA2IMAGE; + return (void *)x; +} + +void *pdp_image2ca_new(void) +{ + t_pdp_ca_conv *x = (t_pdp_ca_conv *)pd_new(pdp_image2ca_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("threshold")); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet_type = gensym("grey"); + x->x_packet = -1; + x->x_whoami = PDP_IMAGE2CA; + x->x_threshold = 0x4000; + return (void *)x; +} + + +// *********************** CLASS SETUP FUNCTIONS ********************* + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_ca2image_setup(void) +{ + pdp_ca2image_class = class_new(gensym("pdp_ca2image"), (t_newmethod)pdp_ca2image_new, + (t_method)pdp_ca_conv_free, sizeof(t_pdp_ca), 0, A_NULL); + class_addmethod(pdp_ca2image_class, (t_method)pdp_ca_conv_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); +} + +void pdp_image2ca_setup(void) +{ + pdp_image2ca_class = class_new(gensym("pdp_image2ca"), (t_newmethod)pdp_image2ca_new, + (t_method)pdp_ca_conv_free, sizeof(t_pdp_ca), 0, A_NULL); + class_addmethod(pdp_image2ca_class, (t_method)pdp_ca_conv_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_image2ca_class, (t_method)pdp_image2ca_threshold, gensym("threshold"), A_FLOAT, A_NULL); +} + +void pdp_ca_setup(void) +{ + + + pdp_ca_class = class_new(gensym("pdp_ca"), (t_newmethod)pdp_ca_new, + (t_method)pdp_ca_free, sizeof(t_pdp_ca), 0, A_NULL); + + + class_addmethod(pdp_ca_class, (t_method)pdp_ca_iterations, gensym("iterations"), A_FLOAT, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_printrules, gensym("rules"), A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_rand, gensym("random"), A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_newca, gensym("ca"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_close, gensym("close"), A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_rule, gensym("rule"), A_SYMBOL, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_rule_index, gensym("ruleindex"), A_FLOAT, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ca_class, (t_method)pdp_ca_input_1, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/scaf/pdp/pdp_ca_system.c b/scaf/pdp/pdp_ca_system.c new file mode 100644 index 0000000..0800380 --- /dev/null +++ b/scaf/pdp/pdp_ca_system.c @@ -0,0 +1,228 @@ +/* + * Cellular Automata Extension Module for pdp - Main system code + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * 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 "pdp_ca.h" + +/* all symbols are C-style */ +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* check if packet is a valid ca packet */ +int pdp_type_isvalid_ca(int packet) +{ + t_pdp *header = pdp_packet_header(packet); + if (!header) return 0; + if (PDP_CA != header->type) return 0; + if (PDP_CA_STANDARD != pdp_type_ca_info(header)->encoding) return 0; + + return 1; +} + +/* convert a CA packet to greyscale */ + +inline void _pdp_type_ca2grey_convert_word(unsigned short int source, short int *dest) +{ + + int i; + for (i = 15; i>=0; i--){ + dest[i] = ((unsigned short)(((short int)(source & 0x8000)) >> 14)) >> 1; + source <<= 1; + } +} + +int pdp_type_ca2grey(int packet) +{ + int w, h, s, x, y, srcindex; + long long offset, xoffset, yoffset; + short int *dest; + unsigned short int *source; + t_pdp *header; + t_pdp *newheader; + int newpacket; + if (!(pdp_type_isvalid_ca(packet))) return -1; + + header = pdp_packet_header(packet); + w = pdp_type_ca_info(header)->width; + h = pdp_type_ca_info(header)->height; + s = w*h; + source = (unsigned short int *)pdp_packet_data(packet); + offset = pdp_type_ca_info(header)->offset; + yoffset = (offset / w) * w; + xoffset = offset % w; + + //post("pdp_type_ca2grey: offset: %d, xoffset: %d, yoffset: %d", offset, xoffset, yoffset); + + newpacket = pdp_packet_new(PDP_IMAGE, s<<1); + newheader = pdp_packet_header(newpacket); + newheader->info.image.width = w; + newheader->info.image.height = h; + newheader->info.image.encoding = PDP_IMAGE_GREY; + dest = (short int *)pdp_packet_data(newpacket); + + +#define check_srcindex \ +if (srcindex >= (s >> 4)) post ("pdp_type_ca2grey: srcindex out of bound"); + +#define check_dstindex \ +if ((x+y) >= s) post ("pdp_type_ca2grey: dstindex out of bound"); + + + /* dont' shift offset */ + if (0){ + for(y=0; y< (h*w); y+=w){ + for(x=0; x<w; x+=16){ + _pdp_type_ca2grey_convert_word (source[(x+y)>>4], &dest[x+y]); + } + } + return newpacket; + } + + + /* create top left */ + for (y=0; y < (h*w) - yoffset; y+=w) { + for (x=0; x< (w - xoffset); x+=16) { + srcindex = (x+xoffset + y+yoffset) >> 4; + //check_srcindex; + //check_dstindex; + _pdp_type_ca2grey_convert_word (source[srcindex], &dest[x+y]); + } + } + + /* create top right */ + for (y=0; y < (h*w) - yoffset; y+=w) { + for (x = (w - xoffset); x < w; x+=16) { + srcindex = (x+xoffset-w + y+yoffset) >> 4; + //check_srcindex; + //check_dstindex; + _pdp_type_ca2grey_convert_word (source[srcindex], &dest[x+y]); + } + } + + /* create bottom left */ + for (y=(h*w) - yoffset; y < h*w; y+=w) { + for (x=0; x< (w - xoffset); x+=16) { + srcindex = (x+xoffset + y+yoffset-(w*h)) >> 4; + //check_srcindex; + //check_dstindex; + _pdp_type_ca2grey_convert_word (source[srcindex], &dest[x+y]); + } + } + + /* create bottom right */ + for (y=(h*w) - yoffset; y < h*w; y+=w) { + for (x = (w - xoffset); x < w; x+=16) { + srcindex = (x+xoffset-w + y+yoffset-(w*h)) >> 4; + //check_srcindex; + //check_dstindex; + _pdp_type_ca2grey_convert_word (source[srcindex], &dest[x+y]); + } + } + + + return newpacket; + +} + + +inline unsigned short int _pdp_type_grey2ca_convert_word(short int *src, short int threshold) +{ + short int tmp; + short int dest = 0; + int i; + + for (i = 15; i >= 0; i--){ + dest <<= 1; + dest |= (src[i] > threshold); + } + + return dest; +} + + + +int pdp_type_grey2ca(int packet, short int threshold) +{ + int w, h, s, x, y, srcindex; + long long offset, xoffset, yoffset; + short int *dest; + short int *source; + t_pdp *header; + t_pdp *newheader; + int newpacket; + if (!(pdp_type_isvalid_image(packet))) return -1; + + header = pdp_packet_header(packet); + w = header->info.image.width; + h = header->info.image.height; + s = w*h; + source = (unsigned short int *)pdp_packet_data(packet); + + if ( (PDP_IMAGE_GREY != header->info.image.encoding) + && (PDP_IMAGE_YV12 != header->info.image.encoding)) return -1; + + newpacket = pdp_packet_new(PDP_CA, s>>3); + newheader = pdp_packet_header(newpacket); + pdp_type_ca_info(newheader)->width = w; + pdp_type_ca_info(newheader)->height = h; + pdp_type_ca_info(newheader)->encoding = PDP_CA_STANDARD; + pdp_type_ca_info(newheader)->offset = 0; + + dest = (short int *)pdp_packet_data(newpacket); + + for(y=0; y< (h*w); y+=w){ + for(x=0; x<w; x+=16){ + dest[(x+y)>>4] = _pdp_type_grey2ca_convert_word (&source[x+y], threshold); + } + } + return newpacket; + + +} + +/* returns a pointer to the ca subheader given the pdp header */ +t_ca *pdp_type_ca_info(t_pdp *x){return (t_ca *)(&x->info.raw);} + + +void pdp_ca_setup(void); +void pdp_ca2image_setup(void); +void pdp_image2ca_setup(void); + + +void pdp_scaf_setup(void) +{ + /* babble */ + post ("PDP: pdp_scaf extension lib (mmx version)"); + + /* setup modules */ + pdp_ca_setup(); + pdp_ca2image_setup(); + pdp_image2ca_setup(); + +} + + + + +#ifdef __cplusplus +} +#endif diff --git a/scaf/system/Makefile b/scaf/system/Makefile new file mode 100644 index 0000000..2a07f4f --- /dev/null +++ b/scaf/system/Makefile @@ -0,0 +1,21 @@ +all: scaf_feeder.o + +test: scaf_feeder_test + +OBJ = scaf_feeder_test.o scaf_feeder.o + + +scaf_feeder_test: $(OBJ) + gcc -o scaf_feeder_test *.o -g -ldl + +.s.o: + as -o $*.o $*.s + +.c.o: + gcc -c $*.c -o $*.o -g + +clean: + rm -f *~ + rm -f *.o + rm -f scaf_feeder_test + diff --git a/scaf/system/kernel.scaf b/scaf/system/kernel.scaf new file mode 100644 index 0000000..0bc2788 --- /dev/null +++ b/scaf/system/kernel.scaf @@ -0,0 +1,130 @@ +( Pure Data Packet - scaforth kernel. ) +( Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> ) +( ) +( 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. ) + + + + + +( this file contains the inline words in the scaforth kernel. ) +( when a file is compiled to asm, it will consist of word ) +( definition asm routines, macros and jmp call ret instructions. ) +( ) +( all words in this file are defined in terms of asm macros ) +( defined in scafmacros.s ) + + + +( stack manip words ) + +: over dup dropover ; + +( neighbourhood cell fetch words ) + +: @-+ dup dropldTL ; +: @0+ dup dropldTM ; +: @++ dup dropldTR ; +: @-0 dup dropldML ; +: @00 dup dropldMM ; +: @+0 dup dropldMR ; +: @-- dup dropldBL ; +: @0- dup dropldBM ; +: @+- dup dropldBR ; + +( boolean logic ) + +: or overor nip ; +: xor overxor nip ; +: and overand nip ; + +( binary constant loading ) + +: 1 dup dropone ; +: 0 dup dropzero ; + +( 4,3,2,1 bit add stack to register, leave carry on stack ) + +: ++++ adb0 adb1 adb2 adb3 ; +: +++ adb0 adb1 adb2 ; +: ++ adb0 adb1 ; +: + adb0 ; + +( 4,3,2 bit shifted 1 add ) + +: ++++<<1 adb1 adb2 adb3 ; +: +++<<1 adb1 adb2 ; +: ++<<1 adb1 ; + +( 4,3 bit shifted 2 add ) + +: ++++<<2 adb2 adb3 ; +: +++<<2 adb2 ; + +( 4 bit shifted 3 add ) + +: ++++<<3 adb3 ; + +( 4 bit accumulator access ) + +: !a0 dupsta0 drop ; +: !a1 dupsta1 drop ; +: !a2 dupsta2 drop ; +: !a3 dupsta3 drop ; + +: @a0 dup droplda0 ; +: @a1 dup droplda1 ; +: @a2 dup droplda2 ; +: @a3 dup droplda3 ; + +( 4,3,2,1 bit accumulator zero tests ) + +: ?anz dup dropisnonzero4 ; +: ?anz4 dup dropisnonzero4 ; +: ?anz3 dup dropisnonzero3 ; +: ?anz2 dup dropisnonzero2 ; +: ?anz1 dup dropisnonzero1 ; + +( load constants into accumulator ) + +: a0 a0000 ; +: a-0 a0000 ; +: a+0 a0000 ; +: a+1 a0001 ; +: a+2 a0010 ; +: a+3 a0011 ; +: a+4 a0100 ; +: a+5 a0101 ; +: a+6 a0110 ; +: a+7 a0111 ; + +: a+8 a1000 ; +: a+9 a1001 ; +: a+10 a1010 ; +: a+11 a1011 ; +: a+12 a1100 ; +: a+13 a1101 ; +: a+14 a1110 ; +: a+15 a1111 ; + +: a-8 a1000 ; +: a-7 a1001 ; +: a-6 a1010 ; +: a-5 a1011 ; +: a-4 a1100 ; +: a-3 a1101 ; +: a-2 a1110 ; +: a-1 a1111 ; + diff --git a/scaf/system/optim.rules b/scaf/system/optim.rules new file mode 100644 index 0000000..282caf4 --- /dev/null +++ b/scaf/system/optim.rules @@ -0,0 +1,74 @@ +# Pure Data Packet - scaf optimization rules. +# Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> +# +# 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. + + +# this file contains scaf source optimization rules for scaf compiler +# applied after kernel word inlining and before compilation to asm + +# one rule that's not in here, and is the responsability for the +# final compilation step: "word ;" is "jmp word" instead of "call word ret" + +# TODO: think about order! + +# no discrimination between pre inline and post inline optimization ops yet + +# pre inline optimizations + +"over xor" -> "overxor" +"over and" -> "overand" +"over or" -> "overor" + +"drop 1" -> "dropone" +"drop 0" -> "dropzero" +"over add" -> "overadd" +"over addc" -> "overaddc" + +"dup !a0" -> "dupsta0" +"dup !a1" -> "dupsta1" +"dup !a2" -> "dupsta2" +"dup !a3" -> "dupsta3" + +"drop @a0" -> "droplda0" +"drop @a1" -> "droplda1" +"drop @a2" -> "droplda2" +"drop @a3" -> "droplda3" + +"drop ?anz" -> "dropisnonzero4" +"drop ?anz4" -> "dropisnonzero4" +"drop ?anz3" -> "dropisnonzero3" +"drop ?anz2" -> "dropisnonzero2" +"drop ?anz1" -> "dropisnonzero1" + +"drop @-+" -> "dropldTL" +"drop @0+" -> "dropldTM" +"drop @++" -> "dropldTR" +"drop @-0" -> "dropldML" +"drop @00" -> "dropldMM" +"drop @+0" -> "dropldMR" +"drop @--" -> "dropldBL" +"drop @0-" -> "dropldBM" +"drop @+-" -> "dropldBR" + + +# post inline optimizations + +"dup drop" -> "" +"swap drop" -> "nip" +"dup swap" -> "dup" +"drop dup" -> "dropdup" +"drop over" -> "dropover" +"nip dup" -> "nipdup" diff --git a/scaf/system/scaf_feeder.s b/scaf/system/scaf_feeder.s new file mode 100644 index 0000000..1cd8fd3 --- /dev/null +++ b/scaf/system/scaf_feeder.s @@ -0,0 +1,49 @@ +# Pure Data Packet - scaf feeder routine. +# Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> +# +# 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 "scafmacro.s" + + +# *rg is only used for returning the stack pointer +# the 4 bit counter is using registers mm4-mm7 now +# long long scaf_feeder(void *tos, void *rg, *void() ca_rule, void *env) +.globl scaf_feeder +.type scaf_feeder, @function +scaf_feeder: + pushl %ebp + movl %esp, %ebp + push %esi + push %edi + + movl 20(%ebp), %edi # load env ptr + movl 8(%ebp), %esi # load TOS2 ptr + movl 16(%ebp), %eax # address of ca routine + pcmpeqw %mm3, %mm3 # load 1 reg + + call *%eax # TOS = 32x2 cell result + dup # push %mm0 to memory + movl (%esi), %eax + movl 4(%esi), %edx + lea 16(%esi), %esi # discard stack + movl %esi, (%edi) # store for stack underflow check + + emms + pop %edi + pop %esi + leave + ret diff --git a/scaf/system/scaf_feeder_test.c b/scaf/system/scaf_feeder_test.c new file mode 100644 index 0000000..23a2661 --- /dev/null +++ b/scaf/system/scaf_feeder_test.c @@ -0,0 +1,30 @@ +#include <dlfcn.h> + +void scaf_feeder_asm (void *tos, void *reg, void (*ca_rule)(), void *env); + +void ca_test() {} + +main() +{ + int stack[256]; + int reg[8]; + int env[8]; + + void *libhandle; + void *ca_routine; + + + if (!(libhandle = dlopen("../modules/test.scafo", RTLD_NOW))){ + printf("error: %s\n", dlerror()); + exit(1); + } + + if (!(ca_routine = dlsym(libhandle, "carule_1"))){ + printf("error: %s\n", dlerror()); + exit(1); + } + + scaf_feeder_asm(stack+254, reg, ca_routine, env); + + dlclose(libhandle); +} diff --git a/scaf/system/scafc.pl b/scaf/system/scafc.pl new file mode 100755 index 0000000..ee6b969 --- /dev/null +++ b/scaf/system/scafc.pl @@ -0,0 +1,269 @@ +#!/usr/bin/perl + +# Pure Data Packet - scafc: scaf compiler. +# Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> +# +# 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. +# + +# set this if you want to enable/disable optimizing + +$optimize = 1; + + +# this parses a single scaf line +# it is not very intelligent. only looks for 1 def on a line +# todo: change later so it can read multiple lines + + +sub remove_illegal_characters { + my $line = shift; + $$line =~ s/\+/_PLUS_/g; + $$line =~ s/-/_MINUS_/g; + $$line =~ s/\@/_AT_/g; + $$line =~ s/:/_COLON_/g; + $$line =~ s/\?/_QMARK_/g; + $$line =~ s/<</_SHIFT_/g; + $$line =~ s/</_ST_/g; + $$line =~ s/>/_GT_/g; + $$line =~ s/=/_EQ_/g; + $$line =~ s/\(/_OPEN_/g; + $$line =~ s/\)/_CLOSE_/g; +} + +sub parse_scaf_line { + my $word, $def, $sub; + shift; + + # this transforms the source into a parsed assembly like form + # a word label: "<word>:<ret>" + # a word definition line "<tab><word><ret>" + # last def = <ret><ret> + + # dont process if line doesn't have a def + + # first remove comments + s/\(\s+(\S+\s+)*?\)//g; + + if (m/:\s+/){ + + # separate word and definition + m/:\s+(\S+)\s+(.*)/; + $word = $1; + $def = $2; + + # remove illegal characters; + remove_illegal_characters \$word; + remove_illegal_characters \$def; + + # format definition in asm style + $def =~ s/(\S+)(\s*)/\t$1\n/g; + + # replace ; by r + $def =~ s/\s+;\s*/\n\tr\n/; + + # put word: def into one string + $sub = "$word:\n$def\n"; + + # debug + #$sub =~ s/\t/<tab>/g; + #$sub =~ s/\n/<ret>\n/g; + #print "$sub"; + + return $sub; + + } + +}; + + + +# load and parse scaf source file +sub load_source { + my $filename = shift; + open(SOURCE, $filename) or die "Can't locate source module $filename\n"; + my @parsedsource; + while (<SOURCE>){ + my $sub = parse_scaf_line $_; + if ($sub) { + push @parsedsource, ($sub); + } + + } + close(SOURCE); + return @parsedsource; + +} + +# this routine parses the optimization rules +sub load_optim { + my $filename = shift; + open(OPTIM, $filename) or die "Can't locate optimization rule file $filename\n"; + my @parsedoptim; + while (<OPTIM>){ + unless (m/\A\#/){ + + if (m/\"\s*(.*?)\s*\".*?\"\s*(.*?)\s*\"/) + { + my $source = $1; + my $dest = $2; + + $source =~ s/\s+/\n\t/; + $dest =~ s/\s+/\n\t/; + $source = "\t$source\n"; + $dest = "\t$dest\n"; + + remove_illegal_characters \$source; + remove_illegal_characters \$dest; + + push @parsedoptim, ("$source:$dest"); + } + } + } + close(OPTIM); + + return @parsedoptim; + + +} + + + +# inline one parsed source's definitions into another parsed source's +sub inline_defs { + my $dest = shift; + my $source = shift; + + #print @$dest; + #print @$source; + + + # loop over file with inline defs + foreach (@$source) { + #print "<SUB>$_</SUB>\n"; + m/(\S+):\n(.*)\tr\n/s; + + my $def = "\t$1\n"; + my $body = $2; + + #print "<DEF>$def</DEF>\n"; + #print "<BODY>$body</BODY>\n"; + + foreach (@$dest) { + s/$def/$body/g; + } + + } + +} + +# this changes <WORD> to c <WORD> or j <WORD> all defined words +# the undefined words are supposed to be asm macros +sub call_defs { + my $dest = shift; + + foreach (@$dest){ + m/(\S+):\n/s; + my $word = $1; + foreach (@$dest){ + s/\t$word\n\tr\n/\tj $word\n/sg; + s/\t$word\n/\tc $word\n/sg; + } + } +} + +# substitue word sequences in dest using optim table +sub subst_optim { + my $dest = shift; + my $optim = shift; + foreach (@$optim){ + m/(.*?):(.*)/s; + my $key = $1; + my $subst = $2; + + foreach (@$dest){ + s/$key/$subst/sg; + } + } +} + +# add directives to produce global symbols +# global symbols need to start with carule_ +sub global_syms { + my $source = shift; + foreach (@$source){ + s/rule_(\S+):\n/.globl\trule_$1\n.type\trule_$1,\@function\nrule_$1:\n/sg; + } +} + +# create an array with names for bookkeeping +sub name_array { + my @namearray; + my $source = shift; + push @namearray, (".globl rulenames\nrulenames:\n"); + foreach (@$source){ + if (m/rule_(\S+):/s){ + push @namearray, (".asciz\t\"$1\"\n"); + } + } + push @namearray, (".byte\t0\n"); + return @namearray; + +} + +# main program body + +$dir="."; + +$source = "-"; + + +# parse command line +foreach (@ARGV){ + if (m/-I(.*)/) { + $dir = $1; + } + else { + $source = $_; + } +} + +$kernel = "$dir/kernel.scaf"; +$macro = "$dir/scafmacro.s"; +$rules = "$dir/optim.rules"; + + + +# load files +@psource = load_source $source; +@pkernel = load_source $kernel; +@poptim = load_optim $rules; + + +# substitute kernel defs in source +if ($optimize) {subst_optim \@psource, \@poptim;} +inline_defs \@psource, \@pkernel; + +if ($optimize) {subst_optim \@psource, \@poptim;} + +call_defs \@psource; +global_syms \@psource; +@pnames = name_array \@psource; + +# print out asm file +print ".include \"$macro\"\n\n"; +print @psource; +print @pnames; + diff --git a/scaf/system/scafmacro.s b/scaf/system/scafmacro.s new file mode 100644 index 0000000..04e6537 --- /dev/null +++ b/scaf/system/scafmacro.s @@ -0,0 +1,487 @@ + # Pure Data Packet - scaf assembler macros. + # Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + # + # 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. + # + + + # this file contains pure asm macros. it is to be included before assembly + # after scaforth.pl has processed the .scaf file + + # *************************** JMP CALL RET ************************************** + # j c r + + .macro j address + jmp \address + .endm + + .macro c address + call \address + .endm + + .macro r + ret + .endm + + + # *************************** CA CELL ACCESS MACROS ***************************** + # dropldTL - dropldBR + + # shift / load rectangle macros: + + # shift rectangle horizontal + # result is in reg1 + .macro shift reg1 reg2 count + psllq $(16-\count), \reg1 + psrlq $(16+\count), \reg2 + psrlq $32, \reg1 + psllq $32, \reg2 + por \reg2, \reg1 + .endm + + .macro ldtop reg1 reg2 + movq (%edi), \reg1 + movq 8(%edi), \reg2 + .endm + + .macro ldcenter reg1 reg2 + movq 8(%edi), \reg1 + movq 16(%edi), \reg2 + .endm + + .macro ldbottom reg1 reg2 + movq 16(%edi), \reg1 + movq 24(%edi), \reg2 + .endm + + + # dropld from top row + + # dropld the top left square + .macro dropldTL + ldtop %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # dropld the top mid square + .macro dropldTM + ldtop %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # dropld the top right square + .macro dropldTR + ldtop %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + # dropld from center row + + # dropld the mid left square + .macro dropldML + ldcenter %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # dropld the mid mid square + .macro dropldMM + ldcenter %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # dropld the mid right square + .macro dropldMR + ldcenter %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + + + # dropld from bottom row + + # dropld the bottom left square + .macro dropldBL + ldbottom %mm0, %mm1 + shift %mm0, %mm1, -1 + .endm + + # dropld the bottom mid square + .macro dropldBM + ldbottom %mm0, %mm1 + shift %mm0, %mm1, 0 + .endm + + # dropld the bottom right square + .macro dropldBR + ldbottom %mm0, %mm1 + shift %mm0, %mm1, 1 + .endm + + + + # *************************** CA STACK MANIP MACROS ***************************** + # these are the only asm macros that have a stack effect other than + # just replacing the TOS + # + # dup drop dropdup swap nip dropover + + .macro dup + lea -8(%esi), %esi + movq %mm0, (%esi) + .endm + + .macro drop + movq (%esi), %mm0 + lea 8(%esi), %esi + .endm + + .macro dropdup + movq (%esi), %mm0 + .endm + + .macro nipdup + movq %mm0, (%esi) + .endm + + .macro swap + movq (%esi), %mm1 + movq %mm0, (%esi) + movq %mm1, %mm0 + .endm + + .macro nip + lea 8(%esi), %esi + .endm + + .macro dropover + movq 8(%esi), %mm0 + .endm + + + # *************************** CA BOOLEAN LOGIC MACROS ***************************** + # overxor overand overor not + + .macro overxor + pxor (%esi), %mm0 + .endm + + .macro overand + pand (%esi), %mm0 + .endm + + .macro overor + por (%esi), %mm0 + .endm + + .macro not + pxor %mm3, %mm0 + .endm + + + + # *************************** CONSTANTS ***************************** + # dropzero dropone + + .macro dropzero + pxor %mm0, %mm0 + .endm + + .macro dropone + pcmpeqw %mm0, %mm0 + .endm + + + # *************************** 4 BIT REG ACCESS ****************************** + # dupsta0 - dupsta4 droplda0 - droplda4 + # store bit in accumulator + + # bit store + + .macro dupsta0 + movq %mm0, %mm4 + .endm + + .macro dupsta1 + movq %mm0, %mm5 + .endm + + .macro dupsta2 + movq %mm0, %mm6 + .endm + + .macro dupsta3 + movq %mm0, %mm7 + .endm + + # load bit from accumulator + + .macro droplda0 + movq %mm4, %mm0 + .endm + + .macro droplda1 + movq %mm5, %mm0 + .endm + + .macro droplda2 + movq %mm6, %mm0 + .endm + + .macro droplda3 + movq %mm7, %mm0 + .endm + + + # *************************** LOAD 4 BIT CONSTANT IN REG ****************************** + # a0000 - a1111 + + .macro ldbit0 value + .ifeq \value + movq %mm1, %mm4 + .else + movq %mm3, %mm4 + .endif + .endm + + .macro ldbit1 value + .ifeq \value + movq %mm1, %mm5 + .else + movq %mm3, %mm5 + .endif + .endm + + .macro ldbit2 value + .ifeq \value + movq %mm1, %mm6 + .else + movq %mm3, %mm6 + .endif + .endm + + .macro ldbit3 value + .ifeq \value + movq %mm1, %mm7 + .else + movq %mm3, %mm7 + .endif + .endm + + .macro ldbin b3 b2 b1 b0 + pxor %mm1, %mm1 + ldbit0 \b0 + ldbit1 \b1 + ldbit2 \b2 + ldbit3 \b3 + .endm + + .macro a0000 + ldbin 0 0 0 0 + .endm + + .macro a0001 + ldbin 0 0 0 1 + .endm + + .macro a0010 + ldbin 0 0 1 0 + .endm + + .macro a0011 + ldbin 0 0 1 1 + .endm + + .macro a0100 + ldbin 0 1 0 0 + .endm + + .macro a0101 + ldbin 0 1 0 1 + .endm + + .macro a0110 + ldbin 0 1 1 0 + .endm + + .macro a0111 + ldbin 0 1 1 1 + .endm + + .macro a1000 + ldbin 1 0 0 0 + .endm + + .macro a1001 + ldbin 1 0 0 1 + .endm + + .macro a1010 + ldbin 1 0 1 0 + .endm + + .macro a1011 + ldbin 1 0 1 1 + .endm + + .macro a1100 + ldbin 1 1 0 0 + .endm + + .macro a1101 + ldbin 1 1 0 1 + .endm + + .macro a1110 + ldbin 1 1 1 0 + .endm + + .macro a1111 + ldbin 1 1 1 1 + .endm + + + + + # *************************** 4 BIT COUNTER ****************************** + # adds TOS to bit of counter and returns carry in TOS + # + # adb0 - adb3 + + + .macro adb0 + movq %mm4, %mm2 + pxor %mm0, %mm4 + pand %mm2, %mm0 + .endm + + .macro adb1 + movq %mm5, %mm2 + pxor %mm0, %mm5 + pand %mm2, %mm0 + .endm + + .macro adb2 + movq %mm6, %mm2 + pxor %mm0, %mm6 + pand %mm2, %mm0 + .endm + + .macro adb3 + movq %mm7, %mm2 + pxor %mm0, %mm7 + pand %mm2, %mm0 + .endm + + + # *************************** ACCUMULATOR TESTS *************************** + # dropisnonzero4 - dropisnonzero1 + + .macro dropisnonzero4 + movq %mm4, %mm0 + por %mm5, %mm0 + por %mm6, %mm0 + por %mm7, %mm0 + .endm + + .macro dropisnonzero3 + movq %mm4, %mm0 + por %mm5, %mm0 + por %mm6, %mm0 + .endm + + .macro dropisnonzero2 + movq %mm4, %mm0 + por %mm5, %mm0 + .endm + + .macro dropisnonzero1 + movq %mm4, %mm0 + .endm + + + # *************************** REGISTER SHIFT OPERATIONS ********************** + # shift and leave shifted out byte on stack + # rotate trough top of stack + + .macro dropshiftright + movq %mm4, %mm0 + movq %mm5, %mm4 + movq %mm6, %mm5 + movq %mm7, %mm6 + pxor %mm7, %mm7 + .endm + + .macro dropshiftleft + movq %mm7, %mm0 + movq %mm6, %mm7 + movq %mm5, %mm6 + movq %mm4, %mm5 + pxor %mm4, %mm4 + .endm + + .macro dropshiftrighta + movq %mm4, %mm0 + movq %mm5, %mm4 + movq %mm6, %mm5 + movq %mm7, %mm6 + .endm + + .macro rotateright + movq %mm4, %mm1 + movq %mm5, %mm4 + movq %mm6, %mm5 + movq %mm7, %mm6 + movq %mm1, %mm7 + .endm + + .macro rotateleft + movq %mm7, %mm1 + movq %mm6, %mm7 + movq %mm5, %mm6 + movq %mm4, %mm5 + movq %mm1, %mm4 + .endm + + .macro rotaterightstack + movq %mm0, %mm1 + movq %mm4, %mm0 + movq %mm5, %mm4 + movq %mm6, %mm5 + movq %mm7, %mm6 + movq %mm1, %mm7 + .endm + + .macro rotateleftstack + movq %mm0, %mm1 + movq %mm7, %mm0 + movq %mm6, %mm7 + movq %mm5, %mm6 + movq %mm4, %mm5 + movq %mm1, %mm4 + .endm + + # *************************** OTHER REGISTER OPERATIONS ********************** + # anot : complement reg (can be used to implement subtraction) + + .macro anot + pxor %mm3, %mm4 + pxor %mm3, %mm5 + pxor %mm3, %mm6 + pxor %mm3, %mm7 + .endm diff --git a/scaf/test/test_pdp_ca.pd b/scaf/test/test_pdp_ca.pd new file mode 100644 index 0000000..1b84312 --- /dev/null +++ b/scaf/test/test_pdp_ca.pd @@ -0,0 +1,132 @@ +#N canvas 650 350 625 557 10; +#X obj 287 82 openpanel; +#X msg 287 56 bang; +#X msg 288 110 open \$1; +#X obj 230 191 pdp_ca; +#X obj 193 76 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 223 80 metro 40; +#X msg 211 56 bang; +#X msg 247 56 stop; +#X obj 146 235 pdp_xv; +#X obj 28 31 t b b b; +#X obj 11 7 loadbang; +#X msg 99 80 rule id; +#X msg 328 155 ca 256 256; +#X msg 256 296 rule shifttopright; +#X msg 331 134 ca 64 64; +#X msg 262 270 rule shiftleft; +#X msg 298 216 random; +#X msg 251 321 rule gameoflife; +#X floatatom 344 52 5 0 0; +#X msg 331 184 ca 1024 1024; +#X floatatom 279 154 5 0 0; +#X msg 357 106 ca 512 512; +#X msg 363 80 ca 320 240; +#X obj 211 120 pdp_v4l; +#X obj 179 99 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 138 53 metro 40; +#X msg 126 29 bang; +#X msg 162 29 stop; +#X floatatom 205 28 5 0 0; +#X msg 243 348 rule w110; +#X msg 245 371 rule w110mod; +#X msg 54 371 rule w110mod2; +#X msg 387 47 ca 640 394; +#X obj 481 289 count; +#X obj 397 248 route 0 1 2 3; +#X floatatom 517 254 5 0 0; +#X msg 466 184 bang; +#X msg 502 184 stop; +#X floatatom 552 171 5 0 0; +#X obj 478 208 metro 1000; +#X msg 258 403 rule golmod; +#X floatatom 34 129 5 0 0; +#X obj 90 207 pdp_bqt; +#X msg 62 107 dim 64 64; +#X msg 44 167 hpf \$1 0.5; +#X msg 272 430 rule golmod2; +#X msg 273 467 rule golmod3; +#X msg 277 494 rule golmod4; +#X msg 280 515 rule golmod5; +#X msg 283 537 rule golmod6; +#X msg 380 403 rule golmod7; +#X obj 46 267 pdp_mix; +#X floatatom 120 297 5 0 0; +#X msg 61 462 type grey; +#X msg 112 407 rule w110mod3; +#X msg 64 490 type yv12; +#X msg 438 471 dim 512 512; +#X obj 270 134 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X msg 463 111 ca 1024 512; +#X msg 105 4 open /home/tom/pd/packet/scaf/modules/carules.scafo; +#X msg 163 468 rule toomuch; +#X msg 165 500 rule underflow; +#X connect 0 0 2 0; +#X connect 1 0 0 0; +#X connect 2 0 3 0; +#X connect 3 0 8 0; +#X connect 4 0 3 0; +#X connect 5 0 3 0; +#X connect 6 0 5 0; +#X connect 7 0 5 0; +#X connect 9 1 11 0; +#X connect 9 2 59 0; +#X connect 10 0 9 0; +#X connect 11 0 3 0; +#X connect 12 0 3 0; +#X connect 13 0 3 0; +#X connect 14 0 3 0; +#X connect 15 0 3 0; +#X connect 16 0 3 0; +#X connect 17 0 3 0; +#X connect 18 0 5 1; +#X connect 19 0 3 0; +#X connect 20 0 3 2; +#X connect 21 0 3 0; +#X connect 22 0 3 0; +#X connect 23 0 51 1; +#X connect 23 0 3 1; +#X connect 24 0 23 0; +#X connect 25 0 24 0; +#X connect 26 0 25 0; +#X connect 27 0 25 0; +#X connect 28 0 25 1; +#X connect 29 0 3 0; +#X connect 30 0 3 0; +#X connect 31 0 3 0; +#X connect 32 0 3 0; +#X connect 33 0 34 0; +#X connect 34 0 30 0; +#X connect 34 1 17 0; +#X connect 34 2 40 0; +#X connect 34 3 17 0; +#X connect 35 0 33 1; +#X connect 36 0 39 0; +#X connect 37 0 39 0; +#X connect 38 0 39 1; +#X connect 39 0 33 0; +#X connect 40 0 3 0; +#X connect 41 0 44 0; +#X connect 42 0 8 0; +#X connect 43 0 23 0; +#X connect 44 0 42 0; +#X connect 45 0 3 0; +#X connect 46 0 3 0; +#X connect 47 0 3 0; +#X connect 48 0 3 0; +#X connect 49 0 3 0; +#X connect 50 0 3 0; +#X connect 51 0 8 0; +#X connect 52 0 51 2; +#X connect 53 0 23 0; +#X connect 54 0 3 0; +#X connect 55 0 23 0; +#X connect 56 0 8 0; +#X connect 57 0 20 0; +#X connect 58 0 3 0; +#X connect 59 0 3 0; +#X connect 60 0 3 0; +#X connect 61 0 3 0; diff --git a/scaf/test/test_pdp_ca2.pd b/scaf/test/test_pdp_ca2.pd new file mode 100644 index 0000000..8af44a8 --- /dev/null +++ b/scaf/test/test_pdp_ca2.pd @@ -0,0 +1,154 @@ +#N canvas 454 152 625 557 10; +#X obj 325 83 openpanel; +#X msg 325 57 bang; +#X msg 326 111 open \$1; +#X obj 428 234 pdp_ca; +#X obj 256 149 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 223 80 metro 40; +#X msg 211 56 bang; +#X msg 247 56 stop; +#X obj 402 407 pdp_xv; +#X obj 28 31 t b b b; +#X obj 11 7 loadbang; +#X msg 391 99 ca 256 256; +#X msg 394 78 ca 64 64; +#X msg 298 216 random; +#X msg 251 321 rule gameoflife; +#X floatatom 490 187 5 0 0; +#X msg 420 50 ca 512 512; +#X obj 107 188 pdp_v4l; +#X obj 97 127 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X msg 243 348 rule w110; +#X msg 245 371 rule w110mod; +#X msg 258 403 rule golmod; +#X msg 272 430 rule golmod2; +#X msg 273 467 rule golmod3; +#X msg 277 494 rule golmod4; +#X msg 280 515 rule golmod5; +#X msg 283 537 rule golmod6; +#X obj 481 167 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X msg 526 55 ca 1024 512; +#X msg 105 4 open /home/tom/pd/packet/scaf/modules/carules.scafo; +#X obj 426 294 pdp_ca2image; +#X obj 104 228 pdp_image2ca; +#X obj 136 155 metro 40; +#X msg 124 131 bang; +#X msg 160 131 stop; +#X floatatom 510 273 5 0 0; +#X obj 501 253 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 +1; +#X msg 468 416 rule gameoflife; +#X msg 450 455 rule golmod6; +#X msg 380 440 rule golmod3; +#X msg 159 287 rules; +#X floatatom 27 213 5 0 0; +#X msg 40 319 ruleindex \$1; +#X obj 31 254 t b f; +#X msg 52 78 rule gameoflife; +#X floatatom 193 198 5 0 0; +#X msg 504 143 close; +#X msg 149 448 rule test1; +#X obj 181 178 hsl 128 15 0 1 0 0 empty empty empty -2 -6 0 8 -262144 +-1 -1 0 1; +#X msg 5 129 dim 640 480; +#X obj 156 51 pdp_qt; +#X msg 174 23 open /home/ben/MOV/test1.mov; +#X floatatom 93 31 5 0 0; +#X msg 65 56 autoplay 1; +#X obj 144 25 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X msg 22 94 loop 1; +#X obj 439 483 osc~ 150; +#X obj 439 508 *~ 0.1; +#X obj 437 536 dac~; +#X floatatom 282 57 5 0 0; +#X msg 503 113 ca 32 32; +#X obj 428 322 pdp_motion_blur; +#X obj 488 349 pdp_gradient; +#X obj 425 382 pdp_mix; +#X floatatom 508 380 5 0 0; +#X floatatom 204 128 5 0 0; +#X obj 39 399 pdp_control; +#X obj 83 476 pdp_control; +#X obj 84 519 print two; +#X obj 39 438 print one; +#X obj 275 149 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 265 128 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X msg 38 374 thread \$1; +#X obj 32 349 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X connect 0 0 2 0; +#X connect 1 0 0 0; +#X connect 2 0 3 0; +#X connect 3 0 30 0; +#X connect 4 0 3 0; +#X connect 5 0 3 0; +#X connect 6 0 5 0; +#X connect 7 0 5 0; +#X connect 9 1 44 0; +#X connect 9 2 29 0; +#X connect 10 0 9 0; +#X connect 11 0 3 0; +#X connect 12 0 3 0; +#X connect 13 0 3 0; +#X connect 14 0 3 0; +#X connect 15 0 3 2; +#X connect 16 0 3 0; +#X connect 17 0 31 0; +#X connect 17 0 63 1; +#X connect 18 0 17 0; +#X connect 19 0 3 0; +#X connect 20 0 3 0; +#X connect 21 0 3 0; +#X connect 22 0 3 0; +#X connect 23 0 3 0; +#X connect 24 0 3 0; +#X connect 25 0 3 0; +#X connect 26 0 3 0; +#X connect 27 0 15 0; +#X connect 28 0 3 0; +#X connect 29 0 3 0; +#X connect 30 0 62 0; +#X connect 31 0 3 0; +#X connect 32 0 17 0; +#X connect 33 0 32 0; +#X connect 34 0 32 0; +#X connect 36 0 35 0; +#X connect 40 0 3 0; +#X connect 41 0 43 0; +#X connect 42 0 3 0; +#X connect 43 0 13 0; +#X connect 43 1 42 0; +#X connect 44 0 3 0; +#X connect 45 0 31 1; +#X connect 46 0 3 0; +#X connect 47 0 3 0; +#X connect 48 0 45 0; +#X connect 49 0 17 0; +#X connect 50 0 31 0; +#X connect 51 0 50 0; +#X connect 52 0 50 0; +#X connect 53 0 50 0; +#X connect 54 0 50 0; +#X connect 55 0 50 0; +#X connect 56 0 57 0; +#X connect 57 0 58 0; +#X connect 57 0 58 1; +#X connect 59 0 5 1; +#X connect 60 0 3 0; +#X connect 62 0 63 0; +#X connect 63 0 8 0; +#X connect 64 0 63 2; +#X connect 65 0 32 1; +#X connect 66 0 69 0; +#X connect 67 0 68 0; +#X connect 70 0 3 0; +#X connect 71 0 4 0; +#X connect 71 0 70 0; +#X connect 72 0 66 0; +#X connect 73 0 72 0; |