Canola  0.8.D001
lib/calculator/save_program_as.cc
Go to the documentation of this file.
00001 //
00002 // canola - canon canola 1614p emulator
00003 // Copyright (C) 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License, version 3, as
00007 // published by the Free Software Foundation.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program. If not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include <lib/ac/assert.h>
00019 #include <lib/ac/fcntl.h>
00020 #include <lib/ac/stdio.h>
00021 #include <lib/ac/unistd.h>
00022 #include <libexplain/close.h>
00023 #include <libexplain/creat.h>
00024 #include <libexplain/fclose.h>
00025 #include <libexplain/fflush.h>
00026 #include <libexplain/fopen.h>
00027 #include <libexplain/write.h>
00028 #include <map>
00029 
00030 #include <lib/calculator.h>
00031 #include <lib/sizeof.h>
00032 
00033 
00034 typedef std::map<int, const char *> system_subroutines_t;
00035 static system_subroutines_t system_subroutines;
00036 
00037 
00038 static void
00039 setup_system_subroutines(void)
00040 {
00041     if (!system_subroutines.empty())
00042         return;
00043 
00044     struct table_t
00045     {
00046         const char *name;
00047         int number;
00048     };
00049 
00050     // Instruction Manual, p. 56
00051     static const table_t table[] =
00052     {
00053         { "sin(x)", 0x21 },
00054         { "cos(x)", 0x22 },
00055         { "tan(x)", 0x23 },
00056         { "asin(x)", 0x24 },
00057         { "acos(x)", 0x25 },
00058         { "atan(x)", 0x26 },
00059         { "a**n", 0x27 },
00060         { "a**x", 0x28 },
00061         { "10**n", 0x29 },
00062         { "log10(x)", 0x2A },
00063         { "exp(x)", 0x2B },
00064         { "ln(x)", 0x2C },
00065         { "sinh(x)", 0x2D },
00066         { "cosh(x)", 0x2E },
00067         { "tanh(x)", 0x31 },
00068         { "asinh(x)", 0x32 },
00069         { "acosh(x)", 0x33 },
00070         { "atanh(x)", 0x34 },
00071         { "polar_to_rectangular", 0x35 },
00072         { "rectangular_to_polar", 0x36 },
00073         { "dms_to_degree", 0x37 },
00074         { "degree_to_dms", 0x38 },
00075         { "degree_to_radian", 0x39 },
00076         { "radian_to_degree", 0x3A },
00077         { "factorial(n)", 0x3B },
00078     };
00079 
00080     for (const table_t *tp = table; tp < ENDOF(table); ++tp)
00081         system_subroutines[tp->number] = tp->name;
00082 }
00083 
00084 
00085 void
00086 calculator::save_program_as(const std::string &filename, bool binary)
00087 {
00088     unsigned char *prog_data = programme + 1;
00089     size_t prog_size = 240;
00090     switch (program_selector)
00091     {
00092     case program_selector_i_ii:
00093     default:
00094         break;
00095 
00096     case program_selector_i:
00097         prog_size = 120;
00098         break;
00099 
00100     case program_selector_ii:
00101         prog_data = programme + 121;
00102         prog_size = 120;
00103         break;
00104     }
00105     assert(prog_data >= programme);
00106     assert(prog_data < programme + sizeof(programme));
00107     assert(prog_data + prog_size >= programme);
00108     assert(prog_data + prog_size <= programme + sizeof(programme));
00109 
00110     if (binary)
00111     {
00112         const int mode = 0666;
00113         int fd = creat(filename.c_str(), mode);
00114         if (fd < 0)
00115         {
00116             on_error("%s", explain_creat(filename.c_str(), mode));
00117             return;
00118         }
00119         if (write(fd, prog_data, prog_size) < 0)
00120         {
00121             on_error("%s", explain_write(fd, prog_data, prog_size));
00122             close(fd);
00123             return;
00124         }
00125         if (close(fd) < 0)
00126         {
00127             on_error("%s", explain_close(fd));
00128             return;
00129         }
00130     }
00131     else
00132     {
00133         FILE *fp = fopen(filename.c_str(), "w");
00134         if (!fp)
00135         {
00136             on_error("%s", explain_fopen(filename.c_str(), "w"));
00137             return;
00138         }
00139 
00140         // text mode requires a little more work.
00141         const unsigned char *p = prog_data;
00142         const unsigned char *prog_end = prog_data + prog_size;
00143         while (p < prog_end && prog_end[-1] == 0)
00144             --prog_end;
00145         while (p < prog_end)
00146         {
00147             unsigned char op = *p++;
00148             switch (op)
00149             {
00150             case 0:
00151             case 0xFF:
00152                 break;
00153 
00154             case opcode_dot:
00155             case opcode_n0:
00156             case opcode_n1:
00157             case opcode_n2:
00158             case opcode_n3:
00159             case opcode_n4:
00160             case opcode_n5:
00161             case opcode_n6:
00162             case opcode_n7:
00163             case opcode_n8:
00164             case opcode_n9:
00165                 fputs("    ", fp);
00166                 for (;;)
00167                 {
00168                     fprintf(fp, "%s", opcode_name((opcode_t)op).c_str());
00169                     if (p >= prog_end)
00170                         break;
00171                     op = *p;
00172                     switch (op)
00173                     {
00174                     case opcode_dot:
00175                     case opcode_n0:
00176                     case opcode_n1:
00177                     case opcode_n2:
00178                     case opcode_n3:
00179                     case opcode_n4:
00180                     case opcode_n5:
00181                     case opcode_n6:
00182                     case opcode_n7:
00183                     case opcode_n8:
00184                     case opcode_n9:
00185                         ++p;
00186                         continue;
00187 
00188                     default:
00189                         break;
00190                     }
00191                     break;
00192                 }
00193                 fputc('\n', fp);
00194                 break;
00195 
00196             case opcode_sj:
00197             case opcode_ej:
00198             case opcode_mj:
00199             case opcode_uj:
00200             case opcode_suj:
00201                 fputs("    ", fp);
00202                 // Fall through...
00203 
00204             case opcode_fj:
00205             case opcode_sfj:
00206                 fputs(opcode_name((opcode_t)op).c_str(), fp);
00207                 if (p < prog_end)
00208                 {
00209                     fputc(' ', fp);
00210                     int op2 = *p++;
00211                     if (op2 < 0x70)
00212                         fputs(opcode_raw_name((opcode_t)op2).c_str(), fp);
00213                     else
00214                         fputs(opcode_name((opcode_t)op2).c_str(), fp);
00215 
00216                     if (op == opcode_suj)
00217                     {
00218                         //
00219                         // Let's assume users don't re-use library
00220                         // subroutine labels, and thus include the names of
00221                         // library subroutines.
00222                         //
00223                         setup_system_subroutines();
00224                         system_subroutines_t::const_iterator it =
00225                             system_subroutines.find((int)op2);
00226                         if (it != system_subroutines.end())
00227                             fprintf(fp, " ; %s", it->second);
00228                     }
00229                 }
00230                 fputc('\n', fp);
00231                 break;
00232 
00233             case opcode_round_up:
00234             case opcode_round_off:
00235             case opcode_round_down:
00236                 fputs("    ", fp);
00237                 fputs(opcode_name((opcode_t)op).c_str(), fp);
00238                 if (p < prog_end)
00239                 {
00240                     fputc(' ', fp);
00241                     op = *p++;
00242                     fputs(opcode_name((opcode_t)op).c_str(), fp);
00243                 }
00244                 fputc('\n', fp);
00245                 break;
00246 
00247             default:
00248                 fputs("    ", fp);
00249                 fputs(opcode_name((opcode_t)op).c_str(), fp);
00250                 fputc('\n', fp);
00251                 break;
00252             }
00253         }
00254         if (fflush(fp))
00255         {
00256             on_error("%s", explain_fflush(fp));
00257             fclose(fp);
00258             return;
00259         }
00260         if (fclose(fp))
00261         {
00262             on_error("%s", explain_fclose(fp));
00263             return;
00264         }
00265     }
00266 }