diff --git a/applications/plugins/tama_p1/tamalib/LICENSE b/applications/plugins/tama_p1/tamalib/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/applications/plugins/tama_p1/tamalib/README.md b/applications/plugins/tama_p1/tamalib/README.md new file mode 100644 index 000000000..add42da1a --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/README.md @@ -0,0 +1,64 @@ +# TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + + +## Synopsis + +TamaLib is a hardware agnostic Tamagotchi P1 emulation library built from scratch. It is self-contained and aims at running on any platform powerful enough, from microcontrollers (MCUs) to desktop computers, thus spreading virtual life across the digital world. + +So far, it has been successfully implemented on different platforms: +- Desktop computers (check out [TamaTool](https://github.com/jcrona/tamatool/) for more information) +- STM32F072 MCU based board (check out [MCUGotchi](https://github.com/jcrona/mcugotchi/) for more information). +- OpenTama which is an STM32L072 MCU based board (check out [OpenTama](https://github.com/Sparkr-tech/opentama) and [MCUGotchi](https://github.com/jcrona/mcugotchi/) for more information). +- Arduino UNO (check out [ArduinoGotchi](https://github.com/GaryZ88/ArduinoGotchi/) for more information). + +## Importing TamaLIB + +TamaLIB cannot be used as is. In order to create life on a specific target, you need to import all TamaLIB related __.c__ and __.h__ files in your project (for instance in a __lib__ subfolder), to create a __hal_types.h__ file using the template provided and to implement the __hal_t__ structure, that will act as an abstraction layer between TamaLIB and your OS or SDK (detailed information can be found in __hal.h__). This abstraction layer basically connects TamaLIB to your target's buttons, clock, audio and screen, while also defining the C types that TamaLIB should use to represent 4-bit, 5-bit, 8-bit, 12-bit, 13-bit and 32-bit variables. Once done, you will be able to call the TamaLIB API from your project. + + +## Using the TamaLIB API + +Basically: +``` +/* ... */ + +/* Register the HAL */ +tamalib_register_hal(&my_hal); + +/* ... */ + +/* Initialize TamaLIB */ +tamalib_init(my_program, my_breakpoints, 1000000); // my_breakpoints can be NULL, 1000000 means that timestamps will be expressed in us + +/* ... */ + +/* Enter TamaLIB's loop */ +tamalib_mainloop(); + +/* ... */ + +/* Release TamaLIB */ +tamalib_release(); + +/* ... */ +``` +Your main project should then forward any button input to TamaLIB using the `tamalib_set_button()` function. + +As an alternative to `tamalib_mainloop()`, you can call `tamalib_step()` directly if your execution flow requires something more complex than a simple mainloop. In that case, TamaLIB will neither call the HAL `handler()` function, nor the HAL `update_screen()` function by itslef. + + +## License + +TamaLIB is distributed under the GPLv2 license. See the LICENSE file for more information. + + +## Hardware information + +The Tamagotchi P1 is based on an +[E0C6S46 Epson MCU](https://download.epson-europe.com/pub/electronics-de/asmic/4bit/62family/technicalmanual/tm_6s46.pdf), +and runs at 32,768 kHz. Its LCD is 32x16 B/W pixels, with 8 icons. +To my knowledge, the ROM available online has been extracted from a high-res picture of a die. The ROM mask was clear enough to be optically read. The pictures can be seen [there](https://siliconpr0n.org/map/bandai/tamagotchi-v1/) (thx asterick for the link !). +I would love to see the same work done on a P2 and add support for it in TamaLIB/TamaTool ! + +__ +Copyright (C) 2021 Jean-Christophe Rona diff --git a/applications/plugins/tama_p1/tamalib/cpu.c b/applications/plugins/tama_p1/tamalib/cpu.c new file mode 100644 index 000000000..166580230 --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/cpu.c @@ -0,0 +1,1789 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "cpu.h" +#include "hw.h" +#include "hal.h" + +#define TICK_FREQUENCY 32768 // Hz + +#define TIMER_1HZ_PERIOD 32768 // in ticks +#define TIMER_256HZ_PERIOD 128 // in ticks + +#define MASK_4B 0xF00 +#define MASK_6B 0xFC0 +#define MASK_7B 0xFE0 +#define MASK_8B 0xFF0 +#define MASK_10B 0xFFC +#define MASK_12B 0xFFF + +#define PCS (pc & 0xFF) +#define PCSL (pc & 0xF) +#define PCSH ((pc >> 4) & 0xF) +#define PCP ((pc >> 8) & 0xF) +#define PCB ((pc >> 12) & 0x1) +#define TO_PC(bank, page, step) ((step & 0xFF) | ((page & 0xF) << 8) | (bank & 0x1) << 12) +#define NBP ((np >> 4) & 0x1) +#define NPP (np & 0xF) +#define TO_NP(bank, page) ((page & 0xF) | (bank & 0x1) << 4) +#define XHL (x & 0xFF) +#define XL (x & 0xF) +#define XH ((x >> 4) & 0xF) +#define XP ((x >> 8) & 0xF) +#define YHL (y & 0xFF) +#define YL (y & 0xF) +#define YH ((y >> 4) & 0xF) +#define YP ((y >> 8) & 0xF) +#define M(n) get_memory(n) +#define SET_M(n, v) set_memory(n, v) +#define RQ(i) get_rq(i) +#define SET_RQ(i, v) set_rq(i, v) +#define SPL (sp & 0xF) +#define SPH ((sp >> 4) & 0xF) + +#define FLAG_C (0x1 << 0) +#define FLAG_Z (0x1 << 1) +#define FLAG_D (0x1 << 2) +#define FLAG_I (0x1 << 3) + +#define C !!(flags & FLAG_C) +#define Z !!(flags & FLAG_Z) +#define D !!(flags & FLAG_D) +#define I !!(flags & FLAG_I) + +#define SET_C() {flags |= FLAG_C;} +#define CLEAR_C() {flags &= ~FLAG_C;} +#define SET_Z() {flags |= FLAG_Z;} +#define CLEAR_Z() {flags &= ~FLAG_Z;} +#define SET_D() {flags |= FLAG_D;} +#define CLEAR_D() {flags &= ~FLAG_D;} +#define SET_I() {flags |= FLAG_I;} +#define CLEAR_I() {flags &= ~FLAG_I;} + +#define REG_CLK_INT_FACTOR_FLAGS 0xF00 +#define REG_SW_INT_FACTOR_FLAGS 0xF01 +#define REG_PROG_INT_FACTOR_FLAGS 0xF02 +#define REG_SERIAL_INT_FACTOR_FLAGS 0xF03 +#define REG_K00_K03_INT_FACTOR_FLAGS 0xF04 +#define REG_K10_K13_INT_FACTOR_FLAGS 0xF05 +#define REG_CLOCK_INT_MASKS 0xF10 +#define REG_SW_INT_MASKS 0xF11 +#define REG_PROG_INT_MASKS 0xF12 +#define REG_SERIAL_INT_MASKS 0xF13 +#define REG_K00_K03_INT_MASKS 0xF14 +#define REG_K10_K13_INT_MASKS 0xF15 +#define REG_PROG_TIMER_DATA_L 0xF24 +#define REG_PROG_TIMER_DATA_H 0xF25 +#define REG_PROG_TIMER_RELOAD_DATA_L 0xF26 +#define REG_PROG_TIMER_RELOAD_DATA_H 0xF27 +#define REG_K00_K03_INPUT_PORT 0xF40 +#define REG_K10_K13_INPUT_PORT 0xF42 +#define REG_K40_K43_BZ_OUTPUT_PORT 0xF54 +#define REG_CPU_OSC3_CTRL 0xF70 +#define REG_LCD_CTRL 0xF71 +#define REG_LCD_CONTRAST 0xF72 +#define REG_SVD_CTRL 0xF73 +#define REG_BUZZER_CTRL1 0xF74 +#define REG_BUZZER_CTRL2 0xF75 +#define REG_CLK_WD_TIMER_CTRL 0xF76 +#define REG_SW_TIMER_CTRL 0xF77 +#define REG_PROG_TIMER_CTRL 0xF78 +#define REG_PROG_TIMER_CLK_SEL 0xF79 + +#define INPUT_PORT_NUM 2 + +typedef struct { + char *log; + u12_t code; + u12_t mask; + u12_t shift_arg0; + u12_t mask_arg0; // != 0 only if there are two arguments + u8_t cycles; + void (*cb)(u8_t arg0, u8_t arg1); +} op_t; + +typedef struct { + u4_t states; +} input_port_t; + +/* Registers */ +static u13_t pc, next_pc; +static u12_t x, y; +static u4_t a, b; +static u5_t np; +static u8_t sp; + +/* Flags */ +static u4_t flags; + +static const u12_t *g_program = NULL; +static MEM_BUFFER_TYPE memory[MEM_BUFFER_SIZE]; + +static input_port_t inputs[INPUT_PORT_NUM] = {{0}}; + +/* Interrupts (in priority order) */ +static interrupt_t interrupts[INT_SLOT_NUM] = { + {0x0, 0x0, 0, 0x0C}, // Prog timer + {0x0, 0x0, 0, 0x0A}, // Serial interface + {0x0, 0x0, 0, 0x08}, // Input (K10-K13) + {0x0, 0x0, 0, 0x06}, // Input (K00-K03) + {0x0, 0x0, 0, 0x04}, // Stopwatch timer + {0x0, 0x0, 0, 0x02}, // Clock timer +}; + +static breakpoint_t *g_breakpoints = NULL; + +static u32_t call_depth = 0; + +static u32_t clk_timer_timestamp = 0; // in ticks +static u32_t prog_timer_timestamp = 0; // in ticks +static bool_t prog_timer_enabled = 0; +static u8_t prog_timer_data = 0; +static u8_t prog_timer_rld = 0; + +static u32_t tick_counter = 0; +static u32_t ts_freq; +static u8_t speed_ratio = 1; +static timestamp_t ref_ts; + +static state_t cpu_state = { + .pc = &pc, + .x = &x, + .y = &y, + .a = &a, + .b = &b, + .np = &np, + .sp = &sp, + .flags = &flags, + + .tick_counter = &tick_counter, + .clk_timer_timestamp = &clk_timer_timestamp, + .prog_timer_timestamp = &prog_timer_timestamp, + .prog_timer_enabled = &prog_timer_enabled, + .prog_timer_data = &prog_timer_data, + .prog_timer_rld = &prog_timer_rld, + + .call_depth = &call_depth, + + .interrupts = interrupts, + + .memory = memory, +}; + + +void cpu_add_bp(breakpoint_t **list, u13_t addr) +{ + breakpoint_t *bp; + + bp = (breakpoint_t *) g_hal->malloc(sizeof(breakpoint_t)); + if (!bp) { + g_hal->log(LOG_ERROR, "Cannot allocate memory for breakpoint 0x%04X!\n", addr); + return; + } + + bp->addr = addr; + + if (*list != NULL) { + bp->next = *list; + } else { + /* List is empty */ + bp->next = NULL; + } + + *list = bp; +} + +void cpu_free_bp(breakpoint_t **list) +{ + breakpoint_t *bp = *list, *tmp; + + while (bp != NULL) { + tmp = bp->next; + g_hal->free(bp); + bp = tmp; + } + + *list = NULL; +} + +void cpu_set_speed(u8_t speed) +{ + speed_ratio = speed; +} + +state_t * cpu_get_state(void) +{ + return &cpu_state; +} + +u32_t cpu_get_depth(void) +{ + return call_depth; +} + +static void generate_interrupt(int_slot_t slot, u8_t bit) +{ + /* Set the factor flag no matter what */ + interrupts[slot].factor_flag_reg = interrupts[slot].factor_flag_reg | (0x1 << bit); + + /* Trigger the INT only if not masked */ + if (interrupts[slot].mask_reg & (0x1 << bit)) { + interrupts[slot].triggered = 1; + } +} + +void cpu_set_input_pin(pin_t pin, pin_state_t state) +{ + /* Set the I/O */ + inputs[pin & 0x4].states = (inputs[pin & 0x4].states & ~(0x1 << (pin & 0x3))) | (state << (pin & 0x3)); + + /* Trigger the interrupt (TODO: handle relation register) */ + if (state == PIN_STATE_LOW) { + switch ((pin & 0x4) >> 2) { + case 0: + generate_interrupt(INT_K00_K03_SLOT, pin & 0x3); + break; + + case 1: + generate_interrupt(INT_K10_K13_SLOT, pin & 0x3); + break; + } + } +} + +void cpu_sync_ref_timestamp(void) +{ + ref_ts = g_hal->get_timestamp(); +} + +static u4_t get_io(u12_t n) +{ + u4_t tmp; + + switch (n) { + case REG_CLK_INT_FACTOR_FLAGS: + /* Interrupt factor flags (clock timer) */ + tmp = interrupts[INT_CLOCK_TIMER_SLOT].factor_flag_reg; + interrupts[INT_CLOCK_TIMER_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_SW_INT_FACTOR_FLAGS: + /* Interrupt factor flags (stopwatch) */ + tmp = interrupts[INT_STOPWATCH_SLOT].factor_flag_reg; + interrupts[INT_STOPWATCH_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_PROG_INT_FACTOR_FLAGS: + /* Interrupt factor flags (prog timer) */ + tmp = interrupts[INT_PROG_TIMER_SLOT].factor_flag_reg; + interrupts[INT_PROG_TIMER_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_SERIAL_INT_FACTOR_FLAGS: + /* Interrupt factor flags (serial) */ + tmp = interrupts[INT_SERIAL_SLOT].factor_flag_reg; + interrupts[INT_SERIAL_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_K00_K03_INT_FACTOR_FLAGS: + /* Interrupt factor flags (K00-K03) */ + tmp = interrupts[INT_K00_K03_SLOT].factor_flag_reg; + interrupts[INT_K00_K03_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_K10_K13_INT_FACTOR_FLAGS: + /* Interrupt factor flags (K10-K13) */ + tmp = interrupts[INT_K10_K13_SLOT].factor_flag_reg; + interrupts[INT_K10_K13_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_CLOCK_INT_MASKS: + /* Clock timer interrupt masks */ + return interrupts[INT_CLOCK_TIMER_SLOT].mask_reg; + + case REG_SW_INT_MASKS: + /* Stopwatch interrupt masks */ + return interrupts[INT_STOPWATCH_SLOT].mask_reg & 0x3; + + case REG_PROG_INT_MASKS: + /* Prog timer interrupt masks */ + return interrupts[INT_PROG_TIMER_SLOT].mask_reg & 0x1; + + case REG_SERIAL_INT_MASKS: + /* Serial interface interrupt masks */ + return interrupts[INT_SERIAL_SLOT].mask_reg & 0x1; + + case REG_K00_K03_INT_MASKS: + /* Input (K00-K03) interrupt masks */ + return interrupts[INT_K00_K03_SLOT].mask_reg; + + case REG_K10_K13_INT_MASKS: + /* Input (K10-K13) interrupt masks */ + return interrupts[INT_K10_K13_SLOT].mask_reg; + + case REG_PROG_TIMER_DATA_L: + /* Prog timer data (low) */ + return prog_timer_data & 0xF; + + case REG_PROG_TIMER_DATA_H: + /* Prog timer data (high) */ + return (prog_timer_data >> 4) & 0xF; + + case REG_PROG_TIMER_RELOAD_DATA_L: + /* Prog timer reload data (low) */ + return prog_timer_rld & 0xF; + + case REG_PROG_TIMER_RELOAD_DATA_H: + /* Prog timer reload data (high) */ + return (prog_timer_rld >> 4) & 0xF; + + case REG_K00_K03_INPUT_PORT: + /* Input port (K00-K03) */ + return inputs[0].states; + + case REG_K10_K13_INPUT_PORT: + /* Input port (K10-K13) */ + return inputs[1].states; + + case REG_K40_K43_BZ_OUTPUT_PORT: + /* Output port (R40-R43) */ + return GET_IO_MEMORY(memory, n); + + case REG_CPU_OSC3_CTRL: + /* CPU/OSC3 clocks switch, CPU voltage switch */ + return GET_IO_MEMORY(memory, n); + + case REG_LCD_CTRL: + /* LCD control */ + return GET_IO_MEMORY(memory, n); + + case REG_LCD_CONTRAST: + /* LCD contrast */ + break; + + case REG_SVD_CTRL: + /* SVD */ + return GET_IO_MEMORY(memory, n) & 0x7; // Voltage always OK + + case REG_BUZZER_CTRL1: + /* Buzzer config 1 */ + return GET_IO_MEMORY(memory, n); + + case REG_BUZZER_CTRL2: + /* Buzzer config 2 */ + return GET_IO_MEMORY(memory, n) & 0x3; // Buzzer ready + + case REG_CLK_WD_TIMER_CTRL: + /* Clock/Watchdog timer reset */ + break; + + case REG_SW_TIMER_CTRL: + /* Stopwatch stop/run/reset */ + break; + + case REG_PROG_TIMER_CTRL: + /* Prog timer stop/run/reset */ + return !!prog_timer_enabled; + + case REG_PROG_TIMER_CLK_SEL: + /* Prog timer clock selection */ + break; + + default: + g_hal->log(LOG_ERROR, "Read from unimplemented I/O 0x%03X - PC = 0x%04X\n", n, pc); + } + + return 0; +} + +static void set_io(u12_t n, u4_t v) +{ + switch (n) { + case REG_CLOCK_INT_MASKS: + /* Clock timer interrupt masks */ + /* Assume 1Hz timer INT enabled (0x8) */ + interrupts[INT_CLOCK_TIMER_SLOT].mask_reg = v; + break; + + case REG_SW_INT_MASKS: + /* Stopwatch interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_STOPWATCH_SLOT].mask_reg = v; + break; + + case REG_PROG_INT_MASKS: + /* Prog timer interrupt masks */ + /* Assume Prog timer INT enabled (0x1) */ + interrupts[INT_PROG_TIMER_SLOT].mask_reg = v; + break; + + case REG_SERIAL_INT_MASKS: + /* Serial interface interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_K10_K13_SLOT].mask_reg = v; + break; + + case REG_K00_K03_INT_MASKS: + /* Input (K00-K03) interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_SERIAL_SLOT].mask_reg = v; + break; + + case REG_K10_K13_INT_MASKS: + /* Input (K10-K13) interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_K10_K13_SLOT].mask_reg = v; + break; + + case REG_PROG_TIMER_RELOAD_DATA_L: + /* Prog timer reload data (low) */ + prog_timer_rld = v | (prog_timer_rld & 0xF0); + break; + + case REG_PROG_TIMER_RELOAD_DATA_H: + /* Prog timer reload data (high) */ + prog_timer_rld = (prog_timer_rld & 0xF) | (v << 4); + break; + + case REG_K00_K03_INPUT_PORT: + /* Input port (K00-K03) */ + /* Write not allowed */ + break; + + case REG_K40_K43_BZ_OUTPUT_PORT: + /* Output port (R40-R43) */ + //g_hal->log(LOG_INFO, "Output/Buzzer: 0x%X\n", v); + hw_enable_buzzer(!(v & 0x8)); + break; + + case REG_CPU_OSC3_CTRL: + /* CPU/OSC3 clocks switch, CPU voltage switch */ + /* Assume 32,768 OSC1 selected, OSC3 off, battery >= 3,1V (0x1) */ + break; + + case REG_LCD_CTRL: + /* LCD control */ + break; + + case REG_LCD_CONTRAST: + /* LCD contrast */ + /* Assume medium contrast (0x8) */ + break; + + case REG_SVD_CTRL: + /* SVD */ + /* Assume battery voltage always OK (0x6) */ + break; + + case REG_BUZZER_CTRL1: + /* Buzzer config 1 */ + hw_set_buzzer_freq(v & 0x7); + break; + + case REG_BUZZER_CTRL2: + /* Buzzer config 2 */ + break; + + case REG_CLK_WD_TIMER_CTRL: + /* Clock/Watchdog timer reset */ + /* Ignore watchdog */ + break; + + case REG_SW_TIMER_CTRL: + /* Stopwatch stop/run/reset */ + break; + + case REG_PROG_TIMER_CTRL: + /* Prog timer stop/run/reset */ + if (v & 0x2) { + prog_timer_data = prog_timer_rld; + } + + if ((v & 0x1) && !prog_timer_enabled) { + prog_timer_timestamp = tick_counter; + } + + prog_timer_enabled = v & 0x1; + break; + + case REG_PROG_TIMER_CLK_SEL: + /* Prog timer clock selection */ + /* Assume 256Hz, output disabled */ + break; + + default: + g_hal->log(LOG_ERROR, "Write 0x%X to unimplemented I/O 0x%03X - PC = 0x%04X\n", v, n, pc); + } +} + +static void set_lcd(u12_t n, u4_t v) +{ + u8_t i; + u8_t seg, com0; + + seg = ((n & 0x7F) >> 1); + com0 = (((n & 0x80) >> 7) * 8 + (n & 0x1) * 4); + + for (i = 0; i < 4; i++) { + hw_set_lcd_pin(seg, com0 + i, (v >> i) & 0x1); + } +} + +static u4_t get_memory(u12_t n) +{ + u4_t res = 0; + + if (n < MEM_RAM_SIZE) { + /* RAM */ + g_hal->log(LOG_MEMORY, "RAM - "); + res = GET_RAM_MEMORY(memory, n); + } else if (n >= MEM_DISPLAY1_ADDR && n < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { + /* Display Memory 1 */ + g_hal->log(LOG_MEMORY, "Display Memory 1 - "); + res = GET_DISP1_MEMORY(memory, n); + } else if (n >= MEM_DISPLAY2_ADDR && n < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { + /* Display Memory 2 */ + g_hal->log(LOG_MEMORY, "Display Memory 2 - "); + res = GET_DISP2_MEMORY(memory, n); + } else if (n >= MEM_IO_ADDR && n < (MEM_IO_ADDR + MEM_IO_SIZE)) { + /* I/O Memory */ + g_hal->log(LOG_MEMORY, "I/O - "); + res = get_io(n); + } else { + g_hal->log(LOG_ERROR, "Read from invalid memory address 0x%03X - PC = 0x%04X\n", n, pc); + return 0; + } + + g_hal->log(LOG_MEMORY, "Read 0x%X - Address 0x%03X - PC = 0x%04X\n", res, n, pc); + + return res; +} + +static void set_memory(u12_t n, u4_t v) +{ + /* Cache any data written to a valid address, and process it */ + if (n < MEM_RAM_SIZE) { + /* RAM */ + SET_RAM_MEMORY(memory, n, v); + g_hal->log(LOG_MEMORY, "RAM - "); + } else if (n >= MEM_DISPLAY1_ADDR && n < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { + /* Display Memory 1 */ + SET_DISP1_MEMORY(memory, n, v); + set_lcd(n, v); + g_hal->log(LOG_MEMORY, "Display Memory 1 - "); + } else if (n >= MEM_DISPLAY2_ADDR && n < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { + /* Display Memory 2 */ + SET_DISP2_MEMORY(memory, n, v); + set_lcd(n, v); + g_hal->log(LOG_MEMORY, "Display Memory 2 - "); + } else if (n >= MEM_IO_ADDR && n < (MEM_IO_ADDR + MEM_IO_SIZE)) { + /* I/O Memory */ + SET_IO_MEMORY(memory, n, v); + set_io(n, v); + g_hal->log(LOG_MEMORY, "I/O - "); + } else { + g_hal->log(LOG_ERROR, "Write 0x%X to invalid memory address 0x%03X - PC = 0x%04X\n", v, n, pc); + return; + } + + g_hal->log(LOG_MEMORY, "Write 0x%X - Address 0x%03X - PC = 0x%04X\n", v, n, pc); +} + +void cpu_refresh_hw(void) +{ + static const struct range { + u12_t addr; + u12_t size; + } refresh_locs[] = { + { MEM_DISPLAY1_ADDR, MEM_DISPLAY1_SIZE }, /* Display Memory 1 */ + { MEM_DISPLAY2_ADDR, MEM_DISPLAY2_SIZE }, /* Display Memory 2 */ + { REG_BUZZER_CTRL1, 1 }, /* Buzzer frequency */ + { REG_K40_K43_BZ_OUTPUT_PORT, 1 }, /* Buzzer enabled */ + + { 0, 0 }, // end of list + }; + + for (int i = 0; refresh_locs[i].size != 0; i++) { + for (u12_t n = refresh_locs[i].addr; n < (refresh_locs[i].addr + refresh_locs[i].size); n++) { + set_memory(n, GET_MEMORY(memory, n)); + } + } +} + +static u4_t get_rq(u12_t rq) +{ + switch (rq & 0x3) { + case 0x0: + return a; + + case 0x1: + return b; + + case 0x2: + return M(x); + + case 0x3: + return M(y); + } + + return 0; +} + +static void set_rq(u12_t rq, u4_t v) +{ + switch (rq & 0x3) { + case 0x0: + a = v; + break; + + case 0x1: + b = v; + break; + + case 0x2: + SET_M(x, v); + break; + + case 0x3: + SET_M(y, v); + break; + } +} + +/* Instructions */ +static void op_pset_cb(u8_t arg0, u8_t arg1) +{ + np = arg0; +} + +static void op_jp_cb(u8_t arg0, u8_t arg1) +{ + next_pc = arg0 | (np << 8); +} + +static void op_jp_c_cb(u8_t arg0, u8_t arg1) +{ + if (flags & FLAG_C) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_nc_cb(u8_t arg0, u8_t arg1) +{ + if (!(flags & FLAG_C)) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_z_cb(u8_t arg0, u8_t arg1) +{ + if (flags & FLAG_Z) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_nz_cb(u8_t arg0, u8_t arg1) +{ + if (!(flags & FLAG_Z)) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jpba_cb(u8_t arg0, u8_t arg1) +{ + next_pc = a | (b << 4) | (np << 8); +} + +static void op_call_cb(u8_t arg0, u8_t arg1) +{ + pc = (pc + 1) & 0x1FFF; // This does not actually change the PC register + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + next_pc = TO_PC(PCB, NPP, arg0); + call_depth++; +} + +static void op_calz_cb(u8_t arg0, u8_t arg1) +{ + pc = (pc + 1) & 0x1FFF; // This does not actually change the PC register + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + next_pc = TO_PC(PCB, 0, arg0); + call_depth++; +} + +static void op_ret_cb(u8_t arg0, u8_t arg1) +{ + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + call_depth--; +} + +static void op_rets_cb(u8_t arg0, u8_t arg1) +{ + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + next_pc = (pc + 1) & 0x1FFF; + call_depth--; +} + +static void op_retd_cb(u8_t arg0, u8_t arg1) +{ + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + SET_M(x, arg0 & 0xF); + SET_M(x + 1, (arg0 >> 4) & 0xF); + x = ((x + 2) & 0xFF) | (XP << 8); + call_depth--; +} + +static void op_nop5_cb(u8_t arg0, u8_t arg1) +{ +} + +static void op_nop7_cb(u8_t arg0, u8_t arg1) +{ +} + +static void op_halt_cb(u8_t arg0, u8_t arg1) +{ + g_hal->halt(); +} + +static void op_inc_x_cb(u8_t arg0, u8_t arg1) +{ + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_inc_y_cb(u8_t arg0, u8_t arg1) +{ + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_ld_x_cb(u8_t arg0, u8_t arg1) +{ + x = arg0 | (XP << 8); +} + +static void op_ld_y_cb(u8_t arg0, u8_t arg1) +{ + y = arg0 | (YP << 8); +} + +static void op_ld_xp_r_cb(u8_t arg0, u8_t arg1) +{ + x = XHL | (RQ(arg0) << 8); +} + +static void op_ld_xh_r_cb(u8_t arg0, u8_t arg1) +{ + x = XL | (RQ(arg0) << 4) | (XP << 8); +} + +static void op_ld_xl_r_cb(u8_t arg0, u8_t arg1) +{ + x = RQ(arg0) | (XH << 4) | (XP << 8); +} + +static void op_ld_yp_r_cb(u8_t arg0, u8_t arg1) +{ + y = YHL | (RQ(arg0) << 8); +} + +static void op_ld_yh_r_cb(u8_t arg0, u8_t arg1) +{ + y = YL | (RQ(arg0) << 4) | (YP << 8); +} + +static void op_ld_yl_r_cb(u8_t arg0, u8_t arg1) +{ + y = RQ(arg0) | (YH << 4) | (YP << 8); +} + +static void op_ld_r_xp_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, XP); +} + +static void op_ld_r_xh_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, XH); +} + +static void op_ld_r_xl_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, XL); +} + +static void op_ld_r_yp_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, YP); +} + +static void op_ld_r_yh_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, YH); +} + +static void op_ld_r_yl_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, YL); +} + +static void op_adc_xh_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = XH + arg0 + C; + x = XL | ((tmp & 0xF) << 4)| (XP << 8); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!(tmp & 0xF)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_adc_xl_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = XL + arg0 + C; + x = (tmp & 0xF) | (XH << 4) | (XP << 8); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!(tmp & 0xF)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_adc_yh_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = YH + arg0 + C; + y = YL | ((tmp & 0xF) << 4)| (YP << 8); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!(tmp & 0xF)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_adc_yl_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = YL + arg0 + C; + y = (tmp & 0xF) | (YH << 4) | (YP << 8); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!(tmp & 0xF)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_xh_cb(u8_t arg0, u8_t arg1) +{ + if (XH < arg0) { SET_C(); } else { CLEAR_C(); } + if (XH == arg0) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_xl_cb(u8_t arg0, u8_t arg1) +{ + if (XL < arg0) { SET_C(); } else { CLEAR_C(); } + if (XL == arg0) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_yh_cb(u8_t arg0, u8_t arg1) +{ + if (YH < arg0) { SET_C(); } else { CLEAR_C(); } + if (YH == arg0) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_yl_cb(u8_t arg0, u8_t arg1) +{ + if (YL < arg0) { SET_C(); } else { CLEAR_C(); } + if (YL == arg0) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_ld_r_i_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, arg1); +} + +static void op_ld_r_q_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg1)); +} + +static void op_ld_a_mn_cb(u8_t arg0, u8_t arg1) +{ + a = M(arg0); +} + +static void op_ld_b_mn_cb(u8_t arg0, u8_t arg1) +{ + b = M(arg0); +} + +static void op_ld_mn_a_cb(u8_t arg0, u8_t arg1) +{ + SET_M(arg0, a); +} + +static void op_ld_mn_b_cb(u8_t arg0, u8_t arg1) +{ + SET_M(arg0, b); +} + +static void op_ldpx_mx_cb(u8_t arg0, u8_t arg1) +{ + SET_M(x, arg0); + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_ldpx_r_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg1)); + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_ldpy_my_cb(u8_t arg0, u8_t arg1) +{ + SET_M(y, arg0); + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_ldpy_r_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg1)); + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_lbpx_cb(u8_t arg0, u8_t arg1) +{ + SET_M(x, arg0 & 0xF); + SET_M(x + 1, (arg0 >> 4) & 0xF); + x = ((x + 2) & 0xFF) | (XP << 8); +} + +static void op_set_cb(u8_t arg0, u8_t arg1) +{ + flags |= arg0; +} + +static void op_rst_cb(u8_t arg0, u8_t arg1) +{ + flags &= arg0; +} + +static void op_scf_cb(u8_t arg0, u8_t arg1) +{ + SET_C(); +} + +static void op_rcf_cb(u8_t arg0, u8_t arg1) +{ + CLEAR_C(); +} + +static void op_szf_cb(u8_t arg0, u8_t arg1) +{ + SET_Z(); +} + +static void op_rzf_cb(u8_t arg0, u8_t arg1) +{ + CLEAR_Z(); +} + +static void op_sdf_cb(u8_t arg0, u8_t arg1) +{ + SET_D(); +} + +static void op_rdf_cb(u8_t arg0, u8_t arg1) +{ + CLEAR_D(); +} + +static void op_ei_cb(u8_t arg0, u8_t arg1) +{ + SET_I(); +} + +static void op_di_cb(u8_t arg0, u8_t arg1) +{ + CLEAR_I(); +} + +static void op_inc_sp_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp + 1) & 0xFF; +} + +static void op_dec_sp_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; +} + +static void op_push_r_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, RQ(arg0)); +} + +static void op_push_xp_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, XP); +} + +static void op_push_xh_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, XH); +} + +static void op_push_xl_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, XL); +} + +static void op_push_yp_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, YP); +} + +static void op_push_yh_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, YH); +} + +static void op_push_yl_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, YL); +} + +static void op_push_f_cb(u8_t arg0, u8_t arg1) +{ + sp = (sp - 1) & 0xFF; + SET_M(sp, flags); +} + +static void op_pop_r_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, M(sp)); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xp_cb(u8_t arg0, u8_t arg1) +{ + x = XL | (XH << 4)| (M(sp) << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xh_cb(u8_t arg0, u8_t arg1) +{ + x = XL | (M(sp) << 4)| (XP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xl_cb(u8_t arg0, u8_t arg1) +{ + x = M(sp) | (XH << 4)| (XP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yp_cb(u8_t arg0, u8_t arg1) +{ + y = YL | (YH << 4)| (M(sp) << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yh_cb(u8_t arg0, u8_t arg1) +{ + y = YL | (M(sp) << 4)| (YP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yl_cb(u8_t arg0, u8_t arg1) +{ + y = M(sp) | (YH << 4)| (YP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_f_cb(u8_t arg0, u8_t arg1) +{ + flags = M(sp); + sp = (sp + 1) & 0xFF; +} + +static void op_ld_sph_r_cb(u8_t arg0, u8_t arg1) +{ + sp = SPL | (RQ(arg0) << 4); +} + +static void op_ld_spl_r_cb(u8_t arg0, u8_t arg1) +{ + sp = RQ(arg0) | (SPH << 4); +} + +static void op_ld_r_sph_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, SPH); +} + +static void op_ld_r_spl_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, SPL); +} + +static void op_add_r_i_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) + arg1; + if (D) { + if (tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_add_r_q_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) + RQ(arg1); + if (D) { + if (tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_adc_r_i_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) + arg1 + C; + if (D) { + if (tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_adc_r_q_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) + RQ(arg1) + C; + if (D) { + if (tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_sub_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) - RQ(arg1); + if (D) { + if (tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_sbc_r_i_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) - arg1 - C; + if (D) { + if (tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_sbc_r_q_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = RQ(arg0) - RQ(arg1) - C; + if (D) { + if (tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_and_r_i_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) & arg1); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_and_r_q_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) & RQ(arg1)); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_or_r_i_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) | arg1); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_or_r_q_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) | RQ(arg1)); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_xor_r_i_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) ^ arg1); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_xor_r_q_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, RQ(arg0) ^ RQ(arg1)); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_r_i_cb(u8_t arg0, u8_t arg1) +{ + if (RQ(arg0) < arg1) { SET_C(); } else { CLEAR_C(); } + if (RQ(arg0) == arg1) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_cp_r_q_cb(u8_t arg0, u8_t arg1) +{ + if (RQ(arg0) < RQ(arg1)) { SET_C(); } else { CLEAR_C(); } + if (RQ(arg0) == RQ(arg1)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_fan_r_i_cb(u8_t arg0, u8_t arg1) +{ + if (!(RQ(arg0) & arg1)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_fan_r_q_cb(u8_t arg0, u8_t arg1) +{ + if (!(RQ(arg0) & RQ(arg1))) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_rlc_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = (RQ(arg0) << 1) | C; + if (RQ(arg0) & 0x8) { SET_C(); } else { CLEAR_C(); } + SET_RQ(arg0, tmp & 0xF); + /* No need to set Z (issue in DS) */ +} + +static void op_rrc_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = (RQ(arg0) >> 1) | (C << 3); + if (RQ(arg0) & 0x1) { SET_C(); } else { CLEAR_C(); } + SET_RQ(arg0, tmp & 0xF); + /* No need to set Z (issue in DS) */ +} + +static void op_inc_mn_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(arg0) + 1; + SET_M(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!M(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_dec_mn_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(arg0) - 1; + SET_M(arg0, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!M(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +static void op_acpx_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(x) + RQ(arg0) + C; + if (D) { + if (tmp >= 10) { + SET_M(x, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_M(x, tmp); + CLEAR_C(); + } + } else { + SET_M(x, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!M(x)) { SET_Z(); } else { CLEAR_Z(); } + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_acpy_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(y) + RQ(arg0) + C; + if (D) { + if (tmp >= 10) { + SET_M(y, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_M(y, tmp); + CLEAR_C(); + } + } else { + SET_M(y, tmp & 0xF); + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + } + if (!M(y)) { SET_Z(); } else { CLEAR_Z(); } + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_scpx_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(x) - RQ(arg0) - C; + if (D) { + if (tmp >> 4) { + SET_M(x, (tmp - 6) & 0xF); + } else { + SET_M(x, tmp); + } + } else { + SET_M(x, tmp & 0xF); + } + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!M(x)) { SET_Z(); } else { CLEAR_Z(); } + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_scpy_cb(u8_t arg0, u8_t arg1) +{ + u8_t tmp; + + tmp = M(y) - RQ(arg0) - C; + if (D) { + if (tmp >> 4) { + SET_M(y, (tmp - 6) & 0xF); + } else { + SET_M(y, tmp); + } + } else { + SET_M(y, tmp & 0xF); + } + if (tmp >> 4) { SET_C(); } else { CLEAR_C(); } + if (!M(y)) { SET_Z(); } else { CLEAR_Z(); } + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_not_cb(u8_t arg0, u8_t arg1) +{ + SET_RQ(arg0, ~RQ(arg0) & 0xF); + if (!RQ(arg0)) { SET_Z(); } else { CLEAR_Z(); } +} + +/* The E0C6S46 supported instructions */ +static const op_t ops[] = { + {"PSET #0x%02X " , 0xE40, MASK_7B , 0, 0 , 5 , &op_pset_cb}, // PSET + {"JP #0x%02X " , 0x000, MASK_4B , 0, 0 , 5 , &op_jp_cb}, // JP + {"JP C #0x%02X " , 0x200, MASK_4B , 0, 0 , 5 , &op_jp_c_cb}, // JP_C + {"JP NC #0x%02X " , 0x300, MASK_4B , 0, 0 , 5 , &op_jp_nc_cb}, // JP_NC + {"JP Z #0x%02X " , 0x600, MASK_4B , 0, 0 , 5 , &op_jp_z_cb}, // JP_Z + {"JP NZ #0x%02X " , 0x700, MASK_4B , 0, 0 , 5 , &op_jp_nz_cb}, // JP_NZ + {"JPBA " , 0xFE8, MASK_12B, 0, 0 , 5 , &op_jpba_cb}, // JPBA + {"CALL #0x%02X " , 0x400, MASK_4B , 0, 0 , 7 , &op_call_cb}, // CALL + {"CALZ #0x%02X " , 0x500, MASK_4B , 0, 0 , 7 , &op_calz_cb}, // CALZ + {"RET " , 0xFDF, MASK_12B, 0, 0 , 7 , &op_ret_cb}, // RET + {"RETS " , 0xFDE, MASK_12B, 0, 0 , 12, &op_rets_cb}, // RETS + {"RETD #0x%02X " , 0x100, MASK_4B , 0, 0 , 12, &op_retd_cb}, // RETD + {"NOP5 " , 0xFFB, MASK_12B, 0, 0 , 5 , &op_nop5_cb}, // NOP5 + {"NOP7 " , 0xFFF, MASK_12B, 0, 0 , 7 , &op_nop7_cb}, // NOP7 + {"HALT " , 0xFF8, MASK_12B, 0, 0 , 5 , &op_halt_cb}, // HALT + {"INC X #0x%02X " , 0xEE0, MASK_12B, 0, 0 , 5 , &op_inc_x_cb}, // INC_X + {"INC Y #0x%02X " , 0xEF0, MASK_12B, 0, 0 , 5 , &op_inc_y_cb}, // INC_Y + {"LD X #0x%02X " , 0xB00, MASK_4B , 0, 0 , 5 , &op_ld_x_cb}, // LD_X + {"LD Y #0x%02X " , 0x800, MASK_4B , 0, 0 , 5 , &op_ld_y_cb}, // LD_Y + {"LD XP R(#0x%02X) " , 0xE80, MASK_10B, 0, 0 , 5 , &op_ld_xp_r_cb}, // LD_XP_R + {"LD XH R(#0x%02X) " , 0xE84, MASK_10B, 0, 0 , 5 , &op_ld_xh_r_cb}, // LD_XH_R + {"LD XL R(#0x%02X) " , 0xE88, MASK_10B, 0, 0 , 5 , &op_ld_xl_r_cb}, // LD_XL_R + {"LD YP R(#0x%02X) " , 0xE90, MASK_10B, 0, 0 , 5 , &op_ld_yp_r_cb}, // LD_YP_R + {"LD YH R(#0x%02X) " , 0xE94, MASK_10B, 0, 0 , 5 , &op_ld_yh_r_cb}, // LD_YH_R + {"LD YL R(#0x%02X) " , 0xE98, MASK_10B, 0, 0 , 5 , &op_ld_yl_r_cb}, // LD_YL_R + {"LD R(#0x%02X) XP " , 0xEA0, MASK_10B, 0, 0 , 5 , &op_ld_r_xp_cb}, // LD_R_XP + {"LD R(#0x%02X) XH " , 0xEA4, MASK_10B, 0, 0 , 5 , &op_ld_r_xh_cb}, // LD_R_XH + {"LD R(#0x%02X) XL " , 0xEA8, MASK_10B, 0, 0 , 5 , &op_ld_r_xl_cb}, // LD_R_XL + {"LD R(#0x%02X) YP " , 0xEB0, MASK_10B, 0, 0 , 5 , &op_ld_r_yp_cb}, // LD_R_YP + {"LD R(#0x%02X) YH " , 0xEB4, MASK_10B, 0, 0 , 5 , &op_ld_r_yh_cb}, // LD_R_YH + {"LD R(#0x%02X) YL " , 0xEB8, MASK_10B, 0, 0 , 5 , &op_ld_r_yl_cb}, // LD_R_YL + {"ADC XH #0x%02X " , 0xA00, MASK_8B , 0, 0 , 7 , &op_adc_xh_cb}, // ADC_XH + {"ADC XL #0x%02X " , 0xA10, MASK_8B , 0, 0 , 7 , &op_adc_xl_cb}, // ADC_XL + {"ADC YH #0x%02X " , 0xA20, MASK_8B , 0, 0 , 7 , &op_adc_yh_cb}, // ADC_YH + {"ADC YL #0x%02X " , 0xA30, MASK_8B , 0, 0 , 7 , &op_adc_yl_cb}, // ADC_YL + {"CP XH #0x%02X " , 0xA40, MASK_8B , 0, 0 , 7 , &op_cp_xh_cb}, // CP_XH + {"CP XL #0x%02X " , 0xA50, MASK_8B , 0, 0 , 7 , &op_cp_xl_cb}, // CP_XL + {"CP YH #0x%02X " , 0xA60, MASK_8B , 0, 0 , 7 , &op_cp_yh_cb}, // CP_YH + {"CP YL #0x%02X " , 0xA70, MASK_8B , 0, 0 , 7 , &op_cp_yl_cb}, // CP_YL + {"LD R(#0x%02X) #0x%02X ", 0xE00, MASK_6B , 4, 0x030, 5 , &op_ld_r_i_cb}, // LD_R_I + {"LD R(#0x%02X) Q(#0x%02X)", 0xEC0, MASK_8B , 2, 0x00C, 5 , &op_ld_r_q_cb}, // LD_R_Q + {"LD A M(#0x%02X) " , 0xFA0, MASK_8B , 0, 0 , 5 , &op_ld_a_mn_cb}, // LD_A_MN + {"LD B M(#0x%02X) " , 0xFB0, MASK_8B , 0, 0 , 5 , &op_ld_b_mn_cb}, // LD_B_MN + {"LD M(#0x%02X) A " , 0xF80, MASK_8B , 0, 0 , 5 , &op_ld_mn_a_cb}, // LD_MN_A + {"LD M(#0x%02X) B " , 0xF90, MASK_8B , 0, 0 , 5 , &op_ld_mn_b_cb}, // LD_MN_B + {"LDPX MX #0x%02X " , 0xE60, MASK_8B , 0, 0 , 5 , &op_ldpx_mx_cb}, // LDPX_MX + {"LDPX R(#0x%02X) Q(#0x%02X)", 0xEE0, MASK_8B , 2, 0x00C, 5 , &op_ldpx_r_cb}, // LDPX_R + {"LDPY MY #0x%02X " , 0xE70, MASK_8B , 0, 0 , 5 , &op_ldpy_my_cb}, // LDPY_MY + {"LDPY R(#0x%02X) Q(#0x%02X)", 0xEF0, MASK_8B , 2, 0x00C, 5 , &op_ldpy_r_cb}, // LDPY_R + {"LBPX #0x%02X " , 0x900, MASK_4B , 0, 0 , 5 , &op_lbpx_cb}, // LBPX + {"SET #0x%02X " , 0xF40, MASK_8B , 0, 0 , 7 , &op_set_cb}, // SET + {"RST #0x%02X " , 0xF50, MASK_8B , 0, 0 , 7 , &op_rst_cb}, // RST + {"SCF " , 0xF41, MASK_12B, 0, 0 , 7 , &op_scf_cb}, // SCF + {"RCF " , 0xF5E, MASK_12B, 0, 0 , 7 , &op_rcf_cb}, // RCF + {"SZF " , 0xF42, MASK_12B, 0, 0 , 7 , &op_szf_cb}, // SZF + {"RZF " , 0xF5D, MASK_12B, 0, 0 , 7 , &op_rzf_cb}, // RZF + {"SDF " , 0xF44, MASK_12B, 0, 0 , 7 , &op_sdf_cb}, // SDF + {"RDF " , 0xF5B, MASK_12B, 0, 0 , 7 , &op_rdf_cb}, // RDF + {"EI " , 0xF48, MASK_12B, 0, 0 , 7 , &op_ei_cb}, // EI + {"DI " , 0xF57, MASK_12B, 0, 0 , 7 , &op_di_cb}, // DI + {"INC SP " , 0xFDB, MASK_12B, 0, 0 , 5 , &op_inc_sp_cb}, // INC_SP + {"DEC SP " , 0xFCB, MASK_12B, 0, 0 , 5 , &op_dec_sp_cb}, // DEC_SP + {"PUSH R(#0x%02X) " , 0xFC0, MASK_10B, 0, 0 , 5 , &op_push_r_cb}, // PUSH_R + {"PUSH XP " , 0xFC4, MASK_12B, 0, 0 , 5 , &op_push_xp_cb}, // PUSH_XP + {"PUSH XH " , 0xFC5, MASK_12B, 0, 0 , 5 , &op_push_xh_cb}, // PUSH_XH + {"PUSH XL " , 0xFC6, MASK_12B, 0, 0 , 5 , &op_push_xl_cb}, // PUSH_XL + {"PUSH YP " , 0xFC7, MASK_12B, 0, 0 , 5 , &op_push_yp_cb}, // PUSH_YP + {"PUSH YH " , 0xFC8, MASK_12B, 0, 0 , 5 , &op_push_yh_cb}, // PUSH_YH + {"PUSH YL " , 0xFC9, MASK_12B, 0, 0 , 5 , &op_push_yl_cb}, // PUSH_YL + {"PUSH F " , 0xFCA, MASK_12B, 0, 0 , 5 , &op_push_f_cb}, // PUSH_F + {"POP R(#0x%02X) " , 0xFD0, MASK_10B, 0, 0 , 5 , &op_pop_r_cb}, // POP_R + {"POP XP " , 0xFD4, MASK_12B, 0, 0 , 5 , &op_pop_xp_cb}, // POP_XP + {"POP XH " , 0xFD5, MASK_12B, 0, 0 , 5 , &op_pop_xh_cb}, // POP_XH + {"POP XL " , 0xFD6, MASK_12B, 0, 0 , 5 , &op_pop_xl_cb}, // POP_XL + {"POP YP " , 0xFD7, MASK_12B, 0, 0 , 5 , &op_pop_yp_cb}, // POP_YP + {"POP YH " , 0xFD8, MASK_12B, 0, 0 , 5 , &op_pop_yh_cb}, // POP_YH + {"POP YL " , 0xFD9, MASK_12B, 0, 0 , 5 , &op_pop_yl_cb}, // POP_YL + {"POP F " , 0xFDA, MASK_12B, 0, 0 , 5 , &op_pop_f_cb}, // POP_F + {"LD SPH R(#0x%02X) " , 0xFE0, MASK_10B, 0, 0 , 5 , &op_ld_sph_r_cb}, // LD_SPH_R + {"LD SPL R(#0x%02X) " , 0xFF0, MASK_10B, 0, 0 , 5 , &op_ld_spl_r_cb}, // LD_SPL_R + {"LD R(#0x%02X) SPH " , 0xFE4, MASK_10B, 0, 0 , 5 , &op_ld_r_sph_cb}, // LD_R_SPH + {"LD R(#0x%02X) SPL " , 0xFF4, MASK_10B, 0, 0 , 5 , &op_ld_r_spl_cb}, // LD_R_SPL + {"ADD R(#0x%02X) #0x%02X ", 0xC00, MASK_6B , 4, 0x030, 7 , &op_add_r_i_cb}, // ADD_R_I + {"ADD R(#0x%02X) Q(#0x%02X)", 0xA80, MASK_8B , 2, 0x00C, 7 , &op_add_r_q_cb}, // ADD_R_Q + {"ADC R(#0x%02X) #0x%02X ", 0xC40, MASK_6B , 4, 0x030, 7 , &op_adc_r_i_cb}, // ADC_R_I + {"ADC R(#0x%02X) Q(#0x%02X)", 0xA90, MASK_8B , 2, 0x00C, 7 , &op_adc_r_q_cb}, // ADC_R_Q + {"SUB R(#0x%02X) Q(#0x%02X)", 0xAA0, MASK_8B , 2, 0x00C, 7 , &op_sub_cb}, // SUB + {"SBC R(#0x%02X) #0x%02X ", 0xB40, MASK_6B , 4, 0x030, 7 , &op_sbc_r_i_cb}, // SBC_R_I + {"SBC R(#0x%02X) Q(#0x%02X)", 0xAB0, MASK_8B , 2, 0x00C, 7 , &op_sbc_r_q_cb}, // SBC_R_Q + {"AND R(#0x%02X) #0x%02X ", 0xC80, MASK_6B , 4, 0x030, 7 , &op_and_r_i_cb}, // AND_R_I + {"AND R(#0x%02X) Q(#0x%02X)", 0xAC0, MASK_8B , 2, 0x00C, 7 , &op_and_r_q_cb}, // AND_R_Q + {"OR R(#0x%02X) #0x%02X ", 0xCC0, MASK_6B , 4, 0x030, 7 , &op_or_r_i_cb}, // OR_R_I + {"OR R(#0x%02X) Q(#0x%02X)", 0xAD0, MASK_8B , 2, 0x00C, 7 , &op_or_r_q_cb}, // OR_R_Q + {"XOR R(#0x%02X) #0x%02X ", 0xD00, MASK_6B , 4, 0x030, 7 , &op_xor_r_i_cb}, // XOR_R_I + {"XOR R(#0x%02X) Q(#0x%02X)", 0xAE0, MASK_8B , 2, 0x00C, 7 , &op_xor_r_q_cb}, // XOR_R_Q + {"CP R(#0x%02X) #0x%02X ", 0xDC0, MASK_6B , 4, 0x030, 7 , &op_cp_r_i_cb}, // CP_R_I + {"CP R(#0x%02X) Q(#0x%02X)", 0xF00, MASK_8B , 2, 0x00C, 7 , &op_cp_r_q_cb}, // CP_R_Q + {"FAN R(#0x%02X) #0x%02X ", 0xD80, MASK_6B , 4, 0x030, 7 , &op_fan_r_i_cb}, // FAN_R_I + {"FAN R(#0x%02X) Q(#0x%02X)", 0xF10, MASK_8B , 2, 0x00C, 7 , &op_fan_r_q_cb}, // FAN_R_Q + {"RLC R(#0x%02X) " , 0xAF0, MASK_8B , 0, 0 , 7 , &op_rlc_cb}, // RLC + {"RRC R(#0x%02X) " , 0xE8C, MASK_10B, 0, 0 , 5 , &op_rrc_cb}, // RRC + {"INC M(#0x%02X) " , 0xF60, MASK_8B , 0, 0 , 7 , &op_inc_mn_cb}, // INC_MN + {"DEC M(#0x%02X) " , 0xF70, MASK_8B , 0, 0 , 7 , &op_dec_mn_cb}, // DEC_MN + {"ACPX R(#0x%02X) " , 0xF28, MASK_10B, 0, 0 , 7 , &op_acpx_cb}, // ACPX + {"ACPY R(#0x%02X) " , 0xF2C, MASK_10B, 0, 0 , 7 , &op_acpy_cb}, // ACPY + {"SCPX R(#0x%02X) " , 0xF38, MASK_10B, 0, 0 , 7 , &op_scpx_cb}, // SCPX + {"SCPY R(#0x%02X) " , 0xF3C, MASK_10B, 0, 0 , 7 , &op_scpy_cb}, // SCPY + {"NOT R(#0x%02X) " , 0xD0F, 0xFCF , 4, 0 , 7 , &op_not_cb}, // NOT + + {NULL, 0, 0, 0, 0, 0, NULL}, +}; + +static timestamp_t wait_for_cycles(timestamp_t since, u8_t cycles) { + timestamp_t deadline; + + tick_counter += cycles; + + if (speed_ratio == 0) { + /* Emulation will be as fast as possible */ + return g_hal->get_timestamp(); + } + + deadline = since + (cycles * ts_freq)/(TICK_FREQUENCY * speed_ratio); + g_hal->sleep_until(deadline); + + return deadline; +} + +static void process_interrupts(void) +{ + u8_t i; + + /* Process interrupts in priority order */ + for (i = 0; i < INT_SLOT_NUM; i++) { + if (interrupts[i].triggered) { + //printf("IT %u !\n", i); + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + CLEAR_I(); + np = TO_NP(NBP, 1); + pc = TO_PC(PCB, 1, interrupts[i].vector); + call_depth++; + + ref_ts = wait_for_cycles(ref_ts, 12); + interrupts[i].triggered = 0; + } + } +} + +static void print_state(u8_t op_num, u12_t op, u13_t addr) +{ + u8_t i; + + if (!g_hal->is_log_enabled(LOG_CPU)) { + return; + } + + g_hal->log(LOG_CPU, "0x%04X: ", addr); + + for (i = 0; i < call_depth; i++) { + g_hal->log(LOG_CPU, " "); + } + + if (ops[op_num].mask_arg0 != 0) { + /* Two arguments */ + g_hal->log(LOG_CPU, ops[op_num].log, (op & ops[op_num].mask_arg0) >> ops[op_num].shift_arg0, op & ~(ops[op_num].mask | ops[op_num].mask_arg0)); + } else { + /* One argument */ + g_hal->log(LOG_CPU, ops[op_num].log, (op & ~ops[op_num].mask) >> ops[op_num].shift_arg0); + } + + if (call_depth < 10) { + for (i = 0; i < (10 - call_depth); i++) { + g_hal->log(LOG_CPU, " "); + } + } + + g_hal->log(LOG_CPU, " ; 0x%03X - ", op); + for (i = 0; i < 12; i++) { + g_hal->log(LOG_CPU, "%s", ((op >> (11 - i)) & 0x1) ? "1" : "0"); + } + g_hal->log(LOG_CPU, " - PC = 0x%04X, SP = 0x%02X, NP = 0x%02X, X = 0x%03X, Y = 0x%03X, A = 0x%X, B = 0x%X, F = 0x%X\n", pc, sp, np, x, y, a, b, flags); +} + +void cpu_reset(void) +{ + u13_t i; + + /* Registers and variables init */ + pc = TO_PC(0, 1, 0x00); // PC starts at bank 0, page 1, step 0 + np = TO_NP(0, 1); // NP starts at page 1 + a = 0; // undef + b = 0; // undef + x = 0; // undef + y = 0; // undef + sp = 0; // undef + flags = 0; + + /* Init RAM to zeros */ + for (i = 0; i < MEM_BUFFER_SIZE; i++) { + memory[i] = 0; + } + + SET_IO_MEMORY(memory, REG_K40_K43_BZ_OUTPUT_PORT, 0xF); // Output port (R40-R43) + SET_IO_MEMORY(memory, REG_LCD_CTRL, 0x8); // LCD control + /* TODO: Input relation register */ + + cpu_sync_ref_timestamp(); +} + +bool_t cpu_init(const u12_t *program, breakpoint_t *breakpoints, u32_t freq) +{ + g_program = program; + g_breakpoints = breakpoints; + ts_freq = freq; + + cpu_reset(); + + return 0; +} + +void cpu_release(void) +{ +} + +int cpu_step(void) +{ + u12_t op; + u8_t i; + breakpoint_t *bp = g_breakpoints; + static u8_t previous_cycles = 0; + + op = g_program[pc]; + + /* Lookup the OP code */ + for (i = 0; ops[i].log != NULL; i++) { + if ((op & ops[i].mask) == ops[i].code) { + break; + } + } + + if (ops[i].log == NULL) { + g_hal->log(LOG_ERROR, "Unknown op-code 0x%X (pc = 0x%04X)\n", op, pc); + return 1; + } + + next_pc = (pc + 1) & 0x1FFF; + + /* Display the operation along with the current state of the processor */ + print_state(i, op, pc); + + /* Match the speed of the real processor + * NOTE: For better accuracy, the final wait should happen here, however + * the downside is that all interrupts will likely be delayed by one OP + */ + ref_ts = wait_for_cycles(ref_ts, previous_cycles); + + /* Process the OP code */ + if (ops[i].cb != NULL) { + if (ops[i].mask_arg0 != 0) { + /* Two arguments */ + ops[i].cb((op & ops[i].mask_arg0) >> ops[i].shift_arg0, op & ~(ops[i].mask | ops[i].mask_arg0)); + } else { + /* One arguments */ + ops[i].cb((op & ~ops[i].mask) >> ops[i].shift_arg0, 0); + } + } + + /* Prepare for the next instruction */ + pc = next_pc; + previous_cycles = ops[i].cycles; + + if (i > 0) { + /* OP code is not PSET, reset NP */ + np = (pc >> 8) & 0x1F; + } + + /* Handle timers using the internal tick counter */ + if (tick_counter - clk_timer_timestamp >= TIMER_1HZ_PERIOD) { + do { + clk_timer_timestamp += TIMER_1HZ_PERIOD; + } while (tick_counter - clk_timer_timestamp >= TIMER_1HZ_PERIOD); + + generate_interrupt(INT_CLOCK_TIMER_SLOT, 3); + } + + if (prog_timer_enabled && tick_counter - prog_timer_timestamp >= TIMER_256HZ_PERIOD) { + do { + prog_timer_timestamp += TIMER_256HZ_PERIOD; + prog_timer_data--; + + if (prog_timer_data == 0) { + prog_timer_data = prog_timer_rld; + generate_interrupt(INT_PROG_TIMER_SLOT, 0); + } + } while (tick_counter - prog_timer_timestamp >= TIMER_256HZ_PERIOD); + } + + /* Check if there is any pending interrupt */ + if (I && i > 0) { // Do not process interrupts after a PSET operation + process_interrupts(); + } + + /* Check if we could pause the execution */ + while (bp != NULL) { + if (bp->addr == pc) { + return 1; + } + + bp = bp->next; + } + + return 0; +} diff --git a/applications/plugins/tama_p1/tamalib/cpu.h b/applications/plugins/tama_p1/tamalib/cpu.h new file mode 100644 index 000000000..e8e406b7f --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/cpu.h @@ -0,0 +1,192 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "hal.h" + +#define MEMORY_SIZE 4096 // 4096 x 4 bits (640 x 4 bits of RAM) + +#define MEM_RAM_ADDR 0x000 +#define MEM_RAM_SIZE 0x280 +#define MEM_DISPLAY1_ADDR 0xE00 +#define MEM_DISPLAY1_SIZE 0x050 +#define MEM_DISPLAY2_ADDR 0xE80 +#define MEM_DISPLAY2_SIZE 0x050 +#define MEM_IO_ADDR 0xF00 +#define MEM_IO_SIZE 0x080 + +/* Define this if you want to reduce the footprint of the memory buffer from 4096 u4_t (most likely bytes) + * to 464 u8_t (bytes for sure), while increasing slightly the number of operations needed to read/write from/to it. + */ +#define LOW_FOOTPRINT + +#ifdef LOW_FOOTPRINT +/* Invalid memory areas are not buffered to reduce the footprint of the library in memory */ +#define MEM_BUFFER_SIZE (MEM_RAM_SIZE + MEM_DISPLAY1_SIZE + MEM_DISPLAY2_SIZE + MEM_IO_SIZE)/2 + +/* Maps the CPU memory to the memory buffer */ +#define RAM_TO_MEMORY(n) ((n - MEM_RAM_ADDR)/2) +#define DISP1_TO_MEMORY(n) ((n - MEM_DISPLAY1_ADDR + MEM_RAM_SIZE)/2) +#define DISP2_TO_MEMORY(n) ((n - MEM_DISPLAY2_ADDR + MEM_RAM_SIZE + MEM_DISPLAY1_SIZE)/2) +#define IO_TO_MEMORY(n) ((n - MEM_IO_ADDR + MEM_RAM_SIZE + MEM_DISPLAY1_SIZE + MEM_DISPLAY2_SIZE)/2) + +#define SET_RAM_MEMORY(buffer, n, v) {buffer[RAM_TO_MEMORY(n)] = (buffer[RAM_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | ((v) & 0xF) << (((n) % 2) << 2);} +#define SET_DISP1_MEMORY(buffer, n, v) {buffer[DISP1_TO_MEMORY(n)] = (buffer[DISP1_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | ((v) & 0xF) << (((n) % 2) << 2);} +#define SET_DISP2_MEMORY(buffer, n, v) {buffer[DISP2_TO_MEMORY(n)] = (buffer[DISP2_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | ((v) & 0xF) << (((n) % 2) << 2);} +#define SET_IO_MEMORY(buffer, n, v) {buffer[IO_TO_MEMORY(n)] = (buffer[IO_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | ((v) & 0xF) << (((n) % 2) << 2);} +#define SET_MEMORY(buffer, n, v) {if ((n) < (MEM_RAM_ADDR + MEM_RAM_SIZE)) { \ + SET_RAM_MEMORY(buffer, n, v); \ + } else if ((n) < MEM_DISPLAY1_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if ((n) < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { \ + SET_DISP1_MEMORY(buffer, n, v); \ + } else if ((n) < MEM_DISPLAY2_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if ((n) < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { \ + SET_DISP2_MEMORY(buffer, n, v); \ + } else if ((n) < MEM_IO_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if ((n) < (MEM_IO_ADDR + MEM_IO_SIZE)) { \ + SET_IO_MEMORY(buffer, n, v); \ + } else { \ + /* INVALID_MEMORY */ \ + }} + +#define GET_RAM_MEMORY(buffer, n) ((buffer[RAM_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_DISP1_MEMORY(buffer, n) ((buffer[DISP1_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_DISP2_MEMORY(buffer, n) ((buffer[DISP2_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_IO_MEMORY(buffer, n) ((buffer[IO_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_MEMORY(buffer, n) ((buffer[ \ + ((n) < (MEM_RAM_ADDR + MEM_RAM_SIZE)) ? RAM_TO_MEMORY(n) : \ + ((n) < MEM_DISPLAY1_ADDR) ? 0 : \ + ((n) < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) ? DISP1_TO_MEMORY(n) : \ + ((n) < MEM_DISPLAY2_ADDR) ? 0 : \ + ((n) < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) ? DISP2_TO_MEMORY(n) : \ + ((n) < MEM_IO_ADDR) ? 0 : \ + ((n) < (MEM_IO_ADDR + MEM_IO_SIZE)) ? IO_TO_MEMORY(n) : 0 \ + ] >> (((n) % 2) << 2)) & 0xF) + +#define MEM_BUFFER_TYPE u8_t +#else +#define MEM_BUFFER_SIZE MEMORY_SIZE + +#define SET_MEMORY(buffer, n, v) {buffer[n] = v;} +#define SET_RAM_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_DISP1_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_DISP2_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_IO_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) + +#define GET_MEMORY(buffer, n) (buffer[n]) +#define GET_RAM_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_DISP1_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_DISP2_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_IO_MEMORY(buffer, n) GET_MEMORY(buffer, n) + +#define MEM_BUFFER_TYPE u4_t +#endif + +typedef struct breakpoint { + u13_t addr; + struct breakpoint *next; +} breakpoint_t; + +/* Pins (TODO: add other pins) */ +typedef enum { + PIN_K00 = 0x0, + PIN_K01 = 0x1, + PIN_K02 = 0x2, + PIN_K03 = 0x3, + PIN_K10 = 0X4, + PIN_K11 = 0X5, + PIN_K12 = 0X6, + PIN_K13 = 0X7, +} pin_t; + +typedef enum { + PIN_STATE_LOW = 0, + PIN_STATE_HIGH = 1, +} pin_state_t; + +typedef enum { + INT_PROG_TIMER_SLOT = 0, + INT_SERIAL_SLOT = 1, + INT_K10_K13_SLOT = 2, + INT_K00_K03_SLOT = 3, + INT_STOPWATCH_SLOT = 4, + INT_CLOCK_TIMER_SLOT = 5, + INT_SLOT_NUM, +} int_slot_t; + +typedef struct { + u4_t factor_flag_reg; + u4_t mask_reg; + bool_t triggered; /* 1 if triggered, 0 otherwise */ + u8_t vector; +} interrupt_t; + +typedef struct { + u13_t *pc; + u12_t *x; + u12_t *y; + u4_t *a; + u4_t *b; + u5_t *np; + u8_t *sp; + u4_t *flags; + + u32_t *tick_counter; + u32_t *clk_timer_timestamp; + u32_t *prog_timer_timestamp; + bool_t *prog_timer_enabled; + u8_t *prog_timer_data; + u8_t *prog_timer_rld; + + u32_t *call_depth; + + interrupt_t *interrupts; + + MEM_BUFFER_TYPE *memory; +} state_t; + + +void cpu_add_bp(breakpoint_t **list, u13_t addr); +void cpu_free_bp(breakpoint_t **list); + +void cpu_set_speed(u8_t speed); + +state_t * cpu_get_state(void); + +u32_t cpu_get_depth(void); + +void cpu_set_input_pin(pin_t pin, pin_state_t state); + +void cpu_sync_ref_timestamp(void); + +void cpu_refresh_hw(void); + +void cpu_reset(void); + +bool_t cpu_init(const u12_t *program, breakpoint_t *breakpoints, u32_t freq); +void cpu_release(void); + +int cpu_step(void); + +#endif /* _CPU_H_ */ diff --git a/applications/plugins/tama_p1/tamalib/hal.h b/applications/plugins/tama_p1/tamalib/hal.h new file mode 100644 index 000000000..4427689af --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/hal.h @@ -0,0 +1,89 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HAL_H_ +#define _HAL_H_ + +#include "../hal_types.h" + +#ifndef NULL + #define NULL 0 +#endif + +typedef enum { + LOG_ERROR = 0x1, + LOG_INFO = (0x1 << 1), + LOG_MEMORY = (0x1 << 2), + LOG_CPU = (0x1 << 3), +} log_level_t; + +/* The Hardware Abstraction Layer + * NOTE: This structure acts as an abstraction layer between TamaLIB and the OS/SDK. + * All pointers MUST be implemented, but some implementations can be left empty. + */ +typedef struct { + /* Memory allocation functions + * NOTE: Needed only if breakpoints support is required. + */ + void * (*malloc)(u32_t size); + void (*free)(void *ptr); + + /* What to do if the CPU has halted + */ + void (*halt)(void); + + /* Log related function + * NOTE: Needed only if log messages are required. + */ + bool_t (*is_log_enabled)(log_level_t level); + void (*log)(log_level_t level, char *buff, ...); + + /* Clock related functions + * NOTE: Timestamps granularity is configured with tamalib_init(), an accuracy + * of ~30 us (1/32768) is required for a cycle accurate emulation. + */ + void (*sleep_until)(timestamp_t ts); + timestamp_t (*get_timestamp)(void); + + /* Screen related functions + * NOTE: In case of direct hardware access to pixels, the set_XXXX() functions + * (called for each pixel/icon update) can directly drive them, otherwise they + * should just store the data in a buffer and let update_screen() do the actual + * rendering (at 30 fps). + */ + void (*update_screen)(void); + void (*set_lcd_matrix)(u8_t x, u8_t y, bool_t val); + void (*set_lcd_icon)(u8_t icon, bool_t val); + + /* Sound related functions + * NOTE: set_frequency() changes the output frequency of the sound, while + * play_frequency() decides whether the sound should be heard or not. + */ + void (*set_frequency)(u32_t freq); + void (*play_frequency)(bool_t en); + + /* Event handler from the main app (if any) + * NOTE: This function usually handles button related events, states loading/saving ... + */ + int (*handler)(void); +} hal_t; + +extern hal_t *g_hal; + +#endif /* _HAL_H_ */ diff --git a/applications/plugins/tama_p1/tamalib/hal_types.h.template b/applications/plugins/tama_p1/tamalib/hal_types.h.template new file mode 100644 index 000000000..38c4d212c --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/hal_types.h.template @@ -0,0 +1,32 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HAL_TYPES_H_ +#define _HAL_TYPES_H_ + +typedef unsigned char bool_t; +typedef unsigned char u4_t; +typedef unsigned char u5_t; +typedef unsigned char u8_t; +typedef unsigned short u12_t; +typedef unsigned short u13_t; +typedef unsigned int u32_t; +typedef unsigned int timestamp_t; // WARNING: Must be an unsigned type to properly handle wrapping (u32 wraps in around 1h11m when expressed in us) + +#endif /* _HAL_TYPES_H_ */ diff --git a/applications/plugins/tama_p1/tamalib/hw.c b/applications/plugins/tama_p1/tamalib/hw.c new file mode 100644 index 000000000..cfce8d925 --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/hw.c @@ -0,0 +1,139 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "hw.h" +#include "cpu.h" +#include "hal.h" + +/* SEG -> LCD mapping */ +static u8_t seg_pos[40] = {0, 1, 2, 3, 4, 5, 6, 7, 32, 8, 9, 10, 11, 12 ,13 ,14, 15, 33, 34, 35, 31, 30, 29, 28, 27, 26, 25, 24, 36, 23, 22, 21, 20, 19, 18, 17, 16, 37, 38, 39}; + + +bool_t hw_init(void) +{ + /* Buttons are active LOW */ + cpu_set_input_pin(PIN_K00, PIN_STATE_HIGH); + cpu_set_input_pin(PIN_K01, PIN_STATE_HIGH); + cpu_set_input_pin(PIN_K02, PIN_STATE_HIGH); + + return 0; +} + +void hw_release(void) +{ +} + +void hw_set_lcd_pin(u8_t seg, u8_t com, u8_t val) +{ + if (seg_pos[seg] < LCD_WIDTH) { + g_hal->set_lcd_matrix(seg_pos[seg], com, val); + } else { + /* + * IC n -> seg-com|... + * IC 0 -> 8-0 |18-3 |19-2 + * IC 1 -> 8-1 |17-0 |19-3 + * IC 2 -> 8-2 |17-1 |37-12|38-13|39-14 + * IC 3 -> 8-3 |17-2 |18-1 |19-0 + * IC 4 -> 28-12|37-13|38-14|39-15 + * IC 5 -> 28-13|37-14|38-15 + * IC 6 -> 28-14|37-15|39-12 + * IC 7 -> 28-15|38-12|39-13 + */ + if (seg == 8 && com < 4) { + g_hal->set_lcd_icon(com, val); + } else if (seg == 28 && com >= 12) { + g_hal->set_lcd_icon(com - 8, val); + } + } +} + +void hw_set_button(button_t btn, btn_state_t state) +{ + pin_state_t pin_state = (state == BTN_STATE_PRESSED) ? PIN_STATE_LOW : PIN_STATE_HIGH; + + switch (btn) { + case BTN_LEFT: + cpu_set_input_pin(PIN_K02, pin_state); + break; + + case BTN_MIDDLE: + cpu_set_input_pin(PIN_K01, pin_state); + break; + + case BTN_RIGHT: + cpu_set_input_pin(PIN_K00, pin_state); + break; + } +} + +void hw_set_buzzer_freq(u4_t freq) +{ + u32_t snd_freq = 0; + + switch (freq) { + case 0: + /* 4096.0 Hz */ + snd_freq = 40960; + break; + + case 1: + /* 3276.8 Hz */ + snd_freq = 32768; + break; + + case 2: + /* 2730.7 Hz */ + snd_freq = 27307; + break; + + case 3: + /* 2340.6 Hz */ + snd_freq = 23406; + break; + + case 4: + /* 2048.0 Hz */ + snd_freq = 20480; + break; + + case 5: + /* 1638.4 Hz */ + snd_freq = 16384; + break; + + case 6: + /* 1365.3 Hz */ + snd_freq = 13653; + break; + + case 7: + /* 1170.3 Hz */ + snd_freq = 11703; + break; + } + + if (snd_freq != 0) { + g_hal->set_frequency(snd_freq); + } +} + +void hw_enable_buzzer(bool_t en) +{ + g_hal->play_frequency(en); +} diff --git a/applications/plugins/tama_p1/tamalib/hw.h b/applications/plugins/tama_p1/tamalib/hw.h new file mode 100644 index 000000000..39a22957a --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/hw.h @@ -0,0 +1,51 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HW_H_ +#define _HW_H_ + +#include "hal.h" + +#define LCD_WIDTH 32 +#define LCD_HEIGHT 16 + +#define ICON_NUM 8 + +typedef enum { + BTN_STATE_RELEASED = 0, + BTN_STATE_PRESSED, +} btn_state_t; + +typedef enum { + BTN_LEFT = 0, + BTN_MIDDLE, + BTN_RIGHT, +} button_t; + + +bool_t hw_init(void); +void hw_release(void); + +void hw_set_lcd_pin(u8_t seg, u8_t com, u8_t val); +void hw_set_button(button_t btn, btn_state_t state); + +void hw_set_buzzer_freq(u4_t freq); +void hw_enable_buzzer(bool_t en); + +#endif /* _HW_H_ */ diff --git a/applications/plugins/tama_p1/tamalib/tamalib.c b/applications/plugins/tama_p1/tamalib/tamalib.c new file mode 100644 index 000000000..4bd8879c0 --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/tamalib.c @@ -0,0 +1,137 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "tamalib.h" +#include "hw.h" +#include "cpu.h" +#include "hal.h" + +#define DEFAULT_FRAMERATE 30 // fps + +static exec_mode_t exec_mode = EXEC_MODE_RUN; + +static u32_t step_depth = 0; + +static timestamp_t screen_ts = 0; + +static u32_t ts_freq; + +static u8_t g_framerate = DEFAULT_FRAMERATE; + +hal_t *g_hal; + + +bool_t tamalib_init(const u12_t *program, breakpoint_t *breakpoints, u32_t freq) +{ + bool_t res = 0; + + res |= cpu_init(program, breakpoints, freq); + res |= hw_init(); + + ts_freq = freq; + + return res; +} + +void tamalib_release(void) +{ + hw_release(); + cpu_release(); +} + +void tamalib_set_framerate(u8_t framerate) +{ + g_framerate = framerate; +} + +u8_t tamalib_get_framerate(void) +{ + return g_framerate; +} + +void tamalib_register_hal(hal_t *hal) +{ + g_hal = hal; +} + +void tamalib_set_exec_mode(exec_mode_t mode) +{ + exec_mode = mode; + step_depth = cpu_get_depth(); + cpu_sync_ref_timestamp(); +} + +void tamalib_step(void) +{ + if (exec_mode == EXEC_MODE_PAUSE) { + return; + } + + if (cpu_step()) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } else { + switch (exec_mode) { + case EXEC_MODE_PAUSE: + case EXEC_MODE_RUN: + break; + + case EXEC_MODE_STEP: + exec_mode = EXEC_MODE_PAUSE; + break; + + case EXEC_MODE_NEXT: + if (cpu_get_depth() <= step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + + case EXEC_MODE_TO_CALL: + if (cpu_get_depth() > step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + + case EXEC_MODE_TO_RET: + if (cpu_get_depth() < step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + } + } +} + +void tamalib_mainloop(void) +{ + timestamp_t ts; + + while (!g_hal->handler()) { + tamalib_step(); + + /* Update the screen @ g_framerate fps */ + ts = g_hal->get_timestamp(); + if (ts - screen_ts >= ts_freq/g_framerate) { + screen_ts = ts; + g_hal->update_screen(); + } + } +} diff --git a/applications/plugins/tama_p1/tamalib/tamalib.h b/applications/plugins/tama_p1/tamalib/tamalib.h new file mode 100644 index 000000000..d18101bf6 --- /dev/null +++ b/applications/plugins/tama_p1/tamalib/tamalib.h @@ -0,0 +1,66 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _TAMALIB_H_ +#define _TAMALIB_H_ + +#include "cpu.h" +#include "hw.h" +#include "hal.h" + +#define tamalib_set_button(btn, state) hw_set_button(btn, state) + +#define tamalib_set_speed(speed) cpu_set_speed(speed) + +#define tamalib_get_state() cpu_get_state() +#define tamalib_refresh_hw() cpu_refresh_hw() + +#define tamalib_reset() cpu_reset() + +#define tamalib_add_bp(list, addr) cpu_add_bp(list, addr) +#define tamalib_free_bp(list) cpu_free_bp(list) + +typedef enum { + EXEC_MODE_PAUSE, + EXEC_MODE_RUN, + EXEC_MODE_STEP, + EXEC_MODE_NEXT, + EXEC_MODE_TO_CALL, + EXEC_MODE_TO_RET, +} exec_mode_t; + + +void tamalib_release(void); +bool_t tamalib_init(const u12_t *program, breakpoint_t *breakpoints, u32_t freq); + +void tamalib_set_framerate(u8_t framerate); +u8_t tamalib_get_framerate(void); + +void tamalib_register_hal(hal_t *hal); + +void tamalib_set_exec_mode(exec_mode_t mode); + +/* NOTE: Only one of these two functions must be used in the main application + * (tamalib_step() should be used only if tamalib_mainloop() does not fit the + * main application execution flow). + */ +void tamalib_step(void); +void tamalib_mainloop(void); + +#endif /* _TAMALIB_H_ */