├── .gitattributes ├── .gitignore ├── LICENSE ├── readme.md ├── resources ├── irtool-demo.gif └── irtool-sc.png └── src ├── gb-ir-bg.h ├── main.c └── main.gb /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 JinGen Lim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # IRTool 2 | 3 | ![IR Demo](https://raw.github.com/jglim/IRTool/master/resources/irtool-demo.gif) 4 | 5 | Use your Gameboy Color to control *National* air-conditioners! 6 | 7 | Build with [GBDK-N](https://github.com/andreasjhkarlsson/gbdk-n) (normal GBDK will not work). 8 | 9 | Also, use [ihx2gb.js](https://github.com/DonaldHays/bubblefactory/tree/969fca69c81a95578537b5e3d28407cc070d5e30/ihx2gb) to rebuild the ROM to enable GBC support using the `--cgb 192` flag. 10 | 11 | ![BGB Screenshot](https://raw.github.com/jglim/IRTool/master/resources/irtool-sc.png) 12 | 13 | The precompiled GBC ROM can be found in the repository as `main.gb`. 14 | 15 | A writeup on this tool is also available [here](http://jg.sn.sg/ir/). 16 | 17 | # License 18 | 19 | MIT 20 | -------------------------------------------------------------------------------- /resources/irtool-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglim/IRTool/f35230940a2b334ebfd48a4bb83e700deeeebfa8/resources/irtool-demo.gif -------------------------------------------------------------------------------- /resources/irtool-sc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglim/IRTool/f35230940a2b334ebfd48a4bb83e700deeeebfa8/resources/irtool-sc.png -------------------------------------------------------------------------------- /src/gb-ir-bg.h: -------------------------------------------------------------------------------- 1 | // /////////////////////// 2 | // // // 3 | // // File Attributes // 4 | // // // 5 | // /////////////////////// 6 | 7 | // Filename: gb-ir-bg.png 8 | // Pixel Width: 160px 9 | // Pixel Height: 144px 10 | 11 | // ///////////////// 12 | // // // 13 | // // Constants // 14 | // // // 15 | // ///////////////// 16 | 17 | const int gbirbg_tile_map_size = 0x0168; 18 | const int gbirbg_tile_map_width = 0x14; 19 | const int gbirbg_tile_map_height = 0x12; 20 | 21 | const int gbirbg_tile_data_size = 0x0340; 22 | const int gbirbg_tile_count = 0x34; 23 | 24 | // //////////////// 25 | // // // 26 | // // Map Data // 27 | // // // 28 | // //////////////// 29 | 30 | const unsigned char gbirbg_map_data[] ={ 31 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 32 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 33 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x00,0x00, 34 | 0x04,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x06, 35 | 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x06,0x00,0x00,0x00, 36 | 0x00,0x00,0x00,0x06,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x06,0x1C,0x1D, 37 | 0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26, 38 | 0x27,0x1E,0x28,0x29,0x2A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 39 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 40 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 41 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 42 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x00,0x2C,0x00,0x00, 43 | 0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2E,0x2F,0x30,0x00,0x00, 44 | 0x00,0x31,0x32,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 45 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 46 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 47 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 48 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 49 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 50 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 51 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 52 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 53 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 54 | }; 55 | 56 | // ///////////////// 57 | // // // 58 | // // Tile Data // 59 | // // // 60 | // ///////////////// 61 | 62 | const unsigned char gbirbg_tile_data[] ={ 63 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 64 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x3F,0x00,0x3F, 65 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x00,0x83,0x00,0xC3, 66 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0xF0,0x00,0xF0, 67 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0xFC, 68 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x7E,0x00,0x7E, 69 | 0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0x3F, 70 | 0x00,0xC3,0x00,0xE3,0x00,0xE3,0x00,0xF3,0x00,0xF3,0x00,0xFB,0x00,0xFB,0x00,0xFF, 71 | 0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF1,0x00,0xF1, 72 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x7F,0x00,0xFF,0x00,0xF9,0x00,0xF1, 73 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x00,0xF3,0x00,0xF3,0x00,0xFB,0x00,0xF8, 74 | 0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFC, 75 | 0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0xFE,0x00,0xFE,0x00,0xFE,0x00,0x7E, 76 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x3F,0x00,0x7F,0x00,0xFF,0x00,0xFC, 77 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0xF0,0x00,0xF8,0x00,0xFC,0x00,0xFC, 78 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0xFD,0x00,0xFF,0x00,0xFF,0x00,0xFE, 79 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0xFE,0x00,0xFE,0x00,0xFF,0x00,0x7F, 80 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x0F,0x00,0x1F,0x00,0x3F,0x00,0x3E, 81 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0xFE,0x00,0xFE,0x00,0x3F,0x00,0x3F, 82 | 0x00,0x7F,0x00,0x7F,0x00,0x3F,0x00,0x1F,0x00,0x1F,0x00,0x0F,0x00,0x0F,0x00,0x07, 83 | 0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF1,0x00,0xF3,0x00,0xF3,0x00,0xF3,0x00,0xF3, 84 | 0x00,0x01,0x00,0x3F,0x00,0xFF,0x00,0xFF,0x00,0xF9,0x00,0xF1,0x00,0xF1,0x00,0xFF, 85 | 0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8, 86 | 0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFF,0x00,0xFF, 87 | 0x00,0x7F,0x00,0x7F,0x00,0x7F,0x00,0x7F,0x00,0x7F,0x00,0x7E,0x00,0xFE,0x00,0xFE, 88 | 0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0xFC,0x00,0xFF,0x00,0x7F, 89 | 0x00,0x7E,0x00,0x7E,0x00,0x7E,0x00,0x7E,0x00,0x7E,0x00,0xFC,0x00,0xFC,0x00,0xF8, 90 | 0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC,0x00,0xFC, 91 | 0x00,0x00,0x00,0x07,0x00,0x1F,0x00,0x3F,0x00,0x7F,0x00,0x7E,0x00,0x7E,0x00,0x7F, 92 | 0x00,0x3F,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x3F,0x00,0x3F,0x00,0x3F,0x00,0xFF, 93 | 0x00,0x3F,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 94 | 0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 95 | 0x00,0xF1,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 96 | 0x00,0xFF,0x00,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 97 | 0x00,0xF8,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 98 | 0x00,0x7F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 99 | 0x00,0xFE,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 100 | 0x00,0x3F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 101 | 0x00,0xF0,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 102 | 0x00,0xFC,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 103 | 0x00,0x3F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 104 | 0x00,0xFF,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 105 | 0x00,0x3F,0x00,0xBF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 106 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xF3,0xF3, 107 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x28,0x28, 108 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x18,0x18, 109 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00, 110 | 0xF7,0xF7,0x77,0x77,0x1F,0x1F,0xF8,0xF8,0xEE,0xEE,0xEF,0xEF,0xCF,0xCF,0x06,0x06, 111 | 0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 112 | 0x13,0x13,0x07,0x07,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x07,0x07,0x03,0x03,0x00,0x00, 113 | 0xC0,0xC0,0xE0,0xE0,0x70,0x70,0x00,0x00,0x70,0x70,0xE0,0xE0,0xC0,0xC0,0x00,0x00, 114 | 0x38,0x38,0x78,0x78,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0x78,0x78,0x00,0x00 115 | }; 116 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "gb-ir-bg.h" 5 | 6 | #define VERSION 100 7 | 8 | #define IR_INIT \ 9 | __asm__("push hl ; save HL since we will trash it"); \ 10 | __asm__("ld hl, #0xFF56 ; load RP_REG address at 0xFF56"); 11 | 12 | #define IR_UNINIT \ 13 | __asm__("pop hl ; restore HL register"); 14 | 15 | // pulse commands require INIT_IR first so that HL registers point the right way 16 | #define IR_ON \ 17 | __asm__("ld (hl), #01 ; IR on"); 18 | 19 | #define IR_OFF \ 20 | __asm__("ld (hl), #00 ; IR off"); 21 | 22 | #define IR_WAIT \ 23 | __asm__("nop"); \ 24 | __asm__("nop"); \ 25 | __asm__("nop"); \ 26 | __asm__("nop"); \ 27 | __asm__("nop"); \ 28 | __asm__("nop"); \ 29 | __asm__("nop"); \ 30 | __asm__("nop"); \ 31 | __asm__("nop"); \ 32 | __asm__("nop"); \ 33 | __asm__("nop"); \ 34 | __asm__("nop"); \ 35 | __asm__("nop"); \ 36 | __asm__("nop"); \ 37 | __asm__("nop"); \ 38 | __asm__("nop"); \ 39 | __asm__("nop"); \ 40 | __asm__("nop"); \ 41 | __asm__("nop"); \ 42 | __asm__("nop"); \ 43 | __asm__("nop"); \ 44 | __asm__("nop"); \ 45 | __asm__("nop"); \ 46 | __asm__("nop"); \ 47 | __asm__("nop"); \ 48 | 49 | // marks and spaces are composed of 33 on-off cycles 50 | #define IR_MARK \ 51 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 52 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 53 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 54 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 55 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 56 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 57 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 58 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 59 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 60 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 61 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 62 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 63 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 64 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 65 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 66 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 67 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 68 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 69 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 70 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 71 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 72 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 73 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 74 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 75 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 76 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 77 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 78 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 79 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 80 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 81 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 82 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 83 | IR_ON; IR_WAIT; IR_OFF; IR_WAIT; \ 84 | 85 | // omitting last row = 32 cycles since we have to compensate for loop delay 86 | #define IR_SPACE \ 87 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 88 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 89 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 90 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 91 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 92 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 93 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 94 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 95 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 96 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 97 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 98 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 99 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 100 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 101 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 102 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 103 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 104 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 105 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 106 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 107 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 108 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 109 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 110 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 111 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 112 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 113 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 114 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 115 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 116 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 117 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 118 | IR_OFF; IR_WAIT; IR_OFF; IR_WAIT; \ 119 | 120 | 121 | 122 | UWORD bkgPalette[] = { 123 | RGB_WHITE, RGB_ORANGE, RGB_RED, RGB_BLACK 124 | }; 125 | 126 | UWORD spritePalette[] = { 127 | (UINT16)0, RGB_RED, RGB_BLACK, RGB_BLACK, 128 | (UINT16)0, RGB_YELLOW, RGB_YELLOW, RGB_YELLOW 129 | }; 130 | 131 | unsigned char fnt[] = 132 | { 133 | 0x7C,0x7C,0xC6,0xC6,0xCE,0xCE,0xDE,0xDE, 134 | 0xF6,0xF6,0xE6,0xE6,0x7C,0x7C,0x00,0x00, 135 | 0x30,0x30,0x70,0x70,0x30,0x30,0x30,0x30, 136 | 0x30,0x30,0x30,0x30,0xFC,0xFC,0x00,0x00, 137 | 0x78,0x78,0xCC,0xCC,0x0C,0x0C,0x38,0x38, 138 | 0x60,0x60,0xCC,0xCC,0xFC,0xFC,0x00,0x00, 139 | 0x78,0x78,0xCC,0xCC,0x0C,0x0C,0x38,0x38, 140 | 0x0C,0x0C,0xCC,0xCC,0x78,0x78,0x00,0x00, 141 | 0x1C,0x1C,0x3C,0x3C,0x6C,0x6C,0xCC,0xCC, 142 | 0xFE,0xFE,0x0C,0x0C,0x1E,0x1E,0x00,0x00, 143 | 0xFC,0xFC,0xC0,0xC0,0xF8,0xF8,0x0C,0x0C, 144 | 0x0C,0x0C,0xCC,0xCC,0x78,0x78,0x00,0x00, 145 | 0x38,0x38,0x60,0x60,0xC0,0xC0,0xF8,0xF8, 146 | 0xCC,0xCC,0xCC,0xCC,0x78,0x78,0x00,0x00, 147 | 0xFC,0xFC,0xCC,0xCC,0x0C,0x0C,0x18,0x18, 148 | 0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00, 149 | 0x78,0x78,0xCC,0xCC,0xCC,0xCC,0x78,0x78, 150 | 0xCC,0xCC,0xCC,0xCC,0x78,0x78,0x00,0x00, 151 | 0x78,0x78,0xCC,0xCC,0xCC,0xCC,0x7C,0x7C, 152 | 0x0C,0x0C,0x18,0x18,0x70,0x70,0x00,0x00, 153 | 0x30,0x30,0x78,0x78,0xCC,0xCC,0xCC,0xCC, 154 | 0xFC,0xFC,0xCC,0xCC,0xCC,0xCC,0x00,0x00, 155 | 0x7E,0x7E,0x60,0x60,0x60,0x60,0x78,0x78, 156 | 0x60,0x60,0x60,0x60,0x60,0x60,0x00,0x00, 157 | 0x3C,0x3C,0x66,0x66,0xC0,0xC0,0xC0,0xC0, 158 | 0xC0,0xC0,0x66,0x66,0x3C,0x3C,0x00,0x00, 159 | 0xF8,0xF8,0x6C,0x6C,0x66,0x66,0x66,0x66, 160 | 0x66,0x66,0x6C,0x6C,0xF8,0xF8,0x00,0x00, 161 | 0x00,0x00,0x18,0x18,0x3C,0x3C,0x7E,0x7E, 162 | 0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00, 163 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 164 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 165 | }; 166 | 167 | 168 | // default: 24deg fan1 mode2 169 | volatile BYTE symbols[] = 170 | { 171 | 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 172 | 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 173 | 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 174 | 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 175 | 176 | 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 177 | 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 178 | 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // original test packet ends at the void 179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180 | 181 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185 | 186 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190 | }; 191 | 192 | UBYTE irCursor = 0; 193 | 194 | UBYTE lastKey = 0; 195 | UBYTE loopIndex = 0; 196 | 197 | UBYTE acFan = 3; // 0: auto, 1:1, 2:3, 3:3 198 | UBYTE acTemperature = 24; // 16-30 199 | UBYTE acMode = 0; // tbd 200 | UBYTE acUpdateType = 0; // 0 -> update only, 1 -> toggle state 201 | 202 | UBYTE selectedAcProperty = 1; 203 | 204 | // write long pulse: 4 symbols of HIGH, 4 symbols of LOW 205 | // used to start and end a packet 206 | void wl() 207 | { 208 | symbols[irCursor++] = 1; 209 | symbols[irCursor++] = 1; 210 | symbols[irCursor++] = 1; 211 | symbols[irCursor++] = 1; 212 | symbols[irCursor++] = 0; 213 | symbols[irCursor++] = 0; 214 | symbols[irCursor++] = 0; 215 | symbols[irCursor++] = 0; 216 | } 217 | 218 | // write medium pulse: 1 symbol of HIGH, 3 symbols of LOW 219 | void wm() 220 | { 221 | symbols[irCursor++] = 1; 222 | symbols[irCursor++] = 0; 223 | symbols[irCursor++] = 0; 224 | symbols[irCursor++] = 0; 225 | } 226 | 227 | // write short pulse: 1 symbol of HIGH, 1 symbol of LOW 228 | void ws() 229 | { 230 | symbols[irCursor++] = 1; 231 | symbols[irCursor++] = 0; 232 | } 233 | 234 | void irTransmitBuffer() 235 | { 236 | __asm__("ld b,b"); // debugger 237 | 238 | disable_interrupts(); 239 | loopIndex = 0; 240 | while (loopIndex < 0xFF) 241 | { 242 | if (symbols[loopIndex]) 243 | { 244 | // IR on (mark) 245 | IR_INIT; 246 | IR_MARK; 247 | IR_UNINIT; 248 | } 249 | else 250 | { 251 | // IR off (space) 252 | IR_INIT; 253 | IR_SPACE; 254 | IR_UNINIT; 255 | } 256 | loopIndex++; 257 | } 258 | enable_interrupts(); 259 | } 260 | 261 | 262 | void irResetPacket() 263 | { 264 | // clear buffer and reset cursor 265 | loopIndex = 0; 266 | while (loopIndex < 0xFF) 267 | { 268 | symbols[loopIndex] = 0; 269 | loopIndex++; 270 | } 271 | irCursor = 0; 272 | } 273 | 274 | void irWriteUnknown() 275 | { 276 | ws();ws();ws();ws(); 277 | } 278 | 279 | void irWriteUpdateFlag() 280 | { 281 | if (acUpdateType == 0) 282 | { 283 | wm(); // update 284 | } 285 | else 286 | { 287 | ws(); // toggle power state (default) 288 | } 289 | } 290 | 291 | void irWriteMode() 292 | { 293 | /* 294 | SSS : FAN 295 | SMS : COOL 296 | MMS : DRY 297 | MMM : AUTO INCREASE (ignore) 298 | MSM : AUTO DECREASE (ignore) 299 | SMM : AUTO MAINTAIN (ignore) 300 | */ 301 | 302 | if (acMode == 0) 303 | { 304 | // cool 305 | ws(); wm(); ws(); 306 | } 307 | else if (acMode == 1) 308 | { 309 | // dry 310 | wm(); wm(); ws(); 311 | } 312 | else if (acMode == 2) 313 | { 314 | // auto 315 | wm(); wm(); wm(); 316 | } 317 | else if (acMode == 3) 318 | { 319 | // fan 320 | ws(); ws(); ws(); 321 | } 322 | } 323 | 324 | void irWriteFan() 325 | { 326 | if (acFan == 1) 327 | { 328 | ws(); wm(); ws(); ws(); 329 | } 330 | else if (acFan == 2) 331 | { 332 | ws(); ws(); wm(); ws(); 333 | } 334 | else if (acFan == 3) 335 | { 336 | ws(); wm(); wm(); ws(); 337 | } 338 | else if (acFan == 0) 339 | { 340 | // auto 341 | wm(); wm(); wm(); wm(); 342 | } 343 | } 344 | 345 | void irWriteTemperature() 346 | { 347 | if (acTemperature == 16) 348 | { 349 | wm(); ws(); ws(); ws(); 350 | } 351 | else if (acTemperature == 17) 352 | { 353 | ws(); wm(); ws(); ws(); 354 | } 355 | else if (acTemperature == 18) 356 | { 357 | wm(); wm(); ws(); ws(); 358 | } 359 | else if (acTemperature == 19) 360 | { 361 | ws(); ws(); wm(); ws(); 362 | } 363 | else if (acTemperature == 20) 364 | { 365 | wm(); ws(); wm(); ws(); 366 | } 367 | else if (acTemperature == 21) 368 | { 369 | ws(); wm(); wm(); ws(); 370 | } 371 | else if (acTemperature == 22) 372 | { 373 | wm(); wm(); wm(); ws(); 374 | } 375 | else if (acTemperature == 23) 376 | { 377 | ws(); ws(); ws(); wm(); 378 | } 379 | else if (acTemperature == 24) 380 | { 381 | wm(); ws(); ws(); wm(); 382 | } 383 | else if (acTemperature == 25) 384 | { 385 | ws(); wm(); ws(); wm(); 386 | } 387 | else if (acTemperature == 26) 388 | { 389 | wm(); wm(); ws(); wm(); 390 | } 391 | else if (acTemperature == 27) 392 | { 393 | ws(); ws(); wm(); wm(); 394 | } 395 | else if (acTemperature == 28) 396 | { 397 | wm(); ws(); wm(); wm(); 398 | } 399 | else if (acTemperature == 29) 400 | { 401 | ws(); wm(); wm(); wm(); 402 | } 403 | else if (acTemperature == 30) 404 | { 405 | wm(); wm(); wm(); wm(); 406 | } 407 | } 408 | 409 | void irCreatePacket() 410 | { 411 | irResetPacket(); 412 | 413 | // preamble 414 | wl(); 415 | 416 | irWriteTemperature(); 417 | irWriteFan(); 418 | irWriteTemperature(); 419 | irWriteFan(); 420 | 421 | irWriteMode(); 422 | irWriteUpdateFlag(); 423 | irWriteUnknown(); 424 | irWriteMode(); 425 | irWriteUpdateFlag(); 426 | irWriteUnknown(); 427 | 428 | // terminate packet 429 | wl(); 430 | ws(); 431 | 432 | } 433 | 434 | void refreshNumbers() 435 | { 436 | // temp 437 | if (acTemperature == 16) 438 | { 439 | set_sprite_tile(0, 1); 440 | set_sprite_tile(1, 6); 441 | } 442 | else if (acTemperature == 17) 443 | { 444 | set_sprite_tile(0, 1); 445 | set_sprite_tile(1, 7); 446 | } 447 | else if (acTemperature == 18) 448 | { 449 | set_sprite_tile(0, 1); 450 | set_sprite_tile(1, 8); 451 | } 452 | else if (acTemperature == 19) 453 | { 454 | set_sprite_tile(0, 1); 455 | set_sprite_tile(1, 9); 456 | } 457 | else if (acTemperature == 20) 458 | { 459 | set_sprite_tile(0, 2); 460 | set_sprite_tile(1, 0); 461 | } 462 | else if (acTemperature == 21) 463 | { 464 | set_sprite_tile(0, 2); 465 | set_sprite_tile(1, 1); 466 | } 467 | else if (acTemperature == 22) 468 | { 469 | set_sprite_tile(0, 2); 470 | set_sprite_tile(1, 2); 471 | } 472 | else if (acTemperature == 23) 473 | { 474 | set_sprite_tile(0, 2); 475 | set_sprite_tile(1, 3); 476 | } 477 | else if (acTemperature == 24) 478 | { 479 | set_sprite_tile(0, 2); 480 | set_sprite_tile(1, 4); 481 | } 482 | else if (acTemperature == 25) 483 | { 484 | set_sprite_tile(0, 2); 485 | set_sprite_tile(1, 5); 486 | } 487 | else if (acTemperature == 26) 488 | { 489 | set_sprite_tile(0, 2); 490 | set_sprite_tile(1, 6); 491 | } 492 | else if (acTemperature == 27) 493 | { 494 | set_sprite_tile(0, 2); 495 | set_sprite_tile(1, 7); 496 | } 497 | else if (acTemperature == 28) 498 | { 499 | set_sprite_tile(0, 2); 500 | set_sprite_tile(1, 8); 501 | } 502 | else if (acTemperature == 29) 503 | { 504 | set_sprite_tile(0, 2); 505 | set_sprite_tile(1, 9); 506 | } 507 | else if (acTemperature == 30) 508 | { 509 | set_sprite_tile(0, 3); 510 | set_sprite_tile(1, 0); 511 | } 512 | 513 | // mode 514 | if (acMode == 0) 515 | { 516 | // cool 517 | set_sprite_tile(2, 12); 518 | } 519 | else if (acMode == 1) 520 | { 521 | // dry 522 | set_sprite_tile(2, 13); 523 | } 524 | else if (acMode == 2) 525 | { 526 | // auto 527 | set_sprite_tile(2, 10); 528 | } 529 | else if (acMode == 3) 530 | { 531 | // fan 532 | set_sprite_tile(2, 11); 533 | } 534 | 535 | // fan 536 | if (acFan == 0) 537 | { 538 | set_sprite_tile(3, 10); 539 | } 540 | else if (acFan == 1) 541 | { 542 | set_sprite_tile(3, 1); 543 | } 544 | else if (acFan == 2) 545 | { 546 | set_sprite_tile(3, 2); 547 | } 548 | else if (acFan == 3) 549 | { 550 | set_sprite_tile(3, 3); 551 | } 552 | 553 | // cursor 554 | if (selectedAcProperty == 0) 555 | { 556 | move_sprite(4, 40, 120); // 40 84 128 557 | } 558 | else if (selectedAcProperty == 1) 559 | { 560 | move_sprite(4, 84, 120); // 40 84 128 561 | } 562 | else if (selectedAcProperty == 2) 563 | { 564 | move_sprite(4, 128, 120); // 40 84 128 565 | } 566 | } 567 | 568 | void beepFeedback() 569 | { 570 | NR10_REG = 0x38U; 571 | NR11_REG = 0x70U; 572 | NR12_REG = 0xE0U; 573 | NR13_REG = 0x0AU; 574 | NR14_REG = 0xC6U; 575 | NR51_REG |= 0x11; 576 | } 577 | 578 | void main(void) 579 | { 580 | // initialize font 581 | SPRITES_8x8; 582 | // load 15 raw sprites 583 | set_sprite_data(0, 15, fnt); 584 | 585 | // init 2 palettes 586 | set_sprite_palette(0, 2, spritePalette); 587 | 588 | // set all sprites to use palette zero 589 | set_sprite_prop(0, 0); 590 | set_sprite_prop(1, 0); 591 | set_sprite_prop(2, 0); 592 | set_sprite_prop(3, 0); 593 | set_sprite_prop(4, 0); 594 | set_sprite_prop(5, 0); 595 | set_sprite_prop(6, 0); 596 | set_sprite_prop(7, 0); 597 | set_sprite_prop(8, 0); 598 | set_sprite_prop(9, 0); 599 | set_sprite_prop(10, 0); 600 | set_sprite_prop(11, 0); 601 | set_sprite_prop(12, 0); 602 | set_sprite_prop(13, 0); 603 | set_sprite_prop(14, 0); 604 | set_sprite_prop(15, 0); 605 | 606 | // set sprite images 607 | set_sprite_tile(4, 14); 608 | refreshNumbers(); 609 | 610 | // move sprites to their locations 611 | move_sprite(0, 80, 110); // temp digit 1 612 | move_sprite(1, 88, 110); // temp digit 2 613 | move_sprite(2, 128, 110); // mode 614 | move_sprite(3, 41, 110); // fan, nudged 1px right 615 | 616 | // cursor "little arrow" 617 | //move_sprite(4, 128, 120); // 40 84 128 618 | SHOW_SPRITES; 619 | 620 | // initialize display 621 | set_bkg_data(0, gbirbg_tile_count, gbirbg_tile_data); 622 | VBK_REG = 1; 623 | VBK_REG = 0; 624 | set_bkg_tiles(0, 0, gbirbg_tile_map_width, gbirbg_tile_map_height, gbirbg_map_data); 625 | set_bkg_palette(0, 1, bkgPalette); 626 | SHOW_BKG; 627 | DISPLAY_ON; 628 | 629 | // switch into CGB double clock speed 630 | disable_interrupts(); 631 | cpu_fast(); 632 | enable_interrupts(); 633 | 634 | // initialize audio 635 | NR50_REG = 0xFF; 636 | NR51_REG = 0xFF; 637 | NR52_REG = 0x80; 638 | 639 | 640 | while(1) 641 | { 642 | lastKey = joypad(); 643 | // left/right for switching between aircon attributes 644 | if (lastKey == J_RIGHT) 645 | { 646 | if (selectedAcProperty == 0) { selectedAcProperty = 1; } 647 | else if (selectedAcProperty == 1) { selectedAcProperty = 2; } 648 | else if (selectedAcProperty == 2) { selectedAcProperty = 2; } 649 | } 650 | else if (lastKey == J_LEFT) 651 | { 652 | if (selectedAcProperty == 2) { selectedAcProperty = 1; } 653 | else if (selectedAcProperty == 1) { selectedAcProperty = 0; } 654 | else if (selectedAcProperty == 0) { selectedAcProperty = 0; } 655 | } 656 | // up/down for incrementing/decrementing aircon attributes 657 | else if (lastKey == J_UP) 658 | { 659 | if (selectedAcProperty == 0) 660 | { 661 | acFan++; 662 | if (acFan > 3) 663 | { 664 | acFan = 3; 665 | } 666 | } 667 | else if (selectedAcProperty == 1) 668 | { 669 | acTemperature++; 670 | if (acTemperature > 30) 671 | { 672 | acTemperature = 30; 673 | } 674 | } 675 | else if (selectedAcProperty == 2) 676 | { 677 | acMode++; 678 | if (acMode > 3) 679 | { 680 | acMode = 3; 681 | } 682 | } 683 | } 684 | else if (lastKey == J_DOWN) 685 | { 686 | if (selectedAcProperty == 0) 687 | { 688 | if (acFan != 0) 689 | { 690 | acFan--; 691 | } 692 | } 693 | else if (selectedAcProperty == 1) 694 | { 695 | if (acTemperature != 16) 696 | { 697 | acTemperature--; 698 | } 699 | } 700 | else if (selectedAcProperty == 2) 701 | { 702 | if (acMode != 0) 703 | { 704 | acMode--; 705 | } 706 | } 707 | } 708 | else if (lastKey == J_SELECT) 709 | { 710 | 711 | } 712 | else if (lastKey == J_START) 713 | { 714 | 715 | } 716 | else if (lastKey == J_A) 717 | { 718 | // commit changes AND change ac state 719 | acUpdateType = 1; 720 | beepFeedback(); 721 | irCreatePacket(); 722 | irTransmitBuffer(); 723 | } 724 | else if (lastKey == J_B) 725 | { 726 | // commit changes only 727 | acUpdateType = 0; 728 | beepFeedback(); 729 | irCreatePacket(); 730 | irTransmitBuffer(); 731 | } 732 | refreshNumbers(); 733 | delay(200); 734 | } 735 | } 736 | -------------------------------------------------------------------------------- /src/main.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglim/IRTool/f35230940a2b334ebfd48a4bb83e700deeeebfa8/src/main.gb --------------------------------------------------------------------------------