diff --git a/.gitignore b/.gitignore index 9c2c119..0292164 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ backup/ *~ *.pgz apple2/ +build-*/ +*.user diff --git a/pc/CMakeLists.txt b/pc/CMakeLists.txt new file mode 100644 index 0000000..a0d7665 --- /dev/null +++ b/pc/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.12) + +project(sierrahotel LANGUAGES C) + + +find_package(SDL2 REQUIRED) + + +set(HEADERS + app.h + flight.h + vrEmu6502.h + a23d2bin.h + a23d2.h +) +list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/include/") + + +set(SOURCE + main.c + flight.c + vrEmu6502.c +) +list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/") + + +add_executable(${CMAKE_PROJECT_NAME} + ${HEADERS} + ${SOURCE} +) + + +target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${SDL2_INCLUDE_DIRS} +) + + +target_link_libraries(${CMAKE_PROJECT_NAME} + ${SDL2_LIBRARIES} + -lGLEW + -lGL + -lm + -lpthread +) + + +target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -fsanitize=address) +target_link_options(${CMAKE_PROJECT_NAME} PRIVATE -fsanitize=address) diff --git a/pc/include/a23d2.h b/pc/include/a23d2.h new file mode 100644 index 0000000..bb59b86 --- /dev/null +++ b/pc/include/a23d2.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com + * + * 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. + */ + + +#ifdef __F256K__ +#include "f256lib.h" +#else +#include +#include +typedef uint8_t byte; +#endif + + +//#define scdMemCpy memcpy +#define scdMemCpy(d,s,l) \ + ({ \ + volatile byte *dp = (d); \ + volatile byte *sp = (s); \ + uint16_t i = 0; \ + for (i=0; i<(l); i++) *dp++ = *sp++; \ + }) + + +#define A23D2_FAR_BLOCK 42 +#define DATABASE_FAR_BLOCK 43 + +#define DATABASE_FAR 0x56000 +#define DRAWLIST_P0_FAR 0x57000 +#define DRAWLIST_P1_FAR 0x57800 + +#define DATABASE 0x8000 +#define DRAWLIST_P0 0x9000 +#define DRAWLIST_P1 0x9800 + + +// A2-3D2 Function Addresses. +#define A23D2_ENTRYN 0x6090 +#define A23D2_NXTPT 0x6118 +#define A23D2_SINEX 0x61f6 +#define A23D2_COSEX 0x620f + +// A2-3D2 Data Addresses. +#define A23D2_TEST_DATABASE 0x80fb +#define A23D2_TDATA 0x613e +#define A23D2_IBP 0x9b + +// Stuff A2-3D2 is going to clobber that our compiler may want. +#define COMPILER_ZP_START 0x60 // To 0xC2 +#define COMPILER_ZP_LENGTH 0x62 +#define A23D2_ZP_START COMPILER_ZP_START +#define A23D2_ZP_LENGTH COMPILER_ZP_LENGTH + +// Our Scratch Addresses. +#define CAMERA_SHARED_START 0x200 // To 0x209 +#define CAMERA_SHARED_LENGTH 0x9 +#define SCRATCH_END 0x2ff +#define COMPILER_ZP_SAVE (CAMERA_SHARED_START + CAMERA_SHARED_LENGTH) +#define A23D2_ZP_SAVE (COMPILER_ZP_SAVE + COMPILER_ZP_LENGTH) + + +// A2-3D1 Commands. Commented out items are Apple ][ only. +#define PNT 0x00 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Point +#define SPNT 0x01 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Start Point +#define CPNT 0x02 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Continue Point +#define RAY 0x03 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Ray +#define CLPSW 0x04 // N - Define Clipper (0=off, 1=on) +#define EYE 0x05 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB, P, B, H - Define 3D Eye/Camera +#define LIN2D 0x06 // X1, Y1, X2, Y2 - Draw 2D Line +//#define DISP 0x07 // N - Display Select (50=set graphics, 51=set text, 52=clear mixed, 53=set mixed, 54=set page 1, 55=set page 2, 56=clear hi-res, 57=set hi-res) +//#define ERAS 0x08 // N - Erase Screen (0=erase page 1, 1=erase page 2, 2=fill page 1, 3=fill page 2) +//#define DRAW 0x09 // N - Select Draw Page (0=page 1, 1=page 2) +#define PNT2D 0x0a // X1, Y1 - Draw 2D Point +#define JMP 0x0b // LSB, MSB - Interpretive Jump +//#define LMODE 0x0c // N - Line Drawing Mode (0=solid, 1=xor) +#define ARRAY 0x0d // LSB, MSB - Enable Output Array +#define SCRSZ 0x0e // WIDTH, HEIGHT, xCENTER, yCENTER - Define Screen Size +#define FIELD 0x0f // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Field of View Selection +#define INIT 0x10 // Easy Init +#define NOP 0x11 // No Operation + +// A2-3D2 Commands. Commented out items are Apple ][ only. +#define STCOL 0x12 // COL - Set Color +#define ICALL 0x13 // STAT, LOC, ADDR - Independent Object Call +#define SRES 0x14 // RES - Set Resolution (0=140x192, 1=280x192) +//#define HLIN 0x15 // x1L, x1H, y1, x2L, x2H, y2 - Hi-Res (280x192) Line 2D +//#define SHRB 0x16 // xL, xH, y - Set Hi-Res Bias +//#define HLIN2 0x17 // x1, y1, x2, y2 - Hi-Res (x limited) Line 2D +//#define HPNT 0x18 // xL, xH, y - Hi-Res (280x192) Point 2D +//#define HPNT2 0x19 // x, y - Hi-Res (x limited) Point 2D +#define SKIP 0x1a // SIZE, STATUS - Skip Segment +//#define PAUS 0x1b // TIME - Pause for TIME/5ths of a Second +#define SET323 0x1c // LSB, MSB - Set 3D to 3D Array Address +#define GN323 0x1d // STATUS - Set 3D to 3D Status +#define END 0x79 // End of Database + + +typedef struct cameraS { // 9 bytes. + int16_t x; + int16_t y; + int16_t z; + byte p; + byte b; + byte h; +} cameraT; + + +extern volatile cameraT *_camera; +extern volatile cameraT *_cameraInDatabase; +extern volatile byte *_pointer; + +extern uint16_t _drawlist; +extern uint16_t _drawlistInDatabase; + +extern uint16_t _bytes; +extern uint8_t _x1; +#define _tdata _x1 // Alias. +extern uint8_t _y1; +extern uint8_t _x2; +extern uint8_t _y2; +extern bool _useColor; +extern byte _mmu; +extern byte _ram; +extern float _trig; + + +void a23d2Cos(void); +void a23d2Draw(void); +void a23d2Init(void); +void a23d2Render(void); +void a23d2Sin(void); diff --git a/pc/include/a23d2bin.h b/pc/include/a23d2bin.h new file mode 100644 index 0000000..fc26014 --- /dev/null +++ b/pc/include/a23d2bin.h @@ -0,0 +1,707 @@ +unsigned char a23d2bin[] = { + 0x4c, 0x6c, 0x60, 0x4c, 0x90, 0x60, 0x4c, 0xf7, 0x61, 0x4c, 0x10, 0x62, + 0x08, 0x16, 0x08, 0x7e, 0x7e, 0x06, 0x08, 0x06, 0x08, 0x0f, 0x00, 0xf5, + 0xf5, 0x3e, 0x10, 0x3e, 0x10, 0x00, 0x00, 0xd3, 0x74, 0xc3, 0x24, 0x01, + 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, + 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, 0xd2, 0x3f, 0x01, + 0xd2, 0x3f, 0x01, 0x3e, 0x50, 0x3e, 0x50, 0x00, 0x00, 0xd3, 0x74, 0xf1, + 0xf1, 0xe3, 0xe3, 0xe3, 0xe3, 0x05, 0x00, 0xc3, 0x52, 0x01, 0xc2, 0x15, + 0x01, 0x23, 0x00, 0xfe, 0x60, 0xfe, 0x60, 0xc3, 0x5e, 0x01, 0xc2, 0x0b, + 0x01, 0x00, 0x15, 0xc3, 0x66, 0x01, 0xc2, 0x0f, 0x01, 0xc3, 0x00, 0x00, + 0x08, 0x48, 0x8a, 0x48, 0x98, 0x48, 0xa2, 0x60, 0xb5, 0x5f, 0x9d, 0x0b, + 0x60, 0xca, 0xd0, 0xf8, 0x20, 0x90, 0x60, 0xa2, 0x60, 0xbd, 0x0b, 0x60, + 0x95, 0x5f, 0xca, 0xd0, 0xf8, 0x68, 0xa8, 0x68, 0xaa, 0x68, 0x28, 0x60, + 0xd8, 0xa2, 0x21, 0xa9, 0x00, 0x95, 0x7b, 0xca, 0xd0, 0xfb, 0xa9, 0x7f, + 0x85, 0x9c, 0xa9, 0xc5, 0x85, 0x9b, 0xa9, 0xfd, 0x85, 0x7e, 0x85, 0x86, + 0x85, 0x8e, 0xa9, 0x7f, 0x85, 0x7f, 0x85, 0x87, 0x85, 0x8f, 0xa9, 0x00, + 0x85, 0xbb, 0xa9, 0x20, 0x85, 0xbc, 0xa9, 0x40, 0x85, 0xbd, 0x4c, 0x18, + 0x61, 0xc8, 0xb1, 0x9b, 0xa0, 0x01, 0x91, 0x9d, 0x88, 0xa9, 0x12, 0x91, + 0x9d, 0xa9, 0x02, 0x18, 0x18, 0x65, 0x9d, 0x85, 0x9d, 0x90, 0x02, 0xe6, + 0x9e, 0x4c, 0xca, 0x63, 0xe2, 0x62, 0x66, 0x62, 0xa0, 0x62, 0xc6, 0x62, + 0xc5, 0x63, 0xd2, 0x63, 0xe3, 0x63, 0xf4, 0x63, 0x78, 0x74, 0xfa, 0x7b, + 0x01, 0x64, 0x10, 0x64, 0x4b, 0x7f, 0x1e, 0x64, 0x42, 0x64, 0x67, 0x64, + 0x2f, 0x64, 0x3d, 0x64, 0x28, 0x7c, 0x1c, 0x6d, 0x06, 0x7f, 0x0a, 0x71, + 0x3f, 0x71, 0x56, 0x71, 0x84, 0x71, 0x9c, 0x71, 0xd4, 0x71, 0xe9, 0x71, + 0x1e, 0x72, 0x2f, 0x72, 0xa9, 0x0a, 0x85, 0xb1, 0xa0, 0x00, 0xb1, 0x9b, + 0x30, 0x04, 0xc9, 0x1e, 0x30, 0x09, 0xa5, 0x7d, 0xf0, 0x04, 0xa9, 0x79, + 0x91, 0x9d, 0x60, 0x0a, 0xaa, 0xbd, 0xdc, 0x60, 0x85, 0xa3, 0xbd, 0xdd, + 0x60, 0x85, 0xa4, 0x6c, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x63, 0xff, 0x7f, + 0xff, 0x7f, 0x00, 0xff, 0x7f, 0xf5, 0x7f, 0xd7, 0x7f, 0xa6, 0x7f, 0x61, + 0x7f, 0x09, 0x7f, 0x9c, 0x7e, 0x1c, 0x7e, 0x89, 0x7d, 0xe3, 0x7c, 0x29, + 0x7c, 0x5c, 0x7b, 0x7c, 0x7a, 0x89, 0x79, 0x83, 0x78, 0x6b, 0x77, 0x40, + 0x76, 0x03, 0x75, 0xb5, 0x73, 0x54, 0x72, 0xe1, 0x70, 0x5e, 0x6f, 0xc9, + 0x6d, 0x23, 0x6c, 0x6c, 0x6a, 0x79, 0x67, 0xce, 0x66, 0xe7, 0x64, 0xf1, + 0x62, 0xeb, 0x60, 0xd6, 0x5e, 0xb3, 0x5c, 0x81, 0x5a, 0x42, 0x58, 0xf4, + 0x55, 0x9a, 0x53, 0x33, 0x51, 0xbf, 0x4e, 0x3f, 0x4c, 0xb3, 0x49, 0x1c, + 0x47, 0x7a, 0x44, 0xcd, 0x41, 0x16, 0x3f, 0x56, 0x3c, 0x8c, 0x39, 0xb9, + 0x36, 0xde, 0x33, 0xfb, 0x30, 0x10, 0x2e, 0x1f, 0x2b, 0x26, 0x28, 0x27, + 0x25, 0x23, 0x22, 0x19, 0x1f, 0x0b, 0x1c, 0xf9, 0x18, 0xe1, 0x15, 0xc7, + 0x12, 0xab, 0x0f, 0x8c, 0x0c, 0x6a, 0x09, 0x47, 0x06, 0x24, 0x03, 0x00, + 0x00, 0x38, 0xe9, 0x40, 0xaa, 0x30, 0x1f, 0xc9, 0x40, 0x30, 0x12, 0x18, + 0x69, 0x7f, 0x49, 0xff, 0x0a, 0xa8, 0x38, 0xa9, 0x00, 0xf9, 0x48, 0x61, + 0xaa, 0xf9, 0x47, 0x61, 0x60, 0x0a, 0xa8, 0xbe, 0x48, 0x61, 0xb9, 0x47, + 0x61, 0x60, 0x49, 0xff, 0x18, 0x69, 0x01, 0x10, 0xda, 0x30, 0xe1, 0x08, + 0x48, 0x8a, 0x48, 0x98, 0x48, 0xad, 0x3e, 0x61, 0x20, 0xc9, 0x61, 0x8e, + 0x3f, 0x61, 0x8d, 0x3e, 0x61, 0x68, 0xa8, 0x68, 0xaa, 0x68, 0x28, 0x60, + 0x08, 0x48, 0x8a, 0x48, 0x98, 0x48, 0xad, 0x3e, 0x61, 0x20, 0xcc, 0x61, + 0x4c, 0x03, 0x62, 0x00, 0x18, 0x65, 0x9b, 0x85, 0x9b, 0x90, 0x02, 0xe6, + 0x9c, 0x60, 0xa0, 0x68, 0x20, 0x62, 0x68, 0x20, 0x90, 0x6a, 0xa2, 0x07, + 0xb5, 0x67, 0x95, 0x6f, 0xca, 0xd0, 0xf9, 0xa9, 0x07, 0x20, 0x20, 0x62, + 0xad, 0x1f, 0x62, 0xc9, 0x02, 0xf0, 0x1a, 0xa5, 0x66, 0x25, 0x6e, 0x60, + 0xa0, 0x60, 0x20, 0x62, 0x68, 0x20, 0x37, 0x6a, 0xa9, 0x07, 0x20, 0x20, + 0x62, 0xad, 0x1f, 0x62, 0xc9, 0x02, 0xf0, 0x01, 0x60, 0x68, 0x68, 0x4c, + 0x18, 0x61, 0x20, 0x4c, 0x62, 0x20, 0x2a, 0x62, 0xd0, 0x55, 0xa5, 0x66, + 0xd0, 0x14, 0xa5, 0x6e, 0xd0, 0x03, 0x4c, 0x33, 0x63, 0xa5, 0x7c, 0xd0, + 0x1e, 0x20, 0x03, 0x6b, 0xc6, 0xb1, 0xf0, 0x3f, 0xd0, 0xec, 0xa5, 0x7c, + 0xd0, 0x11, 0x20, 0xe9, 0x6a, 0xa5, 0x66, 0xf0, 0xe1, 0x25, 0x6e, 0xd0, + 0x2e, 0xc6, 0xb1, 0xd0, 0xf1, 0xf0, 0x28, 0x8d, 0x46, 0x61, 0xd0, 0x23, + 0xa2, 0x07, 0xb5, 0x6f, 0x95, 0x5f, 0xca, 0xd0, 0xf9, 0x20, 0x2a, 0x62, + 0xd0, 0x15, 0xa5, 0x66, 0xd0, 0xd4, 0xa5, 0x6e, 0xf0, 0x5e, 0xa5, 0x7c, + 0xd0, 0xe1, 0x20, 0x03, 0x6b, 0xc6, 0xb1, 0xf0, 0x02, 0xd0, 0xef, 0x4c, + 0x18, 0x61, 0xa2, 0x07, 0xb5, 0x6f, 0x95, 0x67, 0xca, 0xd0, 0xf9, 0x20, + 0x4c, 0x62, 0xa5, 0x66, 0x25, 0x6e, 0xd0, 0xeb, 0xa5, 0x66, 0xd0, 0xaa, + 0xa5, 0x6e, 0xd0, 0x99, 0xf0, 0x42, 0x20, 0x4c, 0x62, 0xa5, 0x66, 0xf0, + 0x03, 0x4c, 0x1c, 0x61, 0xa0, 0x60, 0xa2, 0x9f, 0x20, 0x7c, 0x63, 0xa8, + 0xa6, 0x9f, 0xa5, 0x7d, 0x30, 0x06, 0x20, 0x71, 0x75, 0x4c, 0x1c, 0x61, + 0xa9, 0x0a, 0xa0, 0x00, 0x91, 0x9d, 0xc8, 0xa5, 0x9f, 0x91, 0x9d, 0xc8, + 0xa5, 0xa0, 0x91, 0x9d, 0xa9, 0x03, 0x10, 0x5c, 0xad, 0x46, 0x61, 0xd0, + 0x1a, 0xa5, 0xa1, 0xa6, 0xa2, 0x85, 0x9f, 0x86, 0xa0, 0x4c, 0x3f, 0x63, + 0xad, 0x46, 0x61, 0xd0, 0x0a, 0xa0, 0x60, 0xa2, 0x9f, 0x20, 0x7c, 0x63, + 0x4c, 0x46, 0x63, 0xa9, 0x00, 0x8d, 0x46, 0x61, 0xa0, 0x60, 0xa2, 0x9f, + 0x20, 0x7c, 0x63, 0xa0, 0x68, 0xa2, 0xa1, 0x20, 0x7c, 0x63, 0xa5, 0x7d, + 0xd0, 0x16, 0xa5, 0x9f, 0xa6, 0xa0, 0x85, 0xb3, 0x86, 0xb4, 0xa5, 0xa1, + 0xa6, 0xa2, 0x85, 0xb5, 0x86, 0xb6, 0x20, 0xad, 0x75, 0x4c, 0x18, 0x61, + 0xa9, 0x06, 0xa0, 0x00, 0x91, 0x9d, 0xb9, 0x9f, 0x00, 0xc8, 0xc0, 0x05, + 0xd0, 0xf6, 0xa9, 0x05, 0x18, 0x65, 0x9d, 0x85, 0x9d, 0x90, 0x02, 0xe6, + 0x9e, 0x4c, 0x18, 0x61, 0x86, 0xa7, 0x84, 0xa8, 0xb9, 0x04, 0x00, 0x85, + 0x7a, 0xb9, 0x05, 0x00, 0x85, 0x7b, 0xb6, 0x00, 0xb9, 0x01, 0x00, 0x20, + 0xf3, 0x65, 0xa5, 0x79, 0xa2, 0x45, 0x20, 0xb2, 0x65, 0x18, 0x69, 0x00, + 0xa4, 0xa7, 0x99, 0x00, 0x00, 0xa4, 0xa8, 0xb9, 0x04, 0x00, 0x85, 0x7a, + 0xb9, 0x05, 0x00, 0x85, 0x7b, 0xb6, 0x02, 0xb9, 0x03, 0x00, 0x20, 0xf3, + 0x65, 0xa5, 0x79, 0xa2, 0x5e, 0x20, 0xb2, 0x65, 0x18, 0x69, 0x00, 0xa4, + 0xa7, 0x99, 0x01, 0x00, 0x60, 0xc8, 0xb1, 0x9b, 0x85, 0x7c, 0xa9, 0x02, + 0x20, 0x20, 0x62, 0x4c, 0x18, 0x61, 0xc8, 0xb1, 0x9b, 0x99, 0x8f, 0x00, + 0xc0, 0x09, 0xd0, 0xf6, 0x20, 0x83, 0x66, 0xa9, 0x0a, 0xd0, 0xe9, 0xc8, + 0xb1, 0x9b, 0x99, 0xb2, 0x00, 0xc0, 0x04, 0xd0, 0xf6, 0x20, 0xad, 0x75, + 0xa9, 0x05, 0xd0, 0xd8, 0xc8, 0xb1, 0x9b, 0x8d, 0xfd, 0x63, 0xa9, 0x00, + 0x8d, 0x54, 0xc0, 0xf0, 0xc9, 0xc8, 0xb1, 0x9b, 0xaa, 0xc8, 0xb1, 0x9b, + 0xa8, 0x20, 0x71, 0x75, 0xa9, 0x03, 0xd0, 0xbc, 0xc8, 0xb1, 0x9b, 0xaa, + 0xc8, 0xb1, 0x9b, 0x85, 0x9c, 0x86, 0x9b, 0x4c, 0x1c, 0x61, 0xa9, 0xff, + 0x85, 0x7d, 0xc8, 0xb1, 0x9b, 0x85, 0x9d, 0xc8, 0xb1, 0x9b, 0x85, 0x9e, + 0x4c, 0x0c, 0x64, 0xa9, 0x00, 0x8d, 0x53, 0xc0, 0x8d, 0x57, 0xc0, 0x8d, + 0x50, 0xc0, 0x8d, 0x54, 0xc0, 0xa9, 0x01, 0x4c, 0xcc, 0x63, 0xc8, 0xb1, + 0x9b, 0x18, 0x6a, 0x38, 0xe9, 0x01, 0x8d, 0x95, 0x63, 0xc8, 0xb1, 0x9b, + 0x18, 0x6a, 0x38, 0xe9, 0x01, 0x8d, 0xb8, 0x63, 0xc8, 0xb1, 0x9b, 0x8d, + 0x9b, 0x63, 0xc8, 0xb1, 0x9b, 0x8d, 0xbe, 0x63, 0x4c, 0xf0, 0x63, 0xc8, + 0xb1, 0x9b, 0x99, 0x3f, 0x61, 0xc0, 0x06, 0xd0, 0xf6, 0xa9, 0x07, 0x4c, + 0xcc, 0x63, 0x85, 0xa3, 0xb5, 0x00, 0x85, 0x78, 0xb5, 0x01, 0x85, 0x79, + 0xb9, 0x00, 0x00, 0x85, 0x7a, 0xb9, 0x01, 0x00, 0x85, 0x7b, 0x20, 0x95, + 0x64, 0xa4, 0xa3, 0x99, 0x00, 0x00, 0x96, 0x01, 0x60, 0xa5, 0x78, 0x05, + 0x79, 0xf0, 0x06, 0xa5, 0x7a, 0x05, 0x7b, 0xd0, 0x05, 0xa9, 0x00, 0xa2, + 0x00, 0x60, 0xa5, 0x79, 0x45, 0x7b, 0xa8, 0xa5, 0x79, 0x30, 0x0e, 0xa9, + 0xff, 0x45, 0x78, 0x85, 0x78, 0xa9, 0xff, 0x45, 0x79, 0x85, 0x79, 0x30, + 0x08, 0xa5, 0x78, 0xd0, 0x02, 0xc6, 0x79, 0xc6, 0x78, 0xa5, 0x7b, 0x10, + 0x0d, 0xa9, 0x00, 0x38, 0xe5, 0x7a, 0x85, 0x7a, 0xa9, 0x00, 0xe5, 0x7b, + 0x85, 0x7b, 0x46, 0x78, 0x90, 0x02, 0xa9, 0x00, 0x4a, 0x66, 0x78, 0xb0, + 0x02, 0x65, 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0x66, + 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, + 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0xa2, 0x00, 0x86, 0xa5, + 0x46, 0x78, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, 0x7a, 0x85, 0xa5, 0x8a, + 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0x46, 0x78, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, + 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0x46, 0x79, + 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, + 0x4a, 0x66, 0xa5, 0x46, 0x79, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, 0x7a, + 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0x46, 0x79, 0xb0, 0x0a, + 0xaa, 0xa5, 0xa5, 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x4a, 0x66, + 0xa5, 0x46, 0x79, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, 0x7a, 0x85, 0xa5, + 0x8a, 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0x46, 0x79, 0xb0, 0x0a, 0xaa, 0xa5, + 0xa5, 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0x46, + 0x79, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, + 0x7b, 0x4a, 0x66, 0xa5, 0x46, 0x79, 0xb0, 0x0a, 0xaa, 0xa5, 0xa5, 0x65, + 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x4a, 0x66, 0xa5, 0xc0, 0x00, 0x10, + 0x0d, 0x85, 0xa6, 0xa9, 0x00, 0x38, 0xe5, 0xa5, 0x85, 0xa5, 0xa9, 0x00, + 0xe5, 0xa6, 0xaa, 0xa5, 0xa5, 0x60, 0x49, 0xff, 0x85, 0x78, 0x86, 0x7b, + 0xa9, 0x00, 0x66, 0x78, 0xb0, 0x01, 0x8a, 0x4a, 0x66, 0x78, 0xb0, 0x02, + 0x65, 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0x66, 0x78, + 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, + 0x66, 0x78, 0xb0, 0x02, 0x65, 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x02, 0x65, + 0x7b, 0x4a, 0x66, 0x78, 0xb0, 0x04, 0x38, 0x38, 0xe5, 0x7b, 0x60, 0x09, + 0x00, 0x30, 0x0c, 0xa4, 0x7b, 0x30, 0x02, 0x10, 0x44, 0x20, 0x20, 0x66, + 0x4c, 0x30, 0x66, 0x20, 0x12, 0x66, 0xa4, 0x7b, 0x30, 0x02, 0x10, 0x24, + 0x20, 0x20, 0x66, 0x4c, 0x41, 0x66, 0xa8, 0x8a, 0x49, 0xff, 0x18, 0x69, + 0x01, 0xaa, 0x98, 0x49, 0xff, 0x69, 0x00, 0x60, 0xa8, 0xa9, 0x00, 0x38, + 0xe5, 0x7a, 0x85, 0x7a, 0xa9, 0x00, 0xe5, 0x7b, 0x85, 0x7b, 0x98, 0x60, + 0x20, 0x41, 0x66, 0xa9, 0x00, 0x38, 0xe5, 0x78, 0x85, 0x78, 0xa9, 0x00, + 0xe5, 0x79, 0x85, 0x79, 0x60, 0xa0, 0x0f, 0x86, 0xa5, 0xaa, 0xa5, 0xa5, + 0x38, 0xe5, 0x7a, 0x85, 0xa5, 0x8a, 0xe5, 0x7b, 0x30, 0x0d, 0x38, 0x26, + 0x78, 0x26, 0x79, 0x06, 0xa5, 0x2a, 0x88, 0xd0, 0xe8, 0xf0, 0x19, 0x06, + 0x78, 0x26, 0x79, 0x06, 0xa5, 0x2a, 0x88, 0xf0, 0x0f, 0xaa, 0xa5, 0xa5, + 0x18, 0x65, 0x7a, 0x85, 0xa5, 0x8a, 0x65, 0x7b, 0x30, 0xe9, 0x10, 0xda, + 0x06, 0x78, 0x26, 0x79, 0x10, 0x04, 0xc6, 0x78, 0xc6, 0x79, 0x60, 0xa5, + 0x96, 0x20, 0xc9, 0x61, 0x85, 0x60, 0x86, 0x61, 0xa5, 0x97, 0x20, 0xc9, + 0x61, 0x85, 0x62, 0x86, 0x63, 0xa5, 0x98, 0x20, 0xc9, 0x61, 0x85, 0x64, + 0x86, 0x65, 0xa5, 0x96, 0x20, 0xcc, 0x61, 0x85, 0x66, 0x86, 0x67, 0xa5, + 0x97, 0x20, 0xcc, 0x61, 0x85, 0x68, 0x86, 0x69, 0xa5, 0x98, 0x20, 0xcc, + 0x61, 0x85, 0x6a, 0x86, 0x6b, 0xa2, 0x6a, 0xa0, 0x68, 0xa9, 0x6c, 0x20, + 0x76, 0x64, 0xa2, 0x64, 0xa0, 0x62, 0xa9, 0x6e, 0x20, 0x76, 0x64, 0xa2, + 0x6a, 0xa0, 0x62, 0xa9, 0x70, 0x20, 0x76, 0x64, 0xa2, 0x64, 0xa0, 0x68, + 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, 0x60, 0xa0, 0x6e, 0xa9, 0x74, 0x20, + 0x76, 0x64, 0xa2, 0x60, 0xa0, 0x72, 0xa9, 0x76, 0x20, 0x76, 0x64, 0xa2, + 0x70, 0xa0, 0x60, 0xa9, 0xb3, 0x20, 0x76, 0x64, 0xa2, 0x60, 0xa0, 0x6c, + 0xa9, 0xb5, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x6c, 0x65, 0x74, 0x85, 0x7e, + 0xa5, 0x6d, 0x65, 0x75, 0x85, 0x7f, 0x38, 0xa5, 0x76, 0xe5, 0x70, 0x85, + 0x80, 0xa5, 0x77, 0xe5, 0x71, 0x85, 0x81, 0xa2, 0x64, 0xa0, 0x66, 0xa9, + 0x82, 0x20, 0x76, 0x64, 0xa2, 0x62, 0xa0, 0x66, 0xa9, 0x84, 0x20, 0x76, + 0x64, 0xa2, 0x66, 0xa0, 0x68, 0xa9, 0x86, 0x20, 0x76, 0x64, 0xa9, 0x00, + 0x38, 0xe5, 0x60, 0x85, 0x88, 0xa9, 0x00, 0xe5, 0x61, 0x85, 0x89, 0x38, + 0xa5, 0xb3, 0xe5, 0x72, 0x85, 0x8a, 0xa5, 0xb4, 0xe5, 0x73, 0x85, 0x8b, + 0x18, 0xa5, 0x6e, 0x65, 0xb5, 0x85, 0x8c, 0xa5, 0x6f, 0x65, 0xb6, 0x85, + 0x8d, 0xa2, 0x6a, 0xa0, 0x66, 0xa9, 0x8e, 0x20, 0x76, 0x64, 0xae, 0x41, + 0x61, 0xe0, 0x7f, 0xd0, 0x07, 0xad, 0x40, 0x61, 0xc9, 0xff, 0xf0, 0x45, + 0x85, 0x78, 0x86, 0x79, 0xa5, 0x7e, 0xa6, 0x7f, 0x85, 0x7a, 0x86, 0x7b, + 0x20, 0x95, 0x64, 0x85, 0x7e, 0x86, 0x7f, 0xa5, 0x84, 0xa6, 0x85, 0x85, + 0x78, 0x86, 0x79, 0xad, 0x40, 0x61, 0xae, 0x41, 0x61, 0x85, 0x7a, 0x86, + 0x7b, 0x20, 0x95, 0x64, 0x85, 0x84, 0x86, 0x85, 0xa5, 0x8a, 0xa6, 0x8b, + 0x85, 0x78, 0x86, 0x79, 0xad, 0x40, 0x61, 0xae, 0x41, 0x61, 0x85, 0x7a, + 0x86, 0x7b, 0x20, 0x95, 0x64, 0x85, 0x8a, 0x86, 0x8b, 0xae, 0x43, 0x61, + 0xe0, 0x7f, 0xd0, 0x07, 0xad, 0x42, 0x61, 0xc9, 0xff, 0xf0, 0x45, 0x85, + 0x78, 0x86, 0x79, 0xa5, 0x80, 0xa6, 0x81, 0x85, 0x7a, 0x86, 0x7b, 0x20, + 0x95, 0x64, 0x85, 0x80, 0x86, 0x81, 0xad, 0x42, 0x61, 0xae, 0x43, 0x61, + 0x85, 0x78, 0x86, 0x79, 0xa5, 0x86, 0xa6, 0x87, 0x85, 0x7a, 0x86, 0x7b, + 0x20, 0x95, 0x64, 0x85, 0x86, 0x86, 0x87, 0xad, 0x42, 0x61, 0xae, 0x43, + 0x61, 0x85, 0x78, 0x86, 0x79, 0xa5, 0x8c, 0xa6, 0x8d, 0x85, 0x7a, 0x86, + 0x7b, 0x20, 0x95, 0x64, 0x85, 0x8c, 0x86, 0x8d, 0xae, 0x45, 0x61, 0xe0, + 0x7f, 0xd0, 0x07, 0xad, 0x44, 0x61, 0xc9, 0xff, 0xf0, 0x45, 0x85, 0x78, + 0x86, 0x79, 0xa5, 0x82, 0xa6, 0x83, 0x85, 0x7a, 0x86, 0x7b, 0x20, 0x95, + 0x64, 0x85, 0x82, 0x86, 0x83, 0xad, 0x44, 0x61, 0xae, 0x45, 0x61, 0x85, + 0x78, 0x86, 0x79, 0xa5, 0x88, 0xa6, 0x89, 0x85, 0x7a, 0x86, 0x7b, 0x20, + 0x95, 0x64, 0x85, 0x88, 0x86, 0x89, 0xad, 0x44, 0x61, 0xae, 0x45, 0x61, + 0x85, 0x78, 0x86, 0x79, 0xa5, 0x8e, 0xa6, 0x8f, 0x85, 0x7a, 0x86, 0x7b, + 0x20, 0x95, 0x64, 0x85, 0x8e, 0x86, 0x8f, 0x60, 0x00, 0x00, 0x84, 0xb2, + 0xa0, 0x01, 0xb1, 0x9b, 0xc8, 0x38, 0xe5, 0x90, 0x85, 0xab, 0xb1, 0x9b, + 0xc8, 0xe5, 0x91, 0x50, 0x03, 0x4c, 0xac, 0x69, 0x85, 0xac, 0xb1, 0x9b, + 0xc8, 0x38, 0xe5, 0x92, 0x85, 0xad, 0xb1, 0x9b, 0xc8, 0xe5, 0x93, 0x50, + 0x03, 0x4c, 0xe2, 0x69, 0x85, 0xae, 0xb1, 0x9b, 0xc8, 0x38, 0xe5, 0x94, + 0x85, 0xaf, 0xb1, 0x9b, 0xc8, 0xe5, 0x95, 0x50, 0x03, 0x4c, 0xf1, 0x69, + 0x85, 0xb0, 0xa5, 0xac, 0x29, 0xc0, 0xf0, 0x04, 0xc9, 0xc0, 0xd0, 0x14, + 0xa5, 0xae, 0x29, 0xc0, 0xf0, 0x04, 0xc9, 0xc0, 0xd0, 0x0a, 0xa5, 0xb0, + 0x29, 0xc0, 0xf0, 0x1e, 0xc9, 0xc0, 0xf0, 0x1a, 0xad, 0x1f, 0x62, 0xd0, + 0x15, 0xa5, 0xac, 0x2a, 0x66, 0xac, 0x66, 0xab, 0xa5, 0xae, 0x2a, 0x66, + 0xae, 0x66, 0xad, 0xa5, 0xb0, 0x2a, 0x66, 0xb0, 0x66, 0xaf, 0xa9, 0x00, + 0x8d, 0x60, 0x68, 0x8d, 0x61, 0x68, 0xa2, 0xab, 0xa0, 0x7e, 0xa9, 0xa7, + 0x20, 0x76, 0x64, 0xa2, 0xad, 0xa0, 0x84, 0xa9, 0xa9, 0x20, 0x76, 0x64, + 0xa5, 0xaf, 0xa6, 0xb0, 0x85, 0x78, 0x86, 0x79, 0xa5, 0x8a, 0xa6, 0x8b, + 0x85, 0x7a, 0x86, 0x7b, 0x20, 0x95, 0x64, 0x20, 0x07, 0x6a, 0xa2, 0xab, + 0xa0, 0x80, 0xa9, 0xa7, 0x20, 0x76, 0x64, 0xa2, 0xad, 0xa0, 0x86, 0xa9, + 0xa9, 0x20, 0x76, 0x64, 0xa5, 0xaf, 0xa6, 0xb0, 0x85, 0x78, 0x86, 0x79, + 0xa5, 0x8c, 0xa6, 0x8d, 0x85, 0x7a, 0x86, 0x7b, 0x20, 0x95, 0x64, 0x20, + 0x07, 0x6a, 0xa2, 0xab, 0xa0, 0x82, 0xa9, 0xa7, 0x20, 0x76, 0x64, 0xa2, + 0xad, 0xa0, 0x88, 0xa9, 0xa9, 0x20, 0x76, 0x64, 0xa5, 0xaf, 0xa6, 0xb0, + 0x85, 0x78, 0x86, 0x79, 0xa5, 0x8e, 0xa6, 0x8f, 0x85, 0x7a, 0x86, 0x7b, + 0x20, 0x95, 0x64, 0x20, 0x07, 0x6a, 0xad, 0x1f, 0x62, 0xf0, 0x03, 0x20, + 0x05, 0x72, 0xa6, 0xb2, 0xad, 0x61, 0x68, 0xf0, 0x16, 0xb5, 0xff, 0x2a, + 0x76, 0xff, 0x76, 0xfe, 0xb5, 0xfd, 0x2a, 0x76, 0xfd, 0x76, 0xfc, 0xb5, + 0xfb, 0x2a, 0x76, 0xfb, 0x76, 0xfa, 0x60, 0xad, 0x60, 0x68, 0xd0, 0x27, + 0xb5, 0xfe, 0x0a, 0x36, 0xff, 0x0a, 0x36, 0xff, 0x0a, 0x36, 0xff, 0x95, + 0xfe, 0xb5, 0xfc, 0x0a, 0x36, 0xfd, 0x0a, 0x36, 0xfd, 0x0a, 0x36, 0xfd, + 0x95, 0xfc, 0xb5, 0xfa, 0x0a, 0x36, 0xfb, 0x0a, 0x36, 0xfb, 0x0a, 0x36, + 0xfb, 0x95, 0xfa, 0x60, 0x6a, 0x85, 0xac, 0x66, 0xab, 0xb1, 0x9b, 0xc8, + 0x38, 0xe5, 0x92, 0x85, 0xad, 0xb1, 0x9b, 0xc8, 0xe5, 0x93, 0x70, 0x04, + 0x85, 0xae, 0x26, 0xae, 0x6a, 0x85, 0xae, 0x66, 0xad, 0xb1, 0x9b, 0xc8, + 0x38, 0xe5, 0x94, 0x85, 0xaf, 0xb1, 0x9b, 0xc8, 0xe5, 0x95, 0x70, 0x02, + 0xc9, 0x80, 0x6a, 0x85, 0xb0, 0x66, 0xaf, 0x4c, 0xa2, 0x68, 0x6a, 0x85, + 0xae, 0x66, 0xad, 0xa5, 0xac, 0x2a, 0x66, 0xac, 0x66, 0xab, 0x4c, 0xc9, + 0x69, 0x6a, 0x85, 0xb0, 0x66, 0xaf, 0xa5, 0xae, 0x2a, 0x66, 0xae, 0x66, + 0xad, 0xa5, 0xac, 0x2a, 0x66, 0xac, 0x66, 0xab, 0x4c, 0xa2, 0x68, 0x18, + 0x65, 0xa7, 0xa8, 0x8a, 0x65, 0xa8, 0xaa, 0x98, 0x18, 0x65, 0xa9, 0xa8, + 0x8a, 0x65, 0xaa, 0xa6, 0xb2, 0x95, 0x01, 0x94, 0x00, 0xe6, 0xb2, 0xe6, + 0xb2, 0xaa, 0xf0, 0x12, 0xc9, 0xff, 0xf0, 0x0e, 0xee, 0x60, 0x68, 0x29, + 0xe0, 0xf0, 0x07, 0xc9, 0xe0, 0xf0, 0x03, 0xee, 0x61, 0x68, 0x60, 0xa2, + 0x00, 0xa5, 0x60, 0x18, 0x65, 0x64, 0xa5, 0x61, 0x65, 0x65, 0x30, 0x04, + 0x50, 0x06, 0x70, 0x02, 0x70, 0x02, 0xa2, 0x40, 0xa5, 0x64, 0x38, 0xe5, + 0x60, 0xa5, 0x65, 0xe5, 0x61, 0x30, 0x04, 0x50, 0x08, 0x70, 0x02, 0x70, + 0x04, 0x8a, 0x09, 0x20, 0xaa, 0xa5, 0x62, 0x18, 0x65, 0x64, 0xa5, 0x63, + 0x65, 0x65, 0x30, 0x04, 0x50, 0x08, 0x70, 0x02, 0x70, 0x04, 0x8a, 0x09, + 0x10, 0xaa, 0xa5, 0x64, 0x38, 0xe5, 0x62, 0xa5, 0x65, 0xe5, 0x63, 0x30, + 0x04, 0x50, 0x0a, 0x70, 0x02, 0x70, 0x06, 0x8a, 0x09, 0x08, 0x85, 0x66, + 0x60, 0x86, 0x66, 0x60, 0xa2, 0x00, 0xa5, 0x68, 0x18, 0x65, 0x6c, 0xa5, + 0x69, 0x65, 0x6d, 0x30, 0x04, 0x50, 0x06, 0x70, 0x02, 0x70, 0x02, 0xa2, + 0x40, 0xa5, 0x6c, 0x38, 0xe5, 0x68, 0xa5, 0x6d, 0xe5, 0x69, 0x30, 0x04, + 0x50, 0x08, 0x70, 0x02, 0x70, 0x04, 0x8a, 0x09, 0x20, 0xaa, 0xa5, 0x6a, + 0x18, 0x65, 0x6c, 0xa5, 0x6b, 0x65, 0x6d, 0x30, 0x04, 0x50, 0x08, 0x70, + 0x02, 0x70, 0x04, 0x8a, 0x09, 0x10, 0xaa, 0xa5, 0x6c, 0x38, 0xe5, 0x6a, + 0xa5, 0x6d, 0xe5, 0x6b, 0x30, 0x04, 0x50, 0x0a, 0x70, 0x02, 0x70, 0x06, + 0x8a, 0x09, 0x08, 0x85, 0x6e, 0x60, 0x86, 0x6e, 0x60, 0x20, 0xf3, 0x6a, + 0x20, 0x03, 0x6b, 0x20, 0xf3, 0x6a, 0x60, 0xa0, 0x08, 0xb9, 0x5f, 0x00, + 0xb6, 0x67, 0x99, 0x67, 0x00, 0x96, 0x5f, 0x88, 0xd0, 0xf3, 0x60, 0xa5, + 0x6e, 0x29, 0xbf, 0xf0, 0x0a, 0x29, 0xdf, 0xf0, 0x09, 0x29, 0xef, 0xf0, + 0x08, 0xd0, 0x09, 0x4c, 0x97, 0x6c, 0x4c, 0x1c, 0x6c, 0x4c, 0x97, 0x6b, + 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, 0x65, 0xe5, 0x6d, 0x85, + 0x7b, 0xa5, 0x62, 0x38, 0xe5, 0x6a, 0xaa, 0xa5, 0x63, 0xe5, 0x6b, 0xa8, + 0x8a, 0x38, 0xe5, 0x7a, 0x85, 0x7a, 0x98, 0xe5, 0x7b, 0x85, 0x7b, 0xa5, + 0x6c, 0x38, 0xe5, 0x6a, 0xaa, 0xa5, 0x6d, 0xe5, 0x6b, 0x20, 0xf3, 0x65, + 0x38, 0xa5, 0x60, 0xe5, 0x68, 0x85, 0x7a, 0xa5, 0x61, 0xe5, 0x69, 0x85, + 0x7b, 0xa5, 0x78, 0xa6, 0x79, 0x85, 0xa7, 0x86, 0xa8, 0x20, 0x95, 0x64, + 0x18, 0x65, 0x68, 0x85, 0x68, 0x8a, 0x65, 0x69, 0x85, 0x69, 0x38, 0xa5, + 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, 0x65, 0xe5, 0x6d, 0x85, 0x7b, 0xa5, + 0xa7, 0xa6, 0xa8, 0x85, 0x78, 0x86, 0x79, 0x20, 0x95, 0x64, 0x18, 0x65, + 0x6c, 0x85, 0x6c, 0x85, 0x6a, 0x8a, 0x65, 0x6d, 0x85, 0x6d, 0x85, 0x6b, + 0x4c, 0x90, 0x6a, 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, 0x65, + 0xe5, 0x6d, 0x85, 0x7b, 0xa5, 0x6a, 0x38, 0xe5, 0x62, 0xaa, 0xa5, 0x6b, + 0xe5, 0x63, 0xa8, 0x8a, 0x38, 0xe5, 0x7a, 0x85, 0x7a, 0x98, 0xe5, 0x7b, + 0x85, 0x7b, 0xa5, 0x6c, 0x18, 0x65, 0x6a, 0xaa, 0xa5, 0x6d, 0x65, 0x6b, + 0x20, 0xf3, 0x65, 0x38, 0xa5, 0x60, 0xe5, 0x68, 0x85, 0x7a, 0xa5, 0x61, + 0xe5, 0x69, 0x85, 0x7b, 0xa5, 0x78, 0xa6, 0x79, 0x85, 0xa7, 0x86, 0xa8, + 0x20, 0x95, 0x64, 0x18, 0x65, 0x68, 0x85, 0x68, 0x8a, 0x65, 0x69, 0x85, + 0x69, 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, 0x65, 0xe5, 0x6d, + 0x85, 0x7b, 0xa5, 0xa7, 0xa6, 0xa8, 0x85, 0x78, 0x86, 0x79, 0x20, 0x95, + 0x64, 0x18, 0x65, 0x6c, 0x85, 0x6c, 0x49, 0xff, 0x85, 0x6a, 0x8a, 0x65, + 0x6d, 0x85, 0x6d, 0x49, 0xff, 0x85, 0x6b, 0xe6, 0x6a, 0xd0, 0x02, 0xe6, + 0x6b, 0x4c, 0x90, 0x6a, 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, + 0x65, 0xe5, 0x6d, 0x85, 0x7b, 0xa5, 0x60, 0x38, 0xe5, 0x68, 0xaa, 0xa5, + 0x61, 0xe5, 0x69, 0xa8, 0x8a, 0x38, 0xe5, 0x7a, 0x85, 0x7a, 0x98, 0xe5, + 0x7b, 0x85, 0x7b, 0xa5, 0x6c, 0x38, 0xe5, 0x68, 0xaa, 0xa5, 0x6d, 0xe5, + 0x69, 0x20, 0xf3, 0x65, 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, 0xa5, + 0x65, 0xe5, 0x6d, 0x85, 0x7b, 0xa5, 0x78, 0xa6, 0x79, 0x85, 0xa7, 0x86, + 0xa8, 0x20, 0x95, 0x64, 0x18, 0x65, 0x6c, 0x85, 0x6c, 0x85, 0x68, 0x8a, + 0x65, 0x6d, 0x85, 0x6d, 0x85, 0x69, 0x38, 0xa5, 0x62, 0xe5, 0x6a, 0x85, + 0x7a, 0xa5, 0x63, 0xe5, 0x6b, 0x85, 0x7b, 0xa5, 0xa7, 0xa6, 0xa8, 0x85, + 0x78, 0x86, 0x79, 0x20, 0x95, 0x64, 0x18, 0x65, 0x6a, 0x85, 0x6a, 0x8a, + 0x65, 0x6b, 0x85, 0x6b, 0x4c, 0x90, 0x6a, 0x38, 0xa5, 0x64, 0xe5, 0x6c, + 0x85, 0x7a, 0xa5, 0x65, 0xe5, 0x6d, 0x85, 0x7b, 0xa5, 0x68, 0x38, 0xe5, + 0x60, 0xaa, 0xa5, 0x69, 0xe5, 0x61, 0xa8, 0x8a, 0x38, 0xe5, 0x7a, 0x85, + 0x7a, 0x98, 0xe5, 0x7b, 0x85, 0x7b, 0xa5, 0x6c, 0x18, 0x65, 0x68, 0xaa, + 0xa5, 0x6d, 0x65, 0x69, 0x20, 0xf3, 0x65, 0x38, 0xa5, 0x62, 0xe5, 0x6a, + 0x85, 0x7a, 0xa5, 0x63, 0xe5, 0x6b, 0x85, 0x7b, 0xa5, 0x78, 0xa6, 0x79, + 0x85, 0xa7, 0x86, 0xa8, 0x20, 0x95, 0x64, 0x18, 0x65, 0x6a, 0x85, 0x6a, + 0x8a, 0x65, 0x6b, 0x85, 0x6b, 0x38, 0xa5, 0x64, 0xe5, 0x6c, 0x85, 0x7a, + 0xa5, 0x65, 0xe5, 0x6d, 0x85, 0x7b, 0xa5, 0xa7, 0xa6, 0xa8, 0x85, 0x78, + 0x86, 0x79, 0x20, 0x95, 0x64, 0x18, 0x65, 0x6c, 0x85, 0x6c, 0x49, 0xff, + 0x85, 0x68, 0x8a, 0x65, 0x6d, 0x85, 0x6d, 0x49, 0xff, 0x85, 0x69, 0xe6, + 0x68, 0xd0, 0x02, 0xe6, 0x69, 0x4c, 0x90, 0x6a, 0xc8, 0xb1, 0x9b, 0xd0, + 0x03, 0x4c, 0xf3, 0x70, 0xc9, 0xff, 0xf0, 0x05, 0x38, 0xe9, 0x01, 0x91, + 0x9b, 0xa2, 0x1e, 0xb5, 0x7e, 0x48, 0xca, 0x10, 0xfa, 0xa2, 0x90, 0x20, + 0xf8, 0x70, 0x20, 0xf8, 0x70, 0x20, 0xf8, 0x70, 0xc8, 0xb1, 0x9b, 0x85, + 0x96, 0xc8, 0xb1, 0x9b, 0x85, 0x97, 0xc8, 0xb1, 0x9b, 0x85, 0x98, 0xc8, + 0xb1, 0x9b, 0x48, 0xc8, 0xb1, 0x9b, 0x85, 0x9c, 0x68, 0x85, 0x9b, 0xa5, + 0x96, 0x20, 0xc9, 0x61, 0x85, 0x60, 0x86, 0x61, 0xa5, 0x97, 0x20, 0xc9, + 0x61, 0x85, 0x64, 0x86, 0x65, 0xa5, 0x98, 0x20, 0xc9, 0x61, 0x85, 0x68, + 0x86, 0x69, 0xa5, 0x96, 0x20, 0xcc, 0x61, 0x85, 0x62, 0x86, 0x63, 0xa5, + 0x97, 0x20, 0xcc, 0x61, 0x85, 0x66, 0x86, 0x67, 0xa5, 0x98, 0x20, 0xcc, + 0x61, 0x85, 0x6a, 0x86, 0x6b, 0xa2, 0x90, 0xa0, 0x6a, 0xa9, 0xab, 0x20, + 0x76, 0x64, 0xa2, 0x90, 0xa0, 0x68, 0xa9, 0xaf, 0x20, 0x76, 0x64, 0xa2, + 0x94, 0xa0, 0x68, 0xa9, 0xad, 0x20, 0x76, 0x64, 0x38, 0xa5, 0xab, 0xe5, + 0xad, 0x85, 0x90, 0xa5, 0xac, 0xe5, 0xae, 0x85, 0x91, 0xa2, 0x94, 0xa0, + 0x6a, 0xa9, 0xad, 0x20, 0x76, 0x64, 0x18, 0xa5, 0xad, 0x65, 0xaf, 0x85, + 0x94, 0xa5, 0xae, 0x65, 0xb0, 0x85, 0x95, 0xa2, 0x92, 0xa0, 0x62, 0xa9, + 0xab, 0x20, 0x76, 0x64, 0xa2, 0x92, 0xa0, 0x60, 0xa9, 0xaf, 0x20, 0x76, + 0x64, 0xa2, 0x94, 0xa0, 0x60, 0xa9, 0xad, 0x20, 0x76, 0x64, 0x18, 0xa5, + 0xab, 0x65, 0xad, 0x85, 0x92, 0xa5, 0xac, 0x65, 0xae, 0x85, 0x93, 0xa2, + 0x94, 0xa0, 0x62, 0xa9, 0xad, 0x20, 0x76, 0x64, 0x38, 0xa5, 0xad, 0xe5, + 0xaf, 0x85, 0x94, 0xa5, 0xae, 0xe5, 0xb0, 0x85, 0x95, 0xa2, 0x90, 0xa0, + 0x66, 0xa9, 0xab, 0x20, 0x76, 0x64, 0xa2, 0x90, 0xa0, 0x64, 0xa9, 0xaf, + 0x20, 0x76, 0x64, 0xa2, 0x92, 0xa0, 0x64, 0xa9, 0xad, 0x20, 0x76, 0x64, + 0x18, 0xa5, 0xab, 0x65, 0xad, 0x85, 0x90, 0xa5, 0xac, 0x65, 0xae, 0x85, + 0x91, 0xa2, 0x92, 0xa0, 0x66, 0xa9, 0xad, 0x20, 0x76, 0x64, 0x38, 0xa5, + 0xad, 0xe5, 0xaf, 0x85, 0x92, 0xa5, 0xae, 0xe5, 0xb0, 0x85, 0x93, 0xa9, + 0x00, 0x38, 0xe5, 0x68, 0x85, 0x74, 0xa9, 0x00, 0xe5, 0x69, 0x85, 0x75, + 0xa2, 0x6a, 0xa0, 0x7e, 0xa9, 0x6c, 0x20, 0x76, 0x64, 0xa2, 0x74, 0xa0, + 0x8a, 0xa9, 0x72, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x6c, 0x65, 0x72, 0x85, + 0x6c, 0xa5, 0x6d, 0x65, 0x73, 0x85, 0x6d, 0xa2, 0x6a, 0xa0, 0x80, 0xa9, + 0x6e, 0x20, 0x76, 0x64, 0xa2, 0x74, 0xa0, 0x8c, 0xa9, 0x72, 0x20, 0x76, + 0x64, 0x18, 0xa5, 0x6e, 0x65, 0x72, 0x85, 0x6e, 0xa5, 0x6f, 0x65, 0x73, + 0x85, 0x6f, 0xa2, 0x6a, 0xa0, 0x82, 0xa9, 0x70, 0x20, 0x76, 0x64, 0xa2, + 0x74, 0xa0, 0x8e, 0xa9, 0x72, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x70, 0x65, + 0x72, 0x85, 0x70, 0xa5, 0x71, 0x65, 0x73, 0x85, 0x71, 0xa2, 0x68, 0xa0, + 0x7e, 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, 0x6a, 0xa0, 0x8a, 0xa9, 0x8a, + 0x20, 0x76, 0x64, 0x18, 0xa5, 0x8a, 0x65, 0x72, 0x85, 0x8a, 0xa5, 0x8b, + 0x65, 0x73, 0x85, 0x8b, 0xa5, 0x6c, 0xa6, 0x6d, 0x85, 0x7e, 0x86, 0x7f, + 0xa2, 0x68, 0xa0, 0x80, 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, 0x6a, 0xa0, + 0x8c, 0xa9, 0x8c, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x8c, 0x65, 0x72, 0x85, + 0x8c, 0xa5, 0x8d, 0x65, 0x73, 0x85, 0x8d, 0xa5, 0x6e, 0xa6, 0x6f, 0x85, + 0x80, 0x86, 0x81, 0xa2, 0x68, 0xa0, 0x82, 0xa9, 0x72, 0x20, 0x76, 0x64, + 0xa2, 0x6a, 0xa0, 0x8e, 0xa9, 0x8e, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x8e, + 0x65, 0x72, 0x85, 0x8e, 0xa5, 0x8f, 0x65, 0x73, 0x85, 0x8f, 0xa5, 0x70, + 0xa6, 0x71, 0x85, 0x82, 0x86, 0x83, 0xa9, 0x00, 0x38, 0xe5, 0x60, 0x85, + 0x74, 0xa9, 0x00, 0xe5, 0x61, 0x85, 0x75, 0xa2, 0x62, 0xa0, 0x84, 0xa9, + 0x6c, 0x20, 0x76, 0x64, 0xa2, 0x60, 0xa0, 0x8a, 0xa9, 0x72, 0x20, 0x76, + 0x64, 0x18, 0xa5, 0x6c, 0x65, 0x72, 0x85, 0x6c, 0xa5, 0x6d, 0x65, 0x73, + 0x85, 0x6d, 0xa2, 0x62, 0xa0, 0x86, 0xa9, 0x6e, 0x20, 0x76, 0x64, 0xa2, + 0x60, 0xa0, 0x8c, 0xa9, 0x72, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x6e, 0x65, + 0x72, 0x85, 0x6e, 0xa5, 0x6f, 0x65, 0x73, 0x85, 0x6f, 0xa2, 0x62, 0xa0, + 0x88, 0xa9, 0x70, 0x20, 0x76, 0x64, 0xa2, 0x60, 0xa0, 0x8e, 0xa9, 0x72, + 0x20, 0x76, 0x64, 0x18, 0xa5, 0x70, 0x65, 0x72, 0x85, 0x70, 0xa5, 0x71, + 0x65, 0x73, 0x85, 0x71, 0xa2, 0x74, 0xa0, 0x84, 0xa9, 0x72, 0x20, 0x76, + 0x64, 0xa2, 0x62, 0xa0, 0x8a, 0xa9, 0x8a, 0x20, 0x76, 0x64, 0x18, 0xa5, + 0x8a, 0x65, 0x72, 0x85, 0x8a, 0xa5, 0x8b, 0x65, 0x73, 0x85, 0x8b, 0xa5, + 0x6c, 0xa6, 0x6d, 0x85, 0x84, 0x86, 0x85, 0xa2, 0x74, 0xa0, 0x86, 0xa9, + 0x72, 0x20, 0x76, 0x64, 0xa2, 0x62, 0xa0, 0x8c, 0xa9, 0x8c, 0x20, 0x76, + 0x64, 0x18, 0xa5, 0x8c, 0x65, 0x72, 0x85, 0x8c, 0xa5, 0x8d, 0x65, 0x73, + 0x85, 0x8d, 0xa5, 0x6e, 0xa6, 0x6f, 0x85, 0x86, 0x86, 0x87, 0xa2, 0x74, + 0xa0, 0x88, 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, 0x62, 0xa0, 0x8e, 0xa9, + 0x8e, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x8e, 0x65, 0x72, 0x85, 0x8e, 0xa5, + 0x8f, 0x65, 0x73, 0x85, 0x8f, 0xa5, 0x70, 0xa6, 0x71, 0x85, 0x88, 0x86, + 0x89, 0xa9, 0x00, 0x38, 0xe5, 0x64, 0x85, 0x74, 0xa9, 0x00, 0xe5, 0x65, + 0x85, 0x75, 0xa2, 0x66, 0xa0, 0x7e, 0xa9, 0x6c, 0x20, 0x76, 0x64, 0xa2, + 0x64, 0xa0, 0x84, 0xa9, 0x72, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x6c, 0x65, + 0x72, 0x85, 0x6c, 0xa5, 0x6d, 0x65, 0x73, 0x85, 0x6d, 0xa2, 0x66, 0xa0, + 0x80, 0xa9, 0x6e, 0x20, 0x76, 0x64, 0xa2, 0x64, 0xa0, 0x86, 0xa9, 0x72, + 0x20, 0x76, 0x64, 0x18, 0xa5, 0x6e, 0x65, 0x72, 0x85, 0x6e, 0xa5, 0x6f, + 0x65, 0x73, 0x85, 0x6f, 0xa2, 0x66, 0xa0, 0x82, 0xa9, 0x70, 0x20, 0x76, + 0x64, 0xa2, 0x64, 0xa0, 0x88, 0xa9, 0x72, 0x20, 0x76, 0x64, 0x18, 0xa5, + 0x70, 0x65, 0x72, 0x85, 0x70, 0xa5, 0x71, 0x65, 0x73, 0x85, 0x71, 0xa2, + 0x74, 0xa0, 0x7e, 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, 0x66, 0xa0, 0x84, + 0xa9, 0x84, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x84, 0x65, 0x72, 0x85, 0x84, + 0xa5, 0x85, 0x65, 0x73, 0x85, 0x85, 0xa5, 0x6c, 0xa6, 0x6d, 0x85, 0x7e, + 0x86, 0x7f, 0xa2, 0x74, 0xa0, 0x80, 0xa9, 0x72, 0x20, 0x76, 0x64, 0xa2, + 0x66, 0xa0, 0x86, 0xa9, 0x86, 0x20, 0x76, 0x64, 0x18, 0xa5, 0x86, 0x65, + 0x72, 0x85, 0x86, 0xa5, 0x87, 0x65, 0x73, 0x85, 0x87, 0xa5, 0x6e, 0xa6, + 0x6f, 0x85, 0x80, 0x86, 0x81, 0xa2, 0x74, 0xa0, 0x82, 0xa9, 0x72, 0x20, + 0x76, 0x64, 0xa2, 0x66, 0xa0, 0x88, 0xa9, 0x88, 0x20, 0x76, 0x64, 0x18, + 0xa5, 0x88, 0x65, 0x72, 0x85, 0x88, 0xa5, 0x89, 0x65, 0x73, 0x85, 0x89, + 0xa5, 0x70, 0xa6, 0x71, 0x85, 0x82, 0x86, 0x83, 0x20, 0x18, 0x61, 0xa2, + 0xe1, 0x68, 0x95, 0x9d, 0xe8, 0xd0, 0xfa, 0xa9, 0x0d, 0x4c, 0xcc, 0x63, + 0x38, 0xb5, 0x00, 0xc8, 0xf1, 0x9b, 0x95, 0x00, 0xe8, 0xb5, 0x00, 0xc8, + 0xf1, 0x9b, 0x95, 0x00, 0xe8, 0x60, 0xc8, 0xb1, 0x9b, 0x85, 0xb3, 0xc8, + 0xb1, 0x9b, 0x6a, 0x66, 0xb3, 0xa9, 0x00, 0x6a, 0x85, 0xa4, 0xc8, 0xb1, + 0x9b, 0x85, 0xb4, 0xc8, 0xb1, 0x9b, 0x85, 0xb5, 0xc8, 0xb1, 0x9b, 0x6a, + 0x66, 0xb5, 0xa9, 0x00, 0x6a, 0x85, 0xa3, 0xc8, 0xb1, 0x9b, 0x85, 0xb6, + 0x20, 0xf6, 0x7c, 0xa9, 0x07, 0x4c, 0xcc, 0x63, 0x00, 0x00, 0x00, 0xc8, + 0xb1, 0x9b, 0x8d, 0x3d, 0x71, 0xc8, 0xb1, 0x9b, 0x8d, 0x3c, 0x71, 0xc8, + 0xb1, 0x9b, 0x8d, 0x3e, 0x71, 0xa9, 0x04, 0x4c, 0xcc, 0x63, 0xc8, 0xb1, + 0x9b, 0x20, 0xb4, 0x71, 0x85, 0xa4, 0x86, 0xb3, 0xc8, 0xb1, 0x9b, 0x18, + 0x6d, 0x3e, 0x71, 0x85, 0xb4, 0xc8, 0xb1, 0x9b, 0x20, 0xb4, 0x71, 0x85, + 0xa3, 0x86, 0xb5, 0xc8, 0xb1, 0x9b, 0x18, 0x6d, 0x3e, 0x71, 0x85, 0xb6, + 0x20, 0xf6, 0x7c, 0xa9, 0x05, 0x4c, 0xcc, 0x63, 0xc8, 0xb1, 0x9b, 0xaa, + 0xc8, 0xb1, 0x9b, 0x20, 0xcc, 0x71, 0x85, 0xa4, 0xc8, 0xb1, 0x9b, 0xa8, + 0x20, 0x69, 0x75, 0xa9, 0x04, 0x4c, 0xcc, 0x63, 0xc8, 0xb1, 0x9b, 0x20, + 0xb4, 0x71, 0x85, 0xa4, 0xc8, 0xb1, 0x9b, 0x18, 0x6d, 0x3e, 0x71, 0xa8, + 0x20, 0x69, 0x75, 0xa9, 0x03, 0x4c, 0xcc, 0x63, 0x18, 0x30, 0x0c, 0x6d, + 0x3d, 0x71, 0xaa, 0xa9, 0x00, 0x6d, 0x3c, 0x71, 0x4c, 0xcc, 0x71, 0x6d, + 0x3d, 0x71, 0xaa, 0xa9, 0xff, 0x6d, 0x3c, 0x71, 0x6a, 0x8a, 0x6a, 0xaa, + 0xa9, 0x00, 0x6a, 0x60, 0xc8, 0xb1, 0x9b, 0xaa, 0xc8, 0xb1, 0x9b, 0xf0, + 0x05, 0xa9, 0x03, 0x4c, 0xcc, 0x63, 0x8a, 0x18, 0x69, 0x03, 0x4c, 0xcc, + 0x63, 0xc8, 0xb1, 0x9b, 0xaa, 0xf0, 0x13, 0xa9, 0x9d, 0x85, 0xa4, 0xa9, + 0x71, 0x85, 0xa3, 0xe6, 0xa3, 0xd0, 0xfc, 0xe6, 0xa4, 0xd0, 0xf8, 0xca, + 0xd0, 0xed, 0x4c, 0xca, 0x63, 0xa2, 0x05, 0xb5, 0xab, 0x9d, 0xc5, 0x7f, + 0xca, 0x10, 0xf8, 0xad, 0x0a, 0x72, 0x18, 0x69, 0x06, 0x8d, 0x0a, 0x72, + 0x90, 0x03, 0xee, 0x0b, 0x72, 0x60, 0xc8, 0xb1, 0x9b, 0x8d, 0x0a, 0x72, + 0xc8, 0xb1, 0x9b, 0x8d, 0x0b, 0x72, 0xa9, 0x03, 0x4c, 0xcc, 0x63, 0xc8, + 0xb1, 0x9b, 0x8d, 0x1f, 0x62, 0x4c, 0xca, 0x63, 0x3d, 0x39, 0x35, 0x31, + 0x2d, 0x29, 0x25, 0x21, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, + 0x3f, 0x3b, 0x37, 0x33, 0x2f, 0x2b, 0x27, 0x23, 0x3e, 0x3a, 0x36, 0x32, + 0x2e, 0x2a, 0x26, 0x22, 0xa8, 0x28, 0xa8, 0x28, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd0, 0x50, 0xd0, 0x50, 0xd0, 0x50, 0xd0, 0x50, 0xa8, 0x28, 0xa8, 0x28, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, + 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, + 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, + 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, + 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x13, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, + 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, + 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, + 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, + 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, + 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, + 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, + 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, + 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, + 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, + 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, + 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, + 0x60, 0x03, 0x0c, 0x30, 0xc0, 0x06, 0x18, 0x60, 0x20, 0xe6, 0x74, 0x4c, + 0xca, 0x63, 0xa2, 0x00, 0x9d, 0x00, 0x20, 0x9d, 0x00, 0x21, 0x9d, 0x00, + 0x22, 0x9d, 0x00, 0x23, 0x9d, 0x00, 0x24, 0x9d, 0x00, 0x25, 0x9d, 0x00, + 0x26, 0x9d, 0x00, 0x27, 0x9d, 0x00, 0x28, 0x9d, 0x00, 0x29, 0x9d, 0x00, + 0x2a, 0x9d, 0x00, 0x2b, 0x9d, 0x00, 0x2c, 0x9d, 0x00, 0x2d, 0x9d, 0x00, + 0x2e, 0x9d, 0x00, 0x2f, 0x9d, 0x00, 0x30, 0x9d, 0x00, 0x31, 0x9d, 0x00, + 0x32, 0x9d, 0x00, 0x33, 0x9d, 0x00, 0x34, 0x9d, 0x00, 0x35, 0x9d, 0x00, + 0x36, 0x9d, 0x00, 0x37, 0x9d, 0x00, 0x38, 0x9d, 0x00, 0x39, 0x9d, 0x00, + 0x3a, 0x9d, 0x00, 0x3b, 0x9d, 0x00, 0x3c, 0x9d, 0x00, 0x3d, 0x9d, 0x00, + 0x3e, 0x9d, 0x00, 0x3f, 0xe8, 0xe8, 0xe8, 0xd0, 0x9b, 0x60, 0xc8, 0xb1, + 0x9b, 0xf0, 0x93, 0xc9, 0x02, 0x30, 0x0a, 0xf0, 0x04, 0xa9, 0xff, 0xd0, + 0x08, 0xa9, 0xff, 0xd0, 0x85, 0xa9, 0x00, 0xf0, 0x00, 0xa2, 0x00, 0x9d, + 0x00, 0x40, 0x9d, 0x00, 0x41, 0x9d, 0x00, 0x42, 0x9d, 0x00, 0x43, 0x9d, + 0x00, 0x44, 0x9d, 0x00, 0x45, 0x9d, 0x00, 0x46, 0x9d, 0x00, 0x47, 0x9d, + 0x00, 0x48, 0x9d, 0x00, 0x49, 0x9d, 0x00, 0x4a, 0x9d, 0x00, 0x4b, 0x9d, + 0x00, 0x4c, 0x9d, 0x00, 0x4d, 0x9d, 0x00, 0x4e, 0x9d, 0x00, 0x4f, 0x9d, + 0x00, 0x50, 0x9d, 0x00, 0x51, 0x9d, 0x00, 0x52, 0x9d, 0x00, 0x53, 0x9d, + 0x00, 0x54, 0x9d, 0x00, 0x55, 0x9d, 0x00, 0x56, 0x9d, 0x00, 0x57, 0x9d, + 0x00, 0x58, 0x9d, 0x00, 0x59, 0x9d, 0x00, 0x5a, 0x9d, 0x00, 0x5b, 0x9d, + 0x00, 0x5c, 0x9d, 0x00, 0x5d, 0x9d, 0x00, 0x5e, 0x9d, 0x00, 0x5f, 0xe8, + 0xe8, 0xe8, 0xd0, 0x9b, 0x60, 0xa9, 0x00, 0x85, 0xa4, 0x20, 0x64, 0x7d, + 0x11, 0x99, 0x91, 0x99, 0x60, 0x4c, 0x74, 0x75, 0x98, 0x4a, 0x85, 0xa4, + 0x29, 0x18, 0x85, 0xa3, 0x98, 0x29, 0x07, 0x05, 0xa3, 0xa8, 0xb9, 0x38, + 0x72, 0x18, 0x65, 0xbb, 0x85, 0x9a, 0xa5, 0xa4, 0x4a, 0x4a, 0xa8, 0xb9, + 0x58, 0x72, 0x18, 0x7d, 0x78, 0x72, 0xa8, 0xbd, 0x78, 0x73, 0x30, 0x05, + 0x11, 0x99, 0x91, 0x99, 0x60, 0x11, 0x99, 0x91, 0x99, 0xc8, 0xa9, 0x01, + 0x11, 0x99, 0x91, 0x99, 0x60, 0x4c, 0xb0, 0x75, 0xa5, 0xb5, 0x38, 0xe5, + 0xb3, 0x30, 0x2e, 0x70, 0x2e, 0x85, 0xb9, 0xa5, 0xb6, 0x38, 0xe5, 0xb4, + 0x30, 0x0f, 0x70, 0x0f, 0x85, 0xba, 0x38, 0xe5, 0xb9, 0xb0, 0x03, 0x4c, + 0xfe, 0x75, 0x4c, 0x88, 0x7a, 0x70, 0xf1, 0x49, 0xff, 0x18, 0x69, 0x01, + 0x85, 0xba, 0x38, 0xe5, 0xb9, 0x90, 0x03, 0x4c, 0x31, 0x7b, 0x4c, 0x43, + 0x78, 0x70, 0xd2, 0xa6, 0xb5, 0x86, 0xb3, 0x49, 0xff, 0x18, 0x69, 0x01, + 0x85, 0xb9, 0xa5, 0xb4, 0xa6, 0xb6, 0x38, 0xe5, 0xb6, 0x86, 0xb4, 0x4c, + 0xc0, 0x75, 0xa9, 0x00, 0x38, 0xe5, 0xb9, 0x38, 0x6a, 0x85, 0xb8, 0xa6, + 0xb3, 0xa4, 0xb4, 0x98, 0x4a, 0x85, 0xa4, 0x29, 0x18, 0x85, 0xa3, 0x98, + 0x29, 0x07, 0x05, 0xa3, 0xa8, 0xb9, 0x38, 0x72, 0x18, 0x65, 0xbb, 0x85, + 0x9a, 0xa5, 0xa4, 0x4a, 0x4a, 0xa8, 0xb9, 0x58, 0x72, 0x18, 0x7d, 0x78, + 0x72, 0xa8, 0xbd, 0x78, 0x73, 0x85, 0xb7, 0xa6, 0xb9, 0xe8, 0x29, 0x7f, + 0xc9, 0x18, 0x30, 0x1c, 0xf0, 0x15, 0xc9, 0x40, 0x30, 0x07, 0xf0, 0x0a, + 0x20, 0x05, 0x78, 0xd0, 0x22, 0x20, 0xf7, 0x76, 0xd0, 0x1d, 0x20, 0x38, + 0x77, 0xd0, 0x18, 0x20, 0xbd, 0x77, 0xd0, 0x13, 0xc9, 0x06, 0x30, 0x07, + 0xf0, 0x0a, 0x20, 0x83, 0x76, 0xd0, 0x08, 0x20, 0x70, 0x76, 0xd0, 0x03, + 0x20, 0x4c, 0x77, 0x11, 0x99, 0x91, 0x99, 0x60, 0xa5, 0xb8, 0xca, 0xf0, + 0x1a, 0x18, 0x65, 0xba, 0xb0, 0x1e, 0xca, 0xf0, 0x15, 0x65, 0xba, 0xb0, + 0x31, 0xd0, 0x51, 0xca, 0xf0, 0x0f, 0xa5, 0xb8, 0x18, 0x65, 0xba, 0xb0, + 0x2d, 0xd0, 0x5b, 0xa9, 0x03, 0x60, 0xa9, 0x0f, 0x60, 0xa9, 0x0c, 0x60, + 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x03, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, + 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, 0xbc, 0xb0, 0xd6, 0x20, 0x21, 0x78, + 0xb0, 0xd1, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x0f, 0xd0, 0x06, 0xe5, 0xb9, + 0x85, 0xb8, 0xa9, 0x0c, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0xe9, + 0x03, 0x85, 0x9a, 0xc5, 0xbc, 0xb0, 0x28, 0x20, 0x21, 0x78, 0xb0, 0x23, + 0xca, 0xf0, 0x0d, 0x65, 0xba, 0xb0, 0x45, 0x85, 0xb8, 0xa9, 0x7f, 0x10, + 0x24, 0xa9, 0x3c, 0x60, 0xa9, 0x3f, 0x60, 0xa9, 0x30, 0x60, 0xca, 0xf0, + 0xf4, 0x65, 0xba, 0xb0, 0x27, 0x85, 0xb8, 0xa9, 0x7c, 0x10, 0x0e, 0xca, + 0xf0, 0xed, 0xa5, 0xb8, 0x18, 0x65, 0xba, 0xb0, 0x0f, 0x85, 0xb8, 0xa9, + 0x70, 0x11, 0x99, 0x91, 0x99, 0xc8, 0xca, 0xd0, 0x30, 0xa9, 0x01, 0x60, + 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x30, 0xd0, 0x0e, 0xe5, 0xb9, 0x85, 0xb8, + 0xa9, 0x3c, 0xd0, 0x06, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x3f, 0x11, 0x99, + 0x91, 0x99, 0xa5, 0x9a, 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, 0xbc, 0xb0, + 0x03, 0x20, 0x21, 0x78, 0xa9, 0x40, 0x18, 0xd0, 0xc8, 0xa5, 0xb8, 0x65, + 0xba, 0xb0, 0x1b, 0xca, 0xf0, 0x12, 0x65, 0xba, 0xb0, 0x2e, 0xd0, 0x4e, + 0xca, 0xf0, 0x0c, 0xa5, 0xb8, 0x18, 0x65, 0xba, 0xb0, 0x2a, 0xd0, 0x58, + 0xa9, 0x07, 0x60, 0xa9, 0x06, 0x60, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x01, + 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, + 0xbc, 0xb0, 0xd9, 0x20, 0x21, 0x78, 0xb0, 0xd4, 0xe5, 0xb9, 0x85, 0xb8, + 0xa9, 0x07, 0xd0, 0x06, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x06, 0x11, 0x99, + 0x91, 0x99, 0xa5, 0x9a, 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, 0xbc, 0xb0, + 0x28, 0x20, 0x21, 0x78, 0xb0, 0x23, 0xca, 0xf0, 0x0d, 0x65, 0xba, 0xb0, + 0x4c, 0x85, 0xb8, 0xa9, 0x7f, 0x10, 0x24, 0xa9, 0x1e, 0x60, 0xa9, 0x1f, + 0x60, 0xa9, 0x18, 0x60, 0xca, 0xf0, 0xf4, 0x65, 0xba, 0xb0, 0x2e, 0x85, + 0xb8, 0xa9, 0x7e, 0x10, 0x0e, 0xca, 0xf0, 0xed, 0xa5, 0xb8, 0x18, 0x65, + 0xba, 0xb0, 0x16, 0x85, 0xb8, 0xa9, 0x78, 0x11, 0x99, 0x91, 0x99, 0xc8, + 0xca, 0xf0, 0x36, 0xa5, 0xb8, 0x18, 0x65, 0xba, 0xb0, 0x32, 0x4c, 0x72, + 0x76, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x18, 0xd0, 0x0e, 0xe5, 0xb9, 0x85, + 0xb8, 0xa9, 0x1e, 0xd0, 0x06, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x1f, 0x11, + 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, 0xbc, + 0xb0, 0x03, 0x20, 0x21, 0x78, 0xa9, 0x60, 0xd0, 0xc2, 0x68, 0x68, 0x60, + 0xe5, 0xb9, 0x85, 0xb8, 0xa5, 0x9a, 0x18, 0xe9, 0x03, 0x85, 0x9a, 0xc5, + 0xbc, 0xb0, 0x03, 0x20, 0x21, 0x78, 0x4c, 0x70, 0x76, 0xc9, 0x1c, 0xf0, + 0x0d, 0x18, 0x98, 0x69, 0x80, 0xa8, 0xa5, 0x9a, 0x69, 0x1f, 0x85, 0x9a, + 0x38, 0x60, 0x98, 0xc9, 0x80, 0x18, 0x10, 0xef, 0x69, 0x58, 0xa8, 0xa5, + 0x9a, 0x69, 0x23, 0x85, 0x9a, 0x38, 0x60, 0xa9, 0x00, 0x38, 0xe5, 0xb9, + 0x38, 0x6a, 0x85, 0xb8, 0xa6, 0xb3, 0xa4, 0xb4, 0x98, 0x4a, 0x85, 0xa4, + 0x29, 0x18, 0x85, 0xa3, 0x98, 0x29, 0x07, 0x05, 0xa3, 0xa8, 0xb9, 0x38, + 0x72, 0x18, 0x65, 0xbb, 0x85, 0x9a, 0xa5, 0xa4, 0x4a, 0x4a, 0xa8, 0xb9, + 0x58, 0x72, 0x18, 0x7d, 0x78, 0x72, 0xa8, 0xbd, 0x78, 0x73, 0x85, 0xb7, + 0xa6, 0xb9, 0xe8, 0x29, 0x7f, 0xc9, 0x18, 0x30, 0x1c, 0xf0, 0x15, 0xc9, + 0x40, 0x30, 0x07, 0xf0, 0x0a, 0x20, 0x4a, 0x7a, 0xd0, 0x22, 0x20, 0x3c, + 0x79, 0xd0, 0x1d, 0x20, 0x7d, 0x79, 0xd0, 0x18, 0x20, 0x02, 0x7a, 0xd0, + 0x13, 0xc9, 0x06, 0x30, 0x07, 0xf0, 0x0a, 0x20, 0xc8, 0x78, 0xd0, 0x08, + 0x20, 0xb5, 0x78, 0xd0, 0x03, 0x20, 0x91, 0x79, 0x11, 0x99, 0x91, 0x99, + 0x60, 0xa5, 0xb8, 0xca, 0xf0, 0x1a, 0x18, 0x65, 0xba, 0xb0, 0x1e, 0xca, + 0xf0, 0x15, 0x65, 0xba, 0xb0, 0x31, 0xd0, 0x51, 0xca, 0xf0, 0x0f, 0xa5, + 0xb8, 0x18, 0x65, 0xba, 0xb0, 0x2d, 0xd0, 0x5b, 0xa9, 0x03, 0x60, 0xa9, + 0x0f, 0x60, 0xa9, 0x0c, 0x60, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x03, 0x11, + 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0x69, 0x04, 0x85, 0x9a, 0xc5, 0xbd, + 0x90, 0xd6, 0x20, 0x66, 0x7a, 0xb0, 0xd1, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, + 0x0f, 0xd0, 0x06, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x0c, 0x11, 0x99, 0x91, + 0x99, 0xa5, 0x9a, 0x18, 0x69, 0x04, 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0x28, + 0x20, 0x66, 0x7a, 0xb0, 0x23, 0xca, 0xf0, 0x0d, 0x65, 0xba, 0xb0, 0x45, + 0x85, 0xb8, 0xa9, 0x7f, 0x10, 0x24, 0xa9, 0x3c, 0x60, 0xa9, 0x3f, 0x60, + 0xa9, 0x30, 0x60, 0xca, 0xf0, 0xf4, 0x65, 0xba, 0xb0, 0x27, 0x85, 0xb8, + 0xa9, 0x7c, 0x10, 0x0e, 0xca, 0xf0, 0xed, 0xa5, 0xb8, 0x18, 0x65, 0xba, + 0xb0, 0x0f, 0x85, 0xb8, 0xa9, 0x70, 0x11, 0x99, 0x91, 0x99, 0xc8, 0xca, + 0xd0, 0x30, 0xa9, 0x01, 0x60, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x30, 0xd0, + 0x0e, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x3c, 0xd0, 0x06, 0xe5, 0xb9, 0x85, + 0xb8, 0xa9, 0x3f, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0x69, 0x04, + 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0x03, 0x20, 0x66, 0x7a, 0xa9, 0x40, 0x18, + 0xd0, 0xc8, 0xa5, 0xb8, 0x65, 0xba, 0xb0, 0x1b, 0xca, 0xf0, 0x12, 0x65, + 0xba, 0xb0, 0x2e, 0xd0, 0x4e, 0xca, 0xf0, 0x0c, 0xa5, 0xb8, 0x18, 0x65, + 0xba, 0xb0, 0x2a, 0xd0, 0x58, 0xa9, 0x07, 0x60, 0xa9, 0x06, 0x60, 0xe5, + 0xb9, 0x85, 0xb8, 0xa9, 0x01, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, + 0x69, 0x04, 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0xd9, 0x20, 0x66, 0x7a, 0xb0, + 0xd4, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x07, 0xd0, 0x06, 0xe5, 0xb9, 0x85, + 0xb8, 0xa9, 0x06, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0x69, 0x04, + 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0x28, 0x20, 0x66, 0x7a, 0xb0, 0x23, 0xca, + 0xf0, 0x0d, 0x65, 0xba, 0xb0, 0x4c, 0x85, 0xb8, 0xa9, 0x7f, 0x10, 0x24, + 0xa9, 0x1e, 0x60, 0xa9, 0x1f, 0x60, 0xa9, 0x18, 0x60, 0xca, 0xf0, 0xf4, + 0x65, 0xba, 0xb0, 0x2e, 0x85, 0xb8, 0xa9, 0x7e, 0x10, 0x0e, 0xca, 0xf0, + 0xed, 0xa5, 0xb8, 0x18, 0x65, 0xba, 0xb0, 0x16, 0x85, 0xb8, 0xa9, 0x78, + 0x11, 0x99, 0x91, 0x99, 0xc8, 0xca, 0xf0, 0x36, 0xa5, 0xb8, 0x18, 0x65, + 0xba, 0xb0, 0x32, 0x4c, 0xb7, 0x78, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x18, + 0xd0, 0x0e, 0xe5, 0xb9, 0x85, 0xb8, 0xa9, 0x1e, 0xd0, 0x06, 0xe5, 0xb9, + 0x85, 0xb8, 0xa9, 0x1f, 0x11, 0x99, 0x91, 0x99, 0xa5, 0x9a, 0x18, 0x69, + 0x04, 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0x03, 0x20, 0x66, 0x7a, 0xa9, 0x60, + 0xd0, 0xc2, 0x68, 0x68, 0x60, 0xe5, 0xb9, 0x85, 0xb8, 0xa5, 0x9a, 0x18, + 0x69, 0x04, 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0x03, 0x20, 0x66, 0x7a, 0x4c, + 0xb5, 0x78, 0xc9, 0x43, 0xf0, 0x0d, 0x38, 0x98, 0xe9, 0x80, 0xa8, 0xa5, + 0x9a, 0xe9, 0x1f, 0x85, 0x9a, 0x38, 0x60, 0x98, 0xc9, 0x80, 0x38, 0x30, + 0xef, 0xe9, 0x58, 0xa8, 0xa5, 0x9a, 0xe9, 0x23, 0x85, 0x9a, 0x38, 0x60, + 0xa5, 0xba, 0x18, 0x6a, 0x85, 0xb8, 0xa6, 0xb3, 0xa4, 0xb4, 0x98, 0x4a, + 0x85, 0xa4, 0x29, 0x18, 0x85, 0xa3, 0x98, 0x29, 0x07, 0x05, 0xa3, 0xa8, + 0xb9, 0x38, 0x72, 0x18, 0x65, 0xbb, 0x85, 0x9a, 0xa5, 0xa4, 0x4a, 0x4a, + 0xa8, 0xb9, 0x58, 0x72, 0x18, 0x7d, 0x78, 0x72, 0xa8, 0xbd, 0x78, 0x73, + 0x85, 0xb7, 0xa6, 0xba, 0xe8, 0xa5, 0xb7, 0x30, 0x32, 0x11, 0x99, 0x91, + 0x99, 0xca, 0xf0, 0x2a, 0xa5, 0x9a, 0x38, 0xe9, 0x04, 0xc9, 0x20, 0x90, + 0x37, 0x85, 0x9a, 0xa5, 0xb8, 0x38, 0xe5, 0xb9, 0x85, 0xb8, 0xb0, 0xe1, + 0x65, 0xba, 0x85, 0xb8, 0x18, 0xa5, 0xb7, 0x30, 0x47, 0x2a, 0x2a, 0x30, + 0x16, 0x85, 0xb7, 0x11, 0x99, 0x91, 0x99, 0xca, 0xd0, 0xd6, 0x60, 0xc8, + 0xa9, 0x01, 0x11, 0x99, 0x91, 0x99, 0x88, 0xa9, 0xc0, 0x30, 0xc2, 0xc9, + 0x80, 0xd0, 0x23, 0xc8, 0xa9, 0x03, 0x10, 0xe1, 0xc9, 0x1c, 0xf0, 0x0b, + 0x18, 0x98, 0x69, 0x80, 0xa8, 0xa5, 0x9a, 0x69, 0x1b, 0x90, 0xba, 0x98, + 0xc9, 0x80, 0x18, 0x10, 0xf1, 0x69, 0x58, 0xa8, 0xa5, 0x9a, 0x69, 0x1f, + 0x90, 0xab, 0xa9, 0xc0, 0x85, 0xb7, 0x30, 0xc7, 0xc8, 0xa9, 0x06, 0xd0, + 0xb8, 0xa5, 0xba, 0x18, 0x6a, 0x85, 0xb8, 0xa6, 0xb3, 0xa4, 0xb4, 0x98, + 0x4a, 0x85, 0xa4, 0x29, 0x18, 0x85, 0xa3, 0x98, 0x29, 0x07, 0x05, 0xa3, + 0xa8, 0xb9, 0x38, 0x72, 0x18, 0x65, 0xbb, 0x85, 0x9a, 0xa5, 0xa4, 0x4a, + 0x4a, 0xa8, 0xb9, 0x58, 0x72, 0x18, 0x7d, 0x78, 0x72, 0xa8, 0xbd, 0x78, + 0x73, 0x85, 0xb7, 0xa6, 0xba, 0xe8, 0xa5, 0xb7, 0x30, 0x32, 0x11, 0x99, + 0x91, 0x99, 0xca, 0xf0, 0x2a, 0xa5, 0x9a, 0x18, 0x69, 0x04, 0xc9, 0x40, + 0xb0, 0x37, 0x85, 0x9a, 0xa5, 0xb8, 0x38, 0xe5, 0xb9, 0x85, 0xb8, 0xb0, + 0xe1, 0x65, 0xba, 0x85, 0xb8, 0x18, 0xa5, 0xb7, 0x30, 0x47, 0x2a, 0x2a, + 0x30, 0x16, 0x85, 0xb7, 0x11, 0x99, 0x91, 0x99, 0xca, 0xd0, 0xd6, 0x60, + 0xc8, 0xa9, 0x01, 0x11, 0x99, 0x91, 0x99, 0x88, 0xa9, 0xc0, 0x30, 0xc2, + 0xc9, 0x80, 0xd0, 0x23, 0xc8, 0xa9, 0x03, 0x10, 0xe1, 0xc9, 0x43, 0xf0, + 0x0b, 0x38, 0x98, 0xe9, 0x80, 0xa8, 0xa5, 0x9a, 0xe9, 0x1b, 0xb0, 0xba, + 0x98, 0xc9, 0x80, 0x38, 0x30, 0xf1, 0xe9, 0x58, 0xa8, 0xa5, 0x9a, 0xe9, + 0x1f, 0xb0, 0xab, 0xa9, 0xc0, 0x85, 0xb7, 0x30, 0xc7, 0xc8, 0xa9, 0x06, + 0xd0, 0xb8, 0x85, 0xa9, 0x49, 0xff, 0x31, 0x99, 0x09, 0x80, 0x85, 0xa3, + 0x98, 0x6a, 0xb0, 0x09, 0xa5, 0xa9, 0x25, 0xbe, 0x05, 0xa3, 0x91, 0x99, + 0x60, 0xa5, 0xa9, 0x25, 0xbf, 0x05, 0xa3, 0x91, 0x99, 0x60, 0xc8, 0xb1, + 0x9b, 0xf0, 0x02, 0xa9, 0x20, 0x85, 0xbb, 0x18, 0x69, 0x20, 0x8d, 0xce, + 0x7a, 0x85, 0xbc, 0x38, 0xe9, 0x04, 0x8d, 0x22, 0x78, 0x8d, 0x09, 0x7b, + 0x18, 0x69, 0x24, 0x8d, 0x77, 0x7b, 0x85, 0xbd, 0x18, 0x69, 0x03, 0x8d, + 0x67, 0x7a, 0x8d, 0xb2, 0x7b, 0x4c, 0xca, 0x63, 0xa5, 0x7d, 0xf0, 0x03, + 0x4c, 0xc1, 0x60, 0xc8, 0xb1, 0x9b, 0xf0, 0x26, 0xc9, 0x01, 0xf0, 0x1c, + 0xc9, 0x02, 0xf0, 0x37, 0xc9, 0x03, 0xd0, 0x06, 0xa9, 0x55, 0xa2, 0x2a, + 0xd0, 0x04, 0xa9, 0x2a, 0xa2, 0x55, 0x85, 0xbe, 0x86, 0xbf, 0xa9, 0x09, + 0xa2, 0x80, 0xd0, 0x2b, 0xa9, 0x2a, 0xa2, 0x55, 0xd0, 0x1d, 0xa2, 0x00, + 0xa9, 0x11, 0x20, 0xa1, 0x7c, 0xe8, 0xa9, 0x99, 0x20, 0xa1, 0x7c, 0xe8, + 0xa9, 0x91, 0x20, 0xa1, 0x7c, 0xe8, 0xa9, 0x99, 0x4c, 0x9b, 0x7c, 0xa9, + 0x55, 0xa2, 0x2a, 0x85, 0xbe, 0x86, 0xbf, 0xa9, 0x29, 0xa2, 0x7f, 0x8d, + 0xe0, 0x7b, 0x8e, 0xe1, 0x7b, 0xa2, 0x00, 0xa9, 0x20, 0x20, 0xa1, 0x7c, + 0xe8, 0xa9, 0xda, 0x20, 0xa1, 0x7c, 0xe8, 0xa9, 0x7b, 0x20, 0xa1, 0x7c, + 0xe8, 0xa9, 0xea, 0x20, 0xa1, 0x7c, 0x4c, 0xca, 0x63, 0x9d, 0x6b, 0x76, + 0x9d, 0x9e, 0x76, 0x9d, 0xc0, 0x76, 0x9d, 0x05, 0x77, 0x9d, 0x26, 0x77, + 0x9d, 0x64, 0x77, 0x9d, 0x86, 0x77, 0x9d, 0xcb, 0x77, 0x9d, 0xf3, 0x77, + 0x9d, 0xc1, 0x7a, 0x9d, 0xeb, 0x7a, 0x9d, 0xf6, 0x7a, 0x9d, 0x6a, 0x7b, + 0x9d, 0x94, 0x7b, 0x9d, 0x9f, 0x7b, 0x9d, 0xb0, 0x78, 0x9d, 0xe3, 0x78, + 0x9d, 0x05, 0x79, 0x9d, 0x4a, 0x79, 0x9d, 0x6b, 0x79, 0x9d, 0xa9, 0x79, + 0x9d, 0xcb, 0x79, 0x9d, 0x10, 0x7a, 0x9d, 0x38, 0x7a, 0x9d, 0x9c, 0x75, + 0x9d, 0xa1, 0x75, 0x60, 0xa9, 0x00, 0x85, 0xa3, 0x85, 0xa4, 0xa5, 0xa3, + 0x38, 0xe5, 0xa4, 0x85, 0xa5, 0xa5, 0xb5, 0xe5, 0xb3, 0x70, 0x48, 0x30, + 0x36, 0x85, 0xb9, 0xa5, 0xb6, 0x38, 0xe5, 0xb4, 0x30, 0x14, 0x70, 0x14, + 0x85, 0xba, 0x26, 0xa5, 0x26, 0xb9, 0xa5, 0xba, 0xe5, 0xb9, 0xb0, 0x03, + 0x4c, 0xa4, 0x7d, 0x4c, 0xee, 0x7d, 0x70, 0xec, 0x49, 0xff, 0x18, 0x69, + 0x01, 0x85, 0xba, 0x26, 0xa5, 0x26, 0xb9, 0xa5, 0xba, 0xe5, 0xb9, 0x90, + 0x03, 0x4c, 0x33, 0x7e, 0x4c, 0x78, 0x7e, 0x85, 0xb9, 0xa5, 0xa4, 0x38, + 0xe5, 0xa3, 0x85, 0xa5, 0xa5, 0xb3, 0xe5, 0xb5, 0x10, 0x04, 0x38, 0x4c, + 0xc2, 0x7e, 0x85, 0xb9, 0xa6, 0xa3, 0x86, 0xa4, 0xa6, 0xb5, 0x86, 0xb3, + 0xa5, 0xb4, 0xa6, 0xb6, 0x38, 0xe5, 0xb6, 0x86, 0xb4, 0x4c, 0x0c, 0x7d, + 0x98, 0x4a, 0x85, 0xa4, 0x29, 0x18, 0x85, 0xa3, 0x98, 0x29, 0x07, 0x05, + 0xa3, 0xa8, 0xb9, 0x38, 0x72, 0x18, 0x65, 0xbb, 0x85, 0x9a, 0xa5, 0xa4, + 0x4a, 0x4a, 0xa8, 0xb9, 0x58, 0x72, 0x18, 0x7d, 0x78, 0x72, 0xa8, 0x4a, + 0x6a, 0x45, 0xa4, 0x30, 0x04, 0xa9, 0x55, 0xd0, 0x0d, 0xa9, 0x2a, 0x3d, + 0x78, 0x73, 0xd0, 0x03, 0xc8, 0xa9, 0x01, 0x85, 0xb7, 0x60, 0x3d, 0x78, + 0x73, 0x85, 0xb7, 0x60, 0xa9, 0x00, 0x38, 0xe5, 0xb9, 0x38, 0x6a, 0x85, + 0xb8, 0xa6, 0xb3, 0xa4, 0xb4, 0x20, 0x64, 0x7d, 0xa6, 0xb9, 0xe8, 0xa5, + 0xb7, 0x11, 0x99, 0x91, 0x99, 0xca, 0xf0, 0x2d, 0x06, 0xb7, 0x30, 0x21, + 0xa5, 0xb8, 0x65, 0xba, 0xb0, 0x05, 0x85, 0xb8, 0x4c, 0xb7, 0x7d, 0x38, + 0xe5, 0xb9, 0x85, 0xb8, 0xa5, 0x9a, 0x38, 0xe9, 0x04, 0x85, 0x9a, 0xc5, + 0xbc, 0xb0, 0xd8, 0x20, 0x21, 0x78, 0x4c, 0xb7, 0x7d, 0xc8, 0xa9, 0x01, + 0x85, 0xb7, 0x4c, 0xc4, 0x7d, 0x60, 0xa5, 0xba, 0x18, 0x6a, 0x85, 0xb8, + 0xa6, 0xb3, 0xa4, 0xb4, 0x20, 0x64, 0x7d, 0xa6, 0xba, 0xe8, 0xa5, 0xb7, + 0x11, 0x99, 0x91, 0x99, 0xca, 0xf0, 0x2b, 0xa5, 0x9a, 0x38, 0xe9, 0x04, + 0x85, 0x9a, 0xc5, 0xbc, 0xb0, 0x03, 0x20, 0x21, 0x78, 0xa5, 0xb8, 0x38, + 0xe5, 0xb9, 0x85, 0xb8, 0xb0, 0xe0, 0x65, 0xba, 0x85, 0xb8, 0xa5, 0xb7, + 0x0a, 0x30, 0x04, 0x85, 0xb7, 0xd0, 0xd3, 0xc8, 0xa9, 0x01, 0x85, 0xb7, + 0xd0, 0xcc, 0x60, 0xa5, 0xba, 0x18, 0x6a, 0x85, 0xb8, 0xa6, 0xb3, 0xa4, + 0xb4, 0x20, 0x64, 0x7d, 0xa6, 0xba, 0xe8, 0xa5, 0xb7, 0x11, 0x99, 0x91, + 0x99, 0xca, 0xf0, 0x2b, 0xa5, 0x9a, 0x18, 0x69, 0x04, 0x85, 0x9a, 0xc5, + 0xbd, 0x90, 0x03, 0x20, 0x66, 0x7a, 0xa5, 0xb8, 0x38, 0xe5, 0xb9, 0x85, + 0xb8, 0xb0, 0xe0, 0x65, 0xba, 0x85, 0xb8, 0xa5, 0xb7, 0x0a, 0x30, 0x04, + 0x85, 0xb7, 0xd0, 0xd3, 0xc8, 0xa9, 0x01, 0x85, 0xb7, 0xd0, 0xcc, 0x60, + 0xa9, 0x00, 0x38, 0xe5, 0xb9, 0x38, 0x6a, 0x85, 0xb8, 0xa6, 0xb3, 0xa4, + 0xb4, 0x20, 0x64, 0x7d, 0xa6, 0xb9, 0xe8, 0xa5, 0xb7, 0x11, 0x99, 0x91, + 0x99, 0xca, 0xf0, 0x2d, 0x06, 0xb7, 0x30, 0x21, 0xa5, 0xb8, 0x65, 0xba, + 0xb0, 0x05, 0x85, 0xb8, 0x4c, 0x8b, 0x7e, 0x38, 0xe5, 0xb9, 0x85, 0xb8, + 0xa5, 0x9a, 0x18, 0x69, 0x04, 0x85, 0x9a, 0xc5, 0xbd, 0x90, 0xd8, 0x20, + 0x66, 0x7a, 0x4c, 0x8b, 0x7e, 0xc8, 0xa9, 0x01, 0x85, 0xb7, 0x4c, 0x98, + 0x7e, 0x60, 0x6a, 0x85, 0xb9, 0x66, 0xa5, 0xa5, 0xa3, 0x48, 0xa5, 0xb5, + 0x48, 0xa5, 0xa4, 0x18, 0x65, 0xa5, 0x85, 0xa3, 0x48, 0xa5, 0xb3, 0x65, + 0xb9, 0x85, 0xb5, 0x48, 0xa5, 0xb6, 0x48, 0x38, 0xe5, 0xb4, 0x70, 0x02, + 0xc9, 0x80, 0x6a, 0x18, 0x65, 0xb4, 0x85, 0xb6, 0x48, 0x20, 0xf6, 0x7c, + 0x68, 0x85, 0xb4, 0x68, 0x85, 0xb6, 0x68, 0x85, 0xb3, 0x68, 0x85, 0xa4, + 0x68, 0x85, 0xb5, 0x68, 0x85, 0xa3, 0x20, 0xf6, 0x7c, 0x60, 0xa5, 0x7d, + 0xd0, 0x34, 0xc8, 0xb1, 0x9b, 0xf0, 0x06, 0x20, 0x1b, 0x7f, 0x4c, 0xca, + 0x63, 0x20, 0x29, 0x7f, 0x4c, 0xca, 0x63, 0xa9, 0xf0, 0x8d, 0xae, 0x75, + 0xa9, 0x7c, 0xa2, 0x65, 0xa0, 0x75, 0x4c, 0x34, 0x7f, 0xa9, 0xb0, 0x8d, + 0xae, 0x75, 0xa9, 0x75, 0xa2, 0x74, 0xa0, 0x75, 0x8d, 0xaf, 0x75, 0x8e, + 0x72, 0x75, 0x8c, 0x73, 0x75, 0x60, 0xc8, 0xb1, 0x9b, 0xa0, 0x01, 0x91, + 0x9d, 0x88, 0xa9, 0x14, 0x4c, 0xcb, 0x60, 0xc8, 0xb1, 0x9b, 0xf0, 0x04, + 0xa9, 0x51, 0x10, 0x02, 0xa9, 0x11, 0x8d, 0x9c, 0x75, 0x8d, 0xa1, 0x75, + 0x8d, 0xa8, 0x75, 0x8d, 0xb0, 0x78, 0x8d, 0x4a, 0x79, 0x8d, 0x10, 0x7a, + 0x8d, 0x6b, 0x76, 0x8d, 0x9e, 0x76, 0x8d, 0xc0, 0x76, 0x8d, 0x05, 0x77, + 0x8d, 0x26, 0x77, 0x8d, 0x64, 0x77, 0x8d, 0x86, 0x77, 0x8d, 0xcb, 0x77, + 0x8d, 0xf3, 0x77, 0x8d, 0xe3, 0x78, 0x8d, 0x05, 0x79, 0x8d, 0x6b, 0x79, + 0x8d, 0xa9, 0x79, 0x8d, 0xcb, 0x79, 0x8d, 0x38, 0x7a, 0x8d, 0xc1, 0x7a, + 0x8d, 0xeb, 0x7a, 0x8d, 0xf6, 0x7a, 0x8d, 0x6a, 0x7b, 0x8d, 0x94, 0x7b, + 0x8d, 0x6c, 0x75, 0x8d, 0xb9, 0x7d, 0x8d, 0x00, 0x7e, 0x8d, 0x45, 0x7e, + 0x8d, 0x8d, 0x7e, 0x8d, 0x9f, 0x7b, 0xa5, 0x7d, 0xf0, 0x08, 0xa9, 0x79, + 0xa0, 0x00, 0x91, 0x9d, 0x84, 0x7d, 0x4c, 0xca, 0x63, 0x0b, 0xfb, 0x80, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 +}; +unsigned int a23d2bin_len = 8443; diff --git a/pc/include/app.h b/pc/include/app.h new file mode 100644 index 0000000..bc6669c --- /dev/null +++ b/pc/include/app.h @@ -0,0 +1,4819 @@ +/* +------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. +------------------------------------------------------------------------------ + +app.h - v0.5 - Small cross-platform base framework for graphical apps. + +Do this: + #define APP_IMPLEMENTATION +before you include this file in *one* C/C++ file to create the implementation. +*/ + + +#ifndef app_h +#define app_h + +#ifndef APP_S16 + #define APP_S16 short +#endif +#ifndef APP_U32 + #define APP_U32 unsigned int +#endif +#ifndef APP_U64 + #define APP_U64 unsigned long long +#endif + + +typedef struct app_t app_t; + +int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ); + +typedef enum app_state_t { APP_STATE_EXIT_REQUESTED, APP_STATE_NORMAL, } app_state_t; +app_state_t app_yield( app_t* app ); +void app_cancel_exit( app_t* app ); + +void app_title( app_t* app, char const* title ); + +char const* app_cmdline( app_t* app ); +char const* app_filename( app_t* app ); +char const* app_userdata( app_t* app ); +char const* app_appdata( app_t* app ); + +APP_U64 app_time_count( app_t* app ); +APP_U64 app_time_freq( app_t* app ); + +typedef enum app_log_level_t { APP_LOG_LEVEL_INFO, APP_LOG_LEVEL_WARNING, APP_LOG_LEVEL_ERROR, } app_log_level_t; +void app_log( app_t* app, app_log_level_t level, char const* message ); +void app_fatal_error( app_t* app, char const* message ); + +void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ); +void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ); + +void app_pointer_pos( app_t* app, int x, int y ); +int app_pointer_x( app_t* app ); +int app_pointer_y( app_t* app ); + +void app_pointer_limit( app_t* app, int x, int y, int width, int height ); +void app_pointer_limit_off( app_t* app ); + +typedef enum app_interpolation_t { APP_INTERPOLATION_NONE, APP_INTERPOLATION_LINEAR, } app_interpolation_t; +void app_interpolation( app_t* app, app_interpolation_t interpolation ); + +typedef enum app_screenmode_t { APP_SCREENMODE_WINDOW, APP_SCREENMODE_FULLSCREEN, } app_screenmode_t; +void app_screenmode( app_t* app, app_screenmode_t screenmode ); + +void app_window_size( app_t* app, int width, int height ); +int app_window_width( app_t* app ); +int app_window_height( app_t* app ); + +void app_window_pos( app_t* app, int x, int y ); +int app_window_x( app_t* app ); +int app_window_y( app_t* app ); + +typedef struct app_display_t + { + char id[ 64 ]; + int x; + int y; + int width; + int height; + } app_display_t ; + +typedef struct app_displays_t { app_display_t* displays; int count; } app_displays_t; +app_displays_t app_displays( app_t* app ); + +void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ); + +void app_sound( app_t* app, int sample_pairs_count, + void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ), void* user_data ); +void app_sound_volume( app_t* app, float volume ); + +typedef enum app_key_t { APP_KEY_INVALID, APP_KEY_LBUTTON, APP_KEY_RBUTTON, APP_KEY_CANCEL, APP_KEY_MBUTTON, + APP_KEY_XBUTTON1, APP_KEY_XBUTTON2, APP_KEY_BACK, APP_KEY_TAB, APP_KEY_CLEAR, APP_KEY_RETURN, APP_KEY_SHIFT, + APP_KEY_CONTROL, APP_KEY_MENU, APP_KEY_PAUSE, APP_KEY_CAPITAL, APP_KEY_KANA, APP_KEY_HANGUL = APP_KEY_KANA, + APP_KEY_JUNJA, APP_KEY_FINAL, APP_KEY_HANJA, APP_KEY_KANJI = APP_KEY_HANJA, APP_KEY_ESCAPE, APP_KEY_CONVERT, + APP_KEY_NONCONVERT, APP_KEY_ACCEPT, APP_KEY_MODECHANGE, APP_KEY_SPACE, APP_KEY_PRIOR, APP_KEY_NEXT, APP_KEY_END, + APP_KEY_HOME, APP_KEY_LEFT, APP_KEY_UP, APP_KEY_RIGHT, APP_KEY_DOWN, APP_KEY_SELECT, APP_KEY_PRINT, APP_KEY_EXEC, + APP_KEY_SNAPSHOT, APP_KEY_INSERT, APP_KEY_DELETE, APP_KEY_HELP, APP_KEY_0, APP_KEY_1, APP_KEY_2, APP_KEY_3, + APP_KEY_4, APP_KEY_5, APP_KEY_6, APP_KEY_7, APP_KEY_8, APP_KEY_9, APP_KEY_A, APP_KEY_B, APP_KEY_C, APP_KEY_D, + APP_KEY_E, APP_KEY_F, APP_KEY_G, APP_KEY_H, APP_KEY_I, APP_KEY_J, APP_KEY_K, APP_KEY_L, APP_KEY_M, APP_KEY_N, + APP_KEY_O, APP_KEY_P, APP_KEY_Q, APP_KEY_R, APP_KEY_S, APP_KEY_T, APP_KEY_U, APP_KEY_V, APP_KEY_W, APP_KEY_X, + APP_KEY_Y, APP_KEY_Z, APP_KEY_LWIN, APP_KEY_RWIN, APP_KEY_APPS, APP_KEY_SLEEP, APP_KEY_NUMPAD0, APP_KEY_NUMPAD1, + APP_KEY_NUMPAD2, APP_KEY_NUMPAD3, APP_KEY_NUMPAD4, APP_KEY_NUMPAD5, APP_KEY_NUMPAD6, APP_KEY_NUMPAD7, + APP_KEY_NUMPAD8, APP_KEY_NUMPAD9, APP_KEY_MULTIPLY, APP_KEY_ADD, APP_KEY_SEPARATOR, APP_KEY_SUBTRACT, + APP_KEY_DECIMAL, APP_KEY_DIVIDE, APP_KEY_F1, APP_KEY_F2, APP_KEY_F3, APP_KEY_F4, APP_KEY_F5, APP_KEY_F6, APP_KEY_F7, + APP_KEY_F8, APP_KEY_F9, APP_KEY_F10, APP_KEY_F11, APP_KEY_F12, APP_KEY_F13, APP_KEY_F14, APP_KEY_F15, APP_KEY_F16, + APP_KEY_F17, APP_KEY_F18, APP_KEY_F19, APP_KEY_F20, APP_KEY_F21, APP_KEY_F22, APP_KEY_F23, APP_KEY_F24, + APP_KEY_NUMLOCK, APP_KEY_SCROLL, APP_KEY_LSHIFT, APP_KEY_RSHIFT, APP_KEY_LCONTROL, APP_KEY_RCONTROL, APP_KEY_LMENU, + APP_KEY_RMENU, APP_KEY_BROWSER_BACK, APP_KEY_BROWSER_FORWARD, APP_KEY_BROWSER_REFRESH, APP_KEY_BROWSER_STOP, + APP_KEY_BROWSER_SEARCH, APP_KEY_BROWSER_FAVORITES, APP_KEY_BROWSER_HOME, APP_KEY_VOLUME_MUTE, APP_KEY_VOLUME_DOWN, + APP_KEY_VOLUME_UP, APP_KEY_MEDIA_NEXT_TRACK, APP_KEY_MEDIA_PREV_TRACK, APP_KEY_MEDIA_STOP, APP_KEY_MEDIA_PLAY_PAUSE, + APP_KEY_LAUNCH_MAIL, APP_KEY_LAUNCH_MEDIA_SELECT, APP_KEY_LAUNCH_APP1, APP_KEY_LAUNCH_APP2, APP_KEY_OEM_1, + APP_KEY_OEM_PLUS, APP_KEY_OEM_COMMA, APP_KEY_OEM_MINUS, APP_KEY_OEM_PERIOD, APP_KEY_OEM_2, APP_KEY_OEM_3, + APP_KEY_OEM_4, APP_KEY_OEM_5, APP_KEY_OEM_6, APP_KEY_OEM_7, APP_KEY_OEM_8, APP_KEY_OEM_102, APP_KEY_PROCESSKEY, + APP_KEY_ATTN, APP_KEY_CRSEL, APP_KEY_EXSEL, APP_KEY_EREOF, APP_KEY_PLAY, APP_KEY_ZOOM, APP_KEY_NONAME, APP_KEY_PA1, + APP_KEY_OEM_CLEAR, APP_KEYCOUNT } app_key_t; + +typedef enum app_input_type_t { APP_INPUT_KEY_DOWN, APP_INPUT_KEY_UP, APP_INPUT_DOUBLE_CLICK, APP_INPUT_CHAR, + APP_INPUT_MOUSE_MOVE, APP_INPUT_MOUSE_DELTA, APP_INPUT_SCROLL_WHEEL, APP_INPUT_TABLET } app_input_type_t; + +typedef enum app_pressed_t { APP_NOT_PRESSED, APP_PRESSED, } app_pressed_t; + +typedef struct app_input_event_t + { + app_input_type_t type; + union data_t + { + app_key_t key; + char char_code; + struct { int x; int y; } mouse_pos; + struct { float x; float y; } mouse_delta; + float wheel_delta; + struct { int x; int y; float pressure; app_pressed_t tip; app_pressed_t lower; app_pressed_t upper; } tablet; + } data; + } app_input_event_t; + +typedef struct app_input_t { app_input_event_t* events; int count; } app_input_t; +app_input_t app_input( app_t* app ); + +void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ); +void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ); + +#endif /* app_h */ + + +/** + +app.h +===== + +Small cross-platform base framework for graphical apps. + + +Example +------- + +Here's a basic sample program which starts a windowed app and plots random pixels. + + #define APP_IMPLEMENTATION + #define APP_WINDOWS + #include "app.h" + + #include // for rand and __argc/__argv + #include // for memset + + int app_proc( app_t* app, void* user_data ) { + APP_U32 canvas[ 320 * 200 ]; // a place for us to draw stuff + memset( canvas, 0xC0, sizeof( canvas ) ); // clear to grey + app_screenmode( app, APP_SCREENMODE_WINDOW ); + + // keep running until the user close the window + while( app_yield( app ) != APP_STATE_EXIT_REQUESTED ) { + // plot a random pixel on the canvas + int x = rand() % 320; + int y = rand() % 200; + APP_U32 color = rand() | ( (APP_U32) rand() << 16 ); + canvas[ x + y * 320 ] = color; + + // display the canvas + app_present( app, canvas, 320, 200, 0xffffff, 0x000000 ); + } + return 0; + } + + int main( int argc, char** argv ) { + (void) argc, argv; + return app_run( app_proc, NULL, NULL, NULL, NULL ); + } + + // pass-through so the program will build with either /SUBSYSTEM:WINDOWS or /SUBSYSTEN:CONSOLE + extern "C" int __stdcall WinMain( struct HINSTANCE__*, struct HINSTANCE__*, char*, int ) { return main( __argc, __argv ); } + + + +API Documentation +----------------- + +app.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use it, +you just include app.h to get the API declarations. To get the definitions, you must include app.h from *one* single +C or C++ file, and #define the symbol `APP_IMPLEMENTATION` before you do. + +As app.h is a cross platform library, you must also define which platform you are running on, like this for Windows: + + #define APP_IMPLEMENTATION + #define APP_WINDOWS + #include "app.h" + +Or like this for other platforms: + #define APP_IMPLEMENTATION + #define APP_SDL + #include "app.h" + +### Customization + +There are a few different things in app.h which are configurable by #defines. Most of the API use the `int` data type, +for integer values where the exact size is not important. However, for some functions, it specifically makes use of 16, +32 and 64 bit data types. These default to using `short`, `unsigned int` and `unsigned long long` by default, but can be +redefined by #defining APP_S16, APP_U32 and APP_U64 respectively, before including app.h. This is useful if you, for +example, use the types from `` in the rest of your program, and you want app.h to use compatible types. In +this case, you would include app.h using the following code: + + #define APP_S16 int16_t + #define APP_U32 uint32_t + #define APP_U64 uint64_t + #include "app.h" + +Note that when customizing the data types, you need to use the same definition in every place where you include app.h, +as they affect the declarations as well as the definitions. + +The rest of the customizations only affect the implementation, so will only need to be defined in the file where you +have the #define APP_IMPLEMENTATION. + + +#### Custom memory allocators + +Even though app.h attempts to minimize the memory use and number of allocations, it still needs to make *some* use of +dynamic allocation by calling `malloc`. Programs might want to keep track of allocations done, or use custom defined +pools to allocate memory from. app.h allows for specifying custom memory allocation functions for `malloc` and `free`. +This is done with the following code: + + #define APP_IMPLEMENTATION + #define APP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) + #define APP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) + #include "app.h" + +where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter +is an optional parameter of type `void*`. When `app_run` is called, you can pass in a `memctx` parameter, which can be a +pointer to anything you like, and which will be passed through as the `ctx` parameter to every APP_MALLOC/APP_FREE call. +For example, if you are doing memory tracking, you can pass a pointer to your tracking data as `memctx`, and in your +custom allocation/deallocation function, you can cast the `ctx` param back to the right type, and access the tracking +data. + +If no custom allocator is defined, app.h will default to `malloc` and `free` from the C runtime library. + + +#### Custom logging function + +There's a bunch of things being logged when app.h runs. It will log an informational entry with the date and time for +when the app is started and stopped, it will log warnings when non-essential initialization fails, and it will log +error messages when things go wrong. By default, logging is done by a simple printf to stdout. As some applications may +need a different behavior, such as writing out a log file, it is possible to override the default logging behavior +through defines like this: + + #define APP_IMPLEMENTATION + #define APP_LOG( ctx, level, message ) ( my_log_func( ctx, level, message ) ) + #include "app.h" + +where `my_log_func` is your own logging function. Just like for the memory allocators, the `ctx` parameter is optional, +and is just a `void*` value which is passed through. But in the case of logging, it will be passed through as the value +off the `logctx` parameter passed into `app_run`. The `level` parameter specifies the severity level of the logging, +and can be used to direct different types of messages to different logging systems, or filter out messages of certain +severity level, e.g. supressing informational messages. + + +#### Custom fatal error function + +As the app.h library works on the lowest level of your program, interfacing directly with the operating system, there +might occur errors which it can not recover from. In these cases, a *fatal error* will be reported. By default, when a +fatal error happens, app.h will print a message to stdout, show a messagebox to the user, and force exit the program. + +It is possible to change this behaviour using the following define: + + #define APP_IMPLEMENTATION + #define APP_FATAL_ERROR( ctx, message ) ( my_custom_fatal_error_func( ctx, message ) ) + #include "app.h" + +where `my_custom_fatal_error_func` is your own error reporting function. The `ctx` parameter fills the same purpose as +for the allocator and logging functions, but here it is the `fatalctx` parameter to `app_run` which is passed through. + + +app_run +------- + + int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ) + +Creates a new app instance, calls the given app_proc and waits for it to return. Then it destroys the app instance. + +* app_proc - function pointer to the user defined starting point of the app. The parameters to that function are: + app_t* a pointer to the app instance. This is an opaque type, and it is passed to all other functions in the API. + void* pointer to the user defined data that was passed as the `user_data` parameter to `app_run`. +* user_data - pointer to user defined data which will be passed through to app_proc. May be NULL. +* memctx - pointer to user defined data which will be passed through to custom APP_MALLOC/APP_FREE calls. May be NULL. +* logctx - pointer to user defined data to be passed through to custom APP_LOG calls. May be NULL. +* fatalctx - pointer to user defined data to be passed through to custom APP_FATAL_ERROR calls. May be NULL. + +When app_run is called, it will perform all the initialization needed by app.h, and create an `app_t*` instance. It will +then call the user-specified `app_proc`, and wait for it to return. The `app_t*` instance will be passed to `app_proc`, +and can be used to call other functions in the API. When returning from `app_proc`, a return value is specified, and +`app_run` will perform termination and cleanup, and destroy the `app_t*` instance, and then return the same value it got +from the `app_proc`. After `app_run` returns, the `app_t*` value is no longer valid for use in any API calls. + + +app_yield +--------- + + app_state_t app_yield( app_t* app ) + +Allows for app.h and the operating system to perform internal house keeping and updates. It should be called on each +iteration of your main loop. + +The return value can be either `APP_STATE_NORMAL` or `APP_STATE_EXIT_REQUESTED`. `APP_STATE_EXIT_REQUESTED` means that +the user have requested the app to terminate, e.g. by pressing the *close* button on the window, and the user defined +`app_proc` needs to handle this, by either returning (to signal that the app should terminate) or by calling +`app_cancel_exit` to ignore the request. A typical pattern is to display a message to the user to confirm that the app +should exit. In the case of `APP_STATE_NORMAL`, there is no need to do anything. + + +app_cancel_exit +--------------- + + void app_cancel_exit( app_t* app ) + +Used to reset the `APP_STATE_EXIT_REQUESTED` state. See `app_yield` for details. + + +app_title +--------- + + void app_title( app_t* app, char const* title ) + +Sets the name of the application, which is displayed in the task switcher and in the title bar of the window. + + +app_cmdline +----------- + + char const* app_cmdline( app_t* app ) + +Returns the command line string used to launch the executable. This can be parsed to get command line arguments. + + +app_filename +------------ + + char const* app_filename( app_t* app ) + +Returns the full filename and path of the executable. The first part of `app_cmdline` usually contains the name of the +executable, but not necessarily the full path, depending on how it was launched. `app_filename`, however, always returns +the full path. + + +app_userdata +------------ + + char const* app_userdata( app_t* app ) + +Returns the full path to a directory where a users personal files can be stored. Depending on the access rights of the +user, it may or may not be possible to write data to the same location as the executable, and instead it must be stored +in a specific area designated by the operating system. `app_userdata` returns the path to the root if that directory. +A typical use for this is to store the users savegame files, by creating a subfolder corresponding to your app, and save +the data there. + + +app_appdata +----------- + + char const* app_appdata( app_t* app ) + +Returns the full path to a directory where application specific files can be stored. Similar to the location returned by +`app_userdata`, but suitable for application data shared between users. Typical use for this is to store the result of +cached calculations or temporary files. + + +app_time_count +-------------- + + APP_U64 app_time_count( app_t* app ) + +Returns the current value of the high precision clock. The epoch is undefined, and the resolution can vary between +systems. Use `app_time_freq` to convert to seconds. Typical use is to make two calls to `app_time_count` and calculate +the difference, to measure the time elapsed between the two calls. + + +app_time_freq +------------- + + APP_U64 app_time_freq( app_t* app ) + +Returns the number of clock ticks per second of the high precision clock. An example use case could be: + + APP_U64 current_count = app_time_count( app ); + APP_U64 delta_count = current_count - previous_count; + double delta_time = ( (double) delta_count ) / ( (double) app_time_freq( app ) ); + previous_count = current_count; + +to measure the time between two iterations through your main loop. + + +app_log +------- + + void app_log( app_t* app, app_log_level_t level, char const* message ) + +app.h will do logging on certain events, e.q when the app starts and ends or when something goes wrong. As the logging +can be customized (see section on customization), it might be desirable for the program to do its own logging the same +way as app.h does it. By calling `app_log`, logging will be done the same way as it is done inside app.h, whether custom +logging or default logging is being used. + + +app_fatal_error +--------------- + + void app_fatal_error( app_t* app, char const* message ) + +Same as with app_log, but for reporting fatal errors, `app_fatal_error` will report an error the same way as is done +internally in app.h, whether custom or default fatal error reporting is being used. + + +app_pointer +----------- + + void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) + +Sets the appearence current mouse pointer. `app_pointer` is called with the following parameters: + +* width, height - the horizontal and vertical dimensions of the mouse pointer bitmap. +* pixels_abgr - width x height number of pixels making up the pointer bitmap, each pixel being a 32-bit unsigned integer + where the highest 8 bits are the alpha channel, and the following 8-bit groups are blue, green and red channels. +* hotspot_x, hotspot_y - offset into the bitmap of the pointer origin, the center point it will be drawn at. + + +app_pointer_default +------------------- + + void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ) + +Retrieves the width, height, pixel data and hotspot for the default mouse pointer. Useful for restoring the default +pointer after using `app_pointer`, or for doing software rendered pointers. Called with the following parameters: + +* width, height - pointers to integer values that are to receive the width and height of the pointer. May be NULL. +* pixels_abgr - width x height number of pixels to receive the pointer bitmap. May be NULL +* hotspot_x, hotspot_y - pointers to integer values that are to receive the hotspot coordinates. May be NULL. + +A typical pattern for calling `app_pointer_default` is to first call it with `pixels_abgr` as NULL, to query the bitmaps +dimensions, and then call it again after preparing a large enough memory area. + + +app_pointer_pos +--------------- + + void app_pointer_pos( app_t* app, int x, int y ) + +Set the position of the mouse pointer, in window coordinates. The function `app_coordinates_bitmap_to_window` can be +used to convert between the coordinate system of the currently displayed bitmap and that of the window. + + +app_pointer_limit +----------------- + + void app_pointer_limit( app_t* app, int x, int y, int width, int height ) + +Locks the mouse pointer movements to stay within the specified area, in window coordinates. The function +`app_coordinates_bitmap_to_window` can be used to convert between the coordinate system of the currently displayed +bitmap and that of the window. + + +app_pointer_limit_off +--------------------- + + void app_pointer_limit_off( app_t* app ) + +Turns of the mouse pointer movement restriction, allowing the pointer to be moved freely again. + + +app_interpolation +----------------- + + void app_interpolation( app_t* app, app_interpolation_t interpolation ) + +app.h supports two different modes of displaying a bitmap. When using `APP_INTERPOLATION_LINEAR`, the bitmap will be +drawn with bilinear interpolations, stretching it to fill the window (maintaining aspect ratio), giving it a smooth, if +somwhat blurry, look. With `APP_INTERPOLATION_NONE`, scaling will only be done on whole pixel ratios, using no +interpolation, which is particularly suitable to maintain the clean, precise look of pixel art. +`APP_INTERPOLATION_LINEAR` is the default setting. + + +app_screenmode +-------------- + + void app_screenmode( app_t* app, app_screenmode_t screenmode ) + +Switch between windowed mode and fullscreen mode. `APP_SCREENMODE_WINDOW` is used to select windowed mode, and +`APP_SCREENMODE_FULLSCREEN` is used to switch to fullscreen mode. `APP_SCREENMODE_FULLSCREEN` is the default. Note that +the app.h fullscreenmode is of the "borderless windowed" type, meaning that fullscreen mode just means that the window +is set to cover the entire screen, and its borders are hidden. It does not imply any exclusive locking of GPU resources. +When switching from windowed to fullscreen mode on a multi-display system, the app will go fullscreen on the display +that the window is currently on. + + +app_window_size +--------------- + + void app_window_size( app_t* app, int width, int height ) + +Sets the size of the window. If currently in `APP_SCREENMODE_FULLSCREEN` screen mode, the setting will not take effect +until switching to `APP_SCREENMODE_WINDOW`. `width` and `height` specifies the size of the windows client area, not +counting borders, title bar or decorations. + + +app_window_width/app_window_height +---------------------------------- + + int app_window_width( app_t* app ) + int app_window_height( app_t* app ) + +Returns the current dimensions of the window (which might have been resized by the user). Regardless of whether the app +is currently in fullscreen or windowed mode, `app_window_width` and `app_window_height` returns the dimension the window +*would* have in windowed mode. Width and height specifies the size of the windows client area, not counting borders, +title bar or decorations. + + +app_window_pos +-------------- + + void app_window_pos( app_t* app, int x, int y ) + +Sets the position of the top left corner of the window. If currently in `APP_SCREENMODE_FULLSCREEN` screen mode, the +setting will not take effect until switching to `APP_SCREENMODE_WINDOW`. + + +app_window_x/app_window_y +------------------------- + + int app_window_x( app_t* app ) + int app_window_y( app_t* app ) + +Returns the current position of the windows top left corner. Regardless of whether the app is currently in fullscreen or +windowed mode, `app_window_x` and `app_window_y` returns the position the window *would* have in windowed mode. + + +app_displays +------------ + + app_displays_t app_displays( app_t* app ) + +Returns a list of all displays connected to the system. For each display, the following fields are reported: +* id - a platform specific string used to identify the display. Useful for saving which display was in use. +* x, y - position of the top left corner of the display, relative to the primary dispay which is always at 0,0. +* width, height - size of the display, in pixels. + + +app_present +----------- + + void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) + +app.h provides a very minimal API for drawing - the only thing you can really do, is provide it with a bitmap for it to +display on the screen. It is then up to the rest of your program to implement code for drawing shapes or sprites onto +that bitmap. When all your drawing is done, you call `app_present` passing it the bitmap, and it will be displayed on +the screen. `app_present` takes the following parameters: +* pixels_xbgr - width x height number of pixels making up the bitmap to be presented, each pixel being a 32-bit unsigned + integer where the highest 8 bits are not used, and the following 8-bit groups are blue, green and red channels. This + parameter may be NULL, in which case no bitmap is drawn, to allow for custom rendering. See below for details. +* width, height - the horizontal and vertical dimensions of the bitmap +* mod_xbgr - an rgb color value which will be automatically multiplied with each pixel, component by component, before + it is displayed. Can be used to for example fade the bitmap to/from black. Set to 0xffffff for no effect. +* border_xbgr - an rgb color value to be used as *border color*. The borders are the areas outside of the bitmap, which + are visible when the window aspect ratio does not match that of the bitmap, so you get bars above or below it. + +Since app.h uses opengl, you can also opt to not pass a bitmap to `app_present`, by passing NULL as the `pixels_xbgr` +parameter (in which case the rest of the parameters are ignored). When doing this, it is up to your program to perform +drawing using opengl calls. In this case `app_present` will work as a call to SwapBuffers only. Note that glSetViewPort +will be automatically called whenever the window is resized. + + +app_sound_buffer_size +--------------------- + + void app_sound_buffer_size( app_t* app, int sample_pairs_count ) + +The api for playing sound samples is just as minimal as that for drawing. app.h provides a single, looping sound stream, +and it is up to your program to handle sound formats, voices and mixing. By calling `app_sound_buffer_size`, a sound +stream of the specified size is initialized, and playback is started. If a `sample_pairs_count` of 0 is given, sound +playback will be stopped. + +The sound buffer is in 44100hz, signed 16-bit stereo format, and the `sample_pairs_count` specified how many left/right +pairs of 16-bit samples the buffer will contain. As an example, to specify a 1 second stream buffer, you would give the +value 44100 for the `sample_pairs_count` parameter, which would internally create a sound buffer of 176,400 bytes, from +44100 samples x 2 channels x 2 bytes per sample. However, since the bits per sample and number of channels are fixed, +the exact byte size of the sound buffer is not important in the app.h API. + + +app_sound_position +------------------ + + int app_sound_position( app_t* app ) + +Returns the current playback position of the sound stream, given in the number of sample pairs from the start of the +buffer. Typical use of a streaming sound buffer is to fill the buffer with data, wait for the playback position to get +passed the mid point of the buffer, and then write the next bit of data over the part that has already been played, and +so on. + + +app_sound_write +--------------- + + void app_sound_write( app_t* app, int sample_pairs_offset, int sample_pairs_count, APP_S16 const* sample_pairs ) + +Writes sample data to the sound buffer. It takes the following parameters: + +* sample_pairs_offset - the offset into the buffer of where to start writing, in number of sample pairs from the start. +* sample_pairs_count - the number of sample pairs to write. Must be less than the buffer size. +* sample_pairs - an array of sound samples, as signed 16-bit integers. Must be at least `sample_pairs_count` in length. + +The `sample_pairs` parameter can be NULL, in which case the corresponding part of the buffer is cleared. + + +app_sound_volume +---------------- + + void app_sound_volume( app_t* app, float volume ) + +Sets the output volume level of the sound stream, as a normalized linear value in the range 0.0f to 1.0f, inclusive. + + +app_input +--------- + + app_input_t app_input( app_t* app ) + +Returns a list of input events which occured since the last call to `app_input`. Each input event can be of one of a +list of types, and the `type` field of the `app_input_event_t` struct specifies which type the event is. The `data` +struct is a union of fields, where only one of them is valid, depending on the value of `type`: +* APP_INPUT_KEY_DOWN, APP_INPUT_KEY_UP, APP_INPUT_DOUBLE_CLICK - use the `key` field of the `data` union, which contains + one of the keyboard key identifiers from the `app_key_t` enumeration. `APP_INPUT_KEY_DOWN` means a key was pressed, + or that it was held long enough for the key repeat to kick in. `APP_INPUT_KEY_UP` means a key was released, and is + not sent on key repeats. For both these events, a `key` may also mean a mouse button, as those are listed in the + `app_key_t` enum. `APP_INPUT_DOUBLE_CLICK` means a mouse button have been double clicked, and is not sent for + keyboard keys. +* APP_INPUT_CHAR - use the `char_code` field of the `data` union, which contains the ASCII value of the key that was + pressed. This is used to read text input, and will handle things like upper/lower case, and characters which + requires multiple keys to be pressed in sequence to generate one input. This means that generally, when a key is + pressed, you will get both an `APP_INPUT_KEY_DOWN` event and an `APP_INPUT_CHAR` event - just use the one you are + interested in, and ignore the other. +* APP_INPUT_MOUSE_MOVE - use the `mouse_pos` field of the `data` union, which contains the x and y position of the + mouse pointer, in window coordinates. The function `app_coordinates_window_to_bitmap` can be used to convert between + the coordinate system of the window and that of the currently displayed bitmap. The `APP_INPUT_MOUSE_MOVE` event is + sent whenever the user moves the mouse, as long as the window has focus and the pointer is inside its client area. +* APP_INPUT_MOUSE_DELTA - use the `mouse_delta` field of the `data` union, which contains the horizontal and vertical + offset which the mouse has been moved by. Ideally, these values should be in normalized -1.0f to 1.0f range, but as + there is no standardisation on the hardware and os level for this, it is not possible to do, so instead the value + have been scaled to give roughly normalized -1.0f to 1.0f values on a typical setup. For serious use, sensitivity + settings and/or user calibration is recommended. The `APP_INPUT_MOUSE_DELTA` event is sent whenever the user moves + the mouse, regardless of whether the window has focus or whether the pointer is inside the window or not. The + `APP_INPUT_MOUSE_DELTA` event is a better option for reading relative mouse movements than using the + `APP_INPUT_MOUSE_MOVE` event together with `app_pointer_pos` to re-center the pointer on every update. +* APP_INPUT_SCROLL_WHEEL - use the `wheel_delta` field of the `data` union, which contains the number of clicks by which + the scroll wheel on the mouse was turned, where positive values indicate that the wheel have been rotated away from + the user, and negative values means it has turned towards the user. The `APP_INPUT_SCROLL_WHEEL` is sent every time + the user turns the scroll wheel, as long as the window has focus. +* APP_INPUT_TABLET - use the `tablet` field of the `data` union, which contains details about the pen used with a + graphical tablet, if connected and installed. The `x` and `y` fields are the horizontal and vertical positions of + the pen on the tablet, scaled to the coordinate system of the window. The function `app_coordinates_window_to_bitmap` + can be used to convert between the coordinate system of the window and that of the currently displayed bitmap. The + `pressure` field is the current pressure of the pen against the tablet, in normalized 0.0f to 1.0f range, inclusive, + where 0.0f means no pressure and 1.0f means full pressure. The `tip` field is set to `APP_PRESSED` if the tip of the + pen is touching the tablet at all, and to `APP_NOT_PRESSED` otherwise. The `upper` and `lower` fields indicate the + current state of the buttons on the side of the pen. The "eraser" part of the pen is not currently supported. + + +app_coordinates_window_to_bitmap +-------------------------------- + + void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ) + +Functions in the `app.h` API expects and returns coordinates in the windows coordinate system, where 0, 0 is the top +left corner of the windows client area (the area inside of the window borders, excluding title bar and decorations), and +width, height is the dimension, in pixels, of the client area. It is often desirable to translate a position given in +window coordinates into a position on the bitmap that is being displayed - taking into account whether the window is in +fullscreen or windowed mode, and how the bitmap is stretched or padded depending on which interpolation mode is being +used: `APP_INTERPOLATION_NONE` or `APP_INTERPOLATION_LINEAR`. `app_coordinates_window_to_bitmap` performs this +translation, and is called with the following parameters: +* width, height - dimensions of the bitmap being presented, the same as the ones passed to `app_present`. +* x, y - pointers to integer values containing the coordinate, in the coordinate system of the window, to be translated. + When the function returns, their values will have been updated with the corresponding position in the coordinate + system of the bitmap. + + +app_coordinates_bitmap_to_window +-------------------------------- + + void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ) + +This performs the opposite translation to `app_coordinates_window_to_bitmap` - it converts a position given in the +coordinate system of the bitmap into the coordinate system of the window. See `app_coordinates_window_to_bitmap` for +details. + +*/ + +/* +---------------------- + IMPLEMENTATION +---------------------- +*/ +#ifdef APP_IMPLEMENTATION +#undef APP_IMPLEMENTATION + +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// OPENGL CODE - Shared between platform implementations +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef APP_NULL + +#if defined( APP_WINDOWS ) + + #define _CRT_NONSTDC_NO_DEPRECATE + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #include + #define APP_GLCALLTYPE __stdcall + typedef unsigned int APP_GLuint; + typedef int APP_GLsizei; + typedef unsigned int APP_GLenum; + typedef int APP_GLint; + typedef float APP_GLfloat; + typedef char APP_GLchar; + typedef unsigned char APP_GLboolean; + typedef size_t APP_GLsizeiptr; + typedef unsigned int APP_GLbitfield; + + #define APP_GL_FLOAT 0x1406 + #define APP_GL_FALSE 0 + #define APP_GL_FRAGMENT_SHADER 0x8b30 + #define APP_GL_VERTEX_SHADER 0x8b31 + #define APP_GL_COMPILE_STATUS 0x8b81 + #define APP_GL_LINK_STATUS 0x8b82 + #define APP_GL_INFO_LOG_LENGTH 0x8b84 + #define APP_GL_ARRAY_BUFFER 0x8892 + #define APP_GL_TEXTURE_2D 0x0de1 + #define APP_GL_TEXTURE0 0x84c0 + #define APP_GL_CLAMP 0x2900 + #define APP_GL_TEXTURE_WRAP_S 0x2802 + #define APP_GL_TEXTURE_WRAP_T 0x2803 + #define APP_GL_TEXTURE_MIN_FILTER 0x2801 + #define APP_GL_TEXTURE_MAG_FILTER 0x2800 + #define APP_GL_NEAREST 0x2600 + #define APP_GL_LINEAR 0x2601 + #define APP_GL_STATIC_DRAW 0x88e4 + #define APP_GL_RGBA 0x1908 + #define APP_GL_UNSIGNED_BYTE 0x1401 + #define APP_GL_COLOR_BUFFER_BIT 0x00004000 + #define APP_GL_TRIANGLE_FAN 0x0006 + +#elif defined( APP_SDL ) || defined( APP_WASM ) + + #if defined( APP_WASM ) + #include + #define WA_CORO_IMPLEMENT_NANOSLEEP + #include + #else + #include + #include "SDL_opengl.h" + #endif + #define APP_GLCALLTYPE GLAPIENTRY + typedef GLuint APP_GLuint; + typedef GLsizei APP_GLsizei; + typedef GLenum APP_GLenum; + typedef GLint APP_GLint; + typedef GLfloat APP_GLfloat; + typedef GLchar APP_GLchar; + typedef GLboolean APP_GLboolean; + typedef GLsizeiptr APP_GLsizeiptr; + typedef GLbitfield APP_GLbitfield; + + #define APP_GL_FLOAT GL_FLOAT + #define APP_GL_FALSE GL_FALSE + #define APP_GL_FRAGMENT_SHADER GL_FRAGMENT_SHADER + #define APP_GL_VERTEX_SHADER GL_VERTEX_SHADER + #define APP_GL_COMPILE_STATUS GL_COMPILE_STATUS + #define APP_GL_LINK_STATUS GL_LINK_STATUS + #define APP_GL_INFO_LOG_LENGTH GL_INFO_LOG_LENGTH + #define APP_GL_ARRAY_BUFFER GL_ARRAY_BUFFER + #define APP_GL_TEXTURE_2D GL_TEXTURE_2D + #define APP_GL_TEXTURE0 GL_TEXTURE0 + #if defined( APP_WASM ) + #define APP_GL_CLAMP GL_CLAMP_TO_EDGE + #else + #define APP_GL_CLAMP GL_CLAMP + #endif + #define APP_GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S + #define APP_GL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T + #define APP_GL_TEXTURE_MIN_FILTER GL_TEXTURE_MIN_FILTER + #define APP_GL_TEXTURE_MAG_FILTER GL_TEXTURE_MAG_FILTER + #define APP_GL_NEAREST GL_NEAREST + #define APP_GL_LINEAR GL_LINEAR + #define APP_GL_STATIC_DRAW GL_STATIC_DRAW + #define APP_GL_RGBA GL_RGBA + #define APP_GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE + #define APP_GL_COLOR_BUFFER_BIT GL_COLOR_BUFFER_BIT + #define APP_GL_TRIANGLE_FAN GL_TRIANGLE_FAN + +#else + + #error Undefined platform. Define APP_WINDOWS, APP_SDL, APP_WASM or APP_NULL. + #define APP_GLCALLTYPE + typedef int APP_GLuint; + typedef int APP_GLsizei; + typedef int APP_GLenum; + typedef int APP_GLint; + typedef int APP_GLfloat; + typedef int APP_GLchar; + typedef int APP_GLboolean; + typedef int APP_GLsizeiptr; + typedef int APP_GLbitfield; + +#endif + + +#ifdef APP_REPORT_SHADER_ERRORS + #include +#endif + +struct app_internal_opengl_t + { + + APP_GLuint (APP_GLCALLTYPE* CreateShader) (APP_GLenum type); + void (APP_GLCALLTYPE* ShaderSource) (APP_GLuint shader, APP_GLsizei count, APP_GLchar const* const* string, APP_GLint const* length); + void (APP_GLCALLTYPE* CompileShader) (APP_GLuint shader); + void (APP_GLCALLTYPE* GetShaderiv) (APP_GLuint shader, APP_GLenum pname, APP_GLint *params); + APP_GLuint (APP_GLCALLTYPE* CreateProgram) (void); + void (APP_GLCALLTYPE* AttachShader) (APP_GLuint program, APP_GLuint shader); + void (APP_GLCALLTYPE* BindAttribLocation) (APP_GLuint program, APP_GLuint index, APP_GLchar const* name); + void (APP_GLCALLTYPE* LinkProgram) (APP_GLuint program); + void (APP_GLCALLTYPE* GetProgramiv) (APP_GLuint program, APP_GLenum pname, APP_GLint *params); + void (APP_GLCALLTYPE* GenBuffers) (APP_GLsizei n, APP_GLuint *buffers); + void (APP_GLCALLTYPE* BindBuffer) (APP_GLenum target, APP_GLuint buffer); + void (APP_GLCALLTYPE* EnableVertexAttribArray) (APP_GLuint index); + void (APP_GLCALLTYPE* VertexAttribPointer) (APP_GLuint index, APP_GLint size, APP_GLenum type, APP_GLboolean normalized, APP_GLsizei stride, void const* pointer); + void (APP_GLCALLTYPE* GenTextures) (APP_GLsizei n, APP_GLuint* textures); + void (APP_GLCALLTYPE* Enable) (APP_GLenum cap); + void (APP_GLCALLTYPE* ActiveTexture) (APP_GLenum texture); + void (APP_GLCALLTYPE* BindTexture) (APP_GLenum target, APP_GLuint texture); + void (APP_GLCALLTYPE* TexParameteri) (APP_GLenum target, APP_GLenum pname, APP_GLint param); + void (APP_GLCALLTYPE* DeleteBuffers) (APP_GLsizei n, APP_GLuint const* buffers); + void (APP_GLCALLTYPE* DeleteTextures) (APP_GLsizei n, APP_GLuint const* textures); + void (APP_GLCALLTYPE* BufferData) (APP_GLenum target, APP_GLsizeiptr size, void const *data, APP_GLenum usage); + void (APP_GLCALLTYPE* UseProgram) (APP_GLuint program); + void (APP_GLCALLTYPE* Uniform1i) (APP_GLint location, APP_GLint v0); + void (APP_GLCALLTYPE* Uniform3f) (APP_GLint location, APP_GLfloat v0, APP_GLfloat v1, APP_GLfloat v2); + APP_GLint (APP_GLCALLTYPE* GetUniformLocation) (APP_GLuint program, APP_GLchar const* name); + void (APP_GLCALLTYPE* TexImage2D) (APP_GLenum target, APP_GLint level, APP_GLint internalformat, APP_GLsizei width, APP_GLsizei height, APP_GLint border, APP_GLenum format, APP_GLenum type, void const* pixels); + void (APP_GLCALLTYPE* ClearColor) (APP_GLfloat red, APP_GLfloat green, APP_GLfloat blue, APP_GLfloat alpha); + void (APP_GLCALLTYPE* Clear) (APP_GLbitfield mask); + void (APP_GLCALLTYPE* DrawArrays) (APP_GLenum mode, APP_GLint first, APP_GLsizei count); + void (APP_GLCALLTYPE* Viewport) (APP_GLint x, APP_GLint y, APP_GLsizei width, APP_GLsizei height); + void (APP_GLCALLTYPE* DeleteShader) (APP_GLuint shader); + void (APP_GLCALLTYPE* DeleteProgram) (APP_GLuint program); + #ifdef APP_REPORT_SHADER_ERRORS + void (APP_GLCALLTYPE* GetShaderInfoLog) (APP_GLuint shader, APP_GLsizei bufSize, APP_GLsizei *length, APP_GLchar *infoLog); + #endif + + app_interpolation_t interpolation; + int window_width; + int window_height; + + APP_GLuint vertexbuffer; + APP_GLuint texture; + APP_GLuint shader; + }; + + +static int app_internal_opengl_init( app_t* app, struct app_internal_opengl_t* gl, app_interpolation_t interpolation, + int window_width, int window_height ) + { + (void) app; + gl->interpolation = interpolation; + gl->window_width = window_width; + gl->window_height = window_height; + + char const* vs_source = + #ifdef APP_WASM + "precision highp float;\n" + #else + "#version 120\n" + #endif + "attribute vec4 pos;" + "varying vec2 uv;" + "" + "void main( void )" + " {" + " gl_Position = vec4( pos.xy, 0.0, 1.0 );" + " uv = pos.zw;" + " }" + ; + + char const* fs_source = + #ifdef APP_WASM + "precision highp float;\n" + #else + "#version 120\n" + #endif + "varying vec2 uv;" + "" + "uniform sampler2D texture;" + "uniform vec3 modulate;" + "" + "void main(void)" + " {" + " gl_FragColor = texture2D( texture, uv ) * vec4( modulate, 1.0 );" + " }" + ; + + #ifdef APP_REPORT_SHADER_ERRORS + char error_message[ 1024 ]; + #endif + + APP_GLuint vs = gl->CreateShader( APP_GL_VERTEX_SHADER ); + gl->ShaderSource( vs, 1, (char const**) &vs_source, NULL ); + gl->CompileShader( vs ); + APP_GLint vs_compiled; + gl->GetShaderiv( vs, APP_GL_COMPILE_STATUS, &vs_compiled ); + if( !vs_compiled ) + { + #ifdef APP_REPORT_SHADER_ERRORS + char const* prefix = "Vertex Shader Error: "; + memcpy( error_message, prefix, strlen( prefix ) + 1 ); + int len = 0, written = 0; + gl->GetShaderiv( vs, APP_GL_INFO_LOG_LENGTH, &len ); + gl->GetShaderInfoLog( vs, (APP_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, + error_message + strlen( prefix ) ); + app_fatal_error( app, error_message ); + #endif + return 0; + } + + APP_GLuint fs = gl->CreateShader( APP_GL_FRAGMENT_SHADER ); + gl->ShaderSource( fs, 1, (char const**) &fs_source, NULL ); + gl->CompileShader( fs ); + APP_GLint fs_compiled; + gl->GetShaderiv( fs, APP_GL_COMPILE_STATUS, &fs_compiled ); + if( !fs_compiled ) + { + #ifdef APP_REPORT_SHADER_ERRORS + char const* prefix = "Fragment Shader Error: "; + memcpy( error_message, prefix, strlen( prefix ) + 1 ); + int len = 0, written = 0; + gl->GetShaderiv( vs, APP_GL_INFO_LOG_LENGTH, &len ); + gl->GetShaderInfoLog( fs, (APP_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, + error_message + strlen( prefix ) ); + app_fatal_error( app, error_message ); + #endif + return 0; + } + + + APP_GLuint prg = gl->CreateProgram(); + gl->AttachShader( prg, fs ); + gl->AttachShader( prg, vs ); + gl->BindAttribLocation( prg, 0, "pos" ); + gl->LinkProgram( prg ); + + APP_GLint linked; + gl->GetProgramiv( prg, APP_GL_LINK_STATUS, &linked ); + if( !linked ) + { + #ifdef APP_REPORT_SHADER_ERRORS + char const* prefix = "Shader Link Error: "; + memcpy( error_message, prefix, strlen( prefix ) + 1 ); + int len = 0, written = 0; + gl->GetShaderiv( vs, APP_GL_INFO_LOG_LENGTH, &len ); + gl->GetShaderInfoLog( prg, (APP_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, + error_message + strlen( prefix ) ); + app_fatal_error( app, error_message ); + #endif + return 0; + } + + gl->shader = prg; + gl->DeleteShader( fs ); + gl->DeleteShader( vs ); + + gl->GenBuffers( 1, &gl->vertexbuffer ); + gl->BindBuffer( APP_GL_ARRAY_BUFFER, gl->vertexbuffer ); + gl->EnableVertexAttribArray( 0 ); + gl->VertexAttribPointer( 0, 4, APP_GL_FLOAT, APP_GL_FALSE, 4 * sizeof( APP_GLfloat ), 0 ); + + gl->GenTextures( 1, &gl->texture ); + #ifndef APP_WASM + // This enable call is not necessary when using fragment shaders, avoid logged warnings in WebGL + gl->Enable( APP_GL_TEXTURE_2D ); + #endif + gl->ActiveTexture( APP_GL_TEXTURE0 ); + gl->BindTexture( APP_GL_TEXTURE_2D, gl->texture ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MIN_FILTER, APP_GL_NEAREST ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MAG_FILTER, APP_GL_NEAREST ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_WRAP_S, APP_GL_CLAMP ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_WRAP_T, APP_GL_CLAMP ); + + return 1; + } + + +static int app_internal_opengl_term( struct app_internal_opengl_t* gl ) + { + gl->DeleteProgram( gl->shader ); + gl->DeleteBuffers( 1, &gl->vertexbuffer); + gl->DeleteTextures( 1, &gl->texture ); + return 1; + } + + +static int app_internal_opengl_present( struct app_internal_opengl_t* gl, APP_U32 const* pixels_xbgr, int width, + int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) + { + float x1 = 0.0f, y1 = 0.0f, x2 = (float) gl->window_width, y2 = (float) gl->window_height; + + if( gl->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = gl->window_width / (float) width; + float vscale = gl->window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + + float hborder = ( gl->window_width - pixel_scale * width ) / 2.0f; + float vborder = ( gl->window_height - pixel_scale * height ) / 2.0f; + x1 = hborder; + y1 = vborder; + x2 = x1 + pixel_scale * width; + y2 = y1 + pixel_scale * height; + } + else + { + int hscale = gl->window_width / width; + int vscale = gl->window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + + int hborder = ( gl->window_width - pixel_scale * width ) / 2; + int vborder = ( gl->window_height - pixel_scale * height ) / 2; + x1 = (float) hborder; + y1 = (float) vborder; + x2 = x1 + (float) ( pixel_scale * width ); + y2 = y1 + (float) ( pixel_scale * height ); + } + + x1 = ( x1 / gl->window_width ) * 2.0f - 1.0f; + x2 = ( x2 / gl->window_width ) * 2.0f - 1.0f; + y1 = ( y1 / gl->window_height ) * 2.0f - 1.0f; + y2 = ( y2 / gl->window_height ) * 2.0f - 1.0f; + + APP_GLfloat vertices[ 16 ]; + vertices[ 0 ] = x1; + vertices[ 1 ] = y1; + vertices[ 2 ] = 0.0f; + vertices[ 3 ] = 1.0f; + + vertices[ 4 ] = x2; + vertices[ 5 ] = y1; + vertices[ 6 ] = 1.0f; + vertices[ 7 ] = 1.0f; + + vertices[ 8 ] = x2; + vertices[ 9 ] = y2; + vertices[ 10 ] = 1.0f; + vertices[ 11 ] = 0.0f; + + vertices[ 12 ] = x1; + vertices[ 13 ] = y2; + vertices[ 14 ] = 0.0f; + vertices[ 15 ] = 0.0f; + + gl->BindBuffer( APP_GL_ARRAY_BUFFER, gl->vertexbuffer ); + gl->BufferData( APP_GL_ARRAY_BUFFER, 4 * 4 * sizeof( APP_GLfloat ), vertices, APP_GL_STATIC_DRAW ); + gl->VertexAttribPointer( 0, 4, APP_GL_FLOAT, APP_GL_FALSE, 4 * sizeof( APP_GLfloat ), 0 ); + + float mod_r = ( ( mod_xbgr >> 16 ) & 0xff ) / 255.0f; + float mod_g = ( ( mod_xbgr >> 8 ) & 0xff ) / 255.0f; + float mod_b = ( ( mod_xbgr ) & 0xff ) / 255.0f; + + gl->UseProgram( gl->shader ); + gl->Uniform1i( gl->GetUniformLocation( gl->shader, "texture" ), 0 ); + gl->Uniform3f( gl->GetUniformLocation( gl->shader, "modulate" ), mod_r, mod_g, mod_b ); + + gl->ActiveTexture( APP_GL_TEXTURE0 ); + gl->BindTexture( APP_GL_TEXTURE_2D, gl->texture ); + gl->TexImage2D( APP_GL_TEXTURE_2D, 0, APP_GL_RGBA, width, height, 0, APP_GL_RGBA, APP_GL_UNSIGNED_BYTE, pixels_xbgr ); + + if( gl->interpolation == APP_INTERPOLATION_LINEAR ) + { + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MIN_FILTER, APP_GL_LINEAR ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MAG_FILTER, APP_GL_LINEAR ); + } + else + { + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MIN_FILTER, APP_GL_NEAREST ); + gl->TexParameteri( APP_GL_TEXTURE_2D, APP_GL_TEXTURE_MAG_FILTER, APP_GL_NEAREST ); + } + + float b = ( ( border_xbgr >> 16 ) & 0xff ) / 255.0f; + float g = ( ( border_xbgr >> 8 ) & 0xff ) / 255.0f; + float r = ( ( border_xbgr ) & 0xff ) / 255.0f; + gl->ClearColor( r, g, b, 1.0f ); + gl->Clear( APP_GL_COLOR_BUFFER_BIT ); + gl->DrawArrays( APP_GL_TRIANGLE_FAN, 0, 4 ); + + return 1; + } + + +static void app_internal_opengl_resize( struct app_internal_opengl_t* gl, int width, int height ) + { + gl->Viewport( 0, 0, width, height ); + gl->window_width = width; + gl->window_height = height; + } + + +static void app_internal_opengl_interpolation( struct app_internal_opengl_t* gl, app_interpolation_t interpolation ) + { + gl->interpolation = interpolation; + } + + +#endif // #ifndef APP_NULL + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// NULL +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined( APP_NULL ) + + +struct app_t { void* dummy; }; +int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ) { app_t app; return app_proc( &app, user_data ); } +app_state_t app_yield( app_t* app ) { return APP_STATE_NORMAL; } +void app_cancel_exit( app_t* app ) { } +void app_title( app_t* app, char const* title ) { } +char const* app_cmdline( app_t* app ) { return ""; } +char const* app_filename( app_t* app ) { return ""; } +char const* app_userdata( app_t* app ) { return ""; } +char const* app_appdata( app_t* app ) { return ""; } +APP_U64 app_time_count( app_t* app ) { return 0ULL; } +APP_U64 app_time_freq( app_t* app ) { return 0ULL; } +void app_log( app_t* app, app_log_level_t level, char const* message ) { } +void app_fatal_error( app_t* app, char const* message ) { } +void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) { } +void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ) { } +void app_pointer_pos( app_t* app, int x, int y ) { } +int app_pointer_x( app_t* app ) { return 0; } +int app_pointer_y( app_t* app ) { return 0;} +void app_pointer_limit( app_t* app, int x, int y, int width, int height ) { } +void app_pointer_limit_off( app_t* app ) { } +void app_interpolation( app_t* app, app_interpolation_t interpolation ) { } +void app_screenmode( app_t* app, app_screenmode_t screenmode ) { } +void app_window_size( app_t* app, int width, int height ) { } +int app_window_width( app_t* app ) { return 0; } +int app_window_height( app_t* app ) { return 0; } +void app_window_pos( app_t* app, int x, int y ) { } +int app_window_x( app_t* app ) { return 0; } +int app_window_y( app_t* app ) { return 0; } +app_displays_t app_displays( app_t* app ) { app_displays_t ret = { 0 }; return ret; } +void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) { } +void app_sound( app_t* app, int sample_pairs_count, void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ), void* user_data ) { } +void app_sound_volume( app_t* app, float volume ) { } +app_input_t app_input( app_t* app ) { app_input_t ret = { 0 }; return ret; } +void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ) { } +void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ); + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WINDOWS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#elif defined( APP_WINDOWS ) + +#define _CRT_NONSTDC_NO_DEPRECATE +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0601 +#undef _WIN32_WINNT + #define _WIN32_WINNT 0x0601// requires Windows 7 minimum +#endif +// 0x0400=Windows NT 4.0, 0x0500=Windows 2000, 0x0501=Windows XP, 0x0502=Windows Server 2003, 0x0600=Windows Vista, +// 0x0601=Windows 7, 0x0602=Windows 8, 0x0603=Windows 8.1, 0x0A00=Windows 10, +#define _WINSOCKAPI_ +#pragma warning( push ) +#pragma warning( disable: 4619 ) // #pragma warning: there is no warning number 'nnnn' +#pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored +#pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' +#pragma warning( disable: 4917 ) // 'declarator' : a GUID can only be associated with a class, interface or namespace +#define _NTDDSCM_H_ /* Fixes the error of mismatched pragma warning push/pop in Windows SDK 10.0.17763.0 */ +#include +//#include +#pragma warning( pop ) +#ifndef __TINYC__ +#pragma comment( lib, "user32.lib" ) +#pragma comment( lib, "gdi32.lib" ) +#pragma comment( lib, "winmm.lib" ) +#pragma comment( lib, "shell32.lib" ) +#else +#pragma comment( lib, "user32") +#pragma comment( lib, "gdi32") +#endif + +#include +#include + +#pragma warning( push ) +#pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning( pop ) + +#ifndef APP_MALLOC + #include + #if defined(__cplusplus) + #define APP_MALLOC( ctx, size ) ( ::malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( ::free( ptr ) ) + #else + #define APP_MALLOC( ctx, size ) ( malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( free( ptr ) ) + #endif +#endif + +#ifndef APP_LOG + #if defined(__cplusplus) + #define APP_LOG( ctx, level, message ) ::printf( "%s\n", message ) + #else + #define APP_LOG( ctx, level, message ) printf( "%s\n", message ) + #endif +#endif + +#ifndef APP_FATAL_ERROR + #if defined(__cplusplus) + #define APP_FATAL_ERROR( ctx, message ) { ::printf( "FATAL ERROR: %s\n", message ); \ + ::MessageBoxA( 0, message, "Fatal Error!", MB_OK | MB_ICONSTOP ); ::_flushall(); ::_exit( 0xff ); } + #else + #define APP_FATAL_ERROR( ctx, message ) { printf( "FATAL ERROR: %s\n", message ); \ + MessageBoxA( 0, message, "Fatal Error!", MB_OK | MB_ICONSTOP ); _flushall(); _exit( 0xff ); } + #endif +#endif + + +#ifndef APP_WINDOWED_WS_STYLE + #define APP_WINDOWED_WS_STYLE WS_OVERLAPPEDWINDOW +#endif + +#ifndef APP_WINDOWED_WS_EX_STYLE + #define APP_WINDOWED_WS_EX_STYLE 0 +#endif + + +typedef struct APP_LOGCONTEXTA + { + char lcName[ 40 ]; UINT lcOptions; UINT lcStatus; UINT lcLocks; UINT lcMsgBase; UINT lcDevice; UINT lcPktRate; + DWORD lcPktData; DWORD lcPktMode; DWORD lcMoveMask; DWORD lcBtnDnMask; DWORD lcBtnUpMask; LONG lcInOrgX; + LONG lcInOrgY; LONG lcInOrgZ; LONG lcInExtX; LONG lcInExtY; LONG lcInExtZ; LONG lcOutOrgX; LONG lcOutOrgY; + LONG lcOutOrgZ; LONG lcOutExtX; LONG lcOutExtY; LONG lcOutExtZ; DWORD lcSensX; DWORD lcSensY; DWORD lcSensZ; + BOOL lcSysMode; int lcSysOrgX; int lcSysOrgY; int lcSysExtX; int lcSysExtY; DWORD lcSysSensX; DWORD lcSysSensY; + } APP_LOGCONTEXTA; +typedef struct APP_AXIS { LONG axMin; LONG axMax; UINT axUnits; DWORD axResolution; } APP_AXIS; +typedef struct APP_PACKET { DWORD pkButtons; LONG pkX; LONG pkY; UINT pkNormalPressure; } APP_PACKET; +DECLARE_HANDLE( APP_HMGR ); +DECLARE_HANDLE( APP_HCTX ); +#define APP_WTI_DEVICES 100 +#define APP_WTI_DDCTXS 400 /* 1.1 */ +#define APP_CXO_MESSAGES 0x0004 +#define APP_DVC_NPRESSURE 15 +#define APP_PK_BUTTONS 0x0040 // button information +#define APP_PK_X 0x0080 // x axis +#define APP_PK_Y 0x0100 // y axis +#define APP_PK_NORMAL_PRESSURE 0x0400 // normal or tip pressure +#define APP_PACKETDATA APP_PK_X | APP_PK_Y | APP_PK_BUTTONS | APP_PK_NORMAL_PRESSURE +#define APP_PACKETMODE 0 +#define APP_WT_PACKET 0x7FF0 + + + +////// DSOUND DEFINITIONS //////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +typedef struct _DSOUND_WAVEFORMATEX + { + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; + } DSOUND_WAVEFORMATEX; + +typedef struct _DSBUFFERDESC + { + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + DSOUND_WAVEFORMATEX* lpwfxFormat; + } DSBUFFERDESC; + +typedef struct _DSBPOSITIONNOTIFY + { + DWORD dwOffset; + HANDLE hEventNotify; + } DSBPOSITIONNOTIFY; + + +typedef struct _DSCAPS DSCAPS; +typedef struct _DSBCAPS DSBCAPS; +typedef struct _DSEFFECTDESC DSEFFECTDESC; +struct IDirectSound8; + +typedef struct IDirectSoundBuffer8 { struct IDirectSoundBuffer8Vtbl* lpVtbl; } IDirectSoundBuffer8; +typedef struct IDirectSoundBuffer8Vtbl IDirectSoundBuffer8Vtbl; + +struct IDirectSoundBuffer8Vtbl +{ + // IUnknown methods + HRESULT (STDMETHODCALLTYPE *QueryInterface) (IDirectSoundBuffer8*, REFIID, LPVOID*); + ULONG (STDMETHODCALLTYPE *AddRef) (IDirectSoundBuffer8*); + ULONG (STDMETHODCALLTYPE *Release) (IDirectSoundBuffer8*); + + // IDirectSoundBuffer methods + HRESULT (STDMETHODCALLTYPE *GetCaps) (IDirectSoundBuffer8*, DSBCAPS* pDSBufferCaps); + HRESULT (STDMETHODCALLTYPE *GetCurrentPosition) (IDirectSoundBuffer8*, LPDWORD pdwCurrentPlayCursor, LPDWORD pdwCurrentWriteCursor); + HRESULT (STDMETHODCALLTYPE *GetFormat) (IDirectSoundBuffer8*, DSOUND_WAVEFORMATEX* pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten); + HRESULT (STDMETHODCALLTYPE *GetVolume) (IDirectSoundBuffer8*, LPLONG plVolume); + HRESULT (STDMETHODCALLTYPE *GetPan) (IDirectSoundBuffer8*, LPLONG plPan); + HRESULT (STDMETHODCALLTYPE *GetFrequency) (IDirectSoundBuffer8*, LPDWORD pdwFrequency); + HRESULT (STDMETHODCALLTYPE *GetStatus) (IDirectSoundBuffer8*, LPDWORD pdwStatus); + HRESULT (STDMETHODCALLTYPE *Initialize) (IDirectSoundBuffer8*, struct IDirectSound8* pDirectSound, DSBUFFERDESC* pcDSBufferDesc); + HRESULT (STDMETHODCALLTYPE *Lock) (IDirectSoundBuffer8*, DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1, LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE *Play) (IDirectSoundBuffer8*, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE *SetCurrentPosition) (IDirectSoundBuffer8*, DWORD dwNewPosition); + HRESULT (STDMETHODCALLTYPE *SetFormat) (IDirectSoundBuffer8*, DSOUND_WAVEFORMATEX* pcfxFormat); + HRESULT (STDMETHODCALLTYPE *SetVolume) (IDirectSoundBuffer8*, LONG lVolume); + HRESULT (STDMETHODCALLTYPE *SetPan) (IDirectSoundBuffer8*, LONG lPan); + HRESULT (STDMETHODCALLTYPE *SetFrequency) (IDirectSoundBuffer8*, DWORD dwFrequency); + HRESULT (STDMETHODCALLTYPE *Stop) (IDirectSoundBuffer8*); + HRESULT (STDMETHODCALLTYPE *Unlock) (IDirectSoundBuffer8*, LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2); + HRESULT (STDMETHODCALLTYPE *Restore) (IDirectSoundBuffer8*); + + // IDirectSoundBuffer8 methods + HRESULT (STDMETHODCALLTYPE *SetFX) (IDirectSoundBuffer8*, DWORD dwEffectsCount, DSEFFECTDESC* pDSFXDesc, LPDWORD pdwResultCodes); + HRESULT (STDMETHODCALLTYPE *AcquireResources) (IDirectSoundBuffer8*, DWORD dwFlags, DWORD dwEffectsCount, LPDWORD pdwResultCodes); + HRESULT (STDMETHODCALLTYPE *GetObjectInPath) (IDirectSoundBuffer8*, REFGUID rguidObject, DWORD dwIndex, REFGUID rguidInterface, LPVOID *ppObject); +}; + +#define IDirectSoundBuffer8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundBuffer8_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundBuffer8_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#define IDirectSoundBuffer8_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectSoundBuffer8_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundBuffer8_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c) +#define IDirectSoundBuffer8_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a) +#define IDirectSoundBuffer8_Release(p) (p)->lpVtbl->Release(p) + + +typedef struct IDirectSound8 { struct IDirectSound8Vtbl* lpVtbl; } IDirectSound8; +typedef struct IDirectSound8Vtbl IDirectSound8Vtbl; + +struct IDirectSound8Vtbl +{ + // IUnknown methods + HRESULT (STDMETHODCALLTYPE *QueryInterface)(IDirectSound8*, REFIID, LPVOID*); + ULONG (STDMETHODCALLTYPE *AddRef) (IDirectSound8*); + ULONG (STDMETHODCALLTYPE *Release) (IDirectSound8*); + + // IDirectSound methods + HRESULT (STDMETHODCALLTYPE *CreateSoundBuffer) (IDirectSound8*, DSBUFFERDESC* pcDSBufferDesc, struct IDirectSoundBuffer8** ppDSBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE *GetCaps) (IDirectSound8*, DSCAPS* pDSCaps); + HRESULT (STDMETHODCALLTYPE *DuplicateSoundBuffer) (IDirectSound8*, struct IDirectSoundBuffer8* pDSBufferOriginal, struct IDirectSoundBuffer8* *ppDSBufferDuplicate); + HRESULT (STDMETHODCALLTYPE *SetCooperativeLevel) (IDirectSound8*, HWND hwnd, DWORD dwLevel); + HRESULT (STDMETHODCALLTYPE *Compact) (IDirectSound8*); + HRESULT (STDMETHODCALLTYPE *GetSpeakerConfig) (IDirectSound8*, LPDWORD pdwSpeakerConfig); + HRESULT (STDMETHODCALLTYPE *SetSpeakerConfig) (IDirectSound8*, DWORD dwSpeakerConfig); + HRESULT (STDMETHODCALLTYPE *Initialize) (IDirectSound8*, LPCGUID pcGuidDevice); + + // IDirectSound8 methods + HRESULT (STDMETHODCALLTYPE *VerifyCertification) (IDirectSound8*, LPDWORD pdwCertified); +}; + +#define IDirectSound8_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound8_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c) +#define IDirectSound8_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) + + +typedef struct IDirectSoundNotify { struct IDirectSoundNotifyVtbl* lpVtbl; } IDirectSoundNotify; +typedef struct IDirectSoundNotifyVtbl IDirectSoundNotifyVtbl; + +struct IDirectSoundNotifyVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)(IDirectSoundNotify*, REFIID, LPVOID*); + ULONG (STDMETHODCALLTYPE *AddRef) (IDirectSoundNotify*); + ULONG (STDMETHODCALLTYPE *Release) (IDirectSoundNotify*); + HRESULT (STDMETHODCALLTYPE *SetNotificationPositions) (IDirectSoundNotify*, DWORD dwPositionNotifies, DSBPOSITIONNOTIFY* pcPositionNotifies); +}; + +#define IDirectSoundNotify_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->lpVtbl->SetNotificationPositions(p,a,b) + + +#define DS_OK S_OK +#define DSERR_BUFFERLOST MAKE_HRESULT(1, 0x878, 150) + +#define DSSCL_NORMAL 0x00000001 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define DSBCAPS_GLOBALFOCUS 0x00008000 +#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define DSBPLAY_LOOPING 0x00000001 +#define DSBVOLUME_MIN -10000 + + +#ifdef __cplusplus +}; +#endif // __cplusplus + + +///// END DSOUND DEFINITIONS ////// + + +struct app_t + { + void* memctx; + void* logctx; + void* fatalctx; + app_interpolation_t interpolation; + app_screenmode_t screenmode; + + BOOL initialized; + BOOL closed; + + char exe_path[ 260 ]; + char userdata_path[ 260 ]; + char appdata_path[ 260 ]; + char const* cmdline; + + HINSTANCE hinstance; + HWND hwnd; + LRESULT (CALLBACK *user_wndproc)( app_t*, HWND, UINT, WPARAM, LPARAM ); + + HDC hdc; + HICON icon; + BOOL has_focus; + BOOL is_minimized; + + struct app_internal_opengl_t gl; + HMODULE gl_dll; + HGLRC gl_context; + PROC (APP_GLCALLTYPE* wglGetProcAddress) (LPCSTR); + HGLRC (APP_GLCALLTYPE* wglCreateContext) (HDC); + BOOL (APP_GLCALLTYPE* wglDeleteContext) (HGLRC); + BOOL (APP_GLCALLTYPE* wglMakeCurrent) (HDC, HGLRC); + BOOL (APP_GLCALLTYPE* wglSwapIntervalEXT) (int); + + UINT (WINAPI *GetRawInputDataPtr)( HRAWINPUT, UINT, LPVOID, PUINT, UINT ); + + HANDLE sound_notifications[ 2 ]; + HMODULE dsound_dll; + struct IDirectSound8* dsound; + struct IDirectSoundBuffer8* dsoundbuf; + HANDLE sound_thread_handle; + volatile LONG exit_sound_thread; + int sample_pairs_count; + int sound_level; + void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ); + void* sound_user_data; + + HCURSOR current_pointer; + + BOOL clip_cursor; + RECT clip_rect; + + app_input_event_t input_events[ 1024 ]; + int input_count; + + int windowed_x; + int windowed_y; + int windowed_h; + int windowed_w; + int fullscreen_width; + int fullscreen_height; + + int display_count; + app_display_t displays[ 16 ]; + HMONITOR displays_hmonitor[ 16 ]; + + struct + { + HMODULE wintab_dll; + APP_HCTX context; + int max_pressure; + + UINT (WINAPI *WTInfo)( UINT, UINT, LPVOID ); + APP_HCTX (WINAPI *WTOpen)( HWND, APP_LOGCONTEXTA*, BOOL ); + BOOL (WINAPI *WTClose)( APP_HCTX ); + BOOL (WINAPI *WTEnable)( APP_HCTX, BOOL ); + BOOL (WINAPI *WTPacket)( APP_HCTX, UINT, LPVOID ); + } tablet; + }; + + +static app_key_t app_internal_vkcode_to_appkey( app_t* app, int vkcode ) + { + int map[ 256 * 2 ] = { APP_KEY_INVALID, 0x00, APP_KEY_LBUTTON, 0x01, APP_KEY_RBUTTON, 0x02, APP_KEY_CANCEL, 0x03, APP_KEY_MBUTTON, 0x04, + APP_KEY_XBUTTON1, 0x05, APP_KEY_XBUTTON2, 0x06, -1, 0x07, APP_KEY_BACK, 0x08, APP_KEY_TAB, 0x09, -1, 0x0A, -1, 0x0B, APP_KEY_CLEAR, 0x0C, + APP_KEY_RETURN, 0x0D, -1, 0x0E, -1, 0x0F, APP_KEY_SHIFT, 0x10, APP_KEY_CONTROL, 0x11, APP_KEY_MENU, 0x12, APP_KEY_PAUSE, 0x13, + APP_KEY_CAPITAL, 0x14, APP_KEY_KANA, 0x15, -1, 0x16, APP_KEY_JUNJA, 0x17, APP_KEY_FINAL, 0x18, APP_KEY_HANJA, 0x19, -1, 0x1A, + APP_KEY_ESCAPE, 0x1B, APP_KEY_CONVERT, 0x1C, APP_KEY_NONCONVERT, 0x1D, APP_KEY_ACCEPT, 0x1E, APP_KEY_MODECHANGE, 0x1F, APP_KEY_SPACE, 0x20, + APP_KEY_PRIOR, 0x21, APP_KEY_NEXT, 0x22, APP_KEY_END, 0x23, APP_KEY_HOME, 0x24, APP_KEY_LEFT, 0x25, APP_KEY_UP, 0x26, APP_KEY_RIGHT, 0x27, + APP_KEY_DOWN, 0x28, APP_KEY_SELECT, 0x29, APP_KEY_PRINT, 0x2A, APP_KEY_EXEC, 0x2B, APP_KEY_SNAPSHOT, 0x2C, APP_KEY_INSERT, 0x2D, + APP_KEY_DELETE, 0x2E, APP_KEY_HELP, 0x2F, APP_KEY_0, 0x30, APP_KEY_1, 0x31, APP_KEY_2, 0x32, APP_KEY_3, 0x33, APP_KEY_4, 0x34, + APP_KEY_5, 0x35, APP_KEY_6, 0x36, APP_KEY_7, 0x37, APP_KEY_8, 0x38, APP_KEY_9, 0x39, -1, 0x3A, -1, 0x3B, -1, 0x3C, -1, 0x3D, -1, 0x3E, + -1, 0x3F, -1, 0x40, APP_KEY_A, 0x41, APP_KEY_B, 0x42, APP_KEY_C, 0x43, APP_KEY_D, 0x44, APP_KEY_E, 0x45, APP_KEY_F, 0x46, APP_KEY_G, 0x47, + APP_KEY_H, 0x48, APP_KEY_I, 0x49, APP_KEY_J, 0x4A, APP_KEY_K, 0x4B, APP_KEY_L, 0x4C, APP_KEY_M, 0x4D, APP_KEY_N, 0x4E, APP_KEY_O, 0x4F, + APP_KEY_P, 0x50, APP_KEY_Q, 0x51, APP_KEY_R, 0x52, APP_KEY_S, 0x53, APP_KEY_T, 0x54, APP_KEY_U, 0x55, APP_KEY_V, 0x56, APP_KEY_W, 0x57, + APP_KEY_X, 0x58, APP_KEY_Y, 0x59, APP_KEY_Z, 0x5A, APP_KEY_LWIN, 0x5B, APP_KEY_RWIN, 0x5C, APP_KEY_APPS, 0x5D, -1, 0x5E, APP_KEY_SLEEP, 0x5F, + APP_KEY_NUMPAD0, 0x60, APP_KEY_NUMPAD1, 0x61, APP_KEY_NUMPAD2, 0x62, APP_KEY_NUMPAD3, 0x63, APP_KEY_NUMPAD4, 0x64, APP_KEY_NUMPAD5, 0x65, + APP_KEY_NUMPAD6, 0x66, APP_KEY_NUMPAD7, 0x67, APP_KEY_NUMPAD8, 0x68, APP_KEY_NUMPAD9, 0x69, APP_KEY_MULTIPLY, 0x6A, APP_KEY_ADD, 0x6B, + APP_KEY_SEPARATOR, 0x6C, APP_KEY_SUBTRACT, 0x6D, APP_KEY_DECIMAL, 0x6E, APP_KEY_DIVIDE, 0x6F, APP_KEY_F1, 0x70, APP_KEY_F2, 0x71, + APP_KEY_F3, 0x72, APP_KEY_F4, 0x73, APP_KEY_F5, 0x74, APP_KEY_F6, 0x75, APP_KEY_F7, 0x76, APP_KEY_F8, 0x77, APP_KEY_F9, 0x78, + APP_KEY_F10, 0x79, APP_KEY_F11, 0x7A, APP_KEY_F12, 0x7B, APP_KEY_F13, 0x7C, APP_KEY_F14, 0x7D, APP_KEY_F15, 0x7E, APP_KEY_F16, 0x7F, + APP_KEY_F17, 0x80, APP_KEY_F18, 0x81, APP_KEY_F19, 0x82, APP_KEY_F20, 0x83, APP_KEY_F21, 0x84, APP_KEY_F22, 0x85, APP_KEY_F23, 0x86, + APP_KEY_F24, 0x87, -1, 0x88, -1, 0x89, -1, 0x8A, -1, 0x8B, -1, 0x8C, -1, 0x8D, -1, 0x8E, -1, 0x8F, APP_KEY_NUMLOCK, 0x90, + APP_KEY_SCROLL, 0x91, -1, 0x92, -1, 0x93, -1, 0x94, -1, 0x95, -1, 0x96, -1, 0x97, -1, 0x98, -1, 0x99, -1, 0x9A, -1, 0x9B, -1, 0x9C, -1, 0x9D, + -1, 0x9E, -1, 0x9F, APP_KEY_LSHIFT, 0xA0, APP_KEY_RSHIFT, 0xA1, APP_KEY_LCONTROL, 0xA2, APP_KEY_RCONTROL, 0xA3, APP_KEY_LMENU, 0xA4, + APP_KEY_RMENU, 0xA5, APP_KEY_BROWSER_BACK, 0xA6, APP_KEY_BROWSER_FORWARD, 0xA7, APP_KEY_BROWSER_REFRESH, 0xA8, APP_KEY_BROWSER_STOP, 0xA9, + APP_KEY_BROWSER_SEARCH, 0xAA, APP_KEY_BROWSER_FAVORITES, 0xAB, APP_KEY_BROWSER_HOME, 0xAC, APP_KEY_VOLUME_MUTE, 0xAD, + APP_KEY_VOLUME_DOWN, 0xAE, APP_KEY_VOLUME_UP, 0xAF, APP_KEY_MEDIA_NEXT_TRACK, 0xB0, APP_KEY_MEDIA_PREV_TRACK, 0xB1, APP_KEY_MEDIA_STOP, 0xB2, + APP_KEY_MEDIA_PLAY_PAUSE, 0xB3, APP_KEY_LAUNCH_MAIL, 0xB4, APP_KEY_LAUNCH_MEDIA_SELECT, 0xB5, APP_KEY_LAUNCH_APP1, 0xB6, + APP_KEY_LAUNCH_APP2, 0xB7, -1, 0xB8, -1, 0xB9, APP_KEY_OEM_1, 0xBA, APP_KEY_OEM_PLUS, 0xBB, APP_KEY_OEM_COMMA, 0xBC, APP_KEY_OEM_MINUS, 0xBD, + APP_KEY_OEM_PERIOD, 0xBE, APP_KEY_OEM_2, 0xBF, APP_KEY_OEM_3, 0xC0, -1, 0xC1, -1, 0xC2, -1, 0xC3, -1, 0xC4, -1, 0xC5, -1, 0xC6, -1, 0xC7, + -1, 0xC8, -1, 0xC9, -1, 0xCA, -1, 0xCB, -1, 0xCC, -1, 0xCD, -1, 0xCE, -1, 0xCF, -1, 0xD0, -1, 0xD1, -1, 0xD2, -1, 0xD3, -1, 0xD4, -1, 0xD5, + -1, 0xD6, -1, 0xD7, -1, 0xD8, -1, 0xD9, -1, 0xDA, APP_KEY_OEM_4, 0xDB, APP_KEY_OEM_5, 0xDC, APP_KEY_OEM_6, 0xDD, APP_KEY_OEM_7, 0xDE, + APP_KEY_OEM_8, 0xDF, -1, 0xE0, -1, 0xE1, APP_KEY_OEM_102, 0xE2, -1, 0xE3, -1, 0xE4, APP_KEY_PROCESSKEY, 0xE5, -1, 0xE6, -1, 0xE7, -1, 0xE8, + -1, 0xE9, -1, 0xEA, -1, 0xEB, -1, 0xEC, -1, 0xED, -1, 0xEE, -1, 0xEF, -1, 0xF0, -1, 0xF1, -1, 0xF2, -1, 0xF3, -1, 0xF4, -1, 0xF5, + APP_KEY_ATTN, 0xF6, APP_KEY_CRSEL, 0xF7, APP_KEY_EXSEL, 0xF8, APP_KEY_EREOF, 0xF9, APP_KEY_PLAY, 0xFA, APP_KEY_ZOOM, 0xFB, + APP_KEY_NONAME, 0xFC, APP_KEY_PA1, 0xFD, APP_KEY_OEM_CLEAR, 0xFE, -1, 0xFF, }; + if( vkcode < 0 || vkcode >= sizeof( map ) / ( 2 * sizeof( *map ) ) ) return APP_KEY_INVALID; + if( map[ vkcode * 2 + 1 ] != vkcode ) + { + app_log( app, APP_LOG_LEVEL_ERROR, "Keymap definition error" ); + return APP_KEY_INVALID; + } + return (app_key_t) map[ vkcode * 2 ]; + } + + +static void app_internal_add_input_event( app_t* app, app_input_event_t* event ) + { + if( app->has_focus ) + { + if( app->input_count < sizeof( app->input_events ) / sizeof( *app->input_events ) ) + app->input_events[ app->input_count++ ] = *event; + } + } + + +static RECT app_internal_rect( int left, int top, int right, int bottom ) + { + RECT r; r.left = left; r.top = top; r.right = right; r.bottom = bottom; return r; + } + + +static BOOL app_internal_tablet_init( app_t* app ) + { + app->tablet.wintab_dll = LoadLibraryA( "Wintab32.dll" ); + if( !app->tablet.wintab_dll ) return FALSE; + + app->tablet.WTInfo = ( UINT (WINAPI*)( UINT, UINT, LPVOID ) ) + (uintptr_t ) GetProcAddress( app->tablet.wintab_dll, "WTInfoA" ); + app->tablet.WTOpen = ( APP_HCTX (WINAPI*)( HWND, APP_LOGCONTEXTA*, BOOL ) ) + (uintptr_t ) GetProcAddress( app->tablet.wintab_dll, "WTOpenA" ); + app->tablet.WTClose = ( BOOL (WINAPI*)( APP_HCTX ) ) + (uintptr_t ) GetProcAddress( app->tablet.wintab_dll, "WTClose" ); + app->tablet.WTEnable = ( BOOL (WINAPI*)( APP_HCTX, BOOL ) ) + (uintptr_t ) GetProcAddress( app->tablet.wintab_dll, "WTEnable" ); + app->tablet.WTPacket = ( BOOL (WINAPI*)( APP_HCTX, UINT, LPVOID ) ) + (uintptr_t ) GetProcAddress( app->tablet.wintab_dll, "WTPacket" ); + + if( !app->tablet.WTInfo( 0 ,0, NULL ) ) return FALSE; // checks if tablet is present + + APP_LOGCONTEXTA log_context; + memset( &log_context, 0, sizeof( log_context ) ); + app->tablet.WTInfo( APP_WTI_DDCTXS, 0, &log_context ); + + APP_AXIS pressure; + memset( &pressure, 0, sizeof( pressure ) ); + app->tablet.WTInfo( APP_WTI_DEVICES, APP_DVC_NPRESSURE, &pressure ); + app->tablet.max_pressure = pressure.axMax; + + log_context.lcPktData = APP_PACKETDATA; + log_context.lcOptions |= APP_CXO_MESSAGES; + log_context.lcPktMode = APP_PACKETMODE; + log_context.lcMoveMask = APP_PACKETDATA; + log_context.lcBtnUpMask = log_context.lcBtnDnMask; + log_context.lcOutOrgX = 0; + log_context.lcOutOrgY = 0; + log_context.lcOutExtX = GetSystemMetrics( SM_CXSCREEN) ; + log_context.lcOutExtY = -GetSystemMetrics( SM_CYSCREEN ); + + app->tablet.context = app->tablet.WTOpen( app->hwnd, &log_context, FALSE ); + if( !app->tablet.context ) return FALSE; + return TRUE; +} + + +static BOOL app_internal_tablet_term( app_t* app ) + { + if( app->tablet.context ) app->tablet.WTClose( app->tablet.context ); + if( app->tablet.wintab_dll ) FreeLibrary( app->tablet.wintab_dll ); + return TRUE; + } + + + +static LRESULT CALLBACK app_internal_wndproc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) + { + app_t* app = (app_t*)(uintptr_t) GetWindowLongPtr( hwnd, GWLP_USERDATA ); + if( !app ) return DefWindowProc( hwnd, message, wparam, lparam); + + app_input_event_t input_event; + + switch( message ) + { + case WM_CHAR: + input_event.type = APP_INPUT_CHAR; input_event.data.char_code = (char) wparam; + app_internal_add_input_event( app, &input_event ); + break; + case WM_LBUTTONDOWN: + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_LBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_LBUTTONUP: + input_event.type = APP_INPUT_KEY_UP; input_event.data.key = APP_KEY_LBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_LBUTTONDBLCLK: + input_event.type = APP_INPUT_DOUBLE_CLICK; input_event.data.key = APP_KEY_LBUTTON; + app_internal_add_input_event( app, &input_event ); + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_LBUTTON; + app_internal_add_input_event(app, &input_event); + break; + case WM_RBUTTONDOWN: + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_RBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_RBUTTONUP: + input_event.type = APP_INPUT_KEY_UP; input_event.data.key = APP_KEY_RBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_RBUTTONDBLCLK: + input_event.type = APP_INPUT_DOUBLE_CLICK; input_event.data.key = APP_KEY_RBUTTON; + app_internal_add_input_event( app, &input_event ); + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_RBUTTON; + app_internal_add_input_event(app, &input_event); + break; + case WM_MBUTTONDOWN: + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_MBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_MBUTTONUP: + input_event.type = APP_INPUT_KEY_UP; input_event.data.key = APP_KEY_MBUTTON; + app_internal_add_input_event( app, &input_event ); + break; + case WM_MBUTTONDBLCLK: + input_event.type = APP_INPUT_DOUBLE_CLICK; input_event.data.key = APP_KEY_MBUTTON; + app_internal_add_input_event( app, &input_event ); + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = APP_KEY_MBUTTON; + app_internal_add_input_event(app, &input_event); + break; + case WM_XBUTTONDOWN: + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = HIWORD( wparam ) == 1 ? APP_KEY_XBUTTON1 :APP_KEY_XBUTTON2; + app_internal_add_input_event( app, &input_event ); + break; + case WM_XBUTTONUP: + input_event.type = APP_INPUT_KEY_UP; input_event.data.key = HIWORD( wparam ) == 1 ? APP_KEY_XBUTTON1 :APP_KEY_XBUTTON2; + app_internal_add_input_event( app, &input_event ); + break; + case WM_XBUTTONDBLCLK: + input_event.type = APP_INPUT_DOUBLE_CLICK; input_event.data.key = HIWORD( wparam ) == 1 ? APP_KEY_XBUTTON1 :APP_KEY_XBUTTON2; + app_internal_add_input_event( app, &input_event ); + input_event.type = APP_INPUT_KEY_DOWN; input_event.data.key = HIWORD( wparam ) == 1 ? APP_KEY_XBUTTON1 :APP_KEY_XBUTTON2; + app_internal_add_input_event(app, &input_event); + break; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + { + input_event.type = APP_INPUT_KEY_DOWN; + WPARAM vkcode = wparam; + UINT scancode = (UINT)( ( lparam & 0x00ff0000 ) >> 16 ); + int extended = ( lparam & 0x01000000 ) != 0; + UINT const maptype = 3; //MAPVK_VSC_TO_VK_EX + switch( vkcode ) + { + case VK_SHIFT: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) MapVirtualKey( scancode, maptype ) ); + app_internal_add_input_event( app, &input_event ); + break; + case VK_CONTROL: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, extended ? VK_RCONTROL : VK_LCONTROL ); + app_internal_add_input_event( app, &input_event ); + break; + case VK_MENU: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, extended ? VK_RMENU : VK_LMENU ); + app_internal_add_input_event( app, &input_event ); + break; + default: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + break; + } + } break; + + case WM_HOTKEY: + { + input_event.type = APP_INPUT_KEY_DOWN; + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + if( app->input_count < sizeof( app->input_events ) / sizeof( *app->input_events ) ) + app->input_events[ app->input_count++ ] = input_event; + input_event.type = APP_INPUT_KEY_UP; + if( app->input_count < sizeof( app->input_events ) / sizeof( *app->input_events ) ) + app->input_events[ app->input_count++ ] = input_event; + } break; + + case WM_SYSKEYUP: + case WM_KEYUP: + { + input_event.type = APP_INPUT_KEY_UP; + WPARAM vkcode = wparam; + UINT scancode = (UINT)( ( lparam & 0x00ff0000 ) >> 16 ); + int extended = ( lparam & 0x01000000 ) != 0; + UINT const maptype = 3; //MAPVK_VSC_TO_VK_EX + switch( vkcode ) + { + case VK_SHIFT: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) MapVirtualKey( scancode, maptype ) ); + app_internal_add_input_event( app, &input_event ); + break; + case VK_CONTROL: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, extended ? VK_RCONTROL : VK_LCONTROL ); + app_internal_add_input_event( app, &input_event ); + break; + case VK_MENU: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + + input_event.data.key = app_internal_vkcode_to_appkey( app, extended ? VK_RMENU : VK_LMENU ); + app_internal_add_input_event( app, &input_event ); + break; + default: + input_event.data.key = app_internal_vkcode_to_appkey( app, (int) wparam ); + app_internal_add_input_event( app, &input_event ); + break; + } + } break; + + /* + case WM_GESTURE: + printf( "Gesture\n" ); + if( app->has_focus ) + { + GESTUREINFO gesture_info = { sizeof( gesture_info ) }; + if( GetGestureInfo( (HGESTUREINFO) lparam, &gesture_info ) ) + { + printf( "Gesture Info\n" ); + static int prev = 0; + if( gesture_info.dwID == GID_PAN ) + { + if( gesture_info.dwFlags & GF_BEGIN ) + { + prev = gesture_info.ptsLocation.y; + } + else + { + int dist = gesture_info.ptsLocation.y - prev; + prev = gesture_info.ptsLocation.y; + float wheel_delta = (float)( dist / 200.0f ); + printf( "Pan/Inertia: %d\n", dist ); + if( app->input_count > 0 && app->input_events[ app->input_count - 1 ].type == APP_INPUT_SCROLL_WHEEL ) + { + app_input_event_t* event = &app->input_events[ app->input_count - 1 ]; + event->data.wheel_delta += wheel_delta; + } + else + { + input_event.type = APP_INPUT_SCROLL_WHEEL; + input_event.data.wheel_delta = wheel_delta; + app_internal_add_input_event( app, &input_event ); + } + } + } + } + } + break; + */ + case WM_MOUSEWHEEL: + if( app->has_focus ) + { + float const microsoft_mouse_wheel_constant = 120.0f; + float wheel_delta = ( (float) GET_WHEEL_DELTA_WPARAM( wparam ) ) / microsoft_mouse_wheel_constant; + if( app->input_count > 0 && app->input_events[ app->input_count - 1 ].type == APP_INPUT_SCROLL_WHEEL ) + { + app_input_event_t* event = &app->input_events[ app->input_count - 1 ]; + event->data.wheel_delta += wheel_delta; + } + else + { + input_event.type = APP_INPUT_SCROLL_WHEEL; + input_event.data.wheel_delta = wheel_delta; + app_internal_add_input_event( app, &input_event ); + } + } + break; + + case WM_MOUSEMOVE: + if( app->has_focus ) + { + POINT p; + GetCursorPos( &p ); + ScreenToClient( app->hwnd, &p ); + int mouse_x = p.x; + int mouse_y = p.y; + + input_event.type = APP_INPUT_MOUSE_MOVE; + input_event.data.mouse_pos.x = mouse_x; + input_event.data.mouse_pos.y = mouse_y; + app_internal_add_input_event( app, &input_event ); + } + break; + + case WM_INPUT: + { + if( app->GetRawInputDataPtr ) + { + RAWINPUT raw; + UINT size = sizeof( raw ); + app->GetRawInputDataPtr( (HRAWINPUT) lparam, RID_INPUT, &raw, &size, sizeof( RAWINPUTHEADER ) ); + if( raw.header.dwType == RIM_TYPEMOUSE ) + { + if( ( raw.data.mouse.usFlags & 1 ) == MOUSE_MOVE_RELATIVE) + { + float dx = (float) raw.data.mouse.lLastX; + float dy = (float) raw.data.mouse.lLastY; + input_event.type = APP_INPUT_MOUSE_DELTA; + input_event.data.mouse_delta.x = dx; + input_event.data.mouse_delta.y = dy; + app_internal_add_input_event( app, &input_event ); + } + } + } + break; + } + + case APP_WT_PACKET: + { + APP_PACKET packet; + memset( &packet, 0, sizeof( packet ) ); + if( (APP_HCTX) lparam == app->tablet.context && + app->tablet.WTPacket( app->tablet.context, (UINT) wparam, &packet ) ) + { + POINT p; + p.x = packet.pkX; + p.y = packet.pkY; + ScreenToClient( app->hwnd, &p ); + int pen_x = p.x; + int pen_y = p.y; + + input_event.type = APP_INPUT_TABLET; + input_event.data.tablet.x = pen_x; + input_event.data.tablet.y = pen_y; + input_event.data.tablet.pressure = (float) packet.pkNormalPressure / (float) app->tablet.max_pressure; + input_event.data.tablet.tip = ( packet.pkButtons & 1 ) ? APP_PRESSED : APP_NOT_PRESSED; + input_event.data.tablet.lower = ( packet.pkButtons & 2 ) ? APP_PRESSED : APP_NOT_PRESSED; + input_event.data.tablet.upper = ( packet.pkButtons & 4 ) ? APP_PRESSED : APP_NOT_PRESSED; + app_internal_add_input_event( app, &input_event ); + } + } break; + + case WM_SETCURSOR: + if( LOWORD( lparam ) == HTCLIENT ) + { + SetCursor( app->current_pointer ); + return 0; + } + break; + + + case WM_WINDOWPOSCHANGED: + { + if( app->screenmode == APP_SCREENMODE_FULLSCREEN ) + { + RECT wr, cr; + GetWindowRect( app->hwnd, &wr ); + GetClientRect( app->hwnd, &cr ); + if( wr.right - wr.left == cr.right - cr.left && wr.bottom - wr.top == cr.bottom - cr.top ) + { + if( cr.right - cr.left != app->fullscreen_width || cr.bottom - cr.top != app->fullscreen_height ) + app_screenmode( app, APP_SCREENMODE_WINDOW ); + } + } + + if( app->clip_cursor ) + { + RECT r = app->clip_rect; + ClientToScreen( app->hwnd, (POINT*)&r ); + ClientToScreen( app->hwnd, ( (POINT*)&r ) + 1 ); + ClipCursor( &r ); + } + + } break; + + case WM_SIZE: + { + if( wparam == SIZE_MAXIMIZED ) + { + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + app->windowed_x = placement.rcNormalPosition.left; + app->windowed_y = placement.rcNormalPosition.top; + app->windowed_w = placement.rcNormalPosition.right - placement.rcNormalPosition.left; + app->windowed_h = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top; + } + + RECT r; + GetClientRect( app->hwnd, &r ); + app_internal_opengl_resize( &app->gl, r.right - r.left, r.bottom - r.top ); + } break; + + + case WM_ACTIVATEAPP: + app->has_focus = (BOOL) wparam; + if( app->has_focus ) + { + app->is_minimized = FALSE; + if( app->clip_cursor ) + { + RECT r = app->clip_rect; + ClientToScreen( app->hwnd, (POINT*)&r ); + ClientToScreen( app->hwnd, ( (POINT*)&r ) + 1 ); + ClipCursor( &r ); + } + } + else + { + ClipCursor( NULL ); + } + + break; + + case WM_SYSCOMMAND: + if( ( wparam & 0xFFF0 ) == SC_MINIMIZE ) app->is_minimized = TRUE; + break; + + case WM_CLOSE: + app->closed = TRUE; + return 0; + break; + + } + + if( app->user_wndproc ) + return app->user_wndproc( app, hwnd, message, wparam, lparam ); + + return DefWindowProc( hwnd, message, wparam, lparam); + } + + +static BOOL CALLBACK app_internal_monitorenumproc( HMONITOR hmonitor, HDC dc, LPRECT rect, LPARAM data ) + { + (void) dc; + app_t* app = (app_t*) data; + + if( app->display_count >= sizeof( app->displays ) / sizeof( *app->displays ) ) return FALSE; + app->displays_hmonitor[ app->display_count ] = hmonitor; + app_display_t* display = &app->displays[ app->display_count++ ]; + + display->x = rect->left; + display->y = rect->top; + display->width = rect->right - rect->left; + display->height = rect->bottom - rect->top; + + #ifdef __cplusplus + MONITORINFOEXA mi; + memset( &mi, 0, sizeof( MONITORINFOEXA ) ); + mi.cbSize = sizeof( MONITORINFOEXA ); + BOOL res = GetMonitorInfoA( hmonitor, &mi ); + if( res && strlen( mi.szDevice ) >= sizeof( display->id ) ) res = FALSE; + strcpy( display->id, res ? mi.szDevice : "" ) ; + #else + MONITORINFOEXA mi; + memset( &mi, 0, sizeof( MONITORINFOEXA ) ); + BOOL res = GetMonitorInfoA( hmonitor, (LPMONITORINFO)&mi ); + if( res && strlen( mi.szDevice ) >= sizeof( display->id ) ) res = FALSE; + strcpy( display->id, res ? mi.szDevice : "" ) ; + #endif + + return TRUE; + } + + +static void app_internal_app_default_cursor( app_t* app ) + { + APP_U32 pointer_pixels[ 256 * 256 ]; + int pointer_width, pointer_height, pointer_hotspot_x, pointer_hotspot_y; + app_pointer_default( app, &pointer_width, &pointer_height, pointer_pixels, &pointer_hotspot_x, &pointer_hotspot_y ); + app_pointer( app, pointer_width, pointer_height, pointer_pixels, pointer_hotspot_x, pointer_hotspot_y ); + } + + +#pragma warning( push ) +#pragma warning( disable: 4533 ) // initialization of 'wc' is skipped by 'goto init_failed' + +int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ) + { + int result = 0xff; + //SetProcessDPIAware(); + + // Init app instance + app_t* app = (app_t*) APP_MALLOC( memctx, sizeof( app_t ) ); + memset( app, 0, sizeof( *app ) ); + app->memctx = memctx; + app->logctx = logctx; + app->fatalctx = fatalctx; + app->interpolation = APP_INTERPOLATION_LINEAR; + app->screenmode = APP_SCREENMODE_FULLSCREEN; + + // Log start message + char msg[ 64 ]; + time_t t = time( NULL ); + struct tm* start = localtime( &t ); + sprintf( msg, "Application started %02d:%02d:%02d %04d-%02d-%02d.", + start->tm_hour, start->tm_min, start->tm_sec, start->tm_year + 1900, start->tm_mon + 1, start->tm_mday ); + app_log( app, APP_LOG_LEVEL_INFO, msg ); + + // Increase timing precision + #ifndef __TINYC__ + TIMECAPS tc; + if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) + timeBeginPeriod( tc.wPeriodMin ); + #endif + + // Get instance handle + app->hinstance = GetModuleHandle( NULL ); + + // Retrieve the path of our executable + GetModuleFileNameA( 0, app->exe_path, sizeof( app->exe_path ) ); + + HMODULE shell32 = LoadLibraryA( "shell32.dll" ); + if( shell32 ) + { + HRESULT (__stdcall *SHGetFolderPathAPtr)(HWND, int, HANDLE, DWORD, LPSTR ) = + (HRESULT (__stdcall*)(HWND, int, HANDLE, DWORD, LPSTR ) ) (uintptr_t) + GetProcAddress( shell32, "SHGetFolderPathA" ); + + if( SHGetFolderPathAPtr ) + { + #define APP_CSIDL_PERSONAL 0x0005 // My Documents + #define APP_CSIDL_COMMON_APPDATA 0x0023 // All Users\Application Data + #define APP_CSIDL_FLAG_CREATE 0x8000 + + // Retrieve user data path + SHGetFolderPathAPtr( NULL, APP_CSIDL_PERSONAL | APP_CSIDL_FLAG_CREATE, NULL, 0, app->userdata_path ); + + // Retrieve app data path + SHGetFolderPathAPtr( NULL, APP_CSIDL_COMMON_APPDATA | APP_CSIDL_FLAG_CREATE, NULL, 0, app->appdata_path ); + + #undef APP_CSIDL_PERSONAL + #undef APP_CSIDL_COMMON_APPDATA + #undef APP_CSIDL_FLAG_CREATE + } + + FreeLibrary( shell32 ); + } + + // Get command line string + app->cmdline = GetCommandLineA(); + + // Load a default Arrow cursor + app_internal_app_default_cursor( app ); + + // Load first icon in the exe and use as app icon + app->icon = LoadIconA( app->hinstance , MAKEINTRESOURCEA( 1 ) ); + + // List all displays + app->display_count = 0; + EnumDisplayMonitors( NULL, NULL, app_internal_monitorenumproc, (LPARAM) app ); + if( app->display_count <= 0 ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to get display info" ); goto init_failed; } + + // Setup the main application window + app->windowed_w = app->displays[ 0 ].width - app->displays[ 0 ].width / 6; + app->windowed_h = app->displays[ 0 ].height - app->displays[ 0 ].height / 6; + app->windowed_x = ( app->displays[ 0 ].width - app->windowed_w ) / 2; + app->windowed_y = ( app->displays[ 0 ].height - app->windowed_h ) / 2; + + app->fullscreen_width = app->displays[ 0 ].width; + app->fullscreen_height = app->displays[ 0 ].height; + + RECT winrect = app_internal_rect( app->windowed_x, app->windowed_y, + app->windowed_x + app->windowed_w, app->windowed_y + app->windowed_h ); + AdjustWindowRect( &winrect, APP_WINDOWED_WS_STYLE | WS_VISIBLE, FALSE ); + + WNDCLASSEX wc = { sizeof( WNDCLASSEX ), CS_DBLCLKS | CS_OWNDC , + (WNDPROC) app_internal_wndproc, 0, 0, 0, 0, 0, 0, 0, TEXT( "app_wc" ), 0 }; + wc.hInstance = app->hinstance; wc.hIcon = app->icon; wc.hCursor = app->current_pointer; + wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH ); wc.hIconSm = app->icon; + RegisterClassEx( &wc ); + app->hwnd = CreateWindowEx( APP_WINDOWED_WS_EX_STYLE, wc.lpszClassName, 0, APP_WINDOWED_WS_STYLE, app->windowed_x, app->windowed_y, + winrect.right - winrect.left, winrect.bottom - winrect.top, (HWND) 0, (HMENU) 0, app->hinstance, 0 ); + if( !app->hwnd ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to create window." ); goto init_failed; } + app->hdc = GetDC( app->hwnd ); + app->has_focus = TRUE; + app->is_minimized = FALSE; + + // Store app pointer with window + #pragma warning( push ) + #pragma warning( disable: 4244 ) // conversion from 'LONG_PTR' to 'LONG', possible loss of data + SetWindowLongPtr( app->hwnd, GWLP_USERDATA, (LONG_PTR) app ); + #pragma warning( pop ) + + + #ifdef APP_ENABLE_MEDIA_KEYS + int const APP_MOD_NOREPEAT = 0x4000; + RegisterHotKey( app->hwnd, VK_VOLUME_MUTE, APP_MOD_NOREPEAT, VK_VOLUME_MUTE ); + RegisterHotKey( app->hwnd, VK_VOLUME_DOWN, APP_MOD_NOREPEAT, VK_VOLUME_DOWN ); + RegisterHotKey( app->hwnd, VK_VOLUME_UP, APP_MOD_NOREPEAT, VK_VOLUME_UP ); + RegisterHotKey( app->hwnd, VK_MEDIA_NEXT_TRACK, APP_MOD_NOREPEAT, VK_MEDIA_NEXT_TRACK ); + RegisterHotKey( app->hwnd, VK_MEDIA_PREV_TRACK, APP_MOD_NOREPEAT, VK_MEDIA_PREV_TRACK ); + RegisterHotKey( app->hwnd, VK_MEDIA_STOP, APP_MOD_NOREPEAT, VK_MEDIA_STOP ); + RegisterHotKey( app->hwnd, VK_MEDIA_PLAY_PAUSE, APP_MOD_NOREPEAT, VK_MEDIA_PLAY_PAUSE ); + #endif + + ShowWindow( app->hwnd, SW_HIDE ); + // Windows specific OpenGL initialization + app->gl_dll = LoadLibraryA( "opengl32.dll" ); + if( !app->gl_dll ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to load opengl32.dll" ); goto init_failed; } + app->wglGetProcAddress = (PROC(APP_GLCALLTYPE*)(LPCSTR)) (uintptr_t) GetProcAddress( app->gl_dll, "wglGetProcAddress" ); + if( !app->wglGetProcAddress ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to find wglGetProcAddress" ); goto init_failed; } + app->wglCreateContext = (HGLRC(APP_GLCALLTYPE*)(HDC)) (uintptr_t) GetProcAddress( app->gl_dll, "wglCreateContext" ); + if( !app->wglCreateContext ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to find wglCreateContext" ); goto init_failed; } + app->wglDeleteContext = (BOOL(APP_GLCALLTYPE*)(HGLRC)) (uintptr_t) GetProcAddress( app->gl_dll, "wglDeleteContext" ); + if( !app->wglDeleteContext ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to find wglDeleteContext" ); goto init_failed; } + app->wglMakeCurrent = (BOOL(APP_GLCALLTYPE*)(HDC, HGLRC)) (uintptr_t) GetProcAddress( app->gl_dll, "wglMakeCurrent" ); + if( !app->wglMakeCurrent ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to find wglMakeCurrent" ); goto init_failed; } + + PIXELFORMATDESCRIPTOR pfd; + memset( &pfd, 0, sizeof( pfd ) ); + pfd.nSize = sizeof( PIXELFORMATDESCRIPTOR ); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.cDepthBits = 24; + pfd.cStencilBits = 8; + pfd.iLayerType = PFD_MAIN_PLANE; + BOOL res = SetPixelFormat( app->hdc, ChoosePixelFormat( app->hdc, &pfd ), &pfd ); + if( !res ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to set pixel format" ); goto init_failed; } + + app->gl_context = app->wglCreateContext( app->hdc ); + if( !app->gl_context ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to create OpenGL context" ); goto init_failed; } + res = app->wglMakeCurrent( app->hdc, app->gl_context ); + if( !res ) { app_log( app, APP_LOG_LEVEL_ERROR, "Failed to activate OpenGl Context" ); goto init_failed; } + + app->wglSwapIntervalEXT = (BOOL (APP_GLCALLTYPE*)(int)) (uintptr_t) app->wglGetProcAddress( "wglSwapIntervalEXT" ); + if( app->wglSwapIntervalEXT ) app->wglSwapIntervalEXT( 1 ); + + // Attempt to bind opengl functions using GetProcAddress + app->gl.CreateShader = ( APP_GLuint (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) GetProcAddress( app->gl_dll, "glCreateShader" ); + app->gl.ShaderSource = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLsizei, APP_GLchar const* const*, APP_GLint const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glShaderSource" ); + app->gl.CompileShader = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glCompileShader" ); + app->gl.GetShaderiv = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLenum, APP_GLint*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGetShaderiv" ); + app->gl.CreateProgram = ( APP_GLuint (APP_GLCALLTYPE*) (void) ) (uintptr_t) GetProcAddress( app->gl_dll, "glCreateProgram" ); + app->gl.AttachShader = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glAttachShader" ); + app->gl.BindAttribLocation = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLuint, APP_GLchar const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glBindAttribLocation" ); + app->gl.LinkProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glLinkProgram" ); + app->gl.GetProgramiv = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLenum, APP_GLint*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGetProgramiv" ); + app->gl.GenBuffers = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGenBuffers" ); + app->gl.BindBuffer = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glBindBuffer" ); + app->gl.EnableVertexAttribArray = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glEnableVertexAttribArray" ); + app->gl.VertexAttribPointer = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLint, APP_GLenum, APP_GLboolean, APP_GLsizei, void const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glVertexAttribPointer" ); + app->gl.GenTextures = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGenTextures" ); + app->gl.Enable = ( void (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) GetProcAddress( app->gl_dll, "glEnable" ); + app->gl.ActiveTexture = ( void (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) GetProcAddress( app->gl_dll, "glActiveTexture" ); + app->gl.BindTexture = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glBindTexture" ); + app->gl.TexParameteri = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLenum, APP_GLint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glTexParameteri" ); + app->gl.DeleteBuffers = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glDeleteBuffers" ); + app->gl.DeleteTextures = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glDeleteTextures" ); + app->gl.BufferData = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLsizeiptr, void const *, APP_GLenum) ) (uintptr_t) GetProcAddress( app->gl_dll, "glBufferData" ); + app->gl.UseProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glUseProgram" ); + app->gl.Uniform1i = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glUniform1i" ); + app->gl.Uniform3f = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLfloat, APP_GLfloat, APP_GLfloat) ) (uintptr_t) GetProcAddress( app->gl_dll, "glUniform3f" ); + app->gl.GetUniformLocation = ( APP_GLint (APP_GLCALLTYPE*) (APP_GLuint, APP_GLchar const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGetUniformLocation" ); + app->gl.TexImage2D = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLint, APP_GLint, APP_GLsizei, APP_GLsizei, APP_GLint, APP_GLenum, APP_GLenum, void const*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glTexImage2D" ); + app->gl.ClearColor = ( void (APP_GLCALLTYPE*) (APP_GLfloat, APP_GLfloat, APP_GLfloat, APP_GLfloat) ) (uintptr_t) GetProcAddress( app->gl_dll, "glClearColor" ); + app->gl.Clear = ( void (APP_GLCALLTYPE*) (APP_GLbitfield) ) (uintptr_t) GetProcAddress( app->gl_dll, "glClear" ); + app->gl.DrawArrays = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLint, APP_GLsizei) ) (uintptr_t) GetProcAddress( app->gl_dll, "glDrawArrays" ); + app->gl.Viewport = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLint, APP_GLsizei, APP_GLsizei) ) (uintptr_t) GetProcAddress( app->gl_dll, "glViewport" ); + app->gl.DeleteShader = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glDeleteShader" ); + app->gl.DeleteProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) GetProcAddress( app->gl_dll, "glDeleteProgram" ); + #ifdef APP_REPORT_SHADER_ERRORS + app->gl.GetShaderInfoLog = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLsizei, APP_GLsizei*, APP_GLchar*) ) (uintptr_t) GetProcAddress( app->gl_dll, "glGetShaderInfoLog" ); + #endif + + // Any opengl functions which didn't bind, try binding them using wglGetProcAddrss + if( !app->gl.CreateShader ) app->gl.CreateShader = ( APP_GLuint (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) app->wglGetProcAddress( "glCreateShader" ); + if( !app->gl.ShaderSource ) app->gl.ShaderSource = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLsizei, APP_GLchar const* const*, APP_GLint const*) ) (uintptr_t) app->wglGetProcAddress( "glShaderSource" ); + if( !app->gl.CompileShader ) app->gl.CompileShader = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glCompileShader" ); + if( !app->gl.GetShaderiv ) app->gl.GetShaderiv = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLenum, APP_GLint*) ) (uintptr_t) app->wglGetProcAddress( "glGetShaderiv" ); + if( !app->gl.CreateProgram ) app->gl.CreateProgram = ( APP_GLuint (APP_GLCALLTYPE*) (void) ) (uintptr_t) app->wglGetProcAddress( "glCreateProgram" ); + if( !app->gl.AttachShader ) app->gl.AttachShader = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glAttachShader" ); + if( !app->gl.BindAttribLocation ) app->gl.BindAttribLocation = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLuint, APP_GLchar const*) ) (uintptr_t) app->wglGetProcAddress( "glBindAttribLocation" ); + if( !app->gl.LinkProgram ) app->gl.LinkProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glLinkProgram" ); + if( !app->gl.GetProgramiv ) app->gl.GetProgramiv = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLenum, APP_GLint*) ) (uintptr_t) app->wglGetProcAddress( "glGetProgramiv" ); + if( !app->gl.GenBuffers ) app->gl.GenBuffers = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint*) ) (uintptr_t) app->wglGetProcAddress( "glGenBuffers" ); + if( !app->gl.BindBuffer ) app->gl.BindBuffer = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glBindBuffer" ); + if( !app->gl.EnableVertexAttribArray ) app->gl.EnableVertexAttribArray = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glEnableVertexAttribArray" ); + if( !app->gl.VertexAttribPointer ) app->gl.VertexAttribPointer = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLint, APP_GLenum, APP_GLboolean, APP_GLsizei, void const*) ) (uintptr_t) app->wglGetProcAddress( "glVertexAttribPointer" ); + if( !app->gl.GenTextures ) app->gl.GenTextures = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint*) ) (uintptr_t) app->wglGetProcAddress( "glGenTextures" ); + if( !app->gl.Enable ) app->gl.Enable = ( void (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) app->wglGetProcAddress( "glEnable" ); + if( !app->gl.ActiveTexture ) app->gl.ActiveTexture = ( void (APP_GLCALLTYPE*) (APP_GLenum) ) (uintptr_t) app->wglGetProcAddress( "glActiveTexture" ); + if( !app->gl.BindTexture ) app->gl.BindTexture = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glBindTexture" ); + if( !app->gl.TexParameteri ) app->gl.TexParameteri = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLenum, APP_GLint) ) (uintptr_t) app->wglGetProcAddress( "glTexParameteri" ); + if( !app->gl.DeleteBuffers ) app->gl.DeleteBuffers = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint const*) ) (uintptr_t) app->wglGetProcAddress( "glDeleteBuffers" ); + if( !app->gl.DeleteTextures ) app->gl.DeleteTextures = ( void (APP_GLCALLTYPE*) (APP_GLsizei, APP_GLuint const*) ) (uintptr_t) app->wglGetProcAddress( "glDeleteTextures" ); + if( !app->gl.BufferData ) app->gl.BufferData = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLsizeiptr, void const *, APP_GLenum) ) (uintptr_t) app->wglGetProcAddress( "glBufferData" ); + if( !app->gl.UseProgram ) app->gl.UseProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glUseProgram" ); + if( !app->gl.Uniform1i ) app->gl.Uniform1i = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLint) ) (uintptr_t) app->wglGetProcAddress( "glUniform1i" ); + if( !app->gl.Uniform3f ) app->gl.Uniform3f = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLfloat, APP_GLfloat, APP_GLfloat) ) (uintptr_t) app->wglGetProcAddress( "glUniform3f" ); + if( !app->gl.GetUniformLocation ) app->gl.GetUniformLocation = ( APP_GLint (APP_GLCALLTYPE*) (APP_GLuint, APP_GLchar const*) ) (uintptr_t) app->wglGetProcAddress( "glGetUniformLocation" ); + if( !app->gl.TexImage2D ) app->gl.TexImage2D = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLint, APP_GLint, APP_GLsizei, APP_GLsizei, APP_GLint, APP_GLenum, APP_GLenum, void const*) ) (uintptr_t) app->wglGetProcAddress( "glTexImage2D" ); + if( !app->gl.ClearColor ) app->gl.ClearColor = ( void (APP_GLCALLTYPE*) (APP_GLfloat, APP_GLfloat, APP_GLfloat, APP_GLfloat) ) (uintptr_t) app->wglGetProcAddress( "glClearColor" ); + if( !app->gl.Clear ) app->gl.Clear = ( void (APP_GLCALLTYPE*) (APP_GLbitfield) ) (uintptr_t) app->wglGetProcAddress( "glClear" ); + if( !app->gl.DrawArrays ) app->gl.DrawArrays = ( void (APP_GLCALLTYPE*) (APP_GLenum, APP_GLint, APP_GLsizei) ) (uintptr_t) app->wglGetProcAddress( "glDrawArrays" ); + if( !app->gl.Viewport ) app->gl.Viewport = ( void (APP_GLCALLTYPE*) (APP_GLint, APP_GLint, APP_GLsizei, APP_GLsizei) ) (uintptr_t) app->wglGetProcAddress( "glViewport" ); + if( !app->gl.DeleteShader ) app->gl.DeleteShader = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glDeleteShader" ); + if( !app->gl.DeleteProgram ) app->gl.DeleteProgram = ( void (APP_GLCALLTYPE*) (APP_GLuint) ) (uintptr_t) app->wglGetProcAddress( "glDeleteProgram" ); + #ifdef APP_REPORT_SHADER_ERRORS + if( !app->gl.GetShaderInfoLog ) app->gl.GetShaderInfoLog = ( void (APP_GLCALLTYPE*) (APP_GLuint, APP_GLsizei, APP_GLsizei*, APP_GLchar*) ) (uintptr_t) app->wglGetProcAddress( "glGetShaderInfoLog" ); + #endif + + // Report error if any gl function was not found. + if( !app->gl.CreateShader ) { app_fatal_error( app, "Could not find function CreateShader." ); goto init_failed; } + if( !app->gl.ShaderSource ) { app_fatal_error( app, "Could not find function ShaderSource." ); goto init_failed; } + if( !app->gl.CompileShader ) { app_fatal_error( app, "Could not find function CompileShader." ); goto init_failed; } + if( !app->gl.GetShaderiv ) { app_fatal_error( app, "Could not find function GetShaderiv." ); goto init_failed; } + if( !app->gl.CreateProgram ) { app_fatal_error( app, "Could not find function CreateProgram." ); goto init_failed; } + if( !app->gl.AttachShader ) { app_fatal_error( app, "Could not find function AttachShader." ); goto init_failed; } + if( !app->gl.BindAttribLocation ) { app_fatal_error( app, "Could not find function BindAttribLocation." ); goto init_failed; } + if( !app->gl.LinkProgram ) { app_fatal_error( app, "Could not find function LinkProgram." ); goto init_failed; } + if( !app->gl.GetProgramiv ) { app_fatal_error( app, "Could not find function GetProgramiv." ); goto init_failed; } + if( !app->gl.GenBuffers ) { app_fatal_error( app, "Could not find function GenBuffers." ); goto init_failed; } + if( !app->gl.BindBuffer ) { app_fatal_error( app, "Could not find function BindBuffer." ); goto init_failed; } + if( !app->gl.EnableVertexAttribArray ) { app_fatal_error( app, "Could not find function EnableVertexAttribArray." ); goto init_failed; } + if( !app->gl.VertexAttribPointer ) { app_fatal_error( app, "Could not find function VertexAttribPointer." ); goto init_failed; } + if( !app->gl.GenTextures ) { app_fatal_error( app, "Could not find function GenTextures." ); goto init_failed; } + if( !app->gl.Enable ) { app_fatal_error( app, "Could not find function Enable." ); goto init_failed; } + if( !app->gl.ActiveTexture ) { app_fatal_error( app, "Could not find function ActiveTexture." ); goto init_failed; } + if( !app->gl.BindTexture ) { app_fatal_error( app, "Could not find function BindTexture." ); goto init_failed; } + if( !app->gl.TexParameteri ) { app_fatal_error( app, "Could not find function TexParameteri." ); goto init_failed; } + if( !app->gl.DeleteBuffers ) { app_fatal_error( app, "Could not find function DeleteBuffers." ); goto init_failed; } + if( !app->gl.DeleteTextures ) { app_fatal_error( app, "Could not find function DeleteTextures." ); goto init_failed; } + if( !app->gl.BufferData ) { app_fatal_error( app, "Could not find function BufferData." ); goto init_failed; } + if( !app->gl.UseProgram ) { app_fatal_error( app, "Could not find function UseProgram." ); goto init_failed; } + if( !app->gl.Uniform1i ) { app_fatal_error( app, "Could not find function Uniform1i." ); goto init_failed; } + if( !app->gl.Uniform3f ) { app_fatal_error( app, "Could not find function Uniform3f." ); goto init_failed; } + if( !app->gl.GetUniformLocation ) { app_fatal_error( app, "Could not find function GetUniformLocation." ); goto init_failed; } + if( !app->gl.TexImage2D ) { app_fatal_error( app, "Could not find function TexImage2D." ); goto init_failed; } + if( !app->gl.ClearColor ) { app_fatal_error( app, "Could not find function ClearColor." ); goto init_failed; } + if( !app->gl.Clear ) { app_fatal_error( app, "Could not find function Clear." ); goto init_failed; } + if( !app->gl.DrawArrays ) { app_fatal_error( app, "Could not find function DrawArrays." ); goto init_failed; } + if( !app->gl.Viewport ) { app_fatal_error( app, "Could not find function Viewport." ); goto init_failed; } + if( !app->gl.DeleteShader ) { app_fatal_error( app, "Could not find function DeleteShader." ); goto init_failed; } + if( !app->gl.DeleteProgram ) { app_fatal_error( app, "Could not find function DeleteProgram." ); goto init_failed; } + #ifdef APP_REPORT_SHADER_ERRORS + if( !app->gl.GetShaderInfoLog ) { app_fatal_error( app, "Could not find function GetShaderInfoLog." ); goto init_failed; } + #endif + + // Platform independent OpenGL initialization + int width = app->screenmode == APP_SCREENMODE_FULLSCREEN ? app->fullscreen_width : app->windowed_w; + int height = app->screenmode == APP_SCREENMODE_FULLSCREEN ? app->fullscreen_height: app->windowed_h; + if( !app_internal_opengl_init( app, &app->gl, app->interpolation, width, height ) ) + { + app_log( app, APP_LOG_LEVEL_ERROR, "Failed to initialize OpenGL" ); + goto init_failed; + } + + app->sound_notifications[ 0 ] = CreateEventA( NULL, FALSE, FALSE, NULL ); + app->sound_notifications[ 1 ] = CreateEventA( NULL, FALSE, FALSE, NULL ); + + + app->dsound_dll = LoadLibraryA( "dsound.dll" ); + if( !app->dsound_dll ) app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't load dsound.dll. Sound disabled." ); + + if( app->dsound_dll ) + { + HRESULT (WINAPI *DirectSoundCreate8Ptr)(LPCGUID,struct IDirectSound8**,void*) = ( HRESULT (WINAPI*)(LPCGUID,struct IDirectSound8**,void*) ) + (uintptr_t) GetProcAddress( (HMODULE) app->dsound_dll, "DirectSoundCreate8" ); + if( !DirectSoundCreate8Ptr ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't find DirectSoundCreate. Sound disabled." ); + FreeLibrary( app->dsound_dll ); + app->dsound_dll = 0; + } + if( DirectSoundCreate8Ptr ) + { + HRESULT hr = DirectSoundCreate8Ptr( NULL, &app->dsound, NULL ); + if( FAILED( hr ) || !app->dsound ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't create DirectSound object. Sound disabled." ); + DirectSoundCreate8Ptr = 0; + FreeLibrary( app->dsound_dll ); + app->dsound_dll = 0; + } + else + { + hr = IDirectSound8_SetCooperativeLevel( app->dsound, app->hwnd, DSSCL_NORMAL); + if( FAILED( hr ) ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't set cooperative level for DirectSound object. Sound disabled." ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + DirectSoundCreate8Ptr = 0; + FreeLibrary( app->dsound_dll ); + app->dsound_dll = 0; + } + } + } + } + app->sound_thread_handle = INVALID_HANDLE_VALUE; + + HMODULE user32 = LoadLibraryA( "user32.dll" ); + if( user32 ) + { + BOOL (WINAPI *RegisterRawInputDevicesPtr)( PCRAWINPUTDEVICE, UINT, UINT ) = + (BOOL (WINAPI*)( PCRAWINPUTDEVICE, UINT, UINT ) )(uintptr_t) GetProcAddress( user32, "RegisterRawInputDevices" ); + + app->GetRawInputDataPtr = (UINT (WINAPI*)( HRAWINPUT, UINT, LPVOID, PUINT, UINT)) + (uintptr_t) GetProcAddress( user32, "GetRawInputData" ); + + USHORT const USAGE_PAGE_GENERIC = ((USHORT) 0x01); + USHORT const USAGE_GENERIC_MOUSE = ((USHORT) 0x02); + + RAWINPUTDEVICE rid[ 1 ]; + rid[ 0 ].usUsagePage = USAGE_PAGE_GENERIC; + rid[ 0 ].usUsage = USAGE_GENERIC_MOUSE; + rid[ 0 ].dwFlags = RIDEV_INPUTSINK; + rid[ 0 ].hwndTarget = app->hwnd; + RegisterRawInputDevicesPtr( rid, 1, sizeof( *rid ) ); + + FreeLibrary( user32 ); + } + + if( !app_internal_tablet_init( app ) ) app_log( app, APP_LOG_LEVEL_WARNING, "WinTab initialization failed - tablet not available" ); + + result = app_proc( app, user_data ); + +init_failed: + if( !app_internal_tablet_term( app ) ) app_log( app, APP_LOG_LEVEL_WARNING, "WinTab termination failed" ); + if( app->sound_thread_handle != INVALID_HANDLE_VALUE ) + { + InterlockedExchange( &app->exit_sound_thread, 1 ); + WaitForSingleObject( app->sound_thread_handle, INFINITE ); + CloseHandle( app->sound_thread_handle ); + } + if( app->dsoundbuf ) IDirectSoundBuffer8_Release( app->dsoundbuf ); + if( app->dsound ) IDirectSound8_Release( app->dsound ); + if( app->dsound_dll ) FreeLibrary( app->dsound_dll ); + if( app->sound_notifications[ 0 ] ) CloseHandle( app->sound_notifications[ 0 ] ); + if( app->sound_notifications[ 1 ] ) CloseHandle( app->sound_notifications[ 1 ] ); + if( !app_internal_opengl_term( &app->gl ) ) app_log( app, APP_LOG_LEVEL_WARNING, "Failed to terminate OpenGL" ); + if( app->gl_context ) app->wglMakeCurrent( 0, 0 ); + if( app->gl_context ) app->wglDeleteContext( app->gl_context ); + if( app->gl_dll ) FreeLibrary( app->gl_dll ); + if( app->icon ) DestroyIcon( app->icon ); + if( app->current_pointer ) DestroyIcon( app->current_pointer ); + if( app->hdc ) ReleaseDC( app->hwnd, app->hdc ); + if( app->hwnd ) DestroyWindow( app->hwnd ); + UnregisterClass( TEXT( "app_wc" ), app->hinstance ); + + #ifndef __TINYC__ + if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) + timeEndPeriod( tc.wPeriodMin ); + #endif + + t = time( NULL ); + struct tm* end = localtime( &t ); + sprintf( msg, "Application terminated %02d:%02d:%02d %04d-%02d-%02d.", + end->tm_hour, end->tm_min, end->tm_sec, end->tm_year + 1900, end->tm_mon + 1, end->tm_mday ); + app_log( app, APP_LOG_LEVEL_INFO, msg ); + + APP_FREE( memctx, app ); + return result; + } + +#pragma warning( pop ) + + +app_state_t app_yield( app_t* app ) + { + if( !app->initialized ) + { + if( app->screenmode == APP_SCREENMODE_WINDOW ) + { + app->screenmode = APP_SCREENMODE_FULLSCREEN; + app_screenmode( app, APP_SCREENMODE_WINDOW ); + } + else + { + app->screenmode = APP_SCREENMODE_WINDOW; + app_screenmode( app, APP_SCREENMODE_FULLSCREEN ); + } + STARTUPINFOA startup_info = { sizeof( STARTUPINFOA ) }; + GetStartupInfoA( &startup_info ); + if( startup_info.dwFlags & STARTF_USESHOWWINDOW ) { + ShowWindow( app->hwnd, startup_info.wShowWindow ); + } else { + ShowWindow( app->hwnd, SW_SHOWDEFAULT ); + } + SetActiveWindow( app->hwnd ); + BringWindowToTop( app->hwnd ); + SwitchToThisWindow( app->hwnd, TRUE ); + if( app->tablet.context ) app->tablet.WTEnable( app->tablet.context, TRUE ); + app->initialized = TRUE; + } + + MSG msg; + while( PeekMessage( &msg, app->hwnd, 0,0, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + SwitchToThread(); // yield to any thread on same processor + + return app->closed == TRUE ? APP_STATE_EXIT_REQUESTED : APP_STATE_NORMAL; + } + + +void app_cancel_exit( app_t* app ) + { + app->closed = FALSE; + } + + +void app_title( app_t* app, char const* title ) + { + #ifdef UNICODE + int len = (int) strlen (title ); + if( len < 256 ) + { + WCHAR unistring[ 256 ]; + MultiByteToWideChar( CP_ACP, 0, title, -1, unistring, len + 1 ); + SetWindowText( app->hwnd, unistring ); + } + #else + SetWindowText( app->hwnd, title ); + #endif + } + + +char const* app_cmdline( app_t* app ) + { + return app->cmdline; + } + + +char const* app_filename( app_t* app ) + { + return app->exe_path; + } + + +char const* app_userdata( app_t* app ) + { + return app->userdata_path; + } + + +char const* app_appdata( app_t* app ) + { + return app->appdata_path; + } + + +APP_U64 app_time_count( app_t* app ) + { + (void) app; + LARGE_INTEGER c; + QueryPerformanceCounter( &c ); + return (APP_U64) c.QuadPart; + } + + +APP_U64 app_time_freq( app_t* app ) + { + (void) app; + LARGE_INTEGER f; + QueryPerformanceFrequency( &f ); + return (APP_U64) f.QuadPart; + } + + +void app_log( app_t* app, app_log_level_t level, char const* message ) + { + (void) app, (void) level, (void) message; + APP_LOG( app->logctx, level, message ); + } + + +void app_fatal_error( app_t* app, char const* message ) + { + (void) app, (void) message; + APP_FATAL_ERROR( app->fatalctx, message ); + } + + +static HCURSOR app_internal_create_cursor( HWND hwnd, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) + { + int size = width > height ? width : height; + BITMAPV5HEADER header; + memset( &header, 0, sizeof( BITMAPV5HEADER ) ); + header.bV5Size = sizeof( BITMAPV5HEADER ); + header.bV5Width = (LONG) size; + header.bV5Height = -(LONG) size; + header.bV5Planes = 1; + header.bV5BitCount = 32; + header.bV5Compression = BI_BITFIELDS; + header.bV5RedMask = 0x00FF0000; + header.bV5GreenMask = 0x0000FF00; + header.bV5BlueMask = 0x000000FF; + header.bV5AlphaMask = 0xFF000000; + + HDC hdc = GetDC( hwnd ); + void* bits = NULL; + HBITMAP bitmap = CreateDIBSection( hdc, (BITMAPINFO*)&header, DIB_RGB_COLORS, (void**) &bits, NULL, (DWORD) 0); + ReleaseDC( NULL, hdc ); + + APP_U32* ptr = (APP_U32*) bits; + for( int y = 0; y < height; ++y ) + { + for( int x = 0; x < width; ++x ) + { + APP_U32 c = pixels_abgr[ x + y * width ]; + APP_U32 a = ( c & 0xff000000 ) >> 24; + APP_U32 b = ( c & 0x00ff0000 ) >> 16; + APP_U32 g = ( c & 0x0000ff00 ) >> 8; + APP_U32 r = ( c & 0x000000ff ); + ptr[ x + y * size ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b; + } + } + + HBITMAP empty_mask = CreateBitmap( size, size, 1, 1, NULL ); + ICONINFO icon_info; + icon_info.fIcon = FALSE; + icon_info.xHotspot = (DWORD) hotspot_x; + icon_info.yHotspot = (DWORD) hotspot_y; + icon_info.hbmMask = empty_mask; + icon_info.hbmColor = bitmap; + + HCURSOR cursor = CreateIconIndirect( &icon_info ); + DeleteObject( bitmap ); + DeleteObject( empty_mask ); + + return cursor; + } + + +void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) + { + if( app->current_pointer ) DestroyIcon( app->current_pointer ); + app->current_pointer = 0; + + if( pixels_abgr ) + app->current_pointer = app_internal_create_cursor( app->hwnd, width, height, + pixels_abgr, hotspot_x, hotspot_y ); + ShowCursor( FALSE ); + SetCursor( app->current_pointer ); + ShowCursor( TRUE ); + } + + +static BOOL app_internal_extract_default_windows_cursor( int* width, int* height, APP_U32* pixels_abgr, + int* hotspot_x, int* hotspot_y ) + { + HCURSOR cursor = LoadCursor( NULL, IDC_ARROW ); + if( !cursor ) return FALSE; + + ICONINFO info; + if( !GetIconInfo( cursor, &info ) ) { DestroyCursor( cursor ); return FALSE; } + BOOL bw_cursor = ( info.hbmColor == NULL ); + + BITMAP bmpinfo; + memset( &bmpinfo, 0, sizeof( bmpinfo ) ); + if( bw_cursor && GetObject( info.hbmMask, sizeof( BITMAP ), &bmpinfo ) == 0 ) + { + DestroyCursor( cursor ); + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + return FALSE; + } + if( !bw_cursor && GetObject( info.hbmColor, sizeof( BITMAP ), &bmpinfo ) == 0 ) + { + DestroyCursor( cursor ); + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + return FALSE; + } + + if( bmpinfo.bmWidth > 256 || bmpinfo.bmHeight > 256 ) + { + DestroyCursor( cursor ); + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + return FALSE; + } + int pointer_width = bmpinfo.bmWidth; + int pointer_height = ( bmpinfo.bmHeight >= 0 ? bmpinfo.bmHeight : -bmpinfo.bmHeight ) / ( bw_cursor ? 2 : 1 ); + + if( width ) *width = pointer_width; + if( height ) *height = pointer_height; + if( hotspot_x ) *hotspot_x = (int) info.xHotspot; + if( hotspot_y ) *hotspot_y = (int) info.yHotspot; + if( !pixels_abgr ) + { + DestroyCursor( cursor ); + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + return TRUE; + } + + BITMAPINFOHEADER bmi; + bmi.biSize = sizeof( BITMAPINFOHEADER ); + bmi.biPlanes = 1; + bmi.biBitCount = 32; + bmi.biWidth = bmpinfo.bmWidth; + bmi.biHeight = -bmpinfo.bmHeight; + bmi.biCompression = BI_RGB; + bmi.biSizeImage = 0; + HDC hdc = GetDC( NULL ); + if( GetDIBits( hdc, bw_cursor ? info.hbmMask : info.hbmColor, 0, (UINT) bmpinfo.bmHeight, pixels_abgr, + (BITMAPINFO*) &bmi, DIB_RGB_COLORS ) != bmpinfo.bmHeight ) + { + DestroyCursor( cursor ); + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + ReleaseDC( NULL, hdc ); + return FALSE; + } + ReleaseDC( NULL, hdc ); + + if( bw_cursor ) + { + for( int y = 0; y < pointer_height; ++y ) + { + for( int x = 0; x < pointer_width; ++x ) + { + APP_U32 c = pixels_abgr[ x + pointer_width * y ]; + APP_U32 m = pixels_abgr[ x + pointer_width * ( pointer_height + y ) ]; + APP_U32 a = 255 - ( c & 0xff ); + APP_U32 g = m & 0xff; + pixels_abgr[ x + pointer_width * y ] = ( a << 24 ) | ( g << 16 ) | ( g << 8 ) | g; + } + } + } + else + { + for( int y = 0; y < pointer_height; ++y ) + { + for( int x = 0; x < pointer_width; ++x ) + { + APP_U32 c = pixels_abgr[ x + pointer_width * y ]; + APP_U32 a = ( c >> 24 ) & 0xff; + APP_U32 r = ( c >> 16 ) & 0xff; + APP_U32 g = ( c >> 8 ) & 0xff; + APP_U32 b = ( c ) & 0xff; + pixels_abgr[ x + pointer_width * y ] = ( a << 24 ) | ( b << 16 ) | ( g << 8 ) | r; + } + } + } + + DeleteObject( info.hbmColor ); + DeleteObject( info.hbmMask ); + DestroyCursor( cursor ); + return TRUE; + } + + +void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ) + { + (void) app; + + APP_U32 default_pointer_data[ 11 * 16 ] = + { + 0xFF000000,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0xFF000000,0xFF000000,0xFF000000,0xFF000000, + 0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFFFFFFFF,0xFF000000,0x00000000,0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0xFF000000,0x00000000,0x00000000,0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000,0x00000000, + 0xFF000000,0x00000000,0x00000000,0x00000000,0x00000000,0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xFF000000,0xFFFFFFFF,0xFFFFFFFF,0xFF000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xFF000000,0xFF000000,0xFF000000,0x00000000,0x00000000, + }; + + if( !app_internal_extract_default_windows_cursor( width, height, pixels_abgr, hotspot_x, hotspot_y ) ) + { + if( width ) *width = 11; + if( height ) *height = 16; + if( hotspot_x ) *hotspot_x = 0; + if( hotspot_y ) *hotspot_y = 0; + if( pixels_abgr ) memcpy( pixels_abgr, default_pointer_data, sizeof( APP_U32 ) * 11 * 16 ); + } + } + + + +void app_pointer_pos( app_t* app, int x, int y ) + { + POINT p; + p.x = x; + p.y = y; + ClientToScreen( app->hwnd, &p ); + SetCursorPos( p.x, p.y ); + } + + +int app_pointer_x( app_t* app ) + { + POINT p; + GetCursorPos( &p ); + ScreenToClient( app->hwnd, &p ); + return (int) p.x; + } + + +int app_pointer_y( app_t* app ) + { + POINT p; + GetCursorPos( &p ); + ScreenToClient( app->hwnd, &p ); + return (int) p.y; + } + + +void app_pointer_limit( app_t* app, int x, int y, int width, int height ) + { + app->clip_cursor = TRUE; + app->clip_rect.left= x; + app->clip_rect.top = y; + app->clip_rect.right = x + width; + app->clip_rect.bottom = y + height; + + RECT r = app->clip_rect; + ClientToScreen( app->hwnd, (POINT*)&r ); + ClientToScreen( app->hwnd, ( (POINT*)&r ) + 1 ); + ClipCursor( &r ); + } + + +void app_pointer_limit_off( app_t* app ) + { + app->clip_cursor = FALSE; + ClipCursor( 0 ); + } + + +void app_interpolation( app_t* app, app_interpolation_t interpolation ) + { + if( interpolation == app->interpolation ) return; + app->interpolation = interpolation; + + POINT p; + GetCursorPos( &p ); + ScreenToClient( app->hwnd, &p ); + int mouse_x = p.x; + int mouse_y = p.y; + + app_input_event_t input_event; + input_event.type = APP_INPUT_MOUSE_MOVE; + input_event.data.mouse_pos.x = mouse_x; + input_event.data.mouse_pos.y = mouse_y; + app_internal_add_input_event( app, &input_event ); + + app_internal_opengl_interpolation( &app->gl, interpolation ); + } + + +void app_screenmode( app_t* app, app_screenmode_t screenmode ) + { + if( screenmode == app->screenmode ) return; + app->screenmode = screenmode; + BOOL visible = IsWindowVisible( app->hwnd ); + if( screenmode == APP_SCREENMODE_WINDOW ) + { + SetWindowLong( app->hwnd, GWL_STYLE, APP_WINDOWED_WS_STYLE | ( visible ? WS_VISIBLE : 0 ) ); + + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + placement.showCmd = (UINT)( visible ? SW_SHOW : SW_HIDE ); + + placement.rcNormalPosition.left = app->windowed_x; + placement.rcNormalPosition.top = app->windowed_y; + placement.rcNormalPosition.right = app->windowed_x + app->windowed_w; + placement.rcNormalPosition.bottom = app->windowed_y + app->windowed_h; + SetWindowPlacement( app->hwnd, &placement ); + } + else + { + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + + if( visible ) + { + if( placement.showCmd != SW_SHOWMAXIMIZED ) + { + app->windowed_x = placement.rcNormalPosition.left; + app->windowed_y = placement.rcNormalPosition.top; + app->windowed_w = placement.rcNormalPosition.right - placement.rcNormalPosition.left; + app->windowed_h = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top; + } + else + { + ShowWindow( app->hwnd, SW_RESTORE ); + } + } + + HMONITOR hmonitor = MonitorFromWindow( app->hwnd, MONITOR_DEFAULTTOPRIMARY ); + int display_index = 0; + for( int i = 0; i < app->display_count; ++i ) + { + if( app->displays_hmonitor[ i ] == hmonitor ) + { + display_index = i; + break; + } + } + + + RECT r = app_internal_rect( app->displays[ display_index ].x, app->displays[ display_index ].y, + app->displays[ display_index ].x + app->displays[ display_index ].width, + app->displays[ display_index ].y + app->displays[ display_index ].height ); + app->fullscreen_width = r.right - r.left; + app->fullscreen_height = r.bottom - r.top; + SetWindowPos( app->hwnd, 0, r.left, r.top, app->fullscreen_width, app->fullscreen_height, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED ); + + SetWindowLong( app->hwnd, GWL_STYLE, ( visible ? WS_VISIBLE : 0 ) ); + SetWindowPos( app->hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED ); + } + } + + +void app_window_size( app_t* app, int width, int height ) + { + RECT r; + r = app_internal_rect( 0, 0, width, height ); + AdjustWindowRect( &r, APP_WINDOWED_WS_STYLE | WS_VISIBLE, FALSE ); + + width = r.right - r.left; + height = r.bottom - r.top; + app->windowed_w = width; + app->windowed_h = height; + + if( app->screenmode == APP_SCREENMODE_WINDOW ) + SetWindowPos( app->hwnd, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED ); + } + + +int app_window_width( app_t* app ) + { + RECT r; + GetClientRect( app->hwnd, &r ); + return r.right - r.left; + } + + +int app_window_height( app_t* app ) + { + RECT r; + GetClientRect( app->hwnd, &r ); + return r.bottom - r.top; + } + + +void app_window_pos( app_t* app, int x, int y ) + { + if( app->screenmode == APP_SCREENMODE_WINDOW ) + { + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + placement.rcNormalPosition.right = x + ( placement.rcNormalPosition.right - placement.rcNormalPosition.left ); + placement.rcNormalPosition.bottom = y + ( placement.rcNormalPosition.bottom - placement.rcNormalPosition.top ); + placement.rcNormalPosition.left = x; + placement.rcNormalPosition.top = y; + SetWindowPlacement( app->hwnd, &placement ); + } + + app->windowed_x = x; + app->windowed_y = y; + } + + +int app_window_x( app_t* app ) + { + if( app->screenmode == APP_SCREENMODE_WINDOW ) + { + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + return placement.rcNormalPosition.left; + } + else + { + return app->windowed_x; + } + } + + +int app_window_y( app_t* app ) + { + if( app->screenmode == APP_SCREENMODE_WINDOW ) + { + WINDOWPLACEMENT placement; + placement.length = sizeof( placement ); + GetWindowPlacement( app->hwnd, &placement ); + return placement.rcNormalPosition.top; + } + else + { + return app->windowed_y; + } + } + + +app_displays_t app_displays( app_t* app ) + { + app_displays_t displays; + displays.count = app->display_count; + displays.displays = app->displays; + return displays; + } + + +void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) + { + if( app->is_minimized ) return; + if( pixels_xbgr ) app_internal_opengl_present( &app->gl, pixels_xbgr, width, height, mod_xbgr, border_xbgr ); + SwapBuffers( app->hdc ); + } + + +static void app_sound_write( app_t* app, int sample_pairs_offset, int sample_pairs_count ) + { + int offset = sample_pairs_offset * 2 * ( 16 / 8 ); + int length = sample_pairs_count * 2 * ( 16 / 8 ); + + // Obtain memory address of write block. This will be in two parts if the block wraps around. + LPVOID lpvPtr1; + DWORD dwBytes1; + LPVOID lpvPtr2; + DWORD dwBytes2; + HRESULT hr = IDirectSoundBuffer8_Lock( app->dsoundbuf, (DWORD) offset, (DWORD) length, &lpvPtr1, &dwBytes1, + &lpvPtr2, &dwBytes2, 0 ); + + // If DSERR_BUFFERLOST is returned, restore and retry lock. + if( hr == DSERR_BUFFERLOST ) + { + IDirectSoundBuffer8_Restore( app->dsoundbuf ); + hr = IDirectSoundBuffer8_Lock( app->dsoundbuf, (DWORD) offset, (DWORD) length, &lpvPtr1, &dwBytes1, + &lpvPtr2, &dwBytes2, 0 ); + } + if( FAILED( hr) ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't lock sound buffer" ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + return; + } + + // Write to pointers. + app->sound_callback( (APP_S16*) lpvPtr1, (int) dwBytes1 / ( 2 * ( 16 / 8 ) ), app->sound_user_data ); + if( lpvPtr2 ) app->sound_callback( (APP_S16*) lpvPtr2, (int) dwBytes2 / ( 2 * ( 16 / 8 ) ), app->sound_user_data ); + + // Release the data back to DirectSound. + hr = IDirectSoundBuffer8_Unlock( app->dsoundbuf, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2 ); + if( FAILED( hr) ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Couldn't unlock sound buffer" ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + return; + } + } + + +static DWORD WINAPI app_sound_thread_proc( LPVOID lpThreadParameter ) + { + app_t* app = (app_t*) lpThreadParameter; + int mid_point = app->sample_pairs_count / 2; + int half_size = mid_point; + int prev_pos = 0; + while( InterlockedCompareExchange( &app->exit_sound_thread, 0, 0 ) == 0 ) + { + WaitForMultipleObjectsEx( 2, app->sound_notifications, FALSE, 100, FALSE ); + DWORD position = 0; + IDirectSoundBuffer8_GetCurrentPosition( app->dsoundbuf, &position, 0 ); + int pos = ( (int) position )/( 2 * ( 16 / 8 ) ); + + if( prev_pos >= mid_point && pos < mid_point ) + app_sound_write( app, mid_point, half_size ); + else if( prev_pos < mid_point && pos >= mid_point ) + app_sound_write( app, 0, half_size ); + + prev_pos = pos; + } + + return 0; + } + + +void app_sound( app_t* app, int sample_pairs_count, void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ), void* user_data ) + { + if( !app->dsound ) return; + + if( !sound_callback || !sample_pairs_count ) + { + if( app->sound_thread_handle != INVALID_HANDLE_VALUE ) + { + InterlockedExchange( &app->exit_sound_thread, 1 ); + WaitForSingleObject( app->sound_thread_handle, INFINITE ); + CloseHandle( app->sound_thread_handle ); + app->sound_thread_handle = INVALID_HANDLE_VALUE; + } + if( app->dsoundbuf ) + { + IDirectSoundBuffer8_Release( app->dsoundbuf ); + app->dsoundbuf = NULL; + } + app->sample_pairs_count = 0; + app->sound_callback = NULL; + app->sound_user_data = NULL; + return; + } + + if( app->sample_pairs_count != sample_pairs_count ) + { + app->sample_pairs_count = sample_pairs_count; + + if( app->dsoundbuf ) + { + IDirectSoundBuffer8_Release( app->dsoundbuf ); + app->dsoundbuf = 0; + } + + if( sample_pairs_count > 0 ) + { + int const channels = 2; + int const frequency = 44100; + int const bits_per_sample = 16; + + WORD const DSOUND_WAVE_FORMAT_PCM = 1; + DSOUND_WAVEFORMATEX format; + memset( &format, 0, sizeof( DSOUND_WAVEFORMATEX ) ); + format.wFormatTag = DSOUND_WAVE_FORMAT_PCM; + format.nChannels = (WORD) channels; + format.nSamplesPerSec = (DWORD) frequency; + format.nBlockAlign = (WORD) ( ( channels * bits_per_sample ) / 8 ); + format.nAvgBytesPerSec = (DWORD) ( frequency * format.nBlockAlign ); + format.wBitsPerSample = (WORD) bits_per_sample; + format.cbSize = 0; + + DSBUFFERDESC dsbdesc; + memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) ); + dsbdesc.dwSize = sizeof( DSBUFFERDESC ); + + dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY ; + + int size = channels * ( bits_per_sample / 8 ) * sample_pairs_count; + dsbdesc.dwBufferBytes = (DWORD) size; + dsbdesc.lpwfxFormat = &format; + + struct IDirectSoundBuffer8* soundbuf = NULL; + HRESULT hr = IDirectSound8_CreateSoundBuffer( app->dsound, &dsbdesc, &soundbuf, NULL ); + if( FAILED( hr ) || !soundbuf ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Failed to create sound buffer" ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + app->sample_pairs_count = 0; + app->sound_callback = NULL; + app->sound_user_data = NULL; + return; + } + + GUID const GUID_IDirectSoundBuffer8 = { 0x6825a449, 0x7524, 0x4d82, { 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e } }; + #ifdef __cplusplus + GUID const& ref_GUID_IDirectSoundBuffer8 = GUID_IDirectSoundBuffer8; + #else + GUID const* ref_GUID_IDirectSoundBuffer8 = &GUID_IDirectSoundBuffer8; + #endif + hr = IDirectSoundBuffer8_QueryInterface( soundbuf, ref_GUID_IDirectSoundBuffer8, (void**) &app->dsoundbuf ); + IDirectSoundBuffer8_Release( soundbuf ); + + if( FAILED( hr ) || !app->dsoundbuf ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Failed to create sound buffer" ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + app->sample_pairs_count = 0; + app->sound_callback = NULL; + app->sound_user_data = NULL; + return; + } + + struct IDirectSoundNotify* notify = NULL; + GUID const GUID_IDirectSoundNotify8 = { 0xb0210783, 0x89cd, 0x11d0, { 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16 } }; + #ifdef __cplusplus + GUID const& ref_GUID_IDirectSoundNotify8 = GUID_IDirectSoundNotify8; + #else + GUID const* ref_GUID_IDirectSoundNotify8 = &GUID_IDirectSoundNotify8; + #endif + hr = IDirectSoundBuffer8_QueryInterface( app->dsoundbuf, ref_GUID_IDirectSoundNotify8, (void**) ¬ify ); + if( FAILED( hr ) || !notify ) + { + app_log( app, APP_LOG_LEVEL_WARNING, "Failed to create sound buffer" ); + IDirectSoundBuffer8_Release( app->dsoundbuf ); + IDirectSound8_Release( app->dsound ); + app->dsound = 0; + app->dsoundbuf = 0; + app->sample_pairs_count = 0; + app->sound_callback = NULL; + app->sound_user_data = NULL; + return; + } + + DSBPOSITIONNOTIFY notify_positions[ 2 ]; + notify_positions[ 0 ].dwOffset = 0; + notify_positions[ 0 ].hEventNotify = app->sound_notifications[ 0 ]; + notify_positions[ 1 ].dwOffset = (DWORD)( size / 2 ); + notify_positions[ 1 ].hEventNotify = app->sound_notifications[ 1 ]; + + IDirectSoundNotify_SetNotificationPositions( notify, 2, notify_positions ); + IDirectSoundNotify_Release( notify ); + + InterlockedExchange( &app->exit_sound_thread, 0 ); + app->sound_thread_handle = CreateThread( NULL, 0U, app_sound_thread_proc, app, 0, NULL ); + SetThreadPriority( app->sound_thread_handle, THREAD_PRIORITY_HIGHEST ); + + IDirectSoundBuffer8_Play( app->dsoundbuf, 0, 0, DSBPLAY_LOOPING ); + } + } + + app->sound_callback = sound_callback; + app->sound_user_data = user_data; + } + + +void app_sound_volume( app_t* app, float volume ) + { + if( !app->dsound ) return; + if( !app->dsoundbuf ) return; + + int level = volume < 0.000015f ? DSBVOLUME_MIN : (int) ( 2000.0f * (float) log10( (double ) volume ) ); + if( app->sound_level == level ) return; + app->sound_level = level; + + IDirectSoundBuffer8_SetVolume( app->dsoundbuf, level ); + } + + +app_input_t app_input( app_t* app ) + { + app_input_t input; + input.events = app->input_events; + input.count = app->input_count; + app->input_count = 0; + return input; + } + + +void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ) + { + if( width == 0 || height == 0 ) return; + RECT r; + GetClientRect( app->hwnd, &r ); + int window_width = ( app->screenmode == APP_SCREENMODE_FULLSCREEN ) ? app->fullscreen_width : r.right - r.left; + int window_height = ( app->screenmode == APP_SCREENMODE_FULLSCREEN ) ? app->fullscreen_height : r.bottom - r.top; + + + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = window_width / (float) width; + float vscale = window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( window_width - pixel_scale * width ) / 2.0f; + float vborder = ( window_height - pixel_scale * height ) / 2.0f; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = window_width / width; + int vscale = window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( window_width - pixel_scale * width ) / 2; + int vborder = ( window_height - pixel_scale * height ) / 2; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + } + + +void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ) + { + RECT r; + GetClientRect( app->hwnd, &r ); + int window_width = ( app->screenmode == APP_SCREENMODE_FULLSCREEN ) ? app->fullscreen_width : r.right - r.left; + int window_height = ( app->screenmode == APP_SCREENMODE_FULLSCREEN ) ? app->fullscreen_height : r.bottom - r.top; + + + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = window_width / (float) width; + float vscale = window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( window_width - pixel_scale * width ) / 2.0f; + float vborder = ( window_height - pixel_scale * height ) / 2.0f; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = window_width / width; + int vscale = window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( window_width - pixel_scale * width ) / 2; + int vborder = ( window_height - pixel_scale * height ) / 2; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + } + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SDL +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#elif defined( APP_SDL ) + +#ifndef APP_MALLOC + #include + #if defined(__cplusplus) + #define APP_MALLOC( ctx, size ) ( ::malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( ::free( ptr ) ) + #else + #define APP_MALLOC( ctx, size ) ( malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( free( ptr ) ) + #endif +#endif + +#include +#include + +#include "SDL.h" + +#ifndef APP_FATAL_ERROR + #define APP_FATAL_ERROR( ctx, message ) { \ + SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Fatal Error!", message, NULL ); exit( 0xff ); } +#endif + +struct app_t + { + void* memctx; + void* logctx; + void* fatalctx; + struct app_internal_opengl_t gl; + int initialized; + int exit_requested; + int has_focus; + app_interpolation_t interpolation; + app_screenmode_t screenmode; + + SDL_Window* window; + SDL_Cursor* cursor; + + SDL_AudioDeviceID sound_device; + void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ); + void* sound_user_data; + int volume; + + app_input_event_t input_events[ 1024 ]; + int input_count; + + int display_count; + app_display_t displays[ 16 ]; + + }; + + +int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ) + { + app_t* app = (app_t*) APP_MALLOC( memctx, sizeof( app_t ) ); + memset( app, 0, (int)sizeof( app_t ) ); + app->memctx = memctx; + app->logctx = logctx; + app->fatalctx = fatalctx; + app->interpolation = APP_INTERPOLATION_LINEAR; + app->screenmode = APP_SCREENMODE_FULLSCREEN; + + int result = 0xff; + int display_count; + int glres; + + if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) + { +// printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() ); + goto init_failed; + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + + app->window = SDL_CreateWindow( "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 400, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN); + if( !app->window ) + { +// printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() ); + goto init_failed; + } + + app->has_focus = 1; + app->volume = 256; + + display_count = SDL_GetNumVideoDisplays(); + for( int i = 0; i < display_count; ++i ) + { + SDL_Rect r; + SDL_GetDisplayBounds( i, &r ); + app_display_t d; + sprintf( d.id, "DISPLAY%d", i ); + d.x = r.x; + d.y = r.y; + d.width = r.w; + d.height = r.h; + app->displays[ i ] = d; + } + app->display_count = display_count; + + SDL_GL_CreateContext( app->window ); + glewInit(); + + SDL_GL_SetSwapInterval( 1 ); + + app->gl.CreateShader = glCreateShader; + app->gl.ShaderSource = glShaderSource; + app->gl.CompileShader = glCompileShader; + app->gl.GetShaderiv = glGetShaderiv; + app->gl.CreateProgram = glCreateProgram; + app->gl.AttachShader = glAttachShader; + app->gl.BindAttribLocation = glBindAttribLocation; + app->gl.LinkProgram = glLinkProgram; + app->gl.GetProgramiv = glGetProgramiv; + app->gl.GenBuffers = glGenBuffers; + app->gl.BindBuffer = glBindBuffer; + app->gl.EnableVertexAttribArray = glEnableVertexAttribArray; + app->gl.VertexAttribPointer = glVertexAttribPointer; + app->gl.GenTextures = glGenTextures; + app->gl.Enable = glEnable; + app->gl.ActiveTexture = glActiveTexture; + app->gl.BindTexture = glBindTexture; + app->gl.TexParameteri = glTexParameteri; + app->gl.DeleteBuffers = glDeleteBuffers; + app->gl.DeleteTextures = glDeleteTextures; + app->gl.BufferData = glBufferData; + app->gl.UseProgram = glUseProgram; + app->gl.Uniform1i = glUniform1i; + app->gl.Uniform3f = glUniform3f; + app->gl.GetUniformLocation = glGetUniformLocation; + app->gl.TexImage2D = glTexImage2D; + app->gl.ClearColor = glClearColor; + app->gl.Clear = glClear; + app->gl.DrawArrays = glDrawArrays; + app->gl.Viewport = glViewport; + app->gl.DeleteShader = glDeleteShader; + app->gl.DeleteProgram = glDeleteProgram; + #ifdef APP_REPORT_SHADER_ERRORS + app->gl.GetShaderInfoLog = glGetShaderInfoLog; + #endif + + glres = app_internal_opengl_init( app, &app->gl, app->interpolation, 640, 400 ); + if( !glres ) + { + app_fatal_error( app, "OpenGL init fail" ); + goto init_failed; + } + + result = app_proc( app, user_data ); + +init_failed: + if( app->sound_device ) + { + SDL_PauseAudioDevice( app->sound_device, 1 ); + SDL_CloseAudioDevice( app->sound_device ); + app->sound_device = 0; + app->sound_callback = NULL; + app->sound_user_data = NULL; + } + + if( app->cursor ) SDL_FreeCursor( app->cursor ); + + //Destroy window + SDL_DestroyWindow( app->window ); + + + //Quit SDL subsystems + SDL_Quit(); + + APP_FREE( memctx, app ); + return result; + } + + +static app_key_t app_internal_scancode_to_appkey( app_t* app, SDL_Scancode scancode ) + + { + int map[ 287 * 2 ] = { APP_KEY_INVALID, SDL_SCANCODE_UNKNOWN, APP_KEY_INVALID, 1, APP_KEY_INVALID, 2, APP_KEY_INVALID, 3, APP_KEY_A, SDL_SCANCODE_A, + APP_KEY_B, SDL_SCANCODE_B, APP_KEY_C, SDL_SCANCODE_C, APP_KEY_D, SDL_SCANCODE_D, APP_KEY_E, SDL_SCANCODE_E, APP_KEY_F, SDL_SCANCODE_F, APP_KEY_G, + SDL_SCANCODE_G, APP_KEY_H, SDL_SCANCODE_H, APP_KEY_I, SDL_SCANCODE_I, APP_KEY_J, SDL_SCANCODE_J, APP_KEY_K, SDL_SCANCODE_K, APP_KEY_L, + SDL_SCANCODE_L, APP_KEY_M, SDL_SCANCODE_M, APP_KEY_N, SDL_SCANCODE_N, APP_KEY_O, SDL_SCANCODE_O, APP_KEY_P, SDL_SCANCODE_P, APP_KEY_Q, + SDL_SCANCODE_Q, APP_KEY_R, SDL_SCANCODE_R, APP_KEY_S, SDL_SCANCODE_S, APP_KEY_T, SDL_SCANCODE_T, APP_KEY_U, SDL_SCANCODE_U, APP_KEY_V, + SDL_SCANCODE_V, APP_KEY_W, SDL_SCANCODE_W, APP_KEY_X, SDL_SCANCODE_X, APP_KEY_Y, SDL_SCANCODE_Y, APP_KEY_Z, SDL_SCANCODE_Z, APP_KEY_1, + SDL_SCANCODE_1, APP_KEY_2, SDL_SCANCODE_2, APP_KEY_3, SDL_SCANCODE_3, APP_KEY_4, SDL_SCANCODE_4, APP_KEY_5, SDL_SCANCODE_5, APP_KEY_6, + SDL_SCANCODE_6, APP_KEY_7, SDL_SCANCODE_7, APP_KEY_8, SDL_SCANCODE_8, APP_KEY_9, SDL_SCANCODE_9, APP_KEY_0, SDL_SCANCODE_0, APP_KEY_RETURN, + SDL_SCANCODE_RETURN, APP_KEY_ESCAPE, SDL_SCANCODE_ESCAPE, APP_KEY_BACK, SDL_SCANCODE_BACKSPACE, APP_KEY_TAB, SDL_SCANCODE_TAB, APP_KEY_SPACE, + SDL_SCANCODE_SPACE, APP_KEY_OEM_MINUS, SDL_SCANCODE_MINUS, APP_KEY_INVALID, SDL_SCANCODE_EQUALS, APP_KEY_OEM_4, SDL_SCANCODE_LEFTBRACKET, + APP_KEY_OEM_6, SDL_SCANCODE_RIGHTBRACKET, APP_KEY_OEM_5, SDL_SCANCODE_BACKSLASH, APP_KEY_INVALID, SDL_SCANCODE_NONUSHASH, APP_KEY_OEM_1, + SDL_SCANCODE_SEMICOLON, APP_KEY_OEM_7, SDL_SCANCODE_APOSTROPHE, APP_KEY_INVALID, SDL_SCANCODE_GRAVE, APP_KEY_OEM_COMMA, SDL_SCANCODE_COMMA, + APP_KEY_OEM_PERIOD, SDL_SCANCODE_PERIOD, APP_KEY_OEM_2, SDL_SCANCODE_SLASH, APP_KEY_CAPITAL, SDL_SCANCODE_CAPSLOCK, APP_KEY_F1, SDL_SCANCODE_F1, + APP_KEY_F2, SDL_SCANCODE_F2, APP_KEY_F3, SDL_SCANCODE_F3, APP_KEY_F4, SDL_SCANCODE_F4, APP_KEY_F5, SDL_SCANCODE_F5, APP_KEY_F6, SDL_SCANCODE_F6, + APP_KEY_F7, SDL_SCANCODE_F7, APP_KEY_F8, SDL_SCANCODE_F8, APP_KEY_F9, SDL_SCANCODE_F9, APP_KEY_F10, SDL_SCANCODE_F10, APP_KEY_F11, + SDL_SCANCODE_F11, APP_KEY_F12, SDL_SCANCODE_F12, APP_KEY_SNAPSHOT, SDL_SCANCODE_PRINTSCREEN, APP_KEY_SCROLL, SDL_SCANCODE_SCROLLLOCK, + APP_KEY_PAUSE, SDL_SCANCODE_PAUSE, APP_KEY_INSERT, SDL_SCANCODE_INSERT, APP_KEY_HOME, SDL_SCANCODE_HOME, APP_KEY_PRIOR, SDL_SCANCODE_PAGEUP, + APP_KEY_DELETE, SDL_SCANCODE_DELETE, APP_KEY_END, SDL_SCANCODE_END, APP_KEY_NEXT, SDL_SCANCODE_PAGEDOWN, APP_KEY_RIGHT, SDL_SCANCODE_RIGHT, + APP_KEY_LEFT, SDL_SCANCODE_LEFT, APP_KEY_DOWN, SDL_SCANCODE_DOWN, APP_KEY_UP, SDL_SCANCODE_UP, APP_KEY_NUMLOCK, SDL_SCANCODE_NUMLOCKCLEAR, + APP_KEY_DIVIDE, SDL_SCANCODE_KP_DIVIDE, APP_KEY_MULTIPLY, SDL_SCANCODE_KP_MULTIPLY, APP_KEY_SUBTRACT, SDL_SCANCODE_KP_MINUS, APP_KEY_ADD, + SDL_SCANCODE_KP_PLUS, APP_KEY_RETURN, SDL_SCANCODE_KP_ENTER, APP_KEY_NUMPAD1,SDL_SCANCODE_KP_1, APP_KEY_NUMPAD2,SDL_SCANCODE_KP_2, APP_KEY_NUMPAD3, + SDL_SCANCODE_KP_3, APP_KEY_NUMPAD4,SDL_SCANCODE_KP_4, APP_KEY_NUMPAD5,SDL_SCANCODE_KP_5, APP_KEY_NUMPAD6,SDL_SCANCODE_KP_6, APP_KEY_NUMPAD7, + SDL_SCANCODE_KP_7, APP_KEY_NUMPAD8,SDL_SCANCODE_KP_8, APP_KEY_NUMPAD9,SDL_SCANCODE_KP_9, APP_KEY_NUMPAD0,SDL_SCANCODE_KP_0, APP_KEY_DECIMAL, + SDL_SCANCODE_KP_PERIOD, APP_KEY_INVALID, SDL_SCANCODE_NONUSBACKSLASH, APP_KEY_APPS, SDL_SCANCODE_APPLICATION, APP_KEY_INVALID, SDL_SCANCODE_POWER, + APP_KEY_RETURN, SDL_SCANCODE_KP_EQUALS, APP_KEY_F13, SDL_SCANCODE_F13, APP_KEY_F14, SDL_SCANCODE_F14, APP_KEY_F15, SDL_SCANCODE_F15, APP_KEY_F16, + SDL_SCANCODE_F16, APP_KEY_F17, SDL_SCANCODE_F17, APP_KEY_F18, SDL_SCANCODE_F18, APP_KEY_F19, SDL_SCANCODE_F19, APP_KEY_F20, SDL_SCANCODE_F20, + APP_KEY_F21, SDL_SCANCODE_F21, APP_KEY_F22, SDL_SCANCODE_F22, APP_KEY_F23, SDL_SCANCODE_F23, APP_KEY_F24, SDL_SCANCODE_F24, APP_KEY_EXEC, + SDL_SCANCODE_EXECUTE, APP_KEY_HELP, SDL_SCANCODE_HELP, APP_KEY_MENU, SDL_SCANCODE_MENU, APP_KEY_SELECT, SDL_SCANCODE_SELECT, APP_KEY_MEDIA_STOP, + SDL_SCANCODE_STOP, APP_KEY_INVALID, SDL_SCANCODE_AGAIN, APP_KEY_INVALID, SDL_SCANCODE_UNDO, APP_KEY_INVALID, SDL_SCANCODE_CUT, APP_KEY_INVALID, + SDL_SCANCODE_COPY, APP_KEY_INVALID, SDL_SCANCODE_PASTE, APP_KEY_INVALID, SDL_SCANCODE_FIND, APP_KEY_VOLUME_MUTE, SDL_SCANCODE_MUTE, + APP_KEY_VOLUME_UP, SDL_SCANCODE_VOLUMEUP, APP_KEY_VOLUME_DOWN, SDL_SCANCODE_VOLUMEDOWN, APP_KEY_INVALID, 130, APP_KEY_INVALID, 131, + APP_KEY_INVALID, 132, APP_KEY_SEPARATOR, SDL_SCANCODE_KP_COMMA, APP_KEY_INVALID, SDL_SCANCODE_KP_EQUALSAS400, APP_KEY_INVALID, + SDL_SCANCODE_INTERNATIONAL1, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL2, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL3, APP_KEY_INVALID, + SDL_SCANCODE_INTERNATIONAL4, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL5, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL6, APP_KEY_INVALID, + SDL_SCANCODE_INTERNATIONAL7, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL8, APP_KEY_INVALID, SDL_SCANCODE_INTERNATIONAL9, APP_KEY_HANGUL, + SDL_SCANCODE_LANG1, APP_KEY_HANJA, SDL_SCANCODE_LANG2, APP_KEY_INVALID, SDL_SCANCODE_LANG3, APP_KEY_INVALID, SDL_SCANCODE_LANG4, APP_KEY_INVALID, + SDL_SCANCODE_LANG5, APP_KEY_INVALID, SDL_SCANCODE_LANG6, APP_KEY_INVALID, SDL_SCANCODE_LANG7, APP_KEY_INVALID, SDL_SCANCODE_LANG8, APP_KEY_INVALID, + SDL_SCANCODE_LANG9, APP_KEY_INVALID, SDL_SCANCODE_ALTERASE, APP_KEY_INVALID, SDL_SCANCODE_SYSREQ, APP_KEY_CANCEL, SDL_SCANCODE_CANCEL, APP_KEY_CLEAR, + SDL_SCANCODE_CLEAR, APP_KEY_PRIOR, SDL_SCANCODE_PRIOR, APP_KEY_RETURN, SDL_SCANCODE_RETURN2, APP_KEY_OEM_COMMA, SDL_SCANCODE_SEPARATOR, + APP_KEY_INVALID, SDL_SCANCODE_OUT, APP_KEY_INVALID, SDL_SCANCODE_OPER, APP_KEY_INVALID, SDL_SCANCODE_CLEARAGAIN, APP_KEY_CRSEL, SDL_SCANCODE_CRSEL, + APP_KEY_EXSEL, SDL_SCANCODE_EXSEL, APP_KEY_INVALID, 165, APP_KEY_INVALID, 166, APP_KEY_INVALID, 167, APP_KEY_INVALID, 168, APP_KEY_INVALID, 169, + APP_KEY_INVALID, 170, APP_KEY_INVALID, 171, APP_KEY_INVALID, 172, APP_KEY_INVALID, 173, APP_KEY_INVALID, 174, APP_KEY_INVALID, 175, APP_KEY_INVALID, + SDL_SCANCODE_KP_00, APP_KEY_INVALID, SDL_SCANCODE_KP_000, APP_KEY_INVALID, SDL_SCANCODE_THOUSANDSSEPARATOR, APP_KEY_INVALID, + SDL_SCANCODE_DECIMALSEPARATOR, APP_KEY_INVALID, SDL_SCANCODE_CURRENCYUNIT, APP_KEY_INVALID, SDL_SCANCODE_CURRENCYSUBUNIT, APP_KEY_INVALID, + SDL_SCANCODE_KP_LEFTPAREN, APP_KEY_INVALID, SDL_SCANCODE_KP_RIGHTPAREN, APP_KEY_INVALID, SDL_SCANCODE_KP_LEFTBRACE, APP_KEY_INVALID, + SDL_SCANCODE_KP_RIGHTBRACE, APP_KEY_INVALID, SDL_SCANCODE_KP_TAB, APP_KEY_INVALID, SDL_SCANCODE_KP_BACKSPACE, APP_KEY_INVALID, SDL_SCANCODE_KP_A, + APP_KEY_INVALID, SDL_SCANCODE_KP_B, APP_KEY_INVALID, SDL_SCANCODE_KP_C, APP_KEY_INVALID, SDL_SCANCODE_KP_D, APP_KEY_INVALID, SDL_SCANCODE_KP_E, + APP_KEY_INVALID, SDL_SCANCODE_KP_F, APP_KEY_INVALID, SDL_SCANCODE_KP_XOR, APP_KEY_INVALID, SDL_SCANCODE_KP_POWER, APP_KEY_INVALID, + SDL_SCANCODE_KP_PERCENT, APP_KEY_INVALID, SDL_SCANCODE_KP_LESS, APP_KEY_INVALID, SDL_SCANCODE_KP_GREATER, APP_KEY_INVALID, SDL_SCANCODE_KP_AMPERSAND, + APP_KEY_INVALID, SDL_SCANCODE_KP_DBLAMPERSAND, APP_KEY_INVALID, SDL_SCANCODE_KP_VERTICALBAR, APP_KEY_INVALID, SDL_SCANCODE_KP_DBLVERTICALBAR, + APP_KEY_INVALID, SDL_SCANCODE_KP_COLON, APP_KEY_INVALID, SDL_SCANCODE_KP_HASH, APP_KEY_INVALID, SDL_SCANCODE_KP_SPACE, APP_KEY_INVALID, + SDL_SCANCODE_KP_AT, APP_KEY_INVALID, SDL_SCANCODE_KP_EXCLAM, APP_KEY_INVALID, SDL_SCANCODE_KP_MEMSTORE, APP_KEY_INVALID, SDL_SCANCODE_KP_MEMRECALL, + APP_KEY_INVALID, SDL_SCANCODE_KP_MEMCLEAR, APP_KEY_INVALID, SDL_SCANCODE_KP_MEMADD, APP_KEY_INVALID, SDL_SCANCODE_KP_MEMSUBTRACT, APP_KEY_INVALID, + SDL_SCANCODE_KP_MEMMULTIPLY, APP_KEY_INVALID, SDL_SCANCODE_KP_MEMDIVIDE, APP_KEY_INVALID, SDL_SCANCODE_KP_PLUSMINUS, APP_KEY_INVALID, + SDL_SCANCODE_KP_CLEAR, APP_KEY_INVALID, SDL_SCANCODE_KP_CLEARENTRY, APP_KEY_INVALID, SDL_SCANCODE_KP_BINARY, APP_KEY_INVALID, SDL_SCANCODE_KP_OCTAL, + APP_KEY_INVALID, SDL_SCANCODE_KP_DECIMAL, APP_KEY_INVALID, SDL_SCANCODE_KP_HEXADECIMAL, APP_KEY_INVALID, 222, APP_KEY_INVALID, 223, APP_KEY_LCONTROL, + SDL_SCANCODE_LCTRL, APP_KEY_LSHIFT, SDL_SCANCODE_LSHIFT, APP_KEY_LMENU, SDL_SCANCODE_LALT, APP_KEY_LWIN, SDL_SCANCODE_LGUI, APP_KEY_RCONTROL, + SDL_SCANCODE_RCTRL, APP_KEY_RSHIFT, SDL_SCANCODE_RSHIFT, APP_KEY_RMENU, SDL_SCANCODE_RALT, APP_KEY_RWIN, SDL_SCANCODE_RGUI, APP_KEY_INVALID, 232, + APP_KEY_INVALID, 233, APP_KEY_INVALID, 234, APP_KEY_INVALID, 235, APP_KEY_INVALID, 236, APP_KEY_INVALID, 237, APP_KEY_INVALID, 238, APP_KEY_INVALID, + 239, APP_KEY_INVALID, 240, APP_KEY_INVALID, 241, APP_KEY_INVALID, 242, APP_KEY_INVALID, 243, APP_KEY_INVALID, 244, APP_KEY_INVALID, 245, + APP_KEY_INVALID, 246, APP_KEY_INVALID, 247, APP_KEY_INVALID, 248, APP_KEY_INVALID, 249, APP_KEY_INVALID, 250, APP_KEY_INVALID, 251, APP_KEY_INVALID, + 252, APP_KEY_INVALID, 253, APP_KEY_INVALID, 254, APP_KEY_INVALID, 255, APP_KEY_INVALID, 256, APP_KEY_MODECHANGE, SDL_SCANCODE_MODE, + APP_KEY_MEDIA_NEXT_TRACK, SDL_SCANCODE_AUDIONEXT, APP_KEY_MEDIA_PREV_TRACK, SDL_SCANCODE_AUDIOPREV, APP_KEY_MEDIA_PLAY_PAUSE, SDL_SCANCODE_AUDIOSTOP, + APP_KEY_PLAY, SDL_SCANCODE_AUDIOPLAY, APP_KEY_VOLUME_MUTE, SDL_SCANCODE_AUDIOMUTE, APP_KEY_LAUNCH_MEDIA_SELECT, SDL_SCANCODE_MEDIASELECT, + APP_KEY_INVALID, SDL_SCANCODE_WWW, APP_KEY_LAUNCH_MAIL, SDL_SCANCODE_MAIL, APP_KEY_INVALID, SDL_SCANCODE_CALCULATOR, APP_KEY_INVALID, + SDL_SCANCODE_COMPUTER, APP_KEY_BROWSER_SEARCH, SDL_SCANCODE_AC_SEARCH, APP_KEY_BROWSER_HOME, SDL_SCANCODE_AC_HOME, APP_KEY_BROWSER_BACK, + SDL_SCANCODE_AC_BACK, APP_KEY_BROWSER_FORWARD, SDL_SCANCODE_AC_FORWARD, APP_KEY_BROWSER_STOP, SDL_SCANCODE_AC_STOP, APP_KEY_BROWSER_REFRESH, + SDL_SCANCODE_AC_REFRESH, APP_KEY_BROWSER_FAVORITES, SDL_SCANCODE_AC_BOOKMARKS, APP_KEY_INVALID, SDL_SCANCODE_BRIGHTNESSDOWN, APP_KEY_INVALID, + SDL_SCANCODE_BRIGHTNESSUP, APP_KEY_INVALID, SDL_SCANCODE_DISPLAYSWITCH, APP_KEY_INVALID, SDL_SCANCODE_KBDILLUMTOGGLE, APP_KEY_INVALID, + SDL_SCANCODE_KBDILLUMDOWN, APP_KEY_INVALID, SDL_SCANCODE_KBDILLUMUP, APP_KEY_INVALID, SDL_SCANCODE_EJECT, APP_KEY_SLEEP, SDL_SCANCODE_SLEEP, + APP_KEY_LAUNCH_APP1, SDL_SCANCODE_APP1, APP_KEY_LAUNCH_APP2, SDL_SCANCODE_APP2, APP_KEY_INVALID, SDL_SCANCODE_AUDIOREWIND, APP_KEY_INVALID, + SDL_SCANCODE_AUDIOFASTFORWARD, }; + + if( scancode < 0 || scancode >= sizeof( map ) / ( 2 * sizeof( *map ) ) ) return APP_KEY_INVALID; + if( map[ scancode * 2 + 1 ] != scancode ) + { + app_log( app, APP_LOG_LEVEL_ERROR, "Keymap definition error" ); + return APP_KEY_INVALID; + } + return (app_key_t) map[ scancode * 2 ]; + } + + +static void app_internal_add_input_event( app_t* app, app_input_event_t* event ) + { + if( app->has_focus ) + { + if( app->input_count < sizeof( app->input_events ) / sizeof( *app->input_events ) ) + app->input_events[ app->input_count++ ] = *event; + } + } + + +app_state_t app_yield( app_t* app ) + { + if( !app->initialized ) + { + app->initialized = 1; + if( app->screenmode == APP_SCREENMODE_FULLSCREEN ) SDL_SetWindowFullscreen( app->window, SDL_WINDOW_FULLSCREEN_DESKTOP ); + SDL_ShowWindow( app->window ); + int w = app->gl.window_width; + int h = app->gl.window_height; + SDL_GL_GetDrawableSize( app->window, &w, &h ); + app_internal_opengl_resize( &app->gl, w, h ); + } + + SDL_Event e; + while( SDL_PollEvent( &e ) ) + { + if( e.type == SDL_WINDOWEVENT ) + { + if( e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) + { + int w = app->gl.window_width; + int h = app->gl.window_height; + SDL_GL_GetDrawableSize( app->window, &w, &h ); + if( w != app->gl.window_width || h != app->gl.window_height ) + { + app_internal_opengl_resize( &app->gl, w, h ); + } + } + else if( e.window.event == SDL_WINDOWEVENT_CLOSE ) + { + app->exit_requested = 1; + } + else if( e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ) + { + app->has_focus = 1; + } + else if( e.window.event == SDL_WINDOWEVENT_FOCUS_LOST ) + { + app->has_focus = 0; + } + } + else if( e.type == SDL_KEYDOWN ) + { + app_input_event_t input_event; + input_event.type = APP_INPUT_KEY_DOWN; + input_event.data.key = app_internal_scancode_to_appkey( app, e.key.keysym.scancode ); + app_internal_add_input_event( app, &input_event ); + } + else if( e.type == SDL_KEYUP ) + { + app_input_event_t input_event; + input_event.type = APP_INPUT_KEY_UP; + input_event.data.key = app_internal_scancode_to_appkey( app, e.key.keysym.scancode ); + app_internal_add_input_event( app, &input_event ); + } + else if( e.type == SDL_TEXTINPUT ) + { + app_input_event_t input_event; + char *c; + input_event.type = APP_INPUT_CHAR; + for ( c = e.text.text; *c; c++ ) + { + input_event.data.char_code = *c; + app_internal_add_input_event( app, &input_event ); + } + } + else if( e.type == SDL_MOUSEMOTION ) + { + app_input_event_t input_event; + input_event.type = APP_INPUT_MOUSE_MOVE; + input_event.data.mouse_pos.x = e.motion.x; + input_event.data.mouse_pos.y = e.motion.y; + app_internal_add_input_event( app, &input_event ); + + input_event.type = APP_INPUT_MOUSE_DELTA; + input_event.data.mouse_pos.x = e.motion.xrel; + input_event.data.mouse_pos.y = e.motion.yrel; + app_internal_add_input_event( app, &input_event ); + } + else if( e.type == SDL_MOUSEBUTTONDOWN ) + { + app_input_event_t input_event; + input_event.type = APP_INPUT_KEY_DOWN; + if( e.button.button == SDL_BUTTON_LEFT ) + input_event.data.key = APP_KEY_LBUTTON; + else if( e.button.button == SDL_BUTTON_RIGHT ) + input_event.data.key = APP_KEY_RBUTTON; + else if( e.button.button == SDL_BUTTON_MIDDLE ) + input_event.data.key = APP_KEY_MBUTTON; + else if( e.button.button == SDL_BUTTON_X1 ) + input_event.data.key = APP_KEY_XBUTTON1; + else if( e.button.button == SDL_BUTTON_X2 ) + input_event.data.key = APP_KEY_XBUTTON2; + app_internal_add_input_event( app, &input_event ); + } + else if( e.type == SDL_MOUSEBUTTONUP ) + { + app_input_event_t input_event; + input_event.type = APP_INPUT_KEY_UP; + if( e.button.button == SDL_BUTTON_LEFT ) + input_event.data.key = APP_KEY_LBUTTON; + else if( e.button.button == SDL_BUTTON_RIGHT ) + input_event.data.key = APP_KEY_RBUTTON; + else if( e.button.button == SDL_BUTTON_MIDDLE ) + input_event.data.key = APP_KEY_MBUTTON; + else if( e.button.button == SDL_BUTTON_X1 ) + input_event.data.key = APP_KEY_XBUTTON1; + else if( e.button.button == SDL_BUTTON_X2 ) + input_event.data.key = APP_KEY_XBUTTON2; + app_internal_add_input_event( app, &input_event ); + } + else if( e.type == SDL_MOUSEWHEEL ) + { + float const microsoft_mouse_wheel_constant = 120.0f; + float wheel_delta = ( (float) e.wheel.y ) / microsoft_mouse_wheel_constant; + if( app->input_count > 0 && app->input_events[ app->input_count - 1 ].type == APP_INPUT_SCROLL_WHEEL ) + { + app_input_event_t* event = &app->input_events[ app->input_count - 1 ]; + event->data.wheel_delta += wheel_delta; + } + else + { + app_input_event_t input_event; + input_event.type = APP_INPUT_SCROLL_WHEEL; + input_event.data.wheel_delta = wheel_delta; + app_internal_add_input_event( app, &input_event ); + } + } + + } + + return app->exit_requested ? APP_STATE_EXIT_REQUESTED : APP_STATE_NORMAL; + } + + +void app_cancel_exit( app_t* app ) + { + app->exit_requested = 0; + } + + +void app_title( app_t* app, char const* title ) + { + SDL_SetWindowTitle( app->window, title ); + } + + +char const* app_cmdline( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_filename( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_userdata( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_appdata( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } + + +APP_U64 app_time_count( app_t* app ) + { + return SDL_GetPerformanceCounter(); + } + + +APP_U64 app_time_freq( app_t* app ) + { + return SDL_GetPerformanceFrequency(); + } + + +void app_log( app_t* app, app_log_level_t level, char const* message ) { /* NOT IMPLEMENTED */ } + + +void app_fatal_error( app_t* app, char const* message ) + { + APP_FATAL_ERROR( app->fatalctx, message ); + } + + +void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) + { + SDL_Surface* surf = SDL_CreateRGBSurfaceFrom( (void*)pixels_abgr, width, height, 32, 4 * width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ); + if( app->cursor ) SDL_FreeCursor( app->cursor ); + app->cursor = SDL_CreateColorCursor( surf, hotspot_x, hotspot_y ); + SDL_SetCursor( app->cursor ); + SDL_FreeSurface( surf ); + } + + +void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ) { /* NOT IMPLEMENTED */ } + + +void app_pointer_pos( app_t* app, int x, int y ) + { + SDL_WarpMouseInWindow( app->window, x, y ); + } + + +int app_pointer_x( app_t* app ) + { + int x = 0; + SDL_GetMouseState( &x, NULL ); + return x; + } + + +int app_pointer_y( app_t* app ) + { + int y = 0; + SDL_GetMouseState( NULL, &y ); + return y; + } + + +void app_pointer_limit( app_t* app, int x, int y, int width, int height ) { /* NOT IMPLEMENTED */ } +void app_pointer_limit_off( app_t* app ) { /* NOT IMPLEMENTED */ } + +void app_interpolation( app_t* app, app_interpolation_t interpolation ) + { + if( interpolation == app->interpolation ) return; + app->interpolation = interpolation; + + int mouse_x; + int mouse_y; + SDL_GetMouseState( &mouse_x, &mouse_y ); + + app_input_event_t input_event; + input_event.type = APP_INPUT_MOUSE_MOVE; + input_event.data.mouse_pos.x = mouse_x; + input_event.data.mouse_pos.y = mouse_y; + app_internal_add_input_event( app, &input_event ); + + app_internal_opengl_interpolation( &app->gl, interpolation ); + } + + +void app_screenmode( app_t* app, app_screenmode_t screenmode ) + { + if( screenmode != app->screenmode ) + { + app->screenmode = screenmode; + SDL_SetWindowFullscreen( app->window, + screenmode == APP_SCREENMODE_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 ); + } + } + + +void app_window_size( app_t* app, int width, int height ) + { + SDL_SetWindowSize( app->window, width, height ); + } + + +int app_window_width( app_t* app ) + { + int width = 0; + SDL_GetWindowSize( app->window, &width, NULL ); + return width; + } + + +int app_window_height( app_t* app ) + { + int height = 0; + SDL_GetWindowSize( app->window, NULL, &height ); + return height; + } + + +void app_window_pos( app_t* app, int x, int y ) + { + SDL_SetWindowPosition( app->window, x, y ); + } + + +int app_window_x( app_t* app ) + { + int x = 0; + SDL_GetWindowPosition( app->window, &x, NULL ); + return x; + } + + +int app_window_y( app_t* app ) + { + int y = 0; + SDL_GetWindowPosition( app->window, NULL, &y ); + return y; + } + + +app_displays_t app_displays( app_t* app ) + { + app_displays_t displays; + displays.count = app->display_count; + displays.displays = app->displays; + return displays; + } + + +void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) + { + if( pixels_xbgr ) app_internal_opengl_present( &app->gl, pixels_xbgr, width, height, mod_xbgr, border_xbgr ); + SDL_GL_SwapWindow( app->window ); + } + + +static void app_internal_sdl_sound_callback( void* userdata, Uint8* stream, int len ) + { + app_t* app = (app_t*) userdata; + if( app->sound_callback ) + { + app->sound_callback( (APP_S16*) stream, len / ( 2 * sizeof( APP_S16 ) ), app->sound_user_data ); + if( app->volume < 256 ) + { + APP_S16* samples = (APP_S16*) stream; + for( int i = 0; i < len / sizeof( APP_S16 ); ++i ) + { + int s = (int)(*samples); + s = ( s * app->volume ) >> 8; + *samples++ = (APP_S16) s; + } + } + } + } + + +void app_sound( app_t* app, int sample_pairs_count, void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ), void* user_data ) + { + if( app->sound_device ) + { + SDL_PauseAudioDevice( app->sound_device, 1 ); + SDL_CloseAudioDevice( app->sound_device ); + app->sound_callback = NULL; + app->sound_user_data = NULL; + app->sound_device = 0; + } + if( sample_pairs_count > 0 && sound_callback ) + { + SDL_AudioSpec spec; + spec.freq = 44100; + spec.format = AUDIO_S16; + spec.channels = 2; + spec.silence = 0; + spec.samples = sample_pairs_count * 2; + spec.padding = 0; + spec.size = 0; + spec.callback = app_internal_sdl_sound_callback; + spec.userdata = app; + + app->sound_device = SDL_OpenAudioDevice( NULL, 0, &spec, NULL, 0 ); + if( !app->sound_device ) return; + + app->sound_callback = sound_callback; + app->sound_user_data = user_data; + SDL_PauseAudioDevice( app->sound_device, 0 ); + } + } + + +void app_sound_volume( app_t* app, float volume ) + { + int v = (int) ( volume * 256.0f ); + app->volume = v < 0 ? 0 : v > 256 ? 256 : v; + } + + +app_input_t app_input( app_t* app ) + { + app_input_t input; + input.events = app->input_events; + input.count = app->input_count; + app->input_count = 0; + return input; + } + + +void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ) + { + if( width == 0 || height == 0 ) return; + int window_width; + int window_height; + SDL_GL_GetDrawableSize( app->window, &window_width, &window_height ); + + + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = window_width / (float) width; + float vscale = window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( window_width - pixel_scale * width ) / 2.0f; + float vborder = ( window_height - pixel_scale * height ) / 2.0f; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = window_width / width; + int vscale = window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( window_width - pixel_scale * width ) / 2; + int vborder = ( window_height - pixel_scale * height ) / 2; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + } + + +void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ) + { + int window_width; + int window_height; + SDL_GL_GetDrawableSize( app->window, &window_width, &window_height ); + + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = window_width / (float) width; + float vscale = window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( window_width - pixel_scale * width ) / 2.0f; + float vborder = ( window_height - pixel_scale * height ) / 2.0f; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = window_width / width; + int vscale = window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( window_width - pixel_scale * width ) / 2; + int vborder = ( window_height - pixel_scale * height ) / 2; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + } + + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// WEBASSEMBLY +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#elif defined( APP_WASM ) + +#ifndef APP_MALLOC + #include + #if defined(__cplusplus) + #define APP_MALLOC( ctx, size ) ( ::malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( ::free( ptr ) ) + #else + #define APP_MALLOC( ctx, size ) ( malloc( size ) ) + #define APP_FREE( ctx, ptr ) ( free( ptr ) ) + #endif +#endif + +#include +#include +#include + +WAJIC(void, app_js_print, (const char* msg), + { + WA.print(MStrGet(msg) + "\n"); + }) + +#ifndef APP_FATAL_ERROR + #define APP_FATAL_ERROR( ctx, message ) { app_js_print(message); abort(); } +#endif + +struct app_t + { + void* memctx; + void* logctx; + void* fatalctx; + struct app_internal_opengl_t gl; + int has_focus; + app_interpolation_t interpolation; + + void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ); + void* sound_user_data; + int sound_buffer_size; + APP_S16* sound_buffer; + int volume; + + app_input_event_t input_events[ 1024 ]; + int input_count; + int pointer_x; + int pointer_y; + }; + + +// The javascript event handling keeps a simple buffer of events with 3 ints per event +// The first int is the event id, followed by two arguments (not all events use both arguments) +// EVENT ID ARG 1 ARG 2 +// 1 WINDOW_SIZE_CHANGED w h +// 2 WINDOW_FOCUS gained/lost +// 3 KEY down/up scancode +// 4 CHAR charcode +// 5 MOUSE_MOTION x y +// 6 MOUSE_BUTTON down/up buttonnum +// 7 MOUSE_WHEEL wheel + +WAJIC_WITH_INIT( +( + var evts = [], evtcursor = 0, dpr = window.devicePixelRatio||1, canvas, aspect_ratio, fullscreen; + var update_canvas_size = (w)=> + { + var h = (w *= dpr)/aspect_ratio|0; + if (w<32 || h<32 || (w == canvas.width && h == canvas.height) || fullscreen) return; + canvas.width = w; + canvas.height = h; + evts.push(1, canvas.width, canvas.height); + }; + var alias = (el, a, b, c)=> + { + return el[a+c] || el['moz'+b+c] || el['webkit'+b+c] || el['ms'+b+c]; + }; +), +void, app_js_setup_canvas, (int* out_width, int* out_height), +{ + canvas = WA.canvas; + if (!canvas.height) { canvas.width = 1024; canvas.height = 576; } + MU32[out_width>>2] = canvas.clientWidth; + MU32[out_height>>2] = canvas.clientHeight; + aspect_ratio = canvas.width/canvas.height; + + var cancelEvent = (e)=>{ if (e.preventDefault) e.preventDefault(true); else if (e.stopPropagation) e.stopPropagation(true); else e.stopped = true; }; + var documtEvent = (t, f, a)=>{ document.addEventListener(t, f); if (!a) { documtEvent('moz'+t, f, 1); documtEvent('webkit'+t, f, 1); documtEvent('ms'+t, f, 1); } }; + var windowEvent = (t, f)=>{ window.addEventListener(t, f, true); }; + var canvasEvent = (t, f)=>{ canvas.addEventListener(t, f, {capture:true,passive:false}); }; + windowEvent('resize', ()=>{ update_canvas_size(canvas.clientWidth); }); + windowEvent('focus', ()=>{ evts.push(2, 1, 0); }); + windowEvent('blur', ()=>{ evts.push(2, (fullscreen?1:0), 0); }); + windowEvent('keydown', (e)=> + { + evts.push(3, 1, e.keyCode); + if (e.key.length == 1 && e.key.charCodeAt() < 128 && !e.ctrlKey) evts.push(4, e.key.charCodeAt(), 0); + cancelEvent(e); + }); + windowEvent('keyup', (e)=> + { + evts.push(3, 0, e.keyCode); + cancelEvent(e); + }); + canvasEvent('mousemove', (e)=> + { + evts.push(5, + (e.offsetX * canvas.width / canvas.clientWidth )|0, + (e.offsetY * canvas.height / canvas.clientHeight)|0); + cancelEvent(e); + }); + var buttons = 0; + canvasEvent('mousedown', (e)=> + { + var btn = (1< + { + var btn = (1<{ evts.push(7, e.deltaY); cancelEvent(e); }); + documtEvent('fullscreenchange', ()=> + { + fullscreen = alias(document,'f','F','ullscreenElement') || alias(document,'f','F','ullScreenElement'); + if (fullscreen) + { + canvas.orgS = canvas.style.cssText; + canvas.orgW = canvas.clientWidth; + canvas.style.cssText = 'background:black'; + canvas.height = screen.height * dpr; + canvas.width = screen.width * dpr; + evts.push(1, canvas.width, canvas.height); + } + else if (canvas.orgS) + { + canvas.style.cssText = canvas.orgS; + update_canvas_size(canvas.orgW); + } + }); + WA.SetFullscreen = (f)=> + { + if (!f == !fullscreen) return; + var el = (f ? WA.canvas : document); + var fn = (f ? (alias(el,'r','R','equestFullscreen') || alias(el,'r','R','equestFullScreen')) : (alias(el,'e','E','xitFullscreen') || alias(el,'c','C','ancelFullScreen'))); + if (fn) fn.apply(el, []); + }; +}) + +WAJIC(void, app_js_screenmode, (int fullscreen), +{ + WA.SetFullscreen(fullscreen); +}) + +WAJIC(void, app_js_set_aspect_ratio, (int* width, int* height), +{ + var new_aspect_ratio = MU32[width>>2]/MU32[height>>2]; + if (Math.abs(new_aspect_ratio - aspect_ratio) > 0.01) + { + aspect_ratio = new_aspect_ratio; + update_canvas_size(canvas.clientWidth); + } + MU32[width>>2] = canvas.width; + MU32[height>>2] = canvas.height; +}) + +WAJIC(int, app_js_get_event, (int evt[3]), +{ + if (evtcursor >= evts.length) + { + evts.length = evtcursor = 0; + return 0; + } + MU32[(evt>>2)+0] = evts[evtcursor++]; + MU32[(evt>>2)+1] = evts[evtcursor++]; + MU32[(evt>>2)+2] = evts[evtcursor++]; + return 1; +}) + + +int app_run( int (*app_proc)( app_t*, void* ), void* user_data, void* memctx, void* logctx, void* fatalctx ) + { + app_t* app = (app_t*) APP_MALLOC( memctx, sizeof( app_t ) ); + memset( app, 0, (int)sizeof( app_t ) ); + app->memctx = memctx; + app->logctx = logctx; + app->fatalctx = fatalctx; + app->interpolation = APP_INTERPOLATION_LINEAR; + + app->gl.CreateShader = glCreateShader; + app->gl.ShaderSource = glShaderSource; + app->gl.CompileShader = glCompileShader; + app->gl.GetShaderiv = glGetShaderiv; + app->gl.CreateProgram = glCreateProgram; + app->gl.AttachShader = glAttachShader; + app->gl.BindAttribLocation = glBindAttribLocation; + app->gl.LinkProgram = glLinkProgram; + app->gl.GetProgramiv = glGetProgramiv; + app->gl.GenBuffers = glGenBuffers; + app->gl.BindBuffer = glBindBuffer; + app->gl.EnableVertexAttribArray = glEnableVertexAttribArray; + app->gl.VertexAttribPointer = glVertexAttribPointer; + app->gl.GenTextures = glGenTextures; + app->gl.Enable = glEnable; + app->gl.ActiveTexture = glActiveTexture; + app->gl.BindTexture = glBindTexture; + app->gl.TexParameteri = glTexParameteri; + app->gl.DeleteBuffers = glDeleteBuffers; + app->gl.DeleteTextures = glDeleteTextures; + app->gl.BufferData = glBufferData; + app->gl.UseProgram = glUseProgram; + app->gl.Uniform1i = glUniform1i; + app->gl.Uniform3f = glUniform3f; + app->gl.GetUniformLocation = glGetUniformLocation; + app->gl.TexImage2D = glTexImage2D; + app->gl.ClearColor = glClearColor; + app->gl.Clear = glClear; + app->gl.DrawArrays = glDrawArrays; + app->gl.Viewport = glViewport; + app->gl.DeleteShader = glDeleteShader; + app->gl.DeleteProgram = glDeleteProgram; + #ifdef APP_REPORT_SHADER_ERRORS + app->gl.GetShaderInfoLog = glGetShaderInfoLog; + #endif + + int result = 0xff; + + app_js_setup_canvas( &app->gl.window_width, &app->gl.window_height ); + glSetupCanvasContext( 1, 0, 0, 0 ); + glViewport( 0, 0, app->gl.window_width, app->gl.window_height ); + + app->has_focus = 1; + app->volume = 256; + + int glres = app_internal_opengl_init( app, &app->gl, app->interpolation, app->gl.window_width, app->gl.window_height ); + if( !glres ) + { + app_fatal_error( app, "OpenGL init fail" ); + goto init_failed; + } + WaCoroInitNew( NULL, NULL, NULL, 0 ); + result = app_proc( app, user_data ); + +init_failed: + + APP_FREE( memctx, app ); + return result; + } + + +static void app_internal_add_input_event( app_t* app, app_input_event_t* event ) + { + if( app->has_focus ) + { + if( app->input_count < sizeof( app->input_events ) / sizeof( *app->input_events ) ) + app->input_events[ app->input_count++ ] = *event; + } + } + + +static app_key_t app_internal_scancode_to_appkey( app_t* app, int scancode ) + { + static const app_key_t map[] = { + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_CANCEL, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_HELP, APP_KEY_INVALID, APP_KEY_BACK, APP_KEY_TAB, + APP_KEY_RETURN, APP_KEY_INVALID, APP_KEY_CLEAR, APP_KEY_RETURN, APP_KEY_RETURN, APP_KEY_INVALID, APP_KEY_LSHIFT, APP_KEY_LCONTROL, APP_KEY_LMENU, APP_KEY_PAUSE, + APP_KEY_CAPITAL, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_HANJA, APP_KEY_INVALID, APP_KEY_ESCAPE, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_MODECHANGE, APP_KEY_SPACE, APP_KEY_PRIOR, APP_KEY_NEXT, APP_KEY_END, APP_KEY_HOME, APP_KEY_LEFT, APP_KEY_UP, APP_KEY_RIGHT, + APP_KEY_DOWN, APP_KEY_SELECT, APP_KEY_SNAPSHOT, APP_KEY_EXEC, APP_KEY_SNAPSHOT, APP_KEY_INSERT, APP_KEY_DELETE, APP_KEY_HELP, APP_KEY_0, APP_KEY_1, + APP_KEY_2, APP_KEY_3, APP_KEY_4, APP_KEY_5, APP_KEY_6, APP_KEY_7, APP_KEY_8, APP_KEY_9, APP_KEY_INVALID, APP_KEY_OEM_1, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_A, APP_KEY_B, APP_KEY_C, APP_KEY_D, APP_KEY_E, + APP_KEY_F, APP_KEY_G, APP_KEY_H, APP_KEY_I, APP_KEY_J, APP_KEY_K, APP_KEY_L, APP_KEY_M, APP_KEY_N, APP_KEY_O, + APP_KEY_P, APP_KEY_Q, APP_KEY_R, APP_KEY_S, APP_KEY_T, APP_KEY_U, APP_KEY_V, APP_KEY_W, APP_KEY_X, APP_KEY_Y, + APP_KEY_Z, APP_KEY_LWIN, APP_KEY_RWIN, APP_KEY_APPS, APP_KEY_INVALID, APP_KEY_SLEEP, APP_KEY_NUMPAD0, APP_KEY_NUMPAD1, APP_KEY_NUMPAD2, APP_KEY_NUMPAD3, + APP_KEY_NUMPAD4, APP_KEY_NUMPAD5, APP_KEY_NUMPAD6, APP_KEY_NUMPAD7, APP_KEY_NUMPAD8, APP_KEY_NUMPAD9, APP_KEY_MULTIPLY, APP_KEY_ADD, APP_KEY_OEM_COMMA, APP_KEY_SUBTRACT, + APP_KEY_INVALID, APP_KEY_DIVIDE, APP_KEY_F1, APP_KEY_F2, APP_KEY_F3, APP_KEY_F4, APP_KEY_F5, APP_KEY_F6, APP_KEY_F7, APP_KEY_F8, + APP_KEY_F9, APP_KEY_F10, APP_KEY_F11, APP_KEY_F12, APP_KEY_F13, APP_KEY_F14, APP_KEY_F15, APP_KEY_F16, APP_KEY_F17, APP_KEY_F18, + APP_KEY_F19, APP_KEY_F20, APP_KEY_F21, APP_KEY_F22, APP_KEY_F23, APP_KEY_F24, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_NUMLOCK, APP_KEY_SCROLL, APP_KEY_RETURN, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_LSHIFT, APP_KEY_RSHIFT, APP_KEY_LCONTROL, APP_KEY_RCONTROL, APP_KEY_LMENU, APP_KEY_RMENU, APP_KEY_BROWSER_BACK, APP_KEY_BROWSER_FORWARD, APP_KEY_BROWSER_REFRESH, APP_KEY_BROWSER_STOP, + APP_KEY_BROWSER_SEARCH, APP_KEY_BROWSER_FAVORITES, APP_KEY_BROWSER_HOME, APP_KEY_VOLUME_MUTE, APP_KEY_VOLUME_DOWN, APP_KEY_VOLUME_UP, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_RETURN, APP_KEY_INVALID, + APP_KEY_LAUNCH_MAIL, APP_KEY_LAUNCH_MEDIA_SELECT, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_OEM_1, APP_KEY_INVALID, APP_KEY_OEM_COMMA, APP_KEY_OEM_MINUS, + APP_KEY_OEM_PERIOD, APP_KEY_OEM_2, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_OEM_4, + APP_KEY_OEM_5, APP_KEY_OEM_6, APP_KEY_OEM_7, APP_KEY_INVALID, APP_KEY_LWIN, APP_KEY_RMENU, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_HANGUL, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_CRSEL, APP_KEY_EXSEL, APP_KEY_INVALID, + APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_INVALID, APP_KEY_CLEAR, APP_KEY_INVALID, }; + + if( scancode < 0 || scancode >= sizeof( map ) / sizeof( *map ) ) return APP_KEY_INVALID; + return (app_key_t) map[ scancode ]; + } + + +app_state_t app_yield( app_t* app ) + { + int evt[3]; + app_input_event_t input_event; + app_key_t key; + while (app_js_get_event( evt )) + { + switch (evt[0]) + { + // EVENT ID ARG 1 ARG 2 + case 1: // WINDOW_SIZE_CHANGED w h + if( evt[1] != app->gl.window_width || evt[2] != app->gl.window_height ) + { + app_internal_opengl_resize( &app->gl, evt[1], evt[2] ); + } + break; + case 2: // WINDOW_FOCUS gained/lost + app->has_focus = evt[1]; + break; + case 3: // KEY down/up scancode + input_event.type = (evt[1] ? APP_INPUT_KEY_DOWN : APP_INPUT_KEY_UP); + key = app_internal_scancode_to_appkey( app, evt[2] ); + if( key == APP_KEY_LCONTROL || key == APP_KEY_RCONTROL ) + { + input_event.data.key = APP_KEY_CONTROL; + app_internal_add_input_event( app, &input_event ); + } + else if( key == APP_KEY_LSHIFT || key == APP_KEY_RSHIFT ) + { + input_event.data.key = APP_KEY_SHIFT; + app_internal_add_input_event( app, &input_event ); + } + else if( key == APP_KEY_LMENU || key == APP_KEY_RMENU ) + { + input_event.data.key = APP_KEY_MENU; + app_internal_add_input_event( app, &input_event ); + } + input_event.data.key = key; + app_internal_add_input_event( app, &input_event ); + break; + case 4: // CHAR charcode + input_event.type = APP_INPUT_CHAR; + input_event.data.char_code = (char) evt[1]; + app_internal_add_input_event( app, &input_event ); + break; + case 5: // MOUSE_MOTION x y + input_event.type = APP_INPUT_MOUSE_MOVE; + app->pointer_x = input_event.data.mouse_pos.x = evt[1]; + app->pointer_y = input_event.data.mouse_pos.y = evt[2]; + app_internal_add_input_event( app, &input_event ); + break; + case 6: // MOUSE_BUTTON down/up buttonnum + input_event.type = (evt[1] ? APP_INPUT_KEY_DOWN : APP_INPUT_KEY_UP); + if( evt[2] == 0 ) + input_event.data.key = APP_KEY_LBUTTON; + else if( evt[2] == 1 ) + input_event.data.key = APP_KEY_RBUTTON; + else if( evt[2] == 2 ) + input_event.data.key = APP_KEY_MBUTTON; + else if( evt[2] == 3 ) + input_event.data.key = APP_KEY_XBUTTON1; + else if( evt[2] == 4 ) + input_event.data.key = APP_KEY_XBUTTON2; + else + break; + app_internal_add_input_event( app, &input_event ); + break; + case 7: // MOUSE_WHEEL wheel + { + float const microsoft_mouse_wheel_constant = 120.0f; + float wheel_delta = ( (float) evt[1] ) / microsoft_mouse_wheel_constant; + if( app->input_count > 0 && app->input_events[ app->input_count - 1 ].type == APP_INPUT_SCROLL_WHEEL ) + { + app_input_event_t* event = &app->input_events[ app->input_count - 1 ]; + event->data.wheel_delta += wheel_delta; + } + else + { + input_event.type = APP_INPUT_SCROLL_WHEEL; + input_event.data.wheel_delta = wheel_delta; + app_internal_add_input_event( app, &input_event ); + } + } break; + } + } + return APP_STATE_NORMAL; + } + + +void app_cancel_exit( app_t* app ) { /* NOT IMPLEMENTED */ } +void app_title( app_t* app, char const* title ) { /* NOT IMPLEMENTED */ } +char const* app_cmdline( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_filename( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_userdata( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } +char const* app_appdata( app_t* app ) { /* NOT IMPLEMENTED */ return NULL; } + + +WAJIC_WITH_INIT( +( + var start_time = Date.now(); +), +APP_U32, app_js_get_ticks, (), +{ + return Date.now() - start_time; +}) + +APP_U64 app_time_count( app_t* app ) + { + return (APP_U64)app_js_get_ticks()*1000; + } + + +APP_U64 app_time_freq( app_t* app ) + { + return (APP_U64)1000*1000; + } + + +void app_log( app_t* app, app_log_level_t level, char const* message ) + { + printf("[APP] [%d] %s\n", (int)level, message); + } + + +void app_fatal_error( app_t* app, char const* message ) + { + APP_FATAL_ERROR( app->fatalctx, message ); + } + + +void app_pointer( app_t* app, int width, int height, APP_U32* pixels_abgr, int hotspot_x, int hotspot_y ) { /* NOT IMPLEMENTED */ } +void app_pointer_default( app_t* app, int* width, int* height, APP_U32* pixels_abgr, int* hotspot_x, int* hotspot_y ) { /* NOT IMPLEMENTED */ } +void app_pointer_pos( app_t* app, int x, int y ) { /* NOT IMPLEMENTED */ } + + +int app_pointer_x( app_t* app ) + { + return app->pointer_x; + } + + +int app_pointer_y( app_t* app ) + { + return app->pointer_y; + } + + +void app_pointer_limit( app_t* app, int x, int y, int width, int height ) { /* NOT IMPLEMENTED */ } +void app_pointer_limit_off( app_t* app ) { /* NOT IMPLEMENTED */ } + +void app_interpolation( app_t* app, app_interpolation_t interpolation ) + { + if( interpolation == app->interpolation ) return; + app->interpolation = interpolation; + + app_internal_opengl_interpolation( &app->gl, interpolation ); + } + + +void app_screenmode( app_t* app, app_screenmode_t screenmode ) + { + app_js_screenmode( (int) ( screenmode == APP_SCREENMODE_FULLSCREEN ) ); + } + + +void app_window_size( app_t* app, int width, int height ) + { + // view size is controlled by the browser, we only control the display aspect ratio + app_js_set_aspect_ratio( &width, &height ); + if( width != app->gl.window_width || height != app->gl.window_height ) + { + app_internal_opengl_resize( &app->gl, width, height ); + } + } + + +int app_window_width( app_t* app ) + { + return app->gl.window_width; + } + + +int app_window_height( app_t* app ) + { + return app->gl.window_height; + } + + +void app_window_pos( app_t* app, int x, int y ) { /* NOT IMPLEMENTED */ } + + +int app_window_x( app_t* app ) + { + return 0; + } + + +int app_window_y( app_t* app ) + { + return 0; + } + + +app_displays_t app_displays( app_t* app ) + { + // Fixed display for web + static app_display_t display; + display.id[0] = '\0'; + display.x = 0; + display.y = 0; + display.width = 1920; + display.height = 1080; + app_displays_t displays; + displays.count = 1; + displays.displays = &display; + return displays; + } + + +WAJIC_WITH_INIT( +( + var audio_ctx, audio_done = 0, audio_latency = 2048, audio_bufs = [], audio_bufidx = 0, audio_miss = 0; + var start_audio = ()=> + { + if (audio_ctx.state == 'running') return 1; + audio_done = audio_ctx.currentTime; + audio_ctx.resume(); + }; + var set_start_audio_event = (name)=>document.addEventListener(name, start_audio, {once:true}); +), +int, app_js_audio_needed, (bool has_focus), +{ + if (!audio_ctx) + { + if (audio_ctx === false) return 0; + try { (audio_ctx = new (alias(window,"","",'AudioContext'))()).createBuffer(1,1,44100).getChannelData(0); } catch (e) { } + if (!audio_ctx) { audio_ctx = false; WA.print('Warning: WebAudio not supported\n'); return 0; } + for (var i = 0; i != 10; i++) audio_bufs[i] = audio_ctx.createBuffer(2, 2048, 44100); + if (!start_audio()) { set_start_audio_event('click'); set_start_audio_event('touchstart'); set_start_audio_event('keydown'); } + } + if (!start_audio() && !start_audio()) return 0; + var ct = audio_ctx.currentTime; + if (audio_done < ct) + { + if (has_focus && (audio_miss += 2) > 7) + { + audio_latency += 2048; + audio_miss = 0; + } + audio_done = ct; + } + else if (audio_miss > 1) audio_miss--; + return ((ct - audio_done) * 44100 + .5 + audio_latency * (has_focus ? 1 : 2) + 2047)>>11; +}) + +WAJIC(int, app_js_audio_push, (APP_S16* sample_pairs, int volume), +{ + sample_pairs = new Int16Array(MU8.buffer).subarray(sample_pairs>>1); + var buf = audio_bufs[audio_bufidx = ((audio_bufidx + 1) % 10)]; + var left = buf.getChannelData(0), right = buf.getChannelData(1); + var f = (1 / 32768) * (volume / 255); + for (var i = 0; i != 2048; i++) + { + left[i] = sample_pairs[i*2] * f; + right[i] = sample_pairs[i*2+1] * f; + } + var source = audio_ctx.createBufferSource(); + source.connect(audio_ctx.destination); + source.buffer = buf; + source[source.start ? 'start' : 'noteOn'](0.005+audio_done); + audio_done += 2048/44100; +}) + + +void app_present( app_t* app, APP_U32 const* pixels_xbgr, int width, int height, APP_U32 mod_xbgr, APP_U32 border_xbgr ) + { + if( pixels_xbgr ) app_internal_opengl_present( &app->gl, pixels_xbgr, width, height, mod_xbgr, border_xbgr ); + for( int needed = app_js_audio_needed(app->has_focus); app->sound_callback && needed--;) + { + app->sound_callback(app->sound_buffer, 2048, app->sound_user_data); + app_js_audio_push(app->sound_buffer, app->volume); + } + if( app->has_focus ) + WaCoroWaitAnimFrame(); + else + WaCoroSleep(50); + } + + +void app_sound( app_t* app, int sample_pairs_count, void (*sound_callback)( APP_S16* sample_pairs, int sample_pairs_count, void* user_data ), void* user_data ) + { + app->sound_callback = sound_callback; + app->sound_user_data = user_data; + if( sound_callback && !app->sound_buffer ) + app->sound_buffer = (APP_S16*) APP_MALLOC( app->memctx, sizeof(APP_S16) * 2048 * 2 ); + else if( !sound_callback && app->sound_buffer ) + APP_FREE( app->memctx, app->sound_buffer ); + } + + +void app_sound_volume( app_t* app, float volume ) + { + int v = (int) ( volume * 256.0f ); + app->volume = v < 0 ? 0 : v > 256 ? 256 : v; + } + + +app_input_t app_input( app_t* app ) + { + app_input_t input; + input.events = app->input_events; + input.count = app->input_count; + app->input_count = 0; + return input; + } + + +void app_coordinates_window_to_bitmap( app_t* app, int width, int height, int* x, int* y ) + { + if( width == 0 || height == 0 ) return; + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = app->gl.window_width / (float) width; + float vscale = app->gl.window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( app->gl.window_width - pixel_scale * width ) / 2.0f; + float vborder = ( app->gl.window_height - pixel_scale * height ) / 2.0f; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = app->gl.window_width / width; + int vscale = app->gl.window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( app->gl.window_width - pixel_scale * width ) / 2; + int vborder = ( app->gl.window_height - pixel_scale * height ) / 2; + *x -= (int)( hborder ); + *y -= (int)( vborder ); + *x = (int)( *x / pixel_scale ); + *y = (int)( *y / pixel_scale ); + } + } + + +void app_coordinates_bitmap_to_window( app_t* app, int width, int height, int* x, int* y ) + { + if( app->interpolation == APP_INTERPOLATION_LINEAR ) + { + float hscale = app->gl.window_width / (float) width; + float vscale = app->gl.window_height / (float) height; + float pixel_scale = hscale < vscale ? hscale : vscale; + if( pixel_scale > 0.0f ) + { + float hborder = ( app->gl.window_width - pixel_scale * width ) / 2.0f; + float vborder = ( app->gl.window_height - pixel_scale * height ) / 2.0f; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + else + { + *x = 0; + *y = 0; + } + } + else + { + int hscale = app->gl.window_width / width; + int vscale = app->gl.window_height / height; + int pixel_scale = pixel_scale = hscale < vscale ? hscale : vscale; + pixel_scale = pixel_scale < 1 ? 1 : pixel_scale; + int hborder = ( app->gl.window_width - pixel_scale * width ) / 2; + int vborder = ( app->gl.window_height - pixel_scale * height ) / 2; + *x = (int)( *x * pixel_scale ); + *y = (int)( *y * pixel_scale ); + *x += (int)( hborder ); + *y += (int)( vborder ); + } + } + + +#else + #error Undefined platform. Define APP_WINDOWS, APP_SDL, APP_WASM or APP_NULL. +#endif + + +#endif /* APP_IMPLEMENTATION */ + +/* +revision history: + 0.4 pointer x/y, callback for sound, modifier keys fix, gl binding fix, cursor fix + 0.3 added API documentation + 0.2 first publicly released version +*/ + +/* +------------------------------------------------------------------------------ + +This software is available under 2 licenses - you may choose the one you like. + +------------------------------------------------------------------------------ + +ALTERNATIVE A - MIT License + +Copyright (c) 2016 Mattias Gustavsson + +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. + +------------------------------------------------------------------------------ + +ALTERNATIVE B - Public Domain (www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +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 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. + +------------------------------------------------------------------------------ +*/ diff --git a/pc/include/flight.h b/pc/include/flight.h new file mode 100644 index 0000000..7d26957 --- /dev/null +++ b/pc/include/flight.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com + * + * 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 FLIGHT_H +#define FLIGHT_H + + +#include +#include +#include +#include + + +typedef unsigned char byte; + + +typedef struct airplaneS { + int8_t aileron; // -15 to 15 + int8_t elevator; // -15 to 15 + int8_t rudder; // -15 to 15 + int8_t throttle; // -15 to 15 + bool ignition; + bool engine; + int16_t rpm; + float hSpeed; + float vSpeed; + float deltaZ; + float efAOF; + float climbRate; + bool airborne; + bool stall; + bool brake; + int16_t x; + int16_t y; + int16_t z; + double pitch; // 0 to 255 + double yaw; // 0 to 255 + double roll; // 0 to 255 + // Flight model stuff below here. + float tmpX; + float tmpY; + float tmpZ; + float newX; + float newY; + float newZ; + float iSpeed; + float lSpeed; + float hAccel; + float lVeloc; + float gVeloc; + float AOA; + float torque; + float torque2; + uint16_t loopTime; + // Used to be static. Need preserved. + double dPitch; + double dYaw; + double dRoll; + float collectX; + float collectY; + float collectZ; +} airplaneT; + + +extern airplaneT _plane; + + +void resetAircraft(void); +void updateAircraft(void); + + +#endif // FLIGHT_H diff --git a/pc/include/vrEmu6502.h b/pc/include/vrEmu6502.h new file mode 100644 index 0000000..3881155 --- /dev/null +++ b/pc/include/vrEmu6502.h @@ -0,0 +1,283 @@ +/* + * Troy's 6502 Emulator + * + * Copyright (c) 2022 Troy Schrapel + * + * This code is licensed under the MIT license + * + * https://github.com/visrealm/vrEmu6502 + * + */ + +#ifndef _VR_EMU_6502_H_ +#define _VR_EMU_6502_H_ + +/* ------------------------------------------------------------------ + * LINKAGE MODES: + * + * Default (nothing defined): When your executable is using vrEmuTms9918 as a DLL + * VR_EMU_6502_COMPILING_DLL: When compiling vrEmuTms9918 as a DLL + * VR_EMU_6502_STATIC: When linking vrEmu6502 statically in your executable + */ + +#if __EMSCRIPTEN__ + #include +#ifdef __cplusplus +#define VR_EMU_6502_DLLEXPORT EMSCRIPTEN_KEEPALIVE extern "C" +#else +#define VR_EMU_6502_DLLEXPORT EMSCRIPTEN_KEEPALIVE extern +#endif + +#elif VR_EMU_6502_COMPILING_DLL + #define VR_EMU_6502_DLLEXPORT __declspec(dllexport) +#elif defined WIN32 && !defined VR_EMU_6502_STATIC + #define VR_EMU_6502_DLLEXPORT __declspec(dllimport) +#else + #ifdef __cplusplus + #define VR_EMU_6502_DLLEXPORT extern "C" + #else + #define VR_EMU_6502_DLLEXPORT extern + #endif +#endif + + +#include +#include + +/* ------------------------------------------------------------------ + * PRIVATE DATA STRUCTURE + */ +struct vrEmu6502_s; +typedef struct vrEmu6502_s VrEmu6502; + +/* ------------------------------------------------------------------ + * CONSTANTS + */ +typedef enum +{ + CPU_6502, /* NMOS 6502/6510 with documented opcodes only */ + CPU_6502U, /* NMOS 6502/6510 with undocumented opcodes */ + CPU_65C02, /* Standard CMOS 65C02 */ + CPU_W65C02, /* Western Design Centre CMOS 65C02 */ + CPU_R65C02, /* Rockwell CMOS 65C02 */ + CPU_6510 = CPU_6502U, + CPU_8500 = CPU_6510, + CPU_8502 = CPU_8500, + CPU_7501 = CPU_6502, + CPU_8501 = CPU_6502 +} vrEmu6502Model; + +typedef enum +{ + IntRequested, + IntCleared, + IntLow = IntRequested, + IntHigh = IntCleared +} vrEmu6502Interrupt; + +typedef enum +{ + BitC = 0, + BitZ, + BitI, + BitD, + BitB, + BitU, + BitV, + BitN +} vrEmu6502FlagBit; + +typedef enum +{ + FlagC = 0x01 << BitC, /* carry */ + FlagZ = 0x01 << BitZ, /* zero */ + FlagI = 0x01 << BitI, /* interrupt */ + FlagD = 0x01 << BitD, /* decimal */ + FlagB = 0x01 << BitB, /* brk */ + FlagU = 0x01 << BitU, /* undefined */ + FlagV = 0x01 << BitV, /* oVerflow */ + FlagN = 0x01 << BitN /* negative */ +} vrEmu6502Flag; + + +typedef enum +{ + AddrModeAbs, + AddrModeAbsX, + AddrModeAbsY, + AddrModeAcc, + AddrModeImm, + AddrModeImp, + AddrModeAbsInd, + AddrModeAbsIndX, + AddrModeIndX, + AddrModeIndY, + AddrModeRel, + AddrModeZP, + AddrModeZPI, + AddrModeZPX, + AddrModeZPY, +} vrEmu6502AddrMode; + +/* ------------------------------------------------------------------ + * PUBLIC INTERFACE + */ + + /* + * memory write function pointer + */ +typedef void(*vrEmu6502MemWrite)(uint16_t addr, uint8_t val); + +/* + * memory read function pointer + * + * isDbg: some devices change their state when read + * (eg. TMS9918 increments its address pointer) + * this flag will be false when the cpu is running + * however it can be true when querying the memory + * for other purposes. devices should NOT change state + * when isDbg is true. + * + */ +typedef uint8_t(*vrEmu6502MemRead)(uint16_t addr, bool isDbg); + + +/* + * create a new 6502 + */ +VR_EMU_6502_DLLEXPORT VrEmu6502* vrEmu6502New( + vrEmu6502Model model, + vrEmu6502MemRead readFn, + vrEmu6502MemWrite writeFn); + +/* ------------------------------------------------------------------ + * + * destroy a 6502 + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502Destroy(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * reset the 6502 + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502Reset(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * a single clock tick + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502Tick(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * a single instruction cycle + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502InstCycle(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * returns a pointer to the interrupt signal. + * externally, you can modify it to set/reset the interrupt signal + */ +VR_EMU_6502_DLLEXPORT vrEmu6502Interrupt *vrEmu6502Int(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * returns a pointer to the nmi signal. + * externally, you can modify it to set/reset the interrupt signal + */ +VR_EMU_6502_DLLEXPORT vrEmu6502Interrupt *vrEmu6502Nmi(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the program counter + */ +VR_EMU_6502_DLLEXPORT uint16_t vrEmu6502GetPC(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * set the program counter + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502SetPC(VrEmu6502* vr6502, uint16_t pc); + +/* ------------------------------------------------------------------ + * + * return the accumulator + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetAcc(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the x index register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetX(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the y index register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetY(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the processor status register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetStatus(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the stack pointer register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetStackPointer(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the current opcode + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetCurrentOpcode(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the current opcode address + */ +VR_EMU_6502_DLLEXPORT uint16_t vrEmu6502GetCurrentOpcodeAddr(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the next opcode + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetNextOpcode(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the opcode cycle + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetOpcodeCycle(VrEmu6502* vr6502); + +/* ------------------------------------------------------------------ + * + * return the opcode mnemonic string + */ +VR_EMU_6502_DLLEXPORT +const char* vrEmu6502OpcodeToMnemonicStr(VrEmu6502* vr6502, uint8_t opcode); + +/* ------------------------------------------------------------------ + * + * return the opcode address mode + */ +VR_EMU_6502_DLLEXPORT +vrEmu6502AddrMode vrEmu6502GetOpcodeAddrMode(VrEmu6502* vr6502, uint8_t opcode); + +/* ------------------------------------------------------------------ + * + * get disassembled instruction as a string. returns next instruction address + */ +VR_EMU_6502_DLLEXPORT +uint16_t vrEmu6502DisassembleInstruction( + VrEmu6502* vr6502, uint16_t addr, + int bufferSize, char *buffer, + uint16_t *refAddr, const char* const labelMap[0x10000]); + + + +#endif // _VR_EMU_6502_CORE_H_ diff --git a/pc/src/flight.c b/pc/src/flight.c new file mode 100644 index 0000000..cd765d7 --- /dev/null +++ b/pc/src/flight.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com + * + * 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 "flight.h" + + +airplaneT _plane; + + +#define SEGMENT_MATH + + +#define PI 3.1415926 + +#define Rads(d) (((d) < 0 ? (d) + 360 : (d)) * (PI / 180)) +#define Degs(r) ((r) * (180 / PI)) + +// https://www.reddit.com/r/programming/comments/3e7ghi/discrete_arctan_in_6502/ +#define ATAN_SPLINE_C0 (double)(-0.14380550980765507115) /* 3pi/4 - 5/2 */ +#define ATAN_SPLINE_C1 (double)(-0.07079632679489661923) /* 3/2 - pi/2 */ +double ourAtan(double x){ + if (x >= 0) { + if ( x<= 1) { + return x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x; + } else { + x = 1 / x; + return PI / 2 - (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x); + } + } else { + if (x >= -1) { + return x - (ATAN_SPLINE_C0 - ATAN_SPLINE_C1 * x) * x * x; + } else { + x = -1 / x; + return (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x) - PI / 2; + } + } +} + + +float cosD(float d) { + return cos(Rads(d)); +} + + +float sinD(float d) { + return sin(Rads(d)); +} + + +#define SEGMENT_FLIGHT_MODEL_1 + + +// This flight model is basically the one from "Build Your Own Flight Sim +// in C++" by Michael Radtke and Chris Lampton. +void lightPlane(void) { + // Flight model. Power Dynamics. + if (_plane.ignition) { + // Start engine. + if (!_plane.engine) _plane.engine = true; + // Adjust RPM. + if (_plane.rpm < (375 + (_plane.throttle * 117))) _plane.rpm += _plane.loopTime * 0.5; //***TODO*** T + if (_plane.rpm > (375 + (_plane.throttle * 117))) _plane.rpm -= _plane.loopTime * 0.5; + } else { + // Stop engine. + if (_plane.engine) _plane.engine = false; + // Run down engine. + if (_plane.rpm > 0) _plane.rpm -= (int16_t)(_plane.loopTime / 2); //***TODO*** This will never work. + if (_plane.rpm < 0) _plane.rpm = 0; + } + + // Flight model. Flight Dynamics. + // Calculate speed from RPM. + _plane.iSpeed = _plane.rpm / 17.5; + // Modify speed by pitch. + _plane.iSpeed += (_plane.pitch * 1.5); + // Horizontal acceleration - thrust. + _plane.hAccel = ((_plane.rpm * (_plane.iSpeed - _plane.hSpeed)) / 10000); + _plane.hAccel /= 1000; + _plane.hAccel *= _plane.loopTime; + if (_plane.brake && !_plane.airborne) { + // Handle brakes. + if (_plane.hSpeed > 0) { + _plane.hSpeed -= 1; + } else { + _plane.hSpeed = 0; + } + } else { + // Accelerate normally. + _plane.hSpeed += _plane.hAccel; + } + // Force speed to range -1..1. + _plane.lSpeed = (_plane.hSpeed / 65) - 1; + if (_plane.lSpeed > 1) _plane.lSpeed = 1; + // Lift curve. + _plane.lVeloc = Degs(ourAtan(_plane.lSpeed)); + // Force lift to range 0..90. + _plane.lVeloc += 45; + // Shift range to 0..-17. + _plane.lVeloc /= 5.29; + // Multiply by pitch modifier. + _plane.lVeloc *= (-(_plane.pitch * .157) + 1); + // Time slice. + _plane.lVeloc /= 1000; + _plane.lVeloc *= _plane.loopTime; + // Gravity. + _plane.gVeloc = _plane.loopTime * (-16.0 / 10000); // -16.0 is ft/sec for gravity. + // Sum vertical velocity. + _plane.vSpeed = _plane.gVeloc + _plane.lVeloc; + // No vertical speed if we're on the ground. + if (!_plane.airborne && (_plane.vSpeed < 0)) _plane.vSpeed = 0; + // Save climb rate in ft/min. + _plane.climbRate = _plane.vSpeed / _plane.loopTime; + _plane.climbRate *= 60000L; + // Expand to ft/hr. + _plane.deltaZ = _plane.hSpeed * 5280; + // Get ft/ms. + _plane.deltaZ /= 3600000L; + _plane.deltaZ *= _plane.loopTime; + // Find effective angle of flight. + if (_plane.deltaZ) { + _plane.efAOF = -(ourAtan(_plane.vSpeed / _plane.deltaZ)); + } else { + _plane.efAOF = -(ourAtan(_plane.vSpeed)); + } + // Convert to degrees. + _plane.AOA = Degs(_plane.efAOF); + // Handle stalling. + if (((_plane.pitch < _plane.AOA) && (_plane.AOA < 0)) && (_plane.hSpeed < 40)) { + if ((_plane.pitch - _plane.AOA) < -20) _plane.stall = true; + } + if (_plane.stall) { + if (_plane.pitch > 30) { + _plane.stall = false; + } else { + _plane.pitch++; + } + } +} + + +#define SEGMENT_FLIGHT_MODEL_2 + + +void lightPlane2(void) { + // Flight model. Inertial Damping. + if (_plane.dPitch) { + _plane.dPitch -= _plane.dPitch / 10; + if (((_plane.dPitch > 0) && (_plane.dPitch < 0.01)) || ((_plane.dPitch < 0) && (_plane.dPitch > -0.01))) _plane.dPitch = 0; + } + if (_plane.dYaw) { + _plane.dYaw -= _plane.dYaw / 10; + if (((_plane.dYaw > 0) && (_plane.dYaw < 0.01)) || ((_plane.dYaw < 0) && (_plane.dYaw > -0.01))) _plane.dYaw = 0; + } + if (_plane.dRoll) { + _plane.dRoll -= _plane.dRoll / 10; + if (((_plane.dRoll > 0) && (_plane.dRoll < 0.01)) || ((_plane.dRoll < 0) && (_plane.dRoll > -0.01))) _plane.dRoll = 0; + } + + // Flight model. Rate of Change. + if (_plane.airborne) { + if (_plane.aileron != 0) { + _plane.torque = ((_plane.hSpeed * _plane.aileron) / 10000); + if (_plane.dRoll != (_plane.torque * _plane.loopTime)) _plane.dRoll += _plane.torque * 6; + } + } + if (_plane.elevator != 0) { + _plane.torque = ((_plane.hSpeed * _plane.elevator) / 10000); + if ((!_plane.airborne) && (_plane.torque > 0)) _plane.torque = 0; //***FIX*** This is dumb. + if (_plane.dPitch != (_plane.torque * _plane.loopTime)) _plane.dPitch += _plane.torque * 1.5; + } + if (_plane.hSpeed) { + _plane.torque = 0.0; + if (_plane.rudder != 0) _plane.torque -= ((_plane.hSpeed * _plane.rudder) / 10000); + _plane.torque2 = 0.0; + if ((_plane.roll > 0) && (_plane.roll <= 90)) { //***FIX*** This is dumb. + _plane.torque2 = _plane.roll * 0.00050; + } else { + if ((_plane.roll < 0) && (_plane.roll >= -90)) { + _plane.torque2 = _plane.roll * 0.00050; + } + } + _plane.torque += _plane.torque2; + if (_plane.dYaw != (_plane.torque * _plane.loopTime)) _plane.dYaw += _plane.torque * 1.5; + } + + // Flight model. Apply Rotations. + // Transform pitch into components of yaw and pitch based on roll. + _plane.roll += _plane.dRoll; + _plane.yaw += _plane.dYaw; + _plane.pitch += (_plane.dPitch * cosD(_plane.roll)); + _plane.yaw += -(_plane.dPitch * sinD(_plane.roll)); + if (_plane.roll > 180) { + _plane.roll = -180 + (_plane.roll - 180); + } else { + if (_plane.roll < -180) _plane.roll = 180 + (_plane.roll + 180); + } + if (_plane.yaw > 180) { + _plane.yaw = -180 + (_plane.yaw - 180); + } else { + if (_plane.yaw < -180) _plane.yaw = 180 + (_plane.yaw + 180); + } + // Handle special case when aircraft pitch passes vertical. + if ((_plane.pitch > 90) || (_plane.pitch < -90)) { + if (_plane.roll >= 0) { + _plane.roll -= 180; + } else { + if (_plane.roll < 0) _plane.roll += 180; + } + if (_plane.yaw >= 0) { + _plane.yaw -= 180; + } else { + if (_plane.yaw < 0) _plane.yaw += 180; + } + if (_plane.pitch > 0) { + _plane.pitch = (180 - _plane.pitch); + } else { + if (_plane.pitch < 0) _plane.pitch = (-180 - _plane.pitch); + } + } + // Dampen everything out to 0 if they get close enough. + if ((_plane.pitch > -0.5) && (_plane.pitch < 0.5)) _plane.pitch = 0; + if ((_plane.roll > -0.5) && (_plane.roll < 0.5)) _plane.roll = 0; + if ((_plane.yaw > -0.5) && (_plane.yaw < 0.5)) _plane.yaw = 0; +} + + +#define SEGMENT_FLIGHT_MODEL_3 + + +void moveAircraft(void) { + // Calculate new aircraft position. Each coordinate is 1 foot in 3D space. + _plane.tmpX = 0; + _plane.tmpY = 0; + _plane.tmpZ = _plane.deltaZ; + + // Order of these points is significant. + // Rotate in Z. + _plane.newX = (_plane.tmpX * cosD(_plane.roll)) - (_plane.tmpY * sinD(_plane.roll)); + _plane.newY = (_plane.tmpX * sinD(_plane.roll)) + (_plane.tmpY * cosD(_plane.roll)); + _plane.tmpX = _plane.newX; + _plane.tmpY = _plane.newY; + // Rotate in X; + _plane.efAOF = Degs(_plane.efAOF); + _plane.newY = (_plane.tmpY * cosD(_plane.efAOF)) - (_plane.tmpZ * sinD(_plane.efAOF)); + _plane.newZ = (_plane.tmpY * sinD(_plane.efAOF)) + (_plane.tmpZ * cosD(_plane.efAOF)); + _plane.tmpY = _plane.newY; + _plane.tmpZ = _plane.newZ; + // Rotate in X; + _plane.newX = (_plane.tmpZ * sinD(_plane.yaw)) + (_plane.tmpX * cosD(_plane.yaw)); + _plane.newZ = (_plane.tmpZ * cosD(_plane.yaw)) - (_plane.tmpX * sinD(_plane.yaw)); + _plane.tmpX = _plane.newX; + _plane.tmpZ = _plane.newZ; + + // Translate rotated point back to where it should be + // relative to it's last position. + _plane.collectX += _plane.newX; + if ((_plane.collectX > 1) || (_plane.collectX < -1)) { + _plane.x -= _plane.collectX; + _plane.collectX = 0; + } + _plane.collectY += _plane.newY; + if ((_plane.collectY > 1) || (_plane.collectY < -1)) { + _plane.y -= _plane.collectY; + _plane.collectY = 0; + } + _plane.collectZ += _plane.newZ; + if ((_plane.collectY > 1) || (_plane.collectY < -1)) { + _plane.z += _plane.collectZ; + _plane.collectZ = 0; + } + + // Are we flying? + if ((!_plane.airborne) && (-_plane.y)) _plane.airborne = true; +} + + +void resetAircraft(void) { + // Initialize airplane. + memset(&_plane, 0, sizeof(airplaneT)); + + _plane.loopTime = 40; +} + + +#define SEGMENT_MAIN + + +void updateAircraft(void) { + // Do the actual flying! + lightPlane(); + lightPlane2(); + moveAircraft(); +} diff --git a/pc/src/main.c b/pc/src/main.c new file mode 100644 index 0000000..6dd7f95 --- /dev/null +++ b/pc/src/main.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com + * + * 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 "vrEmu6502.h" +#include "a23d2bin.h" +#include "a23d2.h" +#include "flight.h" + + +#define APP_IMPLEMENTATION +#define APP_SDL +#define APP_S16 int16_t +#define APP_U32 uint32_t +#define APP_U64 uint64_t +#include "app.h" + + +#define HEIGHT 240 +#define WIDTH 320 + + +#define JOY_UP 1 +#define JOY_DOWN 2 +#define JOY_LEFT 4 +#define JOY_RIGHT 8 +#define JOY_BUTTON_1 16 +#define JOY_BUTTON_2 32 +#define JOY_BUTTON_3 64 + + +typedef struct ArgsS { + int count; + char **args; +} ArgsT; + +typedef struct ColorS { + byte r; + byte g; + byte b; +} ColorT; + +// Foenix Stuff. +byte _gamepad; + +// Graphics Stuff. +byte _ega[16][3] = { + { 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0xaa }, + { 0x00, 0xaa, 0x00 }, + { 0x00, 0xaa, 0xaa }, + { 0xaa, 0x00, 0x00 }, + { 0xaa, 0x00, 0xaa }, + { 0xaa, 0x55, 0x00 }, + { 0xaa, 0xaa, 0xaa }, + { 0x55, 0x55, 0x55 }, + { 0x55, 0x55, 0xff }, + { 0x55, 0xff, 0x55 }, + { 0x55, 0xff, 0xff }, + { 0xff, 0x55, 0x55 }, + { 0xff, 0x55, 0xff }, + { 0xff, 0xff, 0x55 }, + { 0xff, 0xff, 0xff } +}; +byte *_pixels = NULL; +ColorT _color; + +// Emulator Stuff. +byte _RAM[0x10000]; // 6502 memory. +VrEmu6502 *_v6502 = NULL; + +// A2-3D2 Stuff. +volatile cameraT *_camera; +uint16_t _drawlistInDatabase; + + +#define LOW_BYTE(x) ((uint8_t)(x)) +#define HIGH_BYTE(x) ((uint8_t)(((uint16_t)(x)) >> 8)) + +// ***FIX*** Somehow we're getting invalid drawing coordinates from A2-3D2. +#define bitmapPutPixelIOSet(x,y) \ + ({ \ + if ((x < WIDTH) && (y < HEIGHT)) { \ + byte *p = _pixels + ((x) * 4 + ((y) * WIDTH * 4)); \ + *p++ = _color.r; \ + *p++ = _color.g; \ + *p++ = _color.b; \ + *p++ = 255; \ + } \ + }) + + +void bitmapLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); +void colorSet(byte ega); +void draw(void); +void jsr(uint16_t addr); +void landscape(void); +byte memoryRead(uint16_t addr, bool isDebug); +void memoryWrite(uint16_t addr, byte value); +int appMain(app_t *app, void *userData); + + +void bitmapLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { + uint16_t x; + uint16_t y; + int16_t dx; + int16_t dy; + int16_t incX; + int16_t incY; + int16_t balance; + + if (x2 >= x1) { + dx = x2 - x1; + incX = 1; + } else { + dx = x1 - x2; + incX = -1; + } + + if (y2 >= y1) { + dy = y2 - y1; + incY = 1; + } else { + dy = y1 - y2; + incY = -1; + } + + x = x1; + y = y1; + + if (dx >= dy) { + dy <<= 1; + balance = dy - dx; + dx <<= 1; + while (x != x2) { + bitmapPutPixelIOSet(x, y); + if (balance >= 0) { + y += incY; + balance -= dx; + } + balance += dy; + x += incX; + } + bitmapPutPixelIOSet(x, y); + } else { + dx <<= 1; + balance = dx - dy; + dy <<= 1; + while (y != y2) { + bitmapPutPixelIOSet(x, y); + if (balance >= 0) { + x += incX; + balance -= dy; + } + balance += dx; + y += incY; + } + bitmapPutPixelIOSet(x, y); + } +} + + +void colorSet(byte ega) { + _color.r = _ega[ega][0]; + _color.g = _ega[ega][1]; + _color.b = _ega[ega][2]; +} + + +void draw(void) { + uint16_t pointer = DRAWLIST_P0; + int16_t x1; + int16_t y1; + int16_t x2; + int16_t y2; + + while (true) { + switch (_RAM[pointer++]) { + + // Line. + case LIN2D: + x1 = (int8_t)_RAM[pointer++] + 128; + y1 = 255 - ((int8_t)_RAM[pointer++] + 128); + x2 = (int8_t)_RAM[pointer++] + 128; + y2 = 255 - ((int8_t)_RAM[pointer++] + 128); + bitmapLine(x1, y1, x2, y2); + continue; + + // Point. + case PNT2D: + x1 = (int8_t)_RAM[pointer++] + 128; + y1 = 255 - ((int8_t)_RAM[pointer++] + 128); + bitmapPutPixelIOSet(x1, y1); + continue; + + // Set Color. + case STCOL: + x1 = _RAM[pointer++]; + colorSet(x1); + continue; + + // Set Resolution. + case SRES: + // Eat this byte. We don't need it. + pointer++; + continue; + + // End. + case END: + break; + + } + break; + } +} + + +void jsr(uint16_t addr) { + int32_t depth = 0; + int32_t instructions = 0; + uint16_t PC = addr; + + vrEmu6502SetPC(_v6502, PC); + + while (true) { + // Did we execute an instruction? + if (PC != vrEmu6502GetPC(_v6502)) { + PC = vrEmu6502GetPC(_v6502); + instructions++; + // Track JSRs & RTSs. + if (vrEmu6502GetCurrentOpcode(_v6502) == 0x20) depth++; + if (vrEmu6502GetCurrentOpcode(_v6502) == 0x60) { + depth--; + if (depth < 0) break; + } + } + // Step. + vrEmu6502Tick(_v6502); + } + + //printf("%x = %d instructions\n", addr, instructions); +} + + +void landscape(void) { + int16_t i; + int16_t x; + int16_t y; + int16_t min = 0; + int16_t max = 10000; + byte *db = _RAM; + uint16_t bytes = DATABASE; + + + // All scene databases need to begin with the ARRAY and EYE records. + // The CLPSW clipping setting is up to you. :-) + + db[bytes++] = ARRAY; // Will be filled in by the program. + db[bytes++] = 0; + db[bytes++] = 0; + + db[bytes++] = EYE; // Will be filled in by the program. + db[bytes++] = 0x00; // X + db[bytes++] = 0x00; + db[bytes++] = 0x00; // Y + db[bytes++] = 0x00; + db[bytes++] = 0x00; // Z + db[bytes++] = 0x00; + db[bytes++] = 0x00; // P + db[bytes++] = 0x00; // B + db[bytes++] = 0x00; // H + + db[bytes++] = CLPSW; + db[bytes++] = 0x00; + + // Landscape + + db[bytes++] = STCOL; + db[bytes++] = 2; // Green + + for (i=min; i<=max; i+=1000) { + db[bytes++] = SPNT; + db[bytes++] = LOW_BYTE(i); // X + db[bytes++] = HIGH_BYTE(i); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(min); // Z + db[bytes++] = HIGH_BYTE(min); + + db[bytes++] = CPNT; + db[bytes++] = LOW_BYTE(i); // X + db[bytes++] = HIGH_BYTE(i); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(max); // Z + db[bytes++] = HIGH_BYTE(max); + + db[bytes++] = SPNT; + db[bytes++] = LOW_BYTE(min); // X + db[bytes++] = HIGH_BYTE(min); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(i); // Z + db[bytes++] = HIGH_BYTE(i); + + db[bytes++] = CPNT; + db[bytes++] = LOW_BYTE(max); // X + db[bytes++] = HIGH_BYTE(max); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(i); // Z + db[bytes++] = HIGH_BYTE(i); + } + + // Runway - 3000' x 100' + min = 3500; + max = min + 300; + x = 3500; + db[bytes++] = STCOL; + db[bytes++] = 8; // Dark Grey + for (i=0; i<=10; i+=2) { + db[bytes++] = SPNT; + db[bytes++] = LOW_BYTE(x+i); // X + db[bytes++] = HIGH_BYTE(x+i); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(min); // Z + db[bytes++] = HIGH_BYTE(min); + + db[bytes++] = CPNT; + db[bytes++] = LOW_BYTE(x+i); // X + db[bytes++] = HIGH_BYTE(x+i); + db[bytes++] = 0; // Y + db[bytes++] = 0; + db[bytes++] = LOW_BYTE(max); // Z + db[bytes++] = HIGH_BYTE(max); + } + + + db[bytes++] = END; +} + + +byte memoryRead(uint16_t addr, bool isDebug) { + (void)isDebug; + return _RAM[addr]; +} + + +void memoryWrite(uint16_t addr, byte value) { + _RAM[addr] = value; +} + + +int appMain(app_t *app, void *userData) { + ArgsT *args = (ArgsT *)userData; + uint16_t bytes; + app_input_t input; + + // Set up window. + app_screenmode(app, APP_SCREENMODE_WINDOW); + app_title(app, "Sierra Hotel Flight Model Test"); + app_interpolation(app, APP_INTERPOLATION_NONE); + app_window_size(app, WIDTH * 2, HEIGHT * 2); + + // Clear VM RAM. + memset(_RAM, 0, 0x10000); + + // Create our VM. + _v6502 = vrEmu6502New(CPU_W65C02, memoryRead, memoryWrite); + + // Copy A2-3D2 into 6502 RAM. + memcpy(&_RAM[0x6000], a23d2bin, a23d2bin_len); + + // Initialize A2-3D2 so we can use the "fast entry point" (NXTPT) when rendering. + bytes = A23D2_TEST_DATABASE; + _RAM[bytes++] = SCRSZ; // Screen size. 256x240. Center is 0,0. + _RAM[bytes++] = 255; + _RAM[bytes++] = 239; + _RAM[bytes++] = 0; + _RAM[bytes++] = 0; + _RAM[bytes++] = END; // Setup complete! + jsr(A23D2_ENTRYN); + + // Generate scene database. + landscape(); + + // Set up drawlist. + _drawlistInDatabase = DATABASE + 1; + _RAM[DRAWLIST_P0] = END; + + // Set up camera. + _camera = (cameraT *)&_RAM[DATABASE + 4]; + _camera->x = 5000; + _camera->y = 1000; + _camera->z = 5000; + _camera->p = 25; + + // Set up render surface. + _pixels = (byte *)malloc(HEIGHT * WIDTH * 4); + + // Build a plane! + resetAircraft(); + + // No input. + _gamepad = 0; + + // Loop until exit. + while (app_yield(app) != APP_STATE_EXIT_REQUESTED) { + // Clear screen. + memset(_pixels, 0, HEIGHT * WIDTH * 4); + + // 3D Viewport edge. + colorSet(14); + bitmapLine(256, 0, 256, 239); + + // Update drawlist. + _RAM[_drawlistInDatabase + 1] = HIGH_BYTE(DRAWLIST_P0); + _RAM[_drawlistInDatabase + 0] = LOW_BYTE(DRAWLIST_P0); + + // Set IBP. + _RAM[A23D2_IBP + 1] = HIGH_BYTE(DATABASE); + _RAM[A23D2_IBP + 0] = LOW_BYTE(DATABASE); + + // Render. + jsr(A23D2_NXTPT); + draw(); + + // Get input. + input = app_input(app); + for (bytes=0; bytes -15)) _plane.throttle++; + if ((_gamepad & JOY_DOWN) && (_plane.throttle < 15)) _plane.throttle--; + if (_gamepad & JOY_RIGHT) _plane.brake = !_plane.brake; + } else { + // No button pressed. + if ((_gamepad & JOY_UP) && (_plane.elevator > -15)) _plane.elevator--; + if ((_gamepad & JOY_DOWN) && (_plane.elevator < 15)) _plane.elevator++; + if ((_gamepad & JOY_LEFT) && (_plane.aileron > -15)) _plane.aileron--; + if ((_gamepad & JOY_RIGHT) && (_plane.aileron < 15)) _plane.aileron++; + } + // "Coordinated" flight. + _plane.rudder = _plane.aileron; + + // FLY! + updateAircraft(); + + // Move camera. + //_camera->p += 1; // Change pitch angle. + //_camera->b += 2; // Change bank angle. + _camera->h += 1; // Change heading angle. + + //printf("H = %d\n", _camera->h); + + // Update screen. + app_present(app, (APP_U32 *)_pixels, WIDTH, HEIGHT, 0xffffff, 0x000000); + } + + // Destroy VM. + vrEmu6502Destroy(_v6502); + _v6502 = NULL; + + // Free render surface. + free(_pixels); + + return 0; +} + + +int main(int argc, char *argv[]) { + ArgsT args; + + args.count = argc; + args.args = argv; + + return app_run(appMain, &args, NULL, NULL, NULL); +} diff --git a/pc/src/vrEmu6502.c b/pc/src/vrEmu6502.c new file mode 100644 index 0000000..0fe12d7 --- /dev/null +++ b/pc/src/vrEmu6502.c @@ -0,0 +1,2290 @@ +/* + * Troy's 6502 Emulator + * + * Copyright (c) 2022 Troy Schrapel + * + * This code is licensed under the MIT license + * + * https://github.com/visrealm/vrEmu6502 + * + */ + +#include "vrEmu6502.h" + +#include +#include +#include + +#if PICO_BUILD +#include "pico/stdlib.h" +#else +#define __not_in_flash(x) +#define __time_critical_func(fn) fn +#endif + + /* + * address mode function prototype + */ +typedef uint16_t(*vrEmu6502AddrModeFn)(VrEmu6502*); + +/* + * instruction function prototype + */ +typedef void(*vrEmu6502InstructionFn)(VrEmu6502*, vrEmu6502AddrModeFn); + +struct vrEmu6502Opcode +{ + const vrEmu6502InstructionFn instruction; + const vrEmu6502AddrModeFn addrMode; + const uint8_t cycles; +}; +typedef struct vrEmu6502Opcode vrEmu6502Opcode; + + +/* ------------------------------------------------------------------ + * PRIVATE DATA STRUCTURE + */ +struct vrEmu6502_s +{ + vrEmu6502Model model; + + vrEmu6502MemRead readFn; + vrEmu6502MemWrite writeFn; + + vrEmu6502Interrupt intPin; + vrEmu6502Interrupt nmiPin; + + uint8_t step; + uint8_t currentOpcode; + uint16_t currentOpcodeAddr; + + bool wai; + bool stp; + + uint16_t pc; + + uint8_t ac; + uint8_t ix; + uint8_t iy; + uint8_t sp; + + uint8_t flags; + + uint16_t zpBase; + uint16_t spBase; + uint16_t tmpAddr; + + const vrEmu6502Opcode* opcodes; + const char* mnemonicNames[256]; + vrEmu6502AddrMode addrModes[256]; +}; + +/* ------------------------------------------------------------------ + * PRIVATE CONSTANTS + */ + + +static const uint16_t NMI_VEC = 0xfffa; +static const uint16_t RESET_VEC = 0xfffc; +static const uint16_t INT_VEC = 0xfffe; + +static const uint8_t UNSTABLE_MAGIC_CONST = 0xee; // common values are 0x00, 0xee, 0xff + +/* ------------------------------------------------------------------ + * HELPER FUNCTIONS + * ----------------------------------------------------------------*/ +inline static void push(VrEmu6502* vr6502, uint8_t val) +{ + vr6502->writeFn(vr6502->spBase | vr6502->sp--, val); +} + +/* + * pop a value from the stack + */ +inline static uint8_t pop(VrEmu6502* vr6502) +{ + return vr6502->readFn(vr6502->spBase | (++vr6502->sp), false); +} + +/* + * read a 16-bit value from memory + */ +inline static uint16_t read16(VrEmu6502* vr6502, uint16_t addr) +{ + return vr6502->readFn(addr, false) | (vr6502->readFn(addr + 1, false) << 8); +} + +/* + * read a 16-bit value from memory (with 6502 page crossing bug) + */ +inline static uint16_t read16Bug(VrEmu6502* vr6502, uint16_t addr) +{ + if ((addr & 0xff) == 0xff) /* 6502 bug */ + { + return vr6502->readFn(addr, false) | (vr6502->readFn(addr & 0xff00, false) << 8); + } + return vr6502->readFn(addr, false) | (vr6502->readFn(addr + 1, false) << 8); +} + +/* + * read a 16-bit value from memory (wrap high byte) + */ +inline static uint16_t read16Wrapped(VrEmu6502* vr6502, uint16_t addr) +{ + return vr6502->readFn(addr, false) | (vr6502->readFn((addr + 1) & 0xff, false) << 8); +} + +/* + * additional step if page boundary crossed + */ +inline static void pageBoundary(VrEmu6502* vr6502, uint16_t addr1, uint16_t addr2) +{ + vr6502->step += !!((addr1 ^ addr2) & 0xff00); +} + +/* + * set a processor status flag bit + */ +inline static void setBit(VrEmu6502* vr6502, vrEmu6502Flag flag) +{ + vr6502->flags |= flag; +} + +/* + * clear a processor status flag bit + */ +inline static void clearBit(VrEmu6502* vr6502, vrEmu6502Flag flag) +{ + vr6502->flags &= ~flag; +} + +/* + * set/clear a processor status flag bit + */ +inline static void setOrClearBit(VrEmu6502* vr6502, vrEmu6502Flag flag, bool set) +{ + vr6502->flags &= ~flag; + vr6502->flags |= set * flag; +} + +/* + * test a processor status flag bit (return true if set, false if clear) + */ +inline static bool testBit(VrEmu6502* vr6502, vrEmu6502Flag flag) +{ + return vr6502->flags & flag; +} + +/* + * set negative and zero flags based on given value + */ +inline static void setNZ(VrEmu6502* vr6502, uint8_t val) +{ + vr6502->flags &= ~(FlagN | FlagZ); + vr6502->flags |= val & FlagN; + vr6502->flags |= !val * FlagZ; +} + +/* + * is the model in the 65C02 family? + */ +inline static bool is65c02(VrEmu6502* vr6502) +{ + return vr6502->model == CPU_65C02 || + vr6502->model == CPU_W65C02 || + vr6502->model == CPU_R65C02; +} + +/* ------------------------------------------------------------------ + * DECLARE OPCODE TABLES + * ----------------------------------------------------------------*/ +static const vrEmu6502Opcode* std6502; +static const vrEmu6502Opcode* std6502U; +static const vrEmu6502Opcode* std65c02; +static const vrEmu6502Opcode* wdc65c02; +static const vrEmu6502Opcode* r65c02; + +static vrEmu6502AddrMode opcodeToAddrMode(VrEmu6502* vr6502, uint8_t opcode); +static const char* opcodeToMnemonicStr(VrEmu6502* vr6502, uint8_t opcode); + +/* ------------------------------------------------------------------ + * + * create a new 6502 + */ +VR_EMU_6502_DLLEXPORT VrEmu6502* vrEmu6502New( + vrEmu6502Model model, + vrEmu6502MemRead readFn, + vrEmu6502MemWrite writeFn) +{ + assert(readFn); + assert(writeFn); + + VrEmu6502* vr6502 = (VrEmu6502*)malloc(sizeof(VrEmu6502)); + if (vr6502 != NULL) + { + vr6502->model = model; + vr6502->readFn = readFn; + vr6502->writeFn = writeFn; + + vr6502->zpBase = 0x0; + vr6502->spBase = 0x100; + + switch (model) + { + case CPU_65C02: + vr6502->opcodes = std65c02; + break; + + case CPU_W65C02: + vr6502->opcodes = wdc65c02; + break; + + case CPU_R65C02: + vr6502->opcodes = r65c02; + break; + + case CPU_6502U: + vr6502->model = CPU_6502; + vr6502->opcodes = std6502U; + break; + + default: + vr6502->opcodes = std6502; + break; + } + + /* build mnemonic name cache */ + for (int i = 0; i <= 0xff; ++i) + { + vr6502->mnemonicNames[i] = opcodeToMnemonicStr(vr6502, i & 0xff); + vr6502->addrModes[i] = opcodeToAddrMode(vr6502, i & 0xff); + } + + vrEmu6502Reset(vr6502); + } + + return vr6502; +} + +/* ------------------------------------------------------------------ + * + * destroy a 6502 + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502Destroy(VrEmu6502* vr6502) +{ + if (vr6502) + { + /* destruction */ + free(vr6502); + } +} + +/* ------------------------------------------------------------------ + * + * reset the 6502 + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502Reset(VrEmu6502* vr6502) +{ + if (vr6502) + { + /* initialization */ + vr6502->intPin = IntCleared; + vr6502->nmiPin = IntCleared; + vr6502->pc = read16(vr6502, RESET_VEC); + vr6502->step = 0; + vr6502->wai = false; + vr6502->stp = false; + vr6502->currentOpcode = 0; + vr6502->tmpAddr = 0; + + /* 65c02 clears D and sets I on reset */ + if (is65c02(vr6502)) + { + vr6502->flags &= ~FlagD; + vr6502->flags |= FlagI; + } + + /* B and U don't exist so always 1's */ + vr6502->flags |= FlagU | FlagB; + } +} + +static void beginInterrupt(VrEmu6502* vr6502, uint16_t addr) +{ + push(vr6502, vr6502->pc >> 8); + push(vr6502, vr6502->pc & 0xff); + push(vr6502, (vr6502->flags | FlagU) & ~FlagB); + setBit(vr6502, FlagI); + vr6502->wai = false; + vr6502->pc = read16(vr6502, addr); +} + +/* ------------------------------------------------------------------ + * + * a single clock tick + */ +VR_EMU_6502_DLLEXPORT void __time_critical_func(vrEmu6502Tick)(VrEmu6502* vr6502) +{ + if (vr6502->stp) return; + + if (vr6502->step == 0) + { + /* interrupts are ignored during the 1-cycle nops*/ + if (vr6502->opcodes[vr6502->currentOpcode].cycles != 1) + { + /* check NMI */ + if (vr6502->nmiPin == IntRequested) + { + vr6502->wai = false; + vr6502->nmiPin = IntCleared; + beginInterrupt(vr6502, NMI_VEC); + return; + } + + /* check INT */ + if (vr6502->intPin == IntRequested) + { + vr6502->wai = false; + if (!testBit(vr6502, FlagI)) + { + beginInterrupt(vr6502, INT_VEC); + return; + } + } + } + + if (!vr6502->wai) + { + vr6502->currentOpcodeAddr = vr6502->pc++; + vr6502->currentOpcode = vr6502->readFn(vr6502->currentOpcodeAddr, false); + + /* find the instruction in the table */ + const vrEmu6502Opcode* opcode = &vr6502->opcodes[vr6502->currentOpcode]; + + /* set cycles here as they may be adjusted by addressing mode */ + vr6502->step = opcode->cycles; + + /* execute the instruction */ + opcode->instruction(vr6502, opcode->addrMode); + } + } + + if (vr6502->step) --vr6502->step; +} + +/* ------------------------------------------------------------------ + * + * a single instruction cycle + */ +VR_EMU_6502_DLLEXPORT uint8_t __time_critical_func(vrEmu6502InstCycle)(VrEmu6502* vr6502) +{ + vrEmu6502Tick(vr6502); + uint8_t ticks = vr6502->step + 1; + vr6502->step = 0; + return ticks; +} + + +/* ------------------------------------------------------------------ + * + * returns a pointer to the interrupt signal. + * externally, you can modify it to set/reset the interrupt signal + */ +VR_EMU_6502_DLLEXPORT vrEmu6502Interrupt* vrEmu6502Int(VrEmu6502* vr6502) +{ + if (vr6502) + { + return &vr6502->intPin; + } + return NULL; +} + +/* ------------------------------------------------------------------ + * + * returns a pointer to the nmi signal. + * externally, you can modify it to set/reset the interrupt signal + */ +VR_EMU_6502_DLLEXPORT vrEmu6502Interrupt* vrEmu6502Nmi(VrEmu6502* vr6502) +{ + if (vr6502) + { + return &vr6502->nmiPin; + } + return NULL; +} + +/* ------------------------------------------------------------------ + * + * return the program counter + */ +VR_EMU_6502_DLLEXPORT uint16_t vrEmu6502GetPC(VrEmu6502* vr6502) +{ + return vr6502->pc; +} + +/* ------------------------------------------------------------------ + * + * set the program counter + */ +VR_EMU_6502_DLLEXPORT void vrEmu6502SetPC(VrEmu6502* vr6502, uint16_t pc) +{ + vr6502->pc = pc; + vr6502->step = 0; +} + + +/* ------------------------------------------------------------------ + * + * return the accumulator + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetAcc(VrEmu6502* vr6502) +{ + return vr6502->ac; +} + +/* ------------------------------------------------------------------ + * + * return the x index register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetX(VrEmu6502* vr6502) +{ + return vr6502->ix; +} + +/* ------------------------------------------------------------------ + * + * return the y index register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetY(VrEmu6502* vr6502) +{ + return vr6502->iy; +} + +/* ------------------------------------------------------------------ + * + * return the processor status register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetStatus(VrEmu6502* vr6502) +{ + return vr6502->flags; +} + +/* ------------------------------------------------------------------ + * + * return the stack pointer register + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetStackPointer(VrEmu6502* vr6502) +{ + return vr6502->sp; +} + +/* ------------------------------------------------------------------ + * + * return the current opcode + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetCurrentOpcode(VrEmu6502* vr6502) +{ + return vr6502->currentOpcode; +} + +/* ------------------------------------------------------------------ + * + * return the current opcode address + */ +VR_EMU_6502_DLLEXPORT uint16_t vrEmu6502GetCurrentOpcodeAddr(VrEmu6502* vr6502) +{ + return vr6502->currentOpcodeAddr; +} + +/* ------------------------------------------------------------------ + * + * return the next opcode + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetNextOpcode(VrEmu6502* vr6502) +{ + return vr6502->readFn(vr6502->pc, true); +} + + +/* ------------------------------------------------------------------ + * + * return the opcode cycle + */ +VR_EMU_6502_DLLEXPORT uint8_t vrEmu6502GetOpcodeCycle(VrEmu6502* vr6502) +{ + return vr6502->step; +} + +/* ------------------------------------------------------------------ + * + * return the opcode mnemonic string + */ +VR_EMU_6502_DLLEXPORT const char* vrEmu6502OpcodeToMnemonicStr(VrEmu6502* vr6502, uint8_t opcode) +{ + return vr6502->mnemonicNames[opcode]; +} + +/* ------------------------------------------------------------------ + * + * return the opcode address mode + */ +VR_EMU_6502_DLLEXPORT +vrEmu6502AddrMode vrEmu6502GetOpcodeAddrMode(VrEmu6502* vr6502, uint8_t opcode) +{ + return vr6502->addrModes[opcode]; +} + +/* ------------------------------------------------------------------ + * + * get disassembled instruction as a string. returns next instruction address + */ +VR_EMU_6502_DLLEXPORT +uint16_t vrEmu6502DisassembleInstruction( + VrEmu6502* vr6502, + uint16_t addr, + int bufferSize, + char* buffer, + uint16_t* refAddr, + const char* const labelMap[0x10000]) +{ + if (vr6502) + { + uint8_t opcode = vr6502->readFn(addr, true); + uint8_t arg8 = vr6502->readFn(addr + 1, true); + uint16_t arg16 = (vr6502->readFn(addr + 2, true) << 8) | arg8; + const char* mnemonic = vrEmu6502OpcodeToMnemonicStr(vr6502, opcode); + + const char* addr8Label = labelMap ? labelMap[arg8] : NULL; + const char* addr16Label = labelMap ? labelMap[arg16] : NULL; + + int offset = snprintf(buffer, bufferSize, "%s ", mnemonic); + buffer += offset; + bufferSize -= offset; + + switch (vrEmu6502GetOpcodeAddrMode(vr6502, opcode)) + { + case AddrModeAbs: + if (addr16Label) + snprintf(buffer, bufferSize, "%s", addr16Label); + else + snprintf(buffer, bufferSize, "$%04x", arg16); + if (refAddr) *refAddr = arg16; + return addr + 3; + + case AddrModeAbsX: + if (addr16Label) + snprintf(buffer, bufferSize, "%s, x", addr16Label); + else + snprintf(buffer, bufferSize, "$%04x, x", arg16); + if (refAddr) *refAddr = arg16 + vr6502->ix; + return addr + 3; + + case AddrModeAbsY: + if (addr16Label) + snprintf(buffer, bufferSize, "%s, y", addr16Label); + else + snprintf(buffer, bufferSize, "$%04x, y", arg16); + if (refAddr) *refAddr = arg16 + vr6502->iy; + return addr + 3; + + case AddrModeImm: + if (addr8Label) + snprintf(buffer, bufferSize, "#%s", addr8Label); + else + snprintf(buffer, bufferSize, "#$%02x", arg8); + if (refAddr) *refAddr = addr + 1; + return addr + 2; + + case AddrModeAbsInd: + if (addr16Label) + snprintf(buffer, bufferSize, "(%s)", addr16Label); + else + snprintf(buffer, bufferSize, "($%04x)", arg16); + if (refAddr) *refAddr = arg16; + return addr + 3; + + case AddrModeAbsIndX: + if (addr16Label) + snprintf(buffer, bufferSize, "(%s, x)", addr16Label); + else + snprintf(buffer, bufferSize, "($%04x, x)", arg16); + if (refAddr) *refAddr = arg16 + vr6502->ix; + return addr + 3; + + case AddrModeIndX: + if (addr8Label) + snprintf(buffer, bufferSize, "(%s, x)", addr8Label); + else + snprintf(buffer, bufferSize, "($%02x, x)", arg8); + if (refAddr) *refAddr = arg8 + vr6502->ix; + return addr + 2; + + case AddrModeIndY: + if (addr8Label) + snprintf(buffer, bufferSize, "(%s), y", addr8Label); + else + snprintf(buffer, bufferSize, "($%02x), y", arg8); + if (refAddr) *refAddr = arg8; + return addr + 2; + + case AddrModeRel: + if (labelMap && labelMap[addr + (int8_t)arg8 + 2]) + snprintf(buffer, bufferSize, "%s", labelMap[addr + (int8_t)arg8 + 2]); + else + snprintf(buffer, bufferSize, "$%04x", addr + (int8_t)arg8 + 2); + if (refAddr) *refAddr = addr + (int8_t)arg8 + 2; + return addr + 2; + + case AddrModeZP: + if (addr8Label) + snprintf(buffer, bufferSize, "%s", addr8Label); + else + snprintf(buffer, bufferSize, "$%02x", arg8); + if (refAddr) *refAddr = arg8; + return addr + 2; + + case AddrModeZPI: + if (addr8Label) + snprintf(buffer, bufferSize, "(%s)", addr8Label); + else + snprintf(buffer, bufferSize, "($%02x)", arg8); + if (refAddr) *refAddr = arg8; + return addr + 2; + + case AddrModeZPX: + if (addr8Label) + snprintf(buffer, bufferSize, "%s, x", addr8Label); + else + snprintf(buffer, bufferSize, "$%02x, x", arg8); + if (refAddr) *refAddr = arg8 + vr6502->ix; + return addr + 2; + + case AddrModeZPY: + if (addr8Label) + snprintf(buffer, bufferSize, "%s, y", addr8Label); + else + snprintf(buffer, bufferSize, "$%02x, y", arg8); + if (refAddr) *refAddr = arg8 + vr6502->iy; + return addr + 2; + + case AddrModeAcc: + case AddrModeImp: + return addr + 1; + + default: + break; + + } + } + return 0; +} + + + +/* ------------------------------------------------------------------ + * ADDRESS MODES + * ----------------------------------------------------------------*/ + + + /* + * absolute mode - eg. lda $1234 + */ +static uint16_t ab(VrEmu6502* vr6502) +{ + uint16_t addr = read16(vr6502, vr6502->pc++); + ++vr6502->pc; + return addr; +} + +/* + * absolute indexed x - eg. lda $1234, x + */ +static uint16_t abx(VrEmu6502* vr6502) +{ + uint16_t base = read16(vr6502, vr6502->pc++); ++vr6502->pc; + return base + vr6502->ix; +} + +/* + * absolute indexed y - eg. lda $1234, y + */ +static uint16_t aby(VrEmu6502* vr6502) +{ + uint16_t base = read16(vr6502, vr6502->pc++); ++vr6502->pc; + return base + vr6502->iy; +} + +/* + * absolute indexed x (with page boundary cycle check) - eg. lda $1234, x + */ +static uint16_t axp(VrEmu6502* vr6502) +{ + uint16_t base = read16(vr6502, vr6502->pc++); ++vr6502->pc; + uint16_t addr = base + vr6502->ix; + pageBoundary(vr6502, addr, base); + return addr; +} + +/* + * absolute indexed y (with page boundary cycle check) - eg. lda $1234, y + */ +static uint16_t ayp(VrEmu6502* vr6502) +{ + uint16_t base = read16(vr6502, vr6502->pc++); ++vr6502->pc; + uint16_t addr = base + vr6502->iy; + pageBoundary(vr6502, addr, base); + return addr; +} + +/* + * immediate mdoe - eg. lda #12 + */ +static uint16_t imm(VrEmu6502* vr6502) +{ + return vr6502->pc++; +} + +/* + * indirect mode (jmp only) - eg. jmp ($1234) + */ +static uint16_t ind(VrEmu6502* vr6502) +{ + if (vr6502->model == CPU_6502) + { + /* 6502 had a bug if the low byte was $ff, the high address would come from + the same page rather than the next page */ + uint16_t addr = read16Bug(vr6502, vr6502->pc++); + return read16(vr6502, addr); + } + + uint16_t addr = read16(vr6502, vr6502->pc++); + return read16(vr6502, addr); +} + +/* + * indirect x mode (jmp only) - eg. jmp ($1234, x) + */ +static uint16_t indx(VrEmu6502* vr6502) +{ + uint16_t addr = read16(vr6502, vr6502->pc++) + vr6502->ix; + return read16(vr6502, addr); +} + +/* + * relative mode (branch instructions) - eg. bne -5 + */ +static uint16_t rel(VrEmu6502* vr6502) +{ + int8_t offset = (int8_t)vr6502->readFn(vr6502->pc, false); + return vr6502->pc++ + offset + 1; +} + +/* + * indexed indirect mode - eg. lda ($12, x) + */ +static uint16_t xin(VrEmu6502* vr6502) +{ + return read16Wrapped(vr6502, (vr6502->zpBase + vr6502->readFn(vr6502->pc++, false) + vr6502->ix) & 0xff); +} + +/* + * indirect indexed mode - eg. lda ($12), y + */ +static uint16_t yin(VrEmu6502* vr6502) +{ + uint16_t base = read16Wrapped(vr6502, vr6502->zpBase + vr6502->readFn(vr6502->pc++, false)); + uint16_t addr = base + vr6502->iy; + return addr; +} + +/* + * indirect indexed mode (with page boundary cycle check) - eg. lda ($12), y + */ +static uint16_t yip(VrEmu6502* vr6502) +{ + uint16_t base = read16Wrapped(vr6502, vr6502->zpBase + vr6502->readFn(vr6502->pc++, false)); + uint16_t addr = base + vr6502->iy; + pageBoundary(vr6502, base, addr); + return addr; +} + +/* + * zero page - eg. lda $12 + */ +static uint16_t zp(VrEmu6502* vr6502) +{ + return vr6502->zpBase + vr6502->readFn(vr6502->pc++, false); +} + +/* + * indirect mode - eg. lda ($12) + */ +static uint16_t zpi(VrEmu6502* vr6502) +{ + return read16Wrapped(vr6502, vr6502->zpBase + vr6502->readFn(vr6502->pc++, false)); +} + +/* + * zero page indexed x - eg. lda $12, x + */ +static uint16_t zpx(VrEmu6502* vr6502) +{ + return vr6502->zpBase + ((vr6502->readFn(vr6502->pc++, false) + vr6502->ix) & 0xff); +} + +/* + * zero page indexed y - eg. lda $12, y + */ +static uint16_t zpy(VrEmu6502* vr6502) +{ + return vr6502->zpBase + ((vr6502->readFn(vr6502->pc++, false) + vr6502->iy) & 0xff); +} + +/* + * temporary addresss used in illegal opcodes + */ +static uint16_t tmp(VrEmu6502* vr6502) +{ + return vr6502->tmpAddr; +} + +/* + * accumulator mode (no address) - eg. ror + */ +#define acc NULL + + /* + * implied mode (no address) - eg. tax + */ +#define imp NULL + + + /* ------------------------------------------------------------------ + * INSTRUCTIONS + * ----------------------------------------------------------------*/ + + /* + * add with carry (deicmal mode) + */ +static void adcd(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t value = vr6502->readFn(modeAddr(vr6502), false); + + uint8_t vu = value & 0x0f; + uint8_t vt = (value & 0xf0) >> 4; + + uint8_t au = vr6502->ac & 0x0f; + uint8_t at = (vr6502->ac & 0xf0) >> 4; + + uint8_t units = vu + au + testBit(vr6502, FlagC); + uint8_t tens = vt + at; + + uint8_t tc = 0; + + if (units > 0x09) + { + tc = 1; + tens += 0x01; + units += 0x06; + } + if (tens > 0x09) + { + tens += 0x06; + } + + /* 65c02 takes one extra cycle in decimal mode */ + if (is65c02(vr6502)) + { + ++vr6502->step; + + /* set V flag */ + if (at & 0x08) at |= 0xf0; + if (vt & 0x08) vt |= 0xf0; + + int8_t res = (int8_t)(at + vt + tc); + + setOrClearBit(vr6502, FlagV, res < -8 || res > 7); + + setNZ(vr6502, vr6502->ac = (tens << 4) | (units & 0x0f)); + } + else + { + uint16_t binResult = vr6502->ac + value + testBit(vr6502, FlagC); + setNZ(vr6502, vr6502->ac = (tens << 4) | (units & 0x0f)); + setOrClearBit(vr6502, FlagZ, (binResult & 0xff) == 0); + } + + setOrClearBit(vr6502, FlagC, tens & 0xf0); +} + +/* + * add with carry + */ +static void adc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagD)) + { + adcd(vr6502, modeAddr); + } + else + { + uint8_t opr = vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = vr6502->ac + opr + testBit(vr6502, FlagC); + + setOrClearBit(vr6502, FlagC, result > 0xff); + setOrClearBit(vr6502, FlagV, ((vr6502->ac ^ result) & (opr ^ result) & 0x80)); + + setNZ(vr6502, vr6502->ac = (uint8_t)result); + } +} + +/* + * bitwise and + */ +static void and (VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ac &= vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * aritmetic shift left + */ +static void asl(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + setOrClearBit(vr6502, FlagC, vr6502->ac & 0x80); + setNZ(vr6502, vr6502->ac <<= 1); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t result = vr6502->readFn(addr, false); + setOrClearBit(vr6502, FlagC, result & 0x80); + setNZ(vr6502, result <<= 1); + vr6502->writeFn(addr, result); +} + +/* + * branch (always) + */ +static void bra(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + ++vr6502->step; /* extra because we branched */ + + uint16_t addr = modeAddr(vr6502); + pageBoundary(vr6502, vr6502->pc, addr); + vr6502->pc = addr; +} + +/* + * branch if carry clear + */ +static void bcc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (!testBit(vr6502, FlagC)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * branch if carry set + */ +static void bcs(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagC)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * branch if equal (if zero set) + */ +static void beq(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagZ)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * bit test + */ +static void bit(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t val = vr6502->readFn(modeAddr(vr6502), false); + + if (modeAddr != imm) + { + /* set N and V to match bits 7 and 6 of value at addr */ + setOrClearBit(vr6502, FlagV, val & 0x40); + setOrClearBit(vr6502, FlagN, val & 0x80); + } + + /* set Z if (acc & value at addr) is zero */ + setOrClearBit(vr6502, FlagZ, !(val & vr6502->ac)); +} + +/* + * branch if minus (if N bit set) + */ +static void bmi(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagN)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * branch if not equal (if Z bit not set) + */ +static void bne(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (!testBit(vr6502, FlagZ)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * branch if plus (if N bit not set) + */ +static void bpl(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (!testBit(vr6502, FlagN)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * trigger a software interrupt + */ +static void brk(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + push(vr6502, (++vr6502->pc) >> 8); + push(vr6502, (vr6502->pc) & 0xff); + push(vr6502, vr6502->flags | FlagU | FlagB); + setBit(vr6502, FlagI); + + if (is65c02(vr6502)) + { + clearBit(vr6502, FlagD); + } + + vr6502->pc = read16(vr6502, 0xFFFE); +} + +/* + * branch if overflow clear + */ +static void bvc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (!testBit(vr6502, FlagV)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * branch if overflow set + */ +static void bvs(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagV)) bra(vr6502, modeAddr); else modeAddr(vr6502); +} + +/* + * clear carry flag + */ +static void clc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + clearBit(vr6502, FlagC); +} + +/* + * clear decimal flag + */ +static void cld(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + clearBit(vr6502, FlagD); +} + +/* + * clear interrupt flag + */ +static void cli(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + clearBit(vr6502, FlagI); +} + +/* + * clear overflow flag + */ +static void clv(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + clearBit(vr6502, FlagV); +} + +/* + * compare value with accumulator + */ +static void cmp(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t opr = ~vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = vr6502->ac + opr + 1; + + setOrClearBit(vr6502, FlagC, result > 0xff); + setNZ(vr6502, (uint8_t)result); +} + +/* + * compare value with x register + */ +static void cpx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t opr = ~vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = vr6502->ix + opr + 1; + + setOrClearBit(vr6502, FlagC, result > 0xff); + setNZ(vr6502, (uint8_t)result); +} + +/* + * compare value with y register + */ +static void cpy(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t opr = ~vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = vr6502->iy + opr + 1; + + setOrClearBit(vr6502, FlagC, result > 0xff); + setNZ(vr6502, (uint8_t)result); +} + +/* + * decrement value + */ +static void dec(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + setNZ(vr6502, --vr6502->ac); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t val = vr6502->readFn(addr, false) - 1; + setNZ(vr6502, val); + vr6502->writeFn(addr, val); +} + +/* + * decrement x register + */ +static void dex(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, --vr6502->ix); +} + +/* + * decrement y register + */ +static void dey(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, --vr6502->iy); +} + +/* + * exclusive or with accumulator + */ +static void eor(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ac ^= vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * increment value + */ +static void inc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + setNZ(vr6502, ++vr6502->ac); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t val = vr6502->readFn(addr, false) + 1; + setNZ(vr6502, val); + vr6502->writeFn(addr, val); +} + +/* + * increment x register + */ +static void inx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, ++vr6502->ix); +} + +/* + * increment y register + */ +static void iny(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, ++vr6502->iy); +} + +/* + * jump to address + */ +static void jmp(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->pc = modeAddr(vr6502); +} + +/* + * jump to subroutine + */ +static void jsr(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + push(vr6502, --vr6502->pc >> 8); + push(vr6502, vr6502->pc & 0xff); + vr6502->pc = addr; +} + +/* + * load a value to the accumulator + */ +static void lda(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ac = vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * load a value to the x register + */ +static void ldx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ix = vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * load a value to the y register + */ +static void ldy(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->iy = vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * logical shift left + */ +static void lsr(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + setOrClearBit(vr6502, FlagC, vr6502->ac & 0x01); + setNZ(vr6502, vr6502->ac >>= 1); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t result = vr6502->readFn(addr, false); + setOrClearBit(vr6502, FlagC, result & 0x01); + setNZ(vr6502, result >>= 1); + vr6502->writeFn(addr, result); +} + +/* + * no operation + */ +static void nop(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr) modeAddr(vr6502); +} + +/* + * load and discard (other 65c02 nops) + */ +static void ldd(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr) + { + /* we still want to read the data (and discard it) */ + vr6502->readFn(modeAddr(vr6502), false); + } +} + +/* + * bitwise or with acculmulator + */ +static void ora(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ac |= vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * push accumulator to stack + */ +static void pha(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + push(vr6502, vr6502->ac); +} + +/* + * push status register to stack + */ +static void php(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + push(vr6502, vr6502->flags | FlagB | FlagU); +} + +/* + * push x register to stack + */ +static void phx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + push(vr6502, vr6502->ix); +} + +/* + * push y register to stack + */ +static void phy(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + push(vr6502, vr6502->iy); +} + +/* + * pop from stack to accumulator + */ +static void pla(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ac = pop(vr6502)); +} + +/* + * pop from stack to status register + */ +static void plp(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->flags = pop(vr6502) | FlagU | FlagB; +} + +/* + * pop from stack to x register + */ +static void plx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ix = pop(vr6502)); +} + +/* + * pop from stack to y register + */ +static void ply(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->iy = pop(vr6502)); +} + +/* + * rotate value left + */ +static void rol(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + bool tc = vr6502->ac & 0x80; + vr6502->ac = (vr6502->ac << 1) | testBit(vr6502, FlagC); + setOrClearBit(vr6502, FlagC, tc); + setNZ(vr6502, vr6502->ac); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t val = vr6502->readFn(addr, false); + bool tc = val & 0x80; + val = (val << 1) | testBit(vr6502, FlagC); + setOrClearBit(vr6502, FlagC, tc); + setNZ(vr6502, val); + vr6502->writeFn(addr, val); +} + +/* + * rotate value right + */ +static void ror(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr == acc) + { + bool tc = vr6502->ac & 0x01; + vr6502->ac = (vr6502->ac >> 1) | (testBit(vr6502, FlagC) * 0x80); + setOrClearBit(vr6502, FlagC, tc); + setNZ(vr6502, vr6502->ac); + return; + } + + uint16_t addr = modeAddr(vr6502); + uint8_t val = vr6502->readFn(addr, false); + bool tc = val & 0x01; + val = (val >> 1) | (testBit(vr6502, FlagC) * 0x80); + setOrClearBit(vr6502, FlagC, tc); + setNZ(vr6502, val); + vr6502->writeFn(addr, val); +} + +/* + * return from interrupt + */ +static void rti(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->flags = pop(vr6502) | FlagU | FlagB; + vr6502->pc = pop(vr6502); + vr6502->pc |= pop(vr6502) << 8; +} + +/* + * return from subroutine + */ +static void rts(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->pc = pop(vr6502); + vr6502->pc |= pop(vr6502) << 8; + ++vr6502->pc; +} + +/* + * subtract with carry (decimal mode) + */ +static void sbcd(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t value = vr6502->readFn(modeAddr(vr6502), false); + uint16_t binResult = vr6502->ac + ~value + testBit(vr6502, FlagC); + + uint16_t result = 0; + if (vr6502->model == CPU_6502) + { + uint16_t tmp = (vr6502->ac & 0x0f) - (value & 0x0f) - !testBit(vr6502, FlagC); + + if (tmp & 0x8000) + { + tmp = ((tmp - 0x06) & 0x0f) - 0x10; + } + + result = (vr6502->ac & 0xf0) - (value & 0xf0) + tmp; + if (result & 0x8000) + { + result -= 0x60; + } + + setNZ(vr6502, (uint8_t)binResult); + setOrClearBit(vr6502, FlagV, (int16_t)result < -128 || (int16_t)result > 127); + } + else + { + uint16_t tmp = (vr6502->ac & 0x0f) - (value & 0x0f) - !testBit(vr6502, FlagC); + + result = vr6502->ac - value - !testBit(vr6502, FlagC); + + if (result & 0x8000) + { + result -= 0x60; + } + if (tmp & 0x8000) + { + result -= 0x06; + } + + ++vr6502->step; + + /* set V flag */ + setOrClearBit(vr6502, FlagV, ((vr6502->ac ^ binResult) & (~value ^ binResult) & 0x80)); + setNZ(vr6502, (uint8_t)result); + } + + + setOrClearBit(vr6502, FlagC, (uint16_t)result <= (uint16_t)vr6502->ac || (result & 0xff0) == 0xff0); + + vr6502->ac = result & 0xff; +} + +/* + * subtract with carry + */ +static void sbc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (testBit(vr6502, FlagD)) + { + sbcd(vr6502, modeAddr); + return; + } + + uint8_t opr = ~vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = vr6502->ac + opr + testBit(vr6502, FlagC); + + setOrClearBit(vr6502, FlagC, result > 0xff); + setOrClearBit(vr6502, FlagV, ((vr6502->ac ^ result) & (opr ^ result) & 0x80)); + vr6502->ac = (uint8_t)result; + + setNZ(vr6502, (uint8_t)result); +} + +/* + * set carry flag + */ +static void sec(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setBit(vr6502, FlagC); +} + +/* + * set decimal flag + */ +static void sed(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setBit(vr6502, FlagD); +} + +/* + * set interrupt disable flag + */ +static void sei(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setBit(vr6502, FlagI); +} + +/* + * store accumulator to address + */ +static void sta(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->writeFn(modeAddr(vr6502), vr6502->ac); +} + +/* + * store x register to address + */ +static void stx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->writeFn(modeAddr(vr6502), vr6502->ix); +} + +/* + * store y register to address + */ +static void sty(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->writeFn(modeAddr(vr6502), vr6502->iy); +} + +/* + * store 0 to address + */ +static void stz(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->writeFn(modeAddr(vr6502), 0); +} + +/* + * transfer accumulator to x register + */ +static void tax(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ix = vr6502->ac); +} + +/* + * transfer accumulator to y register + */ +static void tay(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->iy = vr6502->ac); +} + +/* + * transfer stack pointer register to x register + */ +static void tsx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ix = vr6502->sp); +} + +/* + * transfer x register to accumulator + */ +static void txa(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ac = vr6502->ix); +} + +/* + * transfer x register to stack pointer register + */ +static void txs(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->sp = vr6502->ix; +} + +/* + * transfer y register to accumulator + */ +static void tya(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + setNZ(vr6502, vr6502->ac = vr6502->iy); +} + +/* + * test and reset (clear) bit + */ +static void trb(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + uint8_t temp = vr6502->readFn(addr, false); + vr6502->writeFn(addr, temp & ~vr6502->ac); + setOrClearBit(vr6502, FlagZ, !(temp & vr6502->ac)); +} + +/* + * test and set bit + */ +static void tsb(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + uint8_t temp = vr6502->readFn(addr, false); + vr6502->writeFn(addr, temp | vr6502->ac); + setOrClearBit(vr6502, FlagZ, !(temp & vr6502->ac)); +} + +/* + * reset a bit (zero page only) + */ +static void rmb(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr, int bitIndex) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->writeFn(addr, vr6502->readFn(addr, false) & ~(0x01 << bitIndex)); +} + +static void rmb0(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 0); } +static void rmb1(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 1); } +static void rmb2(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 2); } +static void rmb3(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 3); } +static void rmb4(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 4); } +static void rmb5(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 5); } +static void rmb6(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 6); } +static void rmb7(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { rmb(vr6502, modeAddr, 7); } + + +/* + * set a bit (zero page only) + */ +static void smb(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr, int bitIndex) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->writeFn(addr, vr6502->readFn(addr, false) | (0x01 << bitIndex)); +} + +static void smb0(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 0); } +static void smb1(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 1); } +static void smb2(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 2); } +static void smb3(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 3); } +static void smb4(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 4); } +static void smb5(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 5); } +static void smb6(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 6); } +static void smb7(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { smb(vr6502, modeAddr, 7); } + +/* + * branch if bit reset (clear) + */ +static void bbr(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr, int bitIndex) +{ + uint8_t val = vr6502->readFn(modeAddr(vr6502), false); + if (!(val & (0x01 << bitIndex))) + { + vr6502->pc = rel(vr6502); + } + else + { + ++vr6502->pc; + } +} + +static void bbr0(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 0); } +static void bbr1(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 1); } +static void bbr2(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 2); } +static void bbr3(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 3); } +static void bbr4(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 4); } +static void bbr5(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 5); } +static void bbr6(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 6); } +static void bbr7(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbr(vr6502, modeAddr, 7); } + +/* + * branch if bit set (clear) + */ +static void bbs(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr, int bitIndex) +{ + uint8_t val = vr6502->readFn(modeAddr(vr6502), false); + if ((val & (0x01 << bitIndex))) + { + vr6502->pc = rel(vr6502); + } + else + { + ++vr6502->pc; + } +} + +static void bbs0(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 0); } +static void bbs1(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 1); } +static void bbs2(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 2); } +static void bbs3(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 3); } +static void bbs4(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 4); } +static void bbs5(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 5); } +static void bbs6(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 6); } +static void bbs7(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) { bbs(vr6502, modeAddr, 7); } + +/* + * stop the processor + */ +static void stp(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->stp = true; +} + +/* + * wait for interrupt + */ +static void wai(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->wai = true; +} + +/* + * invalid opcode (not supported by this emulator) + */ +static void err(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + if (modeAddr) modeAddr(vr6502); +} + +/* ------------------------------------------------------------------ + * UNDOCUMENTED OPCODES + * ----------------------------------------------------------------*/ + + /* + * aritmetic shift left and bitwise-or with accumulator + */ +static void slo(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + asl(vr6502, tmp); + ora(vr6502, tmp); +} + +/* + * rotate left and bitwise-and with accumulator + */ +static void rla(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + rol(vr6502, tmp); + and (vr6502, tmp); +} + +/* + * logical shift right and bitwise-xor with accumulator + */ +static void sre(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + lsr(vr6502, tmp); + eor(vr6502, tmp); +} + +/* + * rotate right and add with carry + */ +static void rra(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + ror(vr6502, tmp); + adc(vr6502, tmp); +} + +/* + * store accumulator bitwise-and x register to address + */ +static void sax(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->writeFn(modeAddr(vr6502), vr6502->ac & vr6502->ix); +} + +/* + * load accumulator and x register from address + */ +static void lax(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + setNZ(vr6502, vr6502->ac = vr6502->ix = vr6502->readFn(modeAddr(vr6502), false)); +} + +/* + * decrement then compare address + */ +static void dcp(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + dec(vr6502, tmp); + cmp(vr6502, tmp); +} + +/* + * increment then subtract with carry + */ +static void isc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->tmpAddr = modeAddr(vr6502); + inc(vr6502, tmp); + sbc(vr6502, tmp); +} + +/* + * bitwise-and then set carry to bit 7 + */ +static void anc(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + and (vr6502, modeAddr); + setOrClearBit(vr6502, FlagC, testBit(vr6502, FlagN)); +} + +/* + * bitwise-and with value then shift right in accumulator + */ +static void alr(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + and (vr6502, modeAddr); + lsr(vr6502, acc); +} + +/* + * bitwise-and with value then rotate right in accumulator (sets V) + */ +static void arr(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + and (vr6502, modeAddr); + setOrClearBit(vr6502, FlagV, ((bool)vr6502->ac & 0x80) ^ ((bool)vr6502->ac & 0x40)); + vr6502->ac = (vr6502->ac >> 1) | (testBit(vr6502, FlagC) * 0x80); +} + +/* + * bitwise-and x with accumulator, subtract value and store in x + */ +static void sbx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint8_t opr = ~vr6502->readFn(modeAddr(vr6502), false); + uint16_t result = (vr6502->ac & vr6502->ix) + opr + 1; + + setOrClearBit(vr6502, FlagC, result > 0xff); + setNZ(vr6502, vr6502->ix = (uint8_t)result); +} + +/* + * bitwise-and x with sp, store in accumulator, x and sp + */ +static void las(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->sp &= vr6502->readFn(modeAddr(vr6502), false); + setNZ(vr6502, vr6502->ac = vr6502->ix = vr6502->sp); +} + +/* + * store bitwise-and of accumulator, x and ((high byte of addr) + 1) + */ +static void sha(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->writeFn(addr, vr6502->ac & vr6502->ix & (uint8_t)((addr >> 8) + 1)); +} + +/* + * store bitwise-and of x and ((high byte of addr) + 1) + */ +static void shx(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->writeFn(addr, vr6502->ix & (uint8_t)((addr >> 8) + 1)); +} + +/* + * store bitwise-and of y and ((high byte of addr) + 1) + */ +static void shy(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->writeFn(addr, vr6502->iy & (uint8_t)((addr >> 8) + 1)); +} + +/* + * store bitwise-and of y and ((high byte of addr) + 1) + */ +static void tas(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + uint16_t addr = modeAddr(vr6502); + vr6502->sp = vr6502->ac & vr6502->ix; + vr6502->writeFn(addr, vr6502->sp & (uint8_t)((addr >> 8) + 1)); +} + +/* + * bitwise-or acc with magic, bitwise-and with x, bitwise-and + * with value, store in accumulator + */ +static void ane(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->ac = (vr6502->ac | UNSTABLE_MAGIC_CONST) & vr6502->ix; + vr6502->ac &= vr6502->readFn(modeAddr(vr6502), false); + setNZ(vr6502, vr6502->ac); +} + +/* + * bitwise-or acc with magic, bitwise-and with value, store in accumulator + */ +static void lxa(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + vr6502->ac = (vr6502->ac | UNSTABLE_MAGIC_CONST); + vr6502->ac &= vr6502->readFn(modeAddr(vr6502), false); + setNZ(vr6502, vr6502->ac); +} + +/* + * halt/jam cpu + */ +static void jam(VrEmu6502* vr6502, vrEmu6502AddrModeFn modeAddr) +{ + (void)modeAddr; + + vr6502->readFn(imm(vr6502), false); + vr6502->stp = true; +} + +/* ------------------------------------------------------------------ + * OPCODE TABLES + * ----------------------------------------------------------------*/ + +#define invalid { err, imp, 2 } +#define unnop11 { nop, imp, 1 } +#define ldd_imm { ldd, imm, 2 } +#define kil { jam, imp, 1 } + + +static const vrEmu6502Opcode _std6502[256] = { + /* | _0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | */ + /* 0_ */ {brk, imp, 7}, {ora, xin, 6}, invalid , invalid , invalid , {ora, zp, 3}, {asl, zp, 5}, invalid , {php, imp, 3}, {ora, imm, 2}, {asl, acc, 2}, invalid , invalid , {ora, ab, 4}, {asl, ab, 6}, invalid , + /* 1_ */ {bpl, rel, 2}, {ora, yip, 5}, invalid , invalid , invalid , {ora, zpx, 4}, {asl, zpx, 6}, invalid , {clc, imp, 2}, {ora, ayp, 4}, invalid , invalid , invalid , {ora, axp, 4}, {asl, abx, 7}, invalid , + /* 2_ */ {jsr, ab, 6}, { and, xin, 6}, invalid , invalid , {bit, zp, 3}, { and, zp, 3}, {rol, zp, 5}, invalid , {plp, imp, 4}, { and, imm, 2}, {rol, acc, 2}, invalid , {bit, ab, 4}, { and, ab, 4}, {rol, ab, 6}, invalid , + /* 3_ */ {bmi, rel, 2}, { and, yip, 5}, invalid , invalid , invalid , { and, zpx, 4}, {rol, zpx, 6}, invalid , {sec, imp, 2}, { and, ayp, 4}, invalid , invalid , invalid , { and, axp, 4}, {rol, abx, 7}, invalid , + /* 4_ */ {rti, imp, 6}, {eor, xin, 6}, invalid , invalid , invalid , {eor, zp, 3}, {lsr, zp, 5}, invalid , {pha, imp, 3}, {eor, imm, 2}, {lsr, acc, 2}, invalid , {jmp, ab, 3}, {eor, ab, 4}, {lsr, ab, 6}, invalid , + /* 5_ */ {bvc, rel, 2}, {eor, yip, 5}, invalid , invalid , invalid , {eor, zpx, 4}, {lsr, zpx, 6}, invalid , {cli, imp, 2}, {eor, ayp, 4}, invalid , invalid , invalid , {eor, axp, 4}, {lsr, abx, 7}, invalid , + /* 6_ */ {rts, imp, 6}, {adc, xin, 6}, invalid , invalid , invalid , {adc, zp, 3}, {ror, zp, 5}, invalid , {pla, imp, 4}, {adc, imm, 2}, {ror, acc, 2}, invalid , {jmp, ind, 5}, {adc, ab, 4}, {ror, ab, 6}, invalid , + /* 7_ */ {bvs, rel, 2}, {adc, yip, 5}, invalid , invalid , invalid , {adc, zpx, 4}, {ror, zpx, 6}, invalid , {sei, imp, 2}, {adc, ayp, 4}, invalid , invalid , invalid , {adc, axp, 4}, {ror, abx, 7}, invalid , + /* 8_ */ invalid , {sta, xin, 6}, invalid , invalid , {sty, zp, 3}, {sta, zp, 3}, {stx, zp, 3}, invalid , {dey, imp, 2}, invalid , {txa, imp, 2}, invalid , {sty, ab, 4}, {sta, ab, 4}, {stx, ab, 4}, invalid , + /* 9_ */ {bcc, rel, 2}, {sta, yin, 6}, invalid , invalid , {sty, zpx, 4}, {sta, zpx, 4}, {stx, zpy, 4}, invalid , {tya, imp, 2}, {sta, aby, 5}, {txs, imp, 2}, invalid , invalid , {sta, abx, 5}, invalid , invalid , + /* A_ */ {ldy, imm, 2}, {lda, xin, 6}, {ldx, imm, 2}, invalid , {ldy, zp, 3}, {lda, zp, 3}, {ldx, zp, 3}, invalid , {tay, imp, 2}, {lda, imm, 2}, {tax, imp, 2}, invalid , {ldy, ab, 4}, {lda, ab, 4}, {ldx, ab, 4}, invalid , + /* B_ */ {bcs, rel, 2}, {lda, yip, 5}, invalid , invalid , {ldy, zpx, 4}, {lda, zpx, 4}, {ldx, zpy, 4}, invalid , {clv, imp, 2}, {lda, ayp, 4}, {tsx, imp, 2}, invalid , {ldy, axp, 4}, {lda, axp, 4}, {ldx, ayp, 4}, invalid , + /* C_ */ {cpy, imm, 2}, {cmp, xin, 6}, invalid , invalid , {cpy, zp, 3}, {cmp, zp, 3}, {dec, zp, 5}, invalid , {iny, imp, 2}, {cmp, imm, 2}, {dex, imp, 2}, invalid , {cpy, ab, 4}, {cmp, ab, 4}, {dec, ab, 6}, invalid , + /* D_ */ {bne, rel, 2}, {cmp, yip, 5}, invalid , invalid , invalid , {cmp, zpx, 4}, {dec, zpx, 6}, invalid , {cld, imp, 2}, {cmp, ayp, 4}, invalid , invalid , invalid , {cmp, axp, 4}, {dec, abx, 7}, invalid , + /* E_ */ {cpx, imm, 2}, {sbc, xin, 6}, invalid , invalid , {cpx, zp, 3}, {sbc, zp, 3}, {inc, zp, 5}, invalid , {inx, imp, 2}, {sbc, imm, 2}, {nop, imp, 2}, invalid , {cpx, ab, 4}, {sbc, ab, 4}, {inc, ab, 6}, invalid , + /* F_ */ {beq, rel, 2}, {sbc, yip, 5}, invalid , invalid , invalid , {sbc, zpx, 4}, {inc, zpx, 6}, invalid , {sed, imp, 2}, {sbc, ayp, 4}, invalid , invalid , invalid , {sbc, axp, 4}, {inc, abx, 7}, invalid }; + +static const vrEmu6502Opcode _std6502U[256] = { + /* | _0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | */ + /* 0_ */ {brk, imp, 7}, {ora, xin, 6}, kil , {slo, xin, 8}, {nop, zp, 3}, {ora, zp, 3}, {asl, zp, 5}, {slo, zp, 5}, {php, imp, 3}, {ora, imm, 2}, {asl, acc, 2}, {anc, imm, 2}, {nop, ab, 4}, {ora, ab, 4}, {asl, ab, 6}, {slo, ab, 6}, + /* 1_ */ {bpl, rel, 2}, {ora, yip, 5}, kil , {slo, yin, 8}, {nop, zpx, 4}, {ora, zpx, 4}, {asl, zpx, 6}, {slo, zpx, 6}, {clc, imp, 2}, {ora, ayp, 4}, {nop, imp, 2}, {slo, aby, 7}, {nop, axp, 4}, {ora, axp, 4}, {asl, abx, 7}, {slo, abx, 7}, + /* 2_ */ {jsr, ab, 6}, { and, xin, 6}, kil , {rla, xin, 8}, {bit, zp, 3}, { and, zp, 3}, {rol, zp, 5}, {rla, zp, 5}, {plp, imp, 4}, { and, imm, 2}, {rol, acc, 2}, {anc, imm, 2}, {bit, ab, 4}, { and, ab, 4}, {rol, ab, 6}, {rla, ab, 6}, + /* 3_ */ {bmi, rel, 2}, { and, yip, 5}, kil , {rla, yin, 8}, {nop, zpx, 4}, { and, zpx, 4}, {rol, zpx, 6}, {rla, zpx, 6}, {sec, imp, 2}, { and, ayp, 4}, {nop, imp, 2}, {rla, aby, 7}, {nop, axp, 4}, { and, axp, 4}, {rol, abx, 7}, {rla, abx, 7}, + /* 4_ */ {rti, imp, 6}, {eor, xin, 6}, kil , {sre, xin, 8}, {nop, zp, 3}, {eor, zp, 3}, {lsr, zp, 5}, {sre, zp, 5}, {pha, imp, 3}, {eor, imm, 2}, {lsr, acc, 2}, {alr, imm, 2}, {jmp, ab, 3}, {eor, ab, 4}, {lsr, ab, 6}, {sre, ab, 6}, + /* 5_ */ {bvc, rel, 2}, {eor, yip, 5}, kil , {sre, yin, 8}, {nop, zpx, 4}, {eor, zpx, 4}, {lsr, zpx, 6}, {sre, zpx, 6}, {cli, imp, 2}, {eor, ayp, 4}, {nop, imp, 2}, {sre, aby, 7}, {nop, axp, 4}, {eor, axp, 4}, {lsr, abx, 7}, {sre, abx, 7}, + /* 6_ */ {rts, imp, 6}, {adc, xin, 6}, kil , {rra, xin, 8}, {nop, zp, 3}, {adc, zp, 3}, {ror, zp, 5}, {rra, zp, 5}, {pla, imp, 4}, {adc, imm, 2}, {ror, acc, 2}, {arr, imm, 2}, {jmp, ind, 5}, {adc, ab, 4}, {ror, ab, 6}, {rra, ab, 6}, + /* 7_ */ {bvs, rel, 2}, {adc, yip, 5}, kil , {rra, yin, 8}, {nop, zpx, 4}, {adc, zpx, 4}, {ror, zpx, 6}, {rra, zpx, 6}, {sei, imp, 2}, {adc, ayp, 4}, {nop, imp, 2}, {rra, aby, 7}, {nop, axp, 4}, {adc, axp, 4}, {ror, abx, 7}, {rra, abx, 7}, + /* 8_ */ {nop, imm, 2}, {sta, xin, 6}, {nop, imm, 2}, {sax, xin, 6}, {sty, zp, 3}, {sta, zp, 3}, {stx, zp, 3}, {sax, zp, 3}, {dey, imp, 2}, {nop, imm, 2}, {txa, imp, 2}, {ane, imm, 2}, {sty, ab, 4}, {sta, ab, 4}, {stx, ab, 4}, {sax, ab, 4}, + /* 9_ */ {bcc, rel, 2}, {sta, yin, 6}, kil , {sha, zpy, 6}, {sty, zpx, 4}, {sta, zpx, 4}, {stx, zpy, 4}, {sax, zpy, 4}, {tya, imp, 2}, {sta, aby, 5}, {txs, imp, 2}, {tas, aby, 5}, {shy, abx, 5}, {sta, abx, 5}, {shx, aby, 5}, {sha, aby, 5}, + /* A_ */ {ldy, imm, 2}, {lda, xin, 6}, {ldx, imm, 2}, {lax, xin, 6}, {ldy, zp, 3}, {lda, zp, 3}, {ldx, zp, 3}, {lax, zp, 3}, {tay, imp, 2}, {lda, imm, 2}, {tax, imp, 2}, {lxa, imm, 2}, {ldy, ab, 4}, {lda, ab, 4}, {ldx, ab, 4}, {lax, ab, 4}, + /* B_ */ {bcs, rel, 2}, {lda, yip, 5}, kil , {lax, yip, 5}, {ldy, zpx, 4}, {lda, zpx, 4}, {ldx, zpy, 4}, {lax, zpy, 4}, {clv, imp, 2}, {lda, ayp, 4}, {tsx, imp, 2}, {las, ayp, 4}, {ldy, axp, 4}, {lda, axp, 4}, {ldx, ayp, 4}, {lax, ayp, 4}, + /* C_ */ {cpy, imm, 2}, {cmp, xin, 6}, {nop, imm, 2}, {dcp, xin, 8}, {cpy, zp, 3}, {cmp, zp, 3}, {dec, zp, 5}, {dcp, zp, 5}, {iny, imp, 2}, {cmp, imm, 2}, {dex, imp, 2}, {sbx, imm, 2}, {cpy, ab, 4}, {cmp, ab, 4}, {dec, ab, 6}, {dcp, ab, 6}, + /* D_ */ {bne, rel, 2}, {cmp, yip, 5}, kil , {dcp, yin, 8}, {nop, zpx, 4}, {cmp, zpx, 4}, {dec, zpx, 6}, {dcp, zpx, 6}, {cld, imp, 2}, {cmp, ayp, 4}, {nop, imp, 2}, {dcp, aby, 7}, {nop, axp, 4}, {cmp, axp, 4}, {dec, abx, 7}, {dcp, abx, 7}, + /* E_ */ {cpx, imm, 2}, {sbc, xin, 6}, {nop, imm, 2}, {isc, xin, 8}, {cpx, zp, 3}, {sbc, zp, 3}, {inc, zp, 5}, {isc, zp, 5}, {inx, imp, 2}, {sbc, imm, 2}, {nop, imp, 2}, {sbc, imm, 2}, {cpx, ab, 4}, {sbc, ab, 4}, {inc, ab, 6}, {isc, ab, 6}, + /* F_ */ {beq, rel, 2}, {sbc, yip, 5}, kil , {isc, yin, 8}, {nop, zpx, 4}, {sbc, zpx, 4}, {inc, zpx, 6}, {isc, zpx, 6}, {sed, imp, 2}, {sbc, ayp, 4}, {nop, imp, 2}, {isc, aby, 7}, {nop, axp, 4}, {sbc, axp, 4}, {inc, abx, 7}, {isc, abx, 7} }; + +static const vrEmu6502Opcode _std65c02[256] = { + /* | _0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | */ + /* 0_ */ {brk, imp, 7}, {ora, xin, 6}, ldd_imm , unnop11 , {tsb, zp, 5}, {ora, zp, 3}, {asl, zp, 5}, unnop11 , {php, imp, 3}, {ora, imm, 2}, {asl, acc, 2}, unnop11 , {tsb, ab, 6}, {ora, ab, 4}, {asl, ab, 6}, unnop11 , + /* 1_ */ {bpl, rel, 2}, {ora, yip, 5}, {ora, zpi, 5}, unnop11 , {trb, zp, 5}, {ora, zpx, 4}, {asl, zpx, 6}, unnop11 , {clc, imp, 2}, {ora, ayp, 4}, {inc, acc, 2}, unnop11 , {trb, ab, 6}, {ora, axp, 4}, {asl, axp, 6}, unnop11 , + /* 2_ */ {jsr, ab, 6}, { and, xin, 6}, ldd_imm , unnop11 , {bit, zp, 3}, { and, zp, 3}, {rol, zp, 5}, unnop11 , {plp, imp, 4}, { and, imm, 2}, {rol, acc, 2}, unnop11 , {bit, ab, 4}, { and, ab, 4}, {rol, ab, 6}, unnop11 , + /* 3_ */ {bmi, rel, 2}, { and, yip, 5}, { and, zpi, 5}, unnop11 , {bit, zpx, 4}, { and, zpx, 4}, {rol, zpx, 6}, unnop11 , {sec, imp, 2}, { and, ayp, 4}, {dec, acc, 2}, unnop11 , {bit, abx, 4}, { and, axp, 4}, {rol, axp, 6}, unnop11 , + /* 4_ */ {rti, imp, 6}, {eor, xin, 6}, ldd_imm , unnop11 , {ldd, zp, 3}, {eor, zp, 3}, {lsr, zp, 5}, unnop11 , {pha, imp, 3}, {eor, imm, 2}, {lsr, acc, 2}, unnop11 , {jmp, ab, 3}, {eor, ab, 4}, {lsr, ab, 6}, unnop11 , + /* 5_ */ {bvc, rel, 2}, {eor, yip, 5}, {eor, zpi, 5}, unnop11 , {ldd, zpx, 4}, {eor, zpx, 4}, {lsr, zpx, 6}, unnop11 , {cli, imp, 2}, {eor, ayp, 4}, {phy, imp, 3}, unnop11 , {ldd, ab, 8}, {eor, axp, 4}, {lsr, axp, 6}, unnop11 , + /* 6_ */ {rts, imp, 6}, {adc, xin, 6}, ldd_imm , unnop11 , {stz, zp, 3}, {adc, zp, 3}, {ror, zp, 5}, unnop11 , {pla, imp, 4}, {adc, imm, 2}, {ror, acc, 2}, unnop11 , {jmp, ind, 6}, {adc, ab, 4}, {ror, ab, 6}, unnop11 , + /* 7_ */ {bvs, rel, 2}, {adc, yip, 5}, {adc, zpi, 5}, unnop11 , {stz, zpx, 4}, {adc, zpx, 4}, {ror, zpx, 6}, unnop11 , {sei, imp, 2}, {adc, ayp, 4}, {ply, imp, 4}, unnop11 , {jmp,indx, 6}, {adc, axp, 4}, {ror, axp, 6}, unnop11 , + /* 8_ */ {bra, rel, 2}, {sta, xin, 6}, ldd_imm , unnop11 , {sty, zp, 3}, {sta, zp, 3}, {stx, zp, 3}, unnop11 , {dey, imp, 2}, {bit, imm, 2}, {txa, imp, 2}, unnop11 , {sty, ab, 4}, {sta, ab, 4}, {stx, ab, 4}, unnop11 , + /* 9_ */ {bcc, rel, 2}, {sta, yin, 6}, {sta, zpi, 5}, unnop11 , {sty, zpx, 4}, {sta, zpx, 4}, {stx, zpy, 4}, unnop11 , {tya, imp, 2}, {sta, aby, 5}, {txs, imp, 2}, unnop11 , {stz, ab, 4}, {sta, abx, 5}, {stz, abx, 5}, unnop11 , + /* A_ */ {ldy, imm, 2}, {lda, xin, 6}, {ldx, imm, 2}, unnop11 , {ldy, zp, 3}, {lda, zp, 3}, {ldx, zp, 3}, unnop11 , {tay, imp, 2}, {lda, imm, 2}, {tax, imp, 2}, unnop11 , {ldy, ab, 4}, {lda, ab, 4}, {ldx, ab, 4}, unnop11 , + /* B_ */ {bcs, rel, 2}, {lda, yip, 5}, {lda, zpi, 5}, unnop11 , {ldy, zpx, 4}, {lda, zpx, 4}, {ldx, zpy, 4}, unnop11 , {clv, imp, 2}, {lda, ayp, 4}, {tsx, imp, 2}, unnop11 , {ldy, axp, 4}, {lda, axp, 4}, {ldx, ayp, 4}, unnop11 , + /* C_ */ {cpy, imm, 2}, {cmp, xin, 6}, ldd_imm , unnop11 , {cpy, zp, 3}, {cmp, zp, 3}, {dec, zp, 5}, unnop11 , {iny, imp, 2}, {cmp, imm, 2}, {dex, imp, 2}, unnop11 , {cpy, ab, 4}, {cmp, ab, 4}, {dec, ab, 6}, unnop11 , + /* D_ */ {bne, rel, 2}, {cmp, yip, 5}, {cmp, zpi, 5}, unnop11 , {ldd, zpx, 4}, {cmp, zpx, 4}, {dec, zpx, 6}, unnop11 , {cld, imp, 2}, {cmp, ayp, 4}, {phx, imp, 3}, unnop11, {ldd, ab, 4}, {cmp, axp, 4}, {dec, abx, 7}, unnop11 , + /* E_ */ {cpx, imm, 2}, {sbc, xin, 6}, ldd_imm , unnop11 , {cpx, zp, 3}, {sbc, zp, 3}, {inc, zp, 5}, unnop11 , {inx, imp, 2}, {sbc, imm, 2}, {nop, imp, 2}, unnop11 , {cpx, ab, 4}, {sbc, ab, 4}, {inc, ab, 6}, unnop11 , + /* F_ */ {beq, rel, 2}, {sbc, yip, 5}, {sbc, zpi, 5}, unnop11 , {ldd, zpx, 4}, {sbc, zpx, 4}, {inc, zpx, 6}, unnop11 , {sed, imp, 2}, {sbc, ayp, 4}, {plx, imp, 4}, unnop11 , {ldd, ab, 4}, {sbc, axp, 4}, {inc, abx, 7}, unnop11 }; + + +static const vrEmu6502Opcode __not_in_flash("cpu") _wdc65c02[256] = { + /* | _0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | */ + /* 0_ */ {brk, imp, 7}, {ora, xin, 6}, ldd_imm , unnop11 , {tsb, zp, 5}, {ora, zp, 3}, {asl, zp, 5}, {rmb0, zp, 5}, {php, imp, 3}, {ora, imm, 2}, {asl, acc, 2}, unnop11 , {tsb, ab, 6}, {ora, ab, 4}, {asl, ab, 6}, {bbr0, zp, 5}, + /* 1_ */ {bpl, rel, 2}, {ora, yip, 5}, {ora, zpi, 5}, unnop11 , {trb, zp, 5}, {ora, zpx, 4}, {asl, zpx, 6}, {rmb1, zp, 5}, {clc, imp, 2}, {ora, ayp, 4}, {inc, acc, 2}, unnop11 , {trb, ab, 6}, {ora, axp, 4}, {asl, axp, 6}, {bbr1, zp, 5}, + /* 2_ */ {jsr, ab, 6}, { and, xin, 6}, ldd_imm , unnop11 , {bit, zp, 3}, { and, zp, 3}, {rol, zp, 5}, {rmb2, zp, 5}, {plp, imp, 4}, { and, imm, 2}, {rol, acc, 2}, unnop11 , {bit, ab, 4}, { and, ab, 4}, {rol, ab, 6}, {bbr2, zp, 5}, + /* 3_ */ {bmi, rel, 2}, { and, yip, 5}, { and, zpi, 5}, unnop11 , {bit, zpx, 4}, { and, zpx, 4}, {rol, zpx, 6}, {rmb3, zp, 5}, {sec, imp, 2}, { and, ayp, 4}, {dec, acc, 2}, unnop11 , {bit, abx, 4}, { and, axp, 4}, {rol, axp, 6}, {bbr3, zp, 5}, + /* 4_ */ {rti, imp, 6}, {eor, xin, 6}, ldd_imm , unnop11 , {ldd, zp, 3}, {eor, zp, 3}, {lsr, zp, 5}, {rmb4, zp, 5}, {pha, imp, 3}, {eor, imm, 2}, {lsr, acc, 2}, unnop11 , {jmp, ab, 3}, {eor, ab, 4}, {lsr, ab, 6}, {bbr4, zp, 5}, + /* 5_ */ {bvc, rel, 2}, {eor, yip, 5}, {eor, zpi, 5}, unnop11 , {ldd, zpx, 4}, {eor, zpx, 4}, {lsr, zpx, 6}, {rmb5, zp, 5}, {cli, imp, 2}, {eor, ayp, 4}, {phy, imp, 3}, unnop11 , {ldd, ab, 8}, {eor, axp, 4}, {lsr, axp, 6}, {bbr5, zp, 5}, + /* 6_ */ {rts, imp, 6}, {adc, xin, 6}, ldd_imm , unnop11 , {stz, zp, 3}, {adc, zp, 3}, {ror, zp, 5}, {rmb6, zp, 5}, {pla, imp, 4}, {adc, imm, 2}, {ror, acc, 2}, unnop11 , {jmp, ind, 6}, {adc, ab, 4}, {ror, ab, 6}, {bbr6, zp, 5}, + /* 7_ */ {bvs, rel, 2}, {adc, yip, 5}, {adc, zpi, 5}, unnop11 , {stz, zpx, 4}, {adc, zpx, 4}, {ror, zpx, 6}, {rmb7, zp, 5}, {sei, imp, 2}, {adc, ayp, 4}, {ply, imp, 4}, unnop11 , {jmp,indx, 6}, {adc, axp, 4}, {ror, axp, 6}, {bbr7, zp, 5}, + /* 8_ */ {bra, rel, 2}, {sta, xin, 6}, ldd_imm , unnop11 , {sty, zp, 3}, {sta, zp, 3}, {stx, zp, 3}, {smb0, zp, 5}, {dey, imp, 2}, {bit, imm, 2}, {txa, imp, 2}, unnop11 , {sty, ab, 4}, {sta, ab, 4}, {stx, ab, 4}, {bbs0, zp, 5}, + /* 9_ */ {bcc, rel, 2}, {sta, yin, 6}, {sta, zpi, 5}, unnop11 , {sty, zpx, 4}, {sta, zpx, 4}, {stx, zpy, 4}, {smb1, zp, 5}, {tya, imp, 2}, {sta, aby, 5}, {txs, imp, 2}, unnop11 , {stz, ab, 4}, {sta, abx, 5}, {stz, abx, 5}, {bbs1, zp, 5}, + /* A_ */ {ldy, imm, 2}, {lda, xin, 6}, {ldx, imm, 2}, unnop11 , {ldy, zp, 3}, {lda, zp, 3}, {ldx, zp, 3}, {smb2, zp, 5}, {tay, imp, 2}, {lda, imm, 2}, {tax, imp, 2}, unnop11 , {ldy, ab, 4}, {lda, ab, 4}, {ldx, ab, 4}, {bbs2, zp, 5}, + /* B_ */ {bcs, rel, 2}, {lda, yip, 5}, {lda, zpi, 5}, unnop11 , {ldy, zpx, 4}, {lda, zpx, 4}, {ldx, zpy, 4}, {smb3, zp, 5}, {clv, imp, 2}, {lda, ayp, 4}, {tsx, imp, 2}, unnop11 , {ldy, axp, 4}, {lda, axp, 4}, {ldx, ayp, 4}, {bbs3, zp, 5}, + /* C_ */ {cpy, imm, 2}, {cmp, xin, 6}, ldd_imm , unnop11 , {cpy, zp, 3}, {cmp, zp, 3}, {dec, zp, 5}, {smb4, zp, 5}, {iny, imp, 2}, {cmp, imm, 2}, {dex, imp, 2}, {wai, imp, 3}, {cpy, ab, 4}, {cmp, ab, 4}, {dec, ab, 6}, {bbs4, zp, 5}, + /* D_ */ {bne, rel, 2}, {cmp, yip, 5}, {cmp, zpi, 5}, unnop11 , {ldd, zpx, 4}, {cmp, zpx, 4}, {dec, zpx, 6}, {smb5, zp, 5}, {cld, imp, 2}, {cmp, ayp, 4}, {phx, imp, 3}, {stp, imp, 3}, {ldd, ab, 4}, {cmp, axp, 4}, {dec, abx, 7}, {bbs5, zp, 5}, + /* E_ */ {cpx, imm, 2}, {sbc, xin, 6}, ldd_imm , unnop11 , {cpx, zp, 3}, {sbc, zp, 3}, {inc, zp, 5}, {smb6, zp, 5}, {inx, imp, 2}, {sbc, imm, 2}, {nop, imp, 2}, unnop11 , {cpx, ab, 4}, {sbc, ab, 4}, {inc, ab, 6}, {bbs6, zp, 5}, + /* F_ */ {beq, rel, 2}, {sbc, yip, 5}, {sbc, zpi, 5}, unnop11 , {ldd, zpx, 4}, {sbc, zpx, 4}, {inc, zpx, 6}, {smb7, zp, 5}, {sed, imp, 2}, {sbc, ayp, 4}, {plx, imp, 4}, unnop11 , {ldd, ab, 4}, {sbc, axp, 4}, {inc, abx, 7}, {bbs7, zp, 5} }; + +static const vrEmu6502Opcode _r65c02[256] = { + /* | _0 | _1 | _2 | _3 | _4 | _5 | _6 | _7 | _8 | _9 | _A | _B | _C | _D | _E | _F | */ + /* 0_ */ {brk, imp, 7}, {ora, xin, 6}, ldd_imm , unnop11 , {tsb, zp, 5}, {ora, zp, 3}, {asl, zp, 5}, {rmb0, zp, 5}, {php, imp, 3}, {ora, imm, 2}, {asl, acc, 2}, unnop11 , {tsb, ab, 6}, {ora, ab, 4}, {asl, ab, 6}, {bbr0, zp, 5}, + /* 1_ */ {bpl, rel, 2}, {ora, yip, 5}, {ora, zpi, 5}, unnop11 , {trb, zp, 5}, {ora, zpx, 4}, {asl, zpx, 6}, {rmb1, zp, 5}, {clc, imp, 2}, {ora, ayp, 4}, {inc, acc, 2}, unnop11 , {trb, ab, 6}, {ora, axp, 4}, {asl, axp, 6}, {bbr1, zp, 5}, + /* 2_ */ {jsr, ab, 6}, { and, xin, 6}, ldd_imm , unnop11 , {bit, zp, 3}, { and, zp, 3}, {rol, zp, 5}, {rmb2, zp, 5}, {plp, imp, 4}, { and, imm, 2}, {rol, acc, 2}, unnop11 , {bit, ab, 4}, { and, ab, 4}, {rol, ab, 6}, {bbr2, zp, 5}, + /* 3_ */ {bmi, rel, 2}, { and, yip, 5}, { and, zpi, 5}, unnop11 , {bit, zpx, 4}, { and, zpx, 4}, {rol, zpx, 6}, {rmb3, zp, 5}, {sec, imp, 2}, { and, ayp, 4}, {dec, acc, 2}, unnop11 , {bit, abx, 4}, { and, axp, 4}, {rol, axp, 6}, {bbr3, zp, 5}, + /* 4_ */ {rti, imp, 6}, {eor, xin, 6}, ldd_imm , unnop11 , {ldd, zp, 3}, {eor, zp, 3}, {lsr, zp, 5}, {rmb4, zp, 5}, {pha, imp, 3}, {eor, imm, 2}, {lsr, acc, 2}, unnop11 , {jmp, ab, 3}, {eor, ab, 4}, {lsr, ab, 6}, {bbr4, zp, 5}, + /* 5_ */ {bvc, rel, 2}, {eor, yip, 5}, {eor, zpi, 5}, unnop11 , {ldd, zpx, 4}, {eor, zpx, 4}, {lsr, zpx, 6}, {rmb5, zp, 5}, {cli, imp, 2}, {eor, ayp, 4}, {phy, imp, 3}, unnop11 , {ldd, ab, 8}, {eor, axp, 4}, {lsr, axp, 6}, {bbr5, zp, 5}, + /* 6_ */ {rts, imp, 6}, {adc, xin, 6}, ldd_imm , unnop11 , {stz, zp, 3}, {adc, zp, 3}, {ror, zp, 5}, {rmb6, zp, 5}, {pla, imp, 4}, {adc, imm, 2}, {ror, acc, 2}, unnop11 , {jmp, ind, 6}, {adc, ab, 4}, {ror, ab, 6}, {bbr6, zp, 5}, + /* 7_ */ {bvs, rel, 2}, {adc, yip, 5}, {adc, zpi, 5}, unnop11 , {stz, zpx, 4}, {adc, zpx, 4}, {ror, zpx, 6}, {rmb7, zp, 5}, {sei, imp, 2}, {adc, ayp, 4}, {ply, imp, 4}, unnop11 , {jmp,indx, 6}, {adc, axp, 4}, {ror, axp, 6}, {bbr7, zp, 5}, + /* 8_ */ {bra, rel, 2}, {sta, xin, 6}, ldd_imm , unnop11 , {sty, zp, 3}, {sta, zp, 3}, {stx, zp, 3}, {smb0, zp, 5}, {dey, imp, 2}, {bit, imm, 2}, {txa, imp, 2}, unnop11 , {sty, ab, 4}, {sta, ab, 4}, {stx, ab, 4}, {bbs0, zp, 5}, + /* 9_ */ {bcc, rel, 2}, {sta, yin, 6}, {sta, zpi, 5}, unnop11 , {sty, zpx, 4}, {sta, zpx, 4}, {stx, zpy, 4}, {smb1, zp, 5}, {tya, imp, 2}, {sta, aby, 5}, {txs, imp, 2}, unnop11 , {stz, ab, 4}, {sta, abx, 5}, {stz, abx, 5}, {bbs1, zp, 5}, + /* A_ */ {ldy, imm, 2}, {lda, xin, 6}, {ldx, imm, 2}, unnop11 , {ldy, zp, 3}, {lda, zp, 3}, {ldx, zp, 3}, {smb2, zp, 5}, {tay, imp, 2}, {lda, imm, 2}, {tax, imp, 2}, unnop11 , {ldy, ab, 4}, {lda, ab, 4}, {ldx, ab, 4}, {bbs2, zp, 5}, + /* B_ */ {bcs, rel, 2}, {lda, yip, 5}, {lda, zpi, 5}, unnop11 , {ldy, zpx, 4}, {lda, zpx, 4}, {ldx, zpy, 4}, {smb3, zp, 5}, {clv, imp, 2}, {lda, ayp, 4}, {tsx, imp, 2}, unnop11 , {ldy, axp, 4}, {lda, axp, 4}, {ldx, ayp, 4}, {bbs3, zp, 5}, + /* C_ */ {cpy, imm, 2}, {cmp, xin, 6}, ldd_imm , unnop11 , {cpy, zp, 3}, {cmp, zp, 3}, {dec, zp, 5}, {smb4, zp, 5}, {iny, imp, 2}, {cmp, imm, 2}, {dex, imp, 2}, unnop11 , {cpy, ab, 4}, {cmp, ab, 4}, {dec, ab, 6}, {bbs4, zp, 5}, + /* D_ */ {bne, rel, 2}, {cmp, yip, 5}, {cmp, zpi, 5}, unnop11 , {ldd, zpx, 4}, {cmp, zpx, 4}, {dec, zpx, 6}, {smb5, zp, 5}, {cld, imp, 2}, {cmp, ayp, 4}, {phx, imp, 3}, unnop11 , {ldd, ab, 4}, {cmp, axp, 4}, {dec, abx, 7}, {bbs5, zp, 5}, + /* E_ */ {cpx, imm, 2}, {sbc, xin, 6}, ldd_imm , unnop11 , {cpx, zp, 3}, {sbc, zp, 3}, {inc, zp, 5}, {smb6, zp, 5}, {inx, imp, 2}, {sbc, imm, 2}, {nop, imp, 2}, unnop11 , {cpx, ab, 4}, {sbc, ab, 4}, {inc, ab, 6}, {bbs6, zp, 5}, + /* F_ */ {beq, rel, 2}, {sbc, yip, 5}, {sbc, zpi, 5}, unnop11 , {ldd, zpx, 4}, {sbc, zpx, 4}, {inc, zpx, 6}, {smb7, zp, 5}, {sed, imp, 2}, {sbc, ayp, 4}, {plx, imp, 4}, unnop11 , {ldd, ab, 4}, {sbc, axp, 4}, {inc, abx, 7}, {bbs7, zp, 5} }; + + +static const vrEmu6502Opcode* std6502 = _std6502; +static const vrEmu6502Opcode* std6502U = _std6502U; +static const vrEmu6502Opcode* std65c02 = _std65c02; +static const vrEmu6502Opcode* wdc65c02 = _wdc65c02; +static const vrEmu6502Opcode* r65c02 = _r65c02; + +/* ------------------------------------------------------------------ + * return addressing mode for an opcode + * Note: all values are cached in VrEmu6502.addrModes, so calling + * the public vrEmu6502GetOpcodeAddrMode() function will be fast + * ----------------------------------------------------------------*/ +static vrEmu6502AddrMode opcodeToAddrMode(VrEmu6502* vr6502, uint8_t opcode) +{ +#define opCodeAddrMode(x, y) if (oc->addrMode == x) return y + + const vrEmu6502Opcode* oc = &vr6502->opcodes[opcode]; + + opCodeAddrMode(ab, AddrModeAbs); + opCodeAddrMode(abx, AddrModeAbsX); + opCodeAddrMode(aby, AddrModeAbsY); + opCodeAddrMode(axp, AddrModeAbsX); + opCodeAddrMode(ayp, AddrModeAbsY); + opCodeAddrMode(imm, AddrModeImm); + opCodeAddrMode(ind, AddrModeAbsInd); + opCodeAddrMode(ind, AddrModeAbsIndX); + opCodeAddrMode(indx, AddrModeAbsIndX); + opCodeAddrMode(rel, AddrModeRel); + opCodeAddrMode(xin, AddrModeIndX); + opCodeAddrMode(yin, AddrModeIndY); + opCodeAddrMode(yip, AddrModeIndY); + opCodeAddrMode(zp, AddrModeZP); + opCodeAddrMode(zpi, AddrModeZPI); + opCodeAddrMode(zpx, AddrModeZPX); + opCodeAddrMode(zpy, AddrModeZPY); + + return AddrModeImp; +} + +/* ------------------------------------------------------------------ + * return opcode mnemonic string from opcode value + * Note: all values are cached in VrEmu6502.mnemonicNames, so calling + * the public vrEmu6502OpcodeToMnemonicStr() function will be fast + * ----------------------------------------------------------------*/ +static const char* opcodeToMnemonicStr(VrEmu6502* vr6502, uint8_t opcode) +{ +#define mnemonicToStr(x) if (oc->instruction == x) return #x + + const vrEmu6502Opcode* oc = &vr6502->opcodes[opcode]; + + mnemonicToStr(adc); + mnemonicToStr(and); + mnemonicToStr(asl); + mnemonicToStr(bra); + mnemonicToStr(bcc); + mnemonicToStr(bcs); + mnemonicToStr(beq); + mnemonicToStr(bit); + mnemonicToStr(bmi); + mnemonicToStr(bne); + mnemonicToStr(bpl); + mnemonicToStr(brk); + mnemonicToStr(bvc); + mnemonicToStr(bvs); + mnemonicToStr(clc); + mnemonicToStr(cld); + mnemonicToStr(cli); + mnemonicToStr(clv); + mnemonicToStr(cmp); + mnemonicToStr(cpx); + mnemonicToStr(cpy); + mnemonicToStr(dec); + mnemonicToStr(dex); + mnemonicToStr(dey); + mnemonicToStr(eor); + mnemonicToStr(inc); + mnemonicToStr(inx); + mnemonicToStr(iny); + mnemonicToStr(jmp); + mnemonicToStr(jsr); + mnemonicToStr(lda); + mnemonicToStr(ldd); + mnemonicToStr(ldx); + mnemonicToStr(ldy); + mnemonicToStr(lsr); + mnemonicToStr(nop); + mnemonicToStr(ora); + mnemonicToStr(pha); + mnemonicToStr(php); + mnemonicToStr(phx); + mnemonicToStr(phy); + mnemonicToStr(pla); + mnemonicToStr(plp); + mnemonicToStr(plx); + mnemonicToStr(ply); + mnemonicToStr(rol); + mnemonicToStr(ror); + mnemonicToStr(rti); + mnemonicToStr(rts); + mnemonicToStr(sbc); + mnemonicToStr(sec); + mnemonicToStr(sed); + mnemonicToStr(sei); + mnemonicToStr(sta); + mnemonicToStr(stp); + mnemonicToStr(stx); + mnemonicToStr(sty); + mnemonicToStr(stz); + mnemonicToStr(tax); + mnemonicToStr(tay); + mnemonicToStr(tsx); + mnemonicToStr(txa); + mnemonicToStr(txs); + mnemonicToStr(tya); + mnemonicToStr(trb); + mnemonicToStr(tsb); + mnemonicToStr(rmb0); + mnemonicToStr(rmb1); + mnemonicToStr(rmb2); + mnemonicToStr(rmb3); + mnemonicToStr(rmb4); + mnemonicToStr(rmb5); + mnemonicToStr(rmb6); + mnemonicToStr(rmb7); + mnemonicToStr(smb0); + mnemonicToStr(smb1); + mnemonicToStr(smb2); + mnemonicToStr(smb3); + mnemonicToStr(smb4); + mnemonicToStr(smb5); + mnemonicToStr(smb6); + mnemonicToStr(smb7); + mnemonicToStr(bbr0); + mnemonicToStr(bbr1); + mnemonicToStr(bbr2); + mnemonicToStr(bbr3); + mnemonicToStr(bbr4); + mnemonicToStr(bbr5); + mnemonicToStr(bbr6); + mnemonicToStr(bbr7); + mnemonicToStr(bbs0); + mnemonicToStr(bbs1); + mnemonicToStr(bbs2); + mnemonicToStr(bbs3); + mnemonicToStr(bbs4); + mnemonicToStr(bbs5); + mnemonicToStr(bbs6); + mnemonicToStr(bbs7); + mnemonicToStr(wai); + mnemonicToStr(slo); + mnemonicToStr(rla); + mnemonicToStr(sre); + mnemonicToStr(rra); + mnemonicToStr(sax); + mnemonicToStr(lax); + mnemonicToStr(dcp); + mnemonicToStr(isc); + mnemonicToStr(anc); + mnemonicToStr(alr); + mnemonicToStr(arr); + mnemonicToStr(sbx); + mnemonicToStr(las); + mnemonicToStr(sha); + mnemonicToStr(shx); + mnemonicToStr(shy); + mnemonicToStr(tas); + mnemonicToStr(ane); + mnemonicToStr(lxa); + mnemonicToStr(jam); + + return "err"; +} diff --git a/src/main.c b/src/main.c index 5ba694c..972b28f 100644 --- a/src/main.c +++ b/src/main.c @@ -128,11 +128,21 @@ #include -#define WITHOUT_FILE -#define WITHOUT_SPRITE -#define WITHOUT_TILE -#define F256LIB_IMPLEMENTATION -#include "f256lib.h" +#ifdef __F256K__ + #define WITHOUT_FILE + #define WITHOUT_SPRITE + #define WITHOUT_TILE + #define F256LIB_IMPLEMENTATION + #include "f256lib.h" + /* + * embedded.bin is loaded at 0x54000: + * + * 8k a2-3d2.bin @ 0x54000 + * 8k scene.3d @ 0x56000 + * + */ + EMBED(".binarydata.embedded", embedded, "embedded.bin"); +#endif #include "a23d2.h" @@ -168,16 +178,6 @@ byte _gamepad; #endif -/* - * embedded.bin is loaded at 0x54000: - * - * 8k a2-3d2.bin @ 0x54000 - * 8k scene.3d @ 0x56000 - * - */ -EMBED(".binarydata.embedded", embedded, "embedded.bin"); - - #if 0 //#define SEGMENT_MATH