From 53aa7efca291ce404a292c4c21b4c20478176db6 Mon Sep 17 00:00:00 2001 From: David Kuder Date: Wed, 5 Apr 2023 23:53:21 -0400 Subject: [PATCH] Initial import --- CMakeLists.txt | 146 ++ build.sh | 20 + cmake/pico_sdk_import.cmake | 62 + common/abus-4ns.pio | 97 + common/abus-8ns.pio | 97 + common/abus-gs-4ns.pio | 97 + common/abus-gs-8ns.pio | 97 + common/abus.c | 95 + common/abus.h | 13 + common/abus.pio | 97 + common/buffers.c | 56 + common/buffers.h | 111 + common/build.h | 4 + common/cfgtoken.h | 54 + common/config.c | 627 +++++ common/config.h | 111 + common/dmacopy.c | 50 + common/dmacopy.h | 4 + common/flash.c | 24 + common/flash.h | 69 + common/main.c | 177 ++ common/modes.h | 18 + common/usb_descriptors.c | 238 ++ delayed_copy.ld | 271 ++ lwipopts.h | 91 + tusb_config.h | 114 + vga/businterface.c | 248 ++ vga/businterface.h | 3 + vga/hires_color_patterns.h | 525 ++++ vga/hires_dot_patterns.h | 41 + vga/logo.h | 74 + vga/render.c | 263 ++ vga/render.h | 82 + vga/render_80col.c | 72 + vga/render_dgr.c | 102 + vga/render_dhgr.c | 132 + vga/render_hires.c | 113 + vga/render_lores.c | 123 + vga/render_shr.c | 101 + vga/render_test.c | 298 +++ vga/render_text.c | 144 ++ vga/vga12.pio | 116 + vga/vga9.pio | 116 + vga/vgabuf.c | 20 + vga/vgabuf.h | 34 + vga/vgamain.c | 16 + vga/vgaout.c | 259 ++ vga/vgaout.h | 50 + z80/businterface.c | 72 + z80/businterface.h | 3 + z80/z80buf.h | 21 + z80/z80cpu.h | 4639 +++++++++++++++++++++++++++++++++++ z80/z80main.c | 121 + z80/z80rom.h | 130 + 54 files changed, 10758 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 build.sh create mode 100644 cmake/pico_sdk_import.cmake create mode 100644 common/abus-4ns.pio create mode 100644 common/abus-8ns.pio create mode 100644 common/abus-gs-4ns.pio create mode 100644 common/abus-gs-8ns.pio create mode 100644 common/abus.c create mode 100644 common/abus.h create mode 100644 common/abus.pio create mode 100644 common/buffers.c create mode 100644 common/buffers.h create mode 100644 common/build.h create mode 100644 common/cfgtoken.h create mode 100644 common/config.c create mode 100644 common/config.h create mode 100644 common/dmacopy.c create mode 100644 common/dmacopy.h create mode 100644 common/flash.c create mode 100644 common/flash.h create mode 100644 common/main.c create mode 100644 common/modes.h create mode 100644 common/usb_descriptors.c create mode 100644 delayed_copy.ld create mode 100644 lwipopts.h create mode 100644 tusb_config.h create mode 100644 vga/businterface.c create mode 100644 vga/businterface.h create mode 100644 vga/hires_color_patterns.h create mode 100644 vga/hires_dot_patterns.h create mode 100644 vga/logo.h create mode 100644 vga/render.c create mode 100644 vga/render.h create mode 100644 vga/render_80col.c create mode 100644 vga/render_dgr.c create mode 100644 vga/render_dhgr.c create mode 100644 vga/render_hires.c create mode 100644 vga/render_lores.c create mode 100644 vga/render_shr.c create mode 100644 vga/render_test.c create mode 100644 vga/render_text.c create mode 100644 vga/vga12.pio create mode 100644 vga/vga9.pio create mode 100644 vga/vgabuf.c create mode 100644 vga/vgabuf.h create mode 100644 vga/vgamain.c create mode 100644 vga/vgaout.c create mode 100644 vga/vgaout.h create mode 100644 z80/businterface.c create mode 100644 z80/businterface.h create mode 100644 z80/z80buf.h create mode 100644 z80/z80cpu.h create mode 100644 z80/z80main.c create mode 100644 z80/z80rom.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b5bf876 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,146 @@ +cmake_minimum_required(VERSION 3.16) + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") + set(PICO_BOARD pico) + set(BINARY_TAGS "${BINARY_TAGS}-gs") + message(STATUS "Building for V2 AnalogGS") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-lc") + set(PICO_BOARD pico) + set(BINARY_TAGS "${BINARY_TAGS}-lc") + message(STATUS "Building for V2 Analog LC") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-wifi") + set(PICO_BOARD pico_w) + set(BINARY_TAGS "${BINARY_TAGS}-wifi") + message(STATUS "Building for V2 Analog with WiFi") +else() + message(FATAL_ERROR "You must specify -lc or -wifi board type.") +endif() + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-4ns") + set(BINARY_TAGS "${BINARY_TAGS}-4ns") + message(STATUS "SYSCLOCK will be 252MHz") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-8ns") + set(BINARY_TAGS "${BINARY_TAGS}-8ns") + message(STATUS "SYSCLOCK will be 126MHz") +else() + message(FATAL_ERROR "You must specify -4ns (252MHz) or -8ns (126MHz) speed.") +endif() + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-vga") + set(BINARY_TAGS "${BINARY_TAGS}-vga") + message(STATUS "VGA Function Enabled") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-z80") + set(BINARY_TAGS "${BINARY_TAGS}-z80") + message(STATUS "Z80 Function Enabled") +else() + message(FATAL_ERROR "You must specify -vga or -z80 function.") +endif() + +# Pull in SDK (must be before project) +include(cmake/pico_sdk_import.cmake) + +project(v2-analog) +set(CMAKE_C_STANDARD 11) + +pico_sdk_init() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPICO_USE_MALLOC_MUTEX=1") + +add_executable(v2-analog${BINARY_TAGS}) + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DANALOG_GS=1") +endif() + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-4ns") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCONFIG_SYSCLOCK=252 -DPICO_FLASH_SPI_CLKDIV=8 -DOVERCLOCKED=1") +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/common/abus-gs-4ns.pio) +else() + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/common/abus-4ns.pio) +endif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-8ns") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCONFIG_SYSCLOCK=126") +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/common/abus-gs-8ns.pio) +else() + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/common/abus-8ns.pio) +endif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") +endif() + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-vga") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFUNCTION_VGA=1") +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/vga/vga12.pio) +else() + pico_generate_pio_header(v2-analog${BINARY_TAGS} + ${CMAKE_CURRENT_SOURCE_DIR}/vga/vga9.pio) +endif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-gs") +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-z80") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFUNCTION_Z80=1") +endif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-vga") + +target_sources(v2-analog${BINARY_TAGS} PUBLIC + common/main.c + common/abus.c + common/config.c + common/dmacopy.c + common/buffers.c + common/flash.c + common/usb_descriptors.c +) + +if(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-vga") +target_sources(v2-analog${BINARY_TAGS} PUBLIC + vga/vgamain.c + vga/businterface.c + vga/vgabuf.c + vga/render.c + vga/render_hires.c + vga/render_lores.c + vga/render_text.c + vga/render_80col.c + vga/render_dhgr.c + vga/render_dgr.c + vga/render_shr.c + vga/render_test.c + vga/vgaout.c +) +elseif(${CMAKE_CURRENT_BINARY_DIR} MATCHES "-z80") +target_sources(v2-analog${BINARY_TAGS} PUBLIC + z80/businterface.c + z80/z80main.c +) +endif() + +target_include_directories(v2-analog${BINARY_TAGS} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(v2-analog${BINARY_TAGS} PUBLIC + pico_multicore + pico_stdlib + pico_unique_id + tinyusb_device + tinyusb_board + hardware_resets + hardware_irq + hardware_dma + hardware_pio + hardware_flash +) + +if(${PICO_BOARD} MATCHES "pico_w") +target_link_libraries(v2-analog${BINARY_TAGS} PUBLIC + pico_cyw43_arch_lwip_poll +) +endif() + +pico_enable_stdio_usb(v2-analog${BINARY_TAGS} 0) +pico_enable_stdio_uart(v2-analog${BINARY_TAGS} 0) + +pico_set_linker_script(v2-analog${BINARY_TAGS} ${PROJECT_SOURCE_DIR}/delayed_copy.ld) + +pico_add_extra_outputs(v2-analog${BINARY_TAGS}) diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..eee356d --- /dev/null +++ b/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [ -z ${PICO_SDK:+x} ]; then + echo You must set PICO_SDK to the path where you downloaded https://github.com/raspberrypi/pico-sdk.git + exit 1 +fi + +build_firmware() { + if [ ! -d build/$1 ]; then + ( mkdir -p build/$1 && cd build/$1 && cmake ../.. ) + fi + make -C build/$1 +} + +build_firmware v2-analog-lc-4ns-z80 +build_firmware v2-analog-lc-8ns-z80 +build_firmware v2-analog-lc-8ns-vga +build_firmware v2-analog-wifi-4ns-z80 +build_firmware v2-analog-wifi-8ns-z80 +build_firmware v2-analog-wifi-8ns-vga diff --git a/cmake/pico_sdk_import.cmake b/cmake/pico_sdk_import.cmake new file mode 100644 index 0000000..05e884b --- /dev/null +++ b/cmake/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/common/abus-4ns.pio b/common/abus-4ns.pio new file mode 100644 index 0000000..811ebdf --- /dev/null +++ b/common/abus-4ns.pio @@ -0,0 +1,97 @@ +.define public PHI0_GPIO 26 +.define READ_DATA_TRIGGER_IRQ 4 +.define DATA_BUSY_IRQ 5 + +; Apple II bus interface +; Ref: Understanding the Apple II, pages 4-7, 7-8 + +.program abus +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is mapped to the R/W signal +; * IN pins are mapped to ~DEVSEL, R/W, and Data[7:0] +; * SET pins are mapped to the transceiver enable signals +; * input shift left & autopush @ 26 bits +; * run at about 250MHz (4ns/instruction) +; +; SET bits for tranceiver control: +; 0bxxx +; x - select AddrHi, active low +; x - select AddrLo, active low +; x - select Data, active low +.wrap_target +next_bus_cycle: + set PINS, 0b011 ; enable AddrHi tranceiver + wait 1 GPIO, PHI0_GPIO ; wait for PHI0 to rise. Data propagation through the transceiver should + ; be complete by the time this happens. + in PINS, 8 ; read AddrHi[7:0] + set PINS, 0b101 [12] ; enable AddrLo tranceiver and delay for transceiver propagation delay + in PINS, 8 ; read AddrLo[7:0] + + jmp PIN, read_cycle ; jump based on the state of the R/W pin + +write_cycle: + ; the current time is P0+82ns (P0 + 10ns + 2 clocks (input synchronizers) + 16 instructions) + + set PINS, 0b110 [31] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+210ns) + in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + jmp next_bus_cycle + +read_cycle: + ; the current time is P0+82ns (P0 + 10ns + 2 clocks (input synchronizers) + 16 instructions) + + set PINS, 0b110 [4] ; ensure AddrLo transceiver is disabled and delay for ~DEVSEL to become valid (P0+102ns+buffer delay) + in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush + + irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control +.wrap + + +.program abus_device_read +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is the ~DEVSEL signal +; * OUT pins are the 8 data signals +; * SET pins are the Data transceiver control signals +; +; SET bits for tranceiver control: +; 0bxx +; x - select Data transceiver (active low) +; x - Data transceiver direction (0=input, 1=output) +.wrap_target +wait_loop: + wait 1 irq READ_DATA_TRIGGER_IRQ ; wait for the data portion of a read cycle (from the main SM) + jmp PIN, wait_loop ; skip if this device is not being addressed + + ; the current time is P0+114ns (P0 + 10ns + 2 clocks (input synchronizers) + 24 instructions) and + ; this read cycle is addressed to this device. + ; + ; Phase 0 is typically 489 ns long. + ; * Data from peripherals should be valid on the data bus by 45 nanoseconds before the end of phase 0 + ; * Data should be held for 40ns after phase 0 ends + ; * Data bus should be tri-stated within 60ns after phase 0 ends + + irq set DATA_BUSY_IRQ + + set PINS, 0b01 [10] ; enable Data tranceiver with output direction [160ns] + mov OSR, ~NULL [31] ; [288ns] + out PINDIRS, 8 [31] ; set data pins as outputs [416ns] + + pull noblock ; pull value from the FIFO as late as possible [420ns] + out PINS, 8 ; [424ns] + + ; the current time is P0+424ns (P0 + 10ns + 2 clocks (input synchronizers) + 101 instructions) + + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall then hold for 40ns (2 clocks (input synchronizers) + 7 instructions) + set PINS, 0b10 ; disable Data tranceiver to tri-state the data bus + + mov OSR, NULL + out PINDIRS, 8 ; reset data pins as inputs + + pull noblock ; extra late pull to clear out any standing values from the FIFO [P1+56ns] + + irq clear DATA_BUSY_IRQ +.wrap diff --git a/common/abus-8ns.pio b/common/abus-8ns.pio new file mode 100644 index 0000000..8f5ce01 --- /dev/null +++ b/common/abus-8ns.pio @@ -0,0 +1,97 @@ +.define public PHI0_GPIO 26 +.define READ_DATA_TRIGGER_IRQ 4 +.define DATA_BUSY_IRQ 5 + +; Apple II bus interface +; Ref: Understanding the Apple II, pages 4-7, 7-8 + +.program abus +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is mapped to the R/W signal +; * IN pins are mapped to ~DEVSEL, R/W, and Data[7:0] +; * SET pins are mapped to the transceiver enable signals +; * input shift left & autopush @ 26 bits +; * run at about 125MHz (8ns/instruction) +; +; SET bits for tranceiver control: +; 0bxxx +; x - select AddrHi, active low +; x - select AddrLo, active low +; x - select Data, active low +.wrap_target +next_bus_cycle: + set PINS, 0b011 ; enable AddrHi tranceiver + wait 1 GPIO, PHI0_GPIO ; wait for PHI0 to rise. Data propagation through the transceiver should + ; be complete by the time this happens. + in PINS, 8 ; read AddrHi[7:0] + set PINS, 0b101 [2] ; enable AddrLo tranceiver and delay for transceiver propagation delay + in PINS, 8 ; read AddrLo[7:0] + + jmp PIN, read_cycle ; jump based on the state of the R/W pin + +write_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 [15] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+200ns) + in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + jmp next_bus_cycle + +read_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 ; ensure AddrLo transceiver is disabled and delay for ~DEVSEL to become valid (P0+63ns+buffer delay) + in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush + + irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control +.wrap + + +.program abus_device_read +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is the ~DEVSEL signal +; * OUT pins are the 8 data signals +; * SET pins are the Data transceiver control signals +; +; SET bits for tranceiver control: +; 0bxx +; x - select Data transceiver (active low) +; x - Data transceiver direction (0=input, 1=output) +.wrap_target +wait_loop: + wait 1 irq READ_DATA_TRIGGER_IRQ ; wait for the data portion of a read cycle (from the main SM) + jmp PIN, wait_loop ; skip if this device is not being addressed + + ; the current time is P0+136ns (P0 + 16ns + 2 clocks (input synchronizers) + 13 instructions) and + ; this read cycle is addressed to this device. + ; + ; Phase 0 is typically 489 ns long. + ; * Data from peripherals should be valid on the data bus by 45 nanoseconds before the end of phase 0 + ; * Data should be held for 40ns after phase 0 ends + ; * Data bus should be tri-stated within 60ns after phase 0 ends + + irq set DATA_BUSY_IRQ + + set PINS, 0b01 ; enable Data tranceiver with output direction + mov OSR, ~NULL [4] + out PINDIRS, 8 [31] ; set data pins as outputs + + pull noblock ; pull value from the FIFO as late as possible + out PINS, 8 + + ; the current time is P0+440ns (P0 + 16ns + 2 clocks (input synchronizers) + 51 instructions) + + wait 0 GPIO, PHI0_GPIO [2] ; wait for PHI0 to fall then hold for 40ns (2 clocks (input synchronizers) + 2-3 instructions) + set PINS, 0b10 ; disable Data tranceiver to tri-state the data bus + + mov OSR, NULL + out PINDIRS, 8 ; reset data pins as inputs + + pull noblock ; extra late pull to clear out any standing values from the FIFO + + irq clear DATA_BUSY_IRQ +.wrap diff --git a/common/abus-gs-4ns.pio b/common/abus-gs-4ns.pio new file mode 100644 index 0000000..1fdd1ae --- /dev/null +++ b/common/abus-gs-4ns.pio @@ -0,0 +1,97 @@ +.define public PHI0_GPIO 14 +.define READ_DATA_TRIGGER_IRQ 4 +.define DATA_BUSY_IRQ 5 + +; Apple II bus interface +; Ref: Understanding the Apple II, pages 4-7, 7-8 + +.program abus +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is mapped to the R/W signal +; * IN pins are mapped to ~DEVSEL, R/W, and Data[7:0] +; * SET pins are mapped to the transceiver enable signals +; * input shift left & autopush @ 26 bits +; * run at about 250MHz (4ns/instruction) +; +; SET bits for tranceiver control: +; 0bxxx +; x - select AddrHi, active low +; x - select AddrLo, active low +; x - select Data, active low +.wrap_target +next_bus_cycle: + set PINS, 0b011 ; enable AddrHi tranceiver + wait 1 GPIO, PHI0_GPIO ; wait for PHI0 to rise. Data propagation through the transceiver should + ; be complete by the time this happens. + in PINS, 8 ; read AddrHi[7:0] + set PINS, 0b101 [12] ; enable AddrLo tranceiver and delay for transceiver propagation delay + in PINS, 8 ; read AddrLo[7:0] + + jmp PIN, read_cycle ; jump based on the state of the R/W pin + +write_cycle: + ; the current time is P0+82ns (P0 + 10ns + 2 clocks (input synchronizers) + 16 instructions) + + set PINS, 0b110 [31] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+210ns) + in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + jmp next_bus_cycle + +read_cycle: + ; the current time is P0+82ns (P0 + 10ns + 2 clocks (input synchronizers) + 16 instructions) + + set PINS, 0b110 [4] ; ensure AddrLo transceiver is disabled and delay for ~DEVSEL to become valid (P0+102ns+buffer delay) + in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush + + irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control +.wrap + + +.program abus_device_read +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is the ~DEVSEL signal +; * OUT pins are the 8 data signals +; * SET pins are the Data transceiver control signals +; +; SET bits for tranceiver control: +; 0bxx +; x - select Data transceiver (active low) +; x - Data transceiver direction (0=input, 1=output) +.wrap_target +wait_loop: + wait 1 irq READ_DATA_TRIGGER_IRQ ; wait for the data portion of a read cycle (from the main SM) + jmp PIN, wait_loop ; skip if this device is not being addressed + + ; the current time is P0+114ns (P0 + 10ns + 2 clocks (input synchronizers) + 24 instructions) and + ; this read cycle is addressed to this device. + ; + ; Phase 0 is typically 489 ns long. + ; * Data from peripherals should be valid on the data bus by 45 nanoseconds before the end of phase 0 + ; * Data should be held for 40ns after phase 0 ends + ; * Data bus should be tri-stated within 60ns after phase 0 ends + + irq set DATA_BUSY_IRQ + + set PINS, 0b01 [10] ; enable Data tranceiver with output direction [160ns] + mov OSR, ~NULL [31] ; [288ns] + out PINDIRS, 8 [31] ; set data pins as outputs [416ns] + + pull noblock ; pull value from the FIFO as late as possible [420ns] + out PINS, 8 ; [424ns] + + ; the current time is P0+424ns (P0 + 10ns + 2 clocks (input synchronizers) + 101 instructions) + + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall then hold for 40ns (2 clocks (input synchronizers) + 7 instructions) + set PINS, 0b10 ; disable Data tranceiver to tri-state the data bus + + mov OSR, NULL + out PINDIRS, 8 ; reset data pins as inputs + + pull noblock ; extra late pull to clear out any standing values from the FIFO [P1+56ns] + + irq clear DATA_BUSY_IRQ +.wrap diff --git a/common/abus-gs-8ns.pio b/common/abus-gs-8ns.pio new file mode 100644 index 0000000..2bf9e30 --- /dev/null +++ b/common/abus-gs-8ns.pio @@ -0,0 +1,97 @@ +.define public PHI0_GPIO 14 +.define READ_DATA_TRIGGER_IRQ 4 +.define DATA_BUSY_IRQ 5 + +; Apple II bus interface +; Ref: Understanding the Apple II, pages 4-7, 7-8 + +.program abus +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is mapped to the R/W signal +; * IN pins are mapped to ~DEVSEL, R/W, and Data[7:0] +; * SET pins are mapped to the transceiver enable signals +; * input shift left & autopush @ 26 bits +; * run at about 125MHz (8ns/instruction) +; +; SET bits for tranceiver control: +; 0bxxx +; x - select AddrHi, active low +; x - select AddrLo, active low +; x - select Data, active low +.wrap_target +next_bus_cycle: + set PINS, 0b011 ; enable AddrHi tranceiver + wait 1 GPIO, PHI0_GPIO ; wait for PHI0 to rise. Data propagation through the transceiver should + ; be complete by the time this happens. + in PINS, 8 ; read AddrHi[7:0] + set PINS, 0b101 [2] ; enable AddrLo tranceiver and delay for transceiver propagation delay + in PINS, 8 ; read AddrLo[7:0] + + jmp PIN, read_cycle ; jump based on the state of the R/W pin + +write_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 [15] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+200ns) + in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + jmp next_bus_cycle + +read_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 ; ensure AddrLo transceiver is disabled and delay for ~DEVSEL to become valid (P0+63ns+buffer delay) + in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush + + irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control +.wrap + + +.program abus_device_read +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is the ~DEVSEL signal +; * OUT pins are the 8 data signals +; * SET pins are the Data transceiver control signals +; +; SET bits for tranceiver control: +; 0bxx +; x - select Data transceiver (active low) +; x - Data transceiver direction (0=input, 1=output) +.wrap_target +wait_loop: + wait 1 irq READ_DATA_TRIGGER_IRQ ; wait for the data portion of a read cycle (from the main SM) + jmp PIN, wait_loop ; skip if this device is not being addressed + + ; the current time is P0+136ns (P0 + 16ns + 2 clocks (input synchronizers) + 13 instructions) and + ; this read cycle is addressed to this device. + ; + ; Phase 0 is typically 489 ns long. + ; * Data from peripherals should be valid on the data bus by 45 nanoseconds before the end of phase 0 + ; * Data should be held for 40ns after phase 0 ends + ; * Data bus should be tri-stated within 60ns after phase 0 ends + + irq set DATA_BUSY_IRQ + + set PINS, 0b01 ; enable Data tranceiver with output direction + mov OSR, ~NULL [4] + out PINDIRS, 8 [31] ; set data pins as outputs + + pull noblock ; pull value from the FIFO as late as possible + out PINS, 8 + + ; the current time is P0+440ns (P0 + 16ns + 2 clocks (input synchronizers) + 51 instructions) + + wait 0 GPIO, PHI0_GPIO [2] ; wait for PHI0 to fall then hold for 40ns (2 clocks (input synchronizers) + 2-3 instructions) + set PINS, 0b10 ; disable Data tranceiver to tri-state the data bus + + mov OSR, NULL + out PINDIRS, 8 ; reset data pins as inputs + + pull noblock ; extra late pull to clear out any standing values from the FIFO + + irq clear DATA_BUSY_IRQ +.wrap diff --git a/common/abus.c b/common/abus.c new file mode 100644 index 0000000..a33145d --- /dev/null +++ b/common/abus.c @@ -0,0 +1,95 @@ +#include +#include +#include "common/config.h" +#include "common/abus.h" + +#ifdef OVERCLOCKED + #ifdef ANALOG_GS + #include "abus-gs-4ns.pio.h" + #else + #include "abus-4ns.pio.h" + #endif +#else + #ifdef ANALOG_GS + #include "abus-gs-8ns.pio.h" + #else + #include "abus-8ns.pio.h" + #endif +#endif + +#if CONFIG_PIN_APPLEBUS_PHI0 != PHI0_GPIO +#error CONFIG_PIN_APPLEBUS_PHI0 and PHI0_GPIO must be set to the same pin +#endif + + +static void abus_device_read_setup(PIO pio, uint sm) { + uint program_offset = pio_add_program(pio, &abus_device_read_program); + pio_sm_claim(pio, sm); + + pio_sm_config c = abus_device_read_program_get_default_config(program_offset); + + // set the "device selected" pin as the jump pin + sm_config_set_jmp_pin(&c, CONFIG_PIN_APPLEBUS_DEVSEL); + + // map the OUT pin group to the data signals + sm_config_set_out_pins(&c, CONFIG_PIN_APPLEBUS_DATA_BASE, 8); + + // map the SET pin group to the Data transceiver control signals + sm_config_set_set_pins(&c, CONFIG_PIN_APPLEBUS_CONTROL_BASE, 2); + + pio_sm_init(pio, sm, program_offset, &c); + + // All the GPIOs are shared and setup by the main program +} + +static void abus_main_setup(PIO pio, uint sm) { + uint program_offset = pio_add_program(pio, &abus_program); + pio_sm_claim(pio, sm); + + pio_sm_config c = abus_program_get_default_config(program_offset); + + // set the bus R/W pin as the jump pin + sm_config_set_jmp_pin(&c, CONFIG_PIN_APPLEBUS_RW); + + // map the IN pin group to the data signals + sm_config_set_in_pins(&c, CONFIG_PIN_APPLEBUS_DATA_BASE); + + // map the SET pin group to the bus transceiver enable signals + sm_config_set_set_pins(&c, CONFIG_PIN_APPLEBUS_CONTROL_BASE+1, 3); + + // configure left shift into ISR & autopush every 26 bits + sm_config_set_in_shift(&c, false, true, 26); + + pio_sm_init(pio, sm, program_offset, &c); + + // configure the GPIOs + // Ensure all transceivers will start disabled, with Data transceiver direction set to 'in' + pio_sm_set_pins_with_mask(pio, sm, + (uint32_t)0xe << CONFIG_PIN_APPLEBUS_CONTROL_BASE, + (uint32_t)0xf << CONFIG_PIN_APPLEBUS_CONTROL_BASE); + pio_sm_set_pindirs_with_mask(pio, sm, + (0xf << CONFIG_PIN_APPLEBUS_CONTROL_BASE), + (1 << CONFIG_PIN_APPLEBUS_PHI0) | (0xf << CONFIG_PIN_APPLEBUS_CONTROL_BASE) | (0x3ff << CONFIG_PIN_APPLEBUS_DATA_BASE)); + + // Disable input synchronization on input pins that are sampled at known stable times + // to shave off two clock cycles of input latency + pio->input_sync_bypass |= (0x3ff << CONFIG_PIN_APPLEBUS_DATA_BASE); + + pio_gpio_init(pio, CONFIG_PIN_APPLEBUS_PHI0); + gpio_set_pulls(CONFIG_PIN_APPLEBUS_PHI0, false, false); + for(int pin=CONFIG_PIN_APPLEBUS_CONTROL_BASE; pin < CONFIG_PIN_APPLEBUS_CONTROL_BASE+4; pin++) { + pio_gpio_init(pio, pin); + } + for(int pin=CONFIG_PIN_APPLEBUS_DATA_BASE; pin < CONFIG_PIN_APPLEBUS_DATA_BASE+10; pin++) { + pio_gpio_init(pio, pin); + gpio_set_pulls(pin, false, false); + } +} + + +void abus_init() { + abus_device_read_setup(CONFIG_ABUS_PIO, ABUS_DEVICE_READ_SM); + abus_main_setup(CONFIG_ABUS_PIO, ABUS_MAIN_SM); + + pio_enable_sm_mask_in_sync(CONFIG_ABUS_PIO, (1 << ABUS_MAIN_SM) | (1 << ABUS_DEVICE_READ_SM)); +} diff --git a/common/abus.h b/common/abus.h new file mode 100644 index 0000000..05a8830 --- /dev/null +++ b/common/abus.h @@ -0,0 +1,13 @@ +#pragma once + +void abus_init(); + +#define CARD_SELECT ((value & (1u << CONFIG_PIN_APPLEBUS_DEVSEL-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) +#define CARD_DEVSEL ((address & 0xcf80) == 0xc080) +#define CARD_IOSEL (((address & 0xcf00) >= 0xc100) && ((address & 0xcf00) < 0xc700)) +#define CARD_IOSTROBE ((address & 0xc800) == 0xc800) + +enum { + ABUS_MAIN_SM = 0, + ABUS_DEVICE_READ_SM = 1, +}; diff --git a/common/abus.pio b/common/abus.pio new file mode 100644 index 0000000..dec3861 --- /dev/null +++ b/common/abus.pio @@ -0,0 +1,97 @@ +.define public PHI0_GPIO 26 +.define READ_DATA_TRIGGER_IRQ 4 +.define DATA_BUSY_IRQ 5 + +; Apple II bus interface +; Ref: Understanding the Apple II, pages 4-7, 7-8 + +.program abus +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is mapped to the R/W signal +; * IN pins are mapped to ~DEVSEL, R/W, and Data[7:0] +; * SET pins are mapped to the transceiver enable signals +; * input shift left & autopush @ 26 bits +; * run at about 125MHz (8ns/instruction) +; +; SET bits for tranceiver control: +; 0bxxx +; x - select AddrHi, active low +; x - select AddrLo, active low +; x - select Data, active low +.wrap_target +next_bus_cycle: + set PINS, 0b011 ; enable AddrHi tranceiver + wait 1 GPIO, PHI0_GPIO ; wait for PHI0 to rise. Data propagation through the transceiver should + ; be complete by the time this happens. + in PINS, 8 ; read AddrHi[7:0] + set PINS, 0b101 [2] ; enable AddrLo tranceiver and delay for transceiver propagation delay + in PINS, 8 ; read AddrLo[7:0] + + jmp PIN, read_cycle ; jump based on the state of the R/W pin + +write_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 [15] ; enable Data tranceiver & wait until both ~DEVSEL and the written data are valid (P0+200ns) + in PINS, 10 ; read R/W, ~DEVSEL, and Data[7:0], then autopush + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + jmp next_bus_cycle + +read_cycle: + ; the current time is P0+88ns (P0 + 16ns + 2 clocks (input synchronizers) + 7 instructions) + + set PINS, 0b110 ; ensure AddrLo transceiver is disabled and delay for ~DEVSEL to become valid (P0+63ns+buffer delay) + in PINS, 10 ; read R/W, ~DEVSEL, and dontcare[7:0], then autopush + + irq set READ_DATA_TRIGGER_IRQ ; trigger the data read state machine to put data on the data bus + wait 0 GPIO, PHI0_GPIO [7] ; wait for PHI0 to fall + wait 0 irq DATA_BUSY_IRQ ; wait for the data handling state machine to complete to avoid contention w/transceiver control +.wrap + + +.program abus_device_read +; Prerequisites: +; * Bus clock used is PHI0, wired to GPIO 26 +; * JMP pin is the ~DEVSEL signal +; * OUT pins are the 8 data signals +; * SET pins are the Data transceiver control signals +; +; SET bits for tranceiver control: +; 0bxx +; x - select Data transceiver (active low) +; x - Data transceiver direction (0=input, 1=output) +.wrap_target +wait_loop: + wait 1 irq READ_DATA_TRIGGER_IRQ ; wait for the data portion of a read cycle (from the main SM) + jmp PIN, wait_loop ; skip if this device is not being addressed + + ; the current time is P0+136ns (P0 + 16ns + 2 clocks (input synchronizers) + 13 instructions) and + ; this read cycle is addressed to this device. + ; + ; Phase 0 is typically 489 ns long. + ; * Data from peripherals should be valid on the data bus by 45 nanoseconds before the end of phase 0 + ; * Data should be held for 40ns after phase 0 ends + ; * Data bus should be tri-stated within 60ns after phase 0 ends + + irq set DATA_BUSY_IRQ + + pull noblock ; extra early pull to clear out any standing values from the FIFO + + set PINS, 0b01 ; enable Data tranceiver with output direction + mov OSR, ~NULL [3] + out PINDIRS, 8 [31] ; set data pins as outputs + + pull noblock ; pull value from the FIFO as late as possible + out PINS, 8 + + ; the current time is P0+440ns (P0 + 16ns + 2 clocks (input synchronizers) + 51 instructions) + + wait 0 GPIO, PHI0_GPIO [2] ; wait for PHI0 to fall then hold for 40ns (2 clocks (input synchronizers) + 2-3 instructions) + set PINS, 0b10 ; disable Data tranceiver to tri-state the data bus + + mov OSR, NULL + out PINDIRS, 8 ; reset data pins as inputs + + irq clear DATA_BUSY_IRQ +.wrap diff --git a/common/buffers.c b/common/buffers.c new file mode 100644 index 0000000..1ffa91e --- /dev/null +++ b/common/buffers.c @@ -0,0 +1,56 @@ +#include "buffers.h" + +volatile uint32_t soft_switches = 0; +volatile uint32_t internal_flags = 0; + +volatile uint8_t reset_state = 0; + +volatile uint8_t cardslot = 0; +volatile uint32_t busactive = 0; + +volatile uint8_t __attribute__((section (".appledata."))) apple_memory[64*1024]; +volatile uint8_t __attribute__((section (".appledata."))) private_memory[64*1024]; + +#ifdef FUNCTION_VGA +volatile uint8_t *text_p1 = apple_memory + 0x0400; +volatile uint8_t *text_p2 = apple_memory + 0x0800; +volatile uint8_t *text_p3 = private_memory + 0x0400; +volatile uint8_t *text_p4 = private_memory + 0x0800; +volatile uint8_t *hgr_p1 = apple_memory + 0x2000; +volatile uint8_t *hgr_p2 = apple_memory + 0x4000; +volatile uint8_t *hgr_p3 = private_memory + 0x2000; +volatile uint8_t *hgr_p4 = private_memory + 0x4000; +#endif + +volatile uint8_t *baseio = apple_memory + 0xc000; +volatile uint8_t *slotio = apple_memory + 0xc080; +volatile uint8_t *slotrom = apple_memory + 0xc100; +volatile uint8_t *extdrom = apple_memory + 0xc800; + +/* Slot 1: Grapper */ +volatile uint8_t *slot1io = apple_memory + 0xc090; +volatile uint8_t *slot1rom = apple_memory + 0xc100; + +/* Slot 2: Super Serial Card */ +volatile uint8_t *slot2io = apple_memory + 0xc0a0; +volatile uint8_t *slot2rom = apple_memory + 0xc200; + +/* Slot 3: 80 Column Card */ +volatile uint8_t *slot3io = apple_memory + 0xc0b0; +volatile uint8_t *slot3rom = apple_memory + 0xc300; + +/* Slot 4: PCPI Applicard */ +volatile uint8_t *slot4io = apple_memory + 0xc0c0; +volatile uint8_t *slot4rom = apple_memory + 0xc400; + +/* Slot 5: Uthernet */ +volatile uint8_t *slot5io = apple_memory + 0xc0d0; +volatile uint8_t *slot5rom = apple_memory + 0xc500; + +/* Slot 6: Disk II */ +volatile uint8_t *slot6io = apple_memory + 0xc0e0; +volatile uint8_t *slot6rom = apple_memory + 0xc600; + +/* Slot 7: ProDOS ROM Disk */ +volatile uint8_t *slot7io = apple_memory + 0xc0f0; +volatile uint8_t *slot7rom = apple_memory + 0xc700; diff --git a/common/buffers.h b/common/buffers.h new file mode 100644 index 0000000..8ffdfe5 --- /dev/null +++ b/common/buffers.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +extern volatile uint8_t reset_state; + +extern volatile uint8_t cardslot; +extern volatile uint32_t busactive; + +extern volatile uint8_t apple_memory[64*1024]; +extern volatile uint8_t private_memory[64*1024]; + +extern volatile uint16_t cfptr; +extern volatile uint8_t cfbuf[4096]; + +#define config_cmdbuf ((uint8_t*)(apple_memory+0xC0F0+(cardslot<<8))) +#define config_rpybuf ((uint8_t*)(apple_memory+0xC0F8+(cardslot<<8))) + +#ifdef FUNCTION_VGA +extern volatile uint8_t *text_p1; +extern volatile uint8_t *text_p2; +extern volatile uint8_t *text_p3; +extern volatile uint8_t *text_p4; +extern volatile uint8_t *hgr_p1; +extern volatile uint8_t *hgr_p2; +extern volatile uint8_t *hgr_p3; +extern volatile uint8_t *hgr_p4; + +/* Videx VideoTerm */ +extern volatile uint8_t *videx_page; + +#endif + +extern volatile uint8_t *baseio; +extern volatile uint8_t *slotio; +extern volatile uint8_t *slotrom; +extern volatile uint8_t *extdrom; + +/* Slot 1: Grapper */ +extern volatile uint8_t *slot1io; +extern volatile uint8_t *slot1rom; + +/* Slot 2: Super Serial Card */ +extern volatile uint8_t *slot2io; +extern volatile uint8_t *slot2rom; + +/* Slot 3: 80 Column Card */ +extern volatile uint8_t *slot3io; +extern volatile uint8_t *slot3rom; + +/* Slot 4: PCPI Applicard */ +extern volatile uint8_t *slot4io; +extern volatile uint8_t *slot4rom; + +/* Slot 5: Uthernet */ +extern volatile uint8_t *slot5io; +extern volatile uint8_t *slot5rom; + +/* Slot 6: Disk II */ +extern volatile uint8_t *slot6io; +extern volatile uint8_t *slot6rom; + +/* Slot 7: ProDOS ROM Disk */ +extern volatile uint8_t *slot7io; +extern volatile uint8_t *slot7rom; + +extern volatile uint32_t soft_switches; + +extern volatile uint32_t internal_flags; + +#define SOFTSW_TEXT_MODE 0x00000001 +#define SOFTSW_MIX_MODE 0x00000002 +#define SOFTSW_HIRES_MODE 0x00000004 +#define SOFTSW_MODE_MASK 0x00000007 +#define SOFTSW_PAGE_2 0x00000008 + +// Apple IIe/c/gs softswitches +#define SOFTSW_80STORE 0x00000100 +#define SOFTSW_AUX_READ 0x00000200 +#define SOFTSW_AUX_WRITE 0x00000400 +#define SOFTSW_AUXZP 0x00000800 +#define SOFTSW_SLOT3ROM 0x00001000 +#define SOFTSW_80COL 0x00002000 +#define SOFTSW_ALTCHAR 0x00004000 +#define SOFTSW_DGR 0x00008000 + +#define SOFTSW_NEWVID_MASK 0xE0 +#define SOFTSW_NEWVID_SHIFT 11 + +#define SOFTSW_MONOCHROME 0x00010000 +#define SOFTSW_LINEARIZE 0x00020000 +#define SOFTSW_SHR 0x00040000 + +#define SOFTSW_IOUDIS 0x00080000 + +#define SOFTSW_SHADOW_MASK 0x7F +#define SOFTSW_SHADOW_SHIFT 20 + +#define SOFTSW_SHADOW_TEXT 0x00100000 +#define SOFTSW_SHADOW_HGR1 0x00200000 +#define SOFTSW_SHADOW_HGR2 0x00400000 +#define SOFTSW_SHADOW_SHR 0x00800000 +#define SOFTSW_SHADOW_AUXHGR 0x01000000 +#define SOFTSW_SHADOW_ALTDISP 0x02000000 +#define SOFTSW_SHADOW_IO 0x04000000 + +// V2 Analog specific softswitches +#define SOFTSW_TERMINAL 0x10000000 +#define IFLAGS_TEST 0x20000000 +#define IFLAGS_IIE_REGS 0x40000000 +#define IFLAGS_IIGS_REGS 0x80000000 diff --git a/common/build.h b/common/build.h new file mode 100644 index 0000000..b9ca593 --- /dev/null +++ b/common/build.h @@ -0,0 +1,4 @@ +#define BUILDDATE 0x20230403 +#define BUILDID 0x0171 +#define BUILDSTR " 3 Apr 2023 Build 0171" + diff --git a/common/cfgtoken.h b/common/cfgtoken.h new file mode 100644 index 0000000..e8fcffe --- /dev/null +++ b/common/cfgtoken.h @@ -0,0 +1,54 @@ + +#define NEWCONFIG_MAGIC 0x0001434E // "NC\x01\x00" +#define NEWCONFIG_EOF_MARKER 0x00464F45 // "EOF\x00" +#define CFGTOKEN_REVISION 0x0001434E // "RV\xXX\x00" + +#define CFGTOKEN_MODE_VGA 0x0000564D // "MV\x00\x00" VGA +#define CFGTOKEN_MODE_PCPI 0x00005A4D // "MZ\x00\x00" PCPI Applicard +#define CFGTOKEN_MODE_SER 0x0000534D // "MS\x00\x00" Serial +#define CFGTOKEN_MODE_PAR 0x0000504D // "MP\x00\x00" Parallel +#define CFGTOKEN_MODE_SNES 0x0000474D // "MG\x00\x00" SNESMAX +#define CFGTOKEN_MODE_NET 0x0000454D // "ME\x00\x00" Ethernet +#define CFGTOKEN_MODE_FILE 0x0000464D // "MF\x00\x00" Filesystem + +#define CFGTOKEN_HOST_AUTO 0x00004148 // "HA\x00\x00" Autodetect +#define CFGTOKEN_HOST_II 0x00003248 // "H2\x00\x00" II/II+ +#define CFGTOKEN_HOST_IIE 0x00004548 // "HE\x00\x00" IIe +#define CFGTOKEN_HOST_IIGS 0x00004748 // "HG\x00\x00" IIgs +#define CFGTOKEN_HOST_PRAVETZ 0x00005048 // "HP\x00\x00" Pravetz +#define CFGTOKEN_HOST_BASIS 0x00004248 // "HB\x00\x00" Basis 108 + +#define CFGTOKEN_MUX_LOOP 0x00004C53 // "SL\x00\x00" Serial Loopback +#define CFGTOKEN_MUX_USB 0x00005553 // "SU\x00\x00" USB CDC +#define CFGTOKEN_MUX_WIFI 0x00005753 // "SW\x00\x00" WiFi Modem +#define CFGTOKEN_MUX_PRN 0x00005053 // "SP\x00\x00" WiFi Printer +#define CFGTOKEN_SER_BAUD 0x02004253 // "SB\x00\x01" Serial Baudrate Divisor + +#define CFGTOKEN_USB_HOST 0x00004855 // "UH\x00\x00" USB CDC Host +#define CFGTOKEN_USB_GUEST 0x00004755 // "UG\x00\x00" USB CDC Guest Device +#define CFGTOKEN_USB_MIDI 0x00004D55 // "UM\x00\x00" USB MIDI Guest Device + +#define CFGTOKEN_WIFI_AP 0x00004157 // "WA\x00\x00" WiFi AP +#define CFGTOKEN_WIFI_CL 0x00004357 // "WC\x00\x00" WiFi Client + +#define CFGTOKEN_WIFI_SSID 0x00005357 // "WS\x00\xSS" WiFi SSID +#define CFGTOKEN_WIFI_PSK 0x00005057 // "WP\x00\xSS" WiFi PSK + +#define CFGTOKEN_WIFI_IP 0x04004957 // "WI\x00\xSS" WiFi IP +#define CFGTOKEN_WIFI_NM 0x04004E57 // "WN\x00\xSS" WiFi Netmask + +#define CFGTOKEN_JD_HOST 0x0000484A // "JH\x00\x01" JetDirect Hostname +#define CFGTOKEN_JD_PORT 0x0200444A // "JD\x00\x01" JetDirect Port + +#define CFGTOKEN_MONO_00 0x00005056 // "VP\x00\x00" Full Color Video +#define CFGTOKEN_MONO_80 0x00805056 // "VP\x80\x00" B&W Video +#define CFGTOKEN_MONO_90 0x00905056 // "VP\x90\x00" B&W Inverse +#define CFGTOKEN_MONO_A0 0x00A05056 // "VP\xA0\x00" Amber +#define CFGTOKEN_MONO_B0 0x00B05056 // "VP\xB0\x00" Amber Inverse +#define CFGTOKEN_MONO_C0 0x00C05056 // "VP\xC0\x00" Green +#define CFGTOKEN_MONO_D0 0x00D05056 // "VP\xD0\x00" Green Inverse +#define CFGTOKEN_MONO_E0 0x00E05056 // "VP\xE0\x00" C64 +#define CFGTOKEN_MONO_F0 0x00F05056 // "VP\xF0\x00" Custom + +#define CFGTOKEN_TBCOLOR 0x00005456 // "VT\xXX\x00" Custom default TBCOLOR +#define CFGTOKEN_BORDER 0x00004256 // "VB\xXX\x00" Custom default BORDER diff --git a/common/config.c b/common/config.c new file mode 100644 index 0000000..1ef7dc9 --- /dev/null +++ b/common/config.c @@ -0,0 +1,627 @@ +#include "common/config.h" +#include "common/buffers.h" +#include "common/flash.h" +#include "common/build.h" +#include "common/dmacopy.h" +#include "vga/render.h" +#include "vga/vgaout.h" +#include + +volatile compat_t cfg_machine = MACHINE_AUTO; +volatile compat_t current_machine = MACHINE_AUTO; + +#ifdef FUNCTION_Z80 +volatile usbmux_t usbmux; +volatile serialmux_t serialmux; +volatile wifimode_t wifimode; + +volatile uint8_t wifi_ssid[32]; +volatile uint8_t wifi_psk[32]; +#endif + +#ifdef FUNCTION_VGA +extern volatile bool userfont; +extern uint8_t character_rom[4096]; +extern uint8_t terminal_character_rom[4096]; +#endif + +extern volatile bool businterface_hold; +extern volatile bool businterface_lockout; + +volatile uint16_t cfptr = 0; +volatile uint8_t cfbuf[4096]; + +uint32_t config_temp[1024]; + +#define REPLY_OK 0x00 +#define REPLY_BUSY 0xBB +#define REPLY_EOF 0x01 +#define REPLY_NOFILE 0x02 +#define REPLY_EPARAM 0x03 +#define REPLY_ECMD 0x04 + +bool DELAYED_COPY_CODE(parse_config)(uint32_t address) { + uint32_t *config = (uint32_t*)address; + int i = 0; + + if(config[0] != NEWCONFIG_MAGIC) { + return false; + } + + for(i = 0; i < (CONFIG_SIZE/sizeof(uint32_t)); i++) { + if(config[i] == NEWCONFIG_EOF_MARKER) return true; + + switch(config[i] & 0x0000FFFF) { + case CFGTOKEN_HOST_AUTO: + cfg_machine = MACHINE_AUTO; + break; + case CFGTOKEN_HOST_II: + cfg_machine = MACHINE_II; + break; + case CFGTOKEN_HOST_IIE: + cfg_machine = MACHINE_IIE; + break; + case CFGTOKEN_HOST_IIGS: + cfg_machine = MACHINE_IIGS; + break; + case CFGTOKEN_HOST_PRAVETZ: + cfg_machine = MACHINE_PRAVETZ; + break; +#ifdef FUNCTION_VGA + case CFGTOKEN_MONO_00: + mono_palette = (config[i] >> 16) & 0xF; + break; + case CFGTOKEN_TBCOLOR: + terminal_tbcolor = (config[i] >> 16) & 0xFF; + break; + case CFGTOKEN_BORDER: + terminal_border = (config[i] >> 16) & 0xF; + break; +#else + case CFGTOKEN_MUX_LOOP: + serialmux = SERIAL_LOOP; + break; + case CFGTOKEN_MUX_USB: + serialmux = SERIAL_USB; + break; + case CFGTOKEN_MUX_WIFI: + serialmux = SERIAL_WIFI; + break; + case CFGTOKEN_MUX_PRN: + serialmux = SERIAL_PRINTER; + break; + case CFGTOKEN_SER_BAUD: + //baud = config[i+1]; + break; + case CFGTOKEN_USB_HOST: + usbmux = USB_HOST_CDC; + break; + case CFGTOKEN_USB_GUEST: + usbmux = USB_GUEST_CDC; + break; +// case CFGTOKEN_USB_MIDI: +// usbmux = USB_GUEST_MIDI; +// break; + case CFGTOKEN_WIFI_AP: + wifimode = WIFI_AP; + break; + case CFGTOKEN_WIFI_CL: + wifimode = WIFI_CLIENT; + break; + case CFGTOKEN_WIFI_SSID: +// memset((char*)wifi_ssid, 0, sizeof(wifi_ssid)); +// strncpy((char*)wifi_ssid, (char*)(config+i+1), (config[i] >> 24)); + break; + case CFGTOKEN_WIFI_PSK: +// memset((char*)wifi_psk, 0, sizeof(wifi_psk)); +// strncpy((char*)wifi_psk, (char*)(config+i+1), (config[i] >> 24)); + break; + case CFGTOKEN_WIFI_IP: +// wifi_address = config[i+1]; + break; + case CFGTOKEN_WIFI_NM: +// wifi_netmask = config[i+1]; + break; + case CFGTOKEN_JD_HOST: +// memset((uint8_t*)jd_host, 0, sizeof(jd_host)); +// strncpy(jd_host, (char*)(config+i+1), (config[i] >> 24)); + break; + case CFGTOKEN_JD_PORT: +// jd_port = config[i+1]; + break; +#endif + } + + // Advance by the number of dwords for this token + i += (((config[i] >> 24) + 3) >> 2); + } + + return false; +} + +void DELAYED_COPY_CODE(default_config)() { +#ifdef FUNCTION_Z80 + serialmux = SERIAL_LOOP; + usbmux = USB_GUEST_CDC; + wifimode = WIFI_AP; + strcpy((char*)wifi_ssid, "V2RetroNet"); + strcpy((char*)wifi_psk, "Analog"); +#endif + cfg_machine = MACHINE_AUTO; +} + +int DELAYED_COPY_CODE(make_config)(uint8_t rev) { + int i = 0; + + memset(config_temp, 0, sizeof(config_temp)); + + config_temp[i++] = NEWCONFIG_MAGIC; + config_temp[i++] = CFGTOKEN_REVISION | (((uint16_t)rev) << 16); + + switch(cfg_machine) { + default: + case MACHINE_AUTO: + config_temp[i++] = CFGTOKEN_HOST_AUTO; + break; + case MACHINE_II: + config_temp[i++] = CFGTOKEN_HOST_II; + break; + case MACHINE_IIE: + config_temp[i++] = CFGTOKEN_HOST_IIE; + break; + case MACHINE_IIGS: + config_temp[i++] = CFGTOKEN_HOST_IIGS; + break; + } +#ifdef FUNCTION_Z80 + switch(serialmux) { + case SERIAL_USB: + config_temp[i++] = CFGTOKEN_MUX_USB; + break; + case SERIAL_WIFI: + config_temp[i++] = CFGTOKEN_MUX_WIFI; + break; + case SERIAL_PRINTER: + config_temp[i++] = CFGTOKEN_MUX_PRN; + break; + default: + case SERIAL_LOOP: + config_temp[i++] = CFGTOKEN_MUX_LOOP; + break; + } + switch(usbmux) { + case USB_HOST_CDC: + config_temp[i++] = CFGTOKEN_USB_HOST; + break; + default: + case USB_GUEST_CDC: + config_temp[i++] = CFGTOKEN_USB_GUEST; + break; + case USB_GUEST_MIDI: + config_temp[i++] = CFGTOKEN_USB_MIDI; + break; + } + + switch(wifimode) { + case WIFI_CLIENT: + config_temp[i++] = CFGTOKEN_WIFI_AP; + break; + case WIFI_AP: + config_temp[i++] = CFGTOKEN_WIFI_CL; + break; + } + + config_temp[i] = CFGTOKEN_WIFI_SSID | (((uint32_t)strlen((char*)wifi_ssid)+1) << 24); + strcpy((char*)(config_temp+i+1), (char*)wifi_ssid); + i += 1 + (((config_temp[i] >> 24) + 3) >> 2); + + config_temp[i] = CFGTOKEN_WIFI_PSK | (((uint32_t)strlen((char*)wifi_psk)+1) << 24); + strcpy((char*)(config_temp+i+1), (char*)wifi_psk); + i += 1 + (((config_temp[i] >> 24) + 3) >> 2); +#endif + +#ifdef FUNCTION_VGA + config_temp[i++] = CFGTOKEN_MONO_00 | ((mono_palette & 0xF) << 20); + config_temp[i++] = CFGTOKEN_TBCOLOR | ((terminal_tbcolor & 0xFF) << 16); + config_temp[i++] = CFGTOKEN_BORDER | ((terminal_border & 0xF) << 16); + + // TODO: Font + //config_temp[i++] = CFGTOKEN_FONTSLOT | ((terminal_font & 0x1F) << 16); +#endif + config_temp[i++] = NEWCONFIG_EOF_MARKER; + + return i * 4; +} + +// Verify config block starts with the appropriate magic, has an end marker, +// and doesn't have any fields that would overflow the block. +bool DELAYED_COPY_CODE(is_config_valid)(uint32_t address) { + uint32_t *config = (uint32_t*)address; + int i; + + if(config[0] != NEWCONFIG_MAGIC) return false; + + for(i = 0; i < (CONFIG_SIZE/sizeof(uint32_t)); i++) { + if(config[i] == NEWCONFIG_EOF_MARKER) return true; + + i += ((config[i] >> 24) + 3) >> 2; + } + + return false; +} + +// Find and return the config revision number for the block +uint8_t DELAYED_COPY_CODE(get_config_rev)(uint32_t address) { + uint32_t *config = (uint32_t*)address; + int i; + + for(i = 0; i < (CONFIG_SIZE/sizeof(uint32_t)); i++) { + if((config[i] & 0x0000FFFF) == CFGTOKEN_REVISION) + return (config[i] >> 16) & 0xFF; + + if(config[i] == NEWCONFIG_EOF_MARKER) return 0x00; + + i += ((config[i] >> 24) + 3) >> 2; + } + + return 0x00; +} + +// Every time we write the config we overwrite the older slot, +// ensuring we don't leave the user without a configuration. +// We increment the revision number each time, wrapping back to 0x00 from 0xFF. +bool DELAYED_COPY_CODE(is_primary_config_newer)() { + uint8_t a=get_config_rev(FLASH_CONFIG_PRIMARY); + uint8_t b=get_config_rev(FLASH_CONFIG_SECONDARY); + + return (a == ((b+1) & 0xff)); +} + +bool DELAYED_COPY_CODE(read_config)() { + if(is_config_valid(FLASH_CONFIG_ONETIME)) { + internal_flags &= ~IFLAGS_TEST; + soft_switches |= SOFTSW_TEXT_MODE; + if(parse_config(FLASH_CONFIG_ONETIME)) + return true; + } + if(is_config_valid(FLASH_CONFIG_PRIMARY)) { + if(!is_config_valid(FLASH_CONFIG_SECONDARY) || (is_primary_config_newer())) { + if(parse_config(FLASH_CONFIG_PRIMARY)) + return true; + } + } + if(is_config_valid(FLASH_CONFIG_SECONDARY)) { + if(parse_config(FLASH_CONFIG_SECONDARY)) + return true; + } + + default_config(); + return false; +} + +bool DELAYED_COPY_CODE(write_config)(bool onetime) { + uint8_t rev = 0xFF; + bool write_secondary = false; + bool retval = false; + + // Disable video output to stop DMA and IRQs + vga_dpms_sleep(); + + if(is_config_valid(FLASH_CONFIG_PRIMARY) && is_config_valid(FLASH_CONFIG_SECONDARY)) { + write_secondary = is_primary_config_newer(); + rev = write_secondary ? get_config_rev(FLASH_CONFIG_PRIMARY) : get_config_rev(FLASH_CONFIG_SECONDARY); + } else if(is_config_valid(FLASH_CONFIG_PRIMARY)) { + write_secondary = true; + rev = get_config_rev(FLASH_CONFIG_PRIMARY); + } else if(is_config_valid(FLASH_CONFIG_SECONDARY)) { + write_secondary = false; + rev = get_config_rev(FLASH_CONFIG_SECONDARY); + } + + if(make_config(rev + 1) <= CONFIG_SIZE) { + + if(onetime) { + flash_range_erase((FLASH_CONFIG_ONETIME-XIP_BASE), CONFIG_SIZE); + flash_range_program((FLASH_CONFIG_ONETIME-XIP_BASE), (const uint8_t *)config_temp, CONFIG_SIZE); + } else if(write_secondary) { + flash_range_erase((FLASH_CONFIG_SECONDARY-XIP_BASE), CONFIG_SIZE); + flash_range_program((FLASH_CONFIG_SECONDARY-XIP_BASE), (const uint8_t *)config_temp, CONFIG_SIZE); + } else { + flash_range_erase((FLASH_CONFIG_PRIMARY-XIP_BASE), CONFIG_SIZE); + flash_range_program((FLASH_CONFIG_PRIMARY-XIP_BASE), (const uint8_t *)config_temp, CONFIG_SIZE); + } + + retval = true; + } + + // Enable video output now that we are done + vga_dpms_wake(); + + return retval; +} + +#ifdef FUNCTION_VGA +uint8_t DELAYED_COPY_CODE(test_font)(uint16_t param0) { + int i; + + for(i = 0; i < 4096; i++) { + character_rom[i] = apple_memory[param0+i]; + } + + return REPLY_OK; +} + +uint8_t DELAYED_COPY_CODE(write_font)(uint16_t param0, uint16_t param1) { + int i; + + if(param1 > 0x27) return REPLY_EPARAM; + + // Disable video output to stop DMA and IRQs + vga_dpms_sleep(); + + for(i = 0; i < 4096; i++) { + character_rom[i] = apple_memory[param0+i]; + } + + flash_range_erase((FLASH_FONT(param1)-XIP_BASE), FONT_SIZE); + flash_range_program((FLASH_FONT(param1)-XIP_BASE), character_rom, FONT_SIZE); + + // Enable video output now that we are done + vga_dpms_wake(); + + return REPLY_OK; +} +#endif + +uint8_t DELAYED_COPY_CODE(cf_readblock)(uint16_t param0) { + if(param0 >= (FLASH_SIZE/4096)) return REPLY_EPARAM; + + // Disable video output to stop DMA and IRQs + vga_dpms_sleep(); + + memcpy32(cfbuf, XIP_BASE+(param0 * 4096), 4096); + + // Enable video output now that we are done + vga_dpms_wake(); + + return REPLY_OK; +} + +uint8_t DELAYED_COPY_CODE(cf_writeblock)(uint16_t param0) { + // Protect bottom 512K of flash + if(param0 < 0x080) + return REPLY_EPARAM; + + if(param0 >= (FLASH_SIZE/4096)) + return REPLY_EPARAM; + + // Disable video output to stop DMA and IRQs + vga_dpms_sleep(); + + flash_range_program(param0 * 4096, (void*)cfbuf, 4096); + + // Enable video output now that we are done + vga_dpms_wake(); + + return REPLY_OK; +} + +uint8_t DELAYED_COPY_CODE(cf_eraseblock)(uint16_t param0) { + // Protect bottom 512K of flash + if(param0 < 0x080) + return REPLY_EPARAM; + + if(param0 >= (FLASH_SIZE/4096)) + return REPLY_EPARAM; + + // Disable video output to stop DMA and IRQs + vga_dpms_sleep(); + + flash_range_erase(param0 * 4096, 4096); + + // Enable video output now that we are done + vga_dpms_wake(); + + return REPLY_OK; +} + +void DELAYED_COPY_CODE(config_handler)() { + uint8_t retval = 0; + uint16_t param0, param1, param2; + int rs = 1; + + if(config_cmdbuf[0] == 0xFF) return; + + param0 = config_cmdbuf[3]; + param0 <<= 8; + param0 |= config_cmdbuf[2]; + + param1 = config_cmdbuf[5]; + param1 <<= 8; + param1 |= config_cmdbuf[4]; + + param2 = config_cmdbuf[7]; + param2 <<= 8; + param2 |= config_cmdbuf[6]; + + config_rpybuf[7] = 0x00; + config_rpybuf[6] = 0x00; + config_rpybuf[5] = 0x00; + config_rpybuf[4] = 0x00; + config_rpybuf[3] = 0x00; + config_rpybuf[2] = 0x00; + config_rpybuf[1] = 0x00; + config_rpybuf[0] = REPLY_BUSY; + + switch(config_cmdbuf[0]) { +#ifdef FUNCTION_VGA + case 'C': + switch(config_cmdbuf[1]) { + default: + retval = REPLY_ECMD; + break; + case 'T': + // One-time load of font data (lost at reboot) + retval = test_font(param0); + case 'I': + // Save font to flash + retval = write_font(param0, param1); + break; + } + break; +#endif + case 'H': + retval = REPLY_OK; + switch(config_cmdbuf[1]) { + default: + retval = REPLY_ECMD; + break; + case '2': + current_machine = MACHINE_II; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + break; + case 'E': + current_machine = MACHINE_IIE; + internal_flags &= ~IFLAGS_IIGS_REGS; + internal_flags |= IFLAGS_IIE_REGS; + break; + case 'G': + current_machine = MACHINE_IIGS; + internal_flags |= IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS; + break; +#if 0 + case 'B': + current_machine = MACHINE_BASIS; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + break; + case 'P': + current_machine = MACHINE_PRAVETZ; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + break; + case '7': + current_machine = MACHINE_AGAT7; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + break; + case '9': + current_machine = MACHINE_AGAT9; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + break; +#endif + } + break; + + case 'f': + // Flash Commands + // All valid flash commands will reset the cf pointer to 0 + switch(config_cmdbuf[1]) { + default: + retval = REPLY_ECMD; + break; + case 'r': + // Read block + retval = cf_readblock(param0); + cfptr = 0; + break; + case 'w': + // Write block + retval = cf_writeblock(param0); + cfptr = 0; + break; + case 'e': + // Erase block + retval = cf_eraseblock(param0); + cfptr = 0; + break; + } + break; + + case 'R': + // Reboot and bypass auto-detection of machine type. + cfg_machine = current_machine; + write_config(true); + flash_reboot(); + break; + + case 'I': + switch(config_cmdbuf[1]) { + default: + retval = REPLY_ECMD; + break; + case 'H': + // Identify Current Host Setting + retval = REPLY_OK; + switch(current_machine) { + case MACHINE_II: + config_rpybuf[rs++] = '2'; + break; + case MACHINE_IIE: + config_rpybuf[rs++] = 'E'; + break; + case MACHINE_IIGS: + config_rpybuf[rs++] = 'G'; + break; + default: + config_rpybuf[rs++] = '?'; + retval = REPLY_EPARAM; + break; + } + break; + + case 'F': + // Identify Current Firmware Function + retval = REPLY_OK; +#ifdef FUNCTION_VGA + config_rpybuf[rs++] = 'V'; + config_rpybuf[rs++] = 'G'; + config_rpybuf[rs++] = 'A'; +#endif +#ifdef FUNCTION_Z80 + config_rpybuf[rs++] = 'Z'; + config_rpybuf[rs++] = '8'; + config_rpybuf[rs++] = '0'; +#endif + break; + + case 'h': + // Identify Hardware Type + retval = REPLY_OK; + config_rpybuf[rs++] = 0x02; // V2 Retro Computing +#ifdef ANALOG_GS + // AnalogGS Rev 1 + config_rpybuf[rs++] = 'G'; + config_rpybuf[rs++] = '1'; +#else + // Analog Rev 1 + config_rpybuf[rs++] = 'A'; + config_rpybuf[rs++] = '1'; +#endif + break; + + case 'd': + // Identify Date Request + retval = REPLY_OK; + config_rpybuf[rs++] = (BUILDDATE >> 24) & 0xFF; + config_rpybuf[rs++] = (BUILDDATE >> 16) & 0xFF; + config_rpybuf[rs++] = (BUILDDATE >> 8) & 0xFF; + config_rpybuf[rs++] = (BUILDDATE >> 0) & 0xFF; + config_rpybuf[rs++] = (BUILDID >> 8) & 0xFF; + config_rpybuf[rs++] = (BUILDID >> 0) & 0xFF; + break; + } + break; + default: + // Unrecognized command + retval = REPLY_ECMD; + } + + config_cmdbuf[7] = 0xFF; + config_cmdbuf[6] = 0xFF; + config_cmdbuf[5] = 0xFF; + config_cmdbuf[4] = 0xFF; + config_cmdbuf[3] = 0xFF; + config_cmdbuf[2] = 0xFF; + config_cmdbuf[1] = 0xFF; + config_cmdbuf[0] = 0xFF; + + config_rpybuf[0] = retval; +} diff --git a/common/config.h b/common/config.h new file mode 100644 index 0000000..bcccf9d --- /dev/null +++ b/common/config.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include "common/cfgtoken.h" + +#ifndef CONFIG_SYSCLOCK +#warning Defaulting to 126MHz +#define CONFIG_SYSCLOCK 126.0 /* MHz */ +#endif + +// Pin configuration +#ifdef ANALOG_GS +#define CONFIG_PIN_APPLEBUS_DATA_BASE 16 /* 8+2 pins */ +#define CONFIG_PIN_APPLEBUS_DEVSEL (CONFIG_PIN_APPLEBUS_DATA_BASE+8) +#define CONFIG_PIN_APPLEBUS_RW (CONFIG_PIN_APPLEBUS_DATA_BASE+9) +#define CONFIG_PIN_APPLEBUS_CONTROL_BASE 26 /* 4 pins */ +#define CONFIG_PIN_APPLEBUS_PHI0 14 +#else +#define CONFIG_PIN_APPLEBUS_DATA_BASE 0 /* 8+2 pins */ +#define CONFIG_PIN_APPLEBUS_DEVSEL (CONFIG_PIN_APPLEBUS_DATA_BASE+8) +#define CONFIG_PIN_APPLEBUS_RW (CONFIG_PIN_APPLEBUS_DATA_BASE+9) +#define CONFIG_PIN_APPLEBUS_CONTROL_BASE 10 /* 4 pins */ +#define CONFIG_PIN_APPLEBUS_PHI0 26 +#endif + +#define CONFIG_ABUS_PIO pio1 + +#ifdef FUNCTION_VGA +#ifdef ANALOG_GS +#define CONFIG_PIN_HSYNC 13 +#define CONFIG_PIN_VSYNC 12 +#define CONFIG_PIN_RGB_BASE 0 /* 12 pins */ +#else +#define CONFIG_PIN_HSYNC 28 +#define CONFIG_PIN_VSYNC 27 +#define CONFIG_PIN_RGB_BASE 14 /* 9 pins */ +#endif + +// Other resources +#define CONFIG_VGA_PIO pio0 +#define CONFIG_VGA_SPINLOCK_ID 31 + +extern volatile uint32_t mono_palette; +extern volatile uint8_t terminal_tbcolor; +extern volatile uint8_t terminal_border; +#endif + + +#ifdef FUNCTION_Z80 +typedef enum { + SERIAL_LOOP = 0, + SERIAL_USB, + SERIAL_WIFI, + SERIAL_PRINTER, +} serialmux_t; + +extern volatile serialmux_t serialmux; + +typedef enum { + USB_HOST_CDC, + USB_GUEST_CDC, + USB_GUEST_MIDI, +} usbmux_t; + +extern volatile usbmux_t usbmux; + +typedef enum { + WIFI_CLIENT = 0, + WIFI_AP, +} wifimode_t; + +extern volatile wifimode_t wifimode; +#endif + +typedef enum { + MACHINE_II = 0, + MACHINE_IIE = 1, + MACHINE_IIGS = 2, + MACHINE_PRAVETZ = 6, + MACHINE_AGAT7 = 7, + MACHINE_BASIS = 8, + MACHINE_AGAT9 = 9, + MACHINE_INVALID = 0xfe, + MACHINE_AUTO = 0xff +} compat_t; + +extern volatile compat_t cfg_machine; +extern volatile compat_t current_machine; + + +void default_config(); +int make_config(uint8_t rev); +bool read_config(); +bool write_config(bool onetime); + +void config_handler(); + +void dmacopy32(uint32_t *start, uint32_t *end, uint32_t *source); + +#if 1 +#define DELAYED_COPY_CODE(n) __noinline __attribute__((section(".delayed_code."))) n +#else +#define DELAYED_COPY_CODE(n) __noinline __time_critical_func(n) +#endif + +#if 1 +#define DELAYED_COPY_DATA(n) __attribute__((section(".delayed_data."))) n +#else +#define DELAYED_COPY_DATA(n) n +#endif diff --git a/common/dmacopy.c b/common/dmacopy.c new file mode 100644 index 0000000..140d51d --- /dev/null +++ b/common/dmacopy.c @@ -0,0 +1,50 @@ +#include +#include + +static int dmacopy_channel = -1; + +// TODO: mutex this! +void __noinline memcpy32(void *dst, void *src, uint32_t size) { + dma_channel_config c; + + // Nothing to do! + if(!size) return; + + // Cowardly avoid unaligned transfers, let memcpy() handle them. + if((size & 0x3) || (((uint32_t)dst) & 0x3) || (((uint32_t)src) & 0x3)) { + memcpy(dst, src, size); + return; + } + + // 32 bit transfers. Both read and write address increment after each + // transfer (each pointing to a location in src or dst respectively). + // No DREQ is selected, so the DMA transfers as fast as it can. + + // Get a free channel, panic() if there are none + if(dmacopy_channel == -1) + dmacopy_channel = dma_claim_unused_channel(true); + + c = dma_channel_get_default_config(dmacopy_channel); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + channel_config_set_read_increment(&c, true); + channel_config_set_write_increment(&c, true); + + dma_channel_configure(dmacopy_channel, &c, dst, src, (size >> 2), true); + + dma_channel_wait_for_finish_blocking(dmacopy_channel); + + dma_channel_abort(dmacopy_channel); + + // Deinit the DMA channel + c = dma_channel_get_default_config(dmacopy_channel); + dma_channel_configure(dmacopy_channel, &c, NULL, NULL, 0, false); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, false); +} + +void __noinline dmacpy32(void *start, void *end, void *source) { + uint32_t size = ((uint32_t)end) - ((uint32_t)start); + + memcpy32(start, source, size); +} + diff --git a/common/dmacopy.h b/common/dmacopy.h new file mode 100644 index 0000000..d0f6271 --- /dev/null +++ b/common/dmacopy.h @@ -0,0 +1,4 @@ +#pragma once + +void memcpy32(void *dst, void *src, uint32_t size); +void dmacpy32(void *start, void *end, void *source); diff --git a/common/flash.c b/common/flash.c new file mode 100644 index 0000000..157c31b --- /dev/null +++ b/common/flash.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include "config.h" + +#ifdef RASPBERRYPI_PICO_W +#include +#endif + +void __time_critical_func(flash_reboot)() __attribute__ ((noreturn)); + +// Reboot the Pico +void __time_critical_func(flash_reboot)() { + save_and_disable_interrupts(); + + multicore_reset_core1(); + + reset_block((1<<11) | (1<<10) | (1<<2)); + + watchdog_enable(2, 1); + for(;;); +} + diff --git a/common/flash.h b/common/flash.h new file mode 100644 index 0000000..73596df --- /dev/null +++ b/common/flash.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#ifdef HAVE_8MB_FLASH +#define FLASH_SIZE (8*1024*1024) +#else +#define FLASH_SIZE (2*1024*1024) +#endif +#define FLASH_TOP (XIP_BASE + FLASH_SIZE) +#define FLASH_RESERVED (FLASH_TOP - (256*1024)) + +#define CONFIG_SIZE (4*1024) +#define FLASH_CONFIG_ONETIME (FLASH_RESERVED - CONFIG_SIZE) +#define FLASH_CONFIG_PRIMARY (FLASH_CONFIG_ONETIME - CONFIG_SIZE) +#define FLASH_CONFIG_SECONDARY (FLASH_CONFIG_PRIMARY - CONFIG_SIZE) + +#define FONT_SIZE (4*1024) +#define FONT_COUNT 40 +#define FLASH_FONT_BASE (FLASH_CONFIG_SECONDARY - (FONT_SIZE * FONT_COUNT)) +#define FLASH_FONT(n) (FLASH_FONT_BASE + (FONT_SIZE * (n))) + +#define FLASH_FONT_ROMXE00 FLASH_FONT(0x00) +#define FLASH_FONT_ROMXE01 FLASH_FONT(0x01) +#define FLASH_FONT_ROMXE02 FLASH_FONT(0x02) +#define FLASH_FONT_ROMXE03 FLASH_FONT(0x03) +#define FLASH_FONT_ROMXE04 FLASH_FONT(0x04) +#define FLASH_FONT_ROMXE05 FLASH_FONT(0x05) +#define FLASH_FONT_ROMXE06 FLASH_FONT(0x06) +#define FLASH_FONT_ROMXE07 FLASH_FONT(0x07) +#define FLASH_FONT_ROMXE08 FLASH_FONT(0x08) +#define FLASH_FONT_ROMXE09 FLASH_FONT(0x09) +#define FLASH_FONT_ROMXE0A FLASH_FONT(0x0A) +#define FLASH_FONT_ROMXE0B FLASH_FONT(0x0B) +#define FLASH_FONT_ROMXE0C FLASH_FONT(0x0C) +#define FLASH_FONT_ROMXE0D FLASH_FONT(0x0D) +#define FLASH_FONT_ROMXE0E FLASH_FONT(0x0E) +#define FLASH_FONT_ROMXE0F FLASH_FONT(0x0F) +#define FLASH_FONT_ROMXE10 FLASH_FONT(0x10) +#define FLASH_FONT_ROMXE11 FLASH_FONT(0x11) +#define FLASH_FONT_ROMXE12 FLASH_FONT(0x12) +#define FLASH_FONT_ROMXE13 FLASH_FONT(0x13) +#define FLASH_FONT_ROMXE14 FLASH_FONT(0x14) +#define FLASH_FONT_ROMXE15 FLASH_FONT(0x15) +#define FLASH_FONT_ROMXE16 FLASH_FONT(0x16) +#define FLASH_FONT_ROMXE17 FLASH_FONT(0x17) +#define FLASH_FONT_ROMXE18 FLASH_FONT(0x18) +#define FLASH_FONT_ROMXE19 FLASH_FONT(0x19) +#define FLASH_FONT_ROMXE1A FLASH_FONT(0x1A) +#define FLASH_FONT_ROMXE1B FLASH_FONT(0x1B) +#define FLASH_FONT_ROMXE1C FLASH_FONT(0x1C) +#define FLASH_FONT_ROMXE1D FLASH_FONT(0x1D) +#define FLASH_FONT_ROMXE1E FLASH_FONT(0x1E) +#define FLASH_FONT_ROMXE1F FLASH_FONT(0x1F) +#define FLASH_FONT_APPLE_II FLASH_FONT(0x20) +#define FLASH_FONT_APPLE_IIE FLASH_FONT(0x21) +#define FLASH_FONT_APPLE_IIGS FLASH_FONT(0x22) +#define FLASH_FONT_PRAVETZ FLASH_FONT(0x23) +#define FLASH_FONT_EXTRA24 FLASH_FONT(0x24) +#define FLASH_FONT_EXTRA25 FLASH_FONT(0x25) +#define FLASH_FONT_EXTRA26 FLASH_FONT(0x26) +#define FLASH_FONT_EXTRA27 FLASH_FONT(0x27) + +// Firmware for $C000-$CFFF +#define FLASH_6502_SIZE (4*1024) +#define FLASH_6502_BASE (FLASH_FONT_BASE - FLASH_6502_SIZE) + +extern void flash_reboot() __attribute__ ((noreturn)); diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000..05938f0 --- /dev/null +++ b/common/main.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include "common/abus.h" +#include "common/config.h" +#include "common/modes.h" +#include "common/buffers.h" +#include "common/flash.h" +#include "common/dmacopy.h" + +#ifdef RASPBERRYPI_PICO_W +#include +#endif + +#define ACCESS_READ ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) != 0) +#define ACCESS_WRITE ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) + +static void __noinline __time_critical_func(core1_loop)() { + uint32_t value; + uint32_t address; + + for(;;) { + value = pio_sm_get_blocking(CONFIG_ABUS_PIO, ABUS_MAIN_SM); + address = (value >> 10) & 0xffff; + + // device read access + if(ACCESS_READ) { + if(CARD_SELECT) { + //pio_sm_put(CONFIG_ABUS_PIO, ABUS_DEVICE_READ_SM, apple_memory[address]); + CONFIG_ABUS_PIO->txf[ABUS_DEVICE_READ_SM] = apple_memory[address]; + } + } + + busactive = 1; + + if(CARD_SELECT) { + if(CARD_DEVSEL) { + cardslot = (address >> 4) & 0x7; + } else if(CARD_IOSEL) { + cardslot = (address >> 8) & 0x7; + + // Config memory in card slot-rom address space + if(ACCESS_WRITE) { + if((address & 0xFF) == 0xED) { + apple_memory[address] = value; + cfptr = (cfptr & 0x1F00) | value; + apple_memory[address] = cfbuf[cfptr]; + } + if((address & 0xFF) == 0xEE) { + apple_memory[address] = value & 0x1F; + cfptr = ((cfptr & 0xFF) | (value << 8)) & 0x1FFF; + apple_memory[address] = cfbuf[cfptr]; + } + if((address & 0xFF) == 0xEF) { + apple_memory[address] = value; + cfbuf[cfptr] = value; + } + if((address & 0xFF) >= 0xF0) { + apple_memory[address] = value; + } + } + if((address & 0xFF) == 0xEF) { + cfptr = (cfptr + 1) & 0x1FFF; + apple_memory[address] = cfbuf[cfptr]; + apple_memory[address-1] = (cfptr >> 8) & 0xff; + apple_memory[address-2] = cfptr & 0xff; + } + + // Stop further processing by businterface + continue; + } +#ifdef FUNCTION_VGA + } else if(current_machine == MACHINE_AUTO) { + if((apple_memory[0x0417] == 0xE7) && (apple_memory[0x416] == 0xC9)) { // Apple IIgs + current_machine = MACHINE_IIGS; + internal_flags &= ~IFLAGS_IIE_REGS; + internal_flags |= IFLAGS_IIGS_REGS; + } else if((apple_memory[0x0417] == 0xE5) && (apple_memory[0x416] == 0xAF)) { // Apple //e Enhanced + current_machine = MACHINE_IIE; + internal_flags |= IFLAGS_IIE_REGS; + internal_flags &= ~IFLAGS_IIGS_REGS; + } else if((apple_memory[0x0415] == 0xDD) && (apple_memory[0x413] == 0xE5)) { // Apple //e Unenhanced + current_machine = MACHINE_IIE; + internal_flags |= IFLAGS_IIE_REGS; + internal_flags &= ~IFLAGS_IIGS_REGS; + } else if(apple_memory[0x0410] == 0xD0) { // Apple II/Plus/J-Plus with Autostart + current_machine = MACHINE_II; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + } else if((apple_memory[0x07D0] == 0xAA) && (apple_memory[0x07D1] == 0x60)) { // Apple II without Autostart + current_machine = MACHINE_II; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + } else if(apple_memory[0x0410] == 0xF2) { // Pravetz! + current_machine = MACHINE_PRAVETZ; + internal_flags &= ~(IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS); + } +#endif + } else switch(reset_state) { + case 0: + if((value & 0x3FFFF00) == ((0xFFFC << 10) | 0x300)) + reset_state++; + break; + case 1: + if((value & 0x3FFFF00) == ((0xFFFD << 10) | 0x300)) + reset_state++; + else + reset_state=0; + break; + case 2: + if((value & 0x3FFFF00) == ((0xFA62 << 10) | 0x300)) + reset_state++; + else + reset_state=0; + break; + case 3: +#ifdef FUNCTION_VGA + soft_switches = SOFTSW_TEXT_MODE; + soft_switches &= ~SOFTSW_80COL; +#endif + default: + reset_state = 0; + break; + } + +#ifdef FUNCTION_VGA + vga_businterface(address, value); +#endif +#ifdef FUNCTION_Z80 + z80_businterface(address, value); +#endif + } +} + +static void DELAYED_COPY_CODE(core0_loop)() { +#ifdef FUNCTION_VGA + for(;;) vgamain(); +#endif +#ifdef FUNCTION_Z80 + for(;;) z80main(); +#endif +} + +extern uint32_t __ram_delayed_copy_source__[]; +extern uint32_t __ram_delayed_copy_start__[]; +extern uint32_t __ram_delayed_copy_end__[]; + +int main() { + // Adjust system clock for better dividing into other clocks + set_sys_clock_khz(CONFIG_SYSCLOCK*1000, true); + + abus_init(); + + multicore_launch_core1(core1_loop); + + // Load 6502 code from flash into the memory buffer + memcpy32((void*)apple_memory+0xC000, (void *)FLASH_6502_BASE, FLASH_6502_SIZE); + + // Initialize the config window in each rom slot + memcpy((uint8_t*)apple_memory+0xC1F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC2F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC3F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC4F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC5F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC6F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + memcpy((uint8_t*)apple_memory+0xC7F0, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFV2ANALOG", 16); + + // Finish copying remaining data and code to RAM from flash + dmacpy32(__ram_delayed_copy_start__, __ram_delayed_copy_end__, __ram_delayed_copy_source__); + + // Sensible defaults if there is no config / fs + default_config(); + + core0_loop(); + + return 0; +} diff --git a/common/modes.h b/common/modes.h new file mode 100644 index 0000000..0e59504 --- /dev/null +++ b/common/modes.h @@ -0,0 +1,18 @@ + +void vgamain(); +void vga_businterface(uint32_t address, uint32_t value); + +void z80main(); +void z80_businterface(uint32_t address, uint32_t value); + +void serialmain(); +void serial_businterface(uint32_t address, uint32_t value); + +void parallelmain(); +void parallel_businterface(uint32_t address, uint32_t value); + +void diagmain(); +void diag_businterface(uint32_t address, uint32_t value); + +void fsmain(); +void fs_businterface(uint32_t address, uint32_t value); diff --git a/common/usb_descriptors.c b/common/usb_descriptors.c new file mode 100644 index 0000000..e20c2d4 --- /dev/null +++ b/common/usb_descriptors.c @@ -0,0 +1,238 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#include "tusb.h" + + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] MIDI | HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +enum +{ + ITF_NUM_CDC_0 = 0, + ITF_NUM_CDC_0_DATA, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_CDC_0_NOTIF 0x81 + #define EPNUM_CDC_0_OUT 0x02 + #define EPNUM_CDC_0_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X + // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_CDC_0_NOTIF 0x81 + #define EPNUM_CDC_0_OUT 0x02 + #define EPNUM_CDC_0_IN 0x83 + +#else + #define EPNUM_CDC_0_NOTIF 0x81 + #define EPNUM_CDC_0_OUT 0x02 + #define EPNUM_CDC_0_IN 0x82 + +#endif + +uint8_t const desc_fs_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64), + + // 2nd CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. + //TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64), +}; + +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +uint8_t const desc_hs_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512), +}; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const* tud_descriptor_device_qualifier_cb(void) +{ + return (uint8_t const*) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; +} + +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "V2Retro", // 1: Manufacturer + "V2 Analog VGA", // 2: Product + "123456", // 3: Serials, should use chip ID + "V2 Analog CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i FLASH + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + + .delayed_copy : { + __ram_delayed_copy_start__ = .; + *(.delayed_code.*) + . = ALIGN(4); + + *(.delayed_data.*) + . = ALIGN(4); + __ram_delayed_copy_end__ = .; + } > RAM AT> FLASH + __ram_delayed_copy_source__ = LOADADDR(.delayed_copy); + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .appledata (COPY): { + __appledata_start__ = .; + *(.appledata.*) + . = ALIGN(4); + __appledata_end__ = .; + } > APPLEDATA + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/lwipopts.h b/lwipopts.h new file mode 100644 index 0000000..4f98032 --- /dev/null +++ b/lwipopts.h @@ -0,0 +1,91 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// + +// allow override +#ifndef NO_SYS +#define NO_SYS 1 +#endif +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions +#define MEM_LIBC_MALLOC 0 +#endif +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +#endif + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define INET_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define RAW_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define SYS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define PPP_DEBUG LWIP_DBG_OFF +#define SLIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF + + +#endif + diff --git a/tusb_config.h b/tusb_config.h new file mode 100644 index 0000000..e786253 --- /dev/null +++ b/tusb_config.h @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 0 +#define CFG_TUD_CDC 1 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +//------------- CDC -------------// + +// CDC buffer size Should be sufficient to hold data +#define CFG_TUD_CDC_RX_BUFSIZE 16 +#define CFG_TUD_CDC_TX_BUFSIZE 16 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/vga/businterface.c b/vga/businterface.c new file mode 100644 index 0000000..463af66 --- /dev/null +++ b/vga/businterface.c @@ -0,0 +1,248 @@ +#include +#include +#include "common/config.h" +#include "common/abus.h" +#include "vga/businterface.h" +#include "vga/vgabuf.h" + +volatile uint8_t *terminal_page = terminal_memory; + +void __time_critical_func(vga_businterface)(uint32_t address, uint32_t value) { + // Shadow the soft-switches by observing all read & write bus cycles + if((address & 0xff80) == 0xc000) { + switch(address & 0x7f) { + case 0x00: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_80STORE; + } + break; + case 0x01: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_80STORE; + } + break; + case 0x04: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_AUX_WRITE; + } + break; + case 0x05: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_AUX_WRITE; + } + break; + case 0x08: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_AUXZP; + } + break; + case 0x09: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_AUXZP; + } + break; + case 0x0c: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_80COL; + } + break; + case 0x0d: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_80COL; + } + break; + case 0x0e: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_ALTCHAR; + } + break; + case 0x0f: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_ALTCHAR; + } + break; + case 0x21: + if((internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + if(value & 0x80) { + soft_switches |= SOFTSW_MONOCHROME; + } else { + soft_switches &= ~SOFTSW_MONOCHROME; + } + } + break; + case 0x22: + if((internal_flags & IFLAGS_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + apple_tbcolor = value & 0xff; + } + break; + case 0x29: + if((internal_flags & IFLAGS_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches = (soft_switches & ~(SOFTSW_NEWVID_MASK << SOFTSW_NEWVID_SHIFT)) | ((value & SOFTSW_NEWVID_MASK) << SOFTSW_NEWVID_SHIFT); + } + break; + case 0x34: + if((internal_flags & IFLAGS_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + apple_border = value & 0x0f; + } + break; + case 0x35: + if((internal_flags & IFLAGS_IIGS_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches = (soft_switches & ~(SOFTSW_SHADOW_MASK << SOFTSW_SHADOW_SHIFT)) | ((value & SOFTSW_SHADOW_MASK) << SOFTSW_SHADOW_SHIFT); + } + break; + case 0x50: + soft_switches &= ~SOFTSW_TEXT_MODE; + break; + case 0x51: + soft_switches |= SOFTSW_TEXT_MODE; + break; + case 0x52: + soft_switches &= ~SOFTSW_MIX_MODE; + break; + case 0x53: + soft_switches |= SOFTSW_MIX_MODE; + break; + case 0x54: + soft_switches &= ~SOFTSW_PAGE_2; + break; + case 0x55: + soft_switches |= SOFTSW_PAGE_2; + break; + case 0x56: + soft_switches &= ~SOFTSW_HIRES_MODE; + break; + case 0x57: + soft_switches |= SOFTSW_HIRES_MODE; + break; + case 0x5e: + if(internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) { + soft_switches |= SOFTSW_DGR; + } + break; + case 0x5f: + if(internal_flags & (IFLAGS_IIGS_REGS | IFLAGS_IIE_REGS)) { + soft_switches &= ~SOFTSW_DGR; + } + break; + case 0x7e: + if((internal_flags & IFLAGS_IIE_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches |= SOFTSW_IOUDIS; + } + break; + case 0x7f: + if((internal_flags & IFLAGS_IIE_REGS) && ((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0)) { + soft_switches &= ~SOFTSW_IOUDIS; + } + break; + } + return; + } + + // Control sequences used by ROMX and ROMXe + if(value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) { + if(((address >> 8) == 0xCA) || ((address >> 8) == 0xFA)) { + switch(address & 0xFF) { + case 0xCA: + romx_unlocked = (romx_unlocked == 1) ? 2 : 1; + break; + case 0xFE: + romx_unlocked = (romx_unlocked == 2) ? 3 : 0; + if(romx_unlocked == 3) + romx_type = address >> 8; + break; + default: + if(romx_unlocked != 3) + romx_unlocked = 0; + break; + } + } else if(romx_unlocked == 3) { + if(romx_type == 0xFA) { + if((address >> 4) == 0xF81) { + romx_textbank = address & 0xF; + } + if(address == 0xF851) { + romx_changed = 1; + romx_unlocked = 0; + } + } else if(romx_type == 0xCA) { + if((address >> 4) == 0xCFD) { + romx_textbank = address & 0xF; + } + if((address >> 4) == 0xCFE) { + romx_changed = 1; + romx_unlocked = 0; + } + } + } + } + + // Shadow parts of the Apple's memory by observing the bus write cycles + if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) { + // Mirror Video Memory from MAIN & AUX banks + if(soft_switches & SOFTSW_LINEARIZE) { + if((address >= 0x2000) && (address < 0xC000)) { + private_memory[address] = value & 0xff; + return; + } + } + + if(soft_switches & SOFTSW_80STORE) { + if(soft_switches & SOFTSW_PAGE_2) { + if((address >= 0x400) && (address < 0x800)) { + private_memory[address] = value & 0xff; + return; + } else if((soft_switches & SOFTSW_HIRES_MODE) && (address >= 0x2000) && (address < 0x4000)) { + private_memory[address] = value & 0xff; + return; + } + } + } else if(soft_switches & SOFTSW_AUX_WRITE) { + if((address >= 0x200) && (address < 0xC000)) { + private_memory[address] = value & 0xff; + return; + } + } + + if((address >= 0x200) && (address < 0xC000)) { + apple_memory[address] = value & 0xff; + return; + } + + if(CARD_SELECT && CARD_DEVSEL) { + cardslot = (address >> 4) & 0x7; + switch(address & 0x0F) { + case 0x01: + mono_palette = (value >> 4) & 0xF; + apple_memory[address] = value; + break; + case 0x02: + terminal_tbcolor = value & 0xff; + apple_memory[address] = terminal_tbcolor; + break; + case 0x03: + terminal_border = value & 0x0f; + apple_memory[address] = terminal_border; + break; + case 0x08: + terminal_row = (value < 24) ? value : 23; + apple_memory[address] = terminal_row; + apple_memory[address+2] = terminal_memory[terminal_row*80+terminal_col]; + break; + case 0x09: + terminal_col = (value < 80) ? value : 79; + apple_memory[address] = terminal_col; + apple_memory[address+1] = terminal_memory[terminal_row*80+terminal_col]; + break; + case 0x0A: + terminal_memory[terminal_row*80+terminal_col] = value; + break; + case 0x0B: + if(value <= 0x27) { + romx_textbank = value; + romx_changed = 1; + } + break; + } + } + } +} diff --git a/vga/businterface.h b/vga/businterface.h new file mode 100644 index 0000000..12a6835 --- /dev/null +++ b/vga/businterface.h @@ -0,0 +1,3 @@ +#pragma once + +void vga_businterface(); diff --git a/vga/hires_color_patterns.h b/vga/hires_color_patterns.h new file mode 100644 index 0000000..1c77714 --- /dev/null +++ b/vga/hires_color_patterns.h @@ -0,0 +1,525 @@ +#pragma once + +#include "common/config.h" +#include "vga/render.h" + +#define _PIXELS(color1, color2) ((uint32_t)(color1) | ((uint32_t)(color2) << 16)) + +static uint32_t DELAYED_COPY_DATA(hires_color_patterns)[2*256] = { + // Even byte pixels + /* 0b00000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000100 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000101 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000110 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00001000 */ _PIXELS(RGB_BLACK, RGB_DBLUE), + /* 0b00001001 */ _PIXELS(RGB_BLACK, _RGB(0x90, 0x24, 0xD8)), + /* 0b00001010 */ _PIXELS(RGB_BLACK, _RGB(0x90, 0x90, 0x90)), + /* 0b00001011 */ _PIXELS(RGB_BLACK, _RGB(0xD8, 0x6C, 0xD8)), + /* 0b00001100 */ _PIXELS(RGB_BLACK, _RGB(0x00, 0x90, 0xFC)), + /* 0b00001101 */ _PIXELS(RGB_BLACK, _RGB(0x48, 0x6C, 0xFC)), + /* 0b00001110 */ _PIXELS(RGB_BLACK, _RGB(0x90, 0xFC, 0xB4)), + /* 0b00001111 */ _PIXELS(RGB_BLACK, RGB_WHITE), + /* 0b00010000 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), RGB_BLACK), + /* 0b00010001 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), _RGB(0x6C, 0x00, 0x6C)), + /* 0b00010010 */ _PIXELS(_RGB(0xD8, 0x24, 0x24), _RGB(0xFC, 0x48, 0x00)), + /* 0b00010011 */ _PIXELS(_RGB(0xD8, 0x24, 0x24), _RGB(0xFC, 0x48, 0x00)), + /* 0b00010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b00010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b00010110 */ _PIXELS(_RGB(0xB4, 0xB4, 0x24), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00010111 */ _PIXELS(_RGB(0xB4, 0xB4, 0x24), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00011000 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00011001 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00011010 */ _PIXELS(_RGB(0xD8, 0x48, 0xFC), RGB_PINK), + /* 0b00011011 */ _PIXELS(_RGB(0xD8, 0x48, 0xFC), RGB_PINK), + /* 0b00011100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00011101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00100010 */ _PIXELS(_RGB(0x24, 0x24, 0x00), _RGB(0x24, 0x24, 0x00)), + /* 0b00100011 */ _PIXELS(_RGB(0x24, 0x24, 0x00), _RGB(0xB4, 0x48, 0x00)), + /* 0b00100100 */ _PIXELS(_RGB(0x00, 0x6C, 0x00), RGB_DGREEN), + /* 0b00100101 */ _PIXELS(_RGB(0x00, 0x6C, 0x00), _RGB(0x24, 0x48, 0x48)), + /* 0b00100110 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b00100111 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x90, 0xD8, 0x00)), + /* 0b00101000 */ _PIXELS(_RGB(0x24, 0x24, 0xB4), RGB_DBLUE), + /* 0b00101001 */ _PIXELS(_RGB(0x24, 0x24, 0xB4), _RGB(0x90, 0x24, 0xD8)), + /* 0b00101010 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b00101011 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b00101100 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x00, 0x90, 0xFC)), + /* 0b00101101 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x48, 0x6C, 0xFC)), + /* 0b00101110 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00101111 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), RGB_WHITE), + /* 0b00110000 */ _PIXELS(_RGB(0x90, 0x24, 0x48), RGB_BLACK), + /* 0b00110001 */ _PIXELS(_RGB(0x90, 0x24, 0x48), _RGB(0x6C, 0x00, 0x6C)), + /* 0b00110010 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b00110011 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b00110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0x24), _RGB(0x48, 0x48, 0x48)), + /* 0b00110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0x24), _RGB(0x48, 0x48, 0x48)), + /* 0b00110110 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00110111 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00111000 */ _PIXELS(_RGB(0xB4, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00111001 */ _PIXELS(_RGB(0xB4, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00111010 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b00111011 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b00111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000100 */ _PIXELS(RGB_DGREEN, RGB_DGREEN), + /* 0b01000101 */ _PIXELS(RGB_DGREEN, _RGB(0x24, 0x48, 0x24)), + /* 0b01000110 */ _PIXELS(_RGB(0x00, 0xB4, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b01000111 */ _PIXELS(_RGB(0x00, 0xB4, 0x24), _RGB(0x90, 0xD8, 0x00)), + /* 0b01001000 */ _PIXELS(_RGB(0x00, 0x24, 0xB4), RGB_DBLUE), + /* 0b01001001 */ _PIXELS(_RGB(0x00, 0x24, 0xB4), _RGB(0x90, 0x24, 0xFC)), + /* 0b01001010 */ _PIXELS(_RGB(0x6C, 0x90, 0xB4), _RGB(0x90, 0x90, 0x90)), + /* 0b01001011 */ _PIXELS(_RGB(0x6C, 0x90, 0xB4), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b01001100 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b01001101 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x48, 0x6C, 0xFC)), + /* 0b01001110 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b01001111 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), RGB_WHITE), + /* 0b01010000 */ _PIXELS(_RGB(0x6C, 0x24, 0x6C), RGB_BLACK), + /* 0b01010001 */ _PIXELS(_RGB(0x6C, 0x24, 0x6C), _RGB(0x6C, 0x00, 0x6C)), + /* 0b01010010 */ _PIXELS(_RGB(0xD8, 0x48, 0x24), _RGB(0xFC, 0x48, 0x00)), + /* 0b01010011 */ _PIXELS(_RGB(0xD8, 0x48, 0x24), _RGB(0xFC, 0x48, 0x00)), + /* 0b01010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b01010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b01010110 */ _PIXELS(_RGB(0xB4, 0xB4, 0x24), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01010111 */ _PIXELS(_RGB(0xB4, 0xB4, 0x24), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01011000 */ _PIXELS(_RGB(0x90, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01011001 */ _PIXELS(_RGB(0x90, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01011010 */ _PIXELS(_RGB(0xD8, 0x6C, 0xFC), RGB_PINK), + /* 0b01011011 */ _PIXELS(_RGB(0xD8, 0x6C, 0xFC), RGB_PINK), + /* 0b01011100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01011101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01100010 */ _PIXELS(_RGB(0x24, 0x48, 0x00), _RGB(0x24, 0x24, 0x00)), + /* 0b01100011 */ _PIXELS(_RGB(0x24, 0x48, 0x00), _RGB(0xB4, 0x48, 0x00)), + /* 0b01100100 */ _PIXELS(_RGB(0x00, 0x6C, 0x00), RGB_DGREEN), + /* 0b01100101 */ _PIXELS(_RGB(0x00, 0x6C, 0x00), _RGB(0x24, 0x48, 0x48)), + /* 0b01100110 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b01100111 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x90, 0xD8, 0x00)), + /* 0b01101000 */ _PIXELS(_RGB(0x24, 0x48, 0xB4), _RGB(0x00, 0x24, 0xB4)), + /* 0b01101001 */ _PIXELS(_RGB(0x24, 0x48, 0xB4), _RGB(0x90, 0x24, 0xD8)), + /* 0b01101010 */ _PIXELS(_RGB(0x90, 0xB4, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b01101011 */ _PIXELS(_RGB(0x90, 0xB4, 0x90), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b01101100 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x00, 0x90, 0xFC)), + /* 0b01101101 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x48, 0x6C, 0xFC)), + /* 0b01101110 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b01101111 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), RGB_WHITE), + /* 0b01110000 */ _PIXELS(_RGB(0x90, 0x48, 0x48), RGB_BLACK), + /* 0b01110001 */ _PIXELS(0x112, _RGB(0x6C, 0x00, 0x6C)), + /* 0b01110010 */ _PIXELS(_RGB(0xFC, 0x6C, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b01110011 */ _PIXELS(_RGB(0xFC, 0x6C, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b01110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0x24), _RGB(0x48, 0x48, 0x48)), + /* 0b01110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0x24), _RGB(0x48, 0x48, 0x48)), + /* 0b01110110 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01110111 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01111000 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111001 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111010 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111011 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000100 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000101 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000110 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10001000 */ _PIXELS(RGB_DBLUE, RGB_DBLUE), + /* 0b10001001 */ _PIXELS(RGB_DBLUE, _RGB(0x90, 0x24, 0xD8)), + /* 0b10001010 */ _PIXELS(_RGB(0x6C, 0x6C, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b10001011 */ _PIXELS(_RGB(0x6C, 0x6C, 0x90), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b10001100 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b10001101 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x48, 0x6C, 0xFC)), + /* 0b10001110 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10001111 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), RGB_WHITE), + /* 0b10010000 */ _PIXELS(_RGB(0x6C, 0x00, 0x90), RGB_BLACK), + /* 0b10010001 */ _PIXELS(_RGB(0x6C, 0x00, 0x90), _RGB(0x6C, 0x00, 0x6C)), + /* 0b10010010 */ _PIXELS(_RGB(0xD8, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b10010011 */ _PIXELS(_RGB(0xD8, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b10010100 */ _PIXELS(_RGB(0x48, 0x48, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b10010101 */ _PIXELS(_RGB(0x48, 0x48, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b10010110 */ _PIXELS(_RGB(0xB4, 0xB4, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10010111 */ _PIXELS(_RGB(0xB4, 0xB4, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10011000 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10011001 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10011010 */ _PIXELS(_RGB(0xD8, 0x48, 0xFC), RGB_PINK), + /* 0b10011011 */ _PIXELS(_RGB(0xD8, 0x48, 0xFC), RGB_PINK), + /* 0b10011100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10011101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10100010 */ _PIXELS(_RGB(0x48, 0x48, 0x24), _RGB(0x24, 0x24, 0x00)), + /* 0b10100011 */ _PIXELS(_RGB(0x48, 0x48, 0x24), _RGB(0xB4, 0x48, 0x00)), + /* 0b10100100 */ _PIXELS(_RGB(0x24, 0x6C, 0x24), RGB_DGREEN), + /* 0b10100101 */ _PIXELS(_RGB(0x24, 0x6C, 0x24), _RGB(0x24, 0x48, 0x48)), + /* 0b10100110 */ _PIXELS(_RGB(0x24, 0xD8, 0x48), _RGB(0x00, 0xD8, 0x24)), + /* 0b10100111 */ _PIXELS(_RGB(0x24, 0xD8, 0x48), _RGB(0x90, 0xD8, 0x00)), + /* 0b10101000 */ _PIXELS(_RGB(0x24, 0x24, 0xB4), RGB_DBLUE), + /* 0b10101001 */ _PIXELS(_RGB(0x24, 0x24, 0xB4), _RGB(0x90, 0x24, 0xD8)), + /* 0b10101010 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b10101011 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b10101100 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x00, 0x90, 0xFC)), + /* 0b10101101 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x48, 0x6C, 0xFC)), + /* 0b10101110 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10101111 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), RGB_WHITE), + /* 0b10110000 */ _PIXELS(_RGB(0x90, 0x24, 0x90), RGB_BLACK), + /* 0b10110001 */ _PIXELS(_RGB(0x90, 0x24, 0x90), _RGB(0x6C, 0x00, 0x6C)), + /* 0b10110010 */ _PIXELS(_RGB(0xFC, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b10110011 */ _PIXELS(_RGB(0xFC, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b10110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b10110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b10110110 */ _PIXELS(_RGB(0xD8, 0xD8, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10110111 */ _PIXELS(_RGB(0xD8, 0xD8, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10111000 */ _PIXELS(_RGB(0xB4, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10111001 */ _PIXELS(_RGB(0xB4, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10111010 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b10111011 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b10111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000100 */ _PIXELS(_RGB(0x00, 0x48, 0x48), RGB_DGREEN), + /* 0b11000101 */ _PIXELS(_RGB(0x00, 0x48, 0x48), _RGB(0x24, 0x48, 0x24)), + /* 0b11000110 */ _PIXELS(_RGB(0x00, 0xB4, 0x6C), _RGB(0x00, 0xD8, 0x24)), + /* 0b11000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0x00, 0xB4, 0x6C), _RGB(0x90, 0xD8, 0x00)), + /* 0b11001000 */ _PIXELS(_RGB(0x00, 0x24, 0xB4), RGB_DBLUE), + /* 0b11001001 */ _PIXELS(_RGB(0x00, 0x24, 0xB4), _RGB(0x90, 0x24, 0xFC)), + /* 0b11001010 */ _PIXELS(_RGB(0x6C, 0x90, 0xB4), _RGB(0x90, 0x90, 0x90)), + /* 0b11001011 */ _PIXELS(_RGB(0x6C, 0x90, 0xB4), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b11001100 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b11001101 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x48, 0x6C, 0xFC)), + /* 0b11001110 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b11001111 */ _PIXELS(_RGB(0x6C, 0xD8, 0xB4), RGB_WHITE), + /* 0b11010000 */ _PIXELS(_RGB(0x6C, 0x24, 0x90), RGB_BLACK), + /* 0b11010001 */ _PIXELS(_RGB(0x6C, 0x24, 0x90), _RGB(0x6C, 0x00, 0x6C)), + /* 0b11010010 */ _PIXELS(_RGB(0xD8, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b11010011 */ _PIXELS(_RGB(0xD8, 0x48, 0x48), _RGB(0xFC, 0x48, 0x00)), + /* 0b11010100 */ _PIXELS(_RGB(0x48, 0x48, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b11010101 */ _PIXELS(_RGB(0x48, 0x48, 0x6C), _RGB(0x48, 0x48, 0x48)), + /* 0b11010110 */ _PIXELS(_RGB(0xB4, 0xB4, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11010111 */ _PIXELS(_RGB(0xB4, 0xB4, 0x48), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11011000 */ _PIXELS(_RGB(0x90, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11011001 */ _PIXELS(_RGB(0x90, 0x48, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11011010 */ _PIXELS(_RGB(0xD8, 0x6C, 0xFC), RGB_PINK), + /* 0b11011011 */ _PIXELS(_RGB(0xD8, 0x6C, 0xFC), RGB_PINK), + /* 0b11011100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11011101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11100010 */ _PIXELS(_RGB(0x48, 0x6C, 0x24), _RGB(0x24, 0x24, 0x00)), + /* 0b11100011 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0x48, 0x6C, 0x24), _RGB(0xB4, 0x48, 0x00)), + /* 0b11100100 */ _PIXELS(_RGB(0x24, 0x6C, 0x24), RGB_DGREEN), + /* 0b11100101 */ _PIXELS(_RGB(0x24, 0x6C, 0x24), _RGB(0x24, 0x48, 0x48)), + /* 0b11100110 */ _PIXELS(_RGB(0x24, 0xD8, 0x48), _RGB(0x00, 0xD8, 0x24)), + /* 0b11100111 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0x24, 0xD8, 0x48), _RGB(0x90, 0xD8, 0x00)), + /* 0b11101000 */ _PIXELS(_RGB(0x24, 0x48, 0xB4), _RGB(0x00, 0x24, 0xB4)), + /* 0b11101001 */ _PIXELS(_RGB(0x24, 0x48, 0xB4), _RGB(0x90, 0x24, 0xD8)), + /* 0b11101010 */ _PIXELS(_RGB(0x90, 0xB4, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b11101011 */ _PIXELS(_RGB(0x90, 0xB4, 0x90), _RGB(0xD8, 0x6C, 0xD8)), + /* 0b11101100 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x00, 0x90, 0xFC)), + /* 0b11101101 */ _PIXELS(_RGB(0x24, 0xB4, 0xD8), _RGB(0x48, 0x6C, 0xFC)), + /* 0b11101110 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b11101111 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), RGB_WHITE), + /* 0b11110000 */ _PIXELS(RGB_WHITE, RGB_BLACK), + /* 0b11110001 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x00, 0x6C)), + /* 0b11110010 */ _PIXELS(RGB_WHITE, _RGB(0xFC, 0x48, 0x00)), + /* 0b11110011 */ _PIXELS(RGB_WHITE, _RGB(0xFC, 0x48, 0x00)), + /* 0b11110100 */ _PIXELS(RGB_WHITE, _RGB(0x48, 0x48, 0x48)), + /* 0b11110101 */ _PIXELS(RGB_WHITE, _RGB(0x48, 0x48, 0x48)), + /* 0b11110110 */ _PIXELS(RGB_WHITE, _RGB(0xD8, 0xD8, 0x00)), + /* 0b11110111 */ _PIXELS(RGB_WHITE, _RGB(0xD8, 0xD8, 0x00)), + /* 0b11111000 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111001 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111010 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111011 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + + // Odd byte pixels + /* 0b00000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000100 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000101 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000110 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00001000 */ _PIXELS(RGB_BLACK, _RGB(0x24, 0x24, 0x00)), + /* 0b00001001 */ _PIXELS(RGB_BLACK, _RGB(0x00, 0xB4, 0x24)), + /* 0b00001010 */ _PIXELS(RGB_BLACK, _RGB(0x90, 0x90, 0x90)), + /* 0b00001011 */ _PIXELS(RGB_BLACK, _RGB(0x90, 0xD8, 0x90)), + /* 0b00001100 */ _PIXELS(RGB_BLACK, _RGB(0xFC, 0x48, 0x00)), + /* 0b00001101 */ _PIXELS(RGB_BLACK, _RGB(0xD8, 0xB4, 0x00)), + /* 0b00001110 */ _PIXELS(RGB_BLACK, _RGB(0xFC, 0x6C, 0xD8)), + /* 0b00001111 */ _PIXELS(RGB_BLACK, RGB_WHITE), + /* 0b00010000 */ _PIXELS(RGB_DGREEN, RGB_BLACK), + /* 0b00010001 */ _PIXELS(RGB_DGREEN, RGB_DGREEN), + /* 0b00010010 */ _PIXELS(_RGB(0x00, 0x6C, 0xB4), _RGB(0x00, 0x90, 0xD8)), + /* 0b00010011 */ _PIXELS(_RGB(0x00, 0x6C, 0xB4), _RGB(0x00, 0x90, 0xD8)), + /* 0b00010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b00010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b00010110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xB4), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00010111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xB4), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00011000 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b00011001 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b00011010 */ _PIXELS(_RGB(0x6C, 0x90, 0xFC), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00011011 */ _PIXELS(_RGB(0x6C, 0x90, 0xFC), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00011100 */ _PIXELS(_RGB(0xB4, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00011101 */ _PIXELS(_RGB(0xB4, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b00011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b00100010 */ _PIXELS(RGB_DBLUE, RGB_DBLUE), + /* 0b00100011 */ _PIXELS(RGB_DBLUE, _RGB(0x00, 0x6C, 0xD8)), + /* 0b00100100 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00100101 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b00100110 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b00100111 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b00101000 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b00101001 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0xD8, 0xB4)), + /* 0b00101010 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b00101011 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0xD8, 0xB4)), + /* 0b00101100 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b00101101 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b00101110 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b00101111 */ _PIXELS(RGB_PINK, RGB_WHITE), + /* 0b00110000 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), RGB_BLACK), + /* 0b00110001 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b00110010 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b00110011 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b00110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00110110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00110111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b00111000 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00111001 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00111010 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00111011 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b00111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b00111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01000100 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), _RGB(0x6C, 0x00, 0x6C)), + /* 0b01000101 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), _RGB(0x48, 0x24, 0x48)), + /* 0b01000110 */ _PIXELS(_RGB(0x90, 0x24, 0xD8), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01000111 */ _PIXELS(_RGB(0x90, 0x24, 0xD8), _RGB(0x6C, 0x48, 0xFC)), + /* 0b01001000 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b01001001 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xD8, 0xB4, 0x00)), + /* 0b01001010 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0x6C, 0xD8)), + /* 0b01001011 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0xD8, 0xD8)), + /* 0b01001100 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b01001101 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xD8, 0xB4, 0x00)), + /* 0b01001110 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0x6C, 0xD8)), + /* 0b01001111 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), RGB_WHITE), + /* 0b01010000 */ _PIXELS(_RGB(0x48, 0x48, 0x48), RGB_BLACK), + /* 0b01010001 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b01010010 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01010011 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b01010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b01010110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01010111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01011000 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01011001 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01011010 */ _PIXELS(_RGB(0xFC, 0xFC, 0xB4), _RGB(0xFC, 0xFC, 0xD8)), + /* 0b01011011 */ _PIXELS(_RGB(0xFC, 0xFC, 0xB4), _RGB(0xFC, 0xFC, 0xD8)), + /* 0b01011100 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01011101 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b01011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b01100010 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01100011 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b01100100 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01100101 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b01100110 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b01100111 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b01101000 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b01101001 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b01101010 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b01101011 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b01101100 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b01101101 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b01101110 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b01101111 */ _PIXELS(RGB_PINK, RGB_WHITE), + /* 0b01110000 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), RGB_BLACK), + /* 0b01110001 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110010 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110011 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01110111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b01111000 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111001 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111010 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111011 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b01111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000100 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000101 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000110 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10001000 */ _PIXELS(_RGB(0x24, 0x24, 0x00), _RGB(0x24, 0x24, 0x00)), + /* 0b10001001 */ _PIXELS(_RGB(0x24, 0x24, 0x00), _RGB(0x00, 0xB4, 0x24)), + /* 0b10001010 */ _PIXELS(_RGB(0x6C, 0x6C, 0x6C), _RGB(0x90, 0x90, 0x90)), + /* 0b10001011 */ _PIXELS(_RGB(0x6C, 0x6C, 0x6C), _RGB(0x90, 0xD8, 0x90)), + /* 0b10001100 */ _PIXELS(_RGB(0xD8, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b10001101 */ _PIXELS(_RGB(0xD8, 0x48, 0x00), _RGB(0xD8, 0xB4, 0x00)), + /* 0b10001110 */ _PIXELS(_RGB(0xD8, 0x6C, 0xB4), _RGB(0xFC, 0x6C, 0xD8)), + /* 0b10001111 */ _PIXELS(_RGB(0xD8, 0x6C, 0xB4), RGB_WHITE), + /* 0b10010000 */ _PIXELS(RGB_DGREEN, RGB_BLACK), + /* 0b10010001 */ _PIXELS(RGB_DGREEN, RGB_DGREEN), + /* 0b10010010 */ _PIXELS(_RGB(0x00, 0x6C, 0xB4), _RGB(0x00, 0x90, 0xD8)), + /* 0b10010011 */ _PIXELS(_RGB(0x00, 0x6C, 0xB4), _RGB(0x00, 0x90, 0xD8)), + /* 0b10010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b10010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b10010110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xB4), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10010111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xB4), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10011000 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b10011001 */ _PIXELS(_RGB(0x00, 0xD8, 0x24), _RGB(0x00, 0xD8, 0x24)), + /* 0b10011010 */ _PIXELS(_RGB(0x6C, 0x90, 0xFC), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10011011 */ _PIXELS(_RGB(0x6C, 0x90, 0xFC), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10011100 */ _PIXELS(_RGB(0xB4, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10011101 */ _PIXELS(_RGB(0xB4, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b10011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b10100010 */ _PIXELS(RGB_DBLUE, RGB_DBLUE), + /* 0b10100011 */ _PIXELS(RGB_DBLUE, _RGB(0x00, 0x6C, 0xD8)), + /* 0b10100100 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10100101 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b10100110 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b10100111 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b10101000 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b10101001 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0xD8, 0xB4)), + /* 0b10101010 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0x90, 0x90)), + /* 0b10101011 */ _PIXELS(_RGB(0x90, 0x90, 0x90), _RGB(0x90, 0xD8, 0xB4)), + /* 0b10101100 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b10101101 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b10101110 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b10101111 */ _PIXELS(RGB_PINK, RGB_WHITE), + /* 0b10110000 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), RGB_BLACK), + /* 0b10110001 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b10110010 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b10110011 */ _PIXELS(_RGB(0x00, 0x90, 0xFC), _RGB(0x00, 0x90, 0xFC)), + /* 0b10110100 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10110101 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10110110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10110111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xFC), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b10111000 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10111001 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10111010 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10111011 */ _PIXELS(_RGB(0x90, 0xFC, 0xB4), _RGB(0x90, 0xFC, 0xB4)), + /* 0b10111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b10111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11000000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000010 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000011 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11000100 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), _RGB(0x6C, 0x00, 0x6C)), + /* 0b11000101 */ _PIXELS(_RGB(0x6C, 0x00, 0x6C), _RGB(0x48, 0x24, 0x48)), + /* 0b11000110 */ _PIXELS(_RGB(0x90, 0x24, 0xD8), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11000111 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0x90, 0x24, 0xD8), _RGB(0x6C, 0x48, 0xFC)), + /* 0b11001000 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b11001001 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xD8, 0xB4, 0x00)), + /* 0b11001010 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0x6C, 0xD8)), + /* 0b11001011 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0xD8, 0xD8)), + /* 0b11001100 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xFC, 0x48, 0x00)), + /* 0b11001101 */ _PIXELS(_RGB(0xFC, 0x48, 0x00), _RGB(0xD8, 0xB4, 0x00)), + /* 0b11001110 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), _RGB(0xFC, 0x6C, 0xD8)), + /* 0b11001111 */ _PIXELS(_RGB(0xFC, 0x6C, 0xB4), RGB_WHITE), + /* 0b11010000 */ _PIXELS(_RGB(0x48, 0x48, 0x48), RGB_BLACK), + /* 0b11010001 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b11010010 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11010011 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11010100 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b11010101 */ _PIXELS(_RGB(0x48, 0x48, 0x48), _RGB(0x48, 0x48, 0x48)), + /* 0b11010110 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11010111 */ _PIXELS(_RGB(0x6C, 0x6C, 0xD8), _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11011000 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11011001 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11011010 */ _PIXELS(_RGB(0xFC, 0xFC, 0xB4), _RGB(0xFC, 0xFC, 0xD8)), + /* 0b11011011 */ _PIXELS(_RGB(0xFC, 0xFC, 0xB4), _RGB(0xFC, 0xFC, 0xD8)), + /* 0b11011100 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11011101 */ _PIXELS(_RGB(0xD8, 0xD8, 0x00), _RGB(0xD8, 0xD8, 0x00)), + /* 0b11011110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11011111 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11100000 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11100001 */ _PIXELS(RGB_BLACK, RGB_BLACK), + /* 0b11100010 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11100011 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b11100100 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11100101 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b11100110 */ _PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0xB4, 0x24, 0xFC)), + /* 0b11100111 */ _PIXELS(RGB_BLACK, RGB_BLACK), //_PIXELS(_RGB(0xB4, 0x24, 0xFC), _RGB(0x6C, 0x48, 0xFC)), + /* 0b11101000 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b11101001 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b11101010 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b11101011 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b11101100 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b11101101 */ _PIXELS(RGB_PINK, _RGB(0xFC, 0xD8, 0xFC)), + /* 0b11101110 */ _PIXELS(RGB_PINK, RGB_PINK), + /* 0b11101111 */ _PIXELS(RGB_PINK, RGB_WHITE), + /* 0b11110000 */ _PIXELS(RGB_WHITE, RGB_BLACK), + /* 0b11110001 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110010 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110011 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110100 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110101 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110110 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11110111 */ _PIXELS(RGB_WHITE, _RGB(0x6C, 0x6C, 0xFC)), + /* 0b11111000 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111001 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111010 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111011 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111100 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111101 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111110 */ _PIXELS(RGB_WHITE, RGB_WHITE), + /* 0b11111111 */ _PIXELS(RGB_WHITE, RGB_WHITE), +}; +#undef _PIXELS diff --git a/vga/hires_dot_patterns.h b/vga/hires_dot_patterns.h new file mode 100644 index 0000000..5ec8e7b --- /dev/null +++ b/vga/hires_dot_patterns.h @@ -0,0 +1,41 @@ +#pragma once + +#include "common/config.h" +#include + +// Mapping of a hires video byte to 14 half-pixel dots. +// Bits are displayed from MSB to LSB. +static uint16_t DELAYED_COPY_DATA(hires_dot_patterns)[256] = { + 0x0000,0x3000,0x0c00,0x3c00,0x0300,0x3300,0x0f00,0x3f00, + 0x00c0,0x30c0,0x0cc0,0x3cc0,0x03c0,0x33c0,0x0fc0,0x3fc0, + 0x0030,0x3030,0x0c30,0x3c30,0x0330,0x3330,0x0f30,0x3f30, + 0x00f0,0x30f0,0x0cf0,0x3cf0,0x03f0,0x33f0,0x0ff0,0x3ff0, + 0x000c,0x300c,0x0c0c,0x3c0c,0x030c,0x330c,0x0f0c,0x3f0c, + 0x00cc,0x30cc,0x0ccc,0x3ccc,0x03cc,0x33cc,0x0fcc,0x3fcc, + 0x003c,0x303c,0x0c3c,0x3c3c,0x033c,0x333c,0x0f3c,0x3f3c, + 0x00fc,0x30fc,0x0cfc,0x3cfc,0x03fc,0x33fc,0x0ffc,0x3ffc, + 0x0003,0x3003,0x0c03,0x3c03,0x0303,0x3303,0x0f03,0x3f03, + 0x00c3,0x30c3,0x0cc3,0x3cc3,0x03c3,0x33c3,0x0fc3,0x3fc3, + 0x0033,0x3033,0x0c33,0x3c33,0x0333,0x3333,0x0f33,0x3f33, + 0x00f3,0x30f3,0x0cf3,0x3cf3,0x03f3,0x33f3,0x0ff3,0x3ff3, + 0x000f,0x300f,0x0c0f,0x3c0f,0x030f,0x330f,0x0f0f,0x3f0f, + 0x00cf,0x30cf,0x0ccf,0x3ccf,0x03cf,0x33cf,0x0fcf,0x3fcf, + 0x003f,0x303f,0x0c3f,0x3c3f,0x033f,0x333f,0x0f3f,0x3f3f, + 0x00ff,0x30ff,0x0cff,0x3cff,0x03ff,0x33ff,0x0fff,0x3fff, + 0x0000,0x1800,0x0600,0x1e00,0x0180,0x1980,0x0780,0x1f80, + 0x0060,0x1860,0x0660,0x1e60,0x01e0,0x19e0,0x07e0,0x1fe0, + 0x0018,0x1818,0x0618,0x1e18,0x0198,0x1998,0x0798,0x1f98, + 0x0078,0x1878,0x0678,0x1e78,0x01f8,0x19f8,0x07f8,0x1ff8, + 0x0006,0x1806,0x0606,0x1e06,0x0186,0x1986,0x0786,0x1f86, + 0x0066,0x1866,0x0666,0x1e66,0x01e6,0x19e6,0x07e6,0x1fe6, + 0x001e,0x181e,0x061e,0x1e1e,0x019e,0x199e,0x079e,0x1f9e, + 0x007e,0x187e,0x067e,0x1e7e,0x01fe,0x19fe,0x07fe,0x1ffe, + 0x0001,0x1801,0x0601,0x1e01,0x0181,0x1981,0x0781,0x1f81, + 0x0061,0x1861,0x0661,0x1e61,0x01e1,0x19e1,0x07e1,0x1fe1, + 0x0019,0x1819,0x0619,0x1e19,0x0199,0x1999,0x0799,0x1f99, + 0x0079,0x1879,0x0679,0x1e79,0x01f9,0x19f9,0x07f9,0x1ff9, + 0x0007,0x1807,0x0607,0x1e07,0x0187,0x1987,0x0787,0x1f87, + 0x0067,0x1867,0x0667,0x1e67,0x01e7,0x19e7,0x07e7,0x1fe7, + 0x001f,0x181f,0x061f,0x1e1f,0x019f,0x199f,0x079f,0x1f9f, + 0x007f,0x187f,0x067f,0x1e7f,0x01ff,0x19ff,0x07ff,0x1fff, +}; diff --git a/vga/logo.h b/vga/logo.h new file mode 100644 index 0000000..8b38bae --- /dev/null +++ b/vga/logo.h @@ -0,0 +1,74 @@ +#pragma once + +#include "common/config.h" + +//uint8_t DELAYED_COPY_DATA(PicoPalLogo)[2112] = { + +uint8_t __attribute__((section(".delayed_data."))) PicoPalLogo[2112] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0xF4, 0x44, 0xF4, 0x44, 0xFF, 0xF4, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0xF4, 0x44, 0xF4, 0x4F, 0x44, 0x4F, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0xFF, 0xFF, 0xF4, 0x44, 0x44, 0x4F, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0xF4, 0x44, 0xF4, 0x44, 0x4F, 0xF4, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0xF4, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0x4F, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0x44, 0xF4, 0x44, 0x4F, 0xFF, 0xFF, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xD4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4D, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xD4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xD4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xFF, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0xF4, 0xF4, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xFF, 0xFF, 0x44, 0x44, 0xFF, 0xF4, 0x44, 0x4F, 0x44, 0x44, 0xFF, 0xF4, 0x44, 0x4F, 0xFF, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x4F, 0xFF, 0xFF, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0xFF, 0xFF, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xF4, 0x44, 0xF4, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0xFF, 0xF4, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0xFF, 0xFF, 0x44, 0xFF, 0xF4, 0x44, 0xFF, 0xF4, 0x44, 0x44, 0x44, 0xF4, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4F, 0xFF, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0xFF, 0xF4, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4F, 0xFF, 0xF4, 0x44, 0x44, 0x44, 0x44, 0x4F, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x44, 0x44, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0xFF, 0x44, 0x44, 0xFF, 0xFF, 0x44, 0x4F, 0xFF, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x4F, 0xFF, 0x44, 0x44, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0xFF, 0xF4, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x44, 0xF4, 0x44, 0xF4, 0x4F, 0xFF, 0xF4, 0x44, 0x44, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x44, 0xF4, 0x44, 0xF4, 0x4F, 0x44, 0x44, 0x44, 0x4F, 0xFF, 0xF4, 0x44, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x4F, 0x44, 0x4F, 0x44, 0x44, 0x44, 0xF4, 0x44, 0xF4, 0x4F, 0x44, 0x44, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0xF4, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x4F, 0x44, 0x44, 0x44, 0xFF, 0xF4, 0x44, 0xFF, 0xFF, 0x44, 0x4F, 0xFF, 0x44, 0x4F, 0x44, 0x44, 0x44, 0x4F, 0xFF, 0xF4, 0x4F, 0xFF, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0xDD, 0x44, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/vga/render.c b/vga/render.c new file mode 100644 index 0000000..2497e7f --- /dev/null +++ b/vga/render.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include "common/config.h" +#include "common/flash.h" +#include "common/dmacopy.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +uint16_t text_fore; +uint16_t text_back; +uint16_t text_border; + +compat_t machinefont = MACHINE_INVALID; +bool userfont = false; + +uint16_t DELAYED_COPY_DATA(mono_colors)[14] = { + _RGB(0x00, 0x00, 0x00), _RGB(0xFF, 0xFF, 0xFF), // White Normal + _RGB(0xFF, 0xFF, 0xFF), _RGB(0x00, 0x00, 0x00), // White Inverse + _RGB(0x00, 0x00, 0x00), _RGB(0xFE, 0x7F, 0x00), // Amber Normal + _RGB(0xFE, 0x7F, 0x00), _RGB(0x00, 0x00, 0x00), // Amber Inverse + _RGB(0x00, 0x00, 0x00), _RGB(0x00, 0xBF, 0x00), // Green Normal + _RGB(0x00, 0xBF, 0x00), _RGB(0x00, 0x00, 0x00), // Green Inverse + _RGB(0x35, 0x28, 0x79), _RGB(0x6C, 0x5E, 0xB5), // Commodore +}; + +// Initialize the character generator ROM +static void DELAYED_COPY_CODE(switch_font)() { + if(userfont) { + return; + } else if(romx_changed) { + memcpy32(character_rom, (void*)FLASH_FONT(romx_textbank), 4096); + } else if(current_machine != machinefont) { + switch(current_machine) { + default: + case MACHINE_II: + memcpy32(character_rom, (void*)FLASH_FONT_APPLE_II, 4096); + break; + case MACHINE_IIE: + memcpy32(character_rom, (void*)FLASH_FONT_APPLE_IIE, 4096); + break; + case MACHINE_IIGS: + memcpy32(character_rom, (void*)FLASH_FONT_APPLE_IIGS, 4096); + break; + case MACHINE_PRAVETZ: + memcpy32(character_rom, (void*)FLASH_FONT_PRAVETZ, 4096); + break; + } + machinefont = current_machine; + } +} + +uint16_t status_timeout = 900; +uint8_t status_line[81]; + +void DELAYED_COPY_CODE(update_status_right)(const char *str) { + uint i, len; + + if(str != NULL) { + len = strlen(str); + } else { + len = 0; + } + + if(len < 80) { + memset(status_line, ' ', 80 - len); + } else { + len = 80; + } + + for(i = 0; i < len; i++) { + status_line[(80-len) + i] = str[i]; + } + + status_timeout = 900; +} + +void DELAYED_COPY_CODE(update_status_left)(const char *str) { + uint i, len; + + if(str != NULL) { + len = strlen(str); + } else { + len = 0; + } + + if(len < 80) { + memset(status_line + len, ' ', 80 - len); + } else { + len = 80; + } + + for(i = 0; i < len; i++) { + status_line[i] = str[i]; + } + + status_timeout = 900; +} + +void DELAYED_COPY_CODE(render_init)() { + int i; + + switch_font(); + + if((soft_switches & SOFTSW_MODE_MASK) == 0) + internal_flags |= IFLAGS_TEST; + + apple_tbcolor = 0xf0; + apple_border = 0x00; + + terminal_tbcolor = 0xf0; + terminal_border = 0x00; + + memcpy(terminal_character_rom, (void*)FLASH_FONT_APPLE_IIE, 4096); + memset(status_line, 0, sizeof(status_line)); + + render_test_init(); +} + +// Skip lines to center vertically or blank the screen +void DELAYED_COPY_CODE(render_border)(uint count) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + while(sl_pos < VGA_WIDTH/16) { + sl->data[sl_pos] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 8 pixels per word + sl_pos++; + } + + sl->length = sl_pos; + sl->repeat_count = count - 1; + vga_submit_scanline(sl); +} + +uint32_t screentimeout = 0; +uint32_t testdone = 0; + +void DELAYED_COPY_CODE(render_loop)() { + for(;;) { + config_handler(); + +#if 0 + if((busactive == 0) && (screentimeout > (15 * 60))) { + vga_prepare_frame(); + render_border(VGA_HEIGHT); + memset(status_line, 0, sizeof(status_line)); + status_timeout = 0; + vga_dpms_sleep(); + while(busactive == 0); + vga_dpms_wake(); + } else { + if(busactive == 0) { + screentimeout++; + if(screentimeout == 5) { + update_status_right("Going to sleep..."); + } + } else { + if(screentimeout >= 5) { + // Clear the sleep mode message + memset(status_line, 0, sizeof(status_line)); + status_timeout = 0; + } + screentimeout = 0; + } + busactive = 0; +#endif + + if(romx_changed || (machinefont != current_machine)) { + switch_font(); + romx_changed = 0; + machinefont = current_machine; + } + + update_text_flasher(); + + if(!(mono_palette & 0x8)) { + if((current_machine == MACHINE_IIGS) && !(soft_switches & SOFTSW_MONOCHROME)) { + text_fore = lores_palette[APPLE_FORE]; + text_back = lores_palette[APPLE_BACK]; + text_border = lores_palette[APPLE_BORDER]; + } else { + text_fore = mono_colors[1]; + text_back = mono_colors[0]; + text_border = mono_colors[0]; + } + } else if(mono_palette == 0xF) { + text_fore = lores_palette[TERMINAL_FORE]; + text_back = lores_palette[TERMINAL_BACK]; + text_border = lores_palette[TERMINAL_BORDER]; + } else { + int palette = mono_palette & 0x7; + text_fore = mono_colors[palette*2+1]; + text_back = mono_colors[palette*2]; + text_border = (palette == 0x6) ? text_fore : text_back; + } + + if(internal_flags & IFLAGS_TEST) { + render_testpattern(); + // Automatically dismiss the test pattern when the Apple II is seen. + if(((soft_switches & SOFTSW_MODE_MASK) != 0) && (testdone == 0)) { + internal_flags &= ~IFLAGS_TEST; + testdone = 1; + render_about_init(); + } + } else if(soft_switches & SOFTSW_TERMINAL) { + render_terminal(); +#if defined(ANALOG_GS) || defined(OVERCLOCKED) + } else if(soft_switches & SOFTSW_SHR) { + render_shr(); +#endif + } else { + vga_prepare_frame(); + + render_border(24); + if(status_line[0] != 0) { + render_status_line(); + render_border(16); + } else { + render_border(32); + } + + switch(soft_switches & SOFTSW_MODE_MASK) { + case 0: + if(soft_switches & SOFTSW_DGR) { + render_dgr(); + } else { + render_lores(); + } + break; + case SOFTSW_MIX_MODE: + if((soft_switches & (SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80COL | SOFTSW_DGR)) { + render_mixed_dgr(); + } else { + render_mixed_lores(); + } + break; + case SOFTSW_HIRES_MODE: + if(soft_switches & SOFTSW_DGR) { + render_dhgr(); + } else { + render_hires(); + } + break; + case SOFTSW_HIRES_MODE|SOFTSW_MIX_MODE: + if((soft_switches & (SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80COL | SOFTSW_DGR)) { + render_mixed_dhgr(); + } else { + render_mixed_hires(); + } + break; + default: + render_text(); + break; + } + + render_border(48); + } +#if 0 + } +#endif + } +} diff --git a/vga/render.h b/vga/render.h new file mode 100644 index 0000000..e461e70 --- /dev/null +++ b/vga/render.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +// Uncomment to enable test patter generator +#define RENDER_TEST_PATTERN + +extern uint16_t lores_palette[16]; +extern uint16_t text_fore, text_back, text_border; +extern uint8_t status_line[81]; + +extern void update_status_left(const char *str); +extern void update_status_right(const char *str); + +extern void render_init(); +extern void render_loop(); + +extern void render_testpattern(); +extern void render_test_init(); +extern void render_about_init(); +extern void render_test_sleep(); + +extern void update_text_flasher(); +extern void render_text(); +extern void render_text40_line(bool p2, unsigned int line); +extern void render_text80_line(bool p2, unsigned int line); +extern void render_status_line(); + +extern void render_terminal(); +extern void render_terminal_line(unsigned int line); + +extern void render_border(uint count); + +extern void render_lores(); +extern void render_mixed_lores(); + +extern void render_hires(); +extern void render_mixed_hires(); + +extern void render_dhgr(); +extern void render_mixed_dhgr(); + +extern void render_dgr(); +extern void render_mixed_dgr(); + +extern void render_shr(); + +extern volatile uint_fast32_t text_flasher_mask; + +extern void vga_init(); +extern void vga_deinit(); + +#ifdef ANALOG_GS +#define _RGB(r, g, b) ( \ + (((((uint)(r) * 256 / 18) + 256) / 256) << 8) | \ + (((((uint)(g) * 256 / 18) + 256) / 256) << 4) | \ + ((((uint)(b) * 256 / 18) + 256) / 256) \ +) +#else +#define _RGB(r, g, b) ( \ + (((((uint)(r) * 256 / 36) + 128) / 256) << 6) | \ + (((((uint)(g) * 256 / 36) + 128) / 256) << 3) | \ + ((((uint)(b) * 256 / 36) + 128) / 256) \ +) +#endif + +#define RGB_BLACK _RGB(0x00,0x00,0x00) +#define RGB_MAGENTA _RGB(0x6c,0x00,0x6c) +#define RGB_DBLUE _RGB(0x00,0x00,0xb4) +#define RGB_HVIOLET _RGB(0xb4,0x24,0xfc) +#define RGB_DGREEN _RGB(0x00,0x48,0x00) +#define RGB_DGRAY _RGB(0x48,0x48,0x48) +#define RGB_HBLUE _RGB(0x00,0x90,0xfc) +#define RGB_LBLUE _RGB(0x6c,0x6c,0xfc) +#define RGB_BROWN _RGB(0x24,0x24,0x00) +#define RGB_HORANGE _RGB(0xfc,0x48,0x00) +#define RGB_LGRAY _RGB(0x90,0x90,0x90) +#define RGB_PINK _RGB(0xfc,0x6c,0xfc) +#define RGB_HGREEN _RGB(0x00,0xd8,0x24) +#define RGB_YELLOW _RGB(0xd8,0xd8,0x00) +#define RGB_AQUA _RGB(0x90,0xfc,0xb4) +#define RGB_WHITE _RGB(0xff,0xff,0xff) diff --git a/vga/render_80col.c b/vga/render_80col.c new file mode 100644 index 0000000..9cdc8be --- /dev/null +++ b/vga/render_80col.c @@ -0,0 +1,72 @@ +#include +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +static inline uint_fast8_t char_terminal_bits(uint_fast8_t ch, uint_fast8_t glyph_line) { + uint_fast8_t bits = terminal_character_rom[((uint_fast16_t)ch << 3) | glyph_line]; + if(ch & 0x80) { + // normal character + return bits & 0x7f; + } + + if((bits & 0x80) == 0) { + // inverse character + return bits ^ 0x7f; + } else { + // flashing character + return (bits ^ text_flasher_mask) & 0x7f; + } +} + + +void DELAYED_COPY_CODE(render_terminal)() { + for(int line=0; line < 24; line++) { + render_terminal_line(line); + } +} + + +void DELAYED_COPY_CODE(render_terminal_line)(unsigned int line) { + const uint8_t *page = (const uint8_t *)terminal_memory; + const uint8_t *line_buf = page + (line * 80); + + for(uint glyph_line=0; glyph_line < 8; glyph_line++) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + for(uint col=0; col < 80; ) { + // Grab 14 pixels from the next two characters + uint32_t bits_a = char_terminal_bits(line_buf[col], glyph_line); + col++; + uint32_t bits_b = char_terminal_bits(line_buf[col], glyph_line); + col++; + + uint32_t bits = (bits_a << 7) | bits_b; + + // Translate each pair of bits into a pair of pixels + for(int i=0; i < 7; i++) { + uint32_t pixeldata = (bits & 0x2000) ? (text_fore) : (text_back); + pixeldata |= (bits & 0x1000) ? + (((uint32_t)text_fore) << 16) : + (((uint32_t)text_back) << 16); + bits <<= 2; + + sl->data[sl_pos] = pixeldata; + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); + } +} + diff --git a/vga/render_dgr.c b/vga/render_dgr.c new file mode 100644 index 0000000..5ef9a38 --- /dev/null +++ b/vga/render_dgr.c @@ -0,0 +1,102 @@ +#include +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2)) +#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2) + +extern uint16_t lores_palette[16]; +extern uint16_t lores_dot_pattern[16]; + +static void render_dgr_line(bool p2, uint line); + +void DELAYED_COPY_CODE(render_dgr)() { + for(uint line=0; line < 24; line++) { + render_dgr_line(PAGE2SEL, line); + } +} + + +void DELAYED_COPY_CODE(render_mixed_dgr)() { + for(uint line=0; line < 20; line++) { + render_dgr_line(PAGE2SEL, line); + } + + for(uint line=20; line < 24; line++) { + if(soft_switches & SOFTSW_80COL) { + render_text80_line(PAGE2SEL, line); + } else { + render_text40_line(PAGE2SEL, line); + } + } +} + + +static void DELAYED_COPY_CODE(render_dgr_line)(bool p2, uint line) { + // Construct two scanlines for the two different colored cells at the same time + struct vga_scanline *sl1 = vga_prepare_scanline(); + struct vga_scanline *sl2 = vga_prepare_scanline(); + uint sl_pos = 0; + uint i, j; + uint32_t color1, color2, color3, color4; + + const uint8_t *line_bufa = (const uint8_t *)((p2 ? text_p2 : text_p1) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40)); + const uint8_t *line_bufb = (const uint8_t *)((p2 ? text_p4 : text_p3) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40)); + + // Pad 40 pixels on the left to center horizontally + sl1->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl2->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl_pos++; + + if((soft_switches & SOFTSW_MONOCHROME) || (mono_palette & 0x8)) { + for(i = 0; i < 40; i++) { + color1 = lores_dot_pattern[line_bufb[i] & 0xf] << 21; + color2 = lores_dot_pattern[(line_bufb[i] >> 4) & 0xf] << 21; + color1 |= lores_dot_pattern[line_bufa[i] & 0xf] << 7; + color2 |= lores_dot_pattern[(line_bufa[i] >> 4) & 0xf] << 7; + + for(j = 0; j < 7; j++) { + uint32_t pixeldata; + + pixeldata = (color1 & 0x8000000) ? (text_fore) : (text_back); + pixeldata |= (color1 & 0x4000000) ? ((text_fore) << 16) : ((text_back) << 16); + color1 <<= 2; + sl1->data[sl_pos] = pixeldata; + + pixeldata = (color2 & 0x8000000) ? (text_fore) : (text_back); + pixeldata |= (color2 & 0x4000000) ? ((text_fore) << 16) : ((text_back) << 16); + sl2->data[sl_pos] = pixeldata; + color2 <<= 2; + + sl_pos++; + } + } + } else { + for(i=0; i < 40; i++) { + color1 = lores_palette[line_bufb[i] & 0xf]; + color2 = lores_palette[(line_bufb[i] >> 4) & 0xf]; + color3 = lores_palette[line_bufa[i] & 0xf]; + color4 = lores_palette[(line_bufa[i] >> 4) & 0xf]; + + // Each double lores pixel is 7 double hires pixels, or 7 VGA pixels wide + sl1->data[sl_pos] = (color1|THEN_EXTEND_6) | ((color3|THEN_EXTEND_6) << 16); + sl2->data[sl_pos] = (color2|THEN_EXTEND_6) | ((color4|THEN_EXTEND_6) << 16); + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl1->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl2->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl_pos++; + + sl1->length = sl_pos; + sl1->repeat_count = 7; + vga_submit_scanline(sl1); + + sl2->length = sl_pos; + sl2->repeat_count = 7; + vga_submit_scanline(sl2); +} diff --git a/vga/render_dhgr.c b/vga/render_dhgr.c new file mode 100644 index 0000000..4a4b26d --- /dev/null +++ b/vga/render_dhgr.c @@ -0,0 +1,132 @@ +#include +#include "hires_color_patterns.h" +#include "hires_dot_patterns.h" +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +static void render_dhgr_line(bool p2, uint line); + +uint16_t DELAYED_COPY_DATA(dhgr_palette)[16] = { + RGB_BLACK, RGB_DBLUE, RGB_DGREEN, RGB_HBLUE, + RGB_BROWN, RGB_LGRAY, RGB_HGREEN, RGB_AQUA, + RGB_MAGENTA, RGB_HVIOLET, RGB_DGRAY, RGB_LBLUE, + RGB_HORANGE, RGB_PINK, RGB_YELLOW, RGB_WHITE +}; + +//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2)) +#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2) + + +static inline uint dhgr_line_to_mem_offset(uint line) { + return ((line & 0x07) << 10) | ((line & 0x38) << 4) | (((line & 0xc0) >> 6) * 40); +} + + +void DELAYED_COPY_CODE(render_dhgr)() { + for(uint line=0; line < 192; line++) { + render_dhgr_line(PAGE2SEL, line); + } +} + + +void DELAYED_COPY_CODE(render_mixed_dhgr)() { + for(uint line=0; line < 160; line++) { + render_dhgr_line(PAGE2SEL, line); + } + + for(uint line=20; line < 24; line++) { + if(soft_switches & SOFTSW_80COL) { + render_text80_line(PAGE2SEL, line); + } else { + render_text40_line(PAGE2SEL, line); + } + } +} + + +static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + uint i; + + const uint8_t *line_mema = (const uint8_t *)((p2 ? hgr_p2 : hgr_p1) + dhgr_line_to_mem_offset(line)); + const uint8_t *line_memb = (const uint8_t *)((p2 ? hgr_p4 : hgr_p3) + dhgr_line_to_mem_offset(line)); + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + // DHGR is weird. Nuff said. + uint32_t dots = 0; + uint32_t pixeldata; + int j; + + i = 0; + while(i < 40) { + // Load in the next 28 subpixels + dots = (line_memb[i] & 0x7f) << 0; + dots |= (line_mema[i] & 0x7f) << 7; + i++; + dots |= (line_memb[i] & 0x7f) << 14; + dots |= (line_mema[i] & 0x7f) << 21; + i++; + + if((soft_switches & SOFTSW_MONOCHROME) || (mono_palette & 0x8)) { + // Consume 6 pixels (24 subpixel bits) + for(j = 0; j < 12; j++) { + pixeldata = ((dots & 1) ? (text_fore) : (text_back)); + dots >>= 1; + pixeldata |= (((dots & 1) ? (text_fore) : (text_back))) << 16; + dots >>= 1; + sl->data[sl_pos++] = pixeldata; + } + } else { + // Consume 6 pixels (24 subpixel bits) + for(j = 0; j < 3; j++) { + pixeldata = (dhgr_palette[dots & 0xf] | THEN_EXTEND_3); + dots >>= 4; + pixeldata |= (dhgr_palette[dots & 0xf] | THEN_EXTEND_3) << 16; + dots >>= 4; + sl->data[sl_pos++] = pixeldata; + } + } + + // 4 subpixels roll over to the next block + // Load in the next 28 subpixels + dots |= (line_memb[i] & 0x7f) << 4; + dots |= (line_mema[i] & 0x7f) << 11; + i++; + dots |= (line_memb[i] & 0x7f) << 18; + dots |= (line_mema[i] & 0x7f) << 25; + i++; + + if(soft_switches & SOFTSW_MONOCHROME) { + // Consume 8 pixels (32 subpixel bits) + for(j = 0; j < 16; j++) { + pixeldata = ((dots & 1) ? (text_fore) : (text_back)); + dots >>= 1; + pixeldata |= (((dots & 1) ? (text_fore) : (text_back))) << 16; + dots >>= 1; + sl->data[sl_pos++] = pixeldata; + } + } else { + // Consume 8 pixels (32 subpixel bits) + for(j = 0; j < 4; j++) { + pixeldata = (dhgr_palette[dots & 0xf] | THEN_EXTEND_3); + dots >>= 4; + pixeldata |= (dhgr_palette[dots & 0xf] | THEN_EXTEND_3) << 16; + dots >>= 4; + sl->data[sl_pos++] = pixeldata; + } + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); +} + diff --git a/vga/render_hires.c b/vga/render_hires.c new file mode 100644 index 0000000..55ad8ad --- /dev/null +++ b/vga/render_hires.c @@ -0,0 +1,113 @@ +#include +#include "common/config.h" +#include "vga/hires_color_patterns.h" +#include "vga/hires_dot_patterns.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2)) +#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2) + +static void render_hires_line(bool p2, uint line); + +static inline uint hires_line_to_mem_offset(uint line) { + return ((line & 0x07) << 10) | ((line & 0x38) << 4) | (((line & 0xc0) >> 6) * 40); +} + + +void DELAYED_COPY_CODE(render_hires)() { + for(uint line=0; line < 192; line++) { + render_hires_line(PAGE2SEL, line); + } +} + + +void DELAYED_COPY_CODE(render_mixed_hires)() { + for(uint line=0; line < 160; line++) { + render_hires_line(PAGE2SEL, line); + } + + for(uint line=20; line < 24; line++) { + if(soft_switches & SOFTSW_80COL) { + render_text80_line(PAGE2SEL, line); + } else { + render_text40_line(PAGE2SEL, line); + } + } +} + + +static void DELAYED_COPY_CODE(render_hires_line)(bool p2, uint line) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + const uint8_t *line_mem = (const uint8_t *)((p2 ? hgr_p2 : hgr_p1) + hires_line_to_mem_offset(line)); + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + // Each hires byte contains 7 pixels which may be shifted right 1/2 a pixel. That is + // represented here by 14 'dots' to precisely describe the half-pixel positioning. + // + // For each pixel, inspect a window of 8 dots around the pixel to determine the + // precise dot locations and colors. + // + // Dots would be scanned out to the CRT from MSB to LSB (left to right here): + // + // previous | next + // dots | dots + // +-------------------+--------------------------------------------------+ + // dots: | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | ... | 14 | 13 | 12 | ... + // | | | | + // \______________|_________|______________/ + // | | + // \_________/ + // current + // pixel + uint32_t dots = 0; + uint oddness = 0; + uint i, j; + + // Load in the first 14 dots + dots |= (uint32_t)hires_dot_patterns[line_mem[0]] << 15; + + for(i=1; i < 41; i++) { + // Load in the next 14 dots + uint b = (i < 40) ? line_mem[i] : 0; + if(b & 0x80) { + // Extend the last bit from the previous byte + dots |= (dots & (1u << 15)) >> 1; + } + dots |= (uint32_t)hires_dot_patterns[b] << 1; + + if((soft_switches & SOFTSW_MONOCHROME) || (mono_palette & 0x8)) { + // Consume 14 dots + for(j = 0; j < 7; j++) { + uint32_t pixeldata = (dots & 0x40000000) ? (text_fore) : (text_back); + pixeldata |= (dots & 0x20000000) ? + ((text_fore) << 16) : + ((text_back) << 16); + dots <<= 2; + sl->data[sl_pos] = pixeldata; + sl_pos++; + } + } else { + // Consume 14 dots + for(uint j=0; j < 7; j++) { + uint dot_pattern = oddness | ((dots >> 24) & 0xff); + sl->data[sl_pos] = hires_color_patterns[dot_pattern]; + sl_pos++; + dots <<= 2; + oddness ^= 0x100; + } + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); +} diff --git a/vga/render_lores.c b/vga/render_lores.c new file mode 100644 index 0000000..2ac43c0 --- /dev/null +++ b/vga/render_lores.c @@ -0,0 +1,123 @@ +#include +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +uint16_t DELAYED_COPY_DATA(lores_dot_pattern)[16] = { + 0x0000, + 0x2222, + 0x1111, + 0x3333, + 0x0888, + 0x2AAA, + 0x1999, + 0x3BBB, + 0x0444, + 0x2666, + 0x1555, + 0x3777, + 0x0CCC, + 0x2EEE, + 0x1DDD, + 0x3FFF, +}; + +uint16_t DELAYED_COPY_DATA(lores_palette)[16] = { + RGB_BLACK, RGB_MAGENTA, RGB_DBLUE, RGB_HVIOLET, + RGB_DGREEN, RGB_DGRAY, RGB_HBLUE, RGB_LBLUE, + RGB_BROWN, RGB_HORANGE, RGB_LGRAY, RGB_PINK, + RGB_HGREEN, RGB_YELLOW, RGB_AQUA, RGB_WHITE +}; + + +static void render_lores_line(bool p2, uint line); + +//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2)) +#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2) + +void DELAYED_COPY_CODE(render_lores)() { + for(uint line=0; line < 24; line++) { + render_lores_line(PAGE2SEL, line); + } +} + + +void DELAYED_COPY_CODE(render_mixed_lores)() { + for(uint line=0; line < 20; line++) { + render_lores_line(PAGE2SEL, line); + } + + for(uint line=20; line < 24; line++) { + if(soft_switches & SOFTSW_80COL) { + render_text80_line(PAGE2SEL, line); + } else { + render_text40_line(PAGE2SEL, line); + } + } +} + + +static void DELAYED_COPY_CODE(render_lores_line)(bool p2, uint line) { + // Construct two scanlines for the two different colored cells at the same time + struct vga_scanline *sl1 = vga_prepare_scanline(); + struct vga_scanline *sl2 = vga_prepare_scanline(); + uint sl_pos = 0; + uint i, j; + uint32_t color1, color2; + + const uint8_t *line_buf = (const uint8_t *)((p2 ? text_p2 : text_p1) + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40)); + + // Pad 40 pixels on the left to center horizontally + sl1->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl2->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl_pos++; + + if((soft_switches & SOFTSW_MONOCHROME) || (mono_palette & 0x8)) { + for(i = 0; i < 40; i+=2) { + color1 = lores_dot_pattern[line_buf[i] & 0xf] << 14; + color2 = lores_dot_pattern[(line_buf[i] >> 4) & 0xf] << 14; + color1 |= lores_dot_pattern[line_buf[i+1] & 0xf]; + color2 |= lores_dot_pattern[(line_buf[i+1] >> 4) & 0xf]; + + for(j = 0; j < 14; j++) { + uint32_t pixeldata; + + pixeldata = (color1 & 0x8000000) ? (text_fore) : (text_back); + pixeldata |= (color1 & 0x4000000) ? ((text_fore) << 16) : ((text_back) << 16); + color1 <<= 2; + sl1->data[sl_pos] = pixeldata; + + pixeldata = (color2 & 0x8000000) ? (text_fore) : (text_back); + pixeldata |= (color2 & 0x4000000) ? ((text_fore) << 16) : ((text_back) << 16); + sl2->data[sl_pos] = pixeldata; + color2 <<= 2; + + sl_pos++; + } + } + } else { + for(i = 0; i < 40; i++) { + color1 = lores_palette[line_buf[i] & 0xf]; + color2 = lores_palette[(line_buf[i] >> 4) & 0xf]; + + // Each lores pixel is 7 hires pixels, or 14 VGA pixels wide + sl1->data[sl_pos] = (color1|THEN_EXTEND_6) | ((color1|THEN_EXTEND_6) << 16); + sl2->data[sl_pos] = (color2|THEN_EXTEND_6) | ((color2|THEN_EXTEND_6) << 16); + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl1->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl2->data[sl_pos] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + sl_pos++; + + sl1->length = sl_pos; + sl1->repeat_count = 7; + vga_submit_scanline(sl1); + + sl2->length = sl_pos; + sl2->repeat_count = 7; + vga_submit_scanline(sl2); +} diff --git a/vga/render_shr.c b/vga/render_shr.c new file mode 100644 index 0000000..9d6bb7d --- /dev/null +++ b/vga/render_shr.c @@ -0,0 +1,101 @@ +#include +#include "common/config.h" +#include "vga/hires_color_patterns.h" +#include "vga/hires_dot_patterns.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +static void render_shr_line(uint16_t line); + +#ifdef ANALOG_GS +#define rgb444(a) (a) +#else +static inline uint16_t rgb444(uint16_t a) { + return ((a & 0xe00) >> 3) | ((a & 0xe0) >> 2) | ((a & 0xe) >> 1); +} +#endif + +void DELAYED_COPY_CODE(render_shr)() { + vga_prepare_frame(); + + render_border(40); + + for(uint line=0; line < 200; line++) { + render_shr_line(line); + } + + render_border(40); +} + +static void DELAYED_COPY_CODE(render_shr_line)(uint16_t line) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + uint i; + + uint8_t control = private_memory[0x9D00 + line]; + uint32_t line_palette_offset = (control & 0xF) << 5; + + volatile uint16_t *shr_palette = (volatile uint16_t*)(private_memory + 0x9E00 + line_palette_offset); + volatile uint8_t *line_mem = (volatile uint8_t *)(private_memory + 0x2000 + (line * 160)); + + // SHR is weird. Nuff said. + uint32_t dots = 0; + uint32_t pixeldata; + uint16_t color_a = 0, color_b = 0; + int j; + + i = 0; + if(control & 0x80) { // 640 Pixels + while(i < 160) { + // Load in the next 2 pixels + dots = (line_mem[i++] & 0xff); + + color_a = ((dots >> 4) & 0xf); + color_b = ((dots >> 0) & 0xf); + + // Consume 2 pixels + pixeldata = rgb444(shr_palette[color_a]) | THEN_EXTEND_1; + pixeldata |= (rgb444(shr_palette[color_b]) | THEN_EXTEND_1) << 16; + sl->data[sl_pos++] = pixeldata; + } + } else if(control & 0x40) { // 320 Pixels w/ color fill + while(i < 160) { + // Load in the next 4 subpixels + dots = (line_mem[i++] & 0xff); + + // Consume 2 pixels + if(dots & 0xf0) { + color_a = ((dots >> 4) & 0xf); + } else { + color_a = color_b; + } + if(dots & 0x0f) { + color_b = ((dots >> 0) & 0xf); + } else { + color_b = color_a; + } + + pixeldata = rgb444(shr_palette[color_a]) | THEN_EXTEND_1; + pixeldata |= (rgb444(shr_palette[color_b]) | THEN_EXTEND_1) << 16; + sl->data[sl_pos++] = pixeldata; + } + } else { // 320 Pixels + while(i < 160) { + // Load in the next 2 pixels + dots = (line_mem[i++] & 0xff); + + color_a = ((dots >> 4) & 0xf); + color_b = ((dots >> 0) & 0xf); + + // Consume 2 pixels + pixeldata = rgb444(shr_palette[color_a]) | THEN_EXTEND_1; + pixeldata |= (rgb444(shr_palette[color_b]) | THEN_EXTEND_1) << 16; + sl->data[sl_pos++] = pixeldata; + } + } + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); +} diff --git a/vga/render_test.c b/vga/render_test.c new file mode 100644 index 0000000..2e1e084 --- /dev/null +++ b/vga/render_test.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" +#include "vga/logo.h" +#include "common/build.h" + +#include + +#define _PIXPAIR(p1, p2) ((uint32_t)(p1) | (((uint32_t)p2) << 16)) + +char __attribute__((section(".uninitialized_data."))) error_message[32*24+1]; + +void DELAYED_COPY_CODE(render_test_init)() { + memset(error_message, ' ', 32*24); + memcpy(error_message + 0, "HARDWARE: V2 Analog Rev1", 32); + memcpy(error_message + 32, "FIRMWARE: " BUILDSTR, 32); + + memcpy(error_message + 128, " Copyright (C) 2022-2023 ", 32); + memcpy(error_message + 160, " David Kuder ", 32); + + memcpy(error_message + 256, " based on ", 32); + memcpy(error_message + 288, " AppleII-VGA by Mark Aikens ", 32); + + memcpy(error_message + 416, " no Apple II bus activity ", 32); + + memcpy(error_message + 544, " Turn off power & check ", 32); + memcpy(error_message + 576, " for proper card insertion. ", 32); + + memcpy(error_message + 704, " serial number: ", 32); + + // Get Pico's Flash Serial Number (Board ID) and terminating null. + memcpy(error_message + 736, " ", 8); + pico_get_unique_board_id_string(error_message + 744, 17); + memcpy(error_message + 760, " ", 8); +} + +// Clear the error message, in case the user sets 0x20 in terminal switches +// to show the about screen. +void DELAYED_COPY_CODE(render_about_init)() { + memset(error_message + 416, ' ', 32); + memset(error_message + 544, ' ', 64); +} + +static inline uint_fast8_t char_test_bits(uint_fast8_t ch, uint_fast8_t glyph_line) { + uint_fast8_t bits = terminal_character_rom[((uint_fast16_t)(ch & 0x7f) << 3) | glyph_line | 0x400]; + return bits & 0x7f; +} + +void DELAYED_COPY_CODE(render_testpattern)() { + vga_prepare_frame(); + + for(uint line=0; line < VGA_HEIGHT;) { + if(line == 19) { + render_status_line(); + line += 16; + } else { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + if((line == 0) || (line == VGA_HEIGHT-1) || (line == 36) || (line == VGA_HEIGHT-36-1)) { + for(; sl_pos < VGA_WIDTH/32; sl_pos++) { + sl->data[sl_pos] = _PIXPAIR(0x1ff | THEN_EXTEND_15, 0x1ff | THEN_EXTEND_15); + } + sl->length = sl_pos; + } else if(line == 1) { + for(uint i=0; i < VGA_WIDTH/4; i++, sl_pos++) { + sl->data[sl_pos] = _PIXPAIR(0x1ff, 0); + } + for(uint i=0; i < VGA_WIDTH/4; i++, sl_pos++) { + sl->data[sl_pos] = _PIXPAIR(0, 0x1ff); + } + + sl->length = sl_pos; + sl->repeat_count = 16; + } else if(line == VGA_HEIGHT-36) { + for(uint i=0; i < VGA_WIDTH/4; i++, sl_pos++) { + sl->data[sl_pos] = _PIXPAIR(0x1ff, 0); + } + for(uint i=0; i < VGA_WIDTH/4; i++, sl_pos++) { + sl->data[sl_pos] = _PIXPAIR(0, 0x1ff); + } + + sl->length = sl_pos; + sl->repeat_count = 34; + } else if((line >= 48) && (line < 48+128)) { + sl->data[sl_pos++] = _PIXPAIR(0x1ff, 0 | THEN_EXTEND_6); // 8px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + + const uint z = (line - 48); + + if((z & 0xf) < 14) { + const uint g = z / 16; + for(uint b=0; b < 3; b++) { + for(uint r=0; r < 8; r++) { + const uint rgb = (r << 6) | (g << 3) | b; + sl->data[sl_pos++] = _PIXPAIR(0, rgb | THEN_EXTEND_6); // 8px + sl->data[sl_pos++] = _PIXPAIR(rgb | THEN_EXTEND_6, 0); // 8px + } + } + } else { + for(uint i=0; i < 12; i++) { + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_15, 0 | THEN_EXTEND_15); // 384px + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_3, 0 | THEN_EXTEND_3); // 8px + + const uint w = (line - 48) << 1; + for(uint i=0; i < 32; i+=2) { + uint32_t bits_a = char_test_bits(error_message[(w & 0x3E0) | i], (w>>2) & 0x7); + uint32_t bits_b = char_test_bits(error_message[(w & 0x3E0) | (i+1)], (w>>2) & 0x7); + uint32_t bits = (bits_a << 7) | bits_b; + for(uint j=0; j < 7; j++) { + uint32_t pixeldata = (bits & 0x2000) ? (0) : (0x1ff); + pixeldata |= (bits & 0x1000) ? ((0) << 16) : ((0x1ff) << 16); + bits <<= 2; + sl->data[sl_pos++] = pixeldata; + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_6, 0x1ff); // 8px + + sl->repeat_count = 1; + sl->length = sl_pos; + } else if((line >= 176) && (line < 176+128)) { + sl->data[sl_pos++] = _PIXPAIR(0x1ff, 0 | THEN_EXTEND_6); // 8px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + + const uint z = (line - 176); + const uint g = z / 16; + + // center-left square + if((z & 0xf) < 14) { + uint b = 3; + for(uint r=0; r < 8; r++) { + const uint rgb = (r << 6) | (g << 3) | b; + sl->data[sl_pos++] = _PIXPAIR(0, rgb | THEN_EXTEND_6); + sl->data[sl_pos++] = _PIXPAIR(rgb | THEN_EXTEND_6, 0); + } + } else { + for(uint i=0; i < 4; i++) { + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_15, 0 | THEN_EXTEND_15); + } + } + + // center square (logo) + for(uint i=0; i < 32; i++) { + uint color1 = lores_palette[PicoPalLogo[((z & 0x7e) << 4) | i] >> 4]; + uint color2 = lores_palette[PicoPalLogo[((z & 0x7e) << 4) | i] & 0xf]; + sl->data[sl_pos++] = _PIXPAIR(color1 | THEN_EXTEND_1, color2 | THEN_EXTEND_1); + } + + // center-right square + if((z & 0xf) < 14) { + uint b = 4; + for(uint r=0; r < 8; r++) { + const uint rgb = (r << 6) | (g << 3) | b; + sl->data[sl_pos++] = _PIXPAIR(0, rgb | THEN_EXTEND_6); + sl->data[sl_pos++] = _PIXPAIR(rgb | THEN_EXTEND_6, 0); + } + } else { + for(uint i=0; i < 4; i++) { + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_15, 0 | THEN_EXTEND_15); + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_3, 0 | THEN_EXTEND_3); // 8px + + const uint w = (line - 48) << 1; + for(uint i=0; i < 32; i+=2) { + uint32_t bits_a = char_test_bits(error_message[(w & 0x3E0) | i], (w>>2) & 0x7); + uint32_t bits_b = char_test_bits(error_message[(w & 0x3E0) | (i+1)], (w>>2) & 0x7); + uint32_t bits = (bits_a << 7) | bits_b; + for(uint j=0; j < 7; j++) { + uint32_t pixeldata = (bits & 0x2000) ? (0) : (0x1ff); + pixeldata |= (bits & 0x1000) ? ((0) << 16) : ((0x1ff) << 16); + bits <<= 2; + sl->data[sl_pos++] = pixeldata; + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_6, 0x1ff); // 8px + + sl->repeat_count = 1; + sl->length = sl_pos; + } else if((line >= 304) && (line < 304+128)) { + sl->data[sl_pos++] = _PIXPAIR(0x1ff, 0 | THEN_EXTEND_6); // 8px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + + const uint z = (line - 304); + + if((z & 0xf) < 14) { + const uint g = z / 16; + for(uint b=5; b < 8; b++) { + for(uint r=0; r < 8; r++) { + const uint rgb = (r << 6) | (g << 3) | b; + sl->data[sl_pos++] = _PIXPAIR(0, rgb | THEN_EXTEND_6); + sl->data[sl_pos++] = _PIXPAIR(rgb | THEN_EXTEND_6, 0); + } + } + } else { + for(uint i=0; i < 12; i++) { + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_15, 0 | THEN_EXTEND_15); // 384px + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_3, 0 | THEN_EXTEND_3); // 8px + + const uint w = (line - 48) << 1; + for(uint i=0; i < 32; i+=2) { + uint32_t bits_a = char_test_bits(error_message[(w & 0x3E0) | i], (w>>2) & 0x7); + uint32_t bits_b = char_test_bits(error_message[(w & 0x3E0) | (i+1)], (w>>2) & 0x7); + uint32_t bits = (bits_a << 7) | bits_b; + for(uint j=0; j < 7; j++) { + uint32_t pixeldata = (bits & 0x2000) ? (0) : (0x1ff); + pixeldata |= (bits & 0x1000) ? ((0) << 16) : ((0x1ff) << 16); + bits <<= 2; + sl->data[sl_pos++] = pixeldata; + } + } + + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_1, 0 | THEN_EXTEND_1); // 4px + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_6, 0x1ff); // 8px + + sl->repeat_count = 1; + sl->length = sl_pos; + } else if(line < VGA_HEIGHT) { + // black w/ white border + sl->data[sl_pos++] = _PIXPAIR(0x1ff, 0 | THEN_EXTEND_6); + for(uint i=0; i < 39; i++) { + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_7, 0 | THEN_EXTEND_7); + } + sl->data[sl_pos++] = _PIXPAIR(0 | THEN_EXTEND_6, 0x1ff); + + sl->length = sl_pos; + } else { + sl->data[sl_pos++] = _PIXPAIR(0, 0); + + sl->length = sl_pos; + } + + line += sl->repeat_count + 1; + vga_submit_scanline(sl); + } + } +} + +void DELAYED_COPY_CODE(render_status_line)() { + for(uint glyph_line=0; glyph_line < 8; glyph_line++) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint8_t *line_buf = status_line; + uint32_t bits; + uint sl_pos = 0; + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + for(uint col=0; col < 40; ) { + // Grab 14 pixels from the next two characters + if(*line_buf != 0) { + bits = char_test_bits(*line_buf++, glyph_line) << 7; + } else { + bits = 0x7f << 7; + } + if(*line_buf != 0) { + bits |= char_test_bits(*line_buf++, glyph_line); + } else { + bits |= 0x7f; + } + col++; + + // Translate each pair of bits into a pair of pixels + for(int i=0; i < 7; i++) { + uint32_t pixeldata = (bits & 0x2000) ? (0x000) : (0x1ff); + pixeldata |= (bits & 0x1000) ? + ((uint32_t)0x000) << 16 : + ((uint32_t)0x1ff) << 16; + bits <<= 2; + + sl->data[sl_pos] = pixeldata; + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); + } +} diff --git a/vga/render_text.c b/vga/render_text.c new file mode 100644 index 0000000..216f835 --- /dev/null +++ b/vga/render_text.c @@ -0,0 +1,144 @@ +#include +#include "common/config.h" +#include "vga/vgabuf.h" +#include "vga/render.h" +#include "vga/vgaout.h" + +//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2)) +#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2) + +volatile uint_fast32_t text_flasher_mask = 0; +static uint64_t next_flash_tick = 0; + +void DELAYED_COPY_CODE(update_text_flasher)() { + uint64_t now = time_us_64(); + if(now > next_flash_tick) { + text_flasher_mask ^= 0x7f; + + switch(current_machine) { + default: + case MACHINE_II: + case MACHINE_PRAVETZ: + next_flash_tick = now + 125000u; + break; + case MACHINE_IIE: + case MACHINE_IIGS: + next_flash_tick = now + 250000u; + break; + } + } +} + + +static inline uint_fast8_t char_text_bits(uint_fast8_t ch, uint_fast8_t glyph_line) { + uint_fast8_t bits, invert; + + if((soft_switches & SOFTSW_ALTCHAR) || (ch & 0x80)) { + // normal / mousetext character + invert = 0x00; + } else { + // flashing character or inverse character + invert = (ch & 0x40) ? text_flasher_mask : 0x7f; + ch = (ch & 0x3f) | 0x80; + } + + bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line] & 0x7f; + + return bits ^ invert; +} + +void DELAYED_COPY_CODE(render_text)() { + for(uint line=0; line < 24; line++) { + if(soft_switches & SOFTSW_80COL) { + render_text80_line(PAGE2SEL, line); + } else { + render_text40_line(PAGE2SEL, line); + } + } +} + +void DELAYED_COPY_CODE(render_text40_line)(bool p2, unsigned int line) { + const uint8_t *page = (const uint8_t *)(p2 ? text_p2 : text_p1); + const uint8_t *line_buf = (const uint8_t *)(page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40)); + + for(uint glyph_line=0; glyph_line < 8; glyph_line++) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + for(uint col=0; col < 40; ) { + // Grab 14 pixels from the next two characters + uint32_t bits_a = char_text_bits(line_buf[col], glyph_line); + col++; + uint32_t bits_b = char_text_bits(line_buf[col], glyph_line); + col++; + + uint32_t bits = (bits_a << 7) | bits_b; + + // Translate each pair of bits into a pair of pixels + for(int i=0; i < 7; i++) { + uint32_t pixeldata = (bits & 0x2000) ? (text_back|THEN_EXTEND_1) : (text_fore|THEN_EXTEND_1); + pixeldata |= (bits & 0x1000) ? + ((uint32_t)text_back|THEN_EXTEND_1) << 16 : + ((uint32_t)text_fore|THEN_EXTEND_1) << 16; + bits <<= 2; + + sl->data[sl_pos] = pixeldata; + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); + } +} + +void DELAYED_COPY_CODE(render_text80_line)(bool p2, unsigned int line) { + const uint8_t *page_a = (const uint8_t *)(p2 ? text_p2 : text_p1); + const uint8_t *page_b = (const uint8_t *)(p2 ? text_p4 : text_p3); + const uint8_t *line_buf_a = page_a + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40); + const uint8_t *line_buf_b = page_b + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40); + + for(uint glyph_line=0; glyph_line < 8; glyph_line++) { + struct vga_scanline *sl = vga_prepare_scanline(); + uint sl_pos = 0; + + // Pad 40 pixels on the left to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + for(uint col=0; col < 40; ) { + // Grab 14 pixels from the next two characters + uint32_t bits_a = char_text_bits(line_buf_a[col], glyph_line); + uint32_t bits_b = char_text_bits(line_buf_b[col], glyph_line); + col++; + + uint32_t bits = (bits_b << 7) | bits_a; + + // Translate each pair of bits into a pair of pixels + for(int i=0; i < 7; i++) { + uint32_t pixeldata = (bits & 0x2000) ? (text_back) : (text_fore); + pixeldata |= (bits & 0x1000) ? + ((uint32_t)text_back) << 16 : + ((uint32_t)text_fore) << 16; + bits <<= 2; + + sl->data[sl_pos] = pixeldata; + sl_pos++; + } + } + + // Pad 40 pixels on the right to center horizontally + sl->data[sl_pos++] = (text_border|THEN_EXTEND_31) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word + + sl->length = sl_pos; + sl->repeat_count = 1; + vga_submit_scanline(sl); + } +} + diff --git a/vga/vga12.pio b/vga/vga12.pio new file mode 100644 index 0000000..6f1904b --- /dev/null +++ b/vga/vga12.pio @@ -0,0 +1,116 @@ +; This program consists of 3 state machines communicating using state machine IRQs +; - hsync generates the hsync signal and drives the vsync and data state machines +; - vsync generates the vsync signal and drives the data state machine +; - data generates the pixel data stream, synchronized with the hsync and vsync IRQs +; generated by their respective timing state machine +; +; Overall timing (in pixel times) +; +; Time | hsync action | vsync action | data action +; ------------------------------------------------------------------------------------------------ +; 0 | assert lineclk IRQ | | +; 8 | | wait & reset lineclk IRQ | +; 16 | set hsync signal low | set vsync signal high/low | +; 112 | set hsync signal high | | +; 144 | | assert vsync IRQ | +; 144.5 | | | 'wait irq vsync' completes +; 152 | assert hsync IRQ | | +; 152.5 | | | 'wait irq hsync' completes +; 160 | deassert hsync IRQ | | first pixel RGB data out +; 161 | | | second pixel RGB data out +; 216 | | deassert vsync IRQ | + +.define LINECLK_IRQ_NUM 4 +.define HSYNC_IRQ_NUM 5 +.define VSYNC_IRQ_NUM 6 + + +; Run at 4 * pixel frequency +.program vga_data +.origin 0 + +.wrap_target +pixel_out: + out PINS, 12 + out PC, 4 +public wait_vsync: + wait 0 irq VSYNC_IRQ_NUM +public wait_hsync: + wait 0 irq HSYNC_IRQ_NUM [1] +public extend_31: ; 32 pixels / an extra 62 clocks (32+4+12+2+6+4+2) + nop [31] +public extend_15: ; 16 pixels / an extra 30 clocks (4+12+2+6+4+2) + nop [3] +public extend_13: ; 14 pixels / an extra 26 clocks (12+2+6+4+2) + nop [11] +public extend_7: ; 8 pixels / an extra 14 clocks (2+6+4+2) + nop [1] +public extend_6: ; 7 pixels / an extra 12 clocks (6+4+2) + nop [5] +public extend_3: ; 4 pixels / an extra 6 clocks (4+2) + nop [3] +public extend_1: ; 2 pixels / an extra 2 clocks + nop [1] +.wrap + + +; Run at pixel frequency / 8 +.program vga_hsync + pull ; load timing loop count from the CPU +.wrap_target + irq set LINECLK_IRQ_NUM [1] ; (0) assert lineclk IRQ + set PINS, 0 [11] ; (16) set hsync signal low + set PINS, 1 [4] ; (112) set hsync signal high + irq clear HSYNC_IRQ_NUM ; (152) assert hsync IRQ + irq set HSYNC_IRQ_NUM ; (160) deassert hsync IRQ + + ; Wait until the next hsync should be generated + mov X, OSR +skip_loop: + jmp X--, skip_loop +.wrap + + +; Run at pixel frequency / 8 +.program vga_vsync + pull ; load timing loop count from the CPU +.wrap_target + wait 1 irq LINECLK_IRQ_NUM ; (8) wait & reset lineclk IRQ + set pins, 0 [15] ; (16) set vsync signal low + irq clear VSYNC_IRQ_NUM [8] ; (144) assert vsync IRQ + irq set VSYNC_IRQ_NUM ; (216) deassert vsync IRQ + wait 1 irq LINECLK_IRQ_NUM + wait 1 irq LINECLK_IRQ_NUM ; (8) wait for lineclk & reset + set pins, 1 ; (16) set vsync signal high + + ; Skip the remaining scanlines + mov X, OSR +skip_loop: + wait 1 irq LINECLK_IRQ_NUM + jmp X--, skip_loop +.wrap + + +; Pixel Frequency: 25.175MHz +; +; Horizontal timing +; +; 640x480@60Hz and 640x400@70Hz (pulse low) +; Scanline part | Pixels | Time [µs] +; ------------------------------------------ +; Visible area | 640 | 25.422045680238 +; Front porch | 16 | 0.63555114200596 +; Sync pulse | 96 | 3.8133068520357 +; Back porch | 48 | 1.9066534260179 +; Whole line | 800 | 31.777557100298 +; +; Vertical timing +; +; [640x480@60Hz (pulse low)] || [640x400@70Hz (pulse high)] +; Frame part | Lines | Time [ms] || Lines | Time [ms] +;------------------------------------------------------------------------ +; Visible area | 480 | 15.253227408143 || 400 | 12.711022840119 +; Front porch | 10 | 0.31777557100298 || 12 | 0.38133068520357 +; Sync pulse | 2 | 0.063555114200596 || 2 | 0.063555114200596 +; Back porch | 33 | 1.0486593843098 || 35 | 1.1122144985104 +; Whole frame | 525 | 16.683217477656 || 449 | 14.268123138034 diff --git a/vga/vga9.pio b/vga/vga9.pio new file mode 100644 index 0000000..148cde6 --- /dev/null +++ b/vga/vga9.pio @@ -0,0 +1,116 @@ +; This program consists of 3 state machines communicating using state machine IRQs +; - hsync generates the hsync signal and drives the vsync and data state machines +; - vsync generates the vsync signal and drives the data state machine +; - data generates the pixel data stream, synchronized with the hsync and vsync IRQs +; generated by their respective timing state machine +; +; Overall timing (in pixel times) +; +; Time | hsync action | vsync action | data action +; ------------------------------------------------------------------------------------------------ +; 0 | assert lineclk IRQ | | +; 8 | | wait & reset lineclk IRQ | +; 16 | set hsync signal low | set vsync signal high/low | +; 112 | set hsync signal high | | +; 144 | | assert vsync IRQ | +; 144.5 | | | 'wait irq vsync' completes +; 152 | assert hsync IRQ | | +; 152.5 | | | 'wait irq hsync' completes +; 160 | deassert hsync IRQ | | first pixel RGB data out +; 161 | | | second pixel RGB data out +; 216 | | deassert vsync IRQ | + +.define LINECLK_IRQ_NUM 4 +.define HSYNC_IRQ_NUM 5 +.define VSYNC_IRQ_NUM 6 + + +; Run at 4 * pixel frequency +.program vga_data +.origin 0 + +.wrap_target +pixel_out: + out PINS, 9 + out PC, 7 +public wait_vsync: + wait 0 irq VSYNC_IRQ_NUM +public wait_hsync: + wait 0 irq HSYNC_IRQ_NUM [1] +public extend_31: ;public extend_31: ; 32 pixels / an extra 62 clocks (32+4+12+2+6+4+2) + nop [31] +public extend_15: ; 16 pixels / an extra 30 clocks (4+12+2+6+4+2) + nop [3] +public extend_13: ; 14 pixels / an extra 26 clocks (12+2+6+4+2) + nop [11] +public extend_7: ; 8 pixels / an extra 14 clocks (2+6+4+2) + nop [1] +public extend_6: ; 7 pixels / an extra 12 clocks (6+4+2) + nop [5] +public extend_3: ; 4 pixels / an extra 6 clocks (4+2) + nop [3] +public extend_1: ; 2 pixels / an extra 2 clocks + nop [1] +.wrap + + +; Run at pixel frequency / 8 +.program vga_hsync + pull ; load timing loop count from the CPU +.wrap_target + irq set LINECLK_IRQ_NUM [1] ; (0) assert lineclk IRQ + set PINS, 0 [11] ; (16) set hsync signal low + set PINS, 1 [4] ; (112) set hsync signal high + irq clear HSYNC_IRQ_NUM ; (152) assert hsync IRQ + irq set HSYNC_IRQ_NUM ; (160) deassert hsync IRQ + + ; Wait until the next hsync should be generated + mov X, OSR +skip_loop: + jmp X--, skip_loop +.wrap + + +; Run at pixel frequency / 8 +.program vga_vsync + pull ; load timing loop count from the CPU +.wrap_target + wait 1 irq LINECLK_IRQ_NUM ; (8) wait & reset lineclk IRQ + set pins, 0 [15] ; (16) set vsync signal low + irq clear VSYNC_IRQ_NUM [8] ; (144) assert vsync IRQ + irq set VSYNC_IRQ_NUM ; (216) deassert vsync IRQ + wait 1 irq LINECLK_IRQ_NUM + wait 1 irq LINECLK_IRQ_NUM ; (8) wait for lineclk & reset + set pins, 1 ; (16) set vsync signal high + + ; Skip the remaining scanlines + mov X, OSR +skip_loop: + wait 1 irq LINECLK_IRQ_NUM + jmp X--, skip_loop +.wrap + + +; Pixel Frequency: 25.175MHz +; +; Horizontal timing +; +; 640x480@60Hz and 640x400@70Hz (pulse low) +; Scanline part | Pixels | Time [µs] +; ------------------------------------------ +; Visible area | 640 | 25.422045680238 +; Front porch | 16 | 0.63555114200596 +; Sync pulse | 96 | 3.8133068520357 +; Back porch | 48 | 1.9066534260179 +; Whole line | 800 | 31.777557100298 +; +; Vertical timing +; +; [640x480@60Hz (pulse low)] || [640x400@70Hz (pulse high)] +; Frame part | Lines | Time [ms] || Lines | Time [ms] +;------------------------------------------------------------------------ +; Visible area | 480 | 15.253227408143 || 400 | 12.711022840119 +; Front porch | 10 | 0.31777557100298 || 12 | 0.38133068520357 +; Sync pulse | 2 | 0.063555114200596 || 2 | 0.063555114200596 +; Back porch | 33 | 1.0486593843098 || 35 | 1.1122144985104 +; Whole frame | 525 | 16.683217477656 || 449 | 14.268123138034 diff --git a/vga/vgabuf.c b/vga/vgabuf.c new file mode 100644 index 0000000..e5a8df3 --- /dev/null +++ b/vga/vgabuf.c @@ -0,0 +1,20 @@ +#include "common/config.h" +#include "vga/vgabuf.h" + +volatile uint32_t mono_palette = 0; + +// The currently programmed character generator ROM for text mode +uint8_t __attribute__((section(".uninitialized_data."))) character_rom[4096]; +uint8_t __attribute__((section(".uninitialized_data."))) terminal_character_rom[4096]; + +volatile uint8_t terminal_row = 0; +volatile uint8_t terminal_col = 0; + +volatile uint8_t terminal_tbcolor = 0xF6; +volatile uint8_t terminal_border = 0x6; + +volatile uint8_t romx_type = 0; +volatile uint8_t romx_unlocked = 0; +volatile uint8_t romx_textbank = 0; +volatile uint8_t romx_changed = 0; + diff --git a/vga/vgabuf.h b/vga/vgabuf.h new file mode 100644 index 0000000..9f28f82 --- /dev/null +++ b/vga/vgabuf.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "common/buffers.h" + +#define text_memory text_p1 +#define hires_memory hgr_p1 +#define terminal_memory (private_memory+0xF000) + +extern uint8_t character_rom[4096]; +extern uint8_t terminal_character_rom[4096]; + +extern volatile uint8_t terminal_row; +extern volatile uint8_t terminal_col; + +#define apple_tbcolor apple_memory[0xC022] +#define apple_border apple_memory[0xC034] + +#define APPLE_FORE ((terminal_tbcolor>>4) & 0xf) +#define APPLE_BACK (terminal_tbcolor & 0xf) +#define APPLE_BORDER (terminal_border & 0xf) + +extern volatile uint32_t mono_palette; +extern volatile uint8_t terminal_tbcolor; +extern volatile uint8_t terminal_border; + +#define TERMINAL_FORE ((terminal_tbcolor>>4) & 0xf) +#define TERMINAL_BACK (terminal_tbcolor & 0xf) +#define TERMINAL_BORDER (terminal_border & 0xf) + +extern volatile uint8_t romx_type; +extern volatile uint8_t romx_unlocked; +extern volatile uint8_t romx_textbank; +extern volatile uint8_t romx_changed; diff --git a/vga/vgamain.c b/vga/vgamain.c new file mode 100644 index 0000000..2adb9da --- /dev/null +++ b/vga/vgamain.c @@ -0,0 +1,16 @@ +#include +#include +#include "common/config.h" +#include "common/abus.h" +#include "common/config.h" +#include "vga/businterface.h" +#include "vga/render.h" +#include "vga/vgaout.h" + + +void DELAYED_COPY_CODE(vgamain)() { + vga_init(); + render_init(); + render_loop(); + vga_stop(); +} diff --git a/vga/vgaout.c b/vga/vgaout.c new file mode 100644 index 0000000..99f8b29 --- /dev/null +++ b/vga/vgaout.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "common/config.h" +#include "common/buffers.h" + +#ifdef ANALOG_GS +#include "vga12.pio.h" +#else +#include "vga9.pio.h" +#endif + +#include "vgaout.h" + + +#define PIXEL_FREQ 25.2/*MHz*/ +#define PIXELS_PER_LINE 800 +#define LINES_PER_FRAME 525 +#define LINES_IN_BACK_PORCH 33 + +#define HSYNC_TIMING_VALUE (((PIXELS_PER_LINE) / 8) - 23) +#define VSYNC_TIMING_VALUE ((LINES_PER_FRAME) - 4) + +#define NUM_SCANLINE_BUFFERS 32 + +static bool vga_initialized = 0; + +enum { + VGA_HSYNC_SM = 0, + VGA_VSYNC_SM = 1, + VGA_DATA_SM = 2, +}; + +// The scanline flags form a simple state machine: +// Initial state (0) +// \/ prepare() +// BUSY +// \/ ready() +// BUSY|READY +// \/ first DMA started +// BUSY|READY|STARTED +// \/ last DMA completed +// READY|STARTED +enum { + FLAG_BUSY = 0x01, + FLAG_READY = 0x02, + FLAG_STARTED = 0x04, +}; + + +static uint vga_dma_channel; + +// Scanline queue. Scanlines are filled in from the head and are +// sent to the DMA engine from the tail. +static uint scanline_queue_head; +static uint scanline_queue_tail; +static struct vga_scanline scanline_queue[NUM_SCANLINE_BUFFERS]; + + +static void DELAYED_COPY_CODE(vga_hsync_setup)(PIO pio, uint sm) { + uint program_offset = pio_add_program(pio, &vga_hsync_program); + pio_sm_claim(pio, sm); + + pio_sm_config c = vga_hsync_program_get_default_config(program_offset); + sm_config_set_clkdiv(&c, CONFIG_SYSCLOCK * 8 / PIXEL_FREQ); // 1/8 * PIXEL_FREQ + + // Map the state machine's OUT pin group to the sync signal pin + sm_config_set_out_pins(&c, CONFIG_PIN_HSYNC, 1); + sm_config_set_set_pins(&c, CONFIG_PIN_HSYNC, 1); + + // Configure the pins as outputs & connect to the PIO + pio_sm_set_consecutive_pindirs(pio, sm, CONFIG_PIN_HSYNC, 1, true); + pio_gpio_init(pio, CONFIG_PIN_HSYNC); + + // Load the configuration and push in the timing loop value + pio_sm_init(pio, sm, program_offset, &c); + pio_sm_put_blocking(pio, sm, HSYNC_TIMING_VALUE); +} + +static void DELAYED_COPY_CODE(vga_vsync_setup)(PIO pio, uint sm) { + uint program_offset = pio_add_program(pio, &vga_vsync_program); + pio_sm_claim(pio, sm); + + pio_sm_config c = vga_vsync_program_get_default_config(program_offset); + sm_config_set_clkdiv(&c, CONFIG_SYSCLOCK * 8 / PIXEL_FREQ); // 1/8 * PIXEL_FREQ + + // Map the state machine's OUT pin group to the sync signal pin + sm_config_set_out_pins(&c, CONFIG_PIN_VSYNC, 1); + sm_config_set_set_pins(&c, CONFIG_PIN_VSYNC, 1); + + // Configure the pins as outputs & connect to the PIO + pio_sm_set_consecutive_pindirs(pio, sm, CONFIG_PIN_VSYNC, 1, true); + pio_gpio_init(pio, CONFIG_PIN_VSYNC); + + // Load the configuration and push in the timing loop value + pio_sm_init(pio, sm, program_offset, &c); + pio_sm_put_blocking(pio, sm, VSYNC_TIMING_VALUE); +} + +static void DELAYED_COPY_CODE(vga_data_setup)(PIO pio, uint sm) { + uint program_offset = pio_add_program(pio, &vga_data_program); + pio_sm_claim(pio, sm); + + pio_sm_config c = vga_data_program_get_default_config(program_offset); + sm_config_set_clkdiv(&c, CONFIG_SYSCLOCK / (2*PIXEL_FREQ)); + + // Map the state machine's OUT pin group to the data pins + sm_config_set_out_pins(&c, CONFIG_PIN_RGB_BASE, 9); + sm_config_set_set_pins(&c, CONFIG_PIN_RGB_BASE, 9); + + // Enable autopull every 32 bits (2 x (9 data + 5 jump + 2 pad) bits) + sm_config_set_out_shift(&c, true, true, 32); + + // Set join the state machine FIFOs to double the TX fifo size + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // Configure the pins as outputs & connect to the PIO + pio_sm_set_consecutive_pindirs(pio, sm, CONFIG_PIN_RGB_BASE, 9, true); + for(int i=0; i < 9; i++) { + pio_gpio_init(pio, CONFIG_PIN_RGB_BASE+i); + } + + // Load the configuration, starting execution at 'wait_vsync' + pio_sm_init(pio, sm, program_offset+vga_data_offset_wait_vsync, &c); +} + +// Start the DMA operation of the next scanline if it's ready. +// +// Must be called with the VGA spinlock held +static void DELAYED_COPY_CODE(trigger_ready_scanline_dma)() { + struct vga_scanline *active_scanline = &scanline_queue[scanline_queue_tail]; + + if((active_scanline->_flags & (FLAG_BUSY|FLAG_READY|FLAG_STARTED)) == (FLAG_BUSY|FLAG_READY)) { + active_scanline->_flags |= FLAG_STARTED; + dma_channel_transfer_from_buffer_now(vga_dma_channel, &(active_scanline->_sync), active_scanline->length + 2); + } +} + +static void DELAYED_COPY_CODE(vga_dma_irq_handler)() { + spin_lock_t *lock = spin_lock_instance(CONFIG_VGA_SPINLOCK_ID); + struct vga_scanline *active_scanline = &scanline_queue[scanline_queue_tail]; + + // Ack the IRQ + dma_hw->ints0 = 1u << vga_dma_channel; + + // Repeat the scanline as specified + if(active_scanline->repeat_count) { + active_scanline->repeat_count--; + dma_channel_transfer_from_buffer_now(vga_dma_channel, &(active_scanline->_sync), active_scanline->length + 2); + return; + } + + // Mark the scanline done + active_scanline->_flags &= ~(uint_fast8_t)FLAG_BUSY; + + const uint32_t irq_status = spin_lock_blocking(lock); + scanline_queue_tail = (scanline_queue_tail + 1) & (NUM_SCANLINE_BUFFERS-1); + trigger_ready_scanline_dma(); + spin_unlock(lock, irq_status); +} + + +void DELAYED_COPY_CODE(vga_init)() { + if(!vga_initialized) { + spin_lock_claim(CONFIG_VGA_SPINLOCK_ID); + spin_lock_init(CONFIG_VGA_SPINLOCK_ID); + + // Setup the PIO state machines + vga_hsync_setup(CONFIG_VGA_PIO, VGA_HSYNC_SM); + vga_vsync_setup(CONFIG_VGA_PIO, VGA_VSYNC_SM); + vga_data_setup(CONFIG_VGA_PIO, VGA_DATA_SM); + + // Setup the DMA channel for writing to the data PIO state machine + vga_dma_channel = dma_claim_unused_channel(true); + dma_channel_config c = dma_channel_get_default_config(vga_dma_channel); + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); + channel_config_set_dreq(&c, pio_get_dreq(CONFIG_VGA_PIO, VGA_DATA_SM, true)); + dma_channel_configure(vga_dma_channel, &c, &CONFIG_VGA_PIO->txf[VGA_DATA_SM], NULL, 0, false); + + dma_channel_set_irq0_enabled(vga_dma_channel, true); + irq_set_exclusive_handler(DMA_IRQ_0, vga_dma_irq_handler); + irq_set_enabled(DMA_IRQ_0, true); + + vga_initialized = 1; + } + + // Enable all state machines in sync to ensure their instruction cycles line up + pio_enable_sm_mask_in_sync(CONFIG_VGA_PIO, (1 << VGA_HSYNC_SM) | (1 << VGA_VSYNC_SM) | (1 << VGA_DATA_SM)); +} + +void DELAYED_COPY_CODE(vga_stop)() { + pio_set_sm_mask_enabled(CONFIG_VGA_PIO, (1 << VGA_HSYNC_SM) | (1 << VGA_VSYNC_SM) | (1 << VGA_DATA_SM), false); +} + +void DELAYED_COPY_CODE(vga_dpms_sleep)() { + pio_set_sm_mask_enabled(CONFIG_VGA_PIO, (1 << VGA_HSYNC_SM) | (1 << VGA_VSYNC_SM) | (1 << VGA_DATA_SM), false); + irq_set_enabled(DMA_IRQ_0, false); + dma_channel_set_irq0_enabled(vga_dma_channel, false); +} + +void DELAYED_COPY_CODE(vga_dpms_wake)() { + dma_channel_set_irq0_enabled(vga_dma_channel, true); + irq_set_enabled(DMA_IRQ_0, true); + pio_enable_sm_mask_in_sync(CONFIG_VGA_PIO, (1 << VGA_HSYNC_SM) | (1 << VGA_VSYNC_SM) | (1 << VGA_DATA_SM)); +} + +// Set up for a new display frame +void DELAYED_COPY_CODE(vga_prepare_frame)() { + // Populate a 'scanline' with multiple sync instructions to synchronize with the + // vsync and then skip over the vertical back porch. + struct vga_scanline *sl = vga_prepare_scanline(); + + sl->_sync = (uint32_t)THEN_WAIT_VSYNC << 16; + // FIXME: the number of hsyncs we have to wait for seems to be one too few + // because the vsync is supposed to last two lines (we wait one) and THEN + // the back porch lines need to be skipped. + for(int i=0; i < LINES_IN_BACK_PORCH; i++) { + sl->data[i] = (uint32_t)THEN_WAIT_HSYNC << 16; + } + sl->length = LINES_IN_BACK_PORCH; + + vga_submit_scanline(sl); +} + +// Set up and return a new display scanline +struct vga_scanline * DELAYED_COPY_CODE(vga_prepare_scanline)() { + struct vga_scanline *scanline = &scanline_queue[scanline_queue_head]; + + // Wait for the scanline buffer to become available again + while(scanline->_flags & FLAG_BUSY) + tight_loop_contents(); + + // Reinitialize the scanline struct for reuse + scanline->length = 0; + scanline->repeat_count = 0; + scanline->_flags = FLAG_BUSY; + scanline->_sync = (uint32_t)THEN_WAIT_HSYNC << 16; + + scanline_queue_head = (scanline_queue_head + 1) & (NUM_SCANLINE_BUFFERS-1); + + return scanline; +} + +// Mark the scanline as ready so it can be displayed +void DELAYED_COPY_CODE(vga_submit_scanline)(struct vga_scanline *scanline) { + spin_lock_t *lock = spin_lock_instance(CONFIG_VGA_SPINLOCK_ID); + + scanline->data[scanline->length] = 0; // ensure beam off at end of line + + const uint32_t irq_status = spin_lock_blocking(lock); + scanline->_flags |= FLAG_READY; + trigger_ready_scanline_dma(); + spin_unlock(lock, irq_status); +} diff --git a/vga/vgaout.h b/vga/vgaout.h new file mode 100644 index 0000000..0919157 --- /dev/null +++ b/vga/vgaout.h @@ -0,0 +1,50 @@ +#pragma once + +#include + + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +#ifdef ANALOG_GS +#define THEN_WAIT_VSYNC (2 << 12) +#define THEN_WAIT_HSYNC (3 << 12) +#define THEN_EXTEND_31 (4 << 12) +#define THEN_EXTEND_15 (5 << 12) +#define THEN_EXTEND_13 (6 << 12) +#define THEN_EXTEND_7 (7 << 12) +#define THEN_EXTEND_6 (8 << 12) +#define THEN_EXTEND_3 (9 << 12) +#define THEN_EXTEND_1 (10 << 12) +#else +#define THEN_WAIT_VSYNC (2 << 9) +#define THEN_WAIT_HSYNC (3 << 9) +#define THEN_EXTEND_31 (4 << 9) +#define THEN_EXTEND_15 (5 << 9) +#define THEN_EXTEND_13 (6 << 9) +#define THEN_EXTEND_7 (7 << 9) +#define THEN_EXTEND_6 (8 << 9) +#define THEN_EXTEND_3 (9 << 9) +#define THEN_EXTEND_1 (10 << 9) +#endif + +struct vga_scanline { + // number of 32-bit words in the data array + uint_fast16_t length; + + // number of times to repeat the scanline + uint_fast16_t repeat_count; + + volatile uint_fast8_t _flags; + + uint32_t _sync; + uint32_t data[(VGA_WIDTH/2)+8]; +}; + +void vga_prepare_frame(); +struct vga_scanline *vga_prepare_scanline(); +void vga_submit_scanline(struct vga_scanline *scanline); + +void vga_stop(); +void vga_dpms_sleep(); +void vga_dpms_wake(); \ No newline at end of file diff --git a/z80/businterface.c b/z80/businterface.c new file mode 100644 index 0000000..a31b889 --- /dev/null +++ b/z80/businterface.c @@ -0,0 +1,72 @@ +#include +#include +#include "common/config.h" +#include "common/buffers.h" +#include "common/abus.h" +#include "z80/businterface.h" +#include "z80/z80buf.h" + +volatile uint8_t *pcpi_reg = apple_memory + 0xC0C0; + +static inline void __time_critical_func(pcpi_read)(uint32_t address) { + switch(address & 0x7) { + case 0: // Read Data from Z80 + clr_6502_stat; + break; + case 5: // Z80 Reset + z80_res = 1; + break; + case 6: // Z80 INT + //z80_irq = 1; + break; + case 7: // Z80 NMI + z80_nmi = 1; + break; + } +} + + +static inline void __time_critical_func(pcpi_write)(uint32_t address, uint32_t value) { + switch(address & 0x7) { + case 0: + case 2: + case 3: + case 4: + break; + case 1: // Write Data to Z80 + pcpi_reg[1] = value & 0xff; + set_z80_stat; + break; + case 5: + case 6: + case 7: + pcpi_read(address); + break; + } +} + +void __time_critical_func(z80_businterface)(uint32_t address, uint32_t value) { + volatile uint8_t *new_pcpi_reg; + + // Reset the Z80 when the Apple II resets + if(reset_state == 3) z80_res = 1; + + // Shadow parts of the Apple's memory by observing the bus write cycles + if(CARD_SELECT) { + if(CARD_DEVSEL) { + // Ideally this code should only run once. + new_pcpi_reg = apple_memory + (address & 0xFFF0); + if((uint32_t)new_pcpi_reg != (uint32_t)pcpi_reg) { + memcpy(new_pcpi_reg, pcpi_reg, 16); + pcpi_reg = new_pcpi_reg; + } + + if((value & (1u << CONFIG_PIN_APPLEBUS_RW-CONFIG_PIN_APPLEBUS_DATA_BASE)) == 0) { + pcpi_write(address, value); + } else { + pcpi_read(address); + } + } + } +} + diff --git a/z80/businterface.h b/z80/businterface.h new file mode 100644 index 0000000..ae09391 --- /dev/null +++ b/z80/businterface.h @@ -0,0 +1,3 @@ +#pragma once + +void z80_businterface(); diff --git a/z80/z80buf.h b/z80/z80buf.h new file mode 100644 index 0000000..8b5367d --- /dev/null +++ b/z80/z80buf.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "common/buffers.h" + +extern volatile uint32_t z80_vect; +extern volatile uint8_t z80_irq; +extern volatile uint8_t z80_nmi; +extern volatile uint8_t z80_res; +extern uint8_t z80_rom[2*1024]; +#define z80_ram private_memory +extern volatile uint8_t *pcpi_reg; + +#define clr_z80_stat { pcpi_reg[2] &= ~0x80; } +#define set_z80_stat { pcpi_reg[2] |= 0x80; } +#define rd_z80_stat (pcpi_reg[2] >> 7) + +#define clr_6502_stat { pcpi_reg[3] &= ~0x80; } +#define set_6502_stat { pcpi_reg[3] |= 0x80; } +#define rd_6502_stat (pcpi_reg[3] >> 7) + diff --git a/z80/z80cpu.h b/z80/z80cpu.h new file mode 100644 index 0000000..ad68aff --- /dev/null +++ b/z80/z80cpu.h @@ -0,0 +1,4639 @@ +#ifndef Z80CPU_H +#define Z80CPU_H + +//#define DEBUG + +#define LOW_DIGIT(x) ((x) & 0xf) +#define HIGH_DIGIT(x) (((x) >> 4) & 0xf) +#define LOW_REGISTER(x) ((x) & 0xff) +#define HIGH_REGISTER(x) (((x) >> 8) & 0xff) + +#define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) +#define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) + +/* see main.c for definition */ + +int32_t PCX; /* external view of PC */ +int32_t AF; /* AF register */ +int32_t BC; /* BC register */ +int32_t DE; /* DE register */ +int32_t HL; /* HL register */ +int32_t IX; /* IX register */ +int32_t IY; /* IY register */ +int32_t PC; /* program counter */ +int32_t SP; /* SP register */ +int32_t AF1; /* alternate AF register */ +int32_t BC1; /* alternate BC register */ +int32_t DE1; /* alternate DE register */ +int32_t HL1; /* alternate HL register */ +int32_t IFF; /* Interrupt Flip Flop */ +int32_t IR; /* Interrupt (upper) / Refresh (lower) register */ +int32_t IM; /* Interrupt Mode */ +int32_t Debug = 0; +int32_t Break = -1; +int32_t Step = -1; +int32_t Halted = 0; + +#ifdef iDEBUG +FILE* iLogFile; +char iLogBuffer[256]; +const char* iLogTxt; +#endif + +/* increase R by val (to correctly implement refresh counter) if enabled */ +#ifdef DO_INCR +#define INCR(val) IR = (IR & ~0x3f) | ((IR + (val)) & 0x3f) +#else +#define INCR(val) ; +#endif + +/* + Functions needed by the soft CPU implementation +*/ +void cpu_out(const uint16_t Port, const uint8_t Value); +uint8_t cpu_in(const uint16_t Port); + +/* Z80 Custom soft core */ + +/* simulator stop codes */ +#define STOP_HALT 0 /* HALT */ +#define STOP_IBKPT 1 /* breakpoint (program counter) */ +#define STOP_MEM 2 /* breakpoint (memory access) */ +#define STOP_INSTR 3 /* breakpoint (instruction access) */ +#define STOP_OPCODE 4 /* invalid operation encountered (8080, Z80, 8086) */ + +#define ADDRMASK 0xffff + +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) (AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f) +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) + +#define PARITY(x) parityTable[(x) & 0xff] + +#define SET_PVS(s) (((cbits >> 6) ^ (cbits >> 5)) & 4) +#define SET_PV (SET_PVS(sum)) +#define SET_PV2(x) ((temp == (x)) << 2) + +#define POP(x) { \ + uint32_t y = RAM_PP(SP); \ + x = y + (RAM_PP(SP) << 8); \ +} + +#define JPC(cond) { \ + if (cond) { \ + PC = GET_WORD(PC); \ + } else { \ + PC++; \ + PC++; \ + } \ +} + +#define CALLC(cond) { \ + if (cond) { \ + uint32_t addr = GET_WORD(PC); \ + PUSH(PC + 2); \ + PC = addr; \ + } else { \ + PC++; \ + PC++; \ + } \ +} + +/* the following tables precompute some common subexpressions +parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 +incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) +decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 +cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) +cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) +cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) +cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 +rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) +rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) +addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) +subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 +andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] +xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] +rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] +incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) +decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 +cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) +cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8) +cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 +cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8) +negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) +rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] +cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) +*/ + +/* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ +static const uint8_t parityTable[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +/* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ +static const uint8_t incTable[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 +}; + +/* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ +static const uint8_t decTable[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ +static const uint8_t cbitsTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16_t cbitsDup8Table[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, + 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, + 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, + 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, + 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, + 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, + 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, + 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, + 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, + 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, + 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, + 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, + 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, + 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, + 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, + 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, + 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, + 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, + 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, + 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, + 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, + 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, + 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, + 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, + 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, + 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, + 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, + 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, + 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, + 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, + 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, + 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, + 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, + 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, + 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, + 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, + 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, + 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, + 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, +}; + +/* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ +static const uint8_t cbitsDup16Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, +}; + +/* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8_t cbits2Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16_t rrcaTable[256] = { + 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, + 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, + 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, + 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, + 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, + 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, + 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, + 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, + 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, + 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, + 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, + 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, + 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, + 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, + 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, + 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, + 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, + 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, + 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, + 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, + 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, + 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, + 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, + 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, + 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, + 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, + 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, + 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, + 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, + 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, + 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, + 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, +}; + +/* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16_t rraTable[256] = { + 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, + 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, + 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, + 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, + 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, + 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, + 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, + 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, + 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, + 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, + 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, + 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, + 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, + 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, + 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, + 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, + 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, + 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, + 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, + 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, + 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, + 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, + 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, + 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, + 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, + 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, + 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, + 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, + 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, + 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, + 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, + 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, +}; + +/* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16_t addTable[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, +}; + +/* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ +static const uint16_t subTable[256] = { + 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, + 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, + 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, + 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, + 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, + 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, + 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, + 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, + 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, + 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, + 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, + 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, + 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, + 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, + 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, + 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, + 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, + 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, + 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, + 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, + 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, + 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, + 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, + 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, + 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, + 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, + 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, + 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, + 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, + 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, + 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, + 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, +}; + +/* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ +static const uint16_t andTable[256] = { + 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, + 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, + 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, + 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, + 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, + 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, + 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, + 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, + 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, + 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, + 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, + 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, + 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, + 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, + 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, + 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, + 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, + 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, + 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, + 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, + 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, + 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, + 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, + 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, + 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, + 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, + 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, + 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, + 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, + 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, + 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, + 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, +}; + +/* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16_t xororTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ +static const uint8_t rotateShiftTable[256] = { + 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, +}; + +/* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ +static const uint8_t incZ80Table[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, +}; + +/* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ +static const uint8_t decZ80Table[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ +static const uint8_t cbitsZ80Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ +static const uint8_t cbitsZ80DupTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, +}; + +/* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8_t cbits2Z80Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8), i = 0..511 */ +static const uint8_t cbits2Z80DupTable[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, +}; + +/* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ +static const uint8_t negTable[256] = { + 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16_t rrdrldTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ +static const uint8_t cpTable[256] = { + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +}; + +#if defined(DEBUG) || defined(iDEBUG) +static const char* Mnemonics[256] = +{ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", + "EX AF,AF'", "ADD HL,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", + "DJNZ @h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", + "JR @h", "ADD HL,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", + "JR NZ,@h", "LD HL,#h", "LD (#h),HL", "INC HL", "INC H", "DEC H", "LD H,*h", "DAA", + "JR Z,@h", "ADD HL,HL", "LD HL,(#h)", "DEC HL", "INC L", "DEC L", "LD L,*h", "CPL", + "JR NC,@h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL),*h", "SCF", + "JR C,@h", "ADD HL,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,(HL)", "LD B,A", + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,(HL)", "LD C,A", + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,(HL)", "LD D,A", + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,(HL)", "LD E,A", + "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,(HL)", "LD H,A", + "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,(HL)", "LD L,A", + "LD (HL),B", "LD (HL),C", "LD (HL),D", "LD (HL),E", "LD (HL),H", "LD (HL),L", "HALT", "LD (HL),A", + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,(HL)", "LD A,A", + "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD (HL)", "ADD A", + "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC (HL)", "ADC A", + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", + "SBC B", "SBC C", "SBC D", "SBC E", "SBC H", "SBC L", "SBC (HL)", "SBC A", + "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", + "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", + "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", + "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD *h", "RST 00h", + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC *h", "RST 08h", + "RET NC", "POP DE", "JP NC,#h", "OUTA (*h)", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", + "RET C", "EXX", "JP C,#h", "INA (*h)", "CALL C,#h", "PFX_DD", "SBC *h", "RST 18h", + "RET PO", "POP HL", "JP PO,#h", "EX HL,(SP)", "CALL PO,#h", "PUSH HL", "AND *h", "RST 20h", + "RET PE", "LD PC,HL", "JP PE,#h", "EX DE,HL", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", + "RET M", "LD SP,HL", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" +}; + +static const char* MnemonicsCB[256] = +{ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(HL)", "BIT 0,A", + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(HL)", "BIT 1,A", + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(HL)", "BIT 2,A", + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(HL)", "BIT 3,A", + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(HL)", "BIT 4,A", + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(HL)", "BIT 5,A", + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(HL)", "BIT 6,A", + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(HL)", "BIT 7,A", + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(HL)", "RES 0,A", + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(HL)", "RES 1,A", + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(HL)", "RES 2,A", + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(HL)", "RES 3,A", + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(HL)", "RES 4,A", + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(HL)", "RES 5,A", + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(HL)", "RES 6,A", + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(HL)", "RES 7,A", + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(HL)", "SET 0,A", + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(HL)", "SET 1,A", + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(HL)", "SET 2,A", + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(HL)", "SET 3,A", + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(HL)", "SET 4,A", + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(HL)", "SET 5,A", + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(HL)", "SET 6,A", + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(HL)", "SET 7,A" +}; + +static const char* MnemonicsED[256] = +{ + "DB EDh,00h", "DB EDh,01h", "DB EDh,02h", "DB EDh,03h", + "DB EDh,04h", "DB EDh,05h", "DB EDh,06h", "DB EDh,07h", + "DB EDh,08h", "DB EDh,09h", "DB EDh,0Ah", "DB EDh,0Bh", + "DB EDh,0Ch", "DB EDh,0Dh", "DB EDh,0Eh", "DB EDh,0Fh", + "DB EDh,10h", "DB EDh,11h", "DB EDh,12h", "DB EDh,13h", + "DB EDh,14h", "DB EDh,15h", "DB EDh,16h", "DB EDh,17h", + "DB EDh,18h", "DB EDh,19h", "DB EDh,1Ah", "DB EDh,1Bh", + "DB EDh,1Ch", "DB EDh,1Dh", "DB EDh,1Eh", "DB EDh,1Fh", + "DB EDh,20h", "DB EDh,21h", "DB EDh,22h", "DB EDh,23h", + "DB EDh,24h", "DB EDh,25h", "DB EDh,26h", "DB EDh,27h", + "DB EDh,28h", "DB EDh,29h", "DB EDh,2Ah", "DB EDh,2Bh", + "DB EDh,2Ch", "DB EDh,2Dh", "DB EDh,2Eh", "DB EDh,2Fh", + "DB EDh,30h", "DB EDh,31h", "DB EDh,32h", "DB EDh,33h", + "DB EDh,34h", "DB EDh,35h", "DB EDh,36h", "DB EDh,37h", + "DB EDh,38h", "DB EDh,39h", "DB EDh,3Ah", "DB EDh,3Bh", + "DB EDh,3Ch", "DB EDh,3Dh", "DB EDh,3Eh", "DB EDh,3Fh", + "IN B,(C)", "OUT (C),B", "SBC HL,BC", "LD (#h),BC", + "NEG", "RETN", "IM 0", "LD I,A", + "IN C,(C)", "OUT (C),C", "ADC HL,BC", "LD BC,(#h)", + "DB EDh,4Ch", "RETI", "DB EDh,4Eh", "LD R,A", + "IN D,(C)", "OUT (C),D", "SBC HL,DE", "LD (#h),DE", + "DB EDh,54h", "DB EDh,55h", "IM 1", "LD A,I", + "IN E,(C)", "OUT (C),E", "ADC HL,DE", "LD DE,(#h)", + "DB EDh,5Ch", "DB EDh,5Dh", "IM 2", "LD A,R", + "IN H,(C)", "OUT (C),H", "SBC HL,HL", "LD (#h),HL", + "DB EDh,64h", "DB EDh,65h", "DB EDh,66h", "RRD", + "IN L,(C)", "OUT (C),L", "ADC HL,HL", "LD HL,(#h)", + "DB EDh,6Ch", "DB EDh,6Dh", "DB EDh,6Eh", "RLD", + "IN F,(C)", "DB EDh,71h", "SBC HL,SP", "LD (#h),SP", + "DB EDh,74h", "DB EDh,75h", "DB EDh,76h", "DB EDh,77h", + "IN A,(C)", "OUT (C),A", "ADC HL,SP", "LD SP,(#h)", + "DB EDh,7Ch", "DB EDh,7Dh", "DB EDh,7Eh", "DB EDh,7Fh", + "DB EDh,80h", "DB EDh,81h", "DB EDh,82h", "DB EDh,83h", + "DB EDh,84h", "DB EDh,85h", "DB EDh,86h", "DB EDh,87h", + "DB EDh,88h", "DB EDh,89h", "DB EDh,8Ah", "DB EDh,8Bh", + "DB EDh,8Ch", "DB EDh,8Dh", "DB EDh,8Eh", "DB EDh,8Fh", + "DB EDh,90h", "DB EDh,91h", "DB EDh,92h", "DB EDh,93h", + "DB EDh,94h", "DB EDh,95h", "DB EDh,96h", "DB EDh,97h", + "DB EDh,98h", "DB EDh,99h", "DB EDh,9Ah", "DB EDh,9Bh", + "DB EDh,9Ch", "DB EDh,9Dh", "DB EDh,9Eh", "DB EDh,9Fh", + "LDI", "CPI", "INI", "OUTI", + "DB EDh,A4h", "DB EDh,A5h", "DB EDh,A6h", "DB EDh,A7h", + "LDD", "CPD", "IND", "OUTD", + "DB EDh,ACh", "DB EDh,ADh", "DB EDh,AEh", "DB EDh,AFh", + "LDIR", "CPIR", "INIR", "OTIR", + "DB EDh,B4h", "DB EDh,B5h", "DB EDh,B6h", "DB EDh,B7h", + "LDDR", "CPDR", "INDR", "OTDR", + "DB EDh,BCh", "DB EDh,BDh", "DB EDh,BEh", "DB EDh,BFh", + "DB EDh,C0h", "DB EDh,C1h", "DB EDh,C2h", "DB EDh,C3h", + "DB EDh,C4h", "DB EDh,C5h", "DB EDh,C6h", "DB EDh,C7h", + "DB EDh,C8h", "DB EDh,C9h", "DB EDh,CAh", "DB EDh,CBh", + "DB EDh,CCh", "DB EDh,CDh", "DB EDh,CEh", "DB EDh,CFh", + "DB EDh,D0h", "DB EDh,D1h", "DB EDh,D2h", "DB EDh,D3h", + "DB EDh,D4h", "DB EDh,D5h", "DB EDh,D6h", "DB EDh,D7h", + "DB EDh,D8h", "DB EDh,D9h", "DB EDh,DAh", "DB EDh,DBh", + "DB EDh,DCh", "DB EDh,DDh", "DB EDh,DEh", "DB EDh,DFh", + "DB EDh,E0h", "DB EDh,E1h", "DB EDh,E2h", "DB EDh,E3h", + "DB EDh,E4h", "DB EDh,E5h", "DB EDh,E6h", "DB EDh,E7h", + "DB EDh,E8h", "DB EDh,E9h", "DB EDh,EAh", "DB EDh,EBh", + "DB EDh,ECh", "DB EDh,EDh", "DB EDh,EEh", "DB EDh,EFh", + "DB EDh,F0h", "DB EDh,F1h", "DB EDh,F2h", "DB EDh,F3h", + "DB EDh,F4h", "DB EDh,F5h", "DB EDh,F6h", "DB EDh,F7h", + "DB EDh,F8h", "DB EDh,F9h", "DB EDh,FAh", "DB EDh,FBh", + "DB EDh,FCh", "DB EDh,FDh", "DB EDh,FEh", "DB EDh,FFh" +}; + +static const char* MnemonicsXX[256] = +{ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", + "EX AF,AF'", "ADD I%,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", + "DJNZ @h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", + "JR @h", "ADD I%,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", + "JR NZ,@h", "LD I%,#h", "LD (#h),I%", "INC I%", "INC I%h", "DEC I%h", "LD I%h,*h", "DAA", + "JR Z,@h", "ADD I%,I%", "LD I%,(#h)", "DEC I%", "INC I%l", "DEC I%l", "LD I%l,*h", "CPL", + "JR NC,@h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (I%+^h)", "DEC (I%+^h)", "LD (I%+^h),*h", "SCF", + "JR C,@h", "ADD I%,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,I%h", "LD B,I%l", "LD B,(I%+^h)", "LD B,A", + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,I%h", "LD C,I%l", "LD C,(I%+^h)", "LD C,A", + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,I%h", "LD D,I%l", "LD D,(I%+^h)", "LD D,A", + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,I%h", "LD E,I%l", "LD E,(I%+^h)", "LD E,A", + "LD I%h,B", "LD I%h,C", "LD I%h,D", "LD I%h,E", "LD I%h,I%h", "LD I%h,I%l", "LD H,(I%+^h)", "LD I%h,A", + "LD I%l,B", "LD I%l,C", "LD I%l,D", "LD I%l,E", "LD I%l,I%h", "LD I%l,I%l", "LD L,(I%+^h)", "LD I%l,A", + "LD (I%+^h),B", "LD (I%+^h),C", "LD (I%+^h),D", "LD (I%+^h),E", "LD (I%+^h),H", "LD (I%+^h),L", "HALT", "LD (I%+^h),A", + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,I%h", "LD A,I%l", "LD A,(I%+^h)", "LD A,A", + "ADD B", "ADD C", "ADD D", "ADD E", "ADD I%h", "ADD I%l", "ADD (I%+^h)", "ADD A", + "ADC B", "ADC C", "ADC D", "ADC E", "ADC I%h", "ADC I%l", "ADC (I%+^h)", "ADC,A", + "SUB B", "SUB C", "SUB D", "SUB E", "SUB I%h", "SUB I%l", "SUB (I%+^h)", "SUB A", + "SBC B", "SBC C", "SBC D", "SBC E", "SBC I%h", "SBC I%l", "SBC (I%+^h)", "SBC A", + "AND B", "AND C", "AND D", "AND E", "AND I%h", "AND I%l", "AND (I%+^h)", "AND A", + "XOR B", "XOR C", "XOR D", "XOR E", "XOR I%h", "XOR I%l", "XOR (I%+^h)", "XOR A", + "OR B", "OR C", "OR D", "OR E", "OR I%h", "OR I%l", "OR (I%+^h)", "OR A", + "CP B", "CP C", "CP D", "CP E", "CP I%h", "CP I%l", "CP (I%+^h)", "CP A", + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD *h", "RST 00h", + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC *h", "RST 08h", + "RET NC", "POP DE", "JP NC,#h", "OUTA (*h)", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", + "RET C", "EXX", "JP C,#h", "INA (*h)", "CALL C,#h", "PFX_DD", "SBC *h", "RST 18h", + "RET PO", "POP I%", "JP PO,#h", "EX I%,(SP)", "CALL PO,#h", "PUSH I%", "AND *h", "RST 20h", + "RET PE", "LD PC,I%", "JP PE,#h", "EX DE,I%", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", + "RET M", "LD SP,I%", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" +}; + +static const char* MnemonicsXCB[256] = +{ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (I%@h)", "RLC A", + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (I%@h)", "RRC A", + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (I%@h)", "RL A", + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (I%@h)", "RR A", + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (I%@h)", "SLA A", + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (I%@h)", "SRA A", + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (I%@h)", "SLL A", + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (I%@h)", "SRL A", + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(I%@h)", "BIT 0,A", + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(I%@h)", "BIT 1,A", + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(I%@h)", "BIT 2,A", + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(I%@h)", "BIT 3,A", + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(I%@h)", "BIT 4,A", + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(I%@h)", "BIT 5,A", + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(I%@h)", "BIT 6,A", + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(I%@h)", "BIT 7,A", + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(I%@h)", "RES 0,A", + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(I%@h)", "RES 1,A", + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(I%@h)", "RES 2,A", + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(I%@h)", "RES 3,A", + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(I%@h)", "RES 4,A", + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(I%@h)", "RES 5,A", + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(I%@h)", "RES 6,A", + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(I%@h)", "RES 7,A", + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(I%@h)", "SET 0,A", + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(I%@h)", "SET 1,A", + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(I%@h)", "SET 2,A", + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(I%@h)", "SET 3,A", + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(I%@h)", "SET 4,A", + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(I%@h)", "SET 5,A", + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(I%@h)", "SET 6,A", + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(I%@h)", "SET 7,A" +}; + +int32_t Watch = -1; +#endif + +/* Memory management */ +static uint8_t GET_BYTE(uint32_t Addr) { + return _RamRead(Addr & ADDRMASK); +} + +static void PUT_BYTE(uint32_t Addr, uint32_t Value) { + _RamWrite(Addr & ADDRMASK, Value); +} + +static uint16_t GET_WORD(uint32_t a) { + return GET_BYTE(a) | (GET_BYTE(a + 1) << 8); +} + +static void PUT_WORD(uint32_t Addr, uint32_t Value) { + _RamWrite(Addr, Value); + _RamWrite(++Addr, Value >> 8); +} + +#define RAM_MM(a) GET_BYTE(a--) +#define RAM_PP(a) GET_BYTE(a++) + +#define PUT_BYTE_PP(a,v) PUT_BYTE(a++, v) +#define PUT_BYTE_MM(a,v) PUT_BYTE(a--, v) +#define MM_PUT_BYTE(a,v) PUT_BYTE(--a, v) + +#define PUSH(x) do { \ + MM_PUT_BYTE(SP, (x) >> 8); \ + MM_PUT_BYTE(SP, x); \ +} while (0) + +/* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR + +Pre condition +temp == value of register B at entry of the instruction +acu == value of transferred byte (IN or OUT) +Post condition +F is set correctly + +Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where +x == (C + 1) & 0xff for INIR +x == L for OTIR and OTDR +x == (C - 1) & 0xff for INDR +Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where +x == (C + 1) & 0xff for INI +x == L for OUTI and OUTD +x == (C - 1) & 0xff for IND +*/ +#define INOUTFLAGS(syxz, x) \ + AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7) ^ temp] /* PF */ + +#define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) +#define INOUTFLAGS_NONZERO(x) \ + INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) + +static inline void Z80reset(void) { + PC = 0; + IFF = 0; + IR = 0; + Debug = 0; + Break = -1; + Step = -1; +} + +#ifdef DEBUG +void watchprint(uint16_t pos) { + uint8_t I, J; + printf("\r\n"); + printf(" Watch : %04X",Watch); + printf(" = %02X",_RamRead(Watch)); printf(":%02X",_RamRead(Watch + 1)); + printf(" / "); + for (J = 0, I = _RamRead(Watch); J < 8; ++J, I <<= 1) putchar(I & 0x80 ? '1' : '0'); + putchar(':'); + for (J = 0, I = _RamRead(Watch + 1); J < 8; ++J, I <<= 1) putchar(I & 0x80 ? '1' : '0'); +} + +void memdump(uint16_t pos) { + uint16_t h = pos; + uint16_t c = pos; + uint8_t l, i; + uint8_t ch = pos & 0xff; + + printf(" "); + for (i = 0; i < 16; ++i) { + printf("%02X ",ch++ & 0x0f); + } + printf("\r\n"); + printf(" "); + for (i = 0; i < 16; ++i) + printf("---"); + printf("\r\n"); + for (l = 0; l < 16; ++l) { + printf("%04X : ",h); + for (i = 0; i < 16; ++i) { + printf("%02X ",_RamRead(h++)); + } + for (i = 0; i < 16; ++i) { + ch = _RamRead(c++); + putchar(ch > 31 && ch < 127 ? ch : '.'); + } + printf("\r\n"); + } +} + +uint8_t Disasm(uint16_t pos) { + const char* txt; + char jr; + uint8_t ch = _RamRead(pos); + uint8_t count = 1; + uint8_t C = 0; + + switch (ch) { + case 0xCB: ++pos; txt = MnemonicsCB[_RamRead(pos++)]; count++; break; + case 0xED: ++pos; txt = MnemonicsED[_RamRead(pos++)]; count++; break; + case 0xDD: ++pos; C = 'X'; + if (_RamRead(pos) != 0xCB) { + txt = MnemonicsXX[_RamRead(pos++)]; ++count; + } else { + ++pos; txt = MnemonicsXCB[_RamRead(pos++)]; count += 2; + } + break; + case 0xFD: ++pos; C = 'Y'; + if (_RamRead(pos) != 0xCB) { + txt = MnemonicsXX[_RamRead(pos++)]; ++count; + } else { + ++pos; txt = MnemonicsXCB[_RamRead(pos++)]; count += 2; + } + break; + default: txt = Mnemonics[_RamRead(pos++)]; + } + while (*txt != 0) { + switch (*txt) { + case '*': + txt += 2; + ++count; + printf("%02X",_RamRead(pos++)); + break; + case '^': + txt += 2; + ++count; + printf("%02X",_RamRead(pos++)); + break; + case '#': + txt += 2; + count += 2; + printf("%02X",_RamRead(pos + 1)); + printf("%02X",_RamRead(pos)); + break; + case '@': + txt += 2; + ++count; + jr = _RamRead(pos++); + printf("%04X",pos + jr); + break; + case '%': + putchar(C); + ++txt; + break; + default: + putchar(*txt); + ++txt; + } + } + + return(count); +} + +void Z80debug(void) { + uint8_t ch = 0; + uint16_t pos, l; + static const char Flags[9] = "SZ5H3PNC"; + uint8_t J, I; + unsigned int bpoint; + uint8_t loop = 1; + uint8_t res = 0; + + printf("\r\nDebug Mode - Press '?' for help"); + + while (loop) { + pos = PC; + printf("\r\n"); + printf("BC:%04X",BC); + printf(" DE:%04X",DE); + printf(" HL:%04X",HL); + printf(" AF:%04X",AF); + printf(" : ["); + for (J = 0, I = LOW_REGISTER(AF); J < 8; ++J, I <<= 1) putchar(I & 0x80 ? Flags[J] : '.'); + printf("]\r\n"); + printf("IX:%04X",IX); + printf(" IY:%04X",IY); + printf(" SP:%04X",SP); + printf(" PC:%04X",PC); + printf(" : "); + + Disasm(pos); + + if (Watch != -1) { + watchprint(Watch); + } + + printf("\r\n"); + printf("Command|? : "); + ch = getchar(); + if (ch > 21 && ch < 127) + putchar(ch); + switch (ch) { + case 't': + loop = 0; + break; + case 'c': + loop = 0; + printf("\r\n"); + Debug = 0; + break; + case 'b': + printf("\r\n"); memdump(BC); break; + case 'd': + printf("\r\n"); memdump(DE); break; + case 'h': + printf("\r\n"); memdump(HL); break; + case 'p': + printf("\r\n"); memdump(PC & 0xFF00); break; + case 's': + printf("\r\n"); memdump(SP & 0xFF00); break; + case 'x': + printf("\r\n"); memdump(IX & 0xFF00); break; + case 'y': + printf("\r\n"); memdump(IY & 0xFF00); break; + case 'l': + printf("\r\n"); + I = 16; + l = pos; + while (I > 0) { + printf("%04X",l); + printf(" : "); + l += Disasm(l); + printf("\r\n"); + --I; + } + break; + case 'i': + printf(" Addr: "); + res=scanf("%02x", &bpoint); + printf("\r\nIO Read %02X: %02X\r\n", bpoint, cpu_in(bpoint)); + break; + case 'B': + printf(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + Break = bpoint; + printf("Breakpoint set to %04X\r\n",Break); + } + break; + case 'C': + Break = -1; + printf(" Breakpoint cleared\r\n"); + break; + case 'D': + printf(" Addr: "); + res=scanf("%04x", &bpoint); + if(res) + memdump(bpoint); + break; + case 'L': + printf(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + I = 16; + l = bpoint; + while (I > 0) { + printf("%04X : ",l); + l += Disasm(l); + printf("\r\n"); + --I; + } + } + break; + case 'T': + loop = 0; + Step = pos + 3; // This only works correctly with CALL + // If the called function messes with the stack, this will fail as well. + Debug = 0; + break; + case 'W': + printf(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + Watch = bpoint; + printf("Watch set to %04X\r\n",Watch); + } + break; + case '?': + printf("\r\n"); + printf("Lowercase commands:\r\n"); + printf(" t - traces to the next instruction\r\n"); + printf(" c - Continue execution\r\n"); + printf(" b - Dumps memory pointed by (BC)\r\n"); + printf(" d - Dumps memory pointed by (DE)\r\n"); + printf(" h - Dumps memory pointed by (HL)\r\n"); + printf(" p - Dumps the page (PC) points to\r\n"); + printf(" s - Dumps the page (SP) points to\r\n"); + printf(" x - Dumps the page (IX) points to\r\n"); + printf(" y - Dumps the page (IY) points to\r\n"); + printf(" l - Disassembles from current PC\r\n"); + printf("Uppercase commands:\r\n"); + printf(" B - Sets breakpoint at address\r\n"); + printf(" C - Clears breakpoint\r\n"); + printf(" D - Dumps memory at address\r\n"); + printf(" L - Disassembles at address\r\n"); + printf(" T - Steps over a call\r\n"); + printf(" W - Sets a byte/word watch\r\n"); + break; + default: + printf(" ???\r\n"); + } + } +} +#endif + +static inline void Z80run(void) { + uint32_t temp = 0; + uint32_t acu; + uint32_t sum; + uint32_t cbits; + uint32_t op; + uint32_t adr; + + /* main instruction fetch/decode loop */ + while (!Z80break) { /* loop until Status != 0 */ + if(z80_nmi) { + z80_nmi = 0; // Clear NMI Pending + IFF <<= 1; // IFF2 = IFF1 + PUSH(PC); // PC -> Stack + PC = 0x66; // NMI Vector + } + if(z80_irq) { + IFF = 0; + if(IM == 2) { + PUSH(PC); + SET_HIGH_REGISTER(PC, HIGH_REGISTER(IR)); + SET_LOW_REGISTER(PC, z80_vect); + } else if(IM == 1) { + PUSH(PC); + PC = 0x38; // IRQ Vector + } else { + switch(z80_vect & 0xff0000) { + case 0xcd0000: + PUSH(PC); + PC = z80_vect & 0xffff; + break; + case 0xc30000: + PC = z80_vect & 0xffff; + break; + default: + PUSH(PC); + PC = z80_vect & 0x38; + break; + } + } + z80_irq = 0; + } + +#ifdef DEBUG + if (PC == Break) { + printf(":BREAK at %04X:",Break); + Debug = 1; + } + if (PC == Step) { + Debug = 1; + Step = -1; + } + if (Debug) + Z80debug(); +#endif + + PCX = PC; + INCR(1); /* Add one M1 cycle to refresh counter */ + +#ifdef iDEBUG + iLogFile = fopen("iDump.log", "a"); + switch (RAM[PCX & 0xffff]) { + case 0xCB: iLogTxt = MnemonicsCB[RAM[(PCX & 0xffff) + 1]]; break; + case 0xED: iLogTxt = MnemonicsED[RAM[(PCX & 0xffff) + 1]]; break; + case 0xDD: + case 0xFD: + if (RAM[PCX & 0xffff] == 0xCB) { + iLogTxt = MnemonicsXCB[RAM[(PCX & 0xffff) + 1]]; break; + } else { + iLogTxt = MnemonicsXX[RAM[(PCX & 0xffff) + 1]]; break; + } + default: iLogTxt = Mnemonics[RAM[PCX & 0xffff]]; + } + sprintf(iLogBuffer, "0x%04x : 0x%02x = %s\n", PCX, RAM[PCX & 0xffff], iLogTxt); + fprintf(iLogBuffer, iLogFile); + fclose(iLogFile); +#endif + + if(Halted) { + PC--; + goto end_decode; + } + + switch (RAM_PP(PC)) { + + case 0x00: /* NOP */ + break; + + case 0x01: /* LD BC,nnnn */ + BC = GET_WORD(PC++); + PC++; + break; + + case 0x02: /* LD (BC),A */ + PUT_BYTE(BC, HIGH_REGISTER(AF)); + break; + + case 0x03: /* INC BC */ + ++BC; + break; + + case 0x04: /* INC B */ + BC += 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x05: /* DEC B */ + BC -= 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x06: /* LD B,nn */ + SET_HIGH_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x07: /* RLCA */ + AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x08: /* EX AF,AF' */ + temp = AF; + AF = AF1; + AF1 = temp; + break; + + case 0x09: /* ADD HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x0a: /* LD A,(BC) */ + SET_HIGH_REGISTER(AF, GET_BYTE(BC)); + break; + + case 0x0b: /* DEC BC */ + --BC; + break; + + case 0x0c: /* INC C */ + temp = LOW_REGISTER(BC) + 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x0d: /* DEC C */ + temp = LOW_REGISTER(BC) - 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x0e: /* LD C,nn */ + SET_LOW_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x0f: /* RRCA */ + AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; + break; + + case 0x10: /* DJNZ dd */ + if ((BC -= 0x100) & 0xff00) + PC += (int8_t)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x11: /* LD DE,nnnn */ + DE = GET_WORD(PC++); + PC++; + break; + + case 0x12: /* LD (DE),A */ + PUT_BYTE(DE, HIGH_REGISTER(AF)); + break; + + case 0x13: /* INC DE */ + ++DE; + break; + + case 0x14: /* INC D */ + DE += 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x15: /* DEC D */ + DE -= 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x16: /* LD D,nn */ + SET_HIGH_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x17: /* RLA */ + AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x18: /* JR dd */ + PC += (int8_t)GET_BYTE(PC) + 1; + break; + + case 0x19: /* ADD HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x1a: /* LD A,(DE) */ + SET_HIGH_REGISTER(AF, GET_BYTE(DE)); + break; + + case 0x1b: /* DEC DE */ + --DE; + break; + + case 0x1c: /* INC E */ + temp = LOW_REGISTER(DE) + 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x1d: /* DEC E */ + temp = LOW_REGISTER(DE) - 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x1e: /* LD E,nn */ + SET_LOW_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x1f: /* RRA */ + AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; + break; + + case 0x20: /* JR NZ,dd */ + if (TSTFLAG(Z)) + ++PC; + else + PC += (int8_t)GET_BYTE(PC) + 1; + break; + + case 0x21: /* LD HL,nnnn */ + HL = GET_WORD(PC++); + PC++; + break; + + case 0x22: /* LD (nnnn),HL */ + PUT_WORD(GET_WORD(PC++), HL); + PC++; + break; + + case 0x23: /* INC HL */ + ++HL; + break; + + case 0x24: /* INC H */ + HL += 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x25: /* DEC H */ + HL -= 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x26: /* LD H,nn */ + SET_HIGH_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x27: /* DAA */ + acu = HIGH_REGISTER(AF); + temp = LOW_DIGIT(acu); + cbits = TSTFLAG(C); + if (TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) + SETFLAG(H, 0); + acu -= 6; + acu &= 0xff; + } + if (hd) + acu -= 0x160; /* adjust high digit */ + } else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) + acu += 0x60; /* adjust high digit */ + } + AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; + break; + + case 0x28: /* JR Z,dd */ + if (TSTFLAG(Z)) + PC += (int8_t)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x29: /* ADD HL,HL */ + HL &= ADDRMASK; + sum = HL + HL; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + HL = sum; + break; + + case 0x2a: /* LD HL,(nnnn) */ + HL = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x2b: /* DEC HL */ + --HL; + break; + + case 0x2c: /* INC L */ + temp = LOW_REGISTER(HL) + 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x2d: /* DEC L */ + temp = LOW_REGISTER(HL) - 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x2e: /* LD L,nn */ + SET_LOW_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x2f: /* CPL */ + AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + break; + + case 0x30: /* JR NC,dd */ + if (TSTFLAG(C)) + ++PC; + else + PC += (int8_t)GET_BYTE(PC) + 1; + break; + + case 0x31: /* LD SP,nnnn */ + SP = GET_WORD(PC++); + PC++; + break; + + case 0x32: /* LD (nnnn),A */ + PUT_BYTE(GET_WORD(PC++), HIGH_REGISTER(AF)); + PC++; + break; + + case 0x33: /* INC SP */ + ++SP; + break; + + case 0x34: /* INC (HL) */ + temp = GET_BYTE(HL) + 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x35: /* DEC (HL) */ + temp = GET_BYTE(HL) - 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x36: /* LD (HL),nn */ + PUT_BYTE(HL, RAM_PP(PC)); + break; + + case 0x37: /* SCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; + break; + + case 0x38: /* JR C,dd */ + if (TSTFLAG(C)) + PC += (int8_t)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x39: /* ADD HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x3a: /* LD A,(nnnn) */ + SET_HIGH_REGISTER(AF, GET_BYTE(GET_WORD(PC++))); + PC++; + break; + + case 0x3b: /* DEC SP */ + --SP; + break; + + case 0x3c: /* INC A */ + AF += 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x3d: /* DEC A */ + AF -= 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x3e: /* LD A,nn */ + SET_HIGH_REGISTER(AF, RAM_PP(PC)); + break; + + case 0x3f: /* CCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); + break; + + case 0x40: /* LD B,B */ + break; + + case 0x41: /* LD B,C */ + BC = (BC & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x42: /* LD B,D */ + BC = (BC & 0xff) | (DE & ~0xff); + break; + + case 0x43: /* LD B,E */ + BC = (BC & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x44: /* LD B,H */ + BC = (BC & 0xff) | (HL & ~0xff); + break; + + case 0x45: /* LD B,L */ + BC = (BC & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x46: /* LD B,(HL) */ + SET_HIGH_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x47: /* LD B,A */ + BC = (BC & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* LD C,B */ + BC = (BC & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x49: /* LD C,C */ + break; + + case 0x4a: /* LD C,D */ + BC = (BC & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x4b: /* LD C,E */ + BC = (BC & ~0xff) | (DE & 0xff); + break; + + case 0x4c: /* LD C,H */ + BC = (BC & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x4d: /* LD C,L */ + BC = (BC & ~0xff) | (HL & 0xff); + break; + + case 0x4e: /* LD C,(HL) */ + SET_LOW_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x4f: /* LD C,A */ + BC = (BC & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* LD D,B */ + DE = (DE & 0xff) | (BC & ~0xff); + break; + + case 0x51: /* LD D,C */ + DE = (DE & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x52: /* LD D,D */ + break; + + case 0x53: /* LD D,E */ + DE = (DE & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x54: /* LD D,H */ + DE = (DE & 0xff) | (HL & ~0xff); + break; + + case 0x55: /* LD D,L */ + DE = (DE & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x56: /* LD D,(HL) */ + SET_HIGH_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x57: /* LD D,A */ + DE = (DE & 0xff) | (AF & ~0xff); + break; + + case 0x58: /* LD E,B */ + DE = (DE & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x59: /* LD E,C */ + DE = (DE & ~0xff) | (BC & 0xff); + break; + + case 0x5a: /* LD E,D */ + DE = (DE & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x5b: /* LD E,E */ + break; + + case 0x5c: /* LD E,H */ + DE = (DE & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x5d: /* LD E,L */ + DE = (DE & ~0xff) | (HL & 0xff); + break; + + case 0x5e: /* LD E,(HL) */ + SET_LOW_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x5f: /* LD E,A */ + DE = (DE & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x60: /* LD H,B */ + HL = (HL & 0xff) | (BC & ~0xff); + break; + + case 0x61: /* LD H,C */ + HL = (HL & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x62: /* LD H,D */ + HL = (HL & 0xff) | (DE & ~0xff); + break; + + case 0x63: /* LD H,E */ + HL = (HL & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x64: /* LD H,H */ + break; + + case 0x65: /* LD H,L */ + HL = (HL & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x66: /* LD H,(HL) */ + SET_HIGH_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x67: /* LD H,A */ + HL = (HL & 0xff) | (AF & ~0xff); + break; + + case 0x68: /* LD L,B */ + HL = (HL & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x69: /* LD L,C */ + HL = (HL & ~0xff) | (BC & 0xff); + break; + + case 0x6a: /* LD L,D */ + HL = (HL & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x6b: /* LD L,E */ + HL = (HL & ~0xff) | (DE & 0xff); + break; + + case 0x6c: /* LD L,H */ + HL = (HL & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x6d: /* LD L,L */ + break; + + case 0x6e: /* LD L,(HL) */ + SET_LOW_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x6f: /* LD L,A */ + HL = (HL & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x70: /* LD (HL),B */ + PUT_BYTE(HL, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (HL),C */ + PUT_BYTE(HL, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (HL),D */ + PUT_BYTE(HL, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (HL),E */ + PUT_BYTE(HL, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (HL),H */ + PUT_BYTE(HL, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (HL),L */ + PUT_BYTE(HL, LOW_REGISTER(HL)); + break; + + case 0x76: /* HALT */ +#ifdef DEBUG + printf("\r\n::CPU HALTED::"); // A halt is a good indicator of broken code + printf("Press any key..."); + getchar(); +#endif + Halted = 1; + goto end_decode; + break; + + case 0x77: /* LD (HL),A */ + PUT_BYTE(HL, HIGH_REGISTER(AF)); + break; + + case 0x78: /* LD A,B */ + AF = (AF & 0xff) | (BC & ~0xff); + break; + + case 0x79: /* LD A,C */ + AF = (AF & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x7a: /* LD A,D */ + AF = (AF & 0xff) | (DE & ~0xff); + break; + + case 0x7b: /* LD A,E */ + AF = (AF & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x7c: /* LD A,H */ + AF = (AF & 0xff) | (HL & ~0xff); + break; + + case 0x7d: /* LD A,L */ + AF = (AF & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x7e: /* LD A,(HL) */ + SET_HIGH_REGISTER(AF, GET_BYTE(HL)); + break; + + case 0x7f: /* LD A,A */ + break; + + case 0x80: /* ADD A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x81: /* ADD A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x82: /* ADD A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x83: /* ADD A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x84: /* ADD A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x85: /* ADD A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x86: /* ADD A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x87: /* ADD A,A */ + cbits = 2 * HIGH_REGISTER(AF); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x88: /* ADC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x89: /* ADC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8a: /* ADC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8b: /* ADC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8c: /* ADC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8d: /* ADC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8e: /* ADC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8f: /* ADC A,A */ + cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x90: /* SUB B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x91: /* SUB C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x92: /* SUB D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x93: /* SUB E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x94: /* SUB H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x95: /* SUB L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x96: /* SUB (HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x97: /* SUB A */ + AF = 0x42; + break; + + case 0x98: /* SBC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x99: /* SBC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9a: /* SBC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9b: /* SBC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9c: /* SBC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9d: /* SBC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9e: /* SBC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9f: /* SBC A,A */ + cbits = -TSTFLAG(C); + AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); + break; + + case 0xa0: /* AND B */ + AF = andTable[((AF & BC) >> 8) & 0xff]; + break; + + case 0xa1: /* AND C */ + AF = andTable[((AF >> 8)& BC) & 0xff]; + break; + + case 0xa2: /* AND D */ + AF = andTable[((AF & DE) >> 8) & 0xff]; + break; + + case 0xa3: /* AND E */ + AF = andTable[((AF >> 8)& DE) & 0xff]; + break; + + case 0xa4: /* AND H */ + AF = andTable[((AF & HL) >> 8) & 0xff]; + break; + + case 0xa5: /* AND L */ + AF = andTable[((AF >> 8)& HL) & 0xff]; + break; + + case 0xa6: /* AND (HL) */ + AF = andTable[((AF >> 8)& GET_BYTE(HL)) & 0xff]; + break; + + case 0xa7: /* AND A */ + AF = andTable[(AF >> 8) & 0xff]; + break; + + case 0xa8: /* XOR B */ + AF = xororTable[((AF ^ BC) >> 8) & 0xff]; + break; + + case 0xa9: /* XOR C */ + AF = xororTable[((AF >> 8) ^ BC) & 0xff]; + break; + + case 0xaa: /* XOR D */ + AF = xororTable[((AF ^ DE) >> 8) & 0xff]; + break; + + case 0xab: /* XOR E */ + AF = xororTable[((AF >> 8) ^ DE) & 0xff]; + break; + + case 0xac: /* XOR H */ + AF = xororTable[((AF ^ HL) >> 8) & 0xff]; + break; + + case 0xad: /* XOR L */ + AF = xororTable[((AF >> 8) ^ HL) & 0xff]; + break; + + case 0xae: /* XOR (HL) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(HL)) & 0xff]; + break; + + case 0xaf: /* XOR A */ + AF = 0x44; + break; + + case 0xb0: /* OR B */ + AF = xororTable[((AF | BC) >> 8) & 0xff]; + break; + + case 0xb1: /* OR C */ + AF = xororTable[((AF >> 8) | BC) & 0xff]; + break; + + case 0xb2: /* OR D */ + AF = xororTable[((AF | DE) >> 8) & 0xff]; + break; + + case 0xb3: /* OR E */ + AF = xororTable[((AF >> 8) | DE) & 0xff]; + break; + + case 0xb4: /* OR H */ + AF = xororTable[((AF | HL) >> 8) & 0xff]; + break; + + case 0xb5: /* OR L */ + AF = xororTable[((AF >> 8) | HL) & 0xff]; + break; + + case 0xb6: /* OR (HL) */ + AF = xororTable[((AF >> 8) | GET_BYTE(HL)) & 0xff]; + break; + + case 0xb7: /* OR A */ + AF = xororTable[(AF >> 8) & 0xff]; + break; + + case 0xb8: /* CP B */ + temp = HIGH_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xb9: /* CP C */ + temp = LOW_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xba: /* CP D */ + temp = HIGH_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbb: /* CP E */ + temp = LOW_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbc: /* CP H */ + temp = HIGH_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbd: /* CP L */ + temp = LOW_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbe: /* CP (HL) */ + temp = GET_BYTE(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbf: /* CP A */ + SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | 0x42); + break; + + case 0xc0: /* RET NZ */ + if (!(TSTFLAG(Z))) + POP(PC); + break; + + case 0xc1: /* POP BC */ + POP(BC); + break; + + case 0xc2: /* JP NZ,nnnn */ + JPC(!TSTFLAG(Z)); + break; + + case 0xc3: /* JP nnnn */ + JPC(1); + break; + + case 0xc4: /* CALL NZ,nnnn */ + CALLC(!TSTFLAG(Z)); + break; + + case 0xc5: /* PUSH BC */ + PUSH(BC); + break; + + case 0xc6: /* ADD A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xc7: /* RST 0 */ + PUSH(PC); + PC = 0; + break; + + case 0xc8: /* RET Z */ + if (TSTFLAG(Z)) + POP(PC); + break; + + case 0xc9: /* RET */ + POP(PC); + break; + + case 0xca: /* JP Z,nnnn */ + JPC(TSTFLAG(Z)); + break; + + case 0xcb: /* CB prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + adr = HL; + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xcc: /* CALL Z,nnnn */ + CALLC(TSTFLAG(Z)); + break; + + case 0xcd: /* CALL nnnn */ + CALLC(1); + break; + + case 0xce: /* ADC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xcf: /* RST 8 */ + PUSH(PC); + PC = 8; + break; + + case 0xd0: /* RET NC */ + if (!(TSTFLAG(C))) + POP(PC); + break; + + case 0xd1: /* POP DE */ + POP(DE); + break; + + case 0xd2: /* JP NC,nnnn */ + JPC(!TSTFLAG(C)); + break; + + case 0xd3: /* OUT (nn),A */ + cpu_out(RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0xd4: /* CALL NC,nnnn */ + CALLC(!TSTFLAG(C)); + break; + + case 0xd5: /* PUSH DE */ + PUSH(DE); + break; + + case 0xd6: /* SUB nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xd7: /* RST 10H */ + PUSH(PC); + PC = 0x10; + break; + + case 0xd8: /* RET C */ + if (TSTFLAG(C)) + POP(PC); + break; + + case 0xd9: /* EXX */ + temp = BC; + BC = BC1; + BC1 = temp; + temp = DE; + DE = DE1; + DE1 = temp; + temp = HL; + HL = HL1; + HL1 = temp; + break; + + case 0xda: /* JP C,nnnn */ + JPC(TSTFLAG(C)); + break; + + case 0xdb: /* IN A,(nn) */ + SET_HIGH_REGISTER(AF, cpu_in(RAM_PP(PC))); + break; + + case 0xdc: /* CALL C,nnnn */ + CALLC(TSTFLAG(C)); + break; + + case 0xdd: /* DD prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IX,BC */ + IX &= ADDRMASK; + BC &= ADDRMASK; + sum = IX + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; + IX = sum; + break; + + case 0x19: /* ADD IX,DE */ + IX &= ADDRMASK; + DE &= ADDRMASK; + sum = IX + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; + IX = sum; + break; + + case 0x21: /* LD IX,nnnn */ + IX = GET_WORD(PC++); + PC++; + break; + + case 0x22: /* LD (nnnn),IX */ + PUT_WORD(GET_WORD(PC++), IX); + PC++; + break; + + case 0x23: /* INC IX */ + ++IX; + break; + + case 0x24: /* INC IXH */ + IX += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x25: /* DEC IXH */ + IX -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x26: /* LD IXH,nn */ + SET_HIGH_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x29: /* ADD IX,IX */ + IX &= ADDRMASK; + sum = IX + IX; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IX = sum; + break; + + case 0x2a: /* LD IX,(nnnn) */ + IX = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x2b: /* DEC IX */ + --IX; + break; + + case 0x2c: /* INC IXL */ + temp = LOW_REGISTER(IX) + 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IXL */ + temp = LOW_REGISTER(IX) - 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IXL,nn */ + SET_LOW_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x34: /* INC (IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IX+dd),nn */ + adr = IX + (int8_t)RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IX,SP */ + IX &= ADDRMASK; + SP &= ADDRMASK; + sum = IX + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; + IX = sum; + break; + + case 0x44: /* LD B,IXH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x45: /* LD B,IXL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x46: /* LD B,(IX+dd) */ + SET_HIGH_REGISTER(BC, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x4c: /* LD C,IXH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x4d: /* LD C,IXL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x4e: /* LD C,(IX+dd) */ + SET_LOW_REGISTER(BC, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x54: /* LD D,IXH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x55: /* LD D,IXL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x56: /* LD D,(IX+dd) */ + SET_HIGH_REGISTER(DE, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x5c: /* LD E,IXH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x5d: /* LD E,IXL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x5e: /* LD E,(IX+dd) */ + SET_LOW_REGISTER(DE, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x60: /* LD IXH,B */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IXH,C */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IXH,D */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IXH,E */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IXH,IXH */ + break; + + case 0x65: /* LD IXH,IXL */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); + break; + + case 0x66: /* LD H,(IX+dd) */ + SET_HIGH_REGISTER(HL, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x67: /* LD IXH,A */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IXL,B */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IXL,C */ + SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IXL,D */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IXL,E */ + SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IXL,IXH */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); + break; + + case 0x6d: /* LD IXL,IXL */ + break; + + case 0x6e: /* LD L,(IX+dd) */ + SET_LOW_REGISTER(HL, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x6f: /* LD IXL,A */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IX+dd),B */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IX+dd),C */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IX+dd),D */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IX+dd),E */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IX+dd),H */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IX+dd),L */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IX+dd),A */ + PUT_BYTE(IX + (int8_t)RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IXH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); + break; + + case 0x7d: /* LD A,IXL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); + break; + + case 0x7e: /* LD A,(IX+dd) */ + SET_HIGH_REGISTER(AF, GET_BYTE(IX + (int8_t)RAM_PP(PC))); + break; + + case 0x84: /* ADD A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IXH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IXL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IXH */ + AF = andTable[((AF & IX) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IXL */ + AF = andTable[((AF >> 8)& IX) & 0xff]; + break; + + case 0xa6: /* AND (IX+dd) */ + AF = andTable[((AF >> 8)& GET_BYTE(IX + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xac: /* XOR IXH */ + AF = xororTable[((AF ^ IX) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IXL */ + AF = xororTable[((AF >> 8) ^ IX) & 0xff]; + break; + + case 0xae: /* XOR (IX+dd) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(IX + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xb4: /* OR IXH */ + AF = xororTable[((AF | IX) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IXL */ + AF = xororTable[((AF >> 8) | IX) & 0xff]; + break; + + case 0xb6: /* OR (IX+dd) */ + AF = xororTable[((AF >> 8) | GET_BYTE(IX + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xbc: /* CP IXH */ + temp = HIGH_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IXL */ + temp = LOW_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IX+dd) */ + adr = IX + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IX + (int8_t)RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IX */ + POP(IX); + break; + + case 0xe3: /* EX (SP),IX */ + temp = IX; + POP(IX); + PUSH(temp); + break; + + case 0xe5: /* PUSH IX */ + PUSH(IX); + break; + + case 0xe9: /* JP (IX) */ + PC = IX; + break; + + case 0xf9: /* LD SP,IX */ + SP = IX; + break; + + default: /* ignore DD */ + --PC; + } + break; + + case 0xde: /* SBC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xdf: /* RST 18H */ + PUSH(PC); + PC = 0x18; + break; + + case 0xe0: /* RET PO */ + if (!(TSTFLAG(P))) + POP(PC); + break; + + case 0xe1: /* POP HL */ + POP(HL); + break; + + case 0xe2: /* JP PO,nnnn */ + JPC(!TSTFLAG(P)); + break; + + case 0xe3: /* EX (SP),HL */ + temp = HL; + POP(HL); + PUSH(temp); + break; + + case 0xe4: /* CALL PO,nnnn */ + CALLC(!TSTFLAG(P)); + break; + + case 0xe5: /* PUSH HL */ + PUSH(HL); + break; + + case 0xe6: /* AND nn */ + AF = andTable[((AF >> 8)& RAM_PP(PC)) & 0xff]; + break; + + case 0xe7: /* RST 20H */ + PUSH(PC); + PC = 0x20; + break; + + case 0xe8: /* RET PE */ + if (TSTFLAG(P)) + POP(PC); + break; + + case 0xe9: /* JP (HL) */ + PC = HL; + break; + + case 0xea: /* JP PE,nnnn */ + JPC(TSTFLAG(P)); + break; + + case 0xeb: /* EX DE,HL */ + temp = HL; + HL = DE; + DE = temp; + break; + + case 0xec: /* CALL PE,nnnn */ + CALLC(TSTFLAG(P)); + break; + + case 0xed: /* ED prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x40: /* IN B,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x41: /* OUT (C),B */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); + break; + + case 0x42: /* SBC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL - BC - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x43: /* LD (nnnn),BC */ + PUT_WORD(GET_WORD(PC++), BC); + PC++; + break; + + case 0x44: /* NEG */ + + case 0x4C: /* NEG, unofficial */ + + case 0x54: /* NEG, unofficial */ + + case 0x5C: /* NEG, unofficial */ + + case 0x64: /* NEG, unofficial */ + + case 0x6C: /* NEG, unofficial */ + + case 0x74: /* NEG, unofficial */ + + case 0x7C: /* NEG, unofficial */ + temp = HIGH_REGISTER(AF); + AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; + break; + + case 0x45: /* RETN */ + + case 0x55: /* RETN, unofficial */ + + case 0x5D: /* RETN, unofficial */ + + case 0x65: /* RETN, unofficial */ + + case 0x6D: /* RETN, unofficial */ + + case 0x75: /* RETN, unofficial */ + + case 0x7D: /* RETN, unofficial */ + IFF |= IFF >> 1; + POP(PC); + break; + + case 0x46: /* IM 0 */ + IM = 0; /* interrupt mode 0 */ + break; + + case 0x47: /* LD I,A */ + IR = (IR & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* IN C,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x49: /* OUT (C),C */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(BC)); + break; + + case 0x4a: /* ADC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x4b: /* LD BC,(nnnn) */ + BC = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x4d: /* RETI */ + IFF |= IFF >> 1; + POP(PC); + break; + + case 0x4f: /* LD R,A */ + IR = (IR & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* IN D,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x51: /* OUT (C),D */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); + break; + + case 0x52: /* SBC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL - DE - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x53: /* LD (nnnn),DE */ + PUT_WORD(GET_WORD(PC++), DE); + PC++; + break; + + case 0x56: /* IM 1 */ + IM = 1; /* interrupt mode 1 */ + break; + + case 0x57: /* LD A,I */ + AF = (AF & 0x29) | (IR & ~0xff) | ((IR >> 8) & 0x80) | (((IR & ~0xff) == 0) << 6) | ((IFF & 2) << 1); + break; + + case 0x58: /* IN E,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x59: /* OUT (C),E */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(DE)); + break; + + case 0x5a: /* ADC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x5b: /* LD DE,(nnnn) */ + DE = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x5e: /* IM 2 */ + IM = 2; /* interrupt mode 2 */ + break; + + case 0x5f: /* LD A,R */ + AF = (AF & 0x29) | ((IR & 0xff) << 8) | (IR & 0x80) | + (((IR & 0xff) == 0) << 6) | ((IFF & 2) << 1); + break; + + case 0x60: /* IN H,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x61: /* OUT (C),H */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); + break; + + case 0x62: /* SBC HL,HL */ + HL &= ADDRMASK; + sum = HL - HL - TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80DupTable[(sum >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x63: /* LD (nnnn),HL */ + PUT_WORD(GET_WORD(PC++), HL); + PC++; + break; + + case 0x67: /* RRD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); + AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); + break; + + case 0x68: /* IN L,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x69: /* OUT (C),L */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(HL)); + break; + + case 0x6a: /* ADC HL,HL */ + HL &= ADDRMASK; + sum = HL + HL + TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80DupTable[sum >> 8]; + HL = sum; + break; + + case 0x6b: /* LD HL,(nnnn) */ + HL = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x6f: /* RLD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); + AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); + break; + + case 0x70: /* IN (C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(temp, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x71: /* OUT (C),0 */ + cpu_out(LOW_REGISTER(BC), 0); + break; + + case 0x72: /* SBC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL - SP - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x73: /* LD (nnnn),SP */ + PUT_WORD(GET_WORD(PC++), SP); + PC++; + break; + + case 0x78: /* IN A,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(AF, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x79: /* OUT (C),A */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); + break; + + case 0x7a: /* ADC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x7b: /* LD SP,(nnnn) */ + SP = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0xa0: /* LDI */ + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa1: /* CPI */ + acu = HIGH_REGISTER(AF); + temp = RAM_PP(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. So, first of all INI/INIR: + HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) + PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ + case 0xa2: /* INI */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L + after the increment or decrement of HL; add the value written to the I/O port + to; call that k for now. If k > 255, then the CF and HF flags are set. The PF + flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. + HF and CF Both set if ((HL) + L > 255) + PF The parity of ((((HL) + L) & 7) xor B) */ + case 0xa3: /* OUTI */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xa8: /* LDD */ + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa9: /* CPD */ + acu = HIGH_REGISTER(AF); + temp = RAM_MM(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. And last IND/INDR: + HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) + PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ + case 0xaa: /* IND */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xab: /* OUTD */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xb0: /* LDIR */ + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(2); /* Add two M1 cycles to refresh counter */ + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb1: /* CPIR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + temp = RAM_PP(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xb2: /* INIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + case 0xb3: /* OTIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + case 0xb8: /* LDDR */ + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(2); /* Add two M1 cycles to refresh counter */ + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb9: /* CPDR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + temp = RAM_MM(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xba: /* INDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xbb: /* OTDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + default: /* ignore ED and following byte */ + break; + } + break; + + case 0xee: /* XOR nn */ + AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; + break; + + case 0xef: /* RST 28H */ + PUSH(PC); + PC = 0x28; + break; + + case 0xf0: /* RET P */ + if (!(TSTFLAG(S))) + POP(PC); + break; + + case 0xf1: /* POP AF */ + POP(AF); + break; + + case 0xf2: /* JP P,nnnn */ + JPC(!TSTFLAG(S)); + break; + + case 0xf3: /* DI */ + IFF = 0; + break; + + case 0xf4: /* CALL P,nnnn */ + CALLC(!TSTFLAG(S)); + break; + + case 0xf5: /* PUSH AF */ + PUSH(AF); + break; + + case 0xf6: /* OR nn */ + AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; + break; + + case 0xf7: /* RST 30H */ + PUSH(PC); + PC = 0x30; + break; + + case 0xf8: /* RET M */ + if (TSTFLAG(S)) + POP(PC); + break; + + case 0xf9: /* LD SP,HL */ + SP = HL; + break; + + case 0xfa: /* JP M,nnnn */ + JPC(TSTFLAG(S)); + break; + + case 0xfb: /* EI */ + IFF = 3; + break; + + case 0xfc: /* CALL M,nnnn */ + CALLC(TSTFLAG(S)); + break; + + case 0xfd: /* FD prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IY,BC */ + IY &= ADDRMASK; + BC &= ADDRMASK; + sum = IY + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; + IY = sum; + break; + + case 0x19: /* ADD IY,DE */ + IY &= ADDRMASK; + DE &= ADDRMASK; + sum = IY + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; + IY = sum; + break; + + case 0x21: /* LD IY,nnnn */ + IY = GET_WORD(PC++); + PC++; + break; + + case 0x22: /* LD (nnnn),IY */ + temp = GET_WORD(PC++); + PUT_WORD(temp, IY); + PC++; + break; + + case 0x23: /* INC IY */ + ++IY; + break; + + case 0x24: /* INC IYH */ + IY += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x25: /* DEC IYH */ + IY -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x26: /* LD IYH,nn */ + SET_HIGH_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x29: /* ADD IY,IY */ + IY &= ADDRMASK; + sum = IY + IY; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IY = sum; + break; + + case 0x2a: /* LD IY,(nnnn) */ + IY = GET_WORD(GET_WORD(PC++)); + PC++; + break; + + case 0x2b: /* DEC IY */ + --IY; + break; + + case 0x2c: /* INC IYL */ + temp = LOW_REGISTER(IY) + 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IYL */ + temp = LOW_REGISTER(IY) - 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IYL,nn */ + SET_LOW_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x34: /* INC (IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IY+dd),nn */ + adr = IY + (int8_t)RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IY,SP */ + IY &= ADDRMASK; + SP &= ADDRMASK; + sum = IY + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; + IY = sum; + break; + + case 0x44: /* LD B,IYH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x45: /* LD B,IYL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x46: /* LD B,(IY+dd) */ + SET_HIGH_REGISTER(BC, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x4c: /* LD C,IYH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x4d: /* LD C,IYL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x4e: /* LD C,(IY+dd) */ + SET_LOW_REGISTER(BC, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x54: /* LD D,IYH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x55: /* LD D,IYL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x56: /* LD D,(IY+dd) */ + SET_HIGH_REGISTER(DE, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x5c: /* LD E,IYH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x5d: /* LD E,IYL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x5e: /* LD E,(IY+dd) */ + SET_LOW_REGISTER(DE, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x60: /* LD IYH,B */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IYH,C */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IYH,D */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IYH,E */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IYH,IYH */ + break; + + case 0x65: /* LD IYH,IYL */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); + break; + + case 0x66: /* LD H,(IY+dd) */ + SET_HIGH_REGISTER(HL, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x67: /* LD IYH,A */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IYL,B */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IYL,C */ + SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IYL,D */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IYL,E */ + SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IYL,IYH */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); + break; + + case 0x6d: /* LD IYL,IYL */ + break; + + case 0x6e: /* LD L,(IY+dd) */ + SET_LOW_REGISTER(HL, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x6f: /* LD IYL,A */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IY+dd),B */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IY+dd),C */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IY+dd),D */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IY+dd),E */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IY+dd),H */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IY+dd),L */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IY+dd),A */ + PUT_BYTE(IY + (int8_t)RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IYH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); + break; + + case 0x7d: /* LD A,IYL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); + break; + + case 0x7e: /* LD A,(IY+dd) */ + SET_HIGH_REGISTER(AF, GET_BYTE(IY + (int8_t)RAM_PP(PC))); + break; + + case 0x84: /* ADD A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IYH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IYL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IYH */ + AF = andTable[((AF & IY) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IYL */ + AF = andTable[((AF >> 8)& IY) & 0xff]; + break; + + case 0xa6: /* AND (IY+dd) */ + AF = andTable[((AF >> 8)& GET_BYTE(IY + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xac: /* XOR IYH */ + AF = xororTable[((AF ^ IY) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IYL */ + AF = xororTable[((AF >> 8) ^ IY) & 0xff]; + break; + + case 0xae: /* XOR (IY+dd) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(IY + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xb4: /* OR IYH */ + AF = xororTable[((AF | IY) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IYL */ + AF = xororTable[((AF >> 8) | IY) & 0xff]; + break; + + case 0xb6: /* OR (IY+dd) */ + AF = xororTable[((AF >> 8) | GET_BYTE(IY + (int8_t)RAM_PP(PC))) & 0xff]; + break; + + case 0xbc: /* CP IYH */ + temp = HIGH_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IYL */ + temp = LOW_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IY+dd) */ + adr = IY + (int8_t)RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IY + (int8_t)RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IY */ + POP(IY); + break; + + case 0xe3: /* EX (SP),IY */ + temp = IY; + POP(IY); + PUSH(temp); + break; + + case 0xe5: /* PUSH IY */ + PUSH(IY); + break; + + case 0xe9: /* JP (IY) */ + PC = IY; + break; + + case 0xf9: /* LD SP,IY */ + SP = IY; + break; + + default: /* ignore FD */ + --PC; + } + break; + + case 0xfe: /* CP nn */ + temp = RAM_PP(PC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xff: /* RST 38H */ + PUSH(PC); + PC = 0x38; + } + } +end_decode: + ; +} + + +#endif diff --git a/z80/z80main.c b/z80/z80main.c new file mode 100644 index 0000000..556cae8 --- /dev/null +++ b/z80/z80main.c @@ -0,0 +1,121 @@ +#include +#include +#include "common/config.h" +#include "z80/businterface.h" +#include "z80/z80buf.h" +#include "z80/z80rom.h" + +volatile uint32_t z80_vect = 0x000000; +volatile uint8_t __attribute__((section(".uninitialized_data."))) z80_irq; +volatile uint8_t __attribute__((section(".uninitialized_data."))) z80_nmi; +volatile uint8_t __attribute__((section(".uninitialized_data."))) z80_res; +volatile uint8_t __attribute__((section(".uninitialized_data."))) rom_shadow; +volatile uint8_t __attribute__((section(".uninitialized_data."))) ram_bank; +volatile uint8_t __attribute__((section(".uninitialized_data."))) ram_common; + +#define Z80break (z80_res || (config_cmdbuf[7] == 0)) + +uint8_t DELAYED_COPY_CODE(cpu_in)(uint16_t address) { + uint8_t rv = 0; + switch(address & 0xe0) { + case 0x00: // Write Data to 6502 + rv = pcpi_reg[0]; + break; + case 0x20: // Read Data from 6502 + clr_z80_stat; + rv = pcpi_reg[1]; + //printf("I%01X:%02X\r\n", (address >> 4), rv); + break; + case 0x40: // Status Port + if(rd_z80_stat) + rv |= 0x80; + if(rd_6502_stat) + rv |= 0x01; + break; + case 0x60: + break; + } + return rv; +} + +void DELAYED_COPY_CODE(cpu_out)(uint16_t address, uint8_t value) { + switch(address & 0xe0) { + case 0x00: // Write Data to 6502 + //printf("O%01X:%02X\r\n", (address >> 4), value); + pcpi_reg[0] = value; + set_6502_stat; + break; + case 0x60: + rom_shadow = (value & 1); + break; + case 0xC0: + ram_bank = (value >> 1) & 7; + ram_common = (value >> 6) & 1; + break; + } +} + +uint8_t DELAYED_COPY_CODE(_RamRead)(uint16_t address) { + if((rom_shadow & 1) && (address < 0x8000)) + return z80_rom[address & 0x7ff]; + + if((address > 0xE000) && (ram_common)) { + return z80_ram[address]; + } + + if(ram_bank) { + return 0xff; + } + + return z80_ram[address]; +} + +void DELAYED_COPY_CODE(_RamWrite)(uint16_t address, uint8_t value) { + if((rom_shadow & 1) && (address < 0x8000)) + return; + + if((address > 0xE000) && (ram_common)) { + z80_ram[address] = value; + return; + } + + if(ram_bank) { + return; + } + + z80_ram[address] = value; +} + +#include "z80cpu.h" + +void DELAYED_COPY_CODE(z80main)() { + z80_res = 1; + + for(;;) { + if(config_cmdbuf[7] == 0) { + config_handler(); + } else + if(cardslot != 0) { + if(z80_res) { + rom_shadow = 1; + ram_bank = 0; + ram_common = 0; + + z80_nmi = 0; + z80_irq = 0; + z80_res = 0; + + // 6502 -> Z80 + clr_z80_stat; + + // Z80 -> 6502 + clr_6502_stat; + + Z80reset(); + } + + Z80run(); + } + } +} + diff --git a/z80/z80rom.h b/z80/z80rom.h new file mode 100644 index 0000000..3fbaf65 --- /dev/null +++ b/z80/z80rom.h @@ -0,0 +1,130 @@ +uint8_t z80_rom[2*1024] = { +0x18, 0x71, 0x5a, 0x38, 0x30, 0x09, 0x4a, 0x25, 0x00, 0x00, 0x21, 0x00, 0x10, 0x0e, 0x01, 0x18, +0x02, 0x0e, 0x02, 0xe5, 0xdd, 0xe1, 0x11, 0xe0, 0x00, 0xdd, 0x19, 0xdd, 0xe9, 0x00, 0x2a, 0x2a, +0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, +0x39, 0x38, 0x32, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x6e, 0x65, 0x6c, 0x20, 0x43, 0x6f, +0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x20, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x2c, +0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x20, 0x2a, 0x2a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xf9, 0xff, 0x3e, 0x02, 0xf5, 0x21, 0xd3, 0x00, 0xe5, +0xfb, 0xed, 0x45, 0x3e, 0x01, 0x18, 0x39, 0x3e, 0x3e, 0x20, 0x57, 0x69, 0x6e, 0x74, 0x68, 0x72, +0x6f, 0x70, 0x20, 0x4c, 0x2e, 0x20, 0x53, 0x61, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x20, 0x49, 0x49, +0x49, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x61, 0x79, 0x6d, 0x6f, 0x6e, 0x64, 0x20, 0x4b, 0x6c, +0x65, 0x69, 0x6e, 0x20, 0x2d, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x20, 0x3c, 0x3c, +0x06, 0x1f, 0x0e, 0xdf, 0xed, 0x41, 0x0d, 0x10, 0xfb, 0xed, 0x41, 0x31, 0xf9, 0xff, 0xf5, 0x3e, +0x7a, 0xd3, 0x00, 0x2a, 0xfe, 0xff, 0x11, 0xab, 0x55, 0x19, 0x7c, 0xb5, 0xc2, 0xd3, 0x00, 0xf1, +0xc3, 0x6c, 0x02, 0x3e, 0x31, 0xd3, 0x00, 0x3e, 0x32, 0xd3, 0x00, 0x21, 0x00, 0x00, 0x0e, 0x03, +0x11, 0xff, 0x07, 0x06, 0x00, 0x78, 0x86, 0x47, 0x23, 0x1b, 0x7a, 0xb3, 0x20, 0xf7, 0x70, 0x7e, +0xb8, 0xf5, 0x79, 0xfe, 0x03, 0x28, 0x05, 0xe1, 0xfe, 0x02, 0xc8, 0xff, 0xf1, 0x28, 0x07, 0x3a, +0x1d, 0x00, 0xb7, 0xca, 0xae, 0x01, 0xf1, 0xfe, 0x01, 0xca, 0x6c, 0x02, 0x3e, 0x33, 0xd3, 0x00, +0x21, 0x00, 0x00, 0x11, 0x00, 0x80, 0x01, 0x00, 0x08, 0xed, 0xb0, 0xc3, 0x1e, 0x81, 0x3e, 0x00, +0xd3, 0x60, 0x0e, 0x00, 0x21, 0x00, 0x00, 0x11, 0x00, 0x80, 0x06, 0x43, 0x79, 0x80, 0x4f, 0x70, +0x04, 0x23, 0x1b, 0x7b, 0xb2, 0x20, 0xf5, 0x21, 0x00, 0x80, 0x11, 0x00, 0x08, 0x79, 0x86, 0x4f, +0x23, 0x1b, 0x7b, 0xb2, 0x20, 0xf7, 0x06, 0x43, 0x79, 0x80, 0x4f, 0x70, 0x04, 0x23, 0x7c, 0xb5, +0x20, 0xf6, 0x21, 0x00, 0x80, 0x2b, 0x7c, 0xb5, 0x20, 0xfb, 0x21, 0x00, 0x00, 0x06, 0x00, 0x78, +0x86, 0x47, 0x23, 0x7c, 0xb5, 0x20, 0xf8, 0x78, 0xb9, 0x20, 0x43, 0x3e, 0x34, 0xd3, 0x00, 0xc3, +0x72, 0x81, 0x3e, 0x00, 0xd3, 0x60, 0x21, 0x00, 0x00, 0xcd, 0xb9, 0x81, 0x21, 0x00, 0x80, 0x11, +0x00, 0x00, 0x01, 0x00, 0x08, 0xed, 0xb0, 0x21, 0x00, 0x00, 0x06, 0x00, 0x11, 0xff, 0x07, 0x78, +0x86, 0x47, 0x23, 0x1b, 0x7a, 0xb3, 0x20, 0xf7, 0x78, 0xbe, 0x20, 0x12, 0xc3, 0x9f, 0x01, 0x31, +0xf9, 0x7f, 0x21, 0x00, 0x80, 0xcd, 0xb9, 0x01, 0x31, 0xf9, 0xff, 0xc3, 0xe4, 0x01, 0xf3, 0x21, +0xff, 0xff, 0xdb, 0xe0, 0x7e, 0x00, 0x77, 0x18, 0xf9, 0x11, 0x00, 0x80, 0x3e, 0x55, 0x77, 0xbe, +0x20, 0xec, 0x3e, 0xaa, 0x77, 0xbe, 0x20, 0xe6, 0x3e, 0xff, 0x77, 0xbe, 0x20, 0xe0, 0x3e, 0x00, +0x77, 0xbe, 0x20, 0xda, 0x3e, 0x01, 0x77, 0xbe, 0x20, 0xd4, 0x07, 0x30, 0xf9, 0x23, 0x1b, 0x7a, +0xb3, 0x20, 0xd9, 0xc9, 0x3e, 0x35, 0xd3, 0x00, 0x3e, 0x00, 0xd3, 0x80, 0x3e, 0x27, 0xd3, 0x80, +0x3e, 0xff, 0xd3, 0x80, 0x06, 0x00, 0xdb, 0x80, 0xfe, 0xfd, 0xca, 0x07, 0x02, 0x00, 0x00, 0x00, +0x04, 0xc2, 0xf6, 0x01, 0xc3, 0x61, 0x02, 0x06, 0x00, 0xdb, 0x80, 0xfe, 0xfd, 0xca, 0x18, 0x02, +0xfe, 0xfc, 0xca, 0x1f, 0x02, 0xc3, 0x61, 0x02, 0x04, 0xc2, 0x09, 0x02, 0xc3, 0x61, 0x02, 0x3e, +0x36, 0xd3, 0x00, 0xed, 0x5e, 0x3e, 0x90, 0xed, 0x47, 0x21, 0x00, 0x90, 0x36, 0xae, 0x2c, 0x36, +0x01, 0x2c, 0xc2, 0x2c, 0x02, 0x21, 0x68, 0x02, 0x22, 0x02, 0x90, 0x11, 0x00, 0x00, 0x3e, 0x87, +0xd3, 0x81, 0x3e, 0x80, 0xd3, 0x81, 0xfb, 0x21, 0x14, 0x05, 0x2b, 0x7c, 0xb5, 0xc2, 0x4a, 0x02, +0xf3, 0x3e, 0x07, 0xd3, 0x81, 0x3e, 0x00, 0xd3, 0x81, 0x21, 0x0a, 0x00, 0xed, 0x52, 0xf2, 0xae, +0x01, 0x3e, 0x01, 0xd3, 0x60, 0xc3, 0x6c, 0x02, 0x13, 0xfb, 0xed, 0x4d, 0x21, 0x55, 0xaa, 0x22, +0xfe, 0xff, 0x3e, 0x5a, 0xd3, 0x00, 0x11, 0xa0, 0x00, 0x21, 0x00, 0x00, 0xdb, 0x40, 0x17, 0x38, +0x0d, 0x2b, 0x7c, 0xb5, 0x20, 0xf6, 0x1b, 0x7a, 0xb3, 0x20, 0xee, 0xc3, 0xef, 0x03, 0xdb, 0x20, +0xfe, 0x06, 0x30, 0x12, 0x6f, 0x26, 0x00, 0x29, 0x11, 0xb0, 0x02, 0x19, 0xcd, 0xa1, 0x02, 0x18, +0xd5, 0x7e, 0x23, 0x66, 0x6f, 0xe9, 0xfe, 0xf0, 0x20, 0xcc, 0xcd, 0x06, 0x03, 0xc3, 0x76, 0x02, +0xbc, 0x02, 0xc9, 0x02, 0xdb, 0x02, 0xed, 0x02, 0xf2, 0x02, 0xfc, 0x02, 0x21, 0x02, 0x00, 0x06, +0x08, 0x4e, 0xcd, 0xe5, 0x03, 0x23, 0x10, 0xf9, 0xc9, 0xcd, 0xc8, 0x03, 0xeb, 0xcd, 0xc8, 0x03, +0x7a, 0xb3, 0xc8, 0x4e, 0xcd, 0xe5, 0x03, 0x23, 0x1b, 0x18, 0xf5, 0xcd, 0xc8, 0x03, 0xeb, 0xcd, +0xc8, 0x03, 0x7a, 0xb3, 0xc8, 0xcd, 0xdc, 0x03, 0x77, 0x23, 0x1b, 0x18, 0xf5, 0xcd, 0xc8, 0x03, +0xeb, 0xe9, 0xcd, 0xdc, 0x03, 0x4f, 0xcd, 0xdc, 0x03, 0xed, 0x79, 0xc9, 0xcd, 0xdc, 0x03, 0x4f, +0xed, 0x48, 0xcd, 0xe5, 0x03, 0xc9, 0xcd, 0xc8, 0x03, 0xed, 0x53, 0xfd, 0xff, 0xcd, 0xc8, 0x03, +0xed, 0x53, 0xfb, 0xff, 0x7b, 0xb2, 0xca, 0x75, 0x03, 0x2a, 0xfd, 0xff, 0xed, 0x4b, 0xfb, 0xff, +0x3e, 0x55, 0xcd, 0x7b, 0x03, 0x2a, 0xfd, 0xff, 0xed, 0x4b, 0xfb, 0xff, 0x3e, 0x55, 0xcd, 0x8a, +0x03, 0xd8, 0x2a, 0xfd, 0xff, 0xed, 0x4b, 0xfb, 0xff, 0x3e, 0xaa, 0xcd, 0x7b, 0x03, 0x2a, 0xfd, +0xff, 0xed, 0x4b, 0xfb, 0xff, 0x3e, 0xaa, 0xcd, 0x8a, 0x03, 0xd8, 0x2a, 0xfd, 0xff, 0xed, 0x4b, +0xfb, 0xff, 0x75, 0x23, 0x0b, 0x78, 0xb1, 0x20, 0xf9, 0x2a, 0xfd, 0xff, 0xed, 0x4b, 0xfb, 0xff, +0x7e, 0xbd, 0x20, 0x08, 0x23, 0x0b, 0x78, 0xb1, 0x20, 0xf6, 0x18, 0x09, 0x57, 0x5d, 0xcd, 0xa1, +0x03, 0x23, 0x30, 0xec, 0xd8, 0x0e, 0x00, 0xcd, 0xe5, 0x03, 0xc9, 0x5f, 0x78, 0xb1, 0xc8, 0x73, +0x0b, 0x78, 0xb1, 0xc8, 0x54, 0x5d, 0x13, 0xed, 0xb0, 0xc9, 0x5f, 0x78, 0xb1, 0xc8, 0x7e, 0xbb, +0x20, 0x07, 0x23, 0x0b, 0x78, 0xb1, 0x20, 0xf6, 0xc9, 0x57, 0xcd, 0xa1, 0x03, 0x23, 0x30, 0xee, +0xc9, 0xe5, 0xd5, 0xc5, 0x0e, 0x01, 0x7e, 0xbb, 0x20, 0x02, 0x0e, 0x02, 0xcd, 0xe5, 0x03, 0xeb, +0xcd, 0xd1, 0x03, 0x4d, 0xcd, 0xe5, 0x03, 0x4c, 0xcd, 0xe5, 0x03, 0xcd, 0xdc, 0x03, 0xfe, 0x00, +0x37, 0x20, 0x01, 0x3f, 0xc1, 0xd1, 0xe1, 0xc9, 0xcd, 0xdc, 0x03, 0x5f, 0xcd, 0xdc, 0x03, 0x57, +0xc9, 0xc5, 0x4b, 0xcd, 0xe5, 0x03, 0x4a, 0xcd, 0xe5, 0x03, 0xc1, 0xc9, 0xdb, 0x40, 0x17, 0xd2, +0xdc, 0x03, 0xdb, 0x20, 0xc9, 0xdb, 0x40, 0x1f, 0xda, 0xe5, 0x03, 0x79, 0xd3, 0x00, 0xc9, 0x3e, +0x42, 0xd3, 0x00, 0x31, 0xff, 0xef, 0xcd, 0x77, 0x04, 0x32, 0xff, 0xff, 0x21, 0x00, 0x00, 0x11, +0x00, 0x80, 0x01, 0x1f, 0x05, 0xed, 0xb0, 0xc3, 0x0a, 0x84, 0x3e, 0x00, 0xd3, 0x60, 0x21, 0x00, +0x80, 0x11, 0x00, 0x00, 0x01, 0x1f, 0x05, 0xed, 0xb0, 0xc3, 0x1c, 0x04, 0xcd, 0x77, 0x04, 0x21, +0xff, 0xff, 0xbe, 0xc4, 0x6d, 0x04, 0x32, 0x21, 0x05, 0x31, 0x41, 0x05, 0x21, 0x20, 0x05, 0x36, +0x55, 0x2b, 0x36, 0x01, 0x08, 0xaf, 0x08, 0xcd, 0x03, 0x05, 0xcd, 0x8e, 0x04, 0xcd, 0x77, 0x04, +0x21, 0x21, 0x05, 0xbe, 0xc4, 0x6d, 0x04, 0x21, 0x20, 0x05, 0x46, 0x21, 0x00, 0x06, 0x11, 0x00, +0xfa, 0x7e, 0xb8, 0xc4, 0x6d, 0x04, 0x23, 0x1b, 0x7a, 0xb3, 0x20, 0xf5, 0x21, 0x1f, 0x05, 0x34, +0x7e, 0xe6, 0x02, 0x20, 0xd2, 0x21, 0x20, 0x05, 0x7e, 0x2f, 0x77, 0x18, 0xca, 0x21, 0xff, 0xff, +0xdb, 0xe0, 0x7e, 0x00, 0x77, 0x18, 0xf9, 0x21, 0x00, 0x00, 0x11, 0x1f, 0x05, 0x06, 0x00, 0x7e, +0x77, 0x80, 0x47, 0x23, 0x1b, 0x7a, 0xb3, 0x20, 0xf6, 0x78, 0xb7, 0xc0, 0x2f, 0xc9, 0x21, 0x00, +0x06, 0x11, 0x00, 0xfa, 0x3a, 0x20, 0x05, 0x47, 0x70, 0x23, 0x1b, 0x7a, 0xb3, 0xc2, 0x98, 0x04, +0x21, 0x00, 0x06, 0x4c, 0x3e, 0xfa, 0xf5, 0xdb, 0xe0, 0x2e, 0x00, 0x61, 0x3a, 0x1f, 0x05, 0x47, +0x70, 0x04, 0x2c, 0xc2, 0xb0, 0x04, 0xcd, 0x03, 0x05, 0xcd, 0xf9, 0x04, 0xcd, 0x03, 0x05, 0xcd, +0xf9, 0x04, 0x21, 0x00, 0x06, 0x11, 0x00, 0xfa, 0x7c, 0xb9, 0x28, 0x0a, 0x3a, 0x20, 0x05, 0xbe, +0xc4, 0x6d, 0x04, 0xc3, 0xdc, 0x04, 0x7e, 0xb8, 0xc4, 0x6d, 0x04, 0x04, 0x23, 0x1b, 0x7a, 0xb3, +0xc2, 0xc8, 0x04, 0xcd, 0x03, 0x05, 0x2e, 0x00, 0x61, 0x3a, 0x20, 0x05, 0x77, 0x2c, 0xc2, 0xec, +0x04, 0x0c, 0xf1, 0x3d, 0xf5, 0x20, 0xb0, 0xf1, 0xc9, 0x21, 0x00, 0x0b, 0x2b, 0x7c, 0xb5, 0xc2, +0xfc, 0x04, 0xc9, 0xf5, 0xc5, 0x08, 0x4f, 0x3c, 0x08, 0x06, 0x80, 0x79, 0xd3, 0x00, 0x3e, 0x01, +0xd3, 0x60, 0x3e, 0x00, 0xd3, 0x60, 0xdb, 0x40, 0xdb, 0x20, 0x10, 0xef, 0xc1, 0xf1, 0xc9, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13 +};