├── .gitignore ├── .travis.yml ├── CMake └── FindScas.cmake ├── CMakeLists.txt ├── KEYBINDINGS.md ├── LICENSE ├── README.md ├── frontends ├── libz80e │ ├── CMakeLists.txt │ ├── emscripten.config │ └── openti_helpers │ │ └── Debugger │ │ └── Debugger.c ├── sdl │ ├── CMakeLists.txt │ ├── main.c │ ├── tui.c │ └── tui.h ├── test │ ├── CMakeLists.txt │ ├── test.c │ └── tests │ │ ├── alu.c │ │ ├── arithmetic.c │ │ ├── block.c │ │ ├── control.c │ │ ├── debugger.c │ │ ├── devices.c │ │ ├── disassembler.c │ │ ├── index.c │ │ ├── interrupts.c │ │ ├── io.c │ │ ├── load.c │ │ ├── performance.c │ │ └── shifts.c ├── tui │ ├── CMakeLists.txt │ ├── main.c │ ├── tui.c │ └── tui.h └── zex │ ├── CMakeLists.txt │ └── zex.c ├── gpl ├── LICENSE ├── zexall.com ├── zexall.src ├── zexdoc.com └── zexdoc.src └── libz80e ├── CMakeLists.txt ├── include └── z80e │ ├── core │ ├── cpu.h │ └── registers.h │ ├── debugger │ ├── commands.h │ ├── debugger.h │ ├── hooks.h │ └── keys.h │ ├── disassembler │ └── disassemble.h │ ├── log │ └── log.h │ ├── runloop │ └── runloop.h │ └── ti │ ├── asic.h │ ├── hardware │ ├── flash.h │ ├── interrupts.h │ ├── keyboard.h │ ├── link.h │ ├── memorymapping.h │ ├── speed.h │ ├── status.h │ ├── t6a04.h │ └── timers.h │ ├── memory.h │ └── ti.h └── src ├── core ├── cpu.c └── registers.c ├── debugger ├── commands │ ├── commands.c │ ├── disassemble.c │ ├── dump_lcd.c │ ├── hexdump.c │ ├── link.c │ ├── load_register.c │ ├── on.c │ ├── ports.c │ ├── press_key.c │ ├── print_expression.c │ ├── print_mappings.c │ ├── print_registers.c │ ├── release_key.c │ ├── run.c │ ├── stack.c │ ├── stop.c │ ├── tap_key.c │ ├── timers.c │ ├── turn_on.c │ └── unhalt.c ├── debugger.c └── hooks.c ├── disassembler └── disassemble.c ├── log └── log.c ├── runloop └── runloop.c └── ti ├── asic.c ├── hardware ├── flash.c ├── interrupts.c ├── keyboard.c ├── link.c ├── memorymapping.c ├── speed.c ├── status.c ├── t6a04.c └── timers.c └── memory.c /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | install_manifest.txt 6 | *.swp 7 | *.o 8 | bin/ 9 | build/ 10 | *.rom 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - echo "yes" | sudo add-apt-repository ppa:kalakris/cmake 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq cmake libsdl1.2-dev libsdl1.2debian 7 | - git clone git://github.com/KnightOS/scas.git 8 | - cd scas 9 | - cmake -DCMAKE_INSTALL_PREFIX=/usr . 10 | - make 11 | - sudo make install 12 | 13 | script: 14 | - cmake -Denable-test=YES . 15 | - make 16 | - ./frontends/test/bin/tests 17 | 18 | notifications: 19 | email: 20 | recipients: 21 | - sir@cmpwn.com 22 | on_success: never 23 | on_failure: always 24 | irc: 25 | channels: 26 | - "chat.freenode.net#knightos" 27 | on_success: never 28 | on_failure: always 29 | skip_join: true 30 | -------------------------------------------------------------------------------- /CMake/FindScas.cmake: -------------------------------------------------------------------------------- 1 | # - Find scas 2 | # Finds scas includes and library 3 | # 4 | # SCAS_INCLUDES - Headers 5 | # SCAS_LIBRARIES - Libraries 6 | # SCAS_FOUND - True if found 7 | 8 | if(SCAS_INCLUDES) 9 | set (SCAS_FIND_QUIETLY TRUE) 10 | endif() 11 | 12 | find_path(SCAS_INCLUDES 8xp.h PATH_SUFFIXES scas) 13 | 14 | find_library(SCAS_LIBRARIES NAMES scas libscas) 15 | 16 | include(FindPackageHandleStandardArgs) 17 | find_package_handle_standard_args(SCAS DEFAULT_MSG SCAS_LIBRARIES SCAS_INCLUDES) 18 | 19 | if (NOT SCAS_FOUND AND SCAS_FIND_REQUIRED) 20 | message (FATAL_ERROR "Could not find scas!") 21 | endif () 22 | 23 | mark_as_advanced(SCAS_LIBRARIES SCAS_INCLUDES) 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | 3 | project(z80e C) 4 | 5 | option(enable-sdl "Enables the SDL frontend" NO) 6 | option(enable-tui "Enables the TUI frontend" YES) 7 | option(enable-zex "Enables the zex test suite" NO) 8 | option(enable-test "Enables the unit test frontend" NO) 9 | 10 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic") 11 | 12 | if(NOT WIN32 OR CYGWIN) 13 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 14 | endif() 15 | 16 | if (MINGW OR MSYS) 17 | add_definitions(-DNOLINK) 18 | Message(STATUS "Building in MinGW or MSYS environment, disabling link socket") 19 | endif() 20 | 21 | if(APPLE) 22 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAPPLE") 23 | endif() 24 | 25 | add_subdirectory(libz80e libz80e) 26 | add_subdirectory(frontends/libz80e frontends/libz80e) 27 | 28 | if(enable-test) 29 | add_subdirectory(frontends/test frontends/test) 30 | endif() 31 | 32 | if(NOT DEFINED EMSCRIPTEN) 33 | if(enable-tui) 34 | add_subdirectory(frontends/tui frontends/tui) 35 | endif() 36 | if(enable-sdl) 37 | add_subdirectory(frontends/sdl frontends/sdl) 38 | endif() 39 | if(enable-zex) 40 | add_subdirectory(frontends/zex frontends/zex) 41 | endif() 42 | endif() 43 | -------------------------------------------------------------------------------- /KEYBINDINGS.md: -------------------------------------------------------------------------------- 1 | Keyboard | Calculator | Keycode 2 | -----|-----|-----| 3 | Left Shift|2nd|0x65 4 | Right Shift|Clear|0x16 5 | Ctrl|Alpha|0x57 6 | Esc|Mode|0x66 7 | F1|Y=|0x64 8 | F2|Window|0x63 9 | F3|Zoom|0x62 10 | F4|Trace|0x61 11 | F5|Graph|0x60 12 | F7|Variable Tree|N/A 13 | F8|Toggle 400% speed|N/A 14 | F10|Load File|N/A 15 | F11|Debugger|N/A 16 | F12|On |N/A 17 | Space|0|0x40 18 | Insert|VARS|0x26 19 | Home|Math|0x56 20 | Page Up|Apps|0x46 21 | Page Down|Programs|0x36 22 | End|Stats|0x37 23 | Backspace|Clear|0x16 24 | +/=|X,T,theta,n|0x47 25 | -/_|negative|0x20 26 | //?|negative|0x20 27 | Numpad 1|Stats|0x37 28 | Numpad 2|Down|0x00 29 | Numpad 3|Programs|0x36 30 | Numpad 4|Right|0x02 31 | Numpad 6|Left|0x01 32 | Numpad 7|Math|0x56 33 | Numpad 8|Up|0x03 34 | Numpad 9|Apps|0x46 35 | Numpad 0|Vars|0x26 36 | Numpad +|+|0x11 37 | Numpad –|–|0x12 38 | Numpad *|*|0x13 39 | Numpad /|/|0x14 40 | A|Math|0x56 41 | B|Apps|0x46 42 | C|Programs|0x36 43 | D|x^-1|0x55 44 | E|sin(|0x45 45 | F|cos(|0x35 46 | G|tan(|0x25 47 | H|^|0x15 48 | I|x^2|0x54 49 | J|,|0x44 50 | K|(|0x34 51 | L|)|0x24 52 | M|/|0x14 53 | N|log(|0x53 54 | O|7|0x43 55 | P|8|0x33 56 | Q|9|0x23 57 | R|*|0x13 58 | S|ln(|0x52 59 | T|4|0x42 60 | U|5|0x32 61 | V|6|0x22 62 | W|–|0x12 63 | X|sto>|0x51 64 | Y|1|0x41 65 | Z|2|0x31 66 | 0|0|0x40 67 | 1|1|0x41 68 | 2|2|0x31 69 | 3|3|0x21 70 | 4|4|0x42 71 | 5|5|0x32 72 | 6|6|0x22 73 | 7|7|0x43 74 | 8|8|0x33 75 | 9|9|0x23 76 | Up Arrow|Up|0x03 77 | Left Arrow|Left|0x01 78 | Right Arrow|Right|0x02 79 | Down Arrow|Down|0x00 80 | Return|Enter|0x10 81 | Delete|DEL|0x67 82 | :/.|:|0x30 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2020 The KnightOS Group 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # z80e 2 | 3 | A z80 emulator designed for debugging the [KnightOS kernel](https://github.com/KnightSoft/kernel). It emulates Texas Instruments calculators and is not recommended for general-purpose z80 debugging. 4 | It's not done yet. 5 | 6 | A list of keybindings can be found in [KEYBINDINGS.md](KEYBINDINGS.md) 7 | 8 | ## Why z80e? 9 | 10 | Most TI emulators have some of a shared set of problems, which z80e hopes to avoid. z80e is... 11 | 12 | **Portable**. z80e has been described as "portable as fuck". The actual emulation code uses maybe a half dozen external functions and it's been known to run on Linux, BSD, OSX, Windows, TI-Nspires, TI-Nspires running Linux, Rockbox, and two hobby operating systems for i686. It also was forked to form the basis of [CEmu](https://github.com/CE-Programming/CEmu). 13 | 14 | **Flexible**. You can run z80e with a simple terminal interface, a curses debugger, a gtk application (planned), in a web browser (in-progress), etc 15 | 16 | **Powerful**. We've built a powerful GDB-like debugger, a curses-based debugger, a web debugger, and a gtk debugger (planned). 17 | 18 | **Clean**. If you know C (z80 assembly would help, too), you can understand and help improve the codebase. 19 | 20 | **Permissive**. MIT licensed and easy to distribute, modify, or do whatever with. 21 | 22 | ## Compiling 23 | 24 | First make sure you have [scas](https://github.com/KnightOS/scas) installed with 25 | libscas built (SCAS_LIBRARY=1 if using cmake). 26 | 27 | Native (Linux): 28 | 29 | $ cmake . 30 | $ make 31 | 32 | Change `cmake .` to `cmake -Denable-sdl=YES .` to build the SDL (graphical) frontend. 33 | 34 | Compiling on Windows with cygwin requires ncurses and GNU readline. 35 | 36 | Browser: 37 | 38 | $ emconfigure cmake . 39 | $ make 40 | 41 | The tests and z80e files will be compiled to bytecode and then to javascript, exporting all external-use methods into `bin/z80e.js` and `bin/tests.js`. These can be used as you would with any other emscripten module, so you can run tests.js in node to run the tests, or add it to an HTML file. 42 | To build on Windows or Mac, read the cmake docs and submit a pull request fixing this sentence once you figure it out. 43 | 44 | ## Help, Bugs, Feedback 45 | 46 | If you need help with KnightOS, want to keep up with progress, chat with 47 | developers, or ask any other questions about KnightOS, you can hang out in the 48 | IRC channel: [#knightos on irc.freenode.net](http://webchat.freenode.net/?channels=knightos). 49 | 50 | To report bugs, please create [a GitHub issue](https://github.com/KnightOS/KnightOS/issues/new) or contact us on IRC. 51 | 52 | If you'd like to contribute to the project, please see the [contribution guidelines](http://www.knightos.org/contributing). 53 | -------------------------------------------------------------------------------- /frontends/libz80e/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project (libz80e) 3 | 4 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/") 5 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "bin/") 6 | 7 | include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include) 8 | 9 | if(DEFINED EMSCRIPTEN) 10 | add_executable(z80e 11 | $ 12 | openti_helpers/Debugger/Debugger.c 13 | ) # bit of a hack to get it to compile to .js directly 14 | 15 | SET_TARGET_PROPERTIES(z80e PROPERTIES LINK_FLAGS @${CMAKE_CURRENT_SOURCE_DIR}/emscripten.config) 16 | else() 17 | add_library(z80e SHARED 18 | $ 19 | ) 20 | 21 | if(NOT (APPLE OR HAIKU OR MINGW OR MSYS)) 22 | TARGET_LINK_LIBRARIES(z80e rt) 23 | endif() 24 | 25 | endif() 26 | 27 | TARGET_LINK_LIBRARIES(z80e ${SCAS_LIBRARIES}) 28 | 29 | INSTALL(TARGETS z80e 30 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 31 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 32 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) 33 | 34 | install( 35 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include/z80e/ 36 | DESTINATION include/z80e 37 | ) 38 | -------------------------------------------------------------------------------- /frontends/libz80e/emscripten.config: -------------------------------------------------------------------------------- 1 | -s 'EXPORTED_FUNCTIONS=["_asic_add_timer", "_asic_free", "_asic_init", "_asic_remove_timer", "_asic_set_clock_rate", 2 | "_bw_lcd_advance_int_pointer", "_bw_lcd_advance_pointer", "_bw_lcd_data_read", 3 | "_bw_lcd_data_write", "_bw_lcd_read_screen", "_bw_lcd_reset", "_bw_lcd_status_read", 4 | "_bw_lcd_status_write", "_bw_lcd_write_screen", "_command_backwards_hexdump", 5 | "_command_break", "_command_disassemble", "_command_dump_lcd", "_command_hexdump", 6 | "_command_in", "_command_on", "_command_out", "_command_print_expression", 7 | "_command_print_mappings", "_command_print_registers", "_command_run", "_command_stack", 8 | "_command_step", "_command_step_over", "_command_stop", "_command_turn_on", "_command_unhalt", 9 | "_cpu_execute", "_cpu_free", "_cpu_init", "_cpu_read_byte", 10 | "_cpu_read_register_byte", "_cpu_read_register_word", "_cpu_read_word", "_cpu_write_byte", 11 | "_cpu_write_register_byte", "_cpu_write_register_word", "_cpu_write_word", "_create_hook_set", 12 | "_debugger_exec", "_debugger_parse_commandline", "_debugger_source_rc", "_depress_key", 13 | "_exAFAF", "_exDEHL", "_exx", "_find_best_command", "_free_debugger", "_free_keyboard", 14 | "_free_log", "_free_mapping_ports", "_free_status", "_hook_add_after_execution", 15 | "_hook_add_before_execution", "_hook_add_lcd_update", "_hook_add_memory_read", 16 | "_hook_add_memory_write", "_hook_add_register_read", "_hook_add_register_write", 17 | "_hook_on_after_execution", "_hook_on_before_execution", "_hook_on_lcd_update", 18 | "_hook_on_memory_read", "_hook_on_memory_write", "_hook_on_register_read", 19 | "_hook_on_register_write", "_hook_remove_after_execution", "_hook_remove_before_execution", 20 | "_hook_remove_lcd_update", "_hook_remove_memory_read", "_hook_remove_register_read", 21 | "_hook_remove_register_write", "_hook_remove_register_write", "_init_debugger", 22 | "_init_interrupts", "_init_keyboard", "_init_log", "_init_mapping_ports", "_init_status", 23 | "_log_message", "_mmu_force_write", "_parse_expression", "_parse_instruction", "_print_state", 24 | "_read_interrupting_device", "_read_interrupt_mask", "_register_command", "_release_key", 25 | "_reload_mapping", "_runloop_init", "_runloop_tick", "_runloop_tick_cycles", 26 | "_set_first_timer_frequency", "_set_second_timer_frequency", "_setup_lcd_display", 27 | "_ti_interrupts_acknowledge_interrupt", "_ti_interrupts_interrupt", 28 | "_ti_interrupts_set_interrupt_enabled", "_ti_mmu_free", "_ti_mmu_init", "_ti_read_byte", 29 | "_ti_write_byte", "_updateFlags", "_updateFlags_except", "_updateFlags_parity", 30 | "_updateFlags_subtraction", "_updateFlags_withOptions", "_updateParity", 31 | "_write_acknowledged_interrupts", "_write_interrupt_mask", "_write_timer_speed", 32 | "_openti_new_state", "_openti_close_window"]' 33 | -s 'RESERVED_FUNCTION_POINTERS=2048' 34 | -------------------------------------------------------------------------------- /frontends/libz80e/openti_helpers/Debugger/Debugger.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef void (*openti_js_print_string)(int, const char *); 8 | typedef void (*openti_js_window_closed)(int); 9 | typedef int (*openti_js_new_state)(int, debugger_state_t *, const char *); 10 | 11 | typedef struct { 12 | int js_reference; 13 | openti_js_print_string print_string; 14 | openti_js_new_state new_state; 15 | openti_js_window_closed window_closed; 16 | } openti_interface_state; 17 | 18 | char openti_print_buffer[256]; 19 | 20 | int openti_state_vprint(debugger_state_t *state, const char *format, va_list list) { 21 | openti_interface_state *istate = state->interface_state; 22 | int count = vsnprintf(openti_print_buffer, 256, format, list); 23 | istate->print_string(istate->js_reference, openti_print_buffer); 24 | return count; 25 | } 26 | 27 | int openti_state_print(debugger_state_t *state, const char *format, ...) { 28 | va_list list; 29 | va_start(list, format); 30 | return openti_state_vprint(state, format, list); 31 | } 32 | 33 | void openti_close_window(debugger_state_t *state) { 34 | openti_interface_state *istate = state->interface_state; 35 | istate->window_closed(istate->js_reference); 36 | free(state->interface_state); 37 | free(state); 38 | } 39 | 40 | debugger_state_t *openti_create_new_state(debugger_state_t *old_state, const char *title) { 41 | openti_interface_state *old_istate = old_state->interface_state; 42 | 43 | debugger_state_t *state = calloc(sizeof(debugger_state_t), 1); 44 | state->print = openti_state_print; 45 | state->vprint = openti_state_vprint; 46 | state->debugger = old_state->debugger; 47 | state->asic = state->debugger->asic; 48 | state->log = state->asic->log; 49 | state->create_new_state = openti_create_new_state; 50 | state->close_window = openti_close_window; 51 | 52 | openti_interface_state *istate = state->interface_state = calloc(sizeof(openti_interface_state), 1); 53 | istate->js_reference = old_istate->new_state(old_istate->js_reference, state, title); 54 | return state; 55 | } 56 | 57 | debugger_state_t *openti_new_state(debugger_t *debugger, int ref) { 58 | debugger_state_t *state = calloc(sizeof(debugger_state_t), 1); 59 | state->print = openti_state_print; 60 | state->vprint = openti_state_vprint; 61 | state->debugger = debugger; 62 | state->asic = debugger->asic; 63 | state->log = state->asic->log; 64 | state->create_new_state = openti_create_new_state; 65 | state->close_window = openti_close_window; 66 | 67 | openti_interface_state *istate = state->interface_state = calloc(sizeof(openti_interface_state), 1); 68 | istate->js_reference = ref; 69 | 70 | state->interface_state = istate; 71 | return state; 72 | } 73 | -------------------------------------------------------------------------------- /frontends/sdl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../CMake) 2 | find_package(SDL2 REQUIRED) 3 | find_package(Scas REQUIRED) 4 | 5 | add_executable(z80e-sdl 6 | main.c 7 | tui.c 8 | $ 9 | ) 10 | 11 | target_include_directories(z80e-sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include) 12 | TARGET_LINK_LIBRARIES(z80e-sdl readline SDL2 ${SCAS_LIBRARIES}) 13 | 14 | if(NOT APPLE AND NOT HAIKU) 15 | TARGET_LINK_LIBRARIES(z80e-sdl rt) 16 | endif() 17 | 18 | INSTALL(TARGETS z80e-sdl DESTINATION bin) 19 | -------------------------------------------------------------------------------- /frontends/sdl/tui.c: -------------------------------------------------------------------------------- 1 | #include "tui.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | int print_tui(struct debugger_state *a, const char *b, ...) { 16 | va_list list; 17 | va_start(list, b); 18 | return vprintf(b, list); 19 | } 20 | 21 | int vprint_tui(struct debugger_state *a, const char *b, va_list list) { 22 | return vprintf(b, list); 23 | } 24 | 25 | void tui_close_window(struct debugger_state *state) { 26 | free(state); 27 | } 28 | 29 | debugger_state_t *tui_new_state(struct debugger_state *state, const char *title) { 30 | debugger_state_t *stat = calloc(sizeof(debugger_state_t), 1); 31 | stat->print = print_tui; 32 | stat->vprint = vprint_tui; 33 | stat->state = 0; 34 | stat->interface_state = state->interface_state; 35 | stat->asic = state->asic; 36 | stat->debugger = state->debugger; 37 | stat->create_new_state = tui_new_state; 38 | stat->close_window = tui_close_window; 39 | stat->log = state->log; 40 | init_link(stat); 41 | return stat; 42 | } 43 | 44 | tui_state_t *current_state; 45 | 46 | #define dprint(...) printf(__VA_ARGS__) 47 | void tui_init(tui_state_t *state) { 48 | debugger_state_t dstate = { print_tui, vprint_tui, 0, state, state->debugger->asic, state->debugger, tui_new_state, tui_close_window, state->debugger->asic->log}; 49 | debugger_state_t *used_state = tui_new_state(&dstate, "Sourcing z80erc..."); 50 | log_message(dstate.log, L_DEBUG, "TUI", "Running commands in z80erc..."); 51 | debugger_source_rc(used_state, "z80erc"); 52 | tui_close_window(used_state); 53 | } 54 | 55 | struct tui_disasm { 56 | ti_mmu_t *mmu; 57 | char *string_pointer; 58 | }; 59 | 60 | uint8_t tui_disassemble_read(struct disassemble_memory *state, uint16_t mem) { 61 | struct tui_disasm *disasm = state->extra_data; 62 | return ti_read_byte(disasm->mmu, mem); 63 | } 64 | 65 | int tui_disassemble_write(struct disassemble_memory *state, const char *format, ...) { 66 | struct tui_disasm *disasm = state->extra_data; 67 | va_list list; 68 | va_start(list, format); 69 | int count = vsprintf(disasm->string_pointer, format, list); 70 | disasm->string_pointer += count; 71 | return count; 72 | } 73 | 74 | void tui_tick(tui_state_t *state) { 75 | current_state = state; 76 | asic_t *asic = state->debugger->asic; 77 | struct tui_disasm disasm_custom = { asic->mmu, 0 }; 78 | struct disassemble_memory disasm = { tui_disassemble_read, 0, &disasm_custom }; 79 | while (1) { 80 | char prompt_buffer[80]; 81 | char *current_pointer = prompt_buffer; 82 | ti_mmu_bank_state_t *st = &asic->mmu->banks[asic->cpu->registers.PC / 0x4000]; 83 | current_pointer += sprintf(prompt_buffer, "z80e [%c:%02X:0x%04X ", st->flash ? 'F' : 'R', st->page, asic->cpu->registers.PC); 84 | 85 | disasm_custom.string_pointer = current_pointer; 86 | disasm.current = asic->cpu->registers.PC; 87 | parse_instruction(&disasm, tui_disassemble_write, state->debugger->flags.knightos); 88 | current_pointer = disasm_custom.string_pointer; 89 | 90 | sprintf(current_pointer, "] %s> ", asic->cpu->halted ? "HALT " : ""); 91 | char *result = readline(prompt_buffer); 92 | if (result) { 93 | int from_history = 0; 94 | 95 | if (*result == 0) { 96 | HIST_ENTRY *hist = history_get(where_history()); 97 | if (hist == 0) { 98 | free(result); 99 | continue; 100 | } 101 | result = (char *)hist->line; 102 | from_history = 1; 103 | } 104 | if (strcmp(result, "exit") == 0) { 105 | break; 106 | } 107 | 108 | add_history(result); 109 | 110 | debugger_state_t dstate = { print_tui, vprint_tui, 0, state, asic, state->debugger, tui_new_state, tui_close_window, asic->log }; 111 | debugger_state_t *used_state = tui_new_state(&dstate, result); 112 | 113 | int retval = debugger_exec(used_state, result); 114 | if (retval > 0) { 115 | dprint("The command returned %d\n", retval); 116 | } 117 | 118 | tui_close_window(used_state); 119 | 120 | if (!from_history) { 121 | free(result); 122 | } 123 | } else if (result == NULL) { 124 | break; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /frontends/sdl/tui.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_TUI_H 2 | #define DEBUGGER_TUI_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | debugger_t *debugger; 9 | } tui_state_t; 10 | 11 | void tui_init(tui_state_t *state); 12 | void tui_tick(tui_state_t *state); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /frontends/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../CMake) 2 | find_package(Scas REQUIRED) 3 | 4 | add_executable(tests 5 | test.c 6 | $ 7 | ) 8 | 9 | target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include) 10 | 11 | if(NOT EMSCRIPTEN AND NOT HAIKU AND NOT APPLE) 12 | target_link_libraries(tests rt) 13 | endif() 14 | 15 | TARGET_LINK_LIBRARIES(tests ${SCAS_LIBRARIES}) 16 | -------------------------------------------------------------------------------- /frontends/test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void flash(asic_t *device, const uint8_t *data, size_t length); 17 | 18 | #include "tests/alu.c" 19 | #include "tests/control.c" 20 | #include "tests/debugger.c" 21 | #include "tests/disassembler.c" 22 | #include "tests/load.c" 23 | #include "tests/arithmetic.c" 24 | #include "tests/shifts.c" 25 | #include "tests/io.c" 26 | #include "tests/index.c" 27 | #include "tests/performance.c" 28 | #include "tests/block.c" 29 | #include "tests/interrupts.c" 30 | #include "tests/devices.c" 31 | 32 | typedef struct { 33 | int (*execute)(void); 34 | char *name; 35 | } test_t; 36 | 37 | const test_t tests[] = { 38 | { test_performance, "performance" }, 39 | { NULL, "Instruction tests" }, 40 | { test_ADD_A_r, "ADD A, r" }, 41 | { test_ADC_A_r, "ADC A, r" }, 42 | { test_SUB_A_r, "SUB A, r" }, 43 | { test_SBC_A_r, "SBC A, r" }, 44 | { test_AND_A_r, "AND A, r" }, 45 | { test_XOR_A_r, "XOR A, r" }, 46 | { test_OR_A_r, "OR A, r" }, 47 | { test_CP_r, "CP r" }, 48 | { test_RST, "RST y" }, 49 | { test_DJNZ, "DJNZ d" }, 50 | { test_JR, "JR d" }, 51 | { test_JR_cc, "JR cc, d" }, 52 | { test_LD_rp_nn, "LD rp, nn" }, 53 | { test_ADD_HL_rp, "ADD HL, rp" }, 54 | { test_LD_BCptr_A, "LD (BC), A" }, 55 | { test_LD_nnptr_HL, "LD (nn), HL" }, 56 | { test_LD_nnptr_A, "LD (nn), A" }, 57 | { test_LD_HL_nnptr, "LD HL, (nn)" }, 58 | { test_LD_A_nnptr, "LD A, (nn)" }, 59 | { test_INC_rp, "INC rp" }, 60 | { test_INC_r, "INC r" }, 61 | { test_DEC_rp, "DEC rp" }, 62 | { test_DEC_r, "DEC r" }, 63 | { test_LD_r_n, "LD r, n" }, 64 | { test_RLCA, "RLCA" }, 65 | { test_RRCA, "RRCA" }, 66 | { test_RLA, "RLA" }, 67 | { test_RRA, "RRA" }, 68 | { test_CPL, "CPL" }, 69 | { test_DAA, "DAA" }, 70 | { test_LD_r_r, "LD r, r" }, 71 | { test_POP_rp2, "POP rp2" }, 72 | { test_RET_cc, "RET cc" }, 73 | { test_RET, "RET" }, 74 | { test_EXX, "EXX" }, 75 | { test_JP_HL, "JP (HL)" }, 76 | { test_LD_SP_HL, "LD SP, HL" }, 77 | { test_JP_cc_nn, "JP cc, nn" }, 78 | { test_JP_nn, "JP nn" }, 79 | { test_OUT_n_A, "OUT (n), A" }, 80 | { test_IN_A_n, "IN A, (n)" }, 81 | { test_EX_SP_HL, "EX (SP), HL" }, 82 | { test_EX_DE_HL, "EX DE, HL" }, 83 | { test_CALL_cc_nn, "CALL cc, nn" }, 84 | { test_PUSH_rp2, "PUSH rp2" }, 85 | { test_CALL_nn, "CALL nn" }, 86 | { test_alu_n, "alu[y] n" }, 87 | { test_IX_IY, "LD (IX,IY), A / LD A, (IX,IY)" }, 88 | { test_JP_IX__JP_IY, "JP IX / JP IY" }, 89 | { test_ADD_IX_rp, "ADD IX, rp" }, 90 | { test_prefix_reset, "prefix reset" }, 91 | { test_index_offsets, "index offsets" }, 92 | { test_ixh_ixl, "IXH/IXL" }, 93 | { test_IM_set, "interrupt mode set" }, 94 | { test_RLC, "RLC r" }, 95 | { test_RRC, "RRC r" }, 96 | { test_RL, "RL r" }, 97 | { test_RR, "RR r" }, 98 | { test_SLA, "SLA r" }, 99 | { test_SRA, "SRA r" }, 100 | { test_SLL, "SLL r" }, 101 | { test_SRL, "SRL r" }, 102 | { test_BIT, "BIT y, r" }, 103 | { test_RES, "RES y, r" }, 104 | { test_SET, "SET y, r" }, 105 | { test_EI_DI, "EI/DI" }, 106 | { test_NEG, "NEG" }, 107 | { test_IN_C, "IN (C)" }, 108 | { test_IN_r_C, "IN r, (C)" }, 109 | { test_OUT_C_0, "OUT (C), 0" }, 110 | { test_OUT_C_r, "OUT (C), r" }, 111 | { test_SBC_HL_rp, "SBC HL, rp" }, 112 | { test_ADC_HL_rp, "ADC HL, rp" }, 113 | { test_LD_nn_rp, "LD (nn), rp" }, 114 | { test_LD_rp_nn_ind, "LD rp, (nn)" }, 115 | { test_LD_A_I, "LD A, I" }, 116 | { test_RRD_RLD, "RRD/RLD" }, 117 | { test_LDI, "LDI" }, 118 | { test_LDIR, "LDIR" }, 119 | { test_LDD, "LDD" }, 120 | { test_LDDR, "LDDR" }, 121 | { test_CPI, "CPI" }, 122 | { test_CPD, "CPD" }, 123 | { test_CPIR, "CPIR" }, 124 | { test_CPDR, "CPDR" }, 125 | { test_INI, "INI" }, 126 | { test_IND, "IND" }, 127 | { test_INIR, "INIR" }, 128 | { test_INDR, "INDR" }, 129 | { test_OUTI, "OUTI" }, 130 | { test_OUTD, "OUTD" }, 131 | { test_OTIR, "OTIR" }, 132 | { test_OTDR, "OTDR" }, 133 | { NULL, "Interrupt tests" }, 134 | { test_IM_1, "mode 1 interrupts" }, 135 | { test_IM_2, "mode 2 interrupts" }, 136 | { NULL, "Port tests" }, 137 | { test_keyboard, "port 0x01 (keyboard)" }, 138 | { test_status, "port 0x02 (status)" }, 139 | { test_memorymapping_83p, "port 0x04, 0x05, 0x06, 0x07 (mapping, TI-83+)" }, 140 | { test_memorymapping_others, "port 0x04, 0x05, 0x06, 0x07 (mapping, Others)" }, 141 | { test_link_port, "port 0x00 (link port)" }, 142 | { test_link_assist_rx, "port 0x0A (link assist rx)" }, 143 | { test_link_assist_tx, "port 0x0D (link assist tx)" }, 144 | { NULL, "Debugger tests" }, 145 | { test_debugger_init, "debugger_init()" }, 146 | { test_debugger_find_command, "find_best_command()" }, 147 | { test_debugger_commandline, "Commandline parser" }, 148 | { test_disassembler, "Disassembler" }, 149 | }; 150 | 151 | int run_one(char *test) { 152 | const int min_width = 50; 153 | int i; 154 | for (i = 0; i < sizeof(tests) / sizeof(test_t); i++) { 155 | if (strcmp(test, tests[i].name) == 0) { 156 | if (tests[i].execute == NULL) { 157 | int length = (min_width + 9) - strlen(tests[i].name); 158 | while (length--) putchar('.'); 159 | printf("%s....\n", tests[i].name); 160 | continue; 161 | } 162 | printf("Testing %s ", tests[i].name); 163 | int length = min_width - strlen(tests[i].name); 164 | while (length--) printf("."); 165 | int result = tests[i].execute(); 166 | if (result > 0) { 167 | printf("FAIL %d\n", result); 168 | return result; 169 | } else if (result == -1) { 170 | // Silent test 171 | return 0; 172 | } else { 173 | printf("PASS\n"); 174 | return 0; 175 | } 176 | } 177 | } 178 | fprintf(stderr, "Error: \"%s\" not found.\n", test); 179 | return 1; 180 | } 181 | 182 | int run_all() { 183 | int passed = 0, failed = 0; 184 | int i; 185 | const int min_width = 50; 186 | for (i = 0; i < sizeof(tests) / sizeof(test_t); i++) { 187 | if (tests[i].execute == NULL) { 188 | int length = (min_width + 9) - strlen(tests[i].name); 189 | while (length--) putchar('.'); 190 | printf("%s....\n", tests[i].name); 191 | continue; 192 | } 193 | printf("Testing %s ", tests[i].name); 194 | int length = min_width - strlen(tests[i].name); 195 | while (length--) printf("."); 196 | int result = tests[i].execute(); 197 | if (result > 0) { 198 | printf("FAIL %d\n", result); 199 | failed++; 200 | } else if (result == -1) { 201 | // Silent test 202 | } else { 203 | printf("PASS\n"); 204 | passed++; 205 | } 206 | } 207 | printf("%d passed, %d failed\n", passed, failed); 208 | return failed; 209 | } 210 | 211 | int main(int argc, char **argv) { 212 | if (argc == 2) { 213 | return run_one(argv[1]); 214 | } else { 215 | return run_all(); 216 | } 217 | } 218 | 219 | void flash(asic_t *device, const uint8_t *data, size_t length) { 220 | int i; 221 | for (i = 0; i < length; i++) { 222 | device->mmu->flash[i] = data[i]; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /frontends/test/tests/alu.c: -------------------------------------------------------------------------------- 1 | int test_ADD_A_r() { 2 | uint8_t test[] = { 0x80 }; // ADD A, B 3 | uint8_t test_hl[] = { 0x86 }; // ADD A, (HL) 4 | 5 | asic_t *device = asic_init(TI83p, NULL); 6 | device->cpu->registers.A = 0x10; 7 | device->cpu->registers.B = 0x20; 8 | flash(device, test, sizeof(test)); 9 | int cycles = cpu_execute(device->cpu, 4); 10 | if (device->cpu->registers.A != 0x30 || 11 | device->cpu->registers.B != 0x20 || 12 | device->cpu->registers.flags.Z != 0 || 13 | device->cpu->registers.flags.C != 0 || 14 | cycles != 0) { 15 | asic_free(device); 16 | return 1; 17 | } 18 | asic_free(device); 19 | device = asic_init(TI83p, NULL); 20 | device->cpu->registers.A = 0xF0; 21 | device->cpu->registers.B = 0x20; 22 | flash(device, test, sizeof(test)); 23 | cycles = cpu_execute(device->cpu, 4); 24 | if (device->cpu->registers.A != 0x10 || 25 | device->cpu->registers.flags.Z != 0 || 26 | device->cpu->registers.flags.C != 1 || 27 | cycles != 0) { 28 | asic_free(device); 29 | return 1; 30 | } 31 | asic_free(device); 32 | device = asic_init(TI83p, NULL); 33 | device->cpu->registers.A = 0xF0; 34 | device->cpu->registers.B = 0x10; 35 | flash(device, test, sizeof(test)); 36 | cycles = cpu_execute(device->cpu, 4); 37 | if (device->cpu->registers.A != 0 || 38 | device->cpu->registers.flags.Z != 1 || 39 | device->cpu->registers.flags.C != 1 || 40 | cycles != 0) { 41 | asic_free(device); 42 | return 1; 43 | } 44 | asic_free(device); 45 | device = asic_init(TI83p, NULL); 46 | device->cpu->registers.A = 0x10; 47 | device->cpu->registers.HL = 0x1000; 48 | mmu_force_write(device->mmu, 0x1000, 0x20); 49 | flash(device, test_hl, sizeof(test_hl)); 50 | cycles = cpu_execute(device->cpu, 7); 51 | if (device->cpu->registers.A != 0x30 || cycles != 0) { 52 | asic_free(device); 53 | return 1; 54 | } 55 | asic_free(device); 56 | return 0; 57 | } 58 | 59 | int test_ADC_A_r() { 60 | asic_t *device = asic_init(TI83p, NULL); 61 | uint8_t test[] = { 0x88 }; // ADC A, B 62 | device->cpu->registers.A = 0x10; 63 | device->cpu->registers.B = 0x20; 64 | flash(device, test, sizeof(test)); 65 | int cycles = cpu_execute(device->cpu, 4); 66 | if (device->cpu->registers.A != 0x30 || 67 | device->cpu->registers.B != 0x20 || 68 | device->cpu->registers.flags.Z != 0 || 69 | device->cpu->registers.flags.C != 0 || 70 | cycles != 0) { 71 | asic_free(device); 72 | return 1; 73 | } 74 | asic_free(device); 75 | device = asic_init(TI83p, NULL); 76 | device->cpu->registers.A = 0x10; 77 | device->cpu->registers.B = 0x20; 78 | device->cpu->registers.flags.C = 1; 79 | flash(device, test, sizeof(test)); 80 | cycles = cpu_execute(device->cpu, 4); 81 | if (device->cpu->registers.A != 0x31 || 82 | device->cpu->registers.B != 0x20 || 83 | device->cpu->registers.flags.Z != 0 || 84 | device->cpu->registers.flags.C != 0 || 85 | cycles != 0) { 86 | asic_free(device); 87 | return 1; 88 | } 89 | asic_free(device); 90 | return 0; 91 | } 92 | 93 | int test_SUB_A_r() { 94 | asic_t *device = asic_init(TI83p, NULL); 95 | uint8_t test[] = { 0x90 }; // SUB A, B 96 | device->cpu->registers.A = 0x20; 97 | device->cpu->registers.B = 0x10; 98 | flash(device, test, sizeof(test)); 99 | int cycles = cpu_execute(device->cpu, 4); 100 | if (device->cpu->registers.A != 0x10 || 101 | device->cpu->registers.B != 0x10 || 102 | device->cpu->registers.flags.Z != 0 || 103 | device->cpu->registers.flags.C != 0 || 104 | cycles != 0) { 105 | asic_free(device); 106 | return 1; 107 | } 108 | asic_free(device); 109 | device = asic_init(TI83p, NULL); 110 | device->cpu->registers.A = 0x10; 111 | device->cpu->registers.B = 0x20; 112 | flash(device, test, sizeof(test)); 113 | cycles = cpu_execute(device->cpu, 4); 114 | if (device->cpu->registers.A != 0xF0 || 115 | device->cpu->registers.B != 0x20 || 116 | device->cpu->registers.flags.Z != 0 || 117 | device->cpu->registers.flags.C != 1 || 118 | cycles != 0) { 119 | asic_free(device); 120 | return 1; 121 | } 122 | asic_free(device); 123 | return 0; 124 | } 125 | 126 | int test_SBC_A_r() { 127 | asic_t *device = asic_init(TI83p, NULL); 128 | uint8_t test[] = { 0x98 }; // SBC A, B 129 | device->cpu->registers.A = 0x20; 130 | device->cpu->registers.B = 0x10; 131 | flash(device, test, sizeof(test)); 132 | int cycles = cpu_execute(device->cpu, 4); 133 | if (device->cpu->registers.A != 0x10 || 134 | device->cpu->registers.B != 0x10 || 135 | device->cpu->registers.flags.Z != 0 || 136 | device->cpu->registers.flags.C != 0 || 137 | cycles != 0) { 138 | asic_free(device); 139 | return 1; 140 | } 141 | asic_free(device); 142 | device = asic_init(TI83p, NULL); 143 | device->cpu->registers.A = 0x10; 144 | device->cpu->registers.B = 0x20; 145 | device->cpu->registers.flags.C = 1; 146 | flash(device, test, sizeof(test)); 147 | cycles = cpu_execute(device->cpu, 4); 148 | if (device->cpu->registers.A != 0xEF || 149 | device->cpu->registers.B != 0x20 || 150 | device->cpu->registers.flags.Z != 0 || 151 | device->cpu->registers.flags.C != 1 || 152 | cycles != 0) { 153 | asic_free(device); 154 | return 1; 155 | } 156 | asic_free(device); 157 | return 0; 158 | } 159 | 160 | int test_AND_A_r() { 161 | asic_t *device = asic_init(TI83p, NULL); 162 | uint8_t test[] = { 0xA0 }; // AND A, B 163 | device->cpu->registers.A = 0xFF; 164 | device->cpu->registers.B = 0x0F; 165 | flash(device, test, sizeof(test)); 166 | int cycles = cpu_execute(device->cpu, 4); 167 | if (device->cpu->registers.A != 0x0F || 168 | device->cpu->registers.flags.Z != 0 || 169 | cycles != 0) { 170 | asic_free(device); 171 | return 1; 172 | } 173 | asic_free(device); 174 | return 0; 175 | } 176 | 177 | int test_XOR_A_r() { 178 | asic_t *device = asic_init(TI83p, NULL); 179 | uint8_t test[] = { 0xA8 }; // XOR A, B 180 | device->cpu->registers.A = 0xFF; 181 | device->cpu->registers.B = 0x0F; 182 | flash(device, test, sizeof(test)); 183 | int cycles = cpu_execute(device->cpu, 4); 184 | if (device->cpu->registers.A != 0xF0 || 185 | device->cpu->registers.flags.Z != 0 || 186 | cycles != 0) { 187 | asic_free(device); 188 | return 1; 189 | } 190 | asic_free(device); 191 | return 0; 192 | } 193 | 194 | int test_OR_A_r() { 195 | asic_t *device = asic_init(TI83p, NULL); 196 | uint8_t test[] = { 0xB0 }; // OR A, B 197 | device->cpu->registers.A = 0x00; 198 | device->cpu->registers.B = 0x0F; 199 | flash(device, test, sizeof(test)); 200 | int cycles = cpu_execute(device->cpu, 4); 201 | if (device->cpu->registers.A != 0x0F || 202 | device->cpu->registers.flags.Z != 0 || 203 | cycles != 0) { 204 | asic_free(device); 205 | return 1; 206 | } 207 | asic_free(device); 208 | return 0; 209 | } 210 | 211 | int test_CP_r() { 212 | asic_t *device = asic_init(TI83p, NULL); 213 | uint8_t test[] = { 0xB8 }; // CP B 214 | device->cpu->registers.A = 0x00; 215 | device->cpu->registers.B = 0x10; 216 | flash(device, test, sizeof(test)); 217 | int cycles = cpu_execute(device->cpu, 4); 218 | if (device->cpu->registers.A != 0 || 219 | device->cpu->registers.flags.S != 1 || 220 | device->cpu->registers.flags.C != 1 || 221 | device->cpu->registers.flags.PV != 0 || 222 | device->cpu->registers.flags.N != 1 || 223 | device->cpu->registers.flags.Z != 0 || 224 | cycles != 0) { 225 | print_state(&device->cpu->registers); 226 | asic_free(device); 227 | return 1; 228 | } 229 | asic_free(device); 230 | return 0; 231 | } 232 | 233 | int test_alu_n() { 234 | asic_t *device = asic_init(TI83p, NULL); 235 | uint8_t test[] = { 0xC6, 0x20 }; // ADD A, 0x20 236 | device->cpu->registers.A = 0x10; 237 | flash(device, test, sizeof(test)); 238 | int cycles = cpu_execute(device->cpu, 4); 239 | if (device->cpu->registers.A != 0x30 || 240 | cycles != 0) { 241 | asic_free(device); 242 | return 1; 243 | } 244 | asic_free(device); 245 | return 0; 246 | } 247 | -------------------------------------------------------------------------------- /frontends/test/tests/arithmetic.c: -------------------------------------------------------------------------------- 1 | int test_ADD_HL_rp() { 2 | asic_t *device = asic_init(TI83p, NULL); 3 | uint8_t test[] = { 0x09 }; // ADD HL, BC 4 | device->cpu->registers.HL = 0x1000; 5 | device->cpu->registers.BC = 0x0234; 6 | flash(device, test, sizeof(test)); 7 | int cycles = cpu_execute(device->cpu, 11); 8 | if (device->cpu->registers.HL != 0x1234 || 9 | device->cpu->registers.flags.Z != 0 || 10 | device->cpu->registers.flags.C != 0 || 11 | device->cpu->registers.flags.H != 0 || 12 | cycles != 0) { 13 | asic_free(device); 14 | return 1; 15 | } 16 | asic_free(device); 17 | 18 | device = asic_init(TI83p, NULL); 19 | uint8_t test2[] = { 0xED, 0x4A }; // ADC HL, BC 20 | device->cpu->registers.HL = 0xFF00; 21 | device->cpu->registers.BC = 0x1000; 22 | device->cpu->registers.flags.C = 0; 23 | flash(device, test2, sizeof(test2)); 24 | cycles = cpu_execute(device->cpu, 15); 25 | if (device->cpu->registers.HL != 0x0F00 || 26 | device->cpu->registers.flags.Z != 0 || 27 | device->cpu->registers.flags.H != 0 || 28 | device->cpu->registers.flags.PV != 0 || 29 | device->cpu->registers.flags.C != 1 || 30 | cycles != 0) { 31 | asic_free(device); 32 | return 1; 33 | } 34 | asic_free(device); 35 | return 0; 36 | } 37 | 38 | int test_INC_rp() { 39 | asic_t *device = asic_init(TI83p, NULL); 40 | uint8_t test[] = { 0x23 }; // INC HL 41 | device->cpu->registers.HL = 0xFFFF; 42 | flash(device, test, sizeof(test)); 43 | int cycles = cpu_execute(device->cpu, 6); 44 | if (device->cpu->registers.HL != 0 || 45 | device->cpu->registers.flags.Z != 0 || 46 | device->cpu->registers.flags.C != 0 || 47 | cycles != 0) { 48 | asic_free(device); 49 | return 1; 50 | } 51 | asic_free(device); 52 | return 0; 53 | } 54 | 55 | int test_INC_r() { 56 | asic_t *device = asic_init(TI83p, NULL); 57 | uint8_t test[] = { 0x3C }; // INC A 58 | device->cpu->registers.A = 0xFF; 59 | flash(device, test, sizeof(test)); 60 | int cycles = cpu_execute(device->cpu, 4); 61 | if (device->cpu->registers.HL != 0 || 62 | device->cpu->registers.flags.Z != 1 || 63 | device->cpu->registers.flags.C != 0 || 64 | cycles != 0) { 65 | asic_free(device); 66 | return 1; 67 | } 68 | asic_free(device); 69 | return 0; 70 | } 71 | 72 | int test_DEC_rp() { 73 | asic_t *device = asic_init(TI83p, NULL); 74 | uint8_t test[] = { 0x2B }; // DEC HL 75 | device->cpu->registers.HL = 1; 76 | flash(device, test, sizeof(test)); 77 | int cycles = cpu_execute(device->cpu, 6); 78 | if (device->cpu->registers.HL != 0 || 79 | device->cpu->registers.flags.Z != 0 || 80 | device->cpu->registers.flags.C != 0 || 81 | cycles != 0) { 82 | asic_free(device); 83 | return 1; 84 | } 85 | asic_free(device); 86 | return 0; 87 | } 88 | 89 | int test_DEC_r() { 90 | asic_t *device = asic_init(TI83p, NULL); 91 | uint8_t test[] = { 0x3D }; // INC A 92 | device->cpu->registers.A = 1; 93 | flash(device, test, sizeof(test)); 94 | int cycles = cpu_execute(device->cpu, 4); 95 | if (device->cpu->registers.HL != 0 || 96 | device->cpu->registers.flags.Z != 1 || 97 | device->cpu->registers.flags.C != 0 || 98 | cycles != 0) { 99 | asic_free(device); 100 | return 1; 101 | } 102 | asic_free(device); 103 | return 0; 104 | } 105 | 106 | int test_CPL() { 107 | asic_t *device = asic_init(TI83p, NULL); 108 | uint8_t test[] = { 0x2F }; // CPL 109 | device->cpu->registers.A = 0x80; 110 | flash(device, test, sizeof(test)); 111 | int cycles = cpu_execute(device->cpu, 4); 112 | if (device->cpu->registers.A != 0x7F || 113 | cycles != 0) { 114 | asic_free(device); 115 | return 1; 116 | } 117 | asic_free(device); 118 | return 0; 119 | } 120 | 121 | int test_DAA() { // TODO: This could be more comprehensive 122 | asic_t *device = asic_init(TI83p, NULL); 123 | uint8_t test[] = { 0x80, 0x27 }; // ADD A, B \ DAA 124 | device->cpu->registers.A = 0x15; 125 | device->cpu->registers.B = 0x27; 126 | flash(device, test, sizeof(test)); 127 | cpu_execute(device->cpu, 1); 128 | int cycles = cpu_execute(device->cpu, 4); 129 | if (device->cpu->registers.A != 0x42 || 130 | cycles != 0) { 131 | asic_free(device); 132 | return 1; 133 | } 134 | asic_free(device); 135 | return 0; 136 | } 137 | 138 | int test_BIT() { 139 | asic_t *device = asic_init(TI83p, NULL); 140 | uint8_t test[] = { 0xCB, 0x78 }; // BIT 7, B 141 | device->cpu->registers.B = 0x80; 142 | flash(device, test, sizeof(test)); 143 | int cycles = cpu_execute(device->cpu, 8); 144 | if (device->cpu->registers.flags.Z == 1 || 145 | cycles != 0) { 146 | asic_free(device); 147 | return 1; 148 | } 149 | asic_free(device); 150 | return 0; 151 | } 152 | 153 | int test_RES() { 154 | asic_t *device = asic_init(TI83p, NULL); 155 | uint8_t test[] = { 0xCB, 0xB8 }; // RES 7, B 156 | device->cpu->registers.B = 0xFF; 157 | flash(device, test, sizeof(test)); 158 | int cycles = cpu_execute(device->cpu, 8); 159 | if (device->cpu->registers.B != 0x7F || 160 | cycles != 0) { 161 | asic_free(device); 162 | return 1; 163 | } 164 | asic_free(device); 165 | return 0; 166 | } 167 | 168 | int test_SET() { 169 | asic_t *device = asic_init(TI83p, NULL); 170 | uint8_t test[] = { 0xCB, 0xF8 }; // SET 7, B 171 | device->cpu->registers.B = 0; 172 | flash(device, test, sizeof(test)); 173 | int cycles = cpu_execute(device->cpu, 8); 174 | if (device->cpu->registers.B != 0x80 || 175 | cycles != 0) { 176 | asic_free(device); 177 | return 1; 178 | } 179 | asic_free(device); 180 | return 0; 181 | } 182 | 183 | int test_NEG() { 184 | asic_t *device = asic_init(TI83p, NULL); 185 | uint8_t test[] = { 0xED, 0x44 }; // NEG 186 | device->cpu->registers.A = 2; 187 | flash(device, test, sizeof(test)); 188 | int cycles = cpu_execute(device->cpu, 8); 189 | if (device->cpu->registers.A != 0xFE || 190 | cycles != 0) { 191 | asic_free(device); 192 | return 1; 193 | } 194 | asic_free(device); 195 | return 0; 196 | } 197 | 198 | int test_SBC_HL_rp() { 199 | asic_t *device = asic_init(TI83p, NULL); 200 | uint8_t test[] = { 0xED, 0x42 }; // SBC HL, BC 201 | device->cpu->registers.HL = 0x4000; 202 | device->cpu->registers.BC = 0x100; 203 | flash(device, test, sizeof(test)); 204 | int cycles = cpu_execute(device->cpu, 15); 205 | if (device->cpu->registers.HL != 0x3F00 || 206 | device->cpu->registers.BC != 0x100 || 207 | device->cpu->registers.flags.Z != 0 || 208 | device->cpu->registers.flags.C != 0 || 209 | cycles != 0) { 210 | asic_free(device); 211 | return 1; 212 | } 213 | asic_free(device); 214 | device = asic_init(TI83p, NULL); 215 | device->cpu->registers.HL = 0x1000; 216 | device->cpu->registers.BC = 0x2000; 217 | device->cpu->registers.flags.C = 1; 218 | flash(device, test, sizeof(test)); 219 | cycles = cpu_execute(device->cpu, 15); 220 | if (device->cpu->registers.HL != 0xEFFF || 221 | device->cpu->registers.BC != 0x2000 || 222 | device->cpu->registers.flags.Z != 0 || 223 | device->cpu->registers.flags.C != 1 || 224 | cycles != 0) { 225 | asic_free(device); 226 | return 2; 227 | } 228 | asic_free(device); 229 | return 0; 230 | } 231 | 232 | int test_ADC_HL_rp() { 233 | asic_t *device = asic_init(TI83p, NULL); 234 | uint8_t test[] = { 0xED, 0x4A }; // ADC HL, BC 235 | device->cpu->registers.HL = 0x4000; 236 | device->cpu->registers.BC = 0x100; 237 | flash(device, test, sizeof(test)); 238 | int cycles = cpu_execute(device->cpu, 15); 239 | if (device->cpu->registers.HL != 0x4100 || 240 | device->cpu->registers.BC != 0x100 || 241 | device->cpu->registers.flags.Z != 0 || 242 | device->cpu->registers.flags.C != 0 || 243 | cycles != 0) { 244 | asic_free(device); 245 | return 1; 246 | } 247 | asic_free(device); 248 | device = asic_init(TI83p, NULL); 249 | device->cpu->registers.HL = 0xF000; 250 | device->cpu->registers.BC = 0x2000; 251 | device->cpu->registers.flags.C = 1; 252 | flash(device, test, sizeof(test)); 253 | cycles = cpu_execute(device->cpu, 15); 254 | if (device->cpu->registers.HL != 0x1001 || 255 | device->cpu->registers.BC != 0x2000 || 256 | device->cpu->registers.flags.Z != 0 || 257 | device->cpu->registers.flags.C != 1 || 258 | cycles != 0) { 259 | asic_free(device); 260 | return 2; 261 | } 262 | asic_free(device); 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /frontends/test/tests/block.c: -------------------------------------------------------------------------------- 1 | int test_LDI() { 2 | asic_t *device = asic_init(TI83p, NULL); 3 | uint8_t test[] = { 0xED, 0xA0 }; // LDI 4 | device->cpu->registers.HL = 0xC000; 5 | device->cpu->registers.DE = 0xD000; 6 | device->cpu->registers.BC = 5; 7 | cpu_write_byte(device->cpu, 0xC000, 0x11); 8 | cpu_write_byte(device->cpu, 0xC001, 0x22); 9 | cpu_write_byte(device->cpu, 0xC002, 0x33); 10 | cpu_write_byte(device->cpu, 0xC003, 0x44); 11 | cpu_write_byte(device->cpu, 0xC004, 0x55); 12 | flash(device, test, sizeof(test)); 13 | int cycles = cpu_execute(device->cpu, 16); 14 | if (cpu_read_byte(device->cpu, 0xD000) != 0x11 || 15 | device->cpu->registers.HL != 0xC001 || 16 | device->cpu->registers.DE != 0xD001 || 17 | device->cpu->registers.BC != 4 || 18 | cycles != 0) { 19 | asic_free(device); 20 | return 1; 21 | } 22 | asic_free(device); 23 | return 0; 24 | } 25 | 26 | int test_LDIR() { 27 | asic_t *device = asic_init(TI83p, NULL); 28 | uint8_t test[] = { 0xED, 0xB0 }; // LDIR 29 | device->cpu->registers.HL = 0xC000; 30 | device->cpu->registers.DE = 0xD000; 31 | device->cpu->registers.BC = 5; 32 | cpu_write_byte(device->cpu, 0xC000, 0x11); 33 | cpu_write_byte(device->cpu, 0xC001, 0x22); 34 | cpu_write_byte(device->cpu, 0xC002, 0x33); 35 | cpu_write_byte(device->cpu, 0xC003, 0x44); 36 | cpu_write_byte(device->cpu, 0xC004, 0x55); 37 | flash(device, test, sizeof(test)); 38 | int cycles = cpu_execute(device->cpu, 100); 39 | if (cpu_read_byte(device->cpu, 0xD000) != 0x11 || 40 | cpu_read_byte(device->cpu, 0xD001) != 0x22 || 41 | cpu_read_byte(device->cpu, 0xD002) != 0x33 || 42 | cpu_read_byte(device->cpu, 0xD003) != 0x44 || 43 | cpu_read_byte(device->cpu, 0xD004) != 0x55 || 44 | device->cpu->registers.HL != 0xC005 || 45 | device->cpu->registers.DE != 0xD005 || 46 | device->cpu->registers.BC != 0 || 47 | cycles != 0) { 48 | asic_free(device); 49 | return 1; 50 | } 51 | asic_free(device); 52 | return 0; 53 | } 54 | 55 | int test_LDD() { 56 | asic_t *device = asic_init(TI83p, NULL); 57 | uint8_t test[] = { 0xED, 0xA8 }; // LDD 58 | device->cpu->registers.HL = 0xC004; 59 | device->cpu->registers.DE = 0xD004; 60 | device->cpu->registers.BC = 5; 61 | cpu_write_byte(device->cpu, 0xC000, 0x11); 62 | cpu_write_byte(device->cpu, 0xC001, 0x22); 63 | cpu_write_byte(device->cpu, 0xC002, 0x33); 64 | cpu_write_byte(device->cpu, 0xC003, 0x44); 65 | cpu_write_byte(device->cpu, 0xC004, 0x55); 66 | flash(device, test, sizeof(test)); 67 | int cycles = cpu_execute(device->cpu, 16); 68 | if (cpu_read_byte(device->cpu, 0xD004) != 0x55 || 69 | device->cpu->registers.HL != 0xC003 || 70 | device->cpu->registers.DE != 0xD003 || 71 | device->cpu->registers.BC != 4 || 72 | cycles != 0) { 73 | asic_free(device); 74 | return 1; 75 | } 76 | asic_free(device); 77 | return 0; 78 | } 79 | 80 | int test_LDDR() { 81 | asic_t *device = asic_init(TI83p, NULL); 82 | uint8_t test[] = { 0xED, 0xB8 }; // LDDR 83 | device->cpu->registers.HL = 0xC004; 84 | device->cpu->registers.DE = 0xD004; 85 | device->cpu->registers.BC = 5; 86 | cpu_write_byte(device->cpu, 0xC000, 0x11); 87 | cpu_write_byte(device->cpu, 0xC001, 0x22); 88 | cpu_write_byte(device->cpu, 0xC002, 0x33); 89 | cpu_write_byte(device->cpu, 0xC003, 0x44); 90 | cpu_write_byte(device->cpu, 0xC004, 0x55); 91 | flash(device, test, sizeof(test)); 92 | int cycles = cpu_execute(device->cpu, 100); 93 | if (cpu_read_byte(device->cpu, 0xD000) != 0x11 || 94 | cpu_read_byte(device->cpu, 0xD001) != 0x22 || 95 | cpu_read_byte(device->cpu, 0xD002) != 0x33 || 96 | cpu_read_byte(device->cpu, 0xD003) != 0x44 || 97 | cpu_read_byte(device->cpu, 0xD004) != 0x55 || 98 | device->cpu->registers.HL != 0xBFFF || 99 | device->cpu->registers.DE != 0xCFFF || 100 | device->cpu->registers.BC != 0 || 101 | cycles != 0) { 102 | asic_free(device); 103 | return 1; 104 | } 105 | asic_free(device); 106 | return 0; 107 | } 108 | 109 | int test_CPI() { 110 | asic_t *device = asic_init(TI83p, NULL); 111 | uint8_t test[] = { 0xED, 0xA1 }; // CPI 112 | device->cpu->registers.HL = 0xC000; 113 | device->cpu->registers.BC = 5; 114 | device->cpu->registers.A = 0x33; 115 | cpu_write_byte(device->cpu, 0xC000, 0x11); 116 | cpu_write_byte(device->cpu, 0xC001, 0x22); 117 | cpu_write_byte(device->cpu, 0xC002, 0x33); 118 | flash(device, test, sizeof(test)); 119 | int cycles = cpu_execute(device->cpu, 16); 120 | if (device->cpu->registers.flags.Z != 0 || 121 | device->cpu->registers.HL != 0xC001 || 122 | device->cpu->registers.BC != 4 || 123 | cycles != 0) { 124 | asic_free(device); 125 | return 1; 126 | } 127 | device->cpu->registers.PC = 0; 128 | cpu_execute(device->cpu, 16); 129 | device->cpu->registers.PC = 0; 130 | cpu_execute(device->cpu, 16); 131 | if (device->cpu->registers.flags.Z != 1 || 132 | device->cpu->registers.HL != 0xC003 || 133 | device->cpu->registers.BC != 2) { 134 | asic_free(device); 135 | return 1; 136 | } 137 | asic_free(device); 138 | return 0; 139 | } 140 | 141 | int test_CPD() { 142 | asic_t *device = asic_init(TI83p, NULL); 143 | uint8_t test[] = { 0xED, 0xA9 }; // CPD 144 | device->cpu->registers.HL = 0xC002; 145 | device->cpu->registers.BC = 5; 146 | device->cpu->registers.A = 0x11; 147 | cpu_write_byte(device->cpu, 0xC000, 0x11); 148 | cpu_write_byte(device->cpu, 0xC001, 0x22); 149 | cpu_write_byte(device->cpu, 0xC002, 0x33); 150 | flash(device, test, sizeof(test)); 151 | int cycles = cpu_execute(device->cpu, 16); 152 | if (device->cpu->registers.flags.Z != 0 || 153 | device->cpu->registers.HL != 0xC001 || 154 | device->cpu->registers.BC != 4 || 155 | cycles != 0) { 156 | asic_free(device); 157 | return 1; 158 | } 159 | device->cpu->registers.PC = 0; 160 | cpu_execute(device->cpu, 16); 161 | device->cpu->registers.PC = 0; 162 | cpu_execute(device->cpu, 16); 163 | if (device->cpu->registers.flags.Z != 1 || 164 | device->cpu->registers.HL != 0xBFFF || 165 | device->cpu->registers.BC != 2) { 166 | asic_free(device); 167 | return 1; 168 | } 169 | asic_free(device); 170 | return 0; 171 | } 172 | 173 | int test_CPIR() { 174 | asic_t *device = asic_init(TI83p, NULL); 175 | uint8_t test[] = { 0xED, 0xB1 }; // CPIR 176 | device->cpu->registers.HL = 0xC000; 177 | device->cpu->registers.BC = 5; 178 | device->cpu->registers.A = 0x33; 179 | cpu_write_byte(device->cpu, 0xC000, 0x11); 180 | cpu_write_byte(device->cpu, 0xC001, 0x22); 181 | cpu_write_byte(device->cpu, 0xC002, 0x33); 182 | cpu_write_byte(device->cpu, 0xC003, 0x44); 183 | flash(device, test, sizeof(test)); 184 | int cycles = cpu_execute(device->cpu, 58); 185 | if (device->cpu->registers.HL != 0xC003 || 186 | device->cpu->registers.BC != 2 || 187 | cycles != 0) { 188 | asic_free(device); 189 | return 1; 190 | } 191 | asic_free(device); 192 | return 0; 193 | } 194 | 195 | int test_CPDR() { 196 | asic_t *device = asic_init(TI83p, NULL); 197 | uint8_t test[] = { 0xED, 0xB9 }; // CPDR 198 | device->cpu->registers.HL = 0xC004; 199 | device->cpu->registers.BC = 5; 200 | device->cpu->registers.A = 0x33; 201 | cpu_write_byte(device->cpu, 0xC001, 0x22); 202 | cpu_write_byte(device->cpu, 0xC002, 0x33); 203 | cpu_write_byte(device->cpu, 0xC003, 0x44); 204 | cpu_write_byte(device->cpu, 0xC004, 0x55); 205 | flash(device, test, sizeof(test)); 206 | int cycles = cpu_execute(device->cpu, 58); 207 | if (device->cpu->registers.HL != 0xC001 || 208 | device->cpu->registers.BC != 2 || 209 | cycles != 0) { 210 | asic_free(device); 211 | return 1; 212 | } 213 | asic_free(device); 214 | return 0; 215 | } 216 | 217 | // Note: INI, INIR, IND, INDR, OUTI, OUTD, OTIR, and OTDR are tested in io.c 218 | -------------------------------------------------------------------------------- /frontends/test/tests/control.c: -------------------------------------------------------------------------------- 1 | int test_RST() { 2 | uint8_t i; 3 | const uint8_t opcodes[] = { 0xC7, 0xCF, 0xD7, 0xDF, 0xE7, 0xEF, 0xF7, 0xFF }; 4 | const uint8_t results[] = { 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38 }; 5 | for (i = 0; i < 8; i++) { 6 | asic_t *device = asic_init(TI83p, NULL); 7 | uint8_t test[] = { opcodes[i] }; 8 | flash(device, test, sizeof(test)); 9 | int cycles = cpu_execute(device->cpu, 11); 10 | if (device->cpu->registers.PC != results[i] || 11 | cycles != 0) { 12 | asic_free(device); 13 | return 1; 14 | } 15 | asic_free(device); 16 | } 17 | return 0; 18 | } 19 | 20 | int test_DJNZ() { 21 | asic_t *device = asic_init(TI83p, NULL); 22 | uint8_t test[] = { 0x10, 0xFE }; // DJNZ $ 23 | flash(device, test, sizeof(test)); 24 | device->cpu->registers.B = 10; 25 | int cycles = cpu_execute(device->cpu, 13); 26 | if (device->cpu->registers.PC != 0 || 27 | device->cpu->registers.B != 9 || 28 | cycles != 0) { 29 | asic_free(device); 30 | return 1; 31 | } 32 | while (device->cpu->registers.B != 0) { 33 | cycles = cpu_execute(device->cpu, 1); 34 | } 35 | if (device->cpu->registers.PC != 2 || cycles != -7) { 36 | asic_free(device); 37 | return 1; 38 | } 39 | asic_free(device); 40 | return 0; 41 | } 42 | 43 | int test_JR() { 44 | asic_t *device = asic_init(TI83p, NULL); 45 | uint8_t test[] = { 0x18, 0x0E }; // JR 0x10 46 | flash(device, test, sizeof(test)); 47 | int cycles = cpu_execute(device->cpu, 12); 48 | if (device->cpu->registers.PC != 0x10 || 49 | cycles != 0) { 50 | asic_free(device); 51 | return 1; 52 | } 53 | asic_free(device); 54 | return 0; 55 | } 56 | 57 | int test_JR_cc() { 58 | uint8_t test[] = { 0x28, 0x0E }; // JR Z, 0x10 59 | uint8_t test_nz[] = { 0x20, 0x0E }; // JR NZ, 0x10 60 | asic_t *device = asic_init(TI83p, NULL); 61 | device->cpu->registers.flags.Z = 0; 62 | flash(device, test, sizeof(test)); 63 | int cycles = cpu_execute(device->cpu, 7); 64 | if (device->cpu->registers.PC != 2 || 65 | cycles != 0) { 66 | asic_free(device); 67 | return 1; 68 | } 69 | asic_free(device); 70 | device = asic_init(TI83p, NULL); 71 | device->cpu->registers.flags.Z = 0; 72 | flash(device, test_nz, sizeof(test_nz)); 73 | cycles = cpu_execute(device->cpu, 12); 74 | if (device->cpu->registers.PC != 0x10 || cycles != 0) { 75 | asic_free(device); 76 | return 2; 77 | } 78 | asic_free(device); 79 | return 0; 80 | } 81 | 82 | int test_RET_cc() { 83 | uint8_t test[] = { 0xC8 }; // RET Z 84 | uint8_t test_nz[] = { 0xC0 }; // RET NZ 85 | asic_t *device = asic_init(TI83p, NULL); 86 | device->cpu->registers.flags.Z = 0; 87 | device->cpu->registers.SP = 0x3FFE; 88 | flash(device, test, sizeof(test)); 89 | int cycles = cpu_execute(device->cpu, 5); 90 | if (device->cpu->registers.PC != 1 || 91 | cycles != 0) { 92 | asic_free(device); 93 | return 1; 94 | } 95 | asic_free(device); 96 | device = asic_init(TI83p, NULL); 97 | device->cpu->registers.flags.Z = 0; 98 | device->cpu->registers.SP = 0x3FFE; 99 | flash(device, test_nz, sizeof(test_nz)); 100 | cycles = cpu_execute(device->cpu, 11); 101 | if (device->cpu->registers.PC != 0xFFFF || cycles != 0) { 102 | asic_free(device); 103 | return 2; 104 | } 105 | asic_free(device); 106 | return 0; 107 | } 108 | 109 | int test_RET() { 110 | uint8_t test[] = { 0xC9 }; // RET 111 | asic_t *device = asic_init(TI83p, NULL); 112 | device->cpu->registers.SP = 0x3FFE; 113 | flash(device, test, sizeof(test)); 114 | int cycles = cpu_execute(device->cpu, 10); 115 | if (device->cpu->registers.PC != 0xFFFF || 116 | cycles != 0) { 117 | asic_free(device); 118 | return 1; 119 | } 120 | asic_free(device); 121 | return 0; 122 | } 123 | 124 | int test_JP_HL() { 125 | asic_t *device = asic_init(TI83p, NULL); 126 | uint8_t test[] = { 0xE9 }; // JP (HL) 127 | device->cpu->registers.HL = 0x1234; 128 | flash(device, test, sizeof(test)); 129 | int cycles = cpu_execute(device->cpu, 4); 130 | if (device->cpu->registers.PC != 0x1234 || 131 | cycles != 0) { 132 | asic_free(device); 133 | return 1; 134 | } 135 | asic_free(device); 136 | return 0; 137 | } 138 | 139 | int test_LD_SP_HL() { 140 | asic_t *device = asic_init(TI83p, NULL); 141 | uint8_t test[] = { 0xF9 }; // LD SP, HL 142 | device->cpu->registers.HL = 0x1234; 143 | flash(device, test, sizeof(test)); 144 | int cycles = cpu_execute(device->cpu, 6); 145 | if (device->cpu->registers.SP != 0x1234 || 146 | cycles != 0) { 147 | asic_free(device); 148 | return 1; 149 | } 150 | asic_free(device); 151 | return 0; 152 | } 153 | 154 | int test_JP_cc_nn() { 155 | asic_t *device = asic_init(TI83p, NULL); 156 | uint8_t test[] = { 0xCA, 0x34, 0x12 }; // JP Z, 0x1234 157 | flash(device, test, sizeof(test)); 158 | int cycles = cpu_execute(device->cpu, 10); 159 | if (device->cpu->registers.PC != 3 || 160 | cycles != 0) { 161 | asic_free(device); 162 | return 1; 163 | } 164 | asic_free(device); 165 | device = asic_init(TI83p, NULL); 166 | flash(device, test, sizeof(test)); 167 | device->cpu->registers.flags.Z = 1; 168 | cycles = cpu_execute(device->cpu, 10); 169 | if (device->cpu->registers.PC != 0x1234 || 170 | cycles != 0) { 171 | asic_free(device); 172 | return 2; 173 | } 174 | asic_free(device); 175 | return 0; 176 | } 177 | 178 | int test_JP_nn() { 179 | asic_t *device = asic_init(TI83p, NULL); 180 | uint8_t test[] = { 0xC3, 0x34, 0x12 }; // JP 0x1234 181 | flash(device, test, sizeof(test)); 182 | int cycles = cpu_execute(device->cpu, 10); 183 | if (device->cpu->registers.PC != 0x1234 || 184 | cycles != 0) { 185 | asic_free(device); 186 | return 1; 187 | } 188 | asic_free(device); 189 | return 0; 190 | } 191 | 192 | int test_CALL_cc_nn() { 193 | asic_t *device = asic_init(TI83p, NULL); 194 | uint8_t test[] = { 0xCC, 0x34, 0x12 }; // CALL z, 0x1234 195 | flash(device, test, sizeof(test)); 196 | int cycles = cpu_execute(device->cpu, 10); 197 | if (device->cpu->registers.PC != 3 || 198 | cycles != 0) { 199 | asic_free(device); 200 | return 1; 201 | } 202 | asic_free(device); 203 | device = asic_init(TI83p, NULL); 204 | device->cpu->registers.flags.Z = 1; 205 | flash(device, test, sizeof(test)); 206 | cycles = cpu_execute(device->cpu, 17); 207 | if (device->cpu->registers.PC != 0x1234 || 208 | device->cpu->registers.SP != 0xFFFE || 209 | cycles != 0) { 210 | asic_free(device); 211 | return 2; 212 | } 213 | asic_free(device); 214 | return 0; 215 | } 216 | 217 | int test_CALL_nn() { 218 | asic_t *device = asic_init(TI83p, NULL); 219 | uint8_t test[] = { 0xCD, 0x34, 0x12 }; // CALL 0x1234 220 | flash(device, test, sizeof(test)); 221 | int cycles = cpu_execute(device->cpu, 17); 222 | if (device->cpu->registers.PC != 0x1234 || 223 | device->cpu->registers.SP != 0xFFFE || 224 | cycles != 0) { 225 | asic_free(device); 226 | return 1; 227 | } 228 | asic_free(device); 229 | return 0; 230 | } 231 | 232 | int test_IM_set() { 233 | asic_t *device = asic_init(TI83p, NULL); 234 | uint8_t test[] = { 0xED, 0x5E }; // IM 2 235 | flash(device, test, sizeof(test)); 236 | int cycles = cpu_execute(device->cpu, 8); 237 | if (device->cpu->int_mode != 2 || 238 | device->cpu->registers.PC != 2 || 239 | cycles != 0) { 240 | asic_free(device); 241 | return 1; 242 | } 243 | asic_free(device); 244 | return 0; 245 | } 246 | 247 | int test_EI_DI() { 248 | asic_t *device = asic_init(TI83p, NULL); 249 | uint8_t test[] = { 0xFB }; // EI 250 | flash(device, test, sizeof(test)); 251 | int cycles = cpu_execute(device->cpu, 4); 252 | if (device->cpu->IFF1 != 1 || 253 | cycles != 0) { 254 | asic_free(device); 255 | return 1; 256 | } 257 | asic_free(device); 258 | device = asic_init(TI83p, NULL); 259 | uint8_t test2[] = { 0xF3 }; // DI 260 | device->cpu->IFF1 = 1; 261 | flash(device, test2, sizeof(test2)); 262 | cycles = cpu_execute(device->cpu, 4); 263 | if (device->cpu->IFF1 != 0 || 264 | cycles != 0) { 265 | asic_free(device); 266 | return 2; 267 | } 268 | asic_free(device); 269 | return 0; 270 | } 271 | -------------------------------------------------------------------------------- /frontends/test/tests/debugger.c: -------------------------------------------------------------------------------- 1 | int debugger_initialized = 0; 2 | 3 | int debugger_alwaysokc = 0; 4 | int debugger_alwaysfailc = 0; 5 | 6 | int debugger_alwaysok(debugger_state_t *state, int argc, char **argv) { 7 | debugger_alwaysokc++; 8 | return 0; 9 | } 10 | 11 | int debugger_alwaysfail(debugger_state_t *state, int argc, char **argv) { 12 | debugger_alwaysfailc++; 13 | return 0; 14 | } 15 | 16 | int test_debugger_init() { 17 | asic_t uninit; 18 | debugger_t *debugger = init_debugger(&uninit); 19 | 20 | if (debugger->asic != &uninit) { 21 | free_debugger(debugger); 22 | return 1; 23 | } 24 | int orig_count = debugger->commands.count; 25 | 26 | register_command(debugger, "alwaysok", debugger_alwaysok, NULL, 0); 27 | register_command(debugger, "alwaysfail", debugger_alwaysfail, NULL, 0); 28 | 29 | if (debugger->commands.count - orig_count != 2) { 30 | free_debugger(debugger); 31 | return 2; 32 | } 33 | 34 | free_debugger(debugger); 35 | return 0; 36 | } 37 | 38 | int test_debugger_find_command() { 39 | asic_t uninit; 40 | debugger_t *debugger = init_debugger(&uninit); 41 | register_command(debugger, "alwaysok", debugger_alwaysok, NULL, 0); 42 | register_command(debugger, "alwaysfail", debugger_alwaysfail, NULL, 0); 43 | 44 | debugger_command_t *command; 45 | int result = find_best_command(debugger, "always", &command); 46 | if (result != -1) { 47 | return 1; 48 | } 49 | 50 | result = find_best_command(debugger, "alwaysok", &command); 51 | if (!result || command->function != debugger_alwaysok) { 52 | return 2; 53 | } 54 | 55 | result = find_best_command(debugger, "alwaysfail", &command); 56 | if (!result || command->function != debugger_alwaysfail) { 57 | return 3; 58 | } 59 | 60 | result = find_best_command(debugger, "alwaysdontexist", &command); 61 | if (result != 0) { 62 | return 4; 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | int test_debugger_commandline() { 69 | const char *test_data = "5 + 3 \"5 + 3\""; // 5 + 3 "5 + 3" 70 | 71 | int argc = 0; 72 | char **result = debugger_parse_commandline(test_data, &argc); 73 | 74 | if (argc != 4) { 75 | return 1; 76 | } 77 | 78 | if (strcmp(result[0], "5") != 0) { 79 | return 2; 80 | } 81 | 82 | if (strcmp(result[1], "+") != 0) { 83 | return 3; 84 | } 85 | 86 | if (strcmp(result[2], "3") != 0) { 87 | return 4; 88 | } 89 | 90 | if (strcmp(result[3], "5 + 3") != 0) { 91 | return 5; 92 | } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /frontends/test/tests/devices.c: -------------------------------------------------------------------------------- 1 | int test_keyboard() { 2 | z80iodevice_t keyboard = init_keyboard(); 3 | depress_key(keyboard.device, 0); 4 | keyboard.write_out(keyboard.device, 0xFE); 5 | uint8_t value = keyboard.read_in(keyboard.device); 6 | if (value != 0xFE) { 7 | free_keyboard(keyboard.device); 8 | return 1; 9 | } 10 | depress_key(keyboard.device, 1); 11 | value = keyboard.read_in(keyboard.device); 12 | if (value != 0xFC) { 13 | free_keyboard(keyboard.device); 14 | return 2; 15 | } 16 | depress_key(keyboard.device, 0x14); 17 | value = keyboard.read_in(keyboard.device); 18 | if (value != 0xFC) { 19 | free_keyboard(keyboard.device); 20 | return 3; 21 | } 22 | keyboard.write_out(keyboard.device, 0xFC); 23 | value = keyboard.read_in(keyboard.device); 24 | if (value != 0xEC) { 25 | free_keyboard(keyboard.device); 26 | return 4; 27 | } 28 | release_key(keyboard.device, 0x14); 29 | value = keyboard.read_in(keyboard.device); 30 | if (value != 0xFC) { 31 | free_keyboard(keyboard.device); 32 | return 4; 33 | } 34 | return 0; 35 | } 36 | 37 | int test_memorymapping_83p() { 38 | asic_t *asic = asic_init(TI83p, NULL); 39 | memory_mapping_state_t *state = asic->cpu->devices[0x04].device; 40 | 41 | state->bank_a_page = 0; 42 | state->bank_a_flash = 0; 43 | 44 | reload_mapping(state); 45 | ti_write_byte(asic->mmu, 0x4000, 0x12); 46 | 47 | if (asic->mmu->ram[0] != 0x12) { 48 | asic_free(asic); 49 | return 1; 50 | } 51 | 52 | if (ti_read_byte(asic->mmu, 0xC000) != 0x12) { 53 | asic_free(asic); 54 | return 2; 55 | } 56 | 57 | state->map_mode = 1; 58 | state->bank_a_page = 1; 59 | state->bank_a_flash = 0; 60 | state->bank_b_page = 0; 61 | state->bank_b_flash = 0; 62 | reload_mapping(state); 63 | 64 | if (ti_read_byte(asic->mmu, 0x4000) != 0x12) { 65 | asic_free(asic); 66 | return 3; 67 | } 68 | 69 | asic->mmu->ram[0x4000] = 0x34; 70 | if (ti_read_byte(asic->mmu, 0x8000) != 0x34) { 71 | asic_free(asic); 72 | return 4; 73 | } 74 | 75 | if (ti_read_byte(asic->mmu, 0xC000) != 0x12) { 76 | asic_free(asic); 77 | return 5; 78 | } 79 | return 0; 80 | } 81 | 82 | int test_memorymapping_others() { 83 | asic_t *asic = asic_init(TI84p, NULL); 84 | memory_mapping_state_t *state = asic->cpu->devices[0x04].device; 85 | 86 | 87 | state->ram_bank_page = 1; 88 | state->bank_a_page = 0; 89 | state->bank_a_flash = 0; 90 | 91 | reload_mapping(state); 92 | ti_write_byte(asic->mmu, 0x4000, 0x12); 93 | 94 | if (asic->mmu->ram[0] != 0x12) { 95 | asic_free(asic); 96 | return 1; 97 | } 98 | 99 | asic->mmu->ram[0x4000] = 0x34; 100 | if (ti_read_byte(asic->mmu, 0xC000) != 0x34) { 101 | asic_free(asic); 102 | return 2; 103 | } 104 | 105 | state->map_mode = 1; 106 | state->bank_a_page = 0; 107 | state->bank_a_flash = 0; 108 | state->bank_b_page = 0; 109 | state->bank_b_flash = 0; 110 | reload_mapping(state); 111 | 112 | if (ti_read_byte(asic->mmu, 0x4000) != 0x12) { 113 | asic_free(asic); 114 | return 3; 115 | } 116 | 117 | if (ti_read_byte(asic->mmu, 0x8000) != 0x34) { 118 | asic_free(asic); 119 | return 4; 120 | } 121 | 122 | if (ti_read_byte(asic->mmu, 0xC000) != 0x12) { 123 | asic_free(asic); 124 | return 5; 125 | } 126 | return 0; 127 | } 128 | 129 | int test_status() { 130 | asic_t *asic = asic_init(TI83p, NULL); 131 | z80iodevice_t status = init_status(asic); 132 | // Test battery status 133 | asic->battery = BATTERIES_GOOD; 134 | asic->battery_remove_check = 0; 135 | uint8_t value = status.read_in(status.device); 136 | if (!(value & 1)) { 137 | asic_free(asic); 138 | return 1; 139 | } 140 | // Test flash 141 | asic->mmu->flash_unlocked = 1; 142 | value = status.read_in(status.device); 143 | if (!(value & 4)) { 144 | asic_free(asic); 145 | return 1; 146 | } 147 | asic->mmu->flash_unlocked = 0; 148 | value = status.read_in(status.device); 149 | if (value & 4) { 150 | asic_free(asic); 151 | return 1; 152 | } 153 | asic_free(asic); 154 | return 0; 155 | } 156 | 157 | int test_link_port() { 158 | asic_t *asic = asic_init(TI83p, NULL); 159 | z80iodevice_t link = asic->cpu->devices[0x00]; 160 | link_state_t *state = link.device; 161 | uint8_t value = link.read_in(state); 162 | if (value != 0) { 163 | asic_free(asic); 164 | return 1; 165 | } 166 | link.write_out(state, 0x01); 167 | value = link.read_in(state); 168 | if (value != 0x11) { 169 | asic_free(asic); 170 | return 2; 171 | } 172 | return 0; 173 | } 174 | 175 | int test_link_assist_rx() { 176 | asic_t *asic = asic_init(TI83pSE, NULL); 177 | z80iodevice_t link_assist_rx_read = asic->cpu->devices[0x0A]; 178 | z80iodevice_t link_assist_status = asic->cpu->devices[0x09]; 179 | link_state_t *state = link_assist_rx_read.device; 180 | 181 | if (!link_recv_byte(asic, 0xBE)) { 182 | asic_free(asic); 183 | return 1; 184 | } 185 | if (link_recv_byte(asic, 0xEF)) { 186 | asic_free(asic); 187 | return 2; 188 | } 189 | 190 | uint8_t status = link_assist_status.read_in(state); 191 | if (status != state->assist.status.u8 || 192 | !state->assist.status.rx_ready || 193 | !state->assist.status.int_rx_ready) { 194 | return 3; 195 | } 196 | 197 | uint8_t val = link_assist_rx_read.read_in(state); 198 | if (val != 0xBE) { 199 | return 4; 200 | } 201 | 202 | status = link_assist_status.read_in(state); 203 | if (status != state->assist.status.u8 || 204 | state->assist.status.rx_ready || 205 | state->assist.status.int_rx_ready) { 206 | return 5; 207 | } 208 | return 0; 209 | } 210 | 211 | int test_link_assist_tx() { 212 | asic_t *asic = asic_init(TI83pSE, NULL); 213 | z80iodevice_t link_assist_tx_read = asic->cpu->devices[0x0D]; 214 | z80iodevice_t link_assist_status = asic->cpu->devices[0x09]; 215 | link_state_t *state = link_assist_tx_read.device; 216 | 217 | if (link_read_tx_buffer(asic) != EOF) { 218 | asic_free(asic); 219 | return 1; 220 | } 221 | 222 | uint8_t status = link_assist_status.read_in(state); 223 | if (status != state->assist.status.u8 || 224 | !state->assist.status.tx_ready || 225 | !state->assist.status.int_tx_ready) { 226 | return 2; 227 | } 228 | 229 | link_assist_tx_read.write_out(state, 0xDE); 230 | 231 | status = link_assist_status.read_in(state); 232 | if (status != state->assist.status.u8 || 233 | state->assist.status.tx_ready || 234 | state->assist.status.int_tx_ready) { 235 | return 3; 236 | } 237 | 238 | if (link_read_tx_buffer(asic) != 0xDE) { 239 | return 4; 240 | } 241 | 242 | status = link_assist_status.read_in(state); 243 | if (status != state->assist.status.u8 || 244 | !state->assist.status.tx_ready || 245 | !state->assist.status.int_tx_ready) { 246 | return 5; 247 | } 248 | return 0; 249 | } 250 | -------------------------------------------------------------------------------- /frontends/test/tests/disassembler.c: -------------------------------------------------------------------------------- 1 | char dis_buffer[40]; 2 | char *dis_pointer = dis_buffer; 3 | uint8_t *dis_input; 4 | 5 | int dis_write(struct disassemble_memory *memp, const char *data, ...) { 6 | va_list list; 7 | va_start(list, data); 8 | 9 | int count = vsnprintf((char *)dis_pointer, 40 - (dis_pointer - dis_buffer), data, list); 10 | dis_pointer += count; 11 | return count; 12 | } 13 | 14 | uint8_t dis_read_byte(struct disassemble_memory *memp, uint16_t pointer) { 15 | return dis_input[pointer]; 16 | } 17 | 18 | int test_disassembler() { 19 | disassembler_init(); 20 | uint8_t tests[] = 21 | {0x3a, 0x34, 0x12, // LD A, (0x1234) 22 | 0xdd, 0xcb, 0x12, 0x06, // RLC (IX + 0x12) 23 | 0xfd, 0x36, 0x10, 0x20, // LD (IY + 0x10), 0x20 24 | }; 25 | 26 | struct disassemble_memory mem = { dis_read_byte, 0 }; 27 | dis_input = tests; 28 | 29 | parse_instruction(&mem, dis_write, false); 30 | dis_pointer = dis_buffer; 31 | if (strcmp(dis_buffer, "LD A, (0x1234)") != 0) { 32 | return 1; 33 | } 34 | 35 | parse_instruction(&mem, dis_write, false); 36 | dis_pointer = dis_buffer; 37 | if (strcmp(dis_buffer, "RLC (IX + 0x12)") != 0) { 38 | return 2; 39 | } 40 | 41 | parse_instruction(&mem, dis_write, false); 42 | dis_pointer = dis_buffer; 43 | if (strcmp(dis_buffer, "LD (IY + 0x10), 0x20") != 0) { 44 | return 3; 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /frontends/test/tests/index.c: -------------------------------------------------------------------------------- 1 | int test_IX_IY() { 2 | asic_t *device = asic_init(TI83p, NULL); 3 | uint8_t test_1[] = { 0xDD, 0x77, 0x00 }; // LD (IX + 0), A 4 | device->cpu->registers.IX = 0xC000; 5 | device->cpu->registers.A = 0x30; 6 | flash(device, test_1, sizeof(test_1)); 7 | cpu_execute(device->cpu, 19); 8 | if (cpu_read_byte(device->cpu, 0xC000) != 0x30) { 9 | asic_free(device); 10 | return 1; 11 | } 12 | asic_free(device); 13 | 14 | device = asic_init(TI83p, NULL); 15 | uint8_t test_2[] = { 0xDD, 0x7E, 0x00 }; // LD A, (IX + 0) 16 | device->cpu->registers.IX = 0xC000; 17 | cpu_write_byte(device->cpu, 0xC000, 0x30); 18 | flash(device, test_2, sizeof(test_2)); 19 | cpu_execute(device->cpu, 19); 20 | if (device->cpu->registers.A != 0x30) { 21 | asic_free(device); 22 | return 2; 23 | } 24 | asic_free(device); 25 | 26 | device = asic_init(TI83p, NULL); 27 | uint8_t test_3[] = { 0xFD, 0x77, 0x00 }; // LD (IY + 0), A 28 | device->cpu->registers.IY = 0xC000; 29 | device->cpu->registers.A = 0x30; 30 | flash(device, test_3, sizeof(test_3)); 31 | cpu_execute(device->cpu, 19); 32 | if (cpu_read_byte(device->cpu, 0xC000) != 0x30) { 33 | asic_free(device); 34 | return 3; 35 | } 36 | asic_free(device); 37 | 38 | device = asic_init(TI83p, NULL); 39 | uint8_t test_4[] = { 0xFD, 0x7E, 0x00 }; // LD A, (IY + 0) 40 | device->cpu->registers.IY = 0xC000; 41 | cpu_write_byte(device->cpu, 0xC000, 0x30); 42 | flash(device, test_4, sizeof(test_4)); 43 | cpu_execute(device->cpu, 19); 44 | if (device->cpu->registers.A != 0x30) { 45 | asic_free(device); 46 | return 4; 47 | } 48 | asic_free(device); 49 | 50 | return 0; 51 | } 52 | 53 | int test_JP_IX__JP_IY() { 54 | asic_t *device = asic_init(TI83p, NULL); 55 | uint8_t test[] = { 0xDD, 0xE9 }; // JP (IX) 56 | device->cpu->registers.IX = 0x1234; 57 | flash(device, test, sizeof(test)); 58 | int cycles = cpu_execute(device->cpu, 8); 59 | if (device->cpu->registers.PC != 0x1234 || 60 | cycles != 0) { 61 | asic_free(device); 62 | return 1; 63 | } 64 | asic_free(device); 65 | 66 | device = asic_init(TI83p, NULL); 67 | uint8_t test_2[] = { 0xFD, 0xE9 }; // JP (IY) 68 | device->cpu->registers.IY = 0x1234; 69 | flash(device, test_2, sizeof(test_2)); 70 | cycles = cpu_execute(device->cpu, 8); 71 | if (device->cpu->registers.PC != 0x1234 || 72 | cycles != 0) { 73 | asic_free(device); 74 | return 2; 75 | } 76 | asic_free(device); 77 | return 0; 78 | } 79 | 80 | int test_ADD_IX_rp() { 81 | asic_t *device = asic_init(TI83p, NULL); 82 | uint8_t test[] = { 0xDD, 0x09 }; // ADD IX, BC 83 | device->cpu->registers.IX = 0x1000; 84 | device->cpu->registers.BC = 0x0234; 85 | flash(device, test, sizeof(test)); 86 | int cycles = cpu_execute(device->cpu, 15); 87 | if (device->cpu->registers.IX != 0x1234 || 88 | cycles != 0) { 89 | asic_free(device); 90 | return 1; 91 | } 92 | asic_free(device); 93 | 94 | device = asic_init(TI83p, NULL); 95 | device->cpu->registers.IX = 0xF000; 96 | device->cpu->registers.BC = 0x1000; 97 | device->cpu->registers.flags.Z = 0; 98 | flash(device, test, sizeof(test)); 99 | cycles = cpu_execute(device->cpu, 15); 100 | if (device->cpu->registers.IX != 0 || 101 | device->cpu->registers.flags.Z != 0 || 102 | device->cpu->registers.flags.C != 1 || 103 | cycles != 0) { 104 | asic_free(device); 105 | return 1; 106 | } 107 | asic_free(device); 108 | return 0; 109 | } 110 | 111 | int test_prefix_reset() { 112 | asic_t *device = asic_init(TI83p, NULL); 113 | uint8_t test[] = { 0xDD, 0x09, 0x09 }; // ADD IX, BC \ ADD HL, BC 114 | device->cpu->registers.IX = 0x1000; 115 | device->cpu->registers.HL = 0x2000; 116 | device->cpu->registers.BC = 0x0234; 117 | flash(device, test, sizeof(test)); 118 | int cycles = cpu_execute(device->cpu, 26); 119 | if (device->cpu->registers.IX != 0x1234 || 120 | device->cpu->registers.HL != 0x2234 || 121 | cycles != 0) { 122 | asic_free(device); 123 | return 1; 124 | } 125 | asic_free(device); 126 | return 0; 127 | } 128 | 129 | int test_index_offsets() { 130 | asic_t *device = asic_init(TI83p, NULL); 131 | uint8_t test[] = { 0xDD, 0x86, 0x0A }; // ADD A, (IX + 10) 132 | device->cpu->registers.IX = 0x1000; 133 | device->cpu->registers.A = 0x10; 134 | mmu_force_write(device->mmu, 0x1000 + 10, 0x20); 135 | flash(device, test, sizeof(test)); 136 | int cycles = cpu_execute(device->cpu, 19); 137 | if (device->cpu->registers.A != 0x30 || 138 | cycles != 0) { 139 | asic_free(device); 140 | return 1; 141 | } 142 | asic_free(device); 143 | return 0; 144 | } 145 | 146 | int test_ixh_ixl() { 147 | asic_t *device = asic_init(TI83p, NULL); 148 | uint8_t test[] = { 0xDD, 0x84 }; // ADD A, IXH 149 | device->cpu->registers.IXH = 0x20; 150 | device->cpu->registers.A = 0x10; 151 | flash(device, test, sizeof(test)); 152 | int cycles = cpu_execute(device->cpu, 8); 153 | if (device->cpu->registers.A != 0x30 || 154 | cycles != 0) { 155 | asic_free(device); 156 | return 1; 157 | } 158 | asic_free(device); 159 | 160 | device = asic_init(TI83p, NULL); 161 | uint8_t test2[] = { 0xDD, 0x85 }; // ADD A, IXL 162 | device->cpu->registers.IXL = 0x20; 163 | device->cpu->registers.A = 0x10; 164 | flash(device, test2, sizeof(test2)); 165 | cycles = cpu_execute(device->cpu, 8); 166 | if (device->cpu->registers.A != 0x30 || 167 | cycles != 0) { 168 | asic_free(device); 169 | return 2; 170 | } 171 | asic_free(device); 172 | return 0; 173 | } 174 | -------------------------------------------------------------------------------- /frontends/test/tests/interrupts.c: -------------------------------------------------------------------------------- 1 | int test_IM_1() { 2 | uint8_t test[] = { 0xED, 0x56, 0xFB, 0x18, 0xFE }; // IM 1 \ EI \ JR $ 3 | asic_t *device = asic_init(TI83p, NULL); 4 | flash(device, test, sizeof(test)); 5 | cpu_execute(device->cpu, 100); 6 | if (device->cpu->registers.PC != 3) { 7 | asic_free(device); 8 | return 1; 9 | } 10 | device->cpu->interrupt = 1; 11 | int cycles = cpu_execute(device->cpu, 13); 12 | if (device->cpu->registers.PC != 0x38 || 13 | device->cpu->registers.SP != 0xFFFE || 14 | cycles != 0) { 15 | asic_free(device); 16 | return 1; 17 | } 18 | return 0; 19 | } 20 | 21 | int test_IM_2() { 22 | uint8_t test[] = { 0xED, 0x5E, 0x3E, 0x80, 0xED, 0x47, 0xFB, 0x18, 0xFE }; // IM 2 \ LD A, 0x80 \ LD I, A \ EI \ JR $ 23 | asic_t *device = asic_init(TI83p, NULL); 24 | flash(device, test, sizeof(test)); 25 | cpu_execute(device->cpu, 100); 26 | if (device->cpu->registers.PC != 7 || 27 | device->cpu->registers.I != 0x80) { 28 | asic_free(device); 29 | return 1; 30 | } 31 | device->cpu->bus = 0x90; 32 | device->cpu->interrupt = 1; 33 | int cycles = cpu_execute(device->cpu, 19); 34 | if (device->cpu->registers.PC != 0x8090 || 35 | device->cpu->registers.SP != 0xFFFE || 36 | cycles != 0) { 37 | asic_free(device); 38 | return 2; 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /frontends/test/tests/load.c: -------------------------------------------------------------------------------- 1 | int test_LD_rp_nn() { 2 | asic_t *device = asic_init(TI83p, NULL); 3 | uint8_t test[] = { 0x21, 0x34, 0x12 }; // LD HL, 0x1234 4 | flash(device, test, sizeof(test)); 5 | int cycles = cpu_execute(device->cpu, 10); 6 | if (device->cpu->registers.HL != 0x1234 || 7 | cycles != 0) { 8 | asic_free(device); 9 | return 1; 10 | } 11 | asic_free(device); 12 | return 0; 13 | } 14 | 15 | int test_LD_BCptr_A() { 16 | asic_t *device = asic_init(TI83p, NULL); 17 | uint8_t test[] = { 0x02 }; // LD (BC), A 18 | device->cpu->registers.BC = 0xC000; 19 | device->cpu->registers.A = 0x2F; 20 | flash(device, test, sizeof(test)); 21 | int cycles = cpu_execute(device->cpu, 7); 22 | if (cpu_read_byte(device->cpu, 0xC000) != 0x2F || 23 | cycles != 0) { 24 | asic_free(device); 25 | return 1; 26 | } 27 | asic_free(device); 28 | return 0; 29 | } 30 | 31 | int test_LD_nnptr_HL() { 32 | asic_t *device = asic_init(TI83p, NULL); 33 | uint8_t test[] = { 0x22, 0x00, 0xC0 }; // LD (0xC000), HL 34 | device->cpu->registers.HL = 0x1234; 35 | flash(device, test, sizeof(test)); 36 | int cycles = cpu_execute(device->cpu, 16); 37 | if (cpu_read_word(device->cpu, 0xC000) != 0x1234 || 38 | cycles != 0) { 39 | asic_free(device); 40 | return 1; 41 | } 42 | asic_free(device); 43 | return 0; 44 | } 45 | 46 | int test_LD_nnptr_A() { 47 | asic_t *device = asic_init(TI83p, NULL); 48 | uint8_t test[] = { 0x32, 0x00, 0xC0 }; // LD (0xC000), A 49 | device->cpu->registers.A = 0x4F; 50 | flash(device, test, sizeof(test)); 51 | int cycles = cpu_execute(device->cpu, 13); 52 | if (cpu_read_word(device->cpu, 0xC000) != 0x4F || 53 | cycles != 0) { 54 | asic_free(device); 55 | return 1; 56 | } 57 | asic_free(device); 58 | return 0; 59 | } 60 | 61 | int test_LD_A_BCptr() { 62 | asic_t *device = asic_init(TI83p, NULL); 63 | uint8_t test[] = { 0x0A }; // LD (BC), A 64 | device->cpu->registers.BC = 0xC000; 65 | cpu_write_byte(device->cpu, 0xC000, 0x2F); 66 | flash(device, test, sizeof(test)); 67 | int cycles = cpu_execute(device->cpu, 7); 68 | if (device->cpu->registers.A != 0x2F || 69 | cycles != 0) { 70 | asic_free(device); 71 | return 1; 72 | } 73 | asic_free(device); 74 | return 0; 75 | } 76 | 77 | int test_LD_HL_nnptr() { 78 | asic_t *device = asic_init(TI83p, NULL); 79 | uint8_t test[] = { 0x2A, 0x00, 0xC0 }; // LD HL, (0xC000) 80 | cpu_write_word(device->cpu, 0xC000, 0x1234); 81 | flash(device, test, sizeof(test)); 82 | int cycles = cpu_execute(device->cpu, 16); 83 | if (device->cpu->registers.HL != 0x1234 || 84 | cycles != 0) { 85 | asic_free(device); 86 | return 1; 87 | } 88 | asic_free(device); 89 | return 0; 90 | } 91 | 92 | int test_LD_A_nnptr() { 93 | asic_t *device = asic_init(TI83p, NULL); 94 | uint8_t test[] = { 0x3A, 0x00, 0xC0 }; // LD A, (0xC000) 95 | cpu_write_byte(device->cpu, 0xC000, 0x4F); 96 | flash(device, test, sizeof(test)); 97 | int cycles = cpu_execute(device->cpu, 13); 98 | if (device->cpu->registers.A != 0x4F || 99 | cycles != 0) { 100 | asic_free(device); 101 | return 1; 102 | } 103 | asic_free(device); 104 | return 0; 105 | } 106 | 107 | int test_LD_r_n() { 108 | asic_t *device = asic_init(TI83p, NULL); 109 | uint8_t test[] = { 0x06, 0x2F }; // LD B, 0x2F 110 | flash(device, test, sizeof(test)); 111 | int cycles = cpu_execute(device->cpu, 7); 112 | if (device->cpu->registers.B != 0x2F || 113 | device->cpu->registers.PC != 2 || 114 | cycles != 0) { 115 | asic_free(device); 116 | return 1; 117 | } 118 | asic_free(device); 119 | return 0; 120 | } 121 | 122 | int test_LD_r_r() { 123 | asic_t *device = asic_init(TI83p, NULL); 124 | uint8_t test[] = { 0x78 }; // LD A, B 125 | device->cpu->registers.B = 0x4F; 126 | flash(device, test, sizeof(test)); 127 | int cycles = cpu_execute(device->cpu, 4); 128 | if (device->cpu->registers.A != 0x4F || 129 | cycles != 0) { 130 | asic_free(device); 131 | return 1; 132 | } 133 | asic_free(device); 134 | return 0; 135 | } 136 | 137 | int test_POP_rp2() { 138 | asic_t *device = asic_init(TI83p, NULL); 139 | uint8_t test[] = { 0xC1 }; // POP BC 140 | device->cpu->registers.SP = 0xC000; 141 | cpu_write_byte(device->cpu, 0xC000, 0x34); 142 | cpu_write_byte(device->cpu, 0xC001, 0x12); 143 | flash(device, test, sizeof(test)); 144 | int cycles = cpu_execute(device->cpu, 10); 145 | if (device->cpu->registers.BC != 0x1234 || 146 | device->cpu->registers.SP != 0xC002 || 147 | cycles != 0) { 148 | asic_free(device); 149 | return 1; 150 | } 151 | asic_free(device); 152 | return 0; 153 | } 154 | 155 | int test_EXX() { 156 | asic_t *device = asic_init(TI83p, NULL); 157 | uint8_t test[] = { 0xD9 }; 158 | device->cpu->registers.BC = 0x1111; 159 | device->cpu->registers._BC = 0x2222; 160 | device->cpu->registers.DE = 0x3333; 161 | device->cpu->registers._DE = 0x4444; 162 | device->cpu->registers.HL = 0x5555; 163 | device->cpu->registers._HL = 0x6666; 164 | flash(device, test, sizeof(test)); 165 | int cycles = cpu_execute(device->cpu, 4); 166 | if (device->cpu->registers.BC != 0x2222 || 167 | device->cpu->registers._BC != 0x1111 || 168 | device->cpu->registers.DE != 0x4444 || 169 | device->cpu->registers._DE != 0x3333 || 170 | device->cpu->registers.HL != 0x6666 || 171 | device->cpu->registers._HL != 0x5555 || 172 | cycles != 0) { 173 | asic_free(device); 174 | return 1; 175 | } 176 | asic_free(device); 177 | return 0; 178 | } 179 | 180 | int test_EX_SP_HL() { 181 | asic_t *device = asic_init(TI83p, NULL); 182 | uint8_t test[] = { 0xE3 }; // EX (SP), HL 183 | device->cpu->registers.HL = 0xDEAD; 184 | device->cpu->registers.SP = 0xC000; 185 | cpu_write_word(device->cpu, 0xC000, 0xBEEF); 186 | flash(device, test, sizeof(test)); 187 | int cycles = cpu_execute(device->cpu, 19); 188 | if (device->cpu->registers.HL != 0xBEEF || 189 | cpu_read_word(device->cpu, 0xC000) != 0xDEAD || 190 | cycles != 0) { 191 | asic_free(device); 192 | return 1; 193 | } 194 | asic_free(device); 195 | return 0; 196 | } 197 | 198 | int test_EX_DE_HL() { 199 | asic_t *device = asic_init(TI83p, NULL); 200 | uint8_t test[] = { 0xEB }; // EX DE, HL 201 | device->cpu->registers.HL = 0xDEAD; 202 | device->cpu->registers.DE = 0xBEEF; 203 | flash(device, test, sizeof(test)); 204 | int cycles = cpu_execute(device->cpu, 4); 205 | if (device->cpu->registers.HL != 0xBEEF || 206 | device->cpu->registers.DE != 0xDEAD || 207 | cycles != 0) { 208 | asic_free(device); 209 | return 1; 210 | } 211 | asic_free(device); 212 | return 0; 213 | } 214 | 215 | int test_PUSH_rp2() { 216 | asic_t *device = asic_init(TI83p, NULL); 217 | uint8_t test[] = { 0xD5 }; // PUSH DE 218 | device->cpu->registers.DE = 0x1234; 219 | flash(device, test, sizeof(test)); 220 | int cycles = cpu_execute(device->cpu, 11); 221 | if (device->cpu->registers.SP != 0xFFFE || 222 | cpu_read_word(device->cpu, 0xFFFE) != 0x1234 || 223 | cycles != 0) { 224 | asic_free(device); 225 | return 1; 226 | } 227 | asic_free(device); 228 | return 0; 229 | } 230 | 231 | int test_LD_nn_rp() { 232 | asic_t *device = asic_init(TI83p, NULL); 233 | uint8_t test[] = { 0xED, 0x43, 0x00, 0xC0 }; // LD (0xC000), BC 234 | device->cpu->registers.BC = 0x1234; 235 | flash(device, test, sizeof(test)); 236 | int cycles = cpu_execute(device->cpu, 20); 237 | if (cpu_read_word(device->cpu, 0xC000) != 0x1234 || 238 | cycles != 0) { 239 | asic_free(device); 240 | return 1; 241 | } 242 | asic_free(device); 243 | return 0; 244 | } 245 | 246 | int test_LD_rp_nn_ind() { 247 | asic_t *device = asic_init(TI83p, NULL); 248 | uint8_t test[] = { 0xED, 0x4B, 0x00, 0xC0 }; // LD BC, (0xC000) 249 | cpu_write_word(device->cpu, 0xC000, 0x1234); 250 | flash(device, test, sizeof(test)); 251 | int cycles = cpu_execute(device->cpu, 20); 252 | if (device->cpu->registers.BC != 0x1234 || 253 | cycles != 0) { 254 | asic_free(device); 255 | return 1; 256 | } 257 | asic_free(device); 258 | return 0; 259 | } 260 | 261 | int test_LD_A_I() { 262 | asic_t *device = asic_init(TI83p, NULL); 263 | uint8_t test[] = { 0xED, 0x57 }; // LD A, I 264 | device->cpu->registers.I = 0x34; 265 | device->cpu->IFF2 = 1; 266 | flash(device, test, sizeof(test)); 267 | int cycles = cpu_execute(device->cpu, 9); 268 | if (device->cpu->registers.flags.PV != 1 || 269 | cycles != 0) { 270 | asic_free(device); 271 | return 1; 272 | } 273 | asic_free(device); 274 | return 0; 275 | } 276 | 277 | int test_RRD_RLD() { 278 | asic_t *device = asic_init(TI83p, NULL); 279 | uint8_t test[] = { 0xED, 0x67, 0xED, 0x6F }; // RRD \ RLD 280 | device->cpu->registers.HL = 0xC000; 281 | device->cpu->registers.A = 0x12; 282 | cpu_write_word(device->cpu, 0xC000, 0x3456); 283 | flash(device, test, sizeof(test)); 284 | int cycles = cpu_execute(device->cpu, 18); 285 | if (device->cpu->registers.A != 0x16 || 286 | cpu_read_word(device->cpu, 0xC000) != 0x3425 || 287 | cycles != 0) { 288 | asic_free(device); 289 | return 1; 290 | } 291 | cycles = cpu_execute(device->cpu, 18); 292 | if (device->cpu->registers.A != 0x12 || 293 | cpu_read_word(device->cpu, 0xC000) != 0x3456 || 294 | cycles != 0) { 295 | asic_free(device); 296 | return 1; 297 | } 298 | asic_free(device); 299 | return 0; 300 | } 301 | -------------------------------------------------------------------------------- /frontends/test/tests/performance.c: -------------------------------------------------------------------------------- 1 | int test_performance() { 2 | if (getenv("TRAVIS") != NULL) { 3 | // Performance tests do not work on Travis 4 | printf("disabled on Travis builds\n"); 5 | return -1; 6 | } 7 | asic_t *device = asic_init(TI83p, NULL); 8 | 9 | clock_t start, stop; 10 | int i; 11 | start = clock(); 12 | for (i = 0; i < 1000000; i++) { 13 | i -= cpu_execute(device->cpu, 1); 14 | } 15 | stop = clock(); 16 | double time = (double)(stop - start) / (CLOCKS_PER_SEC / 1000); 17 | double mHz = 1000.0 / time; 18 | printf("executed 1,000,000 cycles in %f milliseconds (~%f MHz)\n", time, mHz); 19 | 20 | asic_free(device); 21 | return -1; 22 | } 23 | -------------------------------------------------------------------------------- /frontends/test/tests/shifts.c: -------------------------------------------------------------------------------- 1 | int test_RLCA() { 2 | asic_t *device = asic_init(TI83p, NULL); 3 | uint8_t test[] = { 0x07 }; // RLCA 4 | device->cpu->registers.A = 0x80; 5 | flash(device, test, sizeof(test)); 6 | int cycles = cpu_execute(device->cpu, 4); 7 | if (device->cpu->registers.A != 1 || 8 | device->cpu->registers.flags.C != 1 || 9 | cycles != 0) { 10 | asic_free(device); 11 | return 1; 12 | } 13 | asic_free(device); 14 | return 0; 15 | } 16 | 17 | int test_RRCA() { 18 | asic_t *device = asic_init(TI83p, NULL); 19 | uint8_t test[] = { 0x0F }; // RRCA 20 | device->cpu->registers.A = 1; 21 | flash(device, test, sizeof(test)); 22 | int cycles = cpu_execute(device->cpu, 4); 23 | if (device->cpu->registers.A != 0x80 || 24 | device->cpu->registers.flags.C != 1 || 25 | cycles != 0) { 26 | asic_free(device); 27 | return 1; 28 | } 29 | asic_free(device); 30 | return 0; 31 | } 32 | 33 | int test_RLA() { 34 | asic_t *device = asic_init(TI83p, NULL); 35 | uint8_t test[] = { 0x17 }; // RLA 36 | device->cpu->registers.A = 0x80; 37 | flash(device, test, sizeof(test)); 38 | int cycles = cpu_execute(device->cpu, 4); 39 | if (device->cpu->registers.A != 0 || 40 | device->cpu->registers.flags.C != 1 || 41 | cycles != 0) { 42 | asic_free(device); 43 | return 1; 44 | } 45 | asic_free(device); 46 | return 0; 47 | } 48 | 49 | int test_RRA() { 50 | asic_t *device = asic_init(TI83p, NULL); 51 | uint8_t test[] = { 0x1F }; // RRA 52 | device->cpu->registers.A = 1; 53 | flash(device, test, sizeof(test)); 54 | int cycles = cpu_execute(device->cpu, 4); 55 | if (device->cpu->registers.A != 0 || 56 | device->cpu->registers.flags.C != 1 || 57 | cycles != 0) { 58 | asic_free(device); 59 | return 1; 60 | } 61 | asic_free(device); 62 | return 0; 63 | } 64 | 65 | int test_RLC() { 66 | asic_t *device = asic_init(TI83p, NULL); 67 | uint8_t test[] = { 0xCB, 0x00 }; // RLC B 68 | device->cpu->registers.B = 0x80; 69 | flash(device, test, sizeof(test)); 70 | int cycles = cpu_execute(device->cpu, 8); 71 | if (device->cpu->registers.B != 1 || 72 | device->cpu->registers.flags.C != 1 || 73 | cycles != 0) { 74 | asic_free(device); 75 | return 1; 76 | } 77 | asic_free(device); 78 | return 0; 79 | } 80 | 81 | int test_RRC() { 82 | asic_t *device = asic_init(TI83p, NULL); 83 | uint8_t test[] = { 0xCB, 0x08 }; // RRC B 84 | device->cpu->registers.B = 1; 85 | flash(device, test, sizeof(test)); 86 | int cycles = cpu_execute(device->cpu, 8); 87 | if (device->cpu->registers.B != 0x80 || 88 | device->cpu->registers.flags.C != 1 || 89 | cycles != 0) { 90 | asic_free(device); 91 | return 1; 92 | } 93 | asic_free(device); 94 | return 0; 95 | } 96 | 97 | int test_RL() { 98 | asic_t *device = asic_init(TI83p, NULL); 99 | uint8_t test[] = { 0xCB, 0x10 }; // RL B 100 | device->cpu->registers.B = 0x80; 101 | device->cpu->registers.flags.C = 1; 102 | flash(device, test, sizeof(test)); 103 | int cycles = cpu_execute(device->cpu, 8); 104 | if (device->cpu->registers.B != 1 || 105 | device->cpu->registers.flags.C != 1 || 106 | cycles != 0) { 107 | asic_free(device); 108 | return 1; 109 | } 110 | asic_free(device); 111 | return 0; 112 | } 113 | 114 | int test_RR() { 115 | asic_t *device = asic_init(TI83p, NULL); 116 | uint8_t test[] = { 0xCB, 0x18 }; // RR B 117 | device->cpu->registers.B = 1; 118 | device->cpu->registers.flags.C = 0; 119 | flash(device, test, sizeof(test)); 120 | int cycles = cpu_execute(device->cpu, 8); 121 | if (device->cpu->registers.B != 0 || 122 | device->cpu->registers.flags.C != 1 || 123 | cycles != 0) { 124 | asic_free(device); 125 | return 1; 126 | } 127 | asic_free(device); 128 | return 0; 129 | } 130 | 131 | int test_SLA() { 132 | asic_t *device = asic_init(TI83p, NULL); 133 | uint8_t test[] = { 0xCB, 0x20 }; // SLA B 134 | device->cpu->registers.B = 0x80; 135 | device->cpu->registers.flags.C = 0; 136 | flash(device, test, sizeof(test)); 137 | int cycles = cpu_execute(device->cpu, 8); 138 | if (device->cpu->registers.B != 0 || 139 | device->cpu->registers.flags.C != 1 || 140 | cycles != 0) { 141 | asic_free(device); 142 | return 1; 143 | } 144 | asic_free(device); 145 | return 0; 146 | } 147 | 148 | int test_SRA() { 149 | asic_t *device = asic_init(TI83p, NULL); 150 | uint8_t test[] = { 0xCB, 0x28 }; // SRA B 151 | device->cpu->registers.B = 1; 152 | device->cpu->registers.flags.C = 0; 153 | flash(device, test, sizeof(test)); 154 | int cycles = cpu_execute(device->cpu, 8); 155 | if (device->cpu->registers.B != 0 || 156 | device->cpu->registers.flags.C != 1 || 157 | cycles != 0) { 158 | asic_free(device); 159 | return 1; 160 | } 161 | asic_free(device); 162 | return 0; 163 | } 164 | 165 | int test_SLL() { 166 | asic_t *device = asic_init(TI83p, NULL); 167 | uint8_t test[] = { 0xCB, 0x30 }; // SLL B 168 | device->cpu->registers.B = 1; 169 | device->cpu->registers.flags.C = 0; 170 | flash(device, test, sizeof(test)); 171 | int cycles = cpu_execute(device->cpu, 8); 172 | if (device->cpu->registers.B != 3 || 173 | device->cpu->registers.flags.C != 0 || 174 | cycles != 0) { 175 | asic_free(device); 176 | return 1; 177 | } 178 | asic_free(device); 179 | return 0; 180 | } 181 | 182 | int test_SRL() { 183 | asic_t *device = asic_init(TI83p, NULL); 184 | uint8_t test[] = { 0xCB, 0x38 }; // SRL B 185 | device->cpu->registers.B = 1; 186 | device->cpu->registers.flags.C = 0; 187 | flash(device, test, sizeof(test)); 188 | int cycles = cpu_execute(device->cpu, 8); 189 | if (device->cpu->registers.B != 0 || 190 | device->cpu->registers.flags.C != 1 || 191 | cycles != 0) { 192 | asic_free(device); 193 | return 1; 194 | } 195 | asic_free(device); 196 | return 0; 197 | } 198 | -------------------------------------------------------------------------------- /frontends/tui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../CMake) 2 | find_package(Scas REQUIRED) 3 | 4 | add_executable(z80e-tui 5 | main.c 6 | tui.c 7 | $ 8 | ) 9 | 10 | target_include_directories(z80e-tui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include) 11 | TARGET_LINK_LIBRARIES(z80e-tui readline) 12 | 13 | if(NOT APPLE AND NOT HAIKU) 14 | TARGET_LINK_LIBRARIES(z80e-tui rt) 15 | endif() 16 | TARGET_LINK_LIBRARIES(z80e-tui ${SCAS_LIBRARIES}) 17 | 18 | INSTALL(TARGETS z80e-tui DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 19 | -------------------------------------------------------------------------------- /frontends/tui/tui.c: -------------------------------------------------------------------------------- 1 | #include "tui.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | int print_tui(struct debugger_state *a, const char *b, ...) { 16 | va_list list; 17 | va_start(list, b); 18 | return vprintf(b, list); 19 | } 20 | 21 | int vprint_tui(struct debugger_state *a, const char *b, va_list list) { 22 | return vprintf(b, list); 23 | } 24 | 25 | void tui_close_window(struct debugger_state *state) { 26 | free(state); 27 | } 28 | 29 | debugger_state_t *tui_new_state(struct debugger_state *state, const char *title) { 30 | debugger_state_t *stat = calloc(sizeof(debugger_state_t), 1); 31 | stat->print = print_tui; 32 | stat->vprint = vprint_tui; 33 | stat->state = 0; 34 | stat->interface_state = state->interface_state; 35 | stat->asic = state->asic; 36 | stat->debugger = state->debugger; 37 | stat->create_new_state = tui_new_state; 38 | stat->close_window = tui_close_window; 39 | stat->log = state->log; 40 | init_link(stat); 41 | return stat; 42 | } 43 | 44 | tui_state_t *current_state; 45 | 46 | #define dprint(...) printf(__VA_ARGS__) 47 | void tui_init(tui_state_t *state) { 48 | debugger_state_t dstate = { print_tui, vprint_tui, 0, state, state->debugger->asic, state->debugger, tui_new_state, tui_close_window, state->debugger->asic->log}; 49 | debugger_state_t *used_state = tui_new_state(&dstate, "Sourcing z80erc..."); 50 | log_message(dstate.log, L_DEBUG, "TUI", "Running commands in z80erc..."); 51 | debugger_source_rc(used_state, "z80erc"); 52 | tui_close_window(used_state); 53 | } 54 | 55 | struct tui_disasm { 56 | ti_mmu_t *mmu; 57 | char *string_pointer; 58 | }; 59 | 60 | uint8_t tui_disassemble_read(struct disassemble_memory *state, uint16_t mem) { 61 | struct tui_disasm *disasm = state->extra_data; 62 | return ti_read_byte(disasm->mmu, mem); 63 | } 64 | 65 | int tui_disassemble_write(struct disassemble_memory *state, const char *format, ...) { 66 | struct tui_disasm *disasm = state->extra_data; 67 | va_list list; 68 | va_start(list, format); 69 | int count = vsprintf(disasm->string_pointer, format, list); 70 | disasm->string_pointer += count; 71 | return count; 72 | } 73 | 74 | void tui_tick(tui_state_t *state) { 75 | current_state = state; 76 | asic_t *asic = state->debugger->asic; 77 | struct tui_disasm disasm_custom = { asic->mmu, 0 }; 78 | struct disassemble_memory disasm = { tui_disassemble_read, 0, &disasm_custom }; 79 | while (1) { 80 | char prompt_buffer[80]; 81 | char *current_pointer = prompt_buffer; 82 | ti_mmu_bank_state_t *st = &asic->mmu->banks[asic->cpu->registers.PC / 0x4000]; 83 | current_pointer += sprintf(prompt_buffer, "z80e [%c:%02X:0x%04X ", st->flash ? 'F' : 'R', st->page, asic->cpu->registers.PC); 84 | 85 | disasm_custom.string_pointer = current_pointer; 86 | disasm.current = asic->cpu->registers.PC; 87 | parse_instruction(&disasm, tui_disassemble_write, state->debugger->flags.knightos); 88 | current_pointer = disasm_custom.string_pointer; 89 | 90 | sprintf(current_pointer, "] %s> ", asic->cpu->halted ? "HALT " : ""); 91 | char *result = readline(prompt_buffer); 92 | if (result) { 93 | int from_history = 0; 94 | 95 | if (*result == 0) { 96 | HIST_ENTRY *hist = history_get(where_history()); 97 | if (hist == 0) { 98 | free(result); 99 | continue; 100 | } 101 | result = (char *)hist->line; 102 | from_history = 1; 103 | } 104 | if (strcmp(result, "exit") == 0) { 105 | break; 106 | } 107 | 108 | add_history(result); 109 | 110 | debugger_state_t dstate = { print_tui, vprint_tui, 0, state, asic, state->debugger, tui_new_state, tui_close_window, asic->log }; 111 | debugger_state_t *used_state = tui_new_state(&dstate, result); 112 | 113 | int retval = debugger_exec(used_state, result); 114 | if (retval > 0) { 115 | dprint("The command returned %d\n", retval); 116 | } 117 | 118 | tui_close_window(used_state); 119 | 120 | if (!from_history) { 121 | free(result); 122 | } 123 | } else if (result == NULL) { 124 | break; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /frontends/tui/tui.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_TUI_H 2 | #define DEBUGGER_TUI_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | debugger_t *debugger; 9 | } tui_state_t; 10 | 11 | void tui_init(tui_state_t *state); 12 | void tui_tick(tui_state_t *state); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /frontends/zex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../CMake) 2 | find_package(Scas REQUIRED) 3 | 4 | add_executable(zex 5 | zex.c 6 | $ 7 | ) 8 | 9 | target_include_directories(zex PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../libz80e/include) 10 | TARGET_LINK_LIBRARIES(zex readline ${SCAS_LIBRARIES}) 11 | 12 | if(NOT APPLE AND NOT HAIKU) 13 | TARGET_LINK_LIBRARIES(zex rt) 14 | endif() 15 | -------------------------------------------------------------------------------- /frontends/zex/zex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int stop = 0; 9 | 10 | void cpu_reset(void *device, uint8_t data) { 11 | printf("Jumped to 0x00!\n"); 12 | exit(0); 13 | } 14 | 15 | uint8_t write_text(void *device) { 16 | asic_t *asic = device; 17 | 18 | if (asic->cpu->registers.C == 2) { 19 | printf("%c", asic->cpu->registers.E); 20 | } else if (asic->cpu->registers.C == 9) { 21 | int i; 22 | for (i = asic->cpu->registers.DE; asic->mmu->ram[i & 0xffff] != '$'; i++) { 23 | printf("%c", asic->mmu->ram[i & 0xffff]); 24 | if (asic->mmu->ram[i & 0xffff] == 0) { 25 | break; 26 | } 27 | } 28 | } 29 | fflush(stdout); 30 | return 0; 31 | } 32 | 33 | int main(int argc, char **argv) { 34 | if (argc != 2) { 35 | printf("zex `file` - run the Z80 instruction exerciser\n"); 36 | return 1; 37 | } 38 | 39 | asic_t *device = asic_init(TI84p, NULL); 40 | 41 | int i = 0; 42 | for (i = 0; i < 0x100; i++) { 43 | device->cpu->devices[i].write_out = cpu_reset; 44 | device->cpu->devices[i].read_in = write_text; 45 | device->cpu->devices[i].device = device; 46 | } 47 | 48 | long file_size; 49 | FILE *file; 50 | 51 | if ((file = fopen(argv[1], "rb")) == NULL) { 52 | printf("%s\n", strerror(errno)); 53 | return 1; 54 | } 55 | fseek(file, 0, SEEK_END); 56 | 57 | file_size = ftell(file); 58 | fseek(file, 0, SEEK_SET); 59 | fread(device->mmu->ram + 0x100, 1, file_size, file); 60 | fclose(file); 61 | 62 | device->mmu->ram[0] = 0xd3; /* OUT N, A */ 63 | device->mmu->ram[1] = 0x00; 64 | 65 | device->mmu->ram[5] = 0xdb; /* IN A, N */ 66 | device->mmu->ram[6] = 0x00; 67 | device->mmu->ram[7] = 0xc9; /* RET */ 68 | 69 | device->mmu->banks[0].page = 0; 70 | device->mmu->banks[1].page = 1; 71 | device->mmu->banks[2].page = 2; 72 | device->mmu->banks[3].page = 3; 73 | 74 | device->mmu->banks[0].flash = 0; 75 | device->mmu->banks[1].flash = 0; 76 | device->mmu->banks[2].flash = 0; 77 | device->mmu->banks[3].flash = 0; 78 | 79 | device->cpu->registers.PC = 0x100; 80 | 81 | while (!stop) { 82 | cpu_execute(device->cpu, 10000); 83 | } 84 | return 0; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /gpl/zexall.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnightOS/z80e/a832362be5ce070b10401503b74eb8bdb0f93eb9/gpl/zexall.com -------------------------------------------------------------------------------- /gpl/zexdoc.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KnightOS/z80e/a832362be5ce070b10401503b74eb8bdb0f93eb9/gpl/zexdoc.com -------------------------------------------------------------------------------- /libz80e/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project (z80e_objects) 3 | 4 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/../CMake) 5 | message(STATUS ${CMAKE_MODULE_PATH}) 6 | find_package(Scas REQUIRED) 7 | FILE(GLOB Hardware src/ti/hardware/*.c) 8 | FILE(GLOB Commands src/debugger/commands/*.c) 9 | FILE(GLOB scas_common ../scas/common/*.c) 10 | include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/include/) 11 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/z80e) 12 | include_directories(${SCAS_INCLUDES}) 13 | 14 | if(DEFINED EMSCRIPTEN) 15 | add_compile_options(-Wno-warn-absolute-paths) 16 | add_definitions(-DEMSCRIPTEN) 17 | endif() 18 | 19 | add_library(z80e_objects OBJECT 20 | src/core/cpu.c 21 | src/core/registers.c 22 | src/debugger/debugger.c 23 | src/debugger/hooks.c 24 | src/disassembler/disassemble.c 25 | src/ti/asic.c 26 | src/ti/memory.c 27 | src/log/log.c 28 | src/runloop/runloop.c 29 | ${Hardware} 30 | ${Commands} 31 | ${scas_common} 32 | ) 33 | -------------------------------------------------------------------------------- /libz80e/include/z80e/core/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef CPU_H 2 | #define CPU_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | typedef struct z80cpu z80cpu_t; 10 | typedef struct z80iodevice z80iodevice_t; 11 | 12 | #include 13 | 14 | struct z80iodevice { 15 | void *device; 16 | uint8_t (*read_in)(void *); 17 | void (*write_out)(void *, uint8_t); 18 | }; 19 | 20 | struct z80cpu { 21 | z80iodevice_t devices[0x100]; 22 | z80registers_t registers; 23 | struct { 24 | uint8_t IFF1 : 1; 25 | uint8_t IFF2 : 1; 26 | uint8_t int_mode : 2; 27 | // Internal use: 28 | uint8_t IFF_wait : 1; 29 | uint8_t halted : 1; 30 | }; 31 | uint8_t bus; 32 | uint16_t prefix; 33 | void *memory; 34 | uint8_t (*read_byte)(void *, uint16_t); 35 | void (*write_byte)(void *, uint16_t, uint8_t); 36 | int interrupt; 37 | hook_info_t *hook; 38 | log_t *log; 39 | }; 40 | 41 | 42 | uint8_t cpu_read_register_byte(z80cpu_t *, registers); 43 | uint16_t cpu_read_register_word(z80cpu_t *, registers); 44 | 45 | uint8_t cpu_write_register_byte(z80cpu_t *, registers, uint8_t); 46 | uint16_t cpu_write_register_word(z80cpu_t *, registers, uint16_t); 47 | 48 | z80cpu_t* cpu_init(log_t *log); 49 | void cpu_free(z80cpu_t *cpu); 50 | uint8_t cpu_read_byte(z80cpu_t *cpu, uint16_t address); 51 | void cpu_write_byte(z80cpu_t *cpu, uint16_t address, uint8_t value); 52 | uint16_t cpu_read_word(z80cpu_t *cpu, uint16_t address); 53 | void cpu_write_word(z80cpu_t *cpu, uint16_t address, uint16_t value); 54 | int cpu_execute(z80cpu_t *cpu, int cycles); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /libz80e/include/z80e/core/registers.h: -------------------------------------------------------------------------------- 1 | #ifndef REGISTERS_H 2 | #define REGISTERS_H 3 | #include 4 | 5 | typedef struct { 6 | union { 7 | uint16_t AF; 8 | struct { 9 | union { 10 | uint8_t F; 11 | struct { 12 | uint8_t C : 1; 13 | uint8_t N : 1; 14 | uint8_t PV : 1; 15 | uint8_t _3 : 1; 16 | uint8_t H : 1; 17 | uint8_t _5 : 1; 18 | uint8_t Z : 1; 19 | uint8_t S : 1; 20 | } flags; 21 | }; 22 | uint8_t A; 23 | }; 24 | }; 25 | union { 26 | uint16_t BC; 27 | struct { 28 | uint8_t C; 29 | uint8_t B; 30 | }; 31 | }; 32 | union { 33 | uint16_t DE; 34 | struct { 35 | uint8_t E; 36 | uint8_t D; 37 | }; 38 | }; 39 | union { 40 | uint16_t HL; 41 | struct { 42 | uint8_t L; 43 | uint8_t H; 44 | }; 45 | }; 46 | uint16_t _AF, _BC, _DE, _HL; 47 | uint16_t PC, SP; 48 | union { 49 | uint16_t IX; 50 | struct { 51 | uint8_t IXL; 52 | uint8_t IXH; 53 | }; 54 | }; 55 | union { 56 | uint16_t IY; 57 | struct { 58 | uint8_t IYL; 59 | uint8_t IYH; 60 | }; 61 | }; 62 | uint8_t I, R; 63 | uint16_t WZ; 64 | } z80registers_t; 65 | 66 | typedef enum { 67 | FLAG_S = 1 << 7, 68 | FLAG_Z = 1 << 6, 69 | FLAG_5 = 1 << 5, 70 | FLAG_H = 1 << 4, 71 | FLAG_3 = 1 << 3, 72 | FLAG_PV = 1 << 2, 73 | FLAG_N = 1 << 1, 74 | FLAG_C = 1 << 0, 75 | FLAG_NONE = 0 76 | } z80flags; 77 | 78 | typedef enum { 79 | A = (1 << 0), F = (1 << 1), AF = (1 << 2), _AF = (1 << 3), 80 | B = (1 << 4), C = (1 << 5), BC = (1 << 6), _BC = (1 << 7), 81 | D = (1 << 8), E = (1 << 9), DE = (1 << 10), _DE = (1 << 11), 82 | H = (1 << 12), L = (1 << 13), HL = (1 << 14), _HL = (1 << 15), 83 | PC = (1 << 16), SP = (1 << 17), I = (1 << 18), R = (1 << 19), 84 | IXH = (1 << 20), IXL = (1 << 21), IX = (1 << 22), 85 | IYH = (1 << 23), IYL = (1 << 24), IY = (1 << 25), 86 | } registers; 87 | 88 | void exAFAF(z80registers_t *r); 89 | void exDEHL(z80registers_t *r); 90 | void exx(z80registers_t *r); 91 | int parity(uint8_t x); 92 | 93 | // S Z 5 H 3 PV N C 94 | #define __flag_s(a) ((a) ? FLAG_S : 0) 95 | #define __flag_5(a) ((a) ? FLAG_5 : 0) 96 | #define __flag_h(a) ((a) ? FLAG_H : 0) 97 | #define __flag_3(a) ((a) ? FLAG_3 : 0) 98 | #define __flag_pv(a) ((a) ? FLAG_PV : 0) 99 | #define __flag_c(a) ((a) ? FLAG_C : 0) 100 | 101 | #define _flag_carry_16(a) __flag_c((a) & 0x10000) 102 | #define _flag_carry_8(a) __flag_c((a) & 0x100) 103 | 104 | #define _flag_sign_16(a) __flag_s((a) & 0x8000) 105 | #define _flag_sign_8(a) __flag_s((a) & 0x80) 106 | 107 | #define _flag_parity(a) __flag_pv(!parity(a)) 108 | 109 | #define _flag_undef_8(a) ({ uint8_t _res = (a); __flag_5(_res & 0x20) | __flag_3(_res & 0x8);}) 110 | #define _flag_undef_8_block(a) ({ uint8_t _res = (a); __flag_5(_res & 0x2) | __flag_3(_res & 0x8);}) 111 | #define _flag_undef_16(a) ({ uint16_t _res = (a); __flag_5(_res & 0x2000) | __flag_3(_res & 0x800);}) 112 | 113 | #define _flag_overflow_8_add(op1, op2, result) __flag_pv((op1 & 0x80) == (op2 & 0x80) && (op1 & 0x80) != (result & 0x80)) 114 | #define _flag_overflow_8_sub(op1, op2, result) __flag_pv((op1 & 0x80) != (op2 & 0x80) && (op1 & 0x80) != (result & 0x80)) 115 | #define _flag_overflow_16_add(op1, op2, result) __flag_pv((op1 & 0x8000) == (op2 & 0x8000) && (op1 & 0x8000) != (result & 0x8000)) 116 | #define _flag_overflow_16_sub(op1, op2, result) __flag_pv((op1 & 0x8000) != (op2 & 0x8000) && (op1 & 0x8000) != (result & 0x8000)) 117 | 118 | #define _flag_halfcarry_8_add(op1, op2, carry) __flag_h(((op1 & 0xf) + (op2 & 0xf) + carry) & 0x10) 119 | #define _flag_halfcarry_8_sub(op1, op2, carry) __flag_h(((op1 & 0xf) - (op2 & 0xf) - carry) & 0x10) 120 | #define _flag_halfcarry_16_add(op1, op2, carry) __flag_h(((op1 & 0xfff) + (op2 & 0xfff) + carry) & 0x1000) 121 | #define _flag_halfcarry_16_sub(op1, op2, carry) __flag_h(((op1 & 0xfff) - (op2 & 0xfff) - carry) & 0x1000) 122 | 123 | #define _flag_subtract(a) ((a) ? FLAG_N : 0) 124 | #define _flag_zero(a) ((a) ? 0 : FLAG_Z) 125 | 126 | void print_state(z80registers_t *); 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /libz80e/include/z80e/debugger/commands.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_COMMANDS_H 2 | #define DEBUGGER_COMMANDS_H 3 | 4 | #include 5 | #include 6 | 7 | int command_hexdump(debugger_state_t *state, int argc, char **argv); 8 | int command_backwards_hexdump(debugger_state_t *state, int argc, char **argv); 9 | int command_disassemble(debugger_state_t *state, int argc, char **argv); 10 | int command_print_registers(debugger_state_t *state, int argc, char **argv); 11 | int command_print_expression(debugger_state_t *state, int argc, char **argv); 12 | int command_stack(debugger_state_t *state, int argc, char **argv); 13 | int command_print_mappings(debugger_state_t *state, int argc, char **argv); 14 | int command_unhalt(debugger_state_t *state, int argc, char **argv); 15 | int command_run(debugger_state_t *state, int argc, char **argv); 16 | int command_step(debugger_state_t *state, int argc, char **argv); 17 | int command_stop(debugger_state_t *state, int argc, char **argv); 18 | int command_on(debugger_state_t *state, int argc, char **argv); 19 | int command_in(debugger_state_t *state, int argc, char **argv); 20 | int command_out(debugger_state_t *state, int argc, char **argv); 21 | int command_break(debugger_state_t *state, int argc, char **argv); 22 | int command_step_over(debugger_state_t *state, int argc, char **argv); 23 | int command_link(debugger_state_t *state, int argc, char **argv); 24 | int command_dump_lcd(debugger_state_t *state, int argc, char **argv); 25 | int command_turn_on(debugger_state_t *state, int argc, char **argv); 26 | int command_press_key(debugger_state_t *state, int argc, char **argv); 27 | int command_release_key(debugger_state_t *state, int argc, char **argv); 28 | int command_load_register(debugger_state_t *state, int argc, char **argv); 29 | int command_tap_key(debugger_state_t *state, int argc, char **argv); 30 | int command_timer(debugger_state_t *state, int argc, char **argv); 31 | uint16_t parse_expression_z80e(debugger_state_t *, const char *); 32 | 33 | void init_link(struct debugger_state *state); 34 | #endif 35 | -------------------------------------------------------------------------------- /libz80e/include/z80e/debugger/debugger.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_H 2 | #define DEBUGGER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct debugger_state debugger_state_t; 8 | typedef struct debugger debugger_t; 9 | typedef int (*debugger_function_t)(debugger_state_t *, int, char **); 10 | 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | const char *name; 16 | debugger_function_t function; 17 | int priority; 18 | void *state; 19 | } debugger_command_t; 20 | 21 | typedef struct { 22 | int count; 23 | int capacity; 24 | debugger_command_t **commands; 25 | } debugger_list_t; 26 | 27 | struct debugger_state { 28 | int (*print)(debugger_state_t *, const char *, ...); 29 | int (*vprint)(debugger_state_t *, const char *, va_list); 30 | void *state; 31 | void *interface_state; 32 | asic_t *asic; 33 | debugger_t *debugger; 34 | debugger_state_t *(*create_new_state)(debugger_state_t *, const char *command_name); 35 | void (*close_window)(debugger_state_t *); 36 | log_t *log; 37 | }; 38 | 39 | typedef enum { 40 | DEBUGGER_DISABLED, 41 | DEBUGGER_ENABLED, 42 | DEBUGGER_LONG_OPERATION, 43 | DEBUGGER_LONG_OPERATION_INTERRUPTABLE 44 | } debugger_operation_state; 45 | 46 | struct debugger { 47 | struct { 48 | int echo : 1; 49 | int echo_reg : 1; 50 | int auto_on : 1; 51 | int knightos : 1; 52 | int nointonstep : 1; 53 | } flags; 54 | 55 | debugger_list_t commands; 56 | asic_t *asic; 57 | debugger_operation_state state; 58 | }; 59 | 60 | int debugger_source_rc(debugger_state_t *, const char *rc_name); 61 | 62 | debugger_t *init_debugger(asic_t *); 63 | void free_debugger(debugger_t *); 64 | 65 | int find_best_command(debugger_t *, const char *, debugger_command_t **); 66 | void register_command(debugger_t *, const char *, debugger_function_t, void *, int); 67 | 68 | char **debugger_parse_commandline(const char *, int *); 69 | int debugger_exec(debugger_state_t *, const char *); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /libz80e/include/z80e/debugger/hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_HOOKS_H 2 | #define DEBUGGER_HOOKS_H 3 | 4 | #include 5 | 6 | typedef struct hook_info hook_info_t; 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | hook_info_t *create_hook_set(asic_t *asic); 13 | 14 | // Memory hooks 15 | 16 | uint8_t hook_on_memory_read(hook_info_t *, uint16_t address, uint8_t value); 17 | uint8_t hook_on_memory_write(hook_info_t *, uint16_t address, uint8_t value); 18 | 19 | typedef uint8_t (*hook_memory_callback)(void *data, uint16_t address, uint8_t value); 20 | 21 | void hook_remove_memory_read(hook_info_t *, int); 22 | int hook_add_memory_read(hook_info_t *, uint16_t address_start, uint16_t address_end, void *data, hook_memory_callback); 23 | void hook_remove_register_write(hook_info_t *, int); 24 | int hook_add_memory_write(hook_info_t *, uint16_t address_start, uint16_t address_end, void *data, hook_memory_callback); 25 | 26 | // Register hooks 27 | 28 | uint16_t hook_on_register_read(hook_info_t *, registers flags, uint16_t value); 29 | uint16_t hook_on_register_write(hook_info_t *, registers flags, uint16_t value); 30 | 31 | typedef uint16_t (*hook_register_callback)(void *data, registers reg, uint16_t value); 32 | 33 | void hook_remove_register_read(hook_info_t *, int); 34 | int hook_add_register_read(hook_info_t *, registers flags, void *data, hook_register_callback); 35 | void hook_remove_register_write(hook_info_t *, int); 36 | int hook_add_register_write(hook_info_t *, registers flags, void *data, hook_register_callback); 37 | 38 | // Port hooks 39 | 40 | uint8_t hook_on_port_in(hook_info_t *, uint8_t port, uint8_t value); 41 | uint8_t hook_on_port_out(hook_info_t *, uint8_t port, uint8_t value); 42 | 43 | typedef uint8_t (*hook_port_callback)(void *data, uint8_t port, uint8_t value); 44 | 45 | void hook_remove_port_in(hook_info_t *, int); 46 | int hook_add_port_in(hook_info_t *, uint8_t port_range_start, uint8_t port_range_end, void *data, hook_port_callback); 47 | void hook_remove_port_out(hook_info_t *, int); 48 | int hook_add_port_out(hook_info_t *, uint8_t port_range_start, uint8_t port_range_end, void *data, hook_port_callback); 49 | 50 | // Execution hooks 51 | 52 | void hook_on_before_execution(hook_info_t *, uint16_t address); 53 | void hook_on_after_execution(hook_info_t *, uint16_t address); 54 | 55 | typedef void (*hook_execution_callback)(void *data, uint16_t address); 56 | 57 | void hook_remove_before_execution(hook_info_t *, int); 58 | int hook_add_before_execution(hook_info_t *, void *data, hook_execution_callback); 59 | void hook_remove_after_execution(hook_info_t *, int); 60 | int hook_add_after_execution(hook_info_t *, void *data, hook_execution_callback); 61 | 62 | // LCD hook 63 | 64 | void hook_on_lcd_update(hook_info_t *, ti_bw_lcd_t *); 65 | 66 | typedef void (*hook_lcd_update_callback)(void *data, ti_bw_lcd_t *lcd); 67 | 68 | void hook_remove_lcd_update(hook_info_t *, int); 69 | int hook_add_lcd_update(hook_info_t *, void *data, hook_lcd_update_callback); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /libz80e/include/z80e/debugger/keys.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_KEYS_H 2 | #define DEBUGGER_KEYS_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | char *key; 8 | uint8_t value; 9 | } key_string_t; 10 | 11 | // TODO: Add aliases 12 | static const key_string_t key_strings[] = { 13 | { "DOWN", 0x00 }, 14 | { "LEFT", 0x01 }, 15 | { "RIGHT", 0x02 }, 16 | { "UP", 0x03 }, 17 | { "ENTER", 0x10 }, 18 | { "+", 0x11 }, 19 | { "-", 0x12 }, 20 | { "*", 0x13 }, 21 | { "/", 0x14 }, 22 | { "^", 0x15 }, 23 | { "CLEAR", 0x16 }, 24 | { "(-)", 0x20 }, 25 | { "3", 0x21 }, 26 | { "6", 0x22 }, 27 | { "9", 0x23 }, 28 | { ")", 0x24 }, 29 | { "TAN", 0x25 }, 30 | { "VARS", 0x26 }, 31 | { ".", 0x30 }, 32 | { "2", 0x31 }, 33 | { "5", 0x32 }, 34 | { "8", 0x33 }, 35 | { "(", 0x34 }, 36 | { "COS", 0x35 }, 37 | { "PRGM", 0x36 }, 38 | { "STAT", 0x37 }, 39 | { "0", 0x40 }, 40 | { "1", 0x41 }, 41 | { "4", 0x42 }, 42 | { "7", 0x43 }, 43 | { ",", 0x44 }, 44 | { "SIN", 0x45 }, 45 | { "APPS", 0x46 }, 46 | { "XTON", 0x47 }, 47 | { "STO", 0x51 }, 48 | { "LN", 0x52 }, 49 | { "LOG", 0x53 }, 50 | { "X2", 0x54 }, 51 | { "X-1", 0x55 }, 52 | { "MATH", 0x56 }, 53 | { "ALPHA", 0x57 }, 54 | { "GRAPH", 0x60 }, 55 | { "TRACE", 0x61 }, 56 | { "ZOOM", 0x62 }, 57 | { "WIND", 0x63 }, 58 | { "Y=", 0x64 }, 59 | { "2nd", 0x65 }, 60 | { "MODE", 0x66 }, 61 | { "DEL", 0x67 } 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /libz80e/include/z80e/disassembler/disassemble.h: -------------------------------------------------------------------------------- 1 | #ifndef DISASSEMBLE_H 2 | #define DISASSEMBLE_H 3 | 4 | #include 5 | #include 6 | 7 | struct disassemble_memory { 8 | uint8_t (*read_byte)(struct disassemble_memory *, uint16_t); 9 | uint16_t current; 10 | void *extra_data; 11 | }; 12 | 13 | 14 | typedef int (*write_pointer)(struct disassemble_memory *, const char *, ...); 15 | 16 | uint16_t parse_instruction(struct disassemble_memory *, write_pointer, bool); 17 | 18 | void disassembler_init(); 19 | 20 | void disassembler_load_object(const char *path); 21 | 22 | int get_symbol(struct disassemble_memory *memory, const char *name, uint16_t *val); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /libz80e/include/z80e/log/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | 6 | typedef enum { 7 | L_ERROR = 0, 8 | L_WARN = 1, 9 | L_INFO = 2, 10 | L_DEBUG = 3, 11 | } loglevel_t; 12 | 13 | typedef void (*log_func)(void *, loglevel_t, const char *, const char *, va_list); 14 | 15 | typedef struct { 16 | void *data; 17 | log_func log; 18 | int logging_level; 19 | } log_t; 20 | 21 | log_t *init_log_z80e(log_func, void *, int); 22 | void log_message(log_t *, loglevel_t, const char *, const char *, ...); 23 | void free_log(log_t *); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /libz80e/include/z80e/runloop/runloop.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNLOOP_H 2 | #define RUNLOOP_H 3 | 4 | typedef struct runloop_state runloop_state_t; 5 | #include 6 | 7 | 8 | runloop_state_t *runloop_init(asic_t *); 9 | 10 | void runloop_tick_cycles(runloop_state_t *, int); 11 | void runloop_tick(runloop_state_t *); 12 | #endif 13 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/asic.h: -------------------------------------------------------------------------------- 1 | #ifndef ASIC_H 2 | #define ASIC_H 3 | 4 | #include 5 | #ifndef NOLINK 6 | #include 7 | #endif 8 | 9 | typedef struct asic asic_t; 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | typedef enum { 21 | BATTERIES_REMOVED, 22 | BATTERIES_LOW, 23 | BATTERIES_GOOD 24 | } battery_state; 25 | 26 | 27 | typedef void (*timer_tick)(asic_t *, void *); 28 | typedef struct z80_hardware_timers z80_hardware_timers_t; 29 | typedef struct z80_hardware_timer z80_hardware_timer_t; 30 | typedef struct z80_link_socket z80_link_socket_t; 31 | 32 | enum { 33 | TIMER_IN_USE = (1 << 0), 34 | TIMER_ONE_SHOT = (1 << 1) 35 | }; 36 | 37 | struct z80_hardware_timer { 38 | int cycles_until_tick; 39 | 40 | int flags; 41 | double frequency; 42 | timer_tick on_tick; 43 | void *data; 44 | }; 45 | 46 | struct z80_hardware_timers { 47 | int max_timers; 48 | z80_hardware_timer_t *timers; 49 | }; 50 | 51 | struct z80_link_socket { 52 | #ifndef NOLINK 53 | int accept; 54 | struct pollfd listenfd; 55 | struct pollfd clients[10]; 56 | #endif 57 | }; 58 | 59 | struct asic { 60 | int stopped; 61 | ti_device_type device; 62 | battery_state battery; 63 | int battery_remove_check; 64 | int clock_rate; 65 | 66 | z80cpu_t* cpu; 67 | runloop_state_t *runloop; 68 | ti_mmu_t* mmu; 69 | ti_interrupts_t *interrupts; 70 | z80_hardware_timers_t *timers; 71 | z80_link_socket_t *link; 72 | hook_info_t *hook; 73 | log_t *log; 74 | debugger_t *debugger; 75 | }; 76 | 77 | asic_t* asic_init(ti_device_type, log_t *); 78 | void asic_free(asic_t*); 79 | 80 | int asic_set_clock_rate(asic_t *, int); 81 | 82 | int asic_add_timer(asic_t *, int, double, timer_tick, void *); 83 | void asic_remove_timer(asic_t *, int); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/flash.h: -------------------------------------------------------------------------------- 1 | #ifndef FLASH_H 2 | #define FLASH_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | asic_t *asic; 8 | uint8_t chip_size; 9 | } flash_state_t; 10 | 11 | void init_flash_ports(asic_t *asic); 12 | void free_flash_ports(asic_t *asic); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/interrupts.h: -------------------------------------------------------------------------------- 1 | #ifndef TI_HARDWARE_INTERRUPTS_H 2 | #define TI_HARDWARE_INTERRUPTS_H 3 | 4 | typedef struct ti_interrupts ti_interrupts_t; 5 | 6 | #include 7 | #include 8 | 9 | struct ti_interrupts { 10 | asic_t *asic; 11 | int first_timer_id; 12 | int second_timer_id; 13 | 14 | double first_timer_frequency; 15 | double second_timer_frequency; 16 | 17 | struct { 18 | int on_key : 1; 19 | int first_timer : 1; 20 | int second_timer : 1; 21 | int on_key_pressed : 1; 22 | int link_activity : 1; 23 | int first_crystal : 1; 24 | int second_crystal : 1; 25 | int third_crystal : 1; 26 | } interrupted; 27 | 28 | struct { 29 | int on_key : 1; 30 | int first_timer : 1; 31 | int second_timer : 1; 32 | int low_power_mode : 1; 33 | int link_activity : 1; 34 | int first_crystal : 1; 35 | int second_crystal : 1; 36 | int third_crystal : 1; 37 | } enabled; 38 | }; 39 | 40 | enum { 41 | INTERRUPT_ON_KEY = (1 << 0), 42 | INTERRUPT_FIRST_TIMER = (1 << 1), 43 | INTERRUPT_SECOND_TIMER = (1 << 2), 44 | INTERRUPT_ON_KEY_PRESSED = (1 << 3), 45 | INTERRUPT_LOW_POWER_MODE = (1 << 3), 46 | INTERRUPT_LINK_ACTIVITY = (1 << 4), 47 | INTERRUPT_FIRST_CRYSTAL = (1 << 5), 48 | INTERRUPT_SECOND_CRYSTAL = (1 << 6), 49 | INTERRUPT_THIRD_CRYSTAL = (1 << 7), 50 | }; 51 | 52 | z80iodevice_t init_interrupts(asic_t *, ti_interrupts_t **result); 53 | 54 | void ti_interrupts_interrupt(ti_interrupts_t *, int); 55 | void ti_interrupts_set_interrupt_enabled(ti_interrupts_t *, int, int); 56 | void ti_interrupts_acknowledge_interrupt(ti_interrupts_t *, int); 57 | 58 | void depress_on_key(ti_interrupts_t *); 59 | void release_on_key(ti_interrupts_t *); 60 | 61 | uint8_t read_interrupt_mask(void *); // port 03 62 | void write_interrupt_mask(void *, uint8_t); // port 03 63 | 64 | uint8_t read_interrupting_device(void *); // port 04 65 | void write_acknowledged_interrupts(void *, uint8_t); // port 02 66 | void write_timer_speed(void *, uint8_t); 67 | 68 | void set_first_timer_frequency(void *, double); 69 | void set_second_timer_frequency(void *, double); 70 | #endif 71 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARD_H 2 | #define KEYBOARD_H 3 | #include 4 | 5 | z80iodevice_t init_keyboard(); 6 | void free_keyboard(void *keyboard); 7 | void release_key(void *keyboard, uint8_t keycode); 8 | void depress_key(void *keyboard, uint8_t keycode); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/link.h: -------------------------------------------------------------------------------- 1 | #ifndef LINK_H 2 | #define LINK_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | asic_t *asic; 9 | struct { 10 | uint8_t tip : 1; 11 | uint8_t ring : 1; 12 | } them; 13 | struct { 14 | uint8_t tip : 1; 15 | uint8_t ring : 1; 16 | } us; 17 | union { 18 | uint8_t mask; 19 | struct { 20 | uint8_t rx : 1; 21 | uint8_t tx : 1; 22 | uint8_t error : 1; 23 | uint8_t : 4; 24 | uint8_t disabled : 1; 25 | }; 26 | } interrupts; 27 | struct { 28 | uint8_t rx_buffer; 29 | uint8_t tx_buffer; 30 | union { 31 | struct { 32 | uint8_t int_rx_ready : 1; 33 | uint8_t int_tx_ready : 1; 34 | uint8_t int_error : 1; 35 | uint8_t rx_active : 1; 36 | uint8_t rx_ready : 1; 37 | uint8_t tx_ready : 1; 38 | uint8_t error : 1; 39 | uint8_t tx_active : 1; 40 | }; 41 | uint8_t u8; 42 | } status; 43 | } assist; 44 | bool la_ready; 45 | } link_state_t; 46 | 47 | void init_link_ports(asic_t *asic); 48 | void free_link_ports(asic_t *asic); 49 | /** 50 | * Receives a byte via link assist. 51 | */ 52 | bool link_recv_byte(asic_t *asic, uint8_t val); 53 | /** 54 | * Reads a byte from the tx buffer and sets the link assist state to ready to send another byte. 55 | */ 56 | int link_read_tx_buffer(asic_t *asic); 57 | 58 | bool link_recv_ready(asic_t *asic); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/memorymapping.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct { 4 | asic_t *asic; 5 | int map_mode; 6 | 7 | uint8_t bank_a_page; 8 | int bank_a_flash; 9 | 10 | uint8_t bank_b_page; 11 | int bank_b_flash; 12 | 13 | uint8_t ram_bank_page; 14 | } memory_mapping_state_t; 15 | 16 | void reload_mapping(memory_mapping_state_t *); 17 | void init_mapping_ports(asic_t *asic); 18 | void free_mapping_ports(asic_t *asic); 19 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/speed.h: -------------------------------------------------------------------------------- 1 | #ifndef TI_HW_SPEED_H 2 | #define TI_HW_SPEED_H 3 | #include 4 | #include 5 | #include 6 | 7 | z80iodevice_t init_speed(asic_t *asic); 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/status.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUS_H 2 | #define STATUS_H 3 | #include 4 | #include 5 | #include 6 | 7 | z80iodevice_t init_status(asic_t *asic); 8 | void free_status(z80iodevice_t status); 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/t6a04.h: -------------------------------------------------------------------------------- 1 | #ifndef TI_DISPLAY_H 2 | #define TI_DISPLAY_H 3 | 4 | typedef struct ti_bw_lcd ti_bw_lcd_t; 5 | 6 | #include 7 | #include 8 | 9 | struct ti_bw_lcd { 10 | uint8_t up: 1; // set=up, unset=down 11 | uint8_t counter: 1; // set=Y, unset=X 12 | uint8_t word_length: 1; // set=8, unset=6 13 | uint8_t display_on: 1; // set=on, unset=off 14 | uint8_t op_amp1: 2; // 0-3 15 | uint8_t op_amp2: 2; // 0-3 16 | 17 | int X; // which is up-down 18 | int Y; // which is left-right 19 | int Z; // which is which y is rendered at top 20 | uint8_t contrast; // 0-63 21 | uint8_t read_reg; // on the hardware, reads are buffered in a register 22 | uint8_t *ram; // [X * 64 + Y] 23 | 24 | hook_info_t *hook; 25 | asic_t *asic; 26 | }; 27 | 28 | void setup_lcd_display(asic_t *, hook_info_t *); 29 | 30 | uint8_t bw_lcd_read_screen(ti_bw_lcd_t *, int, int); 31 | int bw_lcd_write_screen(ti_bw_lcd_t *, int, int, char); 32 | 33 | void bw_lcd_reset(ti_bw_lcd_t *); 34 | 35 | uint8_t bw_lcd_status_read(void *); 36 | void bw_lcd_status_write(void *, uint8_t); 37 | 38 | uint8_t bw_lcd_data_read(void *); 39 | void bw_lcd_data_write(void *, uint8_t); 40 | 41 | void bw_lcd_advance_int_pointer(ti_bw_lcd_t *, int *, int *); 42 | void bw_lcd_advance_pointer(ti_bw_lcd_t *); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/hardware/timers.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMERS_H 2 | #define TIMERS_H 3 | 4 | #include 5 | #include 6 | 7 | struct crystal_timer { 8 | asic_t *asic; 9 | uint8_t frequency; 10 | uint8_t loop; 11 | uint8_t counter; 12 | }; 13 | 14 | void init_crystal_timers(asic_t *asic); 15 | void free_crystal_timers(asic_t *asic); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | #include 4 | 5 | typedef struct ti_mmu ti_mmu_t; 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct { 14 | uint16_t ram_pages; 15 | uint16_t flash_pages; 16 | } ti_mmu_settings_t; 17 | 18 | typedef struct { 19 | uint8_t page; 20 | int flash; 21 | } ti_mmu_bank_state_t; 22 | 23 | typedef struct { 24 | uint32_t address; 25 | uint32_t address_mask; 26 | uint8_t value; 27 | uint8_t value_mask; 28 | } flash_write_t; 29 | 30 | struct ti_mmu { 31 | ti_mmu_settings_t settings; 32 | ti_mmu_bank_state_t banks[4]; 33 | uint8_t *ram; 34 | uint8_t *flash; 35 | int flash_unlocked; 36 | int flash_write_index; 37 | flash_write_t flash_writes[6]; 38 | hook_info_t *hook; 39 | log_t *log; 40 | }; 41 | 42 | ti_mmu_t *ti_mmu_init(ti_device_type, log_t *); 43 | void ti_mmu_free(ti_mmu_t *mmu); 44 | uint8_t ti_read_byte(void *memory, uint16_t address); 45 | void ti_write_byte(void *memory, uint16_t address, uint8_t value); 46 | void mmu_force_write(void *memory, uint16_t address, uint8_t value); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /libz80e/include/z80e/ti/ti.h: -------------------------------------------------------------------------------- 1 | #ifndef TI_H 2 | #define TI_H 3 | 4 | typedef enum { 5 | TI73 = 0, 6 | TI83p = 1, 7 | TI83pSE = 2, 8 | TI84p = 3, 9 | TI84pSE = 4, 10 | TI84pCSE = 5 11 | } ti_device_type; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /libz80e/src/core/registers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "core/registers.h" 4 | 5 | void exAFAF(z80registers_t *r) { 6 | uint16_t temp = r->_AF; 7 | r->_AF = r->AF; 8 | r->AF = temp; 9 | } 10 | 11 | void exDEHL(z80registers_t *r) { 12 | uint16_t temp = r->HL; 13 | r->HL = r->DE; 14 | r->DE = temp; 15 | } 16 | 17 | void exx(z80registers_t *r) { 18 | uint16_t temp; 19 | temp = r->_HL; r->_HL = r->HL; r->HL = temp; 20 | temp = r->_DE; r->_DE = r->DE; r->DE = temp; 21 | temp = r->_BC; r->_BC = r->BC; r->BC = temp; 22 | } 23 | 24 | int parity(uint8_t x) { 25 | x ^= x >> 4; 26 | x ^= x >> 2; 27 | x ^= x >> 1; 28 | return x & 1; 29 | } 30 | 31 | void print_state(z80registers_t *r) { 32 | printf(" AF: 0x%04X BC: 0x%04X DE: 0x%04X HL: 0x%04X\n", r->AF, r->BC, r->DE, r->HL); 33 | printf(" 'AF: 0x%04X 'BC: 0x%04X 'DE: 0x%04X 'HL: 0x%04X\n", r->_AF, r->_BC, r->_DE, r->_HL); 34 | printf(" PC: 0x%04X SP: 0x%04X IX: 0x%04X IY: 0x%04X\n", r->PC, r->SP, r->IX, r->IY); 35 | printf("Flags: "); 36 | if (r->flags.S) printf("S "); 37 | if (r->flags.Z) printf("Z "); 38 | if (r->flags.H) printf("H "); 39 | if (r->flags._3) printf("5 "); 40 | if (r->flags.PV) printf("P/V "); 41 | if (r->flags._5) printf("3 "); 42 | if (r->flags.N) printf("N "); 43 | if (r->flags.C) printf("C "); 44 | if (r->F == 0) printf("None set"); 45 | printf("\n"); 46 | } 47 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/commands.c: -------------------------------------------------------------------------------- 1 | #include "ti/memory.h" 2 | #include "core/cpu.h" 3 | #include "debugger/commands.h" 4 | #include "debugger/debugger.h" 5 | #include "disassembler/disassemble.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct mmu_disassemble_memory { 12 | struct disassemble_memory mem; 13 | z80cpu_t *cpu; 14 | struct debugger_state *state; 15 | }; 16 | 17 | uint8_t parse_expression_dasm_read(struct disassemble_memory *s, uint16_t pointer) { 18 | struct mmu_disassemble_memory *m = (struct mmu_disassemble_memory *)s; 19 | return m->cpu->read_byte(m->cpu->memory, pointer); 20 | } 21 | 22 | uint16_t parse_operand(debugger_state_t *state, const char *start, const char **end, 23 | struct mmu_disassemble_memory *mmudasm) { 24 | if (*start >= '0' && *start <= '9') { 25 | return strtol(start, (char **)end, 0); 26 | } else { 27 | uint16_t val; 28 | int len = get_symbol(&mmudasm->mem, start - 1, &val); 29 | if (len) { 30 | *end += len; 31 | return val; 32 | } 33 | 34 | #define REGISTER(num, len, print) \ 35 | if (strncasecmp(start, print, len) == 0) {\ 36 | *end += len; \ 37 | return state->asic->cpu->registers. num; \ 38 | } 39 | REGISTER(IXH, 3, "IXH"); 40 | REGISTER(IXL, 3, "IXL"); 41 | REGISTER(IYH, 3, "IYH"); 42 | REGISTER(IYL, 3, "IYL"); 43 | REGISTER(_BC, 3, "BC'"); 44 | REGISTER(_DE, 3, "DE'"); 45 | REGISTER(_HL, 3, "HL'"); 46 | REGISTER(_AF, 3, "AF'"); 47 | REGISTER(IX, 2, "IX"); 48 | REGISTER(IY, 2, "IY"); 49 | REGISTER(AF, 2, "AF"); 50 | REGISTER(BC, 2, "BC"); 51 | REGISTER(DE, 2, "DE"); 52 | REGISTER(HL, 2, "HL"); 53 | REGISTER(PC, 2, "PC"); 54 | REGISTER(SP, 2, "SP"); 55 | REGISTER(A, 1, "A"); 56 | REGISTER(B, 1, "B"); 57 | REGISTER(C, 1, "C"); 58 | REGISTER(D, 1, "D"); 59 | REGISTER(E, 1, "E"); 60 | REGISTER(F, 1, "F"); 61 | REGISTER(H, 1, "H"); 62 | REGISTER(L, 1, "L"); 63 | REGISTER(I, 1, "I"); 64 | REGISTER(R, 1, "R"); 65 | 66 | state->print(state, "ERROR: Unknown register/number!\n"); 67 | while(!strchr("+-*/(){} \n", *start)) { 68 | start++; 69 | } 70 | *end = 0; 71 | return 0; 72 | } 73 | } 74 | 75 | int precedence(char op) { 76 | switch (op) { 77 | case '+': 78 | case '-': 79 | return 1; 80 | case '*': 81 | case '/': 82 | case '%': 83 | return 2; 84 | case '(': 85 | return -2; 86 | case ')': 87 | return -3; 88 | case '{': 89 | return -4; 90 | case '}': 91 | return -5; 92 | default: 93 | return -1; 94 | } 95 | } 96 | 97 | void print_stack(uint16_t *a, int b, char *c, int d) { 98 | printf("Stack: "); 99 | while (b != 0) { 100 | b--; 101 | printf("%04X (%u) ", a[b], a[b]); 102 | } 103 | 104 | printf("\nOperator: "); 105 | 106 | while(d != 0) { 107 | printf("%c ", c[--d]); 108 | } 109 | 110 | printf("\n"); 111 | } 112 | 113 | uint16_t run_operator(char operator, uint16_t arg2, uint16_t arg1) { 114 | switch(operator) { 115 | case '+': 116 | return arg1 + arg2; 117 | case '-': 118 | return arg1 - arg2; 119 | case '*': 120 | return arg1 * arg2; 121 | case '/': 122 | return arg1 / arg2; 123 | case '%': 124 | return arg1 % arg2; 125 | default: 126 | return 0; 127 | } 128 | } 129 | 130 | uint16_t parse_expression_z80e(debugger_state_t *state, const char *string) { 131 | uint16_t value_stack[20]; 132 | int value_stack_pos = 0; 133 | 134 | char operator_stack[20]; 135 | int operator_stack_pos = 0; 136 | 137 | z80cpu_t *cpu = state->asic->cpu; 138 | uint16_t start = state->asic->cpu->registers.PC; 139 | struct mmu_disassemble_memory mmudasm = { { parse_expression_dasm_read, start }, cpu, state }; 140 | 141 | while (isspace(*string)) { 142 | string++; 143 | } 144 | 145 | while (*string != 0) { 146 | if (strchr("+-*/%(){}", *string)) { 147 | int op = precedence(*string); 148 | 149 | if (op == -2 || op == -4) { // left parenthesis 150 | operator_stack[operator_stack_pos++] = *string; 151 | } else if (op == -3) { // right parenthesis 152 | while (operator_stack_pos > 0) { 153 | char ch = operator_stack[operator_stack_pos - 1]; 154 | int prec = precedence(ch); 155 | if (prec != -2 && value_stack_pos < 2) { 156 | state->print(state, "ERROR: Missing values!\n"); 157 | } else if (prec != -2 && value_stack_pos > 1) { 158 | uint16_t first = value_stack[value_stack_pos - 1]; 159 | uint16_t second = value_stack[value_stack_pos - 2]; 160 | value_stack_pos -= 2; // popped values 161 | value_stack[value_stack_pos++] = run_operator(ch, first, second); 162 | operator_stack_pos--; 163 | } else { 164 | operator_stack_pos--; 165 | break; 166 | } 167 | } 168 | } else if (op == -5) { // right dereference 169 | while (operator_stack_pos > 0) { 170 | char ch = operator_stack[operator_stack_pos - 1]; 171 | int prec = precedence(ch); 172 | if (prec != -4 && value_stack_pos < 2) { 173 | state->print(state, "ERROR: Missing values!\n"); 174 | } else if (prec != -2 && value_stack_pos > 1) { 175 | uint16_t first = value_stack[value_stack_pos - 1]; 176 | uint16_t second = value_stack[value_stack_pos - 2]; 177 | value_stack_pos -= 2; // popped values 178 | value_stack[value_stack_pos++] = run_operator(ch, first, second); 179 | operator_stack_pos--; 180 | } else { 181 | operator_stack_pos--; 182 | break; 183 | } 184 | } 185 | 186 | if (value_stack_pos < 1) { 187 | state->print(state, "ERROR: Dereferencing failed!\n"); 188 | } else { 189 | uint16_t memory = value_stack[value_stack_pos - 1]; 190 | value_stack[value_stack_pos - 1] = state->asic->cpu->read_byte(state->asic->cpu->memory, memory); 191 | } 192 | } else { 193 | while (operator_stack_pos > 0) { 194 | char ch = operator_stack[operator_stack_pos - 1]; 195 | int prec = precedence(ch); 196 | if (prec > op && value_stack_pos < 2) { 197 | state->print(state, "ERROR: Missing values!\n"); 198 | } else if (prec > op) { 199 | uint16_t first = value_stack[value_stack_pos - 1]; 200 | uint16_t second = value_stack[value_stack_pos - 2]; 201 | value_stack_pos -= 2; // popped values 202 | value_stack[value_stack_pos++] = run_operator(ch, first, second); 203 | operator_stack_pos--; 204 | } else { 205 | break; 206 | } 207 | } 208 | operator_stack[operator_stack_pos++] = *string; 209 | } 210 | string++; 211 | } else { 212 | value_stack[value_stack_pos++] = parse_operand(state, string, &string, &mmudasm); 213 | if (string == 0) { 214 | return 0; 215 | } 216 | } 217 | 218 | while (isspace(*string)) { 219 | string++; 220 | } 221 | } 222 | 223 | // print_stack(value_stack, value_stack_pos, operator_stack, operator_stack_pos); 224 | 225 | while (operator_stack_pos > 0) { 226 | char ch = operator_stack[operator_stack_pos - 1]; 227 | int prec = precedence(ch); 228 | 229 | if(prec == -2) { 230 | state->print(state, "ERROR: Mismatched parentheses!\n"); 231 | return 0; 232 | } else if(prec == -4) { 233 | state->print(state, "ERROR: Mismatched dereference!\n"); 234 | return 0; 235 | } else if(value_stack_pos < 2) { 236 | state->print(state, "ERROR: Missing values!\n"); 237 | return 0; 238 | } else { 239 | uint16_t first = value_stack[value_stack_pos - 1]; 240 | uint16_t second = value_stack[value_stack_pos - 2]; 241 | value_stack_pos -= 2; // popped values 242 | value_stack[value_stack_pos++] = run_operator(ch, first, second); 243 | operator_stack_pos--; 244 | } 245 | } 246 | 247 | // print_stack(value_stack, value_stack_pos, operator_stack, operator_stack_pos); 248 | 249 | if (value_stack_pos > 0) { 250 | return value_stack[value_stack_pos - 1]; 251 | } else { 252 | return 0; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/disassemble.c: -------------------------------------------------------------------------------- 1 | #include "core/cpu.h" 2 | 3 | #include "debugger/commands.h" 4 | #include "debugger/debugger.h" 5 | #include "disassembler/disassemble.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct mmu_disassemble_memory { 12 | struct disassemble_memory mem; 13 | z80cpu_t *cpu; 14 | struct debugger_state *state; 15 | }; 16 | 17 | int disassemble_print(struct disassemble_memory *s, const char *format, ...) { 18 | struct mmu_disassemble_memory *m = (struct mmu_disassemble_memory *)s; 19 | 20 | char buffer[50]; 21 | 22 | va_list list; 23 | va_start(list, format); 24 | 25 | vsnprintf(buffer, 50, format, list); 26 | 27 | return m->state->print(m->state, "%s", buffer); 28 | } 29 | 30 | uint8_t disassemble_read(struct disassemble_memory *s, uint16_t pointer) { 31 | struct mmu_disassemble_memory *m = (struct mmu_disassemble_memory *)s; 32 | return m->cpu->read_byte(m->cpu->memory, pointer); 33 | } 34 | 35 | int command_disassemble(struct debugger_state *state, int argc, char **argv) { 36 | if (argc > 3) { 37 | state->print(state, "%s `start` `count` - Print the disassembled commands\n" 38 | " Prints `count` disassembled commands starting in memory from `start`.\n", argv[0]); 39 | return 0; 40 | } 41 | 42 | z80cpu_t *cpu = state->asic->cpu; 43 | 44 | uint16_t start = state->asic->cpu->registers.PC; 45 | uint16_t count = 10; 46 | 47 | if (argc > 1) { 48 | start = parse_expression_z80e(state, argv[1]); 49 | } 50 | if (argc > 2) { 51 | count = parse_expression_z80e(state, argv[2]); 52 | } 53 | 54 | uint16_t i = 0; 55 | 56 | struct mmu_disassemble_memory str = { { disassemble_read, start }, cpu, state }; 57 | 58 | for (i = 0; i < count; i++) { 59 | state->print(state, "0x%04X: ", str.mem.current); 60 | parse_instruction(&(str.mem), disassemble_print, state->debugger->flags.knightos); 61 | state->print(state, "\n"); 62 | } 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/dump_lcd.c: -------------------------------------------------------------------------------- 1 | #include "ti/hardware/t6a04.h" 2 | #include "debugger/debugger.h" 3 | #include "debugger/commands.h" 4 | 5 | void dump_lcd_unicode_to_utf8(char *b, uint32_t c) { 6 | if (c<0x80) *b++=c; 7 | else if (c<0x800) *b++=192+c/64, *b++=128+c%64; 8 | else if (c-0xd800u<0x800) return; 9 | else if (c<0x10000) *b++=224+c/4096, *b++=128+c/64%64, *b++=128+c%64; 10 | else if (c<0x110000) *b++=240+c/262144, *b++=128+c/4096%64, *b++=128+c/64%64, *b++=128+c%64; 11 | } 12 | 13 | int command_dump_lcd(debugger_state_t *state, int argc, char **argv) { 14 | ti_bw_lcd_t *lcd = state->asic->cpu->devices[0x10].device; 15 | int cY; 16 | int cX; 17 | 18 | #define LCD_BRAILLE 19 | #ifndef LCD_BRAILLE 20 | for (cX = 0; cX < 64; cX++) { 21 | for (cY = 0; cY < 120; cY++) { 22 | state->print(state, "%c", bw_lcd_read_screen(lcd, cY, cX) ? 'O' : ' '); 23 | } 24 | state->print(state, "\n"); 25 | } 26 | #else 27 | for (cX = 0; cX < 64; cX += 4) { 28 | for (cY = 0; cY < 120; cY += 2) { 29 | int a = bw_lcd_read_screen(lcd, cY + 0, cX + 0); 30 | int b = bw_lcd_read_screen(lcd, cY + 0, cX + 1); 31 | int c = bw_lcd_read_screen(lcd, cY + 0, cX + 2); 32 | int d = bw_lcd_read_screen(lcd, cY + 1, cX + 0); 33 | int e = bw_lcd_read_screen(lcd, cY + 1, cX + 1); 34 | int f = bw_lcd_read_screen(lcd, cY + 1, cX + 2); 35 | int g = bw_lcd_read_screen(lcd, cY + 0, cX + 3); 36 | int h = bw_lcd_read_screen(lcd, cY + 1, cX + 3); 37 | uint32_t byte_value = 0x2800; 38 | byte_value += ( 39 | (a << 0) | 40 | (b << 1) | 41 | (c << 2) | 42 | (d << 3) | 43 | (e << 4) | 44 | (f << 5) | 45 | (g << 6) | 46 | (h << 7)); 47 | char buff[5] = {0}; 48 | dump_lcd_unicode_to_utf8(buff, byte_value); 49 | state->print(state, "%s", buff); 50 | } 51 | state->print(state, "\n"); 52 | } 53 | #endif 54 | state->print(state, "C: 0x%02X X: 0x%02X Y: 0x%02X Z: 0x%02X\n", lcd->contrast, lcd->X, lcd->Y, lcd->Z); 55 | state->print(state, " %c%c%c%c O1: 0x%01X 02: 0x%01X\n", lcd->up ? 'V' : '^', lcd->counter ? '-' : '|', 56 | lcd->word_length ? '8' : '6', lcd->display_on ? 'O' : ' ', lcd->op_amp1, lcd->op_amp2); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/hexdump.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "ti/memory.h" 4 | 5 | #include 6 | #include 7 | 8 | int command_hexdump(struct debugger_state *state, int argc, char **argv) { 9 | if (argc > 3) { 10 | state->print(state, "%s [start] [length] - print an amount of bytes from the memory\n" 11 | " Prints the bytes starting from `start` (or PC), `length` (default: 64) bytes in total.\n", argv[0]); 12 | return 0; 13 | } 14 | 15 | z80cpu_t *cpu = state->asic->cpu; 16 | 17 | uint16_t start = state->asic->cpu->registers.PC; 18 | if (argc > 1) { 19 | start = parse_expression_z80e(state, argv[1]); 20 | } 21 | 22 | uint16_t length = 64; 23 | if (argc > 2) { 24 | length = parse_expression_z80e(state, argv[2]); 25 | } 26 | 27 | uint16_t i, total = 0; 28 | 29 | while (length > 0) { 30 | state->print(state, "0x%04X ", start); 31 | 32 | for (i = 0; i < 8 && length - i > 0; i++) { 33 | state->print(state, "%02X ", cpu->read_byte(cpu->memory, start + i)); 34 | } 35 | start += i; 36 | length -= i; 37 | total += i; 38 | 39 | state->print(state, " "); 40 | 41 | for (i = 0; i < 8 && length - i > 0; i++) { 42 | state->print(state, "%02X ", cpu->read_byte(cpu->memory, start + i)); 43 | } 44 | start += i; 45 | length -= i; 46 | total += i; 47 | 48 | state->print(state, "%*s|", (16 - total) * 3 + (i < 8 ? 1 : 0), " "); 49 | for (i = 0; i < total; i++) { 50 | char c = cpu->read_byte(cpu->memory, start - 16 + i); 51 | if (isprint(c) && c != '\t') { 52 | state->print(state, "%c", c); 53 | } else { 54 | state->print(state, "."); 55 | } 56 | } 57 | state->print(state, "|\n"); 58 | total = 0; 59 | } 60 | 61 | return 0; 62 | } 63 | int command_backwards_hexdump(struct debugger_state *state, int argc, char **argv) { 64 | if (argc > 3) { 65 | state->print(state, "%s [start] [length] - print an amount of bytes from the memory\n" 66 | " Prints the bytes starting from `start` (or PC), `length` (default: 64) bytes in total.\n", argv[0]); 67 | return 0; 68 | } 69 | 70 | z80cpu_t *cpu = state->asic->cpu; 71 | 72 | uint16_t start = state->asic->cpu->registers.PC; 73 | if (argc > 1) { 74 | start = parse_expression_z80e(state, argv[1]); 75 | } 76 | 77 | uint16_t length = 64; 78 | if (argc > 2) { 79 | length = parse_expression_z80e(state, argv[2]); 80 | } 81 | 82 | uint16_t i, total = 0; 83 | 84 | while (length > 0) { 85 | state->print(state, "0x%04X ", start); 86 | 87 | for (i = 0; i < 8 && length - i > 0; i++) { 88 | state->print(state, "%02X ", cpu->read_byte(cpu->memory, start - i)); 89 | } 90 | start -= i; 91 | length -= i; 92 | total += i; 93 | 94 | state->print(state, " "); 95 | 96 | for (i = 0; i < 8 && length - i > 0; i++) { 97 | state->print(state, "%02X ", cpu->read_byte(cpu->memory, start + i)); 98 | } 99 | start -= i; 100 | length -= i; 101 | total += i; 102 | 103 | state->print(state, "%*s|", (16 - total) * 3 + (i < 8 ? 1 : 0), " "); 104 | for (i = 0; i < total; i++) { 105 | char c = cpu->read_byte(cpu->memory, start - 16 + i); 106 | if (isprint(c) && c != '\t') { 107 | state->print(state, "%c", c); 108 | } else { 109 | state->print(state, "."); 110 | } 111 | } 112 | state->print(state, "|\n"); 113 | total = 0; 114 | } 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/link.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "ti/hardware/link.h" 4 | #include "debugger/hooks.h" 5 | 6 | #ifndef NOLINK 7 | #include 8 | #include 9 | #include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | FILE *link_output = NULL; 19 | FILE *link_input = NULL; 20 | int hook; 21 | 22 | void send_byte_from_input(struct debugger_state *state) { 23 | if (!link_input) { 24 | return; 25 | } 26 | int c = getc(link_input); 27 | if (c == EOF) { 28 | fclose(link_input); 29 | hook_remove_port_in(state->asic->hook, hook); 30 | return; 31 | } 32 | if (!link_recv_byte(state->asic, (uint8_t)c)) { 33 | state->print(state, "Warning: link not ready\n"); 34 | ungetc(c, link_input); 35 | } 36 | } 37 | 38 | uint8_t on_link_rx_buffer_read(void *state, uint8_t port, uint8_t value) { 39 | struct debugger_state *s = state; 40 | s->print(s, "Link rx buffer read from\n"); 41 | send_byte_from_input(state); 42 | return value; 43 | } 44 | 45 | int handle_send(struct debugger_state *state, int argc, char **argv) { 46 | char *path = strdup(argv[2]); 47 | #ifndef NOLINK 48 | wordexp_t p; 49 | if (wordexp(path, &p, 0) == 0) { 50 | free(path); 51 | path = strdup(p.we_wordv[0]); 52 | } 53 | #endif 54 | 55 | link_input = fopen(path, "r"); 56 | if (link_input) { 57 | state->print(state, "Sending file: %s\n", path); 58 | hook = hook_add_port_in(state->asic->hook, 0x0A, 0x0A, state, on_link_rx_buffer_read); 59 | send_byte_from_input(state); 60 | free(path); 61 | return 0; 62 | } 63 | free(path); 64 | 65 | int strl = 0; 66 | int i; 67 | for (i = 1; i < argc - 1; i++) { 68 | strl += strlen(argv[i + 1]) + 1; 69 | } 70 | 71 | char *data = malloc(strl); 72 | char *dpointer = data; 73 | for (i = 1; i < argc - 1; i++) { 74 | strcpy(dpointer, argv[i + 1]); 75 | dpointer += strlen(argv[i + 1]); 76 | *(dpointer++) = ' '; 77 | } 78 | *(dpointer - 1) = 0; 79 | 80 | uint8_t expr = parse_expression_z80e(state, data); 81 | 82 | free(data); 83 | 84 | if (!link_recv_byte(state->asic, expr)) { 85 | state->print(state, "Calculator is not ready to receive another byte.\n"); 86 | } else { 87 | state->print(state, "Sent %02X to calculator's link assist.\n", expr); 88 | } 89 | return 0; 90 | } 91 | 92 | int handle_recv(struct debugger_state *state, int argc, char **argv) { 93 | return 0; 94 | } 95 | 96 | int handle_socket(struct debugger_state *state, int argc, char **argv) { 97 | #ifdef NOLINK 98 | state->print(state, "Sockets are not supported\n"); 99 | #else 100 | state->asic->link->listenfd.fd = 101 | socket(AF_UNIX, SOCK_STREAM, 0); 102 | fcntl(state->asic->link->listenfd.fd, F_SETFL, O_NONBLOCK); 103 | fcntl(state->asic->link->listenfd.fd, F_SETFD, FD_CLOEXEC); 104 | state->asic->link->listenfd.events = POLLIN; 105 | if (state->asic->link->listenfd.fd == -1) { 106 | state->print(state, "Unable to create socket\n"); 107 | return 1; 108 | } 109 | 110 | struct sockaddr_un *link_sockaddr = malloc(sizeof(struct sockaddr_un)); 111 | if (link_sockaddr == NULL) { 112 | state->print(state, "Can't allocate socket address\n"); 113 | return 1; 114 | } 115 | 116 | link_sockaddr->sun_family = AF_UNIX; 117 | strncpy(link_sockaddr->sun_path, argv[2], sizeof(link_sockaddr->sun_path)); 118 | link_sockaddr->sun_path[sizeof(link_sockaddr->sun_path) - 1] = 0; 119 | 120 | unlink(link_sockaddr->sun_path); 121 | if (bind(state->asic->link->listenfd.fd, 122 | (struct sockaddr *)link_sockaddr, 123 | sizeof(*link_sockaddr)) == -1) { 124 | state->print(state, "Unable to bind socket\n"); 125 | return 1; 126 | } 127 | 128 | if (listen(state->asic->link->listenfd.fd, 3) == -1) { 129 | state->print(state, "Unable to listen on socket\n"); 130 | return 1; 131 | } 132 | 133 | state->print(state, "Bound to socket at %s\n", argv[2]); 134 | #endif 135 | return 0; 136 | } 137 | 138 | int handle_status(struct debugger_state *state) { 139 | link_state_t *lstate = state->asic->cpu->devices[0x00].device; 140 | state->print(state, "Ready: %d\n", !lstate->assist.status.rx_ready); 141 | state->print(state, "Tip: %d\n", lstate->us.tip); 142 | state->print(state, "Ring: %d\n", lstate->us.ring); 143 | return 0; 144 | } 145 | 146 | int command_link(struct debugger_state *state, int argc, char **argv) { 147 | if (argc >= 2) { 148 | if ((strcasecmp(argv[1], "send") == 0) && (argc == 3)) { 149 | return handle_send(state, argc, argv); 150 | } else if ((strcasecmp(argv[1], "recv") == 0) && (argc == 3)) { 151 | return handle_recv(state, argc, argv); 152 | } else if ((strcasecmp(argv[1], "socket") == 0) && (argc == 3)) { 153 | return handle_socket(state, argc, argv); 154 | } else if (strcasecmp(argv[1], "status") == 0) { 155 | return handle_status(state); 156 | } else { 157 | state->print(state, "Invalid operation %s\n", argv[1]); 158 | } 159 | } 160 | 161 | // Nothing handled the command? help text 162 | state->print(state, "%s [send|recv|socket|status] [value|behavior|path]\n" 163 | "send a value will send a value to the link port. If you pass a file, it will be sent instead.\n" 164 | "recv [behavior] defines z80e's behavior when receiving link port data.\n" 165 | "Use 'print' to print each value, or a file name to write values to that file.\n", argv[0]); 166 | return 0; 167 | } 168 | 169 | void init_link(struct debugger_state *state) { 170 | } 171 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/load_register.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include 4 | #include 5 | 6 | int command_load_register(debugger_state_t *state, int argc, char **argv) { 7 | if (argc != 3) { 8 | state->print(state, "%s - Load a register with a specified value. Supports hex (i.e. 0x0000) and base-10 (i.e. 12)\n", argv[0]); 9 | return 0; 10 | } 11 | 12 | #define REGISTER(num, len, print) \ 13 | if (strncasecmp(argv[1], print, len) == 0) {\ 14 | state->asic->cpu->registers. num = parse_expression_z80e(state, argv[2]); \ 15 | }\ 16 | 17 | REGISTER(IXH, 3, "IXH"); 18 | REGISTER(IXL, 3, "IXL"); 19 | REGISTER(IYH, 3, "IYH"); 20 | REGISTER(IYL, 3, "IYL"); 21 | REGISTER(_BC, 3, "BC'"); 22 | REGISTER(_DE, 3, "DE'"); 23 | REGISTER(_HL, 3, "HL'"); 24 | REGISTER(_AF, 3, "AF'"); 25 | REGISTER(IX, 2, "IX"); 26 | REGISTER(IY, 2, "IY"); 27 | REGISTER(AF, 2, "AF"); 28 | REGISTER(BC, 2, "BC"); 29 | REGISTER(DE, 2, "DE"); 30 | REGISTER(HL, 2, "HL"); 31 | REGISTER(PC, 2, "PC"); 32 | REGISTER(SP, 2, "SP"); 33 | REGISTER(A, 1, "A"); 34 | REGISTER(B, 1, "B"); 35 | REGISTER(C, 1, "C"); 36 | REGISTER(D, 1, "D"); 37 | REGISTER(E, 1, "E"); 38 | REGISTER(F, 1, "F"); 39 | REGISTER(H, 1, "H"); 40 | REGISTER(L, 1, "L"); 41 | REGISTER(I, 1, "I"); 42 | REGISTER(R, 1, "R"); 43 | 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/on.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "disassembler/disassemble.h" 4 | #include "log/log.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum { 12 | REGISTER, 13 | MEMORY, 14 | EXECUTION, 15 | PORT 16 | }; 17 | 18 | enum { 19 | READ = (1 << 0), 20 | WRITE = (1 << 1) 21 | }; 22 | int register_from_string(char *string) { 23 | #define REGISTER(num, len, print) \ 24 | if (strncasecmp(string, print, len) == 0) {\ 25 | return num; \ 26 | } 27 | REGISTER(A, 1, "A"); 28 | REGISTER(B, 1, "B"); 29 | REGISTER(C, 1, "C"); 30 | REGISTER(D, 1, "D"); 31 | REGISTER(E, 1, "E"); 32 | REGISTER(F, 1, "F"); 33 | REGISTER(H, 1, "H"); 34 | REGISTER(L, 1, "L"); 35 | REGISTER(AF, 2, "AF"); 36 | REGISTER(_AF, 3, "AF'"); 37 | REGISTER(BC, 2, "BC"); 38 | REGISTER(_BC, 3, "BC'"); 39 | REGISTER(DE, 2, "DE"); 40 | REGISTER(_DE, 3, "DE'"); 41 | REGISTER(HL, 2, "HL"); 42 | REGISTER(_HL, 3, "HL'"); 43 | REGISTER(PC, 2, "PC"); 44 | REGISTER(SP, 2, "SP"); 45 | REGISTER(I, 1, "I"); 46 | REGISTER(R, 1, "R"); 47 | REGISTER(IXH, 3, "IXH"); 48 | REGISTER(IXL, 3, "IXL"); 49 | REGISTER(IX, 2, "IX"); 50 | REGISTER(IYH, 3, "IYH"); 51 | REGISTER(IYL, 3, "IYL"); 52 | REGISTER(IY, 2, "IY"); 53 | 54 | return -1; 55 | } 56 | 57 | typedef struct { 58 | int look_for; 59 | debugger_state_t *deb_sta; 60 | char *exec_string; 61 | } on_state_t; 62 | 63 | uint16_t command_on_register_hook(void *state, registers reg, uint16_t value) { 64 | on_state_t *data = state; 65 | debugger_exec(data->deb_sta, data->exec_string); 66 | return value; 67 | } 68 | 69 | uint8_t command_on_memory_hook(void *state, uint16_t location, uint8_t value) { 70 | on_state_t *data = state; 71 | debugger_exec(data->deb_sta, data->exec_string); 72 | return value; 73 | } 74 | 75 | uint8_t command_on_port_hook(void *state, uint8_t port, uint8_t value) { 76 | on_state_t *data = state; 77 | debugger_exec(data->deb_sta, data->exec_string); 78 | return value; 79 | } 80 | 81 | int command_on(struct debugger_state *state, int argc, char **argv) { 82 | if (argc < 5) { 83 | printf("%s `type` `read|write` `value` `command`\n" 84 | " Run a command on a specific case\n" 85 | " The type can be memory / register\n" 86 | " The value can be a register, or an expression\n", 87 | argv[0]); 88 | return 0; 89 | } 90 | 91 | int thing = 0; 92 | if (*argv[2] == 'r') { 93 | thing = READ; 94 | } 95 | 96 | if (*argv[2] == 'w') { 97 | thing = WRITE; 98 | } 99 | 100 | if (strncasecmp(argv[2], "rw", 2) == 0) { 101 | thing = READ | WRITE; 102 | } 103 | 104 | if (thing == 0) { 105 | state->print(state, "ERROR: First argument must be read, write, or rw\n"); 106 | return 1; 107 | } 108 | 109 | on_state_t *sta = malloc(sizeof(on_state_t)); 110 | sta->deb_sta = state->create_new_state(state, argv[4]); 111 | sta->exec_string = malloc(strlen(argv[4]) + 1); 112 | strcpy(sta->exec_string, argv[4]); 113 | 114 | if (strncasecmp(argv[1], "register", 8) == 0) { 115 | sta->look_for = register_from_string(argv[3]); 116 | if (sta->look_for == -1) { 117 | state->print(state, "ERROR: Invalid register!\n"); 118 | free(sta); 119 | return 1; 120 | } 121 | if (thing & READ) { 122 | hook_add_register_read(state->asic->hook, sta->look_for, sta, command_on_register_hook); 123 | } 124 | if (thing & WRITE) { 125 | hook_add_register_write(state->asic->hook, sta->look_for, sta, command_on_register_hook); 126 | } 127 | } else if (strncasecmp(argv[1], "memory", 6) == 0) { 128 | sta->look_for = parse_expression_z80e(state, argv[3]); 129 | if (thing & READ) { 130 | hook_add_memory_read(state->asic->hook, sta->look_for, sta->look_for, sta, command_on_memory_hook); 131 | } 132 | if (thing & WRITE) { 133 | hook_add_memory_write(state->asic->hook, sta->look_for, sta->look_for, sta, command_on_memory_hook); 134 | } 135 | } else if (strncasecmp(argv[1], "port", 4) == 0) { 136 | sta->look_for = parse_expression_z80e(state, argv[3]); 137 | if (thing & READ) { 138 | hook_add_port_in(state->asic->hook, sta->look_for, sta->look_for, sta, command_on_port_hook); 139 | } 140 | if (thing & WRITE) { 141 | hook_add_port_out(state->asic->hook, sta->look_for, sta->look_for, sta, command_on_port_hook); 142 | } 143 | } else { 144 | free(sta); 145 | state->print(state, "ERROR: Second argument must be memory or register!\n"); 146 | return 1; 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | struct break_data { 153 | uint16_t address; 154 | asic_t *asic; 155 | int hook_id; 156 | int count; 157 | int log; 158 | }; 159 | 160 | void break_callback(struct break_data *data, uint16_t address) { 161 | if(data->address != address) { 162 | return; 163 | } 164 | 165 | if (data->log) { 166 | log_message(data->asic->log, L_DEBUG, "break", "Breakpoint hit at 0x%04X", address); 167 | } 168 | 169 | data->asic->stopped = 1; 170 | 171 | if (data->count != -1 && !(--data->count)) { 172 | hook_remove_before_execution(data->asic->hook, data->hook_id); 173 | } 174 | } 175 | 176 | int command_break(struct debugger_state *state, int argc, char **argv) { 177 | if (argc != 2 && argc != 3) { 178 | state->print(state, "%s `address` [count] - break at address\n", argv[0]); 179 | return 0; 180 | } 181 | 182 | uint16_t address = parse_expression_z80e(state, argv[1]); 183 | 184 | int count = -1; 185 | if (argc == 3) { 186 | count = parse_expression_z80e(state, argv[2]); 187 | } 188 | 189 | struct break_data *data = malloc(sizeof(struct break_data)); 190 | data->address = address; 191 | data->asic = state->asic; 192 | data->count = count; 193 | data->log = 1; 194 | data->hook_id = hook_add_before_execution(state->asic->hook, data, (hook_execution_callback)break_callback); 195 | return 0; 196 | } 197 | 198 | typedef struct { 199 | ti_mmu_t *mmu; 200 | debugger_state_t *state; 201 | } command_step_over_dism_extra_t; 202 | 203 | uint8_t step_over_read_byte(struct disassemble_memory *dmem, uint16_t mem) { 204 | command_step_over_dism_extra_t *extra = dmem->extra_data; 205 | return ti_read_byte(extra->mmu, mem); 206 | } 207 | 208 | int step_over_disasm_write(struct disassemble_memory *mem, const char *thing, ...) { 209 | command_step_over_dism_extra_t *extra = mem->extra_data; 210 | if (extra->state->debugger->flags.echo) { 211 | va_list list; 212 | va_start(list, thing); 213 | return extra->state->vprint(extra->state, thing, list); 214 | } else { 215 | return 0; 216 | } 217 | } 218 | 219 | int command_step_over(struct debugger_state *state, int argc, char **argv) { 220 | if (argc != 1) { 221 | state->print(state, "%s - set a breakpoint for the instruction after the current one\n", argv[0]); 222 | return 0; 223 | } 224 | command_step_over_dism_extra_t extra = { state->asic->mmu, state }; 225 | struct disassemble_memory mem = { step_over_read_byte, state->asic->cpu->registers.PC, &extra }; 226 | 227 | if (state->debugger->flags.echo) { 228 | state->print(state, "0x%04X: ", state->asic->cpu->registers.PC); 229 | } 230 | uint16_t size = parse_instruction(&mem, step_over_disasm_write, state->debugger->flags.knightos); 231 | if (state->debugger->flags.echo) { 232 | state->print(state, "\n"); 233 | } 234 | // Note: 0x18, 0xFE is JR $, i.e. an infinite loop, which we step over as a special case 235 | const uint8_t jumps[] = { 0x18, 0x28, 0x38, 0x30, 0x20 }; 236 | int i; 237 | for (i = 0; i < sizeof(jumps) / sizeof(uint8_t); ++i) { 238 | if (cpu_read_byte(state->asic->cpu, state->asic->cpu->registers.PC) == jumps[i] && 239 | cpu_read_byte(state->asic->cpu, state->asic->cpu->registers.PC + 1) == 0xFE) { 240 | state->asic->cpu->registers.PC += 2; 241 | return 0; 242 | } 243 | } 244 | if (state->debugger->flags.knightos) { 245 | if (cpu_read_byte(state->asic->cpu, state->asic->cpu->registers.PC) == 0xE7) { 246 | size += 2; 247 | } 248 | } 249 | 250 | struct break_data *data = malloc(sizeof(struct break_data)); 251 | data->address = state->asic->cpu->registers.PC + size; 252 | data->asic = state->asic; 253 | data->count = 1; 254 | data->log = 0; 255 | data->hook_id = hook_add_before_execution(state->asic->hook, data, (hook_execution_callback) break_callback); 256 | 257 | char *_argv[] = { "run" }; 258 | int orig_echo = state->debugger->flags.echo; 259 | state->debugger->flags.echo = 0; 260 | 261 | int val = command_run(state, 1, _argv); 262 | state->debugger->flags.echo = orig_echo; 263 | return val; 264 | } 265 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/ports.c: -------------------------------------------------------------------------------- 1 | #include "debugger/debugger.h" 2 | #include "debugger/commands.h" 3 | 4 | int command_in(struct debugger_state *state, int argc, char **argv) { 5 | if(argc != 2) { 6 | state->print(state, "%s `port` - read from port `port`.\n", argv[0]); 7 | return 0; 8 | } 9 | 10 | uint8_t port = parse_expression_z80e(state, argv[1]) & 0xFF; 11 | uint8_t val = 0; 12 | if (state->asic->cpu->devices[port].read_in != NULL) { 13 | val = state->asic->cpu->devices[port].read_in(state->asic->cpu->devices[port].device); 14 | } 15 | state->print(state, "port 0x%02X -> 0x%02X\n", port, val); 16 | return 0; 17 | } 18 | 19 | int command_out(struct debugger_state *state, int argc, char **argv) { 20 | if(argc != 3) { 21 | state->print(state, "%s `port` `value` - write `value` into port `port`.\n", argv[0]); 22 | return 0; 23 | } 24 | 25 | uint8_t port = parse_expression_z80e(state, argv[1]) & 0xFF; 26 | uint8_t val = parse_expression_z80e(state, argv[2]) & 0xFF; 27 | if (state->asic->cpu->devices[port].write_out != NULL) { 28 | state->asic->cpu->devices[port].write_out(state->asic->cpu->devices[port].device, val); 29 | } 30 | state->print(state, "port 0x%02X <- 0x%02X\n", port, val); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/press_key.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debugger/commands.h" 3 | #include "debugger/debugger.h" 4 | #include "ti/hardware/keyboard.h" 5 | #include "debugger/keys.h" 6 | 7 | int command_press_key(debugger_state_t *state, int argc, char **argv) { 8 | if (argc != 2) { 9 | state->print(state, "%s - Depress the specified key code, in hex or by name.\n", argv[0]); 10 | return 0; 11 | } 12 | uint8_t key; 13 | 14 | int i, found = 0; 15 | for (i = 0; i < sizeof(key_strings) / sizeof(key_string_t); ++i) { 16 | if (strcasecmp(key_strings[i].key, argv[1]) == 0) { 17 | key = key_strings[i].value; 18 | found = 1; 19 | break; 20 | } 21 | } 22 | if (!found) { 23 | key = parse_expression_z80e(state, argv[1]); 24 | } 25 | 26 | depress_key(state->asic->cpu->devices[0x01].device, key); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/print_expression.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | 4 | #include 5 | #include 6 | 7 | int command_print_expression(struct debugger_state *state, int argc, char **argv) { 8 | if (argc == 1) { 9 | state->print(state, "%s `expression` - Print an expression\n Don't forget to quote your expression!\n", argv[0]); 10 | return 0; 11 | } 12 | 13 | int strl = 0; 14 | int i; 15 | for (i = 0; i < argc - 1; i++) { 16 | strl += strlen(argv[i + 1]) + 1; 17 | } 18 | 19 | char *data = malloc(strl); 20 | char *dpointer = data; 21 | for (i = 0; i < argc - 1; i++) { 22 | strcpy(dpointer, argv[i + 1]); 23 | dpointer += strlen(argv[i + 1]); 24 | *(dpointer++) = ' '; 25 | } 26 | *(dpointer - 1) = 0; 27 | 28 | uint16_t expr = parse_expression_z80e(state, data); 29 | 30 | state->print(state, "%s = 0x%04X (%u)\n", data, expr, expr); 31 | 32 | free(data); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/print_mappings.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | 4 | #include 5 | 6 | int command_print_mappings(struct debugger_state *state, int argc, char **argv) { 7 | if (argc > 2 || (argc == 2 && strcmp(argv[1], "-v") != 0)) { 8 | state->print(state, "%s [-v] - Print the MMU page mappings.\n The -v flag adds verbosity to the output.\n", argv[0]); 9 | state->print(state, " The terse output is formatted like this: \"Bank 0: F:00\".\n" 10 | " 'F' says the mapped page is a flash page, 'R' says the mapped page is a ram page.\n"); 11 | return 0; 12 | } 13 | 14 | int verbose = argc > 1 && strcmp(argv[1], "-v") == 0; 15 | int i = 0; 16 | 17 | for (i = 0; i < 4; i++) { 18 | ti_mmu_bank_state_t mapping = state->asic->mmu->banks[i]; 19 | if (verbose) { 20 | state->print(state, "Page %d (0x%04X - 0x%04X): mapped to %s page %d (0x%04X - 0x%04X).\n", 21 | i, i * 0x4000, (i + 1) * 0x4000 - 1, mapping.flash ? "ROM" : "RAM", mapping.page, 22 | mapping.page * 0x4000, (mapping.page + 1) * 0x4000 - 1); 23 | } else { 24 | state->print(state, "Bank %d: %c:%02X\n", i, mapping.flash ? 'F' : 'R', mapping.page); 25 | } 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/print_registers.c: -------------------------------------------------------------------------------- 1 | #include "debugger/debugger.h" 2 | #include "core/cpu.h" 3 | #include "core/registers.h" 4 | 5 | int command_print_registers(struct debugger_state *state, int argc, char **argv) { 6 | if (argc != 1) { 7 | state->print(state, "print_registers - Print the contents of the emulator's registers\n" 8 | "This command will print the contents of the registers of the emulator\n" 9 | " at the time of running.\n"); 10 | return 0; 11 | } 12 | 13 | z80cpu_t *cpu = state->asic->cpu; 14 | 15 | z80registers_t r = cpu->registers; 16 | state->print(state, " AF: 0x%04X BC: 0x%04X DE: 0x%04X HL: 0x%04X\n", r.AF, r.BC, r.DE, r.HL); 17 | state->print(state, " 'AF: 0x%04X 'BC: 0x%04X 'DE: 0x%04X 'HL: 0x%04X\n", r._AF, r._BC, r._DE, r._HL); 18 | state->print(state, " PC: 0x%04X SP: 0x%04X IX: 0x%04X IY: 0x%04X\n", r.PC, r.SP, r.IX, r.IY); 19 | state->print(state, " IFF1: %d IFF2: %d IM %d\n", cpu->IFF1, cpu->IFF2, cpu->int_mode); 20 | state->print(state, "Flags: "); 21 | if (r.flags.S) state->print(state, "S "); 22 | if (r.flags.Z) state->print(state, "Z "); 23 | if (r.flags.H) state->print(state, "H "); 24 | if (r.flags.PV) state->print(state, "P/V "); 25 | if (r.flags.N) state->print(state, "N "); 26 | if (r.flags.C) state->print(state, "C "); 27 | if (r.F == 0) state->print(state, "None set"); 28 | state->print(state, "\n"); 29 | if (cpu->halted) state->print(state, "CPU halted\n"); 30 | 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/release_key.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debugger/commands.h" 3 | #include "debugger/debugger.h" 4 | #include "ti/hardware/keyboard.h" 5 | #include "debugger/keys.h" 6 | 7 | int command_release_key(debugger_state_t *state, int argc, char **argv) { 8 | if (argc != 2) { 9 | state->print(state, "%s - Release the specified key code, in hex or by name.\n", argv[0]); 10 | return 0; 11 | } 12 | uint8_t key; 13 | 14 | int i, found = 0; 15 | for (i = 0; i < sizeof(key_strings) / sizeof(key_string_t); ++i) { 16 | if (strcasecmp(key_strings[i].key, argv[1]) == 0) { 17 | key = key_strings[i].value; 18 | found = 1; 19 | break; 20 | } 21 | } 22 | if (!found) { 23 | key = parse_expression_z80e(state, argv[1]); 24 | } 25 | 26 | release_key(state->asic->cpu->devices[0x01].device, key); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "ti/asic.h" 4 | #include "debugger/commands.h" 5 | #include "debugger/debugger.h" 6 | #include "disassembler/disassemble.h" 7 | #include "runloop/runloop.h" 8 | #include "ti/hardware/t6a04.h" 9 | 10 | struct run_disassemble_state { 11 | struct disassemble_memory memory; 12 | debugger_state_t *state; 13 | }; 14 | 15 | uint8_t run_command_read_byte(struct disassemble_memory *state, uint16_t pointer) { 16 | struct run_disassemble_state *dstate = (struct run_disassemble_state *)state; 17 | 18 | return dstate->state->asic->cpu->read_byte(dstate->state->asic->cpu->memory, pointer); 19 | } 20 | 21 | int run_command_write(struct disassemble_memory *state, const char *format, ...) { 22 | struct run_disassemble_state *dstate = (struct run_disassemble_state *)state; 23 | 24 | va_list list; 25 | va_start(list, format); 26 | 27 | return dstate->state->vprint(dstate->state, format, list); 28 | } 29 | 30 | int command_run(debugger_state_t *state, int argc, char **argv) { 31 | state->asic->stopped = 0; 32 | uint16_t instructions = -1; 33 | 34 | struct run_disassemble_state dstate; 35 | dstate.memory.read_byte = run_command_read_byte; 36 | dstate.memory.current = state->asic->cpu->registers.PC; 37 | dstate.state = state; 38 | 39 | int oldHalted = 0; 40 | int isFirstInstruction = 1; 41 | 42 | if ((argc == 2 && strcmp(argv[1], "--help") == 0) || argc > 2) { 43 | state->print(state, "run [instructions] - run a specified number of instructions\n" 44 | " If no number is specified, the emulator will run until interrupted (^C).\n"); 45 | return 0; 46 | } else if(argc == 2) { 47 | instructions = parse_expression_z80e(state, argv[1]); 48 | state->debugger->state = DEBUGGER_LONG_OPERATION; 49 | for (; instructions > 0; instructions--) { 50 | hook_on_before_execution(state->asic->hook, state->asic->cpu->registers.PC); 51 | if (!isFirstInstruction && state->asic->stopped) { 52 | state->asic->stopped = 0; 53 | break; 54 | } 55 | 56 | if (isFirstInstruction) { 57 | state->asic->stopped = 0; 58 | isFirstInstruction = 0; 59 | } 60 | 61 | if (state->debugger->flags.echo) { 62 | if (!state->asic->cpu->halted) { 63 | state->print(state, "0x%04X: ", state->asic->cpu->registers.PC); 64 | dstate.memory.current = state->asic->cpu->registers.PC; 65 | parse_instruction(&(dstate.memory), run_command_write, state->debugger->flags.knightos); 66 | state->print(state, "\n"); 67 | } 68 | } else { 69 | if (state->asic->cpu->halted && !oldHalted) { 70 | state->print(state, "CPU is halted\n"); 71 | } 72 | } 73 | 74 | if (state->debugger->flags.echo_reg) { 75 | print_state(&state->asic->cpu->registers); 76 | } 77 | 78 | oldHalted = state->asic->cpu->halted; 79 | 80 | int iff1 = state->asic->cpu->IFF1; 81 | int iff2 = state->asic->cpu->IFF2; 82 | if (state->debugger->flags.nointonstep) { 83 | state->asic->cpu->IFF1 = 0; 84 | state->asic->cpu->IFF2 = 0; 85 | } 86 | runloop_tick_cycles(state->asic->runloop, 1); 87 | if (state->debugger->flags.nointonstep) { 88 | state->asic->cpu->IFF1 = iff1; 89 | state->asic->cpu->IFF2 = iff2; 90 | } 91 | hook_on_after_execution(state->asic->hook, state->asic->cpu->registers.PC); 92 | if (state->asic->stopped) { 93 | state->asic->stopped = 0; 94 | break; 95 | } 96 | } 97 | state->debugger->state = DEBUGGER_ENABLED; 98 | return 0; 99 | } 100 | 101 | state->debugger->state = DEBUGGER_LONG_OPERATION_INTERRUPTABLE; 102 | while (1) { 103 | hook_on_before_execution(state->asic->hook, state->asic->cpu->registers.PC); 104 | if (!isFirstInstruction && state->asic->stopped) { 105 | state->asic->stopped = 0; 106 | break; 107 | } 108 | 109 | if (isFirstInstruction) { 110 | state->asic->stopped = 0; 111 | isFirstInstruction = 0; 112 | } 113 | 114 | if (state->debugger->flags.echo) { 115 | if (!state->asic->cpu->halted) { 116 | state->print(state, "0x%04X: ", state->asic->cpu->registers.PC); 117 | dstate.memory.current = state->asic->cpu->registers.PC; 118 | parse_instruction(&(dstate.memory), run_command_write, state->debugger->flags.knightos); 119 | state->print(state, "\n"); 120 | } 121 | } else { 122 | if (state->asic->cpu->halted && !oldHalted) { 123 | if (state->debugger->flags.auto_on) { 124 | if (!((ti_bw_lcd_t *)state->asic->cpu->devices[0x10].device)->display_on) { 125 | state->asic->cpu->halted = 0; 126 | state->print(state, "Turned on calculator via auto_on\n"); 127 | } 128 | } else { 129 | state->print(state, "CPU is halted\n"); 130 | } 131 | } 132 | } 133 | 134 | if (state->debugger->flags.echo_reg) { 135 | print_state(&state->asic->cpu->registers); 136 | } 137 | 138 | oldHalted = state->asic->cpu->halted; 139 | 140 | runloop_tick_cycles(state->asic->runloop, 1); 141 | 142 | hook_on_before_execution(state->asic->hook, state->asic->cpu->registers.PC); 143 | if (state->asic->stopped) { 144 | state->debugger->state = DEBUGGER_ENABLED; 145 | return 0; 146 | } 147 | } 148 | state->debugger->state = DEBUGGER_ENABLED; 149 | return 0; 150 | } 151 | 152 | int command_step(debugger_state_t *state, int argc, char **argv) { 153 | char *_argv[] = { "run", "1" }; 154 | return command_run(state, 2, _argv); 155 | } 156 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/stack.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "ti/memory.h" 4 | 5 | int command_stack(struct debugger_state *state, int argc, char **argv) { 6 | if (argc > 1) { 7 | state->print(state, "%s [count] - print first `count` (or 10) items on the stack\n", argv[0]); 8 | return 0; 9 | } 10 | 11 | z80cpu_t *cpu = state->asic->cpu; 12 | uint16_t sp = cpu->registers.SP; 13 | 14 | uint16_t i; 15 | for (i = sp; i != (uint16_t)(sp + 20); i += 2) { 16 | state->print(state, "0x%04X: 0x%04X\n", i, cpu_read_word(cpu, i)); 17 | } 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/stop.c: -------------------------------------------------------------------------------- 1 | #include "ti/asic.h" 2 | #include "debugger/debugger.h" 3 | 4 | int command_stop(struct debugger_state *state, int argc, char **argv) { 5 | state->asic->stopped = 1; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/tap_key.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debugger/commands.h" 3 | #include "debugger/debugger.h" 4 | #include "ti/hardware/keyboard.h" 5 | #include "debugger/keys.h" 6 | 7 | int command_tap_key(debugger_state_t *state, int argc, char **argv) { 8 | if (argc != 2) { 9 | state->print(state, "%s - Depress the specified key code, in hex or by name.\n", argv[0]); 10 | return 0; 11 | } 12 | uint8_t key; 13 | 14 | int i, found = 0; 15 | for (i = 0; i < sizeof(key_strings) / sizeof(key_string_t); ++i) { 16 | if (strcasecmp(key_strings[i].key, argv[1]) == 0) { 17 | key = key_strings[i].value; 18 | found = 1; 19 | break; 20 | } 21 | } 22 | if (!found) { 23 | key = parse_expression_z80e(state, argv[1]); 24 | } 25 | 26 | depress_key(state->asic->cpu->devices[0x01].device, key); 27 | char *_argv[] = { "run", "1000" }; 28 | command_run(state, 2, _argv); 29 | release_key(state->asic->cpu->devices[0x01].device, key); 30 | char *__argv[] = { "run" }; 31 | return command_run(state, 1, __argv); 32 | } 33 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/timers.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "ti/hardware/timers.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int command_timer(struct debugger_state *state, int argc, char **argv) { 11 | if (argc < 3) { 12 | state->print(state, "%s [int|set [port] [value]]\n", argv[0]); 13 | return 0; 14 | } 15 | 16 | if (strcasecmp(argv[1], "int") == 0) { 17 | state->asic->interrupts->interrupted.first_crystal = 1; 18 | state->asic->cpu->interrupt = !state->asic->cpu->interrupt; 19 | } else { 20 | uint8_t port = parse_expression_z80e(state, argv[2]); 21 | uint8_t val = parse_expression_z80e(state, argv[3]); 22 | 23 | uint8_t *timer = state->asic->cpu->devices[port].device; 24 | timer[(port - 0x30) % 3] = val; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/turn_on.c: -------------------------------------------------------------------------------- 1 | #include "debugger/commands.h" 2 | #include "debugger/debugger.h" 3 | #include "ti/hardware/interrupts.h" 4 | 5 | int command_turn_on(debugger_state_t *state, int argc, char **argv) { 6 | if (argc != 1) { 7 | state->print(state, "%s - Interrupt the CPU and raise the 'on button' interrupt\n", argv[0]); 8 | return 0; 9 | } 10 | 11 | depress_on_key(state->asic->interrupts); 12 | char *_argv[] = { "run", "50000" }; 13 | command_run(state, 2, _argv); 14 | release_on_key(state->asic->interrupts); 15 | char *__argv[] = { "run" }; 16 | return command_run(state, 1, __argv); 17 | } 18 | -------------------------------------------------------------------------------- /libz80e/src/debugger/commands/unhalt.c: -------------------------------------------------------------------------------- 1 | #include "debugger/debugger.h" 2 | #include "core/cpu.h" 3 | #include "core/registers.h" 4 | 5 | int command_unhalt(struct debugger_state *state, int argc, char **argv) { 6 | if (argc != 1) { 7 | state->print(state, "unhalt - Unhalts the running CPU.\n"); 8 | return 0; 9 | } 10 | 11 | z80cpu_t *cpu = state->asic->cpu; 12 | cpu->halted = 0; 13 | return 0; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /libz80e/src/log/log.c: -------------------------------------------------------------------------------- 1 | #include "log/log.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const char *loglevel_to_string(loglevel_t level) { 9 | switch (level) { 10 | case L_DEBUG: 11 | return "DEBUG"; 12 | case L_INFO: 13 | return "INFO"; 14 | case L_WARN: 15 | return "WARN"; 16 | case L_ERROR: 17 | return "ERROR"; 18 | default: 19 | return "UNKNOWN"; 20 | } 21 | } 22 | 23 | log_t *init_log_z80e(log_func func, void *data, int log_level) { 24 | log_t *log = calloc(sizeof(log_t), 1); 25 | log->log = func; 26 | log->data = data; 27 | log->logging_level = log_level; 28 | return log; 29 | } 30 | 31 | void log_message(log_t *log, loglevel_t level, const char *part, const char *format, ...) { 32 | if (log == 0) { 33 | return; 34 | } 35 | 36 | if (level > log->logging_level) { 37 | return; 38 | } 39 | 40 | va_list format_list; 41 | va_start(format_list, format); 42 | log->log(log->data, level, part, format, format_list); 43 | } 44 | 45 | void free_log(log_t *log) { 46 | free(log); 47 | } 48 | -------------------------------------------------------------------------------- /libz80e/src/runloop/runloop.c: -------------------------------------------------------------------------------- 1 | #include "runloop/runloop.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifndef NOLINK 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #endif 16 | #include 17 | /* Why the heck does "get the current time" have to be so god-dammed platform specific */ 18 | #ifdef EMSCRIPTEN 19 | #include 20 | #endif 21 | #ifdef APPLE 22 | #include 23 | #define ORWL_NANO (+1.0E-9) 24 | #define ORWL_GIGA UINT64_C(1000000000) 25 | static double orwl_timebase = 0.0; 26 | static uint64_t orwl_timestart = 0; 27 | #endif 28 | #include "ti/hardware/link.h" 29 | 30 | long long get_time_nsec() { 31 | #ifdef EMSCRIPTEN 32 | return emscripten_get_now() * 1000000; 33 | #else 34 | #ifdef APPLE 35 | if (!orwl_timestart) { 36 | mach_timebase_info_data_t tb = { 0 }; 37 | mach_timebase_info(&tb); 38 | orwl_timebase = tb.numer; 39 | orwl_timebase /= tb.denom; 40 | orwl_timestart = mach_absolute_time(); 41 | } 42 | struct timespec t; 43 | double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase; 44 | t.tv_sec = diff * ORWL_NANO; 45 | t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA); 46 | return t.tv_nsec; 47 | #else 48 | struct timespec sp; 49 | clock_gettime(CLOCK_MONOTONIC, &sp); 50 | 51 | return sp.tv_sec * 1000000000 + sp.tv_nsec; 52 | #endif 53 | #endif 54 | } 55 | 56 | typedef struct { 57 | int index; 58 | int after_cycle; 59 | } timer_tick_t; 60 | 61 | struct runloop_state { 62 | asic_t *asic; 63 | long long last_end; 64 | int spare_cycles; 65 | timer_tick_t *ticks; 66 | int max_tick_count; 67 | }; 68 | 69 | runloop_state_t *runloop_init(asic_t *asic) { 70 | runloop_state_t *state = calloc(sizeof(runloop_state_t), 1); 71 | 72 | state->asic = asic; 73 | state->last_end = get_time_nsec(); 74 | int i; 75 | for (i = 0; i < asic->timers->max_timers; i++) { 76 | z80_hardware_timer_t *timer = &asic->timers->timers[i]; 77 | if (timer->flags & TIMER_IN_USE) { 78 | timer->cycles_until_tick = asic->clock_rate / timer->frequency; 79 | } 80 | } 81 | 82 | state->ticks = calloc(sizeof(timer_tick_t), 40); 83 | state->max_tick_count = 40; 84 | 85 | return state; 86 | } 87 | 88 | int runloop_compare(const void *first, const void *second) { 89 | const timer_tick_t *a = first; 90 | const timer_tick_t *b = second; 91 | 92 | return a->after_cycle - b->after_cycle; 93 | } 94 | 95 | void link_socket_update(asic_t *asic, int c) { 96 | if (c != EOF) { 97 | char _c = ' '; 98 | if (isprint((char)c)) { 99 | _c = (char)c; 100 | } 101 | // TODO: Log the TX better 102 | printf("Asked to send %02X (%c)\n", c, _c); 103 | } 104 | 105 | #ifndef NOLINK 106 | if (!asic->link->listenfd.fd) { 107 | return; 108 | } 109 | 110 | struct sockaddr_in clientaddr; 111 | int size = sizeof(clientaddr); 112 | poll(&asic->link->listenfd, 1, 0); 113 | int fd = -1; 114 | if (asic->link->accept++ == 4096) { // stupid hack 115 | fd = accept(asic->link->listenfd.fd, (struct sockaddr *)&clientaddr, &size); 116 | asic->link->accept = 0; 117 | } 118 | 119 | int i; 120 | for (i = 0; i < sizeof(asic->link->clients) / sizeof(int); ++i) { 121 | struct pollfd client = asic->link->clients[i]; 122 | if (asic->link->clients[i].fd == 0) { 123 | if (fd != -1) { 124 | asic->link->clients[i].fd = fd; 125 | asic->link->clients[i].events = POLLIN | POLLHUP; 126 | printf("Client accepted with fd %d\n", fd); 127 | fd = -1; 128 | } else { 129 | continue; 130 | } 131 | } 132 | if (asic->link->clients[i].fd != 0) { 133 | uint8_t val; 134 | poll(&asic->link->clients[i], 1, 0); 135 | if (asic->link->clients[i].revents & POLLHUP) { 136 | close(asic->link->clients[i].fd); 137 | asic->link->clients[i].fd = 0; 138 | continue; 139 | } 140 | if (asic->link->clients[i].revents & POLLIN) { 141 | if (link_recv_ready(asic)) { 142 | if (read(asic->link->clients[i].fd, &val, 1)) { 143 | link_recv_byte(asic, val); 144 | } 145 | } 146 | } 147 | if (c != EOF) { 148 | val = c; 149 | write(asic->link->clients[i].fd, &c, 1); 150 | } 151 | } 152 | } 153 | #endif 154 | } 155 | 156 | void runloop_tick_cycles(runloop_state_t *state, int cycles) { 157 | int total_cycles = 0; 158 | int cycles_until_next_tick = cycles; 159 | int current_tick = 0; 160 | int i; 161 | for (i = 0; i < state->asic->timers->max_timers; i++) { 162 | z80_hardware_timer_t *timer = &state->asic->timers->timers[i]; 163 | 164 | if (!(timer->flags & TIMER_IN_USE)) { 165 | continue; 166 | } 167 | 168 | int tot_cycles = cycles; 169 | if (timer->cycles_until_tick < tot_cycles) { 170 | retry: 171 | state->ticks[current_tick].index = i; 172 | state->ticks[current_tick].after_cycle = timer->cycles_until_tick + (cycles - tot_cycles); 173 | tot_cycles -= timer->cycles_until_tick; 174 | timer->cycles_until_tick = state->asic->clock_rate / timer->frequency; 175 | current_tick++; 176 | 177 | if (current_tick == state->max_tick_count) { 178 | state->max_tick_count += 10; 179 | state->ticks = realloc(state->ticks, sizeof(timer_tick_t) * state->max_tick_count); 180 | } 181 | 182 | if (timer->cycles_until_tick <= tot_cycles) { 183 | goto retry; 184 | } 185 | } else { 186 | timer->cycles_until_tick -= tot_cycles; 187 | } 188 | } 189 | 190 | qsort(state->ticks, current_tick, sizeof(timer_tick_t), runloop_compare); 191 | 192 | if (current_tick > 0) { 193 | cycles_until_next_tick = state->ticks[0].after_cycle; 194 | } 195 | 196 | int tick_i = 0; 197 | while (cycles > 0) { 198 | int ran = cycles_until_next_tick - cpu_execute(state->asic->cpu, cycles_until_next_tick); 199 | int c = link_read_tx_buffer(state->asic); 200 | link_socket_update(state->asic, c); 201 | 202 | total_cycles += ran; 203 | cycles -= ran; 204 | 205 | if (total_cycles >= state->ticks[tick_i].after_cycle) { 206 | tick_i++; 207 | if (tick_i <= current_tick) { 208 | int index = state->ticks[tick_i - 1].index; 209 | z80_hardware_timer_t *timer = &state->asic->timers->timers[index]; 210 | timer->on_tick(state->asic, timer->data); 211 | cycles_until_next_tick = state->ticks[tick_i].after_cycle - total_cycles; 212 | } else { 213 | cycles_until_next_tick = cycles; 214 | } 215 | } 216 | } 217 | state->spare_cycles = cycles; 218 | } 219 | 220 | void runloop_tick(runloop_state_t *state) { 221 | long long now = get_time_nsec(); 222 | long long ticks_between = now - state->last_end; 223 | 224 | float seconds = (float)ticks_between / (float)1000000000; 225 | int cycles = seconds * (float)state->asic->clock_rate; 226 | 227 | if (cycles == 0) 228 | return; 229 | 230 | runloop_tick_cycles(state, cycles); 231 | state->last_end = now; 232 | } 233 | -------------------------------------------------------------------------------- /libz80e/src/ti/asic.c: -------------------------------------------------------------------------------- 1 | #include "ti/asic.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "log/log.h" 7 | #include "core/cpu.h" 8 | #include "ti/memory.h" 9 | #include "ti/hardware/t6a04.h" 10 | #include "ti/hardware/speed.h" 11 | #include "ti/hardware/memorymapping.h" 12 | #include "ti/hardware/keyboard.h" 13 | #include "ti/hardware/status.h" 14 | #include "ti/hardware/flash.h" 15 | #include "ti/hardware/link.h" 16 | #include "ti/hardware/timers.h" 17 | 18 | typedef struct { 19 | asic_t *asic; 20 | uint8_t port; 21 | } unimplemented_device_t; 22 | 23 | uint8_t read_unimplemented_port(void *device) { 24 | unimplemented_device_t *d = device; 25 | log_message(d->asic->log, L_INFO, "asic", 26 | "Warning: attempted to read from unimplemented port 0x%02x from 0x%04X.", d->port, d->asic->cpu->registers.PC); 27 | return 0x00; 28 | } 29 | 30 | void write_unimplemented_port(void *device, uint8_t value) { 31 | unimplemented_device_t *d = device; 32 | log_message(d->asic->log, L_INFO, "asic", 33 | "Warning: attempted to write 0x%02x to unimplemented port 0x%02x from 0x%04X.", value, d->port, d->asic->cpu->registers.PC); 34 | } 35 | 36 | void plug_devices(asic_t *asic) { 37 | /* Unimplemented devices */ 38 | int i; 39 | for (i = 0; i < 0x100; i++) { 40 | unimplemented_device_t *d = malloc(sizeof(unimplemented_device_t)); 41 | d->asic = asic; 42 | d->port = i; 43 | z80iodevice_t device = { d, read_unimplemented_port, write_unimplemented_port }; 44 | asic->cpu->devices[i] = device; 45 | } 46 | 47 | asic->cpu->devices[0x01] = init_keyboard(); 48 | asic->cpu->devices[0x02] = init_status(asic); 49 | asic->cpu->devices[0x03] = init_interrupts(asic, &asic->interrupts); 50 | setup_lcd_display(asic, asic->hook); 51 | 52 | if (asic->device != TI73 && asic->device != TI83p) { 53 | asic->cpu->devices[0x20] = init_speed(asic); 54 | init_crystal_timers(asic); 55 | } 56 | 57 | init_link_ports(asic); 58 | init_mapping_ports(asic); 59 | init_flash_ports(asic); 60 | } 61 | 62 | void asic_null_write(void *ignored, uint8_t value) {} 63 | 64 | void asic_mirror_ports(asic_t *asic) { 65 | int i; 66 | switch (asic->device) { 67 | case TI83p: 68 | for (i = 0x08; i < 0x10; i++) { 69 | asic->cpu->devices[i] = asic->cpu->devices[i & 0x07]; 70 | asic->cpu->devices[i].write_out = asic_null_write; 71 | } 72 | asic->cpu->devices[0x12] = asic->cpu->devices[0x10]; 73 | asic->cpu->devices[0x13] = asic->cpu->devices[0x11]; 74 | asic->cpu->devices[0x15] = asic->cpu->devices[0x05]; 75 | asic->cpu->devices[0x15].write_out = asic_null_write; 76 | for (i = 0x17; i < 0x100; i++) { 77 | asic->cpu->devices[i] = asic->cpu->devices[i & 0x07]; 78 | asic->cpu->devices[i].write_out = asic_null_write; 79 | } 80 | break; 81 | default: 82 | for (i = 0x60; i < 0x80; i++) { 83 | asic->cpu->devices[i] = asic->cpu->devices[i - 0x20]; 84 | } 85 | break; 86 | } 87 | } 88 | 89 | void free_devices(asic_t *asic) { 90 | /* Link port unimplemented */ 91 | free_keyboard(asic->cpu->devices[0x01].device); 92 | free_status(asic->cpu->devices[0x02]); 93 | free_mapping_ports(asic); 94 | } 95 | 96 | asic_t *asic_init(ti_device_type type, log_t *log) { 97 | asic_t* device = calloc(1, sizeof(asic_t)); 98 | device->log = log; 99 | device->cpu = cpu_init(log); 100 | device->mmu = ti_mmu_init(type, log); 101 | device->cpu->memory = (void*)device->mmu; 102 | device->cpu->read_byte = ti_read_byte; 103 | device->cpu->write_byte = ti_write_byte; 104 | device->battery = BATTERIES_GOOD; 105 | device->device = type; 106 | device->clock_rate = 6000000; 107 | 108 | device->timers = calloc(1, sizeof(z80_hardware_timers_t)); 109 | device->timers->max_timers = 20; 110 | device->timers->timers = calloc(20, sizeof(z80_hardware_timer_t)); 111 | 112 | device->stopped = 0; 113 | device->debugger = 0; 114 | device->runloop = runloop_init(device); 115 | device->hook = create_hook_set(device); 116 | 117 | device->link = calloc(1, sizeof(z80_link_socket_t)); 118 | 119 | plug_devices(device); 120 | asic_mirror_ports(device); 121 | return device; 122 | } 123 | 124 | void asic_free(asic_t* device) { 125 | ti_mmu_free(device->mmu); 126 | free_devices(device); 127 | cpu_free(device->cpu); 128 | free(device); 129 | } 130 | 131 | int asic_add_timer(asic_t *asic, int flags, double frequency, timer_tick tick, void *data) { 132 | z80_hardware_timer_t *timer = 0; 133 | int i; 134 | for (i = 0; i < asic->timers->max_timers; i++) { 135 | if (!(asic->timers->timers[i].flags & TIMER_IN_USE)) { 136 | timer = &asic->timers->timers[i]; 137 | break; 138 | } 139 | 140 | if (i == asic->timers->max_timers - 1) { 141 | asic->timers->max_timers += 10; 142 | asic->timers->timers = realloc(asic->timers->timers, sizeof(z80_hardware_timer_t) * asic->timers->max_timers); 143 | z80_hardware_timer_t *ne = &asic->timers->timers[asic->timers->max_timers - 10]; 144 | memset(ne, 0, sizeof(z80_hardware_timer_t) * 10); 145 | } 146 | } 147 | 148 | timer->cycles_until_tick = asic->clock_rate / frequency; 149 | timer->flags = flags | TIMER_IN_USE; 150 | timer->frequency = frequency; 151 | timer->on_tick = tick; 152 | timer->data = data; 153 | return i; 154 | } 155 | 156 | void asic_remove_timer(asic_t *asic, int index) { 157 | asic->timers->timers[index].flags &= ~TIMER_IN_USE; 158 | } 159 | 160 | int asic_set_clock_rate(asic_t *asic, int new_rate) { 161 | int old_rate = asic->clock_rate; 162 | 163 | int i; 164 | for (i = 0; i < asic->timers->max_timers; i++) { 165 | z80_hardware_timer_t *timer = &asic->timers->timers[i]; 166 | if (timer->flags & TIMER_IN_USE) { 167 | timer->cycles_until_tick = 168 | new_rate / (timer->cycles_until_tick * timer->frequency); 169 | } 170 | } 171 | 172 | asic->clock_rate = new_rate; 173 | return old_rate; 174 | } 175 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/flash.c: -------------------------------------------------------------------------------- 1 | #include "ti/asic.h" 2 | #include "log/log.h" 3 | #include "ti/memory.h" 4 | #include "ti/hardware/flash.h" 5 | 6 | #include 7 | #include 8 | 9 | uint8_t read_mirror(void *device) { 10 | flash_state_t *state = device; 11 | z80iodevice_t port4 = state->asic->cpu->devices[0x04]; 12 | return port4.read_in(port4.device); 13 | } 14 | 15 | uint8_t read_zero(void *device) { 16 | return 0; 17 | } 18 | 19 | void write_control_port(void *device, uint8_t data) { 20 | flash_state_t *state = device; 21 | // TODO: check permissions 22 | state->asic->mmu->flash_unlocked = data & 1; 23 | } 24 | 25 | void write_exclusion_port(void *device, uint8_t data) { 26 | flash_state_t *state = device; 27 | // TODO: implement 28 | } 29 | 30 | uint8_t read_size_port(void *device) { 31 | flash_state_t *state = device; 32 | return state->chip_size; 33 | } 34 | 35 | void write_size_port(void *device, uint8_t data) { 36 | flash_state_t *state = device; 37 | state->chip_size = data & 0x33; 38 | } 39 | 40 | void init_flash_ports(asic_t *asic) { 41 | flash_state_t *state = malloc(sizeof(flash_state_t)); 42 | 43 | memset(state, 0, sizeof(flash_state_t)); 44 | state->asic = asic; 45 | state->chip_size = 0x33; 46 | 47 | uint8_t (*read_port)(void *) = asic->device == TI83p ? read_mirror : read_zero; 48 | z80iodevice_t chip_control_port = { state, read_port, write_control_port }; 49 | z80iodevice_t chip_exclusion_port = { state, read_port, write_exclusion_port }; 50 | z80iodevice_t chip_size_port = { state, read_size_port, write_size_port }; 51 | 52 | asic->cpu->devices[0x14] = chip_control_port; 53 | asic->cpu->devices[0x21] = chip_size_port; 54 | } 55 | 56 | void free_flash_ports(asic_t *asic) { 57 | free(asic->cpu->devices[0x22].device); 58 | } 59 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "ti/hardware/keyboard.h" 2 | #include 3 | #include 4 | #include 5 | #include "ti/asic.h" 6 | #include "core/cpu.h" 7 | 8 | typedef struct { 9 | uint8_t group_mask; 10 | uint8_t groups[8]; 11 | } keyboard_state_t; 12 | 13 | /* Key codes in z80e are group << 4 | bit. That is, 0x14 is bit 4 of group 1. */ 14 | void depress_key(void *keyboard, uint8_t keycode) { 15 | keyboard_state_t *state = keyboard; 16 | uint8_t group = keycode >> 4; 17 | uint8_t mask = 1 << (keycode & 0xF); 18 | state->groups[group] &= ~mask; 19 | } 20 | 21 | void release_key(void *keyboard, uint8_t keycode) { 22 | keyboard_state_t *state = keyboard; 23 | uint8_t group = keycode >> 4; 24 | uint8_t mask = 1 << (keycode & 0xF); 25 | state->groups[group] |= mask; 26 | } 27 | 28 | uint8_t read_keyboard(void *_state) { 29 | keyboard_state_t *state = (keyboard_state_t*)_state; 30 | uint8_t mask = state->group_mask; 31 | 32 | uint8_t value = 0; 33 | int i; 34 | for (i = 7; i >= 0; i--) { 35 | if (mask & 0x80) { 36 | value |= ~state->groups[i]; 37 | } 38 | mask <<= 1; 39 | } 40 | return ~value; 41 | } 42 | 43 | void write_keyboard(void *_state, uint8_t value) { 44 | keyboard_state_t *state = (keyboard_state_t*)_state; 45 | if (value == 0xFF) { 46 | state->group_mask = 0; 47 | return; 48 | } 49 | state->group_mask |= ~value; 50 | } 51 | 52 | z80iodevice_t init_keyboard() { 53 | keyboard_state_t *state = calloc(1, sizeof(keyboard_state_t)); 54 | int i; 55 | for (i = 0; i < 8; i++) { 56 | state->groups[i] = 0xFF; 57 | } 58 | state->group_mask = 0; 59 | z80iodevice_t device = { state, read_keyboard, write_keyboard }; 60 | return device; 61 | } 62 | 63 | void free_keyboard(void *keyboard) { 64 | free(keyboard); 65 | } 66 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/link.c: -------------------------------------------------------------------------------- 1 | #include "ti/asic.h" 2 | #include "log/log.h" 3 | #include "ti/memory.h" 4 | #include "ti/hardware/link.h" 5 | 6 | #include 7 | #include 8 | 9 | uint8_t read_link_port(void *device) { 10 | link_state_t *state = device; 11 | switch (state->asic->device) { 12 | case TI73: 13 | case TI83p: 14 | // TODO: Link assist for 83+ models 15 | return ( 16 | (state->them.tip | state->us.tip) | 17 | ((state->them.ring | state->us.ring) << 1) | 18 | (state->us.tip << 4) | 19 | (state->us.ring << 5) 20 | ); 21 | case TI83pSE: 22 | case TI84p: 23 | case TI84pSE: 24 | case TI84pCSE: 25 | return ( 26 | (state->them.tip | state->us.tip) | 27 | ((state->them.ring | state->us.ring) << 1) | 28 | (state->us.tip << 4) | 29 | (state->us.ring << 5) 30 | ); 31 | default: 32 | return 0; 33 | } 34 | } 35 | 36 | void write_link_port(void *device, uint8_t val) { 37 | link_state_t *state = device; 38 | bool tip = val & 1; 39 | bool ring = val & 2; 40 | state->us.tip = tip; 41 | state->us.ring = ring; 42 | } 43 | 44 | uint8_t read_link_assist_enable_port(void *device) { 45 | link_state_t *state = device; 46 | switch (state->asic->device) { 47 | case TI73: 48 | case TI83p: 49 | return 0; 50 | default: 51 | return state->interrupts.mask; 52 | } 53 | } 54 | 55 | void write_link_assist_enable_port(void *device, uint8_t val) { 56 | link_state_t *state = device; 57 | switch (state->asic->device) { 58 | case TI73: 59 | case TI83p: 60 | break; 61 | default: 62 | state->interrupts.mask = val; 63 | state->asic->cpu->interrupt = (state->interrupts.tx && state->assist.status.int_tx_ready) 64 | || (state->interrupts.rx && state->assist.status.int_rx_ready); 65 | printf("Just wrote %02X to LA enable port\n", val); 66 | printf("rx int: %d tx int: %d\n", 67 | state->interrupts.rx, state->interrupts.tx); 68 | break; 69 | } 70 | } 71 | 72 | uint8_t read_link_assist_rx_port(void *device) { 73 | link_state_t *state = device; 74 | switch (state->asic->device) { 75 | case TI73: 76 | case TI83p: 77 | return 0; 78 | default: 79 | if (!state->assist.status.rx_ready) { 80 | return 0; 81 | } 82 | if (state->assist.status.int_tx_ready) { 83 | state->asic->cpu->interrupt = 0; 84 | } 85 | state->assist.status.rx_ready = false; 86 | state->assist.status.int_rx_ready = false; 87 | uint8_t val = state->assist.rx_buffer; 88 | state->assist.rx_buffer = 0; 89 | return val; 90 | } 91 | } 92 | 93 | void write_link_assist_rx_port(void *device, uint8_t val) { 94 | // Not emualted by z80e 95 | } 96 | 97 | uint8_t read_link_assist_status_port(void *device) { 98 | link_state_t *state = device; 99 | switch (state->asic->device) { 100 | case TI73: 101 | case TI83p: 102 | return 0; 103 | default: 104 | return state->assist.status.u8 & (state->interrupts.mask | ~7); 105 | } 106 | } 107 | 108 | void write_link_assist_status_port(void *device, uint8_t val) { 109 | // Not emualted by z80e 110 | } 111 | 112 | uint8_t read_link_assist_tx_port(void *device) { 113 | return 0; // no-op 114 | } 115 | 116 | void write_link_assist_tx_port(void *device, uint8_t val) { 117 | link_state_t *state = device; 118 | if (!state->assist.status.tx_ready) { 119 | // TODO: What actually happens? Probably this behavior tbh 120 | return; 121 | } 122 | state->assist.status.tx_ready = state->assist.status.int_tx_ready = false; 123 | state->assist.status.tx_active = true; 124 | state->assist.tx_buffer = val; 125 | } 126 | 127 | void init_link_ports(asic_t *asic) { 128 | link_state_t *state = malloc(sizeof(link_state_t)); 129 | 130 | memset(state, 0, sizeof(link_state_t)); 131 | state->asic = asic; 132 | state->assist.status.tx_ready = state->assist.status.int_tx_ready = true; 133 | 134 | z80iodevice_t link_port = { state, read_link_port, write_link_port }; 135 | z80iodevice_t link_assist_enable = { state, read_link_assist_enable_port, write_link_assist_enable_port }; 136 | z80iodevice_t link_assist_status = { state, read_link_assist_status_port, write_link_assist_status_port }; 137 | z80iodevice_t link_assist_rx_read = { state, read_link_assist_rx_port, write_link_assist_rx_port }; 138 | z80iodevice_t link_assist_tx_read = { state, read_link_assist_tx_port, write_link_assist_tx_port }; 139 | 140 | asic->cpu->devices[0x00] = link_port; 141 | asic->cpu->devices[0x08] = link_assist_enable; 142 | asic->cpu->devices[0x09] = link_assist_status; 143 | asic->cpu->devices[0x0A] = link_assist_rx_read; 144 | asic->cpu->devices[0x0D] = link_assist_tx_read; 145 | } 146 | 147 | void free_link_ports(asic_t *asic) { 148 | free(asic->cpu->devices[0x00].device); 149 | } 150 | 151 | bool link_recv_ready(asic_t *asic) { 152 | link_state_t *state = asic->cpu->devices[0x00].device; 153 | return !state->assist.status.rx_ready; 154 | } 155 | 156 | bool link_recv_byte(asic_t *asic, uint8_t val) { 157 | printf("Receiving %02X via link port\n", val); 158 | link_state_t *state = asic->cpu->devices[0x00].device; 159 | if (!link_recv_ready(asic)) { 160 | return false; 161 | } 162 | state->assist.status.rx_ready = state->assist.status.int_rx_ready = true; 163 | state->assist.rx_buffer = val; 164 | 165 | if (!state->interrupts.disabled && state->interrupts.rx) { 166 | printf("Raising interrupt\n"); 167 | asic->cpu->interrupt = 1; 168 | } 169 | return true; 170 | } 171 | 172 | int link_read_tx_buffer(asic_t *asic) { 173 | link_state_t *state = asic->cpu->devices[0x00].device; 174 | if (state->assist.status.tx_active) { 175 | state->assist.status.tx_active = false; 176 | state->assist.status.tx_ready = state->assist.status.int_tx_ready = true; 177 | if (!state->interrupts.disabled && state->interrupts.tx) { 178 | asic->cpu->interrupt = 1; 179 | } 180 | return state->assist.tx_buffer; 181 | } 182 | return EOF; 183 | } 184 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/memorymapping.c: -------------------------------------------------------------------------------- 1 | #include "ti/asic.h" 2 | #include "log/log.h" 3 | #include "ti/memory.h" 4 | #include "ti/hardware/memorymapping.h" 5 | #include "ti/hardware/interrupts.h" 6 | 7 | #include 8 | #include 9 | 10 | void reload_mapping(memory_mapping_state_t *state) { 11 | ti_mmu_bank_state_t *banks = state->asic->mmu->banks; 12 | 13 | banks[0].page = 0; 14 | banks[0].flash = 1; 15 | 16 | switch (state->map_mode) { 17 | case 0: 18 | banks[1].page = state->bank_a_page; 19 | banks[1].flash = state->bank_a_flash; 20 | banks[2].page = state->bank_b_page; 21 | banks[2].flash = state->bank_b_flash; 22 | if (state->asic->device == TI83p) { 23 | banks[3].page = 0; 24 | banks[3].flash = 0; 25 | } else { 26 | banks[3].page = state->ram_bank_page; 27 | banks[3].flash = 0; 28 | } 29 | break; 30 | case 1: 31 | banks[1].page = state->bank_a_page & 0xFE; 32 | banks[1].flash = state->bank_a_flash; 33 | if (state->asic->device == TI83p) { 34 | banks[2].page = state->bank_a_page; 35 | banks[2].flash = state->bank_a_flash; 36 | } else { 37 | banks[2].page = state->bank_a_page | 1; 38 | banks[2].flash = state->bank_a_flash; 39 | } 40 | banks[3].page = state->bank_b_page; 41 | banks[3].flash = state->bank_b_flash; 42 | break; 43 | } 44 | 45 | int i; 46 | for (i = 0; i < 4; i++) { 47 | if (banks[i].flash && banks[i].page > state->asic->mmu->settings.flash_pages) { 48 | log_message(state->asic->log, L_ERROR, "memorymapping", "ERROR: Flash page 0x%02X doesn't exist! (at 0x%04X)", banks[i].page, state->asic->cpu->registers.PC); 49 | banks[i].page &= state->asic->mmu->settings.flash_pages; 50 | } else if (!banks[i].flash && banks[i].page > state->asic->mmu->settings.ram_pages) { 51 | log_message(state->asic->log, L_ERROR, "memorymapping", "ERROR: RAM page 0x%02X doesn't exist! (at 0x%04X)", banks[i].page, state->asic->cpu->registers.PC); 52 | banks[i].page &= state->asic->mmu->settings.ram_pages; 53 | } 54 | } 55 | } 56 | 57 | uint8_t read_device_status_port(void *device) { 58 | memory_mapping_state_t *state = device; 59 | 60 | return read_interrupting_device(state->asic->interrupts); 61 | } 62 | 63 | void write_device_status_port(void *device, uint8_t data) { 64 | memory_mapping_state_t *state = device; 65 | 66 | state->map_mode = data & 1; 67 | log_message(state->asic->log, L_DEBUG, "memorymapping", "Set mapping mode to %d (at 0x%04X)", state->map_mode, state->asic->cpu->registers.PC); 68 | reload_mapping(state); 69 | 70 | write_timer_speed(state->asic->interrupts, data); 71 | } 72 | 73 | uint8_t read_ram_paging_port(void *device) { 74 | memory_mapping_state_t *state = device; 75 | 76 | return state->ram_bank_page; 77 | } 78 | 79 | void write_ram_paging_port(void *device, uint8_t data) { 80 | memory_mapping_state_t *state = device; 81 | 82 | state->ram_bank_page = data & 0x7; // 0b111 83 | log_message(state->asic->log, L_DEBUG, "memorymapping", "Set ram banking page to %d (at 0x%04X)", state->ram_bank_page, state->asic->cpu->registers.PC); 84 | reload_mapping(state); 85 | } 86 | 87 | uint8_t read_bank_a_paging_port(void *device) { 88 | memory_mapping_state_t *state = device; 89 | 90 | uint8_t return_value = state->bank_a_page; 91 | if (!state->bank_a_flash) { 92 | if (state->asic->device == TI83p) { 93 | return_value |= 1 << 6; 94 | } else { 95 | return_value |= 1 << 7; 96 | } 97 | } 98 | 99 | return return_value; 100 | } 101 | 102 | void write_bank_a_paging_port(void *device, uint8_t data) { 103 | memory_mapping_state_t *state = device; 104 | 105 | int is_flash = 0; 106 | 107 | if (state->asic->device == TI83p) { 108 | is_flash = (data & (1 << 6)) == 0; 109 | data &= 0x1F; // 0b11111 110 | } else { 111 | is_flash = (data & (1 << 7)) == 0; 112 | data &= 0x7F; // 0b111111 113 | } 114 | 115 | state->bank_a_flash = is_flash; 116 | state->bank_a_page = data; 117 | 118 | log_message(state->asic->log, L_DEBUG, "memorymapping", "Set bank A page to %c:%02X (at 0x%04X)", state->bank_a_flash ? 'F' : 'R', state->bank_a_page, state->asic->cpu->registers.PC); 119 | reload_mapping(state); 120 | } 121 | 122 | uint8_t read_bank_b_paging_port(void *device) { 123 | memory_mapping_state_t *state = device; 124 | 125 | uint8_t return_value = state->bank_b_page; 126 | if (!state->bank_b_flash) { 127 | if (state->asic->device == TI83p) { 128 | return_value |= 1 << 6; 129 | } else { 130 | return_value |= 1 << 7; 131 | } 132 | } 133 | 134 | return return_value; 135 | } 136 | 137 | void write_bank_b_paging_port(void *device, uint8_t data) { 138 | memory_mapping_state_t *state = device; 139 | 140 | int is_flash = 0; 141 | 142 | if (state->asic->device == TI83p) { 143 | is_flash = (data & (1 << 6)) == 0; 144 | data &= 0x1F; // 0b11111 145 | } else { 146 | is_flash = (data & (1 << 7)) == 0; 147 | data &= 0x7F; // 0b111111 148 | } 149 | 150 | state->bank_b_flash = is_flash; 151 | state->bank_b_page = data; 152 | 153 | log_message(state->asic->log, L_DEBUG, "memorymapping", "Set bank B page to %c:%02X (at 0x%04X)", state->bank_b_flash ? 'F' : 'R', state->bank_b_page, state->asic->cpu->registers.PC); 154 | reload_mapping(state); 155 | } 156 | 157 | void init_mapping_ports(asic_t *asic) { 158 | memory_mapping_state_t *state = malloc(sizeof(memory_mapping_state_t)); 159 | 160 | memset(state, 0, sizeof(memory_mapping_state_t)); 161 | state->asic = asic; 162 | 163 | state->bank_a_flash = 1; 164 | state->bank_b_flash = 1; // horrible, isn't it? 165 | 166 | z80iodevice_t device_status_port = { state, read_device_status_port, write_device_status_port }; 167 | z80iodevice_t ram_paging_port = { state, read_ram_paging_port, write_ram_paging_port }; 168 | z80iodevice_t bank_a_paging_port = { state, read_bank_a_paging_port, write_bank_a_paging_port }; 169 | z80iodevice_t bank_b_paging_port = { state, read_bank_b_paging_port, write_bank_b_paging_port }; 170 | 171 | asic->cpu->devices[0x04] = device_status_port; 172 | 173 | if (asic->device != TI83p) { 174 | asic->cpu->devices[0x05] = ram_paging_port; 175 | } 176 | 177 | asic->cpu->devices[0x06] = bank_a_paging_port; 178 | asic->cpu->devices[0x07] = bank_b_paging_port; 179 | 180 | reload_mapping(state); 181 | } 182 | 183 | void free_mapping_ports(asic_t *asic) { 184 | free(asic->cpu->devices[0x06].device); 185 | } 186 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/speed.c: -------------------------------------------------------------------------------- 1 | #include "ti/hardware/speed.h" 2 | #include 3 | #include 4 | #include 5 | #include "ti/asic.h" 6 | #include "core/cpu.h" 7 | #include "ti/hardware/interrupts.h" 8 | 9 | uint8_t read_speed(void *_asic) { 10 | asic_t *asic = (asic_t*)_asic; 11 | if (asic->clock_rate == 6000000) { 12 | return 0; 13 | } else if (asic->clock_rate == 15000000) { 14 | return 1; 15 | } 16 | // TODO: set overclock 17 | return 0; 18 | } 19 | 20 | void write_speed(void *_asic, uint8_t value) { 21 | asic_t *asic = (asic_t*)_asic; 22 | if (value == 0) { 23 | asic_set_clock_rate(asic, 6000000); 24 | } else if (value == 1) { 25 | asic_set_clock_rate(asic, 15000000); 26 | } 27 | // TODO: set overclock 28 | } 29 | 30 | z80iodevice_t init_speed(asic_t *asic) { 31 | z80iodevice_t device = { asic, read_speed, write_speed }; 32 | return device; 33 | } 34 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/status.c: -------------------------------------------------------------------------------- 1 | #include "ti/hardware/status.h" 2 | #include 3 | #include 4 | #include 5 | #include "ti/asic.h" 6 | #include "core/cpu.h" 7 | #include "ti/hardware/interrupts.h" 8 | 9 | typedef struct { 10 | asic_t *asic; 11 | } status_t; 12 | 13 | uint8_t read_status(void *_status) { 14 | status_t *status = (status_t*)_status; 15 | uint8_t value = 0x00; 16 | if (status->asic->battery_remove_check) { 17 | if (status->asic->battery != BATTERIES_REMOVED) { 18 | value |= 0x01; 19 | } 20 | } else { 21 | if (status->asic->battery == BATTERIES_GOOD) { 22 | value |= 0x01; 23 | } 24 | } 25 | 26 | value |= status->asic->device != TI73 ? 0x02 : 0x00; 27 | 28 | if (status->asic->mmu->flash_unlocked) { 29 | value |= 0x04; 30 | } 31 | 32 | if (status->asic->device != TI73 && status->asic->device != TI83p) { 33 | value |= 0x80; 34 | } 35 | if (status->asic->device != TI73 && status->asic->device != TI83p) { 36 | value |= 0x40; // link assist available 37 | } 38 | if (status->asic->device == TI84p || status->asic->device == TI84pSE || status->asic->device == TI84pCSE) { 39 | value |= 0x20; 40 | } 41 | return value; 42 | } 43 | 44 | void write_status(void *_status, uint8_t value) { 45 | status_t *status = (status_t*)_status; 46 | if (status->asic->device == TI83p || status->asic->device == TI73) { 47 | return; 48 | } 49 | 50 | write_acknowledged_interrupts(status->asic->interrupts, value); 51 | } 52 | 53 | z80iodevice_t init_status(asic_t *asic) { 54 | status_t *state = malloc(sizeof(status_t)); 55 | state->asic = asic; 56 | z80iodevice_t device = { state, read_status, write_status }; 57 | return device; 58 | } 59 | 60 | void free_status(z80iodevice_t status) { 61 | free(status.device); 62 | } 63 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/t6a04.c: -------------------------------------------------------------------------------- 1 | #include "ti/hardware/t6a04.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "log/log.h" 8 | 9 | #ifdef CURSES 10 | #include 11 | #endif 12 | 13 | #ifdef CURSES 14 | #define lcd_print(...) wprintw(lcd->lcd_display, __VA_ARGS__) 15 | #else 16 | #define lcd_print(...) printf( __VA_ARGS__) 17 | #endif 18 | 19 | void setup_lcd_display(asic_t *asic, hook_info_t *hook) { 20 | ti_bw_lcd_t *lcd = malloc(sizeof(ti_bw_lcd_t)); 21 | 22 | bw_lcd_reset(lcd); 23 | 24 | lcd->ram = malloc((64 * 120)); 25 | int cY; 26 | int cX; 27 | for (cX = 0; cX < 64; cX++) { 28 | for (cY = 0; cY < 120; cY++) { 29 | bw_lcd_write_screen(lcd, cY, cX, 0); 30 | } 31 | } 32 | 33 | lcd->hook = hook; 34 | lcd->asic = asic; 35 | asic->cpu->devices[0x10].device = lcd; 36 | asic->cpu->devices[0x10].read_in = bw_lcd_status_read; 37 | asic->cpu->devices[0x10].write_out = bw_lcd_status_write; 38 | 39 | asic->cpu->devices[0x11].device = lcd; 40 | asic->cpu->devices[0x11].read_in = bw_lcd_data_read; 41 | asic->cpu->devices[0x11].write_out = bw_lcd_data_write; 42 | } 43 | 44 | uint8_t bw_lcd_read_screen(ti_bw_lcd_t *lcd, int Y, int X) { 45 | int location = X * 120 + Y; 46 | return !!(lcd->ram[location >> 3] & (1 << (location % 8))); 47 | } 48 | 49 | int bw_lcd_write_screen(ti_bw_lcd_t *lcd, int Y, int X, char val) { 50 | val = !!val; 51 | int location = X * 120 + Y; 52 | int orig = lcd->ram[location >> 3]; 53 | lcd->ram[location >> 3] &= ~(1 << (location % 8)); 54 | lcd->ram[location >> 3] |= (val << (location % 8)); 55 | 56 | return orig != lcd->ram[location >> 3]; 57 | } 58 | 59 | void bw_lcd_reset(ti_bw_lcd_t *lcd) { 60 | lcd->display_on = 0; 61 | lcd->word_length = 1; 62 | lcd->up = 1; 63 | lcd->counter = 0; 64 | lcd->Y = 0; 65 | lcd->Z = 0; 66 | lcd->X = 0; 67 | lcd->contrast = 0; 68 | lcd->op_amp1 = 0; 69 | lcd->op_amp2 = 0; 70 | } 71 | 72 | uint8_t bw_lcd_status_read(void *device) { 73 | ti_bw_lcd_t *lcd = device; 74 | 75 | uint8_t retval = 0; 76 | retval |= (0) << 7; 77 | retval |= (lcd->word_length) << 6; 78 | retval |= (lcd->display_on) << 5; 79 | retval |= (0) << 4; 80 | retval |= (lcd->counter) << 1; 81 | retval |= (lcd->up) << 0; 82 | 83 | return retval; 84 | } 85 | 86 | void bw_lcd_status_write(void *device, uint8_t val) { 87 | ti_bw_lcd_t *lcd = device; 88 | 89 | log_message(lcd->asic->log, L_DEBUG, "lcd", "Wrote 0x%02X (0b%d%d%d%d%d%d%d%d) to status", val, 90 | !!(val & (1 << 7)), 91 | !!(val & (1 << 6)), 92 | !!(val & (1 << 5)), 93 | !!(val & (1 << 4)), 94 | !!(val & (1 << 3)), 95 | !!(val & (1 << 2)), 96 | !!(val & (1 << 1)), 97 | !!(val & (1 << 0)) 98 | ); 99 | if ((val & 0xC0) == 0xC0) { // 0b11XXXXXX 100 | lcd->contrast = val & 0x3F; 101 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet contrast to 0x%02X", lcd->contrast); 102 | } else if (val & 0x80) { // 0b10XXXXXX 103 | lcd->X = val & 0x3F; 104 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet X (vertical!) to 0x%02X", lcd->X); 105 | } else if (val & 0x40) { // 0b01XXXXXX 106 | lcd->Z = val & 0x3F; 107 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet Z (vertical scroll) to 0x%02X", lcd->Z); 108 | } else if (val & 0x20) { // 0b001XXXXX 109 | lcd->Y = val & 0x1F; 110 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet Y (horizontal!) to 0x%02X", lcd->Y); 111 | } else if ((val & 0x18) == 0x18) { // 0b00011*** 112 | // test mode - not emulating yet 113 | } else if (val & 0x10) { // 0b00010*XX 114 | lcd->op_amp1 = val & 0x03; 115 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet Op-Amp 1 to 0x%02X", lcd->op_amp1); 116 | } else if (val & 0x08) { // 0b00001*XX 117 | lcd->op_amp2 = val & 0x03; 118 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet Op-Amp 2 to 0x%02X", lcd->op_amp2); 119 | } else if (val & 0x04) { // 0b000001XX 120 | lcd->counter = !!(val & 0x02); 121 | lcd->up = !!(val & 0x01); 122 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tSet counter to %s and Up/Down to %s", 123 | lcd->counter ? "Y" : "X", lcd->up ? "Up" : "Down"); 124 | } else if (val & 0x02) { // 0b0000001X 125 | lcd->display_on = !!(val & 0x01); 126 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tDisplay turned %s", lcd->display_on ? "ON" : "OFF"); 127 | hook_on_lcd_update(lcd->hook, lcd); 128 | } else { // 0b0000000X 129 | lcd->word_length = !!(val & 0x01); 130 | log_message(lcd->asic->log, L_DEBUG, "lcd", "\tWord Length set to %d", lcd->word_length ? 8 : 6); 131 | } 132 | } 133 | 134 | void bw_lcd_advance_int_pointer(ti_bw_lcd_t *lcd, int *Y, int *X) { 135 | if (lcd->counter) { // Y 136 | (*Y)++; 137 | *Y = *Y % 120; 138 | } else { // X 139 | (*X)++; 140 | *X = *X % 64; 141 | } 142 | } 143 | 144 | void bw_lcd_advance_pointer(ti_bw_lcd_t *lcd) { 145 | int maxX = lcd->word_length ? 15 : 20; 146 | if (lcd->counter) { // Y 147 | if (!lcd->up) { 148 | lcd->Y--; 149 | if (lcd->Y < 0) { 150 | lcd->Y = maxX - 1; 151 | } 152 | } else { 153 | lcd->Y++; 154 | // wrap at maxX 155 | if (lcd->Y == maxX) { 156 | lcd->Y = 0; 157 | } 158 | // unless already out of range, then wrap at 32 159 | lcd->Y = lcd->Y % 32; 160 | } 161 | } else { // X 162 | if (!lcd->up) { 163 | lcd->X--; 164 | if (lcd->X < 0) { 165 | lcd->X = 63; 166 | } 167 | } else { 168 | lcd->X++; 169 | lcd->X = lcd->X % 64; 170 | } 171 | } 172 | } 173 | 174 | uint8_t bw_lcd_data_read(void *device) { 175 | ti_bw_lcd_t *lcd = device; 176 | 177 | int cY = lcd->Y * (lcd->word_length ? 8 : 6); 178 | int cX = lcd->X; 179 | 180 | uint8_t retval = lcd->read_reg; 181 | int max = lcd->word_length ? 8 : 6; 182 | int i = 0; 183 | lcd->read_reg = 0; 184 | for (i = 0; i < max; i++) { 185 | lcd->read_reg <<= 1; 186 | lcd->read_reg |= bw_lcd_read_screen(lcd, cY, cX); 187 | cY++; 188 | } 189 | 190 | bw_lcd_advance_pointer(lcd); 191 | return retval; 192 | } 193 | 194 | void bw_lcd_data_write(void *device, uint8_t val) { 195 | ti_bw_lcd_t *lcd = device; 196 | 197 | int cY = lcd->Y * (lcd->word_length ? 8 : 6); 198 | int cX = lcd->X; 199 | 200 | int max = lcd->word_length ? 8 : 6; 201 | int i = 0; 202 | int dirty = 0; 203 | 204 | cY += max - 1; 205 | for (i = 0; i < max; i++) { 206 | dirty |= bw_lcd_write_screen(lcd, cY, cX, val & (1 << i)); 207 | cY--; 208 | } 209 | 210 | log_message(lcd->asic->log, L_DEBUG, "lcd", "Wrote %02X (0b%d%d%d%d%d%d%d%d) to %d (Y), %d (X)", 211 | val, 212 | !!(val & (1 << 7)), !!(val & (1 << 6)), 213 | !!(val & (1 << 5)), !!(val & (1 << 4)), 214 | !!(val & (1 << 3)), !!(val & (1 << 2)), 215 | !!(val & (1 << 1)), !!(val & (1 << 0)), 216 | lcd->Y, lcd->X); 217 | 218 | bw_lcd_advance_pointer(lcd); 219 | if (dirty) { 220 | hook_on_lcd_update(lcd->hook, lcd); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /libz80e/src/ti/hardware/timers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Implements the 83+SE/84+((C)SE) crystal timers 3 | */ 4 | #include "ti/asic.h" 5 | #include "log/log.h" 6 | #include "ti/memory.h" 7 | #include "ti/hardware/timers.h" 8 | #include "ti/hardware/interrupts.h" 9 | 10 | #include 11 | #include 12 | 13 | uint8_t read_timer_freq_port(void *device) { 14 | struct crystal_timer *timer = device; 15 | return timer->frequency; 16 | } 17 | 18 | uint8_t read_timer_loop_port(void *device) { 19 | struct crystal_timer *timer = device; 20 | return timer->loop; 21 | } 22 | 23 | uint8_t read_timer_counter_port(void *device) { 24 | struct crystal_timer *timer = device; 25 | return timer->counter; 26 | } 27 | 28 | void write_timer_freq_port(void *device, uint8_t val) { 29 | struct crystal_timer *timer = device; 30 | timer->frequency = val; 31 | } 32 | 33 | void write_timer_loop_port(void *device, uint8_t val) { 34 | struct crystal_timer *timer = device; 35 | timer->loop = val; 36 | } 37 | 38 | void write_timer_counter_port(void *device, uint8_t val) { 39 | struct crystal_timer *timer = device; 40 | timer->counter = val; 41 | } 42 | 43 | void init_crystal_timers(asic_t *asic) { 44 | struct crystal_timer *timer_1 = calloc(1, sizeof(struct crystal_timer)); 45 | struct crystal_timer *timer_2 = calloc(1, sizeof(struct crystal_timer)); 46 | struct crystal_timer *timer_3 = calloc(1, sizeof(struct crystal_timer)); 47 | timer_1->asic = asic; 48 | timer_2->asic = asic; 49 | timer_3->asic = asic; 50 | 51 | z80iodevice_t timer1_freq_port = { timer_1, read_timer_freq_port, write_timer_freq_port }; 52 | z80iodevice_t timer1_loop_port = { timer_1, read_timer_loop_port, write_timer_loop_port }; 53 | z80iodevice_t timer1_count_port = { timer_1, read_timer_counter_port, write_timer_counter_port }; 54 | 55 | z80iodevice_t timer2_freq_port = { timer_2, read_timer_freq_port, write_timer_freq_port }; 56 | z80iodevice_t timer2_loop_port = { timer_2, read_timer_loop_port, write_timer_loop_port }; 57 | z80iodevice_t timer2_count_port = { timer_2, read_timer_counter_port, write_timer_counter_port }; 58 | 59 | z80iodevice_t timer3_freq_port = { timer_3, read_timer_freq_port, write_timer_freq_port }; 60 | z80iodevice_t timer3_loop_port = { timer_3, read_timer_loop_port, write_timer_loop_port }; 61 | z80iodevice_t timer3_count_port = { timer_3, read_timer_counter_port, write_timer_counter_port }; 62 | 63 | asic->cpu->devices[0x30] = timer1_freq_port; 64 | asic->cpu->devices[0x31] = timer1_loop_port; 65 | asic->cpu->devices[0x32] = timer1_count_port; 66 | 67 | asic->cpu->devices[0x33] = timer2_freq_port; 68 | asic->cpu->devices[0x34] = timer2_loop_port; 69 | asic->cpu->devices[0x35] = timer2_count_port; 70 | 71 | asic->cpu->devices[0x36] = timer3_freq_port; 72 | asic->cpu->devices[0x37] = timer3_loop_port; 73 | asic->cpu->devices[0x38] = timer3_count_port; 74 | } 75 | 76 | void free_crystal_timers(asic_t *asic) { 77 | free(asic->cpu->devices[0x30].device); 78 | free(asic->cpu->devices[0x33].device); 79 | free(asic->cpu->devices[0x38].device); 80 | } 81 | -------------------------------------------------------------------------------- /libz80e/src/ti/memory.c: -------------------------------------------------------------------------------- 1 | #include "ti/memory.h" 2 | #include "core/cpu.h" 3 | #include "ti/ti.h" 4 | #include "log/log.h" 5 | 6 | #include "debugger/debugger.h" 7 | #include "disassembler/disassemble.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | ti_mmu_t* ti_mmu_init(ti_device_type device_type, log_t *log) { 16 | ti_mmu_t *mmu = malloc(sizeof(ti_mmu_t)); 17 | switch (device_type) { 18 | case TI83p: 19 | case TI73: 20 | mmu->settings.ram_pages = 3; 21 | mmu->settings.flash_pages = 0x20; 22 | break; 23 | case TI84p: 24 | mmu->settings.ram_pages = 8; 25 | mmu->settings.flash_pages = 0x40; 26 | break; 27 | case TI83pSE: 28 | case TI84pSE: 29 | mmu->settings.ram_pages = 8; 30 | mmu->settings.flash_pages = 0x80; 31 | break; 32 | case TI84pCSE: 33 | mmu->settings.ram_pages = 3; 34 | mmu->settings.flash_pages = 0x100; 35 | break; 36 | } 37 | mmu->ram = malloc(mmu->settings.ram_pages * 0x4000); 38 | memset(mmu->ram, 0, mmu->settings.ram_pages * 0x4000); 39 | mmu->flash = malloc(mmu->settings.flash_pages * 0x4000); 40 | memset(mmu->flash, 0xFF, mmu->settings.flash_pages * 0x4000); 41 | mmu->flash_unlocked = 0; 42 | memset(mmu->flash_writes, 0, sizeof(flash_write_t) * 6); 43 | mmu->flash_write_index = 0; 44 | mmu->log = log; 45 | // Default bank mappings 46 | mmu->banks[0].page = 0; mmu->banks[0].flash = 1; 47 | mmu->banks[1].page = 0; mmu->banks[1].flash = 1; 48 | mmu->banks[2].page = 0; mmu->banks[2].flash = 1; 49 | mmu->banks[3].page = 0; mmu->banks[3].flash = 0; 50 | return mmu; 51 | } 52 | 53 | void ti_mmu_free(ti_mmu_t *mmu) { 54 | free(mmu->ram); 55 | free(mmu->flash); 56 | free(mmu); 57 | } 58 | 59 | void chip_reset(ti_mmu_t *mmu, uint32_t address, uint8_t value) { 60 | mmu->flash_write_index = 0; 61 | } 62 | 63 | void chip_write(ti_mmu_t *mmu, uint32_t address, uint8_t value) { 64 | mmu->flash[address] &= value; 65 | } 66 | 67 | void chip_erase_sector(ti_mmu_t *mmu, uint32_t address, uint8_t value) { 68 | uint32_t length = 0x10000; 69 | if ((address >> 16) + 1 == mmu->settings.flash_pages >> 2) { 70 | switch ((address >> 13) & 7) { 71 | case 0: 72 | case 1: 73 | case 2: 74 | case 3: 75 | length = 0x8000; 76 | break; 77 | case 4: 78 | case 5: 79 | length = 0x2000; 80 | break; 81 | case 6: 82 | case 7: 83 | length = 0x4000; 84 | break; 85 | } 86 | } 87 | memset(&mmu->flash[address & ~(length - 1)], 0xFF, length); 88 | } 89 | 90 | void chip_erase(ti_mmu_t *mmu, uint32_t address, uint8_t value) { 91 | memset(mmu->flash, 0xFF, mmu->settings.flash_pages * 0x4000); 92 | log_message(mmu->log, L_WARN, "mmu", "Erased entire Flash chip - you probably didn't want to do that."); 93 | } 94 | 95 | uint8_t ti_read_byte(void *memory, uint16_t address) { 96 | ti_mmu_t *mmu = memory; 97 | ti_mmu_bank_state_t bank = mmu->banks[address / 0x4000]; 98 | uint32_t mapped_address = address; 99 | mapped_address %= 0x4000; 100 | mapped_address += bank.page * 0x4000; 101 | uint8_t byte = 0; 102 | if (bank.flash) { 103 | chip_reset(mmu, mapped_address, 0); 104 | byte = mmu->flash[mapped_address]; 105 | } else { 106 | byte = mmu->ram[mapped_address]; 107 | } 108 | byte = hook_on_memory_read(mmu->hook, address, byte); 109 | return byte; 110 | } 111 | 112 | struct flash_pattern { 113 | int length; 114 | const flash_write_t pattern[6]; 115 | void (*handler)(ti_mmu_t *memory, uint32_t address, uint8_t value); 116 | }; 117 | 118 | struct flash_pattern patterns[] = { 119 | { 120 | .length = 4, 121 | .pattern = { 122 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xAA, .value_mask = 0xFF }, 123 | { .address = 0x555, .address_mask = 0xFFF, .value = 0x55, .value_mask = 0xFF }, 124 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xA0, .value_mask = 0xFF }, 125 | { .address = 0x000, .address_mask = 0x000, .value = 0x00, .value_mask = 0x00 }, 126 | }, 127 | .handler = chip_write 128 | }, 129 | { 130 | .length = 6, 131 | .pattern = { 132 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xAA, .value_mask = 0xFF }, 133 | { .address = 0x555, .address_mask = 0xFFF, .value = 0x55, .value_mask = 0xFF }, 134 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0x80, .value_mask = 0xFF }, 135 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xAA, .value_mask = 0xFF }, 136 | { .address = 0x555, .address_mask = 0xFFF, .value = 0x55, .value_mask = 0xFF }, 137 | { .address = 0x000, .address_mask = 0x000, .value = 0x00, .value_mask = 0x00 }, 138 | }, 139 | .handler = chip_erase_sector 140 | }, 141 | { 142 | .length = 6, 143 | .pattern = { 144 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xAA, .value_mask = 0xFF }, 145 | { .address = 0x555, .address_mask = 0xFFF, .value = 0x55, .value_mask = 0xFF }, 146 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0x80, .value_mask = 0xFF }, 147 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0xAA, .value_mask = 0xFF }, 148 | { .address = 0x555, .address_mask = 0xFFF, .value = 0x55, .value_mask = 0xFF }, 149 | { .address = 0xAAA, .address_mask = 0xFFF, .value = 0x10, .value_mask = 0xFF }, 150 | }, 151 | .handler = chip_erase 152 | }, 153 | { 154 | .length = 0 155 | } 156 | // TODO: More patterns 157 | }; 158 | 159 | void ti_write_byte(void *memory, uint16_t address, uint8_t value) { 160 | ti_mmu_t *mmu = memory; 161 | ti_mmu_bank_state_t bank = mmu->banks[address / 0x4000]; 162 | uint32_t mapped_address = address; 163 | mapped_address %= 0x4000; 164 | mapped_address += bank.page * 0x4000; 165 | 166 | value = hook_on_memory_write(mmu->hook, address, value); 167 | 168 | if (!bank.flash) 169 | mmu->ram[mapped_address] = value; 170 | else { 171 | if (mmu->flash_unlocked) { 172 | flash_write_t *w = &mmu->flash_writes[mmu->flash_write_index++]; 173 | w->address = address; 174 | w->value = value; 175 | int partial_match = 0; 176 | struct flash_pattern *pattern; 177 | for (pattern = patterns; pattern->length; pattern++) { 178 | int i; 179 | for (i = 0; i < mmu->flash_write_index && i < pattern->length && 180 | (mmu->flash_writes[i].address & pattern->pattern[i].address_mask) == pattern->pattern[i].address && 181 | (mmu->flash_writes[i].value & pattern->pattern[i].value_mask) == pattern->pattern[i].value; i++) { 182 | } 183 | if (i == pattern->length) { 184 | pattern->handler(mmu, mapped_address, value); 185 | partial_match = 0; 186 | break; 187 | } else if (i == mmu->flash_write_index) { 188 | partial_match = 1; 189 | } 190 | } 191 | if (!partial_match) { 192 | chip_reset(mmu, mapped_address, value); 193 | } 194 | } 195 | } 196 | } 197 | 198 | void mmu_force_write(void *memory, uint16_t address, uint8_t value) { 199 | ti_mmu_t *mmu = memory; 200 | ti_mmu_bank_state_t bank = mmu->banks[address / 0x4000]; 201 | uint32_t mapped_address = address; 202 | mapped_address %= 0x4000; 203 | mapped_address += bank.page * 0x4000; 204 | 205 | value = hook_on_memory_write(mmu->hook, address, value); 206 | 207 | if (!bank.flash) 208 | mmu->ram[mapped_address] = value; 209 | else { 210 | mmu->flash[mapped_address] = value; 211 | } 212 | } 213 | --------------------------------------------------------------------------------