Canola  0.8.D001
lib/calculator.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/math.h>
00019 #include <lib/ac/stdio.h>
00020 #include <lib/ac/string.h>
00021 #include <libexplain/output.h>
00022 
00023 #include <lib/calculator.h>
00024 #include <lib/interpose/learn.h>
00025 #include <lib/interpose/ope.h>
00026 #include <lib/interpose/ope_che.h>
00027 #include <lib/interpose/pro_che.h>
00028 #include <lib/interpose/stateful.h>
00029 
00030 
00031 calculator::~calculator()
00032 {
00033 }
00034 
00035 
00036 calculator::calculator() :
00037     mstate(mstate_reset),
00038     x_register(),
00039     y_register(),
00040     program_selector(program_selector_i_ii),
00041     address(0),
00042     program_mode(program_mode_operation),
00043     deferred_action(deferred_action_none),
00044     clock_tick_mode(clock_tick_mode_disabled),
00045     printer_delay_active(false),
00046     accumulate_mode(false),
00047     constant_mode(false),
00048     constant_mode_action(deferred_action_none),
00049     strict_memory_number_checking(true),
00050     number_of_keys_since_ej(0)
00051 {
00052     interpose_by_program_mode[program_mode_learn] =
00053         interpose_stateful::create(interpose_learn::create(this));
00054     interpose_by_program_mode[program_mode_operation] =
00055         interpose_stateful::create(interpose_ope::create(this), true);
00056     interpose_by_program_mode[program_mode_program_check] =
00057         interpose_stateful::create(interpose_pro_che::create(this));
00058     interpose_by_program_mode[program_mode_operation_check] =
00059         interpose_stateful::create(interpose_ope_che::create(this), true);
00060 
00061     memset(programme, 0, sizeof(programme));
00062 }
00063 
00064 
00065 const char *
00066 calculator::mstate_name(mstate_t ms)
00067 {
00068     switch (ms)
00069     {
00070     case mstate_reset:
00071         return "mstate_reset";
00072 
00073     case mstate_lhs_digits_before_dot:
00074         return "mstate_lhs_digits_before_dot";
00075 
00076     case mstate_lhs_digits_after_dot:
00077         return "mstate_lhs_digits_after_dot";
00078 
00079     case mstate_lhs_complete:
00080         return "mstate_lhs_complete";
00081 
00082     case mstate_mul:
00083         return "mstate_mul";
00084 
00085     case mstate_mul_digits_before_dot:
00086         return "mstate_mul_digits_before_dot";
00087 
00088     case mstate_mul_digits_after_dot:
00089         return "mstate_mul_digits_after_dot";
00090 
00091     case mstate_mul_complete:
00092         return "mstate_mul_complete";
00093 
00094     default:
00095         return "???";
00096     }
00097 }
00098 
00099 
00100 const char *
00101 calculator::program_mode_name(program_mode_t os)
00102 {
00103     switch (os)
00104     {
00105     case program_mode_learn:
00106         return "LRN";
00107 
00108     case program_mode_operation:
00109         return "OPE";
00110 
00111     case program_mode_program_check:
00112         return "PRO CHE";
00113 
00114     case program_mode_operation_check:
00115         return "OPE CHE";
00116 
00117     default:
00118         return "unknown";
00119     }
00120 }
00121 
00122 
00123 calculator::program_selector_t
00124 calculator::program_selector_get(void)
00125     const
00126 {
00127     return program_selector;
00128 }
00129 
00130 
00131 void
00132 calculator::program_selector_set(program_selector_t ps)
00133 {
00134     program_selector = ps;
00135     switch (program_selector)
00136     {
00137     default:
00138     case program_selector_i_ii:
00139         break;
00140 
00141     case program_selector_i:
00142         // <question>
00143         // In LRN mode, with the Program Selector Switch is set to "I+II",
00144         // if the display address is more than 121, what happends if the
00145         // Program Selector Switch is set to "I"?  Does the address change?
00146         // If so, to what?
00147         // </question>
00148         if (address > 120)
00149             address = 0;
00150         break;
00151 
00152     case program_selector_ii:
00153         // <question>
00154         // In LRN mode, with the Program Selector Switch is set to "I+II",
00155         // if the display address is less than 120, what happends if the
00156         // Program Selector Switch is set to "II"?  Does the address change?
00157         // If so, to what?
00158         // </question>
00159         if (address < 121)
00160             address = 0;
00161         break;
00162     }
00163     update_display();
00164 }
00165 
00166 
00167 void
00168 calculator::on_error(const char *fmt, ...)
00169 {
00170     va_list ap;
00171     va_start(ap, fmt);
00172     on_error_v(fmt, ap);
00173     va_end(ap);
00174 }
00175 
00176 
00177 void
00178 calculator::program_mode_set(program_mode_t pm)
00179 {
00180     if (program_mode == pm)
00181         return;
00182     program_mode = pm;
00183     switch (program_mode)
00184     {
00185     case program_mode_learn:
00186         break;
00187 
00188     case program_mode_operation:
00189         // See the Instruction Manual, p. 45
00190         address = 0;
00191         x_register = 0;
00192         y_register = 0;
00193         mstate = mstate_reset;
00194         break;
00195 
00196     case program_mode_program_check:
00197         break;
00198 
00199     case program_mode_operation_check:
00200         address = 0;
00201         x_register = 0;
00202         y_register = 0;
00203         mstate = mstate_reset;
00204         break;
00205 
00206     default:
00207         assert(!"this is strange");
00208         break;
00209     }
00210     update_display();
00211 }
00212 
00213 
00214 calculator::program_mode_t
00215 calculator::program_mode_get(void)
00216 {
00217     return program_mode;
00218 }
00219 
00220 
00221 void
00222 calculator::on_opcode(opcode_t op, const location::pointer &where)
00223 {
00224     assert(interpose_by_program_mode[program_mode]);
00225     interpose_by_program_mode[program_mode]->on_opcode(op, where);
00226     update_display();
00227 }
00228 
00229 
00230 unsigned
00231 calculator::wrap_address(unsigned addr)
00232     const
00233 {
00234     switch (program_selector)
00235     {
00236     case program_selector_i_ii:
00237     default:
00238         if (addr < 1 || addr > 240)
00239             return 1;
00240         break;
00241 
00242     case program_selector_i:
00243         if (addr < 1 || addr > 120)
00244             return 1;
00245         break;
00246 
00247     case program_selector_ii:
00248         if (addr < 121 || addr > 240)
00249             return 121;
00250         break;
00251     }
00252     return addr;
00253 }
00254 
00255 
00256 unsigned char
00257 calculator::next_opcode(void)
00258 {
00259     if (!address)
00260         address = wrap_address(address);
00261     // FIXME: skip NUL bytes, but avoid infinite loop when programme is empty.
00262     unsigned char op = programme[address];
00263     address = wrap_address(address + 1);
00264     return op;
00265 }
00266 
00267 
00268 location::pointer
00269 calculator::next_location(void)
00270     const
00271 {
00272     int addr = address;
00273     if (!addr)
00274         addr = wrap_address(addr);
00275     return programme_location[addr];
00276 }
00277 
00278 
00279 unsigned
00280 calculator::find_flag_jump(unsigned char label)
00281 {
00282     // Question: If executing in bank II, does UJ find FJ in bank I?
00283     //
00284     // The Instruction Manual seems to say this, p. 54:
00285     //
00286     //     "Consequently, where two programs A and B are simultaneously
00287     //     stored, care should be exercised to avoid using a receiver
00288     //     (Flag for Jump) identical to that of program A stored in I
00289     //     position, when makeing program B which is to be stored in II
00290     //     position."
00291     //
00292     // FIXME: The physical calculator really did look for labels this
00293     // way, but it had a slow clock speed for this.  We should think
00294     // about emulating that delay.
00295     //
00296     unsigned lo = 1;
00297     unsigned hi = 240;
00298 
00299     //
00300     // Instruction Manual, p. 54, says:
00301     //
00302     //     "A jump is accomplished to the smallest step as viewed from
00303     //     the 1st step regardless of the linear sequence of steps or
00304     //     specifications such as the Program Selector Switch."
00305     //
00306     // Which makes this code a bit suspect, because it DOES honor the
00307     // Program Selector Switch.
00308     //
00309     // Question: does UJ ignore the Program Select Switch when looking for FJ?
00310     //
00311     switch (program_selector)
00312     {
00313     case program_selector_i_ii:
00314     default:
00315         break;
00316 
00317     case program_selector_i:
00318         hi = 120;
00319         break;
00320 
00321     case program_selector_ii:
00322         lo = 121;
00323         break;
00324     }
00325 
00326     //
00327     // <question>
00328     // When searching for a FJ label, how many steps can it scan per
00329     // second?  The Instruction Manual says distant jumps are slower,
00330     // but how much slower?
00331     // </question>
00332     //
00333     for (unsigned n = lo; n <= hi; ++n)
00334     {
00335         if (programme[n] == opcode_fj && programme[n + 1] == label)
00336         {
00337             // Instruction Manual, p. 54, use the first flag you find.
00338             return (n + 2);
00339         }
00340     }
00341 
00342     //
00343     // It does no exist.  The Instruction Manual, p.53, says to treat
00344     // this as a jump to the first instruction.
00345     //
00346     // We will issue a warning, just to be friendly-like.
00347     //
00348     on_error
00349     (
00350         "flag jump %s not found, destination address %d used by default",
00351         opcode_raw_name((opcode_t)label).c_str(),
00352         lo
00353     );
00354     return lo;
00355 }
00356 
00357 
00358 unsigned
00359 calculator::find_subroutine_flag_jump(unsigned char label)
00360 {
00361     // FIXME: The physical calculator really did look for labels this
00362     // way, but it had a slow clock speed for this.  We should think
00363     // about emulating that delay.
00364     unsigned lo = 1;
00365     unsigned hi = 240;
00366     switch (program_selector)
00367     {
00368     case program_selector_i_ii:
00369     default:
00370         break;
00371 
00372     case program_selector_i:
00373         hi = 120;
00374         break;
00375 
00376     case program_selector_ii:
00377         lo = 121;
00378         break;
00379     }
00380     for (unsigned n = lo; n <= hi; ++n)
00381     {
00382         if (programme[n] == opcode_sfj && programme[n + 1] == label)
00383         {
00384             return (n + 2);
00385         }
00386     }
00387 
00388     //
00389     // It does no exist.  The manual (somewhere) says to treat this as a
00390     // jump to the first instruction.
00391     //
00392     // We will issue a warning, just to be friendly-like.
00393     //
00394     on_error
00395     (
00396         "subroutine flag jump %s not found, destination address %d used "
00397             "by default",
00398         opcode_raw_name((opcode_t)label).c_str(),
00399         lo
00400     );
00401     return lo;
00402 }
00403 
00404 
00405 void
00406 calculator::clock_tick_event(void)
00407 {
00408     //
00409     // If the printer has yet to finish printing the last line it was
00410     // given to print, we can't proceed.
00411     //
00412     if (printer_delay_active)
00413         return;
00414 
00415     switch (program_mode)
00416     {
00417     case program_mode_learn:
00418         // ignore
00419         clock_tick_mode = clock_tick_mode_disabled;
00420         break;
00421 
00422     case program_mode_operation:
00423         switch (clock_tick_mode)
00424         {
00425         case clock_tick_mode_disabled:
00426             break;
00427 
00428         case clock_tick_mode_running:
00429             execute_one_instruction();
00430             break;
00431 
00432         case clock_tick_mode_entry:
00433         case clock_tick_mode_sj:
00434         case clock_tick_mode_ej:
00435         default:
00436             break;
00437         }
00438         update_display();
00439         break;
00440 
00441     case program_mode_program_check:
00442     case program_mode_operation_check:
00443     default:
00444         clock_tick_mode = clock_tick_mode_disabled;
00445         break;
00446     }
00447 }
00448 
00449 
00450 bool
00451 calculator::clock_tick_expected(void)
00452     const
00453 {
00454     return (clock_tick_mode == clock_tick_mode_running);
00455 }
00456 
00457 
00458 void
00459 calculator::precision_and_rounding_set(const precision_and_rounding &value)
00460 {
00461     precro_front_panel = value;
00462     precro_current = value;
00463 }
00464 
00465 
00466 const precision_and_rounding &
00467 calculator::precision_and_rounding_get(void)
00468     const
00469 {
00470     return precro_front_panel;
00471 }
00472 
00473 
00474 bool
00475 calculator::printer_finished_event_expected(void)
00476     const
00477 {
00478     return printer_delay_active;
00479 }
00480 
00481 
00482 void
00483 calculator::printer_finished_event(void)
00484 {
00485     printer_delay_active = false;
00486 }
00487 
00488 
00489 bool
00490 calculator::accumulate_mode_get(void)
00491     const
00492 {
00493     return accumulate_mode;
00494 }
00495 
00496 
00497 void
00498 calculator::accumulate_mode_set(bool value)
00499 {
00500     accumulate_mode = value;
00501 }
00502 
00503 
00504 void
00505 calculator::constant_mode_set(bool value)
00506 {
00507     constant_mode = value;
00508 }
00509 
00510 
00511 static std::string
00512 downcase(const std::string &text)
00513 {
00514     std::string result;
00515     const char *s = text.c_str();
00516     for (;;)
00517     {
00518         unsigned char c = *s++;
00519         if (!c)
00520             return result;
00521         if (isupper(c))
00522             c = tolower(c);
00523         result += c;
00524     }
00525 }
00526 
00527 
00528 void
00529 calculator::property(const std::string &name, const std::string &value)
00530 {
00531     properties.insert(properties_t::value_type(downcase(name), value));
00532 }
00533 
00534 
00535 bool
00536 calculator::strict_memory_number_checking_required(void)
00537     const
00538 {
00539     return strict_memory_number_checking;
00540 }
00541 
00542 
00543 void
00544 calculator::strict_memory_number_checking_set(bool value)
00545 {
00546     strict_memory_number_checking = value;
00547 }
00548 
00549 
00550 // vim: set ts=8 sw=4 et :