├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── apps ├── forth │ ├── bcd.words │ ├── block.c │ ├── block.words │ ├── bytepair.words │ ├── compile.c │ ├── compiling.words │ ├── console.words │ ├── controlstructures.words │ ├── debug.c │ ├── debug.words │ ├── defining.words │ ├── dict.c │ ├── disasm.c │ ├── disasm.h │ ├── draw_ops.inc │ ├── editor.c │ ├── environment.words │ ├── error_codes.c │ ├── error_codes.h │ ├── forth.c │ ├── forth.h │ ├── forth_defs.h │ ├── forth_macros.inc │ ├── forth_mem_layout.S │ ├── forth_opcode_types.h │ ├── forth_opcodes.S │ ├── forth_opcodes.h │ ├── forth_opcodes_2.S │ ├── forth_opcodes_3.S │ ├── forth_outer_interpreter.S │ ├── forth_regs.h │ ├── forth_version.h │ ├── forth_video.c │ ├── forth_words.S │ ├── hwregs.words │ ├── interpret.c │ ├── io.c │ ├── iovec.words │ ├── keyboard.words │ ├── math.c │ ├── parsing.words │ ├── random.words │ ├── special.words │ ├── testsuite.fs │ ├── video.words │ └── wordlist-to-tsv.py ├── launcher │ └── launcher.c ├── mandel │ └── mandel.c ├── plasma │ └── plasma.c ├── rfk │ ├── fortunes.nki.h │ ├── matt.nki │ ├── matt.nki.h │ ├── nki-to-h.py │ ├── rfk.c │ ├── vanilla.nki │ ├── vanillaplusplus.nki │ └── vanillaplusplus.nki.h ├── serialterminal │ ├── dec_graphics_extra_8x8.h │ ├── dec_graphics_extra_8x8.png │ ├── serialterminal.c │ ├── vtparser.c │ └── vtparser.h ├── slideshow │ └── slideshow.c ├── test_bitmap_modes │ └── test_bitmap_modes.c ├── test_colors │ └── test_colors.c ├── test_keys │ └── test_keys.c ├── test_mode_alignment │ └── test_mode_alignment.c ├── test_patterns │ └── test_patterns.c ├── test_raster_interrupts │ └── test_raster_interrupts.c ├── test_split_screen │ └── test_split_screen.c ├── test_text_modes │ ├── _testfnt │ └── test_text_modes.c └── test_tiled_modes │ └── test_tiled_modes.c ├── assets ├── amscii_font_8x8.S ├── amscii_font_8x8.png ├── bitspreadtable.S ├── cosine.S ├── crumbquadtable.S ├── hsvtable.S ├── nibbledoubletable.S ├── test_tileset_8x8.S └── test_tileset_8x8.png ├── atmega1284_amethyst.ld ├── banned_stdlib_functions ├── bootloader ├── README_bootloader.txt ├── optiboot-amethyst-support.patch └── optiboot_amethyst.hex ├── circuit ├── .gitignore ├── ChangeLog ├── DESIGN_FLAWS.txt ├── avr-colorvideo-board.pdf ├── avr-colorvideo-schematic.pdf ├── avr-colorvideo.kicad_pcb ├── avr-colorvideo.pro ├── avr-colorvideo.sch ├── bill-of-materials.md ├── board-changes.txt ├── eeprom-board │ ├── eeprom-board.kicad_pcb │ ├── eeprom-board.pro │ ├── eeprom-board.sch │ └── fp-lib-table ├── fp-lib-table ├── rca.pretty │ └── RCA_Jack_Jameco_KY-006-1.kicad_mod └── sym-lib-table ├── doc ├── Amethyst Specs.pdf ├── Ramforth Reference Manual.pdf └── asmtips.txt ├── enclosure ├── enclosure-flattened.svg └── enclosure.svg ├── include ├── amscii.h ├── app.h ├── audio.h ├── colors.h ├── console.h ├── console_macros.h ├── defs.h ├── fastrand.h ├── isr_reserved_registers.h ├── keycodes.h ├── keys.h ├── minio.h ├── note_freqs.h ├── note_numbers.h ├── numeric_io.h ├── raster_interrupt.h ├── readline.h ├── serial.h ├── spi.h ├── video.h ├── videodefs.h └── videomodes.h ├── palettes ├── 2bit-bo-preview.png ├── 2bit-bo.gpl ├── 2bit-gm-preview.png ├── 2bit-gm.gpl ├── 2bit.gpl ├── 4bit-bo-preview.png ├── 4bit-bo.gpl ├── 4bit-gm-preview.png ├── 4bit-gm.gpl ├── 4bit-preview.png ├── 4bit.gpl ├── 8bit-preview.png ├── 8bit.gpl ├── calcpalette.py └── hsv.gpl ├── scripts ├── analyze-register-usage.py ├── codegen_40x25_color_text_romfont.py ├── colorbargen.py ├── colorize_16x8_font.py ├── generate-app-linker-script-fragment.sh ├── generate-app-linker-script-overlay.sh ├── generate-bitspread-table.py ├── genfont.py ├── genhsvtable.py ├── gentrigtable.py ├── img2bin.py ├── regusage.html ├── serial-slideshow.py ├── serial-stress-test.py ├── serialconsole.plist ├── sortpalette.py ├── to16colorbin.py └── tone-freqs.py ├── sim ├── Makefile ├── sim.cpp ├── uart_pty.cpp └── uart_pty.h └── system ├── app.c ├── app_launch.S ├── asm_macros.inc ├── audio.c ├── avr_mcu_section.h ├── console.c ├── fastrand.c ├── isrs.S ├── keys.c ├── linehandler_40x25_color_text_romfont.inc ├── main.c ├── minio.c ├── minio_decimal.S ├── minio_handlers.S ├── note_freqs.c ├── randseed.S ├── readline.c ├── serial.c ├── spi.c ├── tinyprintf.c ├── video.c └── videomodes.S /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.elf 3 | *.hex 4 | *.o 5 | *.lst 6 | *.map 7 | *.swp 8 | *.gbr 9 | *.drl 10 | build 11 | junk 12 | stackusage.txt 13 | sim/amsim 14 | .vscode/ipch 15 | scripts/*.jpg 16 | scripts/*.png 17 | scripts/*.bin 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Matt Sarnoff 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Amethyst Colour Video System 2 | 3 | https://www.youtube.com/watch?v=6GKgxBEGH1M 4 | 5 | The Amethyst is a retro-styled "home computer" built around an Atmel ATmega1284 microcontroller. 6 | It features composite (NTSC) video output with both high-color and high-resolution modes, mono 7 | audio output, and a full-travel mechanical keyboard. It includes a full-featured implementation 8 | of the powerful Forth programming language, with graphics and sound commands, debugger, and 9 | screen editor. A single USB Type-B cable provides 5V power as well as serial communication with 10 | a PC. Amethyst is compatible with the Optiboot (Arduino) bootloader, and new firmware may be 11 | uploaded to it without a specialized programming device. 12 | 13 | 14 | ## Full specifications 15 | 16 | - ATmega1284 microcontroller (8-bit) running at 14.318 MHz 17 | - 16KB internal memory (16352 bytes available for applications) 18 | - 4KB nonvolatile EEPROM memory 19 | - Mechanical keyboard with Cherry MX keyswitches 20 | - NTSC color and monochrome composite video output: 21 | 22 | - 256-color, 16-color, and 4-color bitmap graphics at resolutions up to 160x200 pixels 23 | - Monochrome bitmap graphics at resolutions up to 640x200 pixels 24 | - 40x25 and 80x25 monochrome text modes 25 | - 40x25 color text mode 26 | - 40x25 color tiled graphics modes 27 | 28 | - Single-channel audio output (pulse wave or PWM) 29 | - USB serial communication at speeds up to 57600 baud 30 | - Four SPI expansion ports for peripherals, controllers, and storage devices 31 | - Only 6 chips (ATmega1284, FT320X, 2x 74HC157, 2x 74HC166) 32 | 33 | **TODO 1/5/20: add more detailed docs, instructions, and BOM** 34 | 35 | 36 | ## About 37 | 38 | Amethyst is a hobby project and is not currently available for purchase in kit or built form. 39 | 40 | Copyright 2019 Matt Sarnoff. 41 | 42 | https://twitter.com/txsector 43 | 44 | http://msarnoff.org 45 | -------------------------------------------------------------------------------- /apps/forth/bcd.words: -------------------------------------------------------------------------------- 1 | 2 | 3 | ; BCD. ( u -- ) print binary-coded-decimal number as 4 hex digits (with leading zeros) 4 | ; this is just an alias for H. 5 | dictentry_colon "BCD." 6 | op hdot 7 | dictentryend 8 | 9 | ; DBCD. ( d -- ) print double-cell binary-coded-decimal number as 8 hex digits 10 | ; (with leading zeros). This is just an alias for DH. 11 | dictentry_colon "DBCD." 12 | op hdot 13 | op hdot 14 | dictentryend 15 | 16 | ; BCD2* ( u1 -- u2 ) multiply binary-coded-decimal number by 2 17 | dictentry_colon "BCD2*" 18 | op dup 19 | opx bcdplus 20 | dictentryend 21 | 22 | ; BCD10* ( u1 -- u2 ) multiply binary-coded-decimal number by 10 23 | dictentry_colon "BCD10*" 24 | l8 4 25 | opx lshift 26 | dictentryend 27 | 28 | ; BCD10/ ( u1 -- u2 ) divide binary-coded-decimal number by 10 (truncating) 29 | dictentry_colon "BCD10/" 30 | l8 4 31 | opx rshift 32 | dictentryend 33 | 34 | ; BCD100* ( u1 -- u2 ) multiply binary-coded-decimal number by 100 35 | dictentry_colon "BCD100*" 36 | op twofivesixstar 37 | dictentryend 38 | 39 | ; BCD100/ ( u1 -- u2 ) divide binary-coded-decimal number by 100 (truncating) 40 | dictentry_colon "BCD100/" 41 | op highbyte 42 | dictentryend 43 | 44 | ; BCD100MOD ( u1 -- u2 ) binary-coded-decimal modulo 100 45 | dictentry_colon "BCD100MOD" 46 | op lowbyte 47 | dictentryend 48 | 49 | ; BCD100/MOD ( u1 -- rem quot ) binary-coded-decimal remainder and quotient after 50 | ; division by 100 51 | dictentry_colon "BCD100/MOD" 52 | op splitbytes 53 | dictentryend 54 | 55 | ; BCD>ASCII ( u -- d ) push 4-byte ASCII representation of the binary-coded-decimal 56 | ; number on top of the stack. Just an alias for U>HEX. 57 | dictentry_colon "BCD>ASCII" 58 | opx utohex 59 | dictentryend 60 | 61 | ; BCD>ASCII! ( u c-addr -- ) store 4-byte ASCII representation of a 62 | ; binary-coded-decimal number to memory 63 | dictentry_colon "BCD>ASCII!" 64 | op swap ; ( c-addr u -- ) 65 | opx utohex ; ( c-addr u1 u2 -- ) 66 | op rot 67 | op twostore 68 | dictentryend 69 | -------------------------------------------------------------------------------- /apps/forth/block.c: -------------------------------------------------------------------------------- 1 | #include "forth.h" 2 | #include "forth_opcodes.h" 3 | #include "spi.h" 4 | #include "minio.h" 5 | #include 6 | 7 | void load_block_range(char *dst, uint8_t blocknum, uint16_t offset, uint16_t nbytes) 8 | { 9 | if (blocknum <= NUM_EEPROM_BLOCKS) { 10 | uint16_t src = ((uint16_t)blocknum-1)*BLOCK_SIZE; 11 | src += offset; 12 | eeprom_read_block(dst, (char*)src, nbytes); 13 | } else { 14 | uint16_t src = ((uint16_t)blocknum-NUM_EEPROM_BLOCKS-1)*BLOCK_SIZE; 15 | src += offset; 16 | spi_eeprom_read_block(dst, src, nbytes); 17 | } 18 | } 19 | 20 | 21 | void save_block_range(const char *src, uint8_t blocknum, uint16_t offset, uint16_t nbytes) 22 | { 23 | if (blocknum <= NUM_EEPROM_BLOCKS) { 24 | uint16_t dst = ((uint16_t)blocknum-1)*BLOCK_SIZE; 25 | dst += offset; 26 | eeprom_update_block(src, (void*)dst, nbytes); 27 | } else { 28 | uint16_t dst = (((uint16_t)blocknum)-NUM_EEPROM_BLOCKS-1)*BLOCK_SIZE; 29 | dst += offset; 30 | spi_eeprom_write_block(src, dst, nbytes); 31 | } 32 | } 33 | 34 | 35 | static void fill_block(uint8_t blocknum, char c) 36 | { 37 | uint16_t maxblk = num_blocks(); 38 | if (blocknum == 0 || blocknum > maxblk) { 39 | forth_throw(FE_INVALID_BLOCK_NUMBER); 40 | } else if (blocknum <= NUM_EEPROM_BLOCKS) { 41 | uint32_t *dst = (uint32_t*)(((uint16_t)blocknum-1)*BLOCK_SIZE); 42 | union { char byte[4]; uint32_t dword; } val = { .byte = {c,c,c,c}}; 43 | uint8_t n = BLOCK_SIZE/4; 44 | while (n--) { eeprom_update_dword(dst++, val.dword); } 45 | } else { 46 | uint16_t dst = (((uint16_t)blocknum)-NUM_EEPROM_BLOCKS-1)*BLOCK_SIZE; 47 | spi_eeprom_memset(dst, c, BLOCK_SIZE); 48 | } 49 | } 50 | 51 | 52 | static void copy_block(uint8_t dstblk, uint8_t srcblk) 53 | { 54 | uint16_t maxblk = num_blocks(); 55 | if ((srcblk == 0 || srcblk > maxblk) || 56 | (dstblk == 0 || dstblk > maxblk)) { 57 | forth_throw(FE_INVALID_BLOCK_NUMBER); 58 | } 59 | /* use the pictured numeric output buffer as temporary space */ 60 | uint16_t offset = 0; 61 | uint8_t n = BLOCK_SIZE/32; 62 | while (n--) { 63 | load_block_range(forth_hld0, srcblk, offset, 32); 64 | save_block_range(forth_hld0, dstblk, offset, 32); 65 | offset += 32; 66 | } 67 | } 68 | 69 | 70 | void fill_blocks(uint8_t startblk, uint8_t nblocks, char c) { 71 | while (nblocks--) { fill_block(startblk++, c); } 72 | } 73 | 74 | 75 | void copy_blocks(uint8_t dstblk, uint8_t srcblk, uint8_t nblocks) { 76 | while (nblocks--) { copy_block(dstblk++, srcblk++); } 77 | } 78 | 79 | 80 | uint8_t num_xmem_blocks(void) { 81 | return (spi_eeprom_present()) ? NUM_XMEM_BLOCKS : 0; 82 | } 83 | 84 | 85 | uint8_t num_blocks(void) { 86 | return NUM_EEPROM_BLOCKS + num_xmem_blocks(); 87 | } 88 | 89 | 90 | void chain_load_next_block(void) { 91 | /* raise if current input source is not a block */ 92 | if (!forth_inputsrc.isblock || forth_inputsrc.blk == 0) { 93 | forth_throw(FE_INVALID_BLOCK_NUMBER); 94 | } 95 | /* raise if next block isn't a valid block number */ 96 | if (forth_inputsrc.blk+1 == 0 || forth_inputsrc.blk+1 > num_blocks()) { 97 | forth_throw(FE_INVALID_BLOCK_NUMBER); 98 | } 99 | /* discard remainder of current line */ 100 | forth_inputpos = forth_inputlen; 101 | /* next block */ 102 | forth_inputsrc.blk += 1; 103 | forth_inputsrc.blkline = 0; 104 | } 105 | 106 | 107 | /* runtime of --> word */ 108 | cell do_chain_load_next_block(void) { 109 | chain_load_next_block(); 110 | /* no code for the VM to execute */ 111 | forth_rom_word_buf[0] = OP_endword; 112 | return (cell)forth_rom_word_buf; 113 | } 114 | 115 | 116 | void forth_dot(cell n, cell width); 117 | void list_block(uint8_t blocknum) { 118 | forth_scr = blocknum; 119 | if (blocknum == 0 || blocknum > num_blocks()) { 120 | forth_throw(FE_INVALID_BLOCK_NUMBER); 121 | } 122 | Pnl(); Psl("Block "); Pu8(blocknum); Pnl(); 123 | uint16_t src = 0; 124 | for (uint8_t r = 0; r < LINES_PER_BLOCK; r++) { 125 | forth_dot(r, 2); Pbl(); 126 | for (uint8_t c = 0; c < BLOCK_BYTES_PER_LINE; c++) { 127 | char ch = 0; 128 | load_block_range(&ch, blocknum, src++, 1); 129 | Pc(ch); 130 | } 131 | Pnl(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /apps/forth/block.words: -------------------------------------------------------------------------------- 1 | ; NOTE: this word returns a *byte* address, must use C@ 2 | dictentry_colon "SCR" 3 | laddr forth_scr 4 | dictentryend 5 | 6 | dictentry_interpretonly "EDIT" 7 | ccall edit_block 8 | opx load 9 | dictentryend 10 | 11 | dictentry_interpretonly "LIST" 12 | ccall list_block 13 | op drop 14 | dictentryend 15 | 16 | dictentry_colon "COPY-BLOCK" 17 | l8 1 18 | opx copyblocks 19 | dictentryend 20 | 21 | dictentry_colon "FILL-BLOCK" 22 | l8 1 23 | op swap 24 | opx fillblocks 25 | dictentryend 26 | 27 | dictentry_colon "ERASE-BLOCKS" 28 | l8 0x20 ; fill with blank character 29 | opx fillblocks 30 | dictentryend 31 | 32 | dictentry_colon "ERASE-BLOCK" 33 | l8 1 34 | op zero 35 | opx fillblocks 36 | dictentryend 37 | 38 | ; ED ( -- ) edit last block listed/edited 39 | dictentry_interpretonly "ED" 40 | .byte OP_cfetchlithi, HIGHADDR(forth_scr) 41 | ccall edit_block 42 | opx load 43 | dictentryend 44 | 45 | ; LD ( -- ) load last block listed/edited 46 | dictentry_colon "LD" 47 | .byte OP_cfetchlithi, HIGHADDR(forth_scr) 48 | opx load 49 | dictentryend 50 | 51 | ; Bytes per block (from gforth) 52 | dictentry_colon "CHARS/BLOCK" 53 | laddr BLOCK_SIZE 54 | dictentryend 55 | 56 | ; Bytes per block (alias) 57 | dictentry_colon "/BLOCK" 58 | laddr BLOCK_SIZE 59 | dictentryend 60 | 61 | ; Number of blocks in internal EEPROM 62 | dictentry_colon "#EE-BLOCKS" 63 | l8 NUM_EEPROM_BLOCKS 64 | dictentryend 65 | 66 | ; Block lines per screen (from gforth) 67 | dictentry_colon "L/S" 68 | l8 LINES_PER_BLOCK 69 | dictentryend 70 | 71 | ; Block characters per line (from gforth) 72 | dictentry_colon "C/L" 73 | l8 BLOCK_BYTES_PER_LINE 74 | dictentryend 75 | 76 | ; push -1 if input source is a string 77 | ; push 0 if input source is a terminal 78 | ; push n>0 if input source is a block 79 | dictentry_colon "SOURCE-ID" 80 | .byte OP_cfetchlithi, HIGHADDR(forth_inputsrc) 81 | op minus1 82 | .byte OP_cfetchbitshi, 0x80, HIGHADDR(forth_inputsrc+1) 83 | op select 84 | dictentryend 85 | 86 | dictentry_colon "SAVE-INPUT" 87 | .byte OP_fetchlithi, HIGHADDR(forth_inputsrc) 88 | .byte OP_fetchlithi, HIGHADDR(forth_inputlen) ; fetch length AND position 89 | dictentryend 90 | 91 | dictentry_colon "RESTORE-INPUT" 92 | .byte OP_storelithi, HIGHADDR(forth_inputlen) ; store length AND position 93 | .byte OP_storelithi, HIGHADDR(forth_inputsrc) 94 | op zero ; push 0 flag indicating the input source was restored 95 | dictentryend 96 | 97 | ; Number of the block being interpreted. 0 if input source is not a block. 98 | dictentry_colon "BLK@" 99 | .byte OP_cfetchlithi, HIGHADDR(forth_inputsrc) 100 | op zero 101 | .byte OP_cfetchbitshi, 0x80, HIGHADDR(forth_inputsrc+1) 102 | op select 103 | dictentryend 104 | 105 | dictheader "-->" 106 | cfunc_immediate do_chain_load_next_block 107 | dictentryend 108 | -------------------------------------------------------------------------------- /apps/forth/bytepair.words: -------------------------------------------------------------------------------- 1 | ; alias to match "X^Y" byte pair literal syntax 2 | dictentry_colon "^" 3 | op mergebytes 4 | dictentryend 5 | 6 | dictentry_colon "XY>" 7 | op splitbytes 8 | dictentryend 9 | 10 | dictentry_colon ">XY" 11 | op mergebytes 12 | dictentryend 13 | 14 | dictentry_colon ">X" 15 | op tolowbyte 16 | dictentryend 17 | 18 | dictentry_colon ">Y" 19 | op tohighbyte 20 | dictentryend 21 | 22 | dictentry_colon "XC" 23 | op lowbyte 24 | dictentryend 25 | 26 | dictentry_colon "YC" 27 | op highbyte 28 | dictentryend 29 | 30 | dictentry_colon "XY+" 31 | op bytepairplus 32 | dictentryend 33 | 34 | dictentry_colon "XY-" 35 | op bytepairminus 36 | dictentryend 37 | 38 | dictentry_colon "X+" 39 | op lplus 40 | dictentryend 41 | 42 | dictentry_colon "Y+" 43 | op hplus 44 | dictentryend 45 | 46 | dictentry_colon "XNEGATE" 47 | op lnegate 48 | dictentryend 49 | 50 | dictentry_colon "YNEGATE" 51 | op hnegate 52 | dictentryend 53 | -------------------------------------------------------------------------------- /apps/forth/compiling.words: -------------------------------------------------------------------------------- 1 | ; DOES> ( -- ) 2 | ; At compile time: ends the current definition and begins a new anonymous 3 | ; definition in code space. (the parent definition) 4 | dictheader "DOES>" 5 | cfunc_compile_only do_compile_does 6 | .word FL_COLON 7 | dictentryend 8 | 9 | ; ::DOES> ( -- ) 10 | ; At compile time: ends the current definition and begins a new anonymous 11 | ; definition in name space. (the parent definition) 12 | dictheader "::DOES>" 13 | cfunc_compile_only do_compile_does 14 | .word FL_COMPILER 15 | dictentryend 16 | 17 | ; is executed. 21 | dictheader " ( -- ) 46 | ; Ends the custom compilation semantics of the current word, and indicates the 47 | ; beginning of the implementation semantics. 48 | dictheader "RUNS>" 49 | cfunc_compile_only do_runs 50 | dictentryend 51 | 52 | ; ['] ( -- xt ) look up name and compile execution token 53 | dictentry_compileonly "[']" 54 | op tick ; get xt (or break if not found) 55 | ccall compile_literal 56 | op drop 57 | dictentryend 58 | 59 | ; [COMP'] ( -- ct ) look up name and compile compilation token 60 | dictentry_compileonly "[COMP']" 61 | opx comptick ; get ct (or break if not found) 62 | ccall compile_literal 63 | op drop 64 | dictentryend 65 | 66 | ; ABORT" ( n -- ) pop TOS; abort and print message if TOS is nonzero 67 | dictentry_compileonly "ABORT\x22" 68 | opx parsequote ; get addr/len - stack is now ( srcaddr len ) 69 | ccall compile_abortq ; stack is now ( srcaddr flag ) 70 | op twodrop 71 | dictentryend 72 | 73 | dictentry_compileonly "POSTPONE" 74 | op tick ; get xt (or break if not found) 75 | ccall compile_xt_compsem ; compile the xt's compilation semantics 76 | op drop 77 | dictentryend 78 | 79 | dictheader "RECURSE" 80 | cfunc_compile_only compile_recursive_call 81 | dictentryend 82 | -------------------------------------------------------------------------------- /apps/forth/console.words: -------------------------------------------------------------------------------- 1 | dictentry_colon "+CURSOR" 2 | op minus1 3 | op cstorebitshi 4 | .byte fcon_bitmask_cursorphase 5 | .byte HIGHADDR(fcon_cursorphase) 6 | dictentryend 7 | 8 | dictentry_colon "-CURSOR" 9 | op zero 10 | op cstorebitshi 11 | .byte fcon_bitmask_cursorphase 12 | .byte HIGHADDR(fcon_cursorphase) 13 | dictentryend 14 | 15 | dictentry_colon "SLOW" 16 | op minus1 17 | op cstorebitshi 18 | .byte fcon_bitmask_slow 19 | .byte HIGHADDR(fcon_slow) 20 | dictentryend 21 | 22 | dictentry_colon "FAST" 23 | op zero 24 | op cstorebitshi 25 | .byte fcon_bitmask_slow 26 | .byte HIGHADDR(fcon_slow) 27 | dictentryend 28 | 29 | dictentry_colon "+RVS" 30 | op minus1 31 | op cstorebitshi 32 | .byte fcon_bitmask_rvs 33 | .byte HIGHADDR(fcon_rvs) 34 | dictentryend 35 | 36 | dictentry_colon "-RVS" 37 | op zero 38 | op cstorebitshi 39 | .byte fcon_bitmask_rvs 40 | .byte HIGHADDR(fcon_rvs) 41 | dictentryend 42 | 43 | dictentry_colon "XY>CADDR" 44 | opx xytoaddr 45 | laddr 1000 46 | op plus 47 | dictentryend 48 | 49 | dictentry_colon "THLIN" 50 | op lowbyte 51 | opx tbox 52 | dictentryend 53 | 54 | dictentry_colon "TVLIN" 55 | op twofivesixstar 56 | opx tbox 57 | dictentryend 58 | 59 | dictentry_colon "-WINDOW" 60 | op zero 61 | op zero 62 | opx window 63 | dictentryend 64 | 65 | dictentry_colon "FRAMES" 66 | dictentryend 67 | 68 | dictentry_colon "FRAME" 69 | dictentryend 70 | 71 | dictentry_colon "SECOND" 72 | l8 60 73 | opx star 74 | dictentryend 75 | 76 | dictentry_colon "SECONDS" 77 | l8 60 78 | opx star 79 | dictentryend 80 | 81 | dictentry_colon "MS" 82 | l8 60 83 | laddr 1000 84 | opx starslash 85 | dictentryend 86 | -------------------------------------------------------------------------------- /apps/forth/controlstructures.words: -------------------------------------------------------------------------------- 1 | .macro mark, opc 2 | .byte OP_mark 3 | .byte OP_\opc 4 | .endm 5 | 6 | dictentry_compileonly "IF" 7 | mark zbranchfwd ; push current location onto stack and compile dummy (0BRANCH) 8 | dictentryend 9 | 10 | dictentry_compileonly "AHEAD" 11 | mark branchfwd ; push current location onto stack and compile dummy (BRANCH) 12 | dictentryend 13 | 14 | dictentry_compileonly "ELSE" 15 | mark branchfwd ; push current location onto stack and compile dummy (BRANCH) 16 | op swap ; bring address of first (0BRANCH) back to top 17 | opx resolve ; resolve previous (0BRANCH) 18 | dictentryend 19 | 20 | dictentry_compileonly "THEN" 21 | opx resolve ; resolve relative branch 22 | dictentryend 23 | 24 | dictentry_compileonly "BEGIN" 25 | op here 26 | dictentryend 27 | 28 | dictentry_compileonly "AGAIN" 29 | mark branchback ; compile dummy (BRANCH) 30 | opx bresolve ; resolve backward branch 31 | dictentryend 32 | 33 | dictentry_compileonly "UNTIL" 34 | mark zbranchback ; compile dummy (0BRANCH) instruction 35 | opx bresolve ; resolve backward branch 36 | dictentryend 37 | 38 | dictentry_compileonly "WHILE" 39 | mark zbranchfwd ; push address of dummy (0BRANCH) 40 | op swap ; bring address of loop start back to top 41 | dictentryend 42 | 43 | dictentry_compileonly "REPEAT" 44 | mark branchback ; compile dummy (BRANCH) instruction to start of loop 45 | opx bresolve ; resolve backward branch to start of loop 46 | opx resolve ; resolve forward (0BRANCH) to end of loop 47 | dictentryend 48 | 49 | dictentry_compileonly_rstack "DO" 50 | op zero ; push a 0 on return stack to mark end of leave stack 51 | op tor 52 | op litccomma 53 | .byte OP_twotor ; compile a (DO) instruction 54 | op here ; push loop start address 55 | dictentryend 56 | 57 | dictentry_compileonly_rstack "?DO" 58 | op zero ; push a 0 on return stack to mark end of leave stack 59 | op tor 60 | mark qdo ; push address of dummy (BRANCH) onto leave stack (return stack) 61 | op tor 62 | op here ; push loop start address 63 | dictentryend 64 | 65 | dictentry_compileonly_rstack "XY-DO" 66 | op zero ; push a 0 on return stack to mark end of leave stack 67 | op tor 68 | op litccomma 69 | .byte OP_zero ; compile an instruction that pushes a 0 on the stack at runtime 70 | op litccomma 71 | .byte OP_twotor ; compile a (DO) instruction 72 | op here ; push loop start address 73 | dictentryend 74 | 75 | dictentry_compileonly_rstack "LEAVE" 76 | op litccomma ; compile 2>R to drop loop context 77 | .byte OP_twordrop 78 | mark branchfwd ; push address of dummy (BRANCH) onto leave stack (return stack) 79 | op tor 80 | dictentryend 81 | 82 | dictentry_compileonly_rstack "LOOP" 83 | mark loop ; compile test and branch to start of loop 84 | opx bresolve 85 | opx rake 86 | dictentryend 87 | 88 | dictentry_compileonly_rstack "+LOOP" 89 | mark plusloop ; compile test and branch to start of loop 90 | opx bresolve 91 | opx rake 92 | dictentryend 93 | 94 | dictentry_compileonly_rstack "XY-LOOP" 95 | mark xyloop ; compile test and branch to start of loop 96 | opx bresolve 97 | opx rake 98 | dictentryend 99 | 100 | dictentry_compileonly_rstack "CASE" 101 | op zero 102 | op tor ; push a 0 on return stack to mark end of leave stack 103 | dictentryend 104 | 105 | dictentry_compileonly "OF" 106 | mark of ; compile an (OF) and put address of its branch offset on the stack 107 | dictentryend 108 | 109 | dictentry_compileonly_rstack "ENDOF" 110 | mark branchfwd ; compile (BRANCH) to end of case 111 | op tor ; put address of its branch offset on the leave stack (return stack) 112 | opx resolve ; resolve forward branch of previous (OF) 113 | dictentryend 114 | 115 | dictentry_compileonly_rstack "ENDCASE" 116 | op litccomma ; compile a DROP to delete the case selector 117 | .byte OP_drop 118 | opx rake 119 | dictentryend -------------------------------------------------------------------------------- /apps/forth/debug.words: -------------------------------------------------------------------------------- 1 | dictentry_colon ".S" 2 | opx dumpstack 3 | .byte -1 4 | dictentryend 5 | 6 | dictentry_colon "U.S" 7 | opx dumpstack 8 | .byte 1 9 | dictentryend 10 | 11 | dictentry_colon "H.S" 12 | opx dumpstack 13 | .byte 0 14 | dictentryend 15 | 16 | dictentry_immediate "[.S]" 17 | opx dumpstack 18 | .byte -1 19 | dictentryend 20 | 21 | dictentry_immediate "[U.S]" 22 | opx dumpstack 23 | .byte 1 24 | dictentryend 25 | 26 | dictentry_immediate "[H.S]" 27 | opx dumpstack 28 | .byte 0 29 | dictentryend 30 | 31 | dictheader "WORDS" 32 | cfunc_interpret_only dict_print_words 33 | dictentryend 34 | 35 | dictentry_interpretonly "DUMP" 36 | ccall hexdump_range 37 | op twodrop 38 | dictentryend 39 | 40 | dictheader "DESCRIBE" 41 | cfunc_interpret_only do_describe 42 | dictentryend 43 | 44 | dictheader "SEE" 45 | cfunc_interpret_only do_see 46 | dictentryend 47 | 48 | dictheader "DEBUG" 49 | cfunc_interpret_only debug_enter 50 | dictentryend 51 | 52 | dictheader "RESUME" 53 | cfunc_interpret_only debug_exit 54 | dictentryend 55 | 56 | dictheader "TRACE" 57 | cfunc_interpret_only debug_trace 58 | dictentryend 59 | 60 | ; /CODE ( -- u ) number of bytes of used code space 61 | dictentry_colon "/CODE" 62 | .byte OP_fetchlithi, HIGHADDR(forth_cp) 63 | laddr forth_cp0 64 | op minus 65 | dictentryend 66 | 67 | ; /NAME ( -- u ) number of bytes of used name space 68 | dictentry_colon "/NAME" 69 | .byte OP_fetchlithi, HIGHADDR(forth_np) 70 | .byte OP_fetchlithi, HIGHADDR(forth_np0) 71 | op minus 72 | dictentryend 73 | -------------------------------------------------------------------------------- /apps/forth/disasm.c: -------------------------------------------------------------------------------- 1 | #include "forth.h" 2 | #include "disasm.h" 3 | #include "forth_opcodes.h" 4 | #include "minio.h" 5 | 6 | #include 7 | 8 | #undef X 9 | #define X(opnum,str,props,asmid,enumid,v) static __attribute__((unused)) const char opcode_name_ ## enumid[] PROGMEM = str; 10 | OPCODES 11 | 12 | #undef X 13 | #define X(opnum,str,props,asmid,enumid,v) static __attribute__((unused)) const char ext_opcode_name_ ## enumid[] PROGMEM = str; 14 | EXT_OPCODES 15 | 16 | #undef X 17 | #define X(opnum,str,props,asmid,enumid,v) opcode_name_ ## asmid, 18 | static const char * const opcode_names[] PROGMEM = { 19 | OPCODES 20 | }; 21 | 22 | #undef X 23 | #define X(opnum,str,props,asmid,enumid,v) ext_opcode_name_ ## asmid, 24 | static const char * const ext_opcode_names[] PROGMEM = { 25 | EXT_OPCODES 26 | }; 27 | 28 | #undef X 29 | #define X(opnum,str,props,asmid,enumid,v) OP_TYPE_TO_FLAGS(OP_##props), 30 | static const uint8_t opcode_properties[] PROGMEM = { 31 | OPCODES 32 | }; 33 | 34 | 35 | static uint8_t *vm_disassemble_helper(uint8_t *location, const char *opname, uint8_t opflags) 36 | { 37 | uint8_t opbyte1 = location[0]; 38 | uint8_t opbyte2 = location[1]; 39 | uint8_t opbyte3 = location[2]; 40 | switch (OP_FLAGS_TYPE(opflags)) { 41 | case OP_2U: 42 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Pbl(); Pu8(opbyte2); Pnl(); 43 | return location+2; 44 | case OP_2X: 45 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Pbl(); Pu8(opbyte2); Pnl(); 46 | return location+2; 47 | case OP_2I: 48 | Pbl(); Px8(opbyte2); Psl(" "); Psp(pgm_read_ptr(ext_opcode_names+opbyte2)); Pnl(); 49 | return location+2; 50 | case OP_2F: 51 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Pbl(); Pu8(opbyte2); Psl(" ["); 52 | Px16(((cell)location)+2+opbyte2); Psl("]\n"); 53 | return location+2; 54 | case OP_2B: 55 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Pbl(); Pu8(opbyte2); Psl(" ["); 56 | Px16(((cell)location)-opbyte2); Psl("]\n"); 57 | return location+2; 58 | case OP_2C: 59 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Pbl(); Px8(opbyte1); Px8(opbyte2); Pnl(); 60 | return location+2; 61 | case OP_3S: 62 | Pbl(); Px8(opbyte2); Pbl(); Px8(opbyte3); Psl(" "); Psp(opname); Pbl(); Pd16(((opbyte3<<8)|opbyte2)); Pnl(); 63 | return location+3; 64 | case OP_3X: 65 | Pbl(); Px8(opbyte2); Pbl(); Px8(opbyte3); Psl(" "); Psp(opname); Pbl(); Px16(((opbyte3<<8)|opbyte2)); Pnl(); 66 | return location+3; 67 | case OP_STR: 68 | Pbl(); Px8(opbyte2); Psl(" "); Psp(opname); Psl(" ["); 69 | mio_putns((char *)(location+2), opbyte2); 70 | Psl("]\n"); 71 | return location+opbyte2+2; 72 | default: 73 | Psl(" "); Psp(opname); Pnl(); 74 | return location+1; 75 | } 76 | } 77 | 78 | 79 | uint8_t *vm_disassemble(disasm_options opts) 80 | { 81 | uint8_t *location = (uint8_t*)((cell)opts.location); 82 | uint8_t opc = location[0]; 83 | uint8_t opflags = pgm_read_byte(opcode_properties+opc); 84 | const char *opname = pgm_read_ptr(opcode_names+opc); 85 | if (!opts.suppress_address) { 86 | Pp(location); Psl(": "); 87 | } else { 88 | Psl(" "); 89 | } 90 | Px8(opc); 91 | return vm_disassemble_helper(location, opname, opflags); 92 | } 93 | 94 | 95 | static inline uint8_t mem_read(uint16_t addr) { 96 | return *((uint8_t *)addr); 97 | } 98 | 99 | static inline uint16_t mem_read_word(uint16_t addr) { 100 | return *((uint16_t *)addr); 101 | } 102 | 103 | bool using_console(void); 104 | static uint8_t termwidth(void) { 105 | return (using_console()) ? console_cols(&fcon) : 80; 106 | } 107 | 108 | /* how many bytes should hexdump display on one line? */ 109 | static uint8_t hexdump_nbytes(void) { 110 | return (termwidth() >= 80) ? 16 : 8; 111 | } 112 | 113 | /* hexdump bytes between addr1 and addr2 inclusive */ 114 | uint16_t hexdump(uint16_t addr1, uint16_t addr2, bool as_words) 115 | { 116 | uint8_t cols = hexdump_nbytes(); 117 | if (as_words) { cols /= 2; } 118 | while (1) { 119 | Pbl(); Px16(addr1); Pc('-'); 120 | for (uint8_t i = 0; i < cols; i++) { 121 | Pbl(); 122 | uint16_t prevaddr = addr1; 123 | if (as_words) { 124 | Px16(mem_read_word(addr1)); addr1 += 2; 125 | } else { 126 | Px8(mem_read(addr1)); addr1++; 127 | } 128 | /* stop after printing value at last address, */ 129 | /* or if we wrap around FFFF->0000 */ 130 | if (addr1 <= prevaddr || addr1 > addr2) { 131 | Pnl(); 132 | goto done; 133 | } 134 | } 135 | Pnl(); 136 | } 137 | done: 138 | return addr1; 139 | } 140 | -------------------------------------------------------------------------------- /apps/forth/disasm.h: -------------------------------------------------------------------------------- 1 | #ifndef DISASM_H_ 2 | #define DISASM_H_ 3 | 4 | #include "forth.h" 5 | 6 | /** 7 | * Prints the disassembly of a single instruction to the output device. 8 | * Returns the address of the next instruction. 9 | */ 10 | typedef struct { 11 | cell location:15; 12 | cell suppress_address:1; 13 | } __attribute__((packed)) disasm_options; 14 | 15 | uint8_t *vm_disassemble(disasm_options opts); 16 | 17 | uint16_t hexdump(uint16_t addr1, uint16_t addr2, bool as_words); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /apps/forth/draw_ops.inc: -------------------------------------------------------------------------------- 1 | ; values for tilemap_hh: 2 | ; 000xxxx0 text (tilemap in ROM, lower 64KB) 3 | ; 000xxxx1 text (tilemap in ROM, upper 64KB) 4 | ; 001xxxxX 256 color bitmap 5 | ; 010xxxxX 16 color bitmap 6 | ; 011xxxxX 4 color bitmap 7 | ; 100xxxxX text (tilemap in RAM) 8 | ; 101xxxxX mono bitmap 9 | 10 | .macro invoke_drawop n 11 | lds ZL, tilemap_hh ; dispatch based format 12 | ori ZL, DRAW_OP_\n ; or in the operation number 13 | ldi ZH, pm_hi8(draw_op_table) 14 | ijmp 15 | .endm 16 | 17 | .equ DRAW_OP_xres, 0<<1 18 | .equ DRAW_OP_clear, 1<<1 19 | .equ DRAW_OP_getcolor, 2<<1 20 | .equ DRAW_OP_setcolor, 3<<1 21 | .equ DRAW_OP_plot, 4<<1 22 | .equ DRAW_OP_pset, 5<<1 23 | .equ DRAW_OP_vlin, 6<<1 24 | .equ DRAW_OP_hlin, 7<<1 25 | .equ DRAW_OP_rect, 8<<1 26 | .equ DRAW_OP_line, 9<<1 27 | 28 | .macro draw_op, fmtprefix, op 29 | rjmp opcode_\fmtprefix\op 30 | rjmp opcode_\fmtprefix\op 31 | .endm 32 | 33 | .macro draw_op_invalid 34 | rjmp invalid_drawop 35 | rjmp invalid_drawop 36 | .endm 37 | 38 | .macro draw_ops, fmtprefix 39 | draw_op \fmtprefix, xres ; Draw operation 0: push screen width 40 | draw_op \fmtprefix, clear ; Draw operation 1: clear screen 41 | draw_op \fmtprefix, getcolor ; Draw operation 2: get current color 42 | draw_op \fmtprefix, setcolor ; Draw operation 3: set current color 43 | draw_op \fmtprefix, plot_chk ; Draw operation 4: set pixel to current color 44 | draw_op \fmtprefix, pset_chk ; Draw operation 5: set color of pixel 45 | draw_op \fmtprefix, vlin_chk ; Draw operation 6: vertical line 46 | draw_op \fmtprefix, hlin_chk ; Draw operation 7: horizontal line 47 | draw_op \fmtprefix, rect_chk ; Draw operation 8: filled rectangle 48 | draw_op \fmtprefix, line_chk ; Draw operation 9: any-direction line 49 | .rept 6 50 | draw_op_invalid 51 | .endr 52 | .endm 53 | 54 | .align 9 55 | draw_op_table: 56 | draw_ops t ; 0b000xxxxX: text mode 57 | draw_ops h ; 0b001xxxxX: 256 color bitmap mode 58 | draw_ops m ; 0b010xxxxX: 16 color bitmap mode 59 | draw_ops l ; 0b011xxxxX: 4 color bitmap mode 60 | draw_ops t ; 0b100xxxxX: text mode 61 | draw_ops b ; 0b101xxxxX: mono bitmap mode 62 | 63 | opcode_tplot_chk: 64 | opcode_thlin_chk: 65 | opcode_tvlin_chk: 66 | opcode_trect_chk: 67 | opcode_tline_chk: 68 | invalid_drawop: 69 | throw FE_UNSUPPORTED_OPERATION 70 | -------------------------------------------------------------------------------- /apps/forth/environment.words: -------------------------------------------------------------------------------- 1 | enventry "/COUNTED_STRING" 2 | l8 255 3 | enventryend 4 | 5 | enventry "/HOLD" 6 | l8 FORTH_HOLD_BUFFER_SIZE 7 | enventryend 8 | 9 | enventry "/PAD" 10 | l8 FORTH_PAD_SIZE 11 | enventryend 12 | 13 | enventry "ADDRESS-UNIT-BITS" 14 | l8 8 15 | enventryend 16 | 17 | enventry "MAX-CHAR" 18 | l8 255 19 | enventryend 20 | 21 | enventry "MAX-D" 22 | op minus1 23 | op minus1 24 | op utwoslash 25 | enventryend 26 | 27 | enventry "MAX-N" 28 | op minus1 29 | op utwoslash 30 | enventryend 31 | 32 | enventry "MAX-U" 33 | op minus1 34 | enventryend 35 | 36 | enventry "MAX-UD" 37 | op minus1 38 | op minus1 39 | enventryend 40 | 41 | ; regular division is round-to-zero, not floored 42 | enventry "FLOORED" 43 | op zero 44 | enventryend 45 | 46 | enventry "RETURN-STACK-CELLS" 47 | l8 forth_rstack_cells 48 | enventryend 49 | 50 | enventry "STACK-CELLS" 51 | l8 forth_dstack_cells 52 | enventryend 53 | 54 | ; ( -- ver-patch ver-minor ver-major ) 55 | #include "forth_version.h" 56 | enventry "VERSION" 57 | l8 VERSION_PATCH 58 | l8 VERSION_MINOR 59 | l8 VERSION_MAJOR 60 | enventryend 61 | -------------------------------------------------------------------------------- /apps/forth/error_codes.c: -------------------------------------------------------------------------------- 1 | #include "forth.h" 2 | #include "defs.h" 3 | #include 4 | 5 | #undef X 6 | #define X(ident,num,str) static const char errstr_##ident[] PROGMEM = str; 7 | FORTH_ERROR_CODES 8 | 9 | #undef X 10 | #define X(ident,num,str) [-ident] = errstr_##ident, 11 | 12 | static const char * const forth_error_strs[57] PROGMEM = { 13 | FORTH_ERROR_CODES 14 | }; 15 | 16 | 17 | PGM_P forth_error_str(forth_err_code err) { 18 | if (err > 0 || err <= -(signed)(COUNT_OF(forth_error_strs))) { 19 | return NULL; 20 | } else { 21 | uint8_t idx = -err; 22 | return pgm_read_ptr(forth_error_strs+idx); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /apps/forth/error_codes.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_CODES_H_ 2 | #define ERROR_CODES_H_ 3 | 4 | /** 5 | * Most of the standard ANS Forth exception codes apply to this implementation, 6 | * some do not. Some irrelevant codes are repurposed. Some standard codes are 7 | * unassigned and/or to be implemented. 8 | */ 9 | #define FORTH_ERROR_CODES \ 10 | X(FE_NO_ERROR, 0, "") \ 11 | X(FE_ABORT, -1, "ABORT") \ 12 | X(FE_ABORTQ, -2, "ABORT\"") \ 13 | X(FE_DSTACK_OVERFLOW, -3, "stack overflow") \ 14 | X(FE_DSTACK_UNDERFLOW, -4, "stack underflow") \ 15 | X(FE_RSTACK_OVERFLOW, -5, "return stack overflow") \ 16 | X(FE_RSTACK_UNDERFLOW, -6, "return stack underflow") \ 17 | X(FE_BRANCH_OUT_OF_RANGE, -7, "branch out of range") /* ANS: "do-loops nested too deeply during execution" */ \ 18 | X(FE_NAMESPACE_OVERFLOW, -8, "name space overflow") \ 19 | X(FE_CODESPACE_OVERFLOW, -9, "code space overflow") /* ANS: "invalid memory address" */ \ 20 | X(FE_DIVIDE_BY_ZERO, -10, "division by zero") \ 21 | X(FE_DICT_ENTRY_TOO_LONG, -11, "dictionary entry too long") /* ANS: "result out of range" */ \ 22 | X(FE_ARG_TYPE_MISMATCH, -12, "argument type mismatch") \ 23 | X(FE_UNDEFINED_WORD, -13, "undefined word") \ 24 | X(FE_INTEPRET_C_ONLY_WORD, -14, "interpreting a compile-only word") \ 25 | X(FE_INVALID_FORGET, -15, "invalid FORGET") \ 26 | X(FE_ZERO_LENGTH_NAME, -16, "attempt to use zero-length string as name") \ 27 | X(FE_NUMERIC_STR_OVERFLOW, -17, "pictured numeric output string overflow") \ 28 | X(FE_PARSED_STR_OVERFLOW, -18, "parsed string overflow") \ 29 | X(FE_NAME_TOO_LONG, -19, "definition name too long") \ 30 | X(FE_READ_ONLY, -20, "write to a read-only location") \ 31 | X(FE_UNSUPPORTED_OPERATION, -21, "unsupported operation") \ 32 | X(FE_CTRL_STRUCT_MISMATCH, -22, "control structure mismatch") \ 33 | X(FE_ALIGNMENT, -23, "address alignment exception") \ 34 | XUNUSED(-24) /* ANS: "invalid numeric argument" */ \ 35 | X(FE_RSTACK_IMBALANCE, -25, "return stack imbalance") \ 36 | X(FE_COMPILE_I_ONLY_WORD, -26, "compiling an interpret-only word") /* ANS: "loop parameters unavailable" */ \ 37 | XUNUSED(-27) /* ANS: "invalid recursion" */ \ 38 | X(FE_USER_INTERRUPT, -28, "user interrupt") \ 39 | XUNUSED(-29) /* ANS: "compiler nesting" */ \ 40 | XUNUSED(-30) /* ANS: "obsolescent feature" */ \ 41 | X(FE_INVALID_TOBODY, -31, ">BODY used on non-CREATEd definition") \ 42 | X(FE_INVALID_NAME_ARG, -32, "invalid name argument") /* e.g. using TO on a non-value */ \ 43 | XUNUSED(-33) /* ANS: "block read exception" */ \ 44 | XUNUSED(-34) /* ANS: "block write exception" */ \ 45 | X(FE_INVALID_BLOCK_NUMBER, -35, "invalid block number") \ 46 | XUNUSED(-36) /* ANS: "invalid file position" */ \ 47 | XUNUSED(-37) /* ANS: "file I/O exception" */ \ 48 | XUNUSED(-38) /* ANS: "non-existent file" */ \ 49 | XUNUSED(-39) /* ANS: "unexpected end of file" */ \ 50 | XUNUSED(-40) /* ANS: "invalid BASE for floating point conversion" */ \ 51 | X(FE_LOSS_OF_PRECISION, -41, "loss of precision") \ 52 | XUNUSED(-42) /* ANS: "floating-point divide by zero" */ \ 53 | XUNUSED(-43) /* ANS: "floating-point result out of range" */ \ 54 | XUNUSED(-44) /* ANS: "floating-point stack overflow" */ \ 55 | XUNUSED(-45) /* ANS: "floating-point stack underflow" */ \ 56 | XUNUSED(-46) /* ANS: "floating-point invalid argument" */ \ 57 | XUNUSED(-47) /* ANS: "compilation word list deleted" */ \ 58 | X(FE_INVALID_POSTPONE, -48, "invalid POSTPONE") /* ANS: "invalid POSTPONE" */ \ 59 | XUNUSED(-49) /* AND: "search-order overflow" */ \ 60 | XUNUSED(-50) /* AND: "search-order underflow" */ \ 61 | XUNUSED(-51) /* AND: "compilation word list changed" */ \ 62 | XUNUSED(-52) /* AND: "control-flow stack overflow" */ \ 63 | XUNUSED(-53) /* AND: "exception stack overflow" */ \ 64 | XUNUSED(-54) /* AND: "floating-point underflow" */ \ 65 | XUNUSED(-55) /* AND: "floating-point unidentified fault" */ \ 66 | X(FE_QUIT, -56, "QUIT")/* AND: "QUIT" */ \ 67 | 68 | #undef X 69 | #define XUNUSED(num) 70 | 71 | #ifndef __ASSEMBLER__ 72 | #define X(ident,num,str) ident = num, 73 | #include 74 | typedef int16_t forth_err_code; 75 | enum { 76 | FORTH_ERROR_CODES 77 | }; 78 | #else /* __ASSEMBLER__ */ 79 | #define X(ident,num,str) .equ ident, (num) $ 80 | FORTH_ERROR_CODES 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /apps/forth/forth_defs.h: -------------------------------------------------------------------------------- 1 | /* included by both C and assembly */ 2 | #ifndef FORTH_DEFS_H_ 3 | #define FORTH_DEFS_H_ 4 | 5 | #include "error_codes.h" 6 | 7 | /* Bits in the forth_flags byte. */ 8 | #define FF_SINGLESTEP_BIT 5 9 | #define FF_TRACE_BIT 6 10 | #define FF_STATE_BIT 7 11 | #define FORTH_STATE_MASK (1<=FL_CONSTANT) 30 | 31 | /* if the flags argument to namespace_create() has this bit set, the new word */ 32 | /* will be created with the smudge bit set, making it invisible */ 33 | #define FL_CREATE_SMUDGED 1 34 | 35 | /* if the flags argument to namespace_create() has this bit set, snapshot the */ 36 | /* stack pointers (to be checked upon exiting compilation mode */ 37 | #define FL_SAVE_STACK_PTRS 2 38 | 39 | #define HIGHADDR(addr) lo8((addr)-(((RAMEND+1)-0x100))) 40 | 41 | #define FORTH_EVALUATE_MAX_LEN 255 42 | #define FORTH_HOLD_BUFFER_SIZE 34 /* capacity of pictured numeric output buffer; minimum required by standard */ 43 | #define FORTH_PAD_SIZE 84 /* to comply with ANS */ 44 | #define MAX_ROMDICT_ENTRY_SIZE 8 45 | 46 | /* Most Forth systems use 1024-byte blocks. */ 47 | /* We use 512-byte blocks (arranged as 32 characters by 16 rows), so that an */ 48 | /* entire block can fit on a 40-column screen, and because this system doesn't */ 49 | /* have much memory in the first place. (It also matches the sector size of */ 50 | /* most mass storage devices, like SD cards.) */ 51 | #define BLOCK_BYTES_PER_LINE 32 52 | #define LINES_PER_BLOCK 16 53 | #define BLOCK_SIZE ((BLOCK_BYTES_PER_LINE)*(LINES_PER_BLOCK)) 54 | #define NUM_EEPROM_BLOCKS ((E2END+1)/BLOCK_SIZE) 55 | 56 | /* How many blocks are available on external media? */ 57 | #define NUM_XMEM_BLOCKS (65536/BLOCK_SIZE) 58 | #define NUM_BLOCKS (NUM_EEPROM_BLOCKS+NUM_XMEM_BLOCKS) 59 | 60 | /* for simavr */ 61 | #define TRACE(c) \ 62 | asm volatile( \ 63 | "ldi r30, %[value]\n" \ 64 | "out 0x1c, r1\n" :: [value] "M" ((c)) : "r30") 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /apps/forth/forth_opcode_types.h: -------------------------------------------------------------------------------- 1 | #ifndef FORTH_OPCODE_TYPES_H_ 2 | #define FORTH_OPCODE_TYPES_H_ 3 | 4 | #define OP_TYPE_TO_FLAGS(n) ((n)&15) 5 | #define OP_FLAGS_TYPE(fl) ((fl)&15) 6 | #define OP_2U 0 /* Two byte instruction with unsigned 8-bit argument */ 7 | #define OP_2X 1 /* Two byte instruction with unsigned 8-bit argument (hex) */ 8 | #define OP_2I 2 /* Two byte extended-opcode instruction */ 9 | #define OP_2F 3 /* 8-bit argument, forward relative branch */ 10 | #define OP_2B 4 /* 8-bit argument, backward relative branch */ 11 | #define OP_2C 5 /* Two byte CALL instruction */ 12 | #define OP_3S 6 /* Three byte instruction with signed 16-bit argument */ 13 | #define OP_3X 7 /* Three byte instruction with 16-bit address argument */ 14 | #define OP_STR 8 /* Inline string instruction */ 15 | #define OP_1 9 /* One byte instruction with no argument */ 16 | 17 | /* Visibility flags */ 18 | #define OP_VISIBLE 0x1 19 | #define OP_COMPILEONLY 0x2 20 | #define OP_USES_RSTACK 0x4 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /apps/forth/forth_opcodes_3.S: -------------------------------------------------------------------------------- 1 | #include "forth_opcodes.h" 2 | #include "forth_macros.inc" 3 | #include "forth_defs.h" 4 | 5 | ; Debug dispatch table aligned to a 256-word (512-byte) boundary. 6 | ; (All opcodes point to a single debugger entry point) 7 | .align 9 8 | .global vm_debug_dispatch_table 9 | vm_debug_dispatch_table: 10 | create_dispatch_table .debug_opcode_, 255 11 | .align 0 12 | 13 | dispatch_stubs .debug_opcode_, 256 14 | ; back up IP 15 | ld IR, -IP 16 | ; breakpoint() takes these args: 17 | ; r25:r24 - TOS 18 | ; r23:r22 - DSP 19 | ; r21:r20 - IP 20 | ; r19:r18 - RSP 21 | movw TMP, DSP 22 | movw r20, IP 23 | rsp_to_r19r18 24 | callc_0arg_prologue 25 | call breakpoint 26 | tst r24 27 | breq 1f 28 | ; reload the dispatch table in case the debug flags changed 29 | ldi CDTH, pm_hi8(vm_dispatch_table) 30 | lds ZL, forth_flags 31 | sbrc ZL, FF_TRACE_BIT 32 | ldi CDTH, pm_hi8(vm_debug_dispatch_table) 33 | 1: callc_0arg_restore 34 | ; single-step 35 | ldi ZH, pm_hi8(vm_debug_dispatch_table) 36 | ; execute the real instruction. 37 | ; we can't use ijmp, because we'd have to clobber ZH and then we couldn't 38 | ; single-step. so use the old push-n-return trick to do an indirect jump. 39 | ld IR, IP+ 40 | ldi r20, pm_hi8(vm_dispatch_table) 41 | push IR 42 | push r20 43 | ret 44 | -------------------------------------------------------------------------------- /apps/forth/forth_regs.h: -------------------------------------------------------------------------------- 1 | #ifndef FORTH_REGS_H_ 2 | #define FORTH_REGS_H_ 3 | 4 | #ifdef __AVR__ 5 | #include 6 | #endif 7 | 8 | /* Used as a 16-bit zero register. */ 9 | #define ZEROL r0 10 | #define ZEROH r1 11 | #define ZERO ZEROL 12 | 13 | /* Used for temporary values. */ 14 | /* Also used as the second argument when calling C functions. */ 15 | #define TMPL r22 16 | #define TMPH r23 17 | #define TMP TMPL 18 | 19 | /* A common optimization: use a register to hold the top-of-stack value. */ 20 | /* Also used as the return value and first argument when calling C functions. */ 21 | #define TOSL r24 22 | #define TOSH r25 23 | #define TOS TOSL 24 | 25 | /* The virtual machine's instruction pointer. */ 26 | #define IPL r26 27 | #define IPH r27 28 | #define IP X 29 | 30 | /* Data stack pointer. */ 31 | #define DSPL r28 32 | #define DSPH r29 33 | #define DSP Y 34 | 35 | /* Dispatch table pointer/instruction register. */ 36 | #define IR r30 /* holds the most recently fetched instruction byte */ 37 | #define DTH r31 /* high byte of dispatch table address */ 38 | #define CDTH r17 /* cached value of current dispatch table high byte */ 39 | 40 | /* Return stack pointer. (Use the hardware stack pointer.) */ 41 | #define RSPL _SFR_IO_ADDR(SPL) 42 | #define RSPH _SFR_IO_ADDR(SPH) 43 | #define RSP SPL 44 | 45 | /* 16 bit "true" constant. (0xFFFF) */ 46 | #define TRUEL r8 47 | #define TRUEH r9 48 | #define TRUE TRUEL 49 | 50 | /* Used to save TOS when calling C functions. */ 51 | #define TSAVL r10 52 | #define TSAVH r11 53 | #define TSAV TSAVL 54 | 55 | /* Used to save IP when calling C functions. */ 56 | #define XSAVL r12 57 | #define XSAVH r13 58 | #define XSAV XSAVL 59 | 60 | /* Temporary register. Its value may be clobbered by an exception. */ 61 | #define LINKL r14 62 | #define LINKH r15 63 | 64 | #define HIGH_PAGE_BASE_ADDR 0x4000 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /apps/forth/forth_version.h: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H_ 2 | #define VERSION_H_ 3 | 4 | #define VERSION_MAJOR 0 5 | #define VERSION_MINOR 9 6 | #define VERSION_PATCH 2 7 | #define VERSION_STR XSTR_(VERSION_MAJOR) "." XSTR_(VERSION_MINOR) "." XSTR_(VERSION_PATCH) 8 | #define PRODUCT_NAME_STR "Ramforth" 9 | #define COPYRIGHT_STR "(C) 2019 Matt Sarnoff" 10 | #define TOTAL_MEM_KB 16 11 | #endif 12 | -------------------------------------------------------------------------------- /apps/forth/forth_video.c: -------------------------------------------------------------------------------- 1 | #include "forth.h" 2 | #include "console.h" 3 | #include "video.h" 4 | #include "videomodes.h" 5 | #include "raster_interrupt.h" 6 | #include 7 | #include 8 | 9 | void forth_40x25_console(void) { 10 | disable_raster_interrupt(); 11 | char *buf = alloc_screen_buffer(1000); 12 | fcon.split = 0; 13 | fcon.eightycols = 0; 14 | fcon.color = 0; 15 | fcon.scrollok = 1; 16 | fcon.cursorphase = 1; 17 | console_init(&fcon, buf, DEFAULT_FONT); 18 | } 19 | 20 | void forth_80x25_console(void) { 21 | disable_raster_interrupt(); 22 | char *buf = alloc_screen_buffer(2000); 23 | fcon.split = 0; 24 | fcon.eightycols = 1; 25 | fcon.color = 0; 26 | fcon.scrollok = 1; 27 | fcon.cursorphase = 1; 28 | console_init(&fcon, buf, DEFAULT_FONT); 29 | } 30 | 31 | void forth_40x25_color_console(void) { 32 | disable_raster_interrupt(); 33 | char *buf = alloc_screen_buffer(2000); 34 | fcon.split = 0; 35 | fcon.eightycols = 0; 36 | fcon.color = 1; 37 | fcon.scrollok = 1; 38 | fcon.cursorphase = 1; 39 | console_init(&fcon, buf, DEFAULT_FONT); 40 | } 41 | 42 | void forth_set_bitmap_mode(enum bitmap_mode_num mode) { 43 | uint16_t bufsize = screen_buf_size_for_bitmap_mode(mode, 0); 44 | if (!bufsize) { forth_throw(FE_UNSUPPORTED_OPERATION); } 45 | disable_raster_interrupt(); 46 | screen_ptr = alloc_screen_buffer(bufsize); 47 | memset(screen_ptr, 0, bufsize); 48 | console_setshowcursor(&fcon, 0); /* don't try to blink the cursor! */ 49 | // for (uint16_t i = 0; i < bufsize; i++) { screen_ptr[i] = i; } /* test pattern */ 50 | set_bitmap_mode(mode, 0); 51 | } 52 | 53 | 54 | #define SPLIT_SCREEN_TEXT_COLS 40 55 | #define SPLIT_SCREEN_TEXT_ROWS 5 56 | #define SPLIT_SCREEN_TEXT_BYTES (SPLIT_SCREEN_TEXT_COLS*SPLIT_SCREEN_TEXT_ROWS) 57 | #define SPLIT_SCREEN_TEXT_LINES (SPLIT_SCREEN_TEXT_ROWS*8) 58 | #define SPLIT_SCREEN_BITMAP_LINES ACTIVE_VIDEO_LINES-SPLIT_SCREEN_TEXT_LINES 59 | 60 | #undef X 61 | #define X(m) \ 62 | static RASTER_INTERRUPT(ri_splitstart_##m) { \ 63 | RI_SET8(OCR2A, OCR2A_VALUE_FOR_SRPERIOD(VIDEO_MODE_40x25_BASICTEXT##_SRPERIOD)); \ 64 | SET_SHIFT_REGISTER_MODE(VIDEO_MODE_40x25_BASICTEXT##_SRMODE); \ 65 | RI_SET_VIDEO_MODE_FAST(VIDEO_MODE_40x25_BASICTEXT); /* don't change bytesperline! */ \ 66 | RI_SET_NEXT_HANDLER(0, ri_splitend_##m); \ 67 | RI_SAVE_SREG(); \ 68 | asm volatile( \ 69 | "lds r30, %[ptr]\n" \ 70 | "lds r31, %[ptr]+1\n" \ 71 | "subi r30, lo8(%[offset])\n" \ 72 | "sbci r31, hi8(%[offset])\n" \ 73 | "movw r4, r30\n" \ 74 | :: [ptr] "m" (screen_ptr), [offset] "M" (SPLIT_SCREEN_TEXT_BYTES)); \ 75 | RI_RESTORE_SREG(); \ 76 | RI_RET(); \ 77 | } \ 78 | static __attribute__((used)) RASTER_INTERRUPT(ri_splitend_##m) { \ 79 | RI_SET_VIDEO_MODE(m); \ 80 | RI_SET_NEXT_HANDLER(40, ri_splitstart_##m); \ 81 | RI_RET(); \ 82 | } 83 | 84 | BITMAP_MODES 85 | 86 | #undef X 87 | #define X(m) ri_splitstart_##m, 88 | 89 | typedef void (*ri_fn)(void); 90 | static const ri_fn split_screen_raster_interrupts[NUM_BITMAP_MODES] PROGMEM = { 91 | BITMAP_MODES 92 | }; 93 | 94 | void forth_set_split_screen_mode(enum bitmap_mode_num mode) { 95 | uint16_t bufsize = screen_buf_size_for_bitmap_mode(mode, SPLIT_SCREEN_BITMAP_LINES); 96 | if (!bufsize) { forth_throw(FE_UNSUPPORTED_OPERATION); } 97 | /* First 200 bytes are used for the text pane. Remainder used for the bitmap. */ 98 | bufsize += SPLIT_SCREEN_TEXT_BYTES; 99 | char *bufstart = alloc_screen_buffer(bufsize); 100 | screen_ptr = bufstart + SPLIT_SCREEN_TEXT_BYTES; 101 | memset(bufstart, 0, bufsize); 102 | // for (uint16_t i = 0; i < bufsize; i++) { bufstart[i] = i; } /* test pattern */ 103 | ri_fn fn = pgm_read_ptr(&split_screen_raster_interrupts[mode]); 104 | set_bitmap_mode(mode, SPLIT_SCREEN_BITMAP_LINES); 105 | set_raster_interrupt(40, fn); 106 | fcon.split = 1; 107 | console_setshowcursor(&fcon, 1); /* blink the cursor */ 108 | console_clrhome(&fcon); 109 | } 110 | -------------------------------------------------------------------------------- /apps/forth/hwregs.words: -------------------------------------------------------------------------------- 1 | ; AVR hardware registers 2 | 3 | ; can't use an assembler macro, because register names are expanded by the 4 | ; C preprocessor 5 | #define ioregword(r) dictentry_colon #r $ l8 r $ dictentryend $ 6 | 7 | ioregword(PINA) 8 | ioregword(DDRA) 9 | ioregword(PORTA) 10 | ioregword(PINB) 11 | ioregword(DDRB) 12 | ioregword(PORTB) 13 | ioregword(PINC) 14 | ioregword(DDRC) 15 | ioregword(PORTC) 16 | ioregword(PIND) 17 | ioregword(DDRD) 18 | ioregword(PORTD) 19 | ioregword(TIFR0) 20 | ioregword(TIFR1) 21 | ioregword(TIFR2) 22 | ioregword(TIFR3) 23 | ioregword(PCIFR) 24 | ioregword(EIFR) 25 | ioregword(EIMSK) 26 | ioregword(GPIOR0) 27 | ioregword(EECR) 28 | ioregword(EEDR) 29 | ioregword(EEAR) 30 | ioregword(GTCCR) 31 | ioregword(TCCR0A) 32 | ioregword(TCCR0B) 33 | ioregword(TCNT0) 34 | ioregword(OCR0A) 35 | ioregword(OCR0B) 36 | ioregword(GPIOR1) 37 | ioregword(GPIOR2) 38 | ioregword(SPCR) 39 | ioregword(SPSR) 40 | ioregword(SPDR) 41 | ioregword(ACSR) 42 | ioregword(OCDR) 43 | ioregword(SMCR) 44 | ioregword(MCUSR) 45 | ioregword(MCUCR) 46 | ioregword(SPMCSR) 47 | ioregword(RAMPZ) 48 | ioregword(SPL) 49 | ioregword(SPH) 50 | ioregword(SREG) 51 | ioregword(WDTCSR) 52 | ioregword(CLKPR) 53 | ioregword(PRR0) 54 | ioregword(PRR1) 55 | ioregword(OSCCAL) 56 | ioregword(PCICR) 57 | ioregword(EICRA) 58 | ioregword(PCMSK0) 59 | ioregword(PCMSK1) 60 | ioregword(PCMSK2) 61 | ioregword(TIMSK0) 62 | ioregword(TIMSK1) 63 | ioregword(TIMSK2) 64 | ioregword(TIMSK3) 65 | ioregword(PCMSK3) 66 | ioregword(ADCL) 67 | ioregword(ADCH) 68 | ioregword(ADCSRA) 69 | ioregword(ADCSRB) 70 | ioregword(ADMUX) 71 | ioregword(DIDR0) 72 | ioregword(DIDR1) 73 | ioregword(TCCR1A) 74 | ioregword(TCCR1B) 75 | ioregword(TCCR1C) 76 | ioregword(TCNT1) 77 | ioregword(TCNT1L) 78 | ioregword(TCNT1H) 79 | ioregword(ICR1) 80 | ioregword(ICR1L) 81 | ioregword(ICR1H) 82 | ioregword(OCR1A) 83 | ioregword(OCR1AL) 84 | ioregword(OCR1AH) 85 | ioregword(OCR1B) 86 | ioregword(OCR1BL) 87 | ioregword(OCR1BH) 88 | ioregword(TCCR3A) 89 | ioregword(TCCR3B) 90 | ioregword(TCCR3C) 91 | ioregword(TCNT3) 92 | ioregword(TCNT3L) 93 | ioregword(TCNT3H) 94 | ioregword(ICR3) 95 | ioregword(ICR3L) 96 | ioregword(ICR3H) 97 | ioregword(OCR3A) 98 | ioregword(OCR3AL) 99 | ioregword(OCR3AH) 100 | ioregword(OCR3B) 101 | ioregword(OCR3BL) 102 | ioregword(OCR3BH) 103 | ioregword(TCCR2A) 104 | ioregword(TCCR2B) 105 | ioregword(TCNT2) 106 | ioregword(OCR2A) 107 | ioregword(OCR2B) 108 | ioregword(ASSR) 109 | ioregword(TWBR) 110 | ioregword(TWSR) 111 | ioregword(TWAR) 112 | ioregword(TWDR) 113 | ioregword(TWCR) 114 | ioregword(TWAMR) 115 | ioregword(UCSR0A) 116 | ioregword(UCSR0B) 117 | ioregword(UCSR0C) 118 | ioregword(UBRR0) 119 | ioregword(UBRR0L) 120 | ioregword(UBRR0H) 121 | ioregword(UDR0) 122 | ioregword(UCSR1A) 123 | ioregword(UCSR1B) 124 | ioregword(UCSR1C) 125 | ioregword(UBRR1) 126 | ioregword(UBRR1L) 127 | ioregword(UBRR1H) 128 | ioregword(UDR1) 129 | -------------------------------------------------------------------------------- /apps/forth/iovec.words: -------------------------------------------------------------------------------- 1 | dictentry_colon "SERIAL-IN" 2 | op lit16 3 | .word pm(serial_mio_getc) 4 | .byte OP_storelithi, HIGHADDR(mio_getc) 5 | dictentryend 6 | 7 | dictentry_colon "SERIAL-OUT" 8 | op lit16 9 | .word pm(serial_mio_putc) 10 | .byte OP_storelithi, HIGHADDR(mio_putc) 11 | dictentryend 12 | 13 | dictentry_colon "KEYBOARD-IN" 14 | op lit16 15 | .word pm(fcon_mio_getc) 16 | .byte OP_storelithi, HIGHADDR(mio_getc) 17 | dictentryend 18 | 19 | dictentry_colon "VIDEO-OUT" 20 | op lit16 21 | .word pm(fcon_mio_putc) 22 | .byte OP_storelithi, HIGHADDR(mio_putc) 23 | dictentryend 24 | -------------------------------------------------------------------------------- /apps/forth/keyboard.words: -------------------------------------------------------------------------------- 1 | #include "keycodes.h" 2 | 3 | ; UNTIL-KEY ( -- ) loop until any key is pressed (pair with BEGIN) 4 | dictentry_compileonly "UNTIL-KEY" 5 | op litccomma 6 | .byte OP_ext 7 | op litccomma 8 | .byte OPX_keyq 9 | mark zbranchback ; compile dummy (0BRANCH) instruction 10 | opx bresolve ; resolve backward branch 11 | dictentryend 12 | 13 | ; Constants for extended character codes 14 | 15 | .macro specialkeycharword, name, k 16 | dictentry_colon "'\name" 17 | l8 \k 18 | dictentryend 19 | .endm 20 | 21 | #define SPECIALKEYCHARWORD(k) specialkeycharword #k, KC_##k 22 | SPECIALKEYCHARWORD(BKSP) 23 | SPECIALKEYCHARWORD(TAB) 24 | SPECIALKEYCHARWORD(ESC) 25 | SPECIALKEYCHARWORD(DEL) 26 | ;SPECIALKEYCHARWORD(ENTER) 27 | SPECIALKEYCHARWORD(UP) 28 | SPECIALKEYCHARWORD(DOWN) 29 | SPECIALKEYCHARWORD(RIGHT) 30 | SPECIALKEYCHARWORD(LEFT) 31 | SPECIALKEYCHARWORD(HOME) 32 | SPECIALKEYCHARWORD(END) 33 | SPECIALKEYCHARWORD(PGUP) 34 | SPECIALKEYCHARWORD(PGDN) 35 | SPECIALKEYCHARWORD(F1) 36 | SPECIALKEYCHARWORD(F2) 37 | SPECIALKEYCHARWORD(F3) 38 | 39 | ; CTRL- ( -- c ) push first character of the following word in the input stream, 40 | ; with the upper 3 bits masked off 41 | dictentry_colon "CTRL-" 42 | op parsename ; get a word 43 | op drop ; ignore length 44 | op cfetch ; get first character and push 45 | op candlit 46 | .byte 0b00011111 47 | dictentryend 48 | 49 | ; [CTRL-] ( -- ) compile literal with value of the first character of the 50 | ; following word in the input stream, with the upper 3 bits masked off 51 | dictentry_compileonly "[CTRL-]" 52 | op parsename ; get a word 53 | op drop ; ignore length 54 | op litccomma 55 | .byte OP_lit8 ; compile (C#) prefix 56 | op cfetch ; get first character 57 | op candlit 58 | .byte 0b00011111 59 | op ccomma ; compile value into code, finishing literal 60 | dictentryend 61 | 62 | 63 | 64 | ; Raw scan matrix codes 65 | 66 | .macro scancodeword, name, row, col 67 | dictentry_colon "K-\name" 68 | l16 1<<\col, \row 69 | dictentryend 70 | .endm 71 | 72 | 73 | #define SCANCODEWORD(k) scancodeword #k, K_##k##_ROW, K_##k##_COL 74 | 75 | SCANCODEWORD(A) 76 | SCANCODEWORD(B) 77 | SCANCODEWORD(C) 78 | SCANCODEWORD(D) 79 | SCANCODEWORD(E) 80 | SCANCODEWORD(F) 81 | SCANCODEWORD(G) 82 | SCANCODEWORD(H) 83 | SCANCODEWORD(I) 84 | SCANCODEWORD(J) 85 | SCANCODEWORD(K) 86 | SCANCODEWORD(L) 87 | SCANCODEWORD(M) 88 | SCANCODEWORD(N) 89 | SCANCODEWORD(O) 90 | SCANCODEWORD(P) 91 | SCANCODEWORD(Q) 92 | SCANCODEWORD(R) 93 | SCANCODEWORD(S) 94 | SCANCODEWORD(T) 95 | SCANCODEWORD(U) 96 | SCANCODEWORD(V) 97 | SCANCODEWORD(W) 98 | SCANCODEWORD(X) 99 | SCANCODEWORD(Y) 100 | SCANCODEWORD(Z) 101 | SCANCODEWORD(ESC) 102 | SCANCODEWORD(DEL) 103 | SCANCODEWORD(SPACE) 104 | SCANCODEWORD(ENTER) 105 | SCANCODEWORD(CTRL) 106 | SCANCODEWORD(SHIFT) 107 | SCANCODEWORD(SYM) 108 | SCANCODEWORD(UP) 109 | SCANCODEWORD(LEFT) 110 | SCANCODEWORD(RIGHT) 111 | SCANCODEWORD(DOWN) 112 | #if KEYBOARD_LAYOUT_1_0 113 | SCANCODEWORD(QUOTE) 114 | SCANCODEWORD(SLASH) 115 | SCANCODEWORD(MINUS) 116 | SCANCODEWORD(EQUAL) 117 | SCANCODEWORD(COMMA) 118 | SCANCODEWORD(DOT) 119 | #else 120 | SCANCODEWORD(1) 121 | SCANCODEWORD(2) 122 | SCANCODEWORD(3) 123 | SCANCODEWORD(4) 124 | SCANCODEWORD(5) 125 | SCANCODEWORD(6) 126 | SCANCODEWORD(7) 127 | SCANCODEWORD(8) 128 | SCANCODEWORD(9) 129 | SCANCODEWORD(0) 130 | #endif 131 | -------------------------------------------------------------------------------- /apps/forth/math.c: -------------------------------------------------------------------------------- 1 | #include "forth.h" 2 | #include "minio.h" 3 | 4 | #include 5 | 6 | int16_t starslash(int16_t n3, int16_t n2, int16_t n1) 7 | { 8 | int32_t d = ((int32_t)n1) * ((int32_t)n2); 9 | return d / n3; 10 | } 11 | 12 | 13 | typedef union { 14 | struct { 15 | cell low; 16 | cell mid; 17 | cell high; 18 | } __attribute__((packed)); 19 | struct { 20 | dcell lowmid; 21 | cell high_; 22 | } __attribute__((packed)); 23 | struct { 24 | cell low_; 25 | dcell midhigh; 26 | } __attribute__((packed)); 27 | } __attribute__((packed)) triple_cell; 28 | 29 | 30 | /* this can probably be optimized further */ 31 | /* also it uses a ton of stack space */ 32 | uint32_t umstarslash(uint16_t n2, uint16_t n1, uint32_t d1) 33 | { 34 | triple_cell i = {0}; 35 | 36 | /* compute partial products */ 37 | uint32_t p1 = (d1&0xFFFF)*n1; 38 | uint32_t p2 = (d1>>16)*n1; 39 | i.lowmid = p1; 40 | i.midhigh += p2; 41 | 42 | /* compute partial quotients */ 43 | uint16_t q1 = i.midhigh / n2; 44 | uint16_t r1 = i.midhigh % n2; 45 | i.mid = r1; 46 | uint16_t q2 = i.lowmid / n2; 47 | return ((uint32_t)q2) | (((uint32_t)q1)<<16); 48 | } 49 | 50 | 51 | /* the spec says that the divisor (n2) must be positive */ 52 | int32_t mstarslash(uint16_t n2, int16_t n1, int32_t d1) 53 | { 54 | /* record sign bits and take absolute values */ 55 | _Bool d1sign = (d1 & 0x80000000) != 0; 56 | _Bool d2sign = (n1 & 0x8000) != 0; 57 | d1 = labs(d1); 58 | n1 = abs(n1); 59 | int32_t result = umstarslash(n2, n1, d1); 60 | if (d1sign ^ d2sign) { result = -result; } 61 | return result; 62 | } 63 | 64 | 65 | /* div_t has quot and rem in the opposite order of what forth wants */ 66 | /* so we use our own struct */ 67 | struct forth_div { 68 | int16_t rem; 69 | int16_t quot; 70 | } __attribute__((packed)); 71 | 72 | struct forth_udiv { 73 | uint16_t rem; 74 | uint16_t quot; 75 | } __attribute__((packed)); 76 | 77 | struct forth_div starslashmod(int16_t n3, int16_t n2, int16_t n1) 78 | { 79 | int32_t d = ((int32_t)n1) * ((int32_t)n2); 80 | /* gcc is smart enough to use a single __divmodsi4 call for / and % */ 81 | return (struct forth_div){ .quot = d/n3, .rem = d%n3 }; 82 | } 83 | 84 | 85 | /* Integer division in C99 always rounds toward zero */ 86 | struct forth_div smslashrem(int16_t n1, int32_t d1) 87 | { 88 | return (struct forth_div){ .quot = d1/n1, .rem = d1%n1 }; 89 | } 90 | 91 | 92 | struct forth_div fmslashmod(int16_t denom, int32_t numer) 93 | { 94 | int16_t quot = numer/denom; 95 | int16_t rem = numer%denom; 96 | if (rem != 0 && ((rem < 0) != (denom < 0))) { 97 | --quot; 98 | rem += denom; 99 | } 100 | return (struct forth_div){ .quot = quot, .rem = rem }; 101 | } 102 | 103 | 104 | struct forth_udiv umslashmod(uint16_t u1, uint32_t ud) 105 | { 106 | return (struct forth_udiv){ .quot = ud/u1, .rem = ud%u1 }; 107 | } 108 | -------------------------------------------------------------------------------- /apps/forth/parsing.words: -------------------------------------------------------------------------------- 1 | ; CHAR ( -- c ) push first character of the following word in the input stream 2 | dictentry_colon "CHAR" 3 | op parsename ; get a word 4 | op drop ; ignore length 5 | op cfetch ; get first character and push 6 | dictentryend 7 | 8 | ; [CHAR] ( -- ) compile literal with value of the first character of the 9 | ; following word in the input stream 10 | dictentry_compileonly "[CHAR]" 11 | op parsename ; get a word 12 | op drop ; ignore length 13 | op litccomma 14 | .byte OP_lit8 ; compile (C#) prefix 15 | op cfetch ; get first character 16 | op ccomma ; compile value into code, finishing literal 17 | dictentryend 18 | 19 | ; .( - print inline string immediately 20 | dictentry_immediate ".(" 21 | ; consume up to ) then TYPE 22 | l8 0x29 ; get up to right-paren 23 | op parse ; get addr/len 24 | op type ; write to screen 25 | dictentryend 26 | 27 | ; comment; discard characters from TIB up to and including the first ) 28 | dictentry_immediate "(" 29 | l8 ')' 30 | op parse 31 | op twodrop 32 | dictentryend 33 | 34 | ; comment; discard characters from TIB up to and including newline 35 | dictentry_immediate "\\" 36 | l8 '\n' 37 | op parse 38 | op twodrop 39 | dictentryend 40 | -------------------------------------------------------------------------------- /apps/forth/random.words: -------------------------------------------------------------------------------- 1 | dictentry_colon "SEED" 2 | laddr seed 3 | dictentryend 4 | 5 | ; CHOOSE ( u1 -- u2 ) return random number in the range (0 <= u2 < u1) 6 | ; this is from Starting Forth. : CHOOSE RANDOM U* SWAP DROP ; 7 | dictentry_colon "CHOOSE" 8 | op random 9 | opx umstar 10 | op nip 11 | dictentryend 12 | 13 | ; COIN-FLIP ( -- flag ) return a random boolean value (true or false) 14 | dictentry_colon "COIN-FLIP" 15 | op random 16 | op lt0 17 | dictentryend 18 | -------------------------------------------------------------------------------- /apps/forth/special.words: -------------------------------------------------------------------------------- 1 | 2 | dictheader "IS" 3 | .word pm(romdict_interpret_bytecode) 4 | .word pm(romdict_interpret_bytecode_compsem) 5 | dictentry_interpretation_semantics 6 | op parsename ; ( xt name-addr name-len ) push string addr/len on stack 7 | ccall find_defer ; ( xt name-addr xt-addr ) get address of old xt or throw if name isn't valid 8 | op nip ; ( xt xt-addr ) don't need string addr on stack anymore 9 | op store 10 | dictentry_compilation_semantics 11 | op parsename 12 | ccall find_defer 13 | op nip 14 | op litccomma ; compile a "store literal" instruction 15 | .byte OP_storelit 16 | op comma 17 | dictentryend 18 | 19 | 20 | dictheader "TO" 21 | .word pm(romdict_interpret_bytecode) 22 | .word pm(romdict_interpret_bytecode_compsem) 23 | dictentry_interpretation_semantics 24 | ; returns a flag indicating if it consumed two words. throws an exception if word isn't a VALUE/2VALUE 25 | ccall interpret_to 26 | op zbranchfwd 27 | .byte 1 28 | op drop 29 | dictentry_compilation_semantics 30 | op zero ; ccall always consumes TOS 31 | ccall compile_to 32 | op drop 33 | dictentryend 34 | 35 | ; S" - compile inline string 36 | dictheader "S\x22" 37 | .word pm(romdict_interpret_bytecode) 38 | .word pm(romdict_interpret_bytecode_compsem) 39 | dictentry_interpretation_semantics 40 | opx parsequote ; get addr/len 41 | dictentry_compilation_semantics 42 | opx parsequote ; get addr/len - stack is now ( srcaddr len ) 43 | ccall compile_string ; stack is now ( srcaddr flag ) 44 | op twodrop 45 | dictentryend 46 | 47 | ; S\" - compile inline string, parsing escape sequences 48 | dictheader "S\\\x22" 49 | .word pm(romdict_interpret_bytecode) 50 | .word pm(romdict_interpret_bytecode_compsem) 51 | dictentry_interpretation_semantics 52 | opx parseslashquote ; get addr/len 53 | dictentry_compilation_semantics 54 | opx parseslashquote ; get addr/len - stack is now ( srcaddr len ) 55 | ccall compile_string ; stack is now ( srcaddr flag ) 56 | op twodrop 57 | dictentryend 58 | 59 | 60 | ; C" - create counted string in temporary buffer 61 | ; Ensure there is enough space in for the string. 62 | ; This implementation uses the pictured numeric output buffer for temporary storage. 63 | dictheader "C\x22" 64 | .word pm(romdict_interpret_bytecode) 65 | .word pm(romdict_interpret_bytecode_compsem) 66 | dictentry_interpretation_semantics 67 | l8 0x22 68 | opx word 69 | dictentry_compilation_semantics 70 | opx parsequote ; get addr/len - stack is now ( srcaddr len ) 71 | ccall compile_cquote ; stack is now ( srcaddr flag ) 72 | op twodrop 73 | dictentryend 74 | 75 | 76 | ; ." - print inline string 77 | dictheader ".\x22" 78 | .word pm(romdict_interpret_bytecode) 79 | .word pm(romdict_interpret_bytecode_compsem) 80 | dictentry_interpretation_semantics 81 | ; consume up to first " char then TYPE 82 | opx parsequote ; get addr/len 83 | op type ; write to screen 84 | dictentry_compilation_semantics 85 | opx parsequote ; get addr/len - stack is now ( srcaddr len ) 86 | ccall compile_dotquote ; stack is now ( srcaddr flag ) 87 | op twodrop 88 | dictentryend 89 | 90 | ; ACTION-OF ( -- xt ) get the xt that is assigned to a DEFERred word 91 | ; TODO probably add more error checking 92 | dictheader "ACTION-OF" 93 | .word pm(romdict_interpret_bytecode) 94 | .word pm(romdict_interpret_bytecode_compsem) 95 | dictentry_interpretation_semantics 96 | op tick 97 | opx tobody 98 | op fetch 99 | dictentry_compilation_semantics 100 | op tick ; get xt (or break if not found) 101 | ccall compile_action_of 102 | op drop 103 | dictentryend 104 | -------------------------------------------------------------------------------- /apps/forth/video.words: -------------------------------------------------------------------------------- 1 | ; push address of screen buffer 2 | dictentry_colon "SCREEN" 3 | .byte OP_fetchlithi, HIGHADDR(screen_ptr) 4 | dictentryend 5 | 6 | ; set screen buffer address 7 | dictentry_colon "SCREEN!" 8 | .byte OP_storelithi, HIGHADDR(screen_ptr) 9 | dictentryend 10 | 11 | ; get current font/tilemap 12 | dictentry_colon "FONT" 13 | .byte OP_fetchlithi, HIGHADDR(tilemap_hi) 14 | dictentryend 15 | 16 | ; set current font/tilemap 17 | dictentry_colon "FONT!" 18 | .byte OP_storelithi, HIGHADDR(tilemap_hi) 19 | dictentryend 20 | 21 | ; push default font 22 | dictentry_colon "DEFAULT-FONT" 23 | l16 hi8(amscii_font_8x8), hh8(amscii_font_8x8) 24 | dictentryend 25 | 26 | dictentry_colon "FONT:" 27 | op header ; get name, create link and header in name space 28 | .byte FL_CONSTANT 29 | opx makefont 30 | dictentryend 31 | 32 | ; push byte address of globalcolor 33 | dictentry_colon "GCOLOR" 34 | laddr globalcolor 35 | dictentryend 36 | 37 | ; COLOR> ( -- | R: -- c ) save current color on return stack 38 | dictentry_colon_rstack "COLOR>" 39 | op getcolor 40 | op tor 41 | dictentryend 42 | 43 | ; >COLOR ( -- | R: c -- ) restore current color from return stack 44 | dictentry_colon_rstack ">COLOR" 45 | op rfrom 46 | op setcolor 47 | dictentryend 48 | 49 | ; >COLOR> ( c1 -- | R: -- c0 ) set current color and save previous color on return stack 50 | dictentry_colon_rstack ">COLOR>" 51 | op getcolor 52 | op tor 53 | op setcolor 54 | dictentryend 55 | 56 | ; number of lines per screen 57 | dictentry_colon "YRES" 58 | .byte OP_cfetchlithi, HIGHADDR(linesperscreen) 59 | dictentryend 60 | 61 | ; TSET ( c xy -- ) set character of text cell at xy, keeping same color 62 | dictentry_colon "TSET" 63 | op xystore 64 | dictentryend 65 | 66 | ; HPSET ( c xy -- ) set color of pixel at xy in high-color bitmap mode 67 | dictentry_colon "HPSET" 68 | op xystore 69 | dictentryend 70 | 71 | ; XMAX ( -- xy ) x coordinate of the pixel in the last column of the screen 72 | dictentry_colon "XMAX" 73 | op xres 74 | op oneminus 75 | dictentryend 76 | 77 | ; YMAX ( -- xy ) y coordinate of the pixel in the last row of the screen 78 | dictentry_colon "YMAX" 79 | .byte OP_cfetchlithi, HIGHADDR(linesperscreen) 80 | op oneminus 81 | dictentryend 82 | 83 | .macro colorword value, name 84 | dictentry_colon "\name" 85 | l8 \value 86 | dictentryend 87 | dictentry_colon "\name!" 88 | l8 \value 89 | op setcolor 90 | dictentryend 91 | .endm 92 | 93 | colorword 0, BLACK 94 | colorword 1, D.GREEN 95 | colorword 2, D.BLUE 96 | colorword 3, BLUE 97 | colorword 4, RED 98 | colorword 5, GRAY 99 | colorword 6, PURPLE 100 | colorword 7, L.BLUE 101 | colorword 8, BROWN 102 | colorword 9, GREEN 103 | colorword 10, GREY 104 | colorword 11, AQUA 105 | colorword 12, ORANGE 106 | colorword 13, YELLOW 107 | colorword 14, PINK 108 | colorword 15, WHITE 109 | colorword 16, BLUE/D.GREEN 110 | colorword 17, AQUA/D.GREEN 111 | colorword 18, YELLOW/D.GREEN 112 | colorword 19, WHITE/D.GREEN 113 | colorword 20, BLUE/D.BLUE 114 | colorword 21, AQUA/D.BLUE 115 | colorword 22, PINK/D.BLUE 116 | colorword 23, WHITE/D.BLUE 117 | colorword 24, BLUE/RED 118 | colorword 25, YELLOW/RED 119 | colorword 26, PINK/RED 120 | colorword 27, WHITE/RED 121 | colorword 28, AQUA/BROWN 122 | colorword 29, YELLOW/BROWN 123 | colorword 30, PINK/BROWN 124 | colorword 31, WHITE/BROWN 125 | colorword 32, WHITE/BLUE 126 | colorword 33, WHITE/GRAY 127 | colorword 34, WHITE/PURPLE 128 | colorword 35, WHITE/GREEN 129 | colorword 36, WHITE/GREY 130 | colorword 37, WHITE/ORANGE 131 | 132 | 133 | .macro modeword value, name 134 | dictentry_colon "\name" 135 | l8 \value 136 | opx gmode 137 | dictentryend 138 | .endm 139 | -------------------------------------------------------------------------------- /apps/forth/wordlist-to-tsv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import defaultdict 4 | from pprint import pprint 5 | 6 | WORDS = defaultdict(list) 7 | WORDLISTS = set() 8 | 9 | with open('words.txt', 'r') as f: 10 | for line in f: 11 | fields = line.split() 12 | word = fields[0] 13 | wordlist = fields[1] 14 | WORDLISTS.add(wordlist) 15 | WORDS[word].append(wordlist) 16 | 17 | wlists = sorted(list(WORDLISTS)) 18 | words = sorted(WORDS.keys()) 19 | 20 | for word in words: 21 | print '{}\t'.format(word), 22 | for wl in wlists: 23 | flag = '\tY' if wl in WORDS[word] else '\tN' 24 | print flag, 25 | print 26 | print wlists -------------------------------------------------------------------------------- /apps/launcher/launcher.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | #include "console.h" 3 | 4 | MAKE_CONSOLE_40COL(stdcon); 5 | 6 | APP_MAIN(launcher,APP_HIDDEN) 7 | { 8 | CONSOLE_MIO_INIT(stdcon); 9 | const struct app_def *app = NULL; 10 | uint8_t n = 0; 11 | while ((app = app_index_next(app))) { 12 | Pc('a'+n); Pc(':'); Pbl(); Psp(app_name(app)); Pnl(); 13 | n++; 14 | } 15 | Pc('?'); 16 | while (1) { 17 | uint8_t key = mio_getc(0); 18 | if (key >= 'a' && key < 'a'+n) { 19 | uint8_t app_num = key-'a'; 20 | video_stop(); 21 | app_launch(app_index_nth(app_num)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/mandel/mandel.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | #define XR 80 4 | #define YR 100 5 | static const int C1 = 256; 6 | static uint8_t screen[XR*YR]; 7 | static uint8_t backbuf[XR*YR]; 8 | 9 | static const uint8_t color_order[256] PROGMEM = { 10 | 0, 80, 160, 240, 5, 10, 85, 90, 165, 170, 245, 250, 15, 95, 175, 255, 192, 148, 12, 76, 44, 60, 92, 172, 197, 108, 204, 236, 124, 252, 158, 207, 72, 104, 200, 232, 28, 140, 202, 156, 213, 188, 220, 77, 109, 205, 237, 143, 208, 88, 120, 133, 138, 248, 218, 13, 45, 141, 93, 173, 125, 221, 253, 223, 128, 8, 40, 24, 136, 56, 168, 152, 184, 216, 201, 233, 29, 61, 157, 189, 144, 9, 41, 73, 137, 149, 154, 105, 89, 169, 153, 121, 185, 217, 249, 159, 16, 176, 1, 129, 17, 193, 145, 209, 177, 25, 57, 139, 75, 203, 155, 219, 81, 161, 21, 26, 241, 181, 186, 11, 27, 91, 171, 235, 187, 31, 251, 191, 65, 33, 97, 49, 225, 113, 131, 19, 147, 211, 179, 43, 59, 107, 123, 63, 48, 3, 35, 67, 99, 51, 53, 58, 83, 163, 195, 115, 227, 243, 151, 183, 2, 112, 34, 18, 130, 50, 146, 82, 114, 178, 210, 37, 42, 7, 23, 55, 32, 66, 98, 162, 242, 117, 122, 39, 135, 87, 167, 119, 215, 247, 47, 127, 96, 194, 6, 226, 38, 22, 54, 86, 150, 101, 106, 182, 71, 103, 199, 231, 36, 52, 116, 70, 134, 102, 166, 198, 118, 230, 214, 246, 46, 110, 62, 111, 4, 68, 20, 100, 228, 180, 244, 14, 78, 94, 174, 238, 126, 254, 79, 239, 64, 224, 132, 84, 164, 196, 212, 69, 74, 229, 234, 142, 30, 206, 222, 190 11 | }; 12 | static uint8_t color(uint8_t c) { return pgm_read_byte(color_order+c); } 13 | 14 | static int CA = 16; 15 | static int CB = 16; 16 | static int C4 = 5*C1; 17 | static int RN = -2*C1; 18 | static int RX = 2*C1; 19 | static int IN = -5*C1/4; 20 | static int IX = 5*C1/4; 21 | static int MC = 12; 22 | static uint8_t PAL = 0; 23 | 24 | static void retint(uint8_t t) { 25 | uint8_t *p = screen; 26 | uint8_t *b = backbuf; 27 | uint16_t n = sizeof(screen); 28 | while (--n) { 29 | *p = color((*b)+t); 30 | p++; b++; 31 | } 32 | } 33 | 34 | 35 | static void mandel(void) { 36 | int RD = (RX - RN)/(XR - 1); 37 | int ID = (IX - IN)/(YR - 1); 38 | int R; 39 | int I = IN; 40 | int TR, TI; 41 | char *px = backbuf; 42 | char *spx = screen; 43 | for (uint8_t y = 0; y < YR; y++) { 44 | R = RN; 45 | for (uint8_t x = 0; x < XR; x++) { 46 | int ZR = 0; 47 | int ZI = 0; 48 | int R2 = 0; 49 | int I2 = 0; 50 | uint8_t CT = 0; 51 | do { 52 | TR = R2 - I2 + R; 53 | TI = ZR*2/CA*ZI/CB + I; 54 | ZR = TR; 55 | ZI = TI; 56 | R2 = ZR/CA*ZR/CB; 57 | I2 = ZI/CA*ZI/CB; 58 | CT++; 59 | } while ((CT 3 | 4 | #define XR 80 5 | #define YR 50 6 | static uint8_t screen[XR*YR]; 7 | 8 | extern const uint8_t hsvtable[256] PROGMEM; 9 | extern const uint8_t cosinetable_lsb[256] PROGMEM; 10 | extern const uint8_t cosinetable_msb[256] PROGMEM; 11 | 12 | 13 | uint8_t polar_to_xy(uint8_t x, uint8_t y, int16_t a, int16_t b) 14 | { 15 | y = 0x40 - y; 16 | int8_t cosx = pgm_read_byte(cosinetable_msb+x); 17 | int8_t siny = pgm_read_byte(cosinetable_msb+y); 18 | int16_t ca = (a*cosx); 19 | int16_t cb = (b*siny); 20 | uint8_t color = 64|((cb-ca)>>10); 21 | return pgm_read_byte(hsvtable+color); 22 | } 23 | 24 | 25 | APP_MAIN(plasma) 26 | { 27 | init_80x50x256_bitmap_mode(screen); 28 | video_start(); 29 | struct keyreader kr = {0}; 30 | 31 | uint8_t t = 0; 32 | int16_t a = 0; 33 | int16_t b = 0; 34 | while (frame_sync()) { 35 | uint8_t *p = screen; 36 | for (uint8_t y = 0; y < YR; y++) { 37 | for (uint8_t x = 0; x < XR; x++) { 38 | int8_t cosa = pgm_read_byte(cosinetable_msb+(a&0xFF)); 39 | int8_t cosb = pgm_read_byte(cosinetable_msb+(b&0xFF)); 40 | uint8_t c = polar_to_xy(t+cosb+x, t+cosa+y, a, b); 41 | *p++ = c; 42 | } 43 | } 44 | t++; a++; b++; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /apps/rfk/matt.nki: -------------------------------------------------------------------------------- 1 | Niblets. 2 | Egg. 3 | Wub. 4 | It's bugeggs. 5 | Failure. 6 | A "Three Wolf Moon" t-shirt. 7 | An old meme. 8 | That Like Button. 9 | A bottle of "non-brewed condiment." 10 | Hot chickens. 11 | A pork roll egg and cheese, on a kaiser bun. 12 | An uncomfortable silence. 13 | Windows Millennium Edition, in its original shrinkwrapped box. 14 | A mousepad. (remember those?) 15 | CYBRMATRIX 100. 16 | Hot hamburg sandwiches. (Pull the **** over, I'm starving!) 17 | A 240p video. 18 | A small liberal arts college in Massachusetts. 19 | A password longer than eight characters. 20 | The computer in the background of the sitcom set that never gets used. 21 | One hundred Rosalina amiibos. 22 | Pet ID #3957294. 23 | A list of good names for cats. 24 | A brand with a hip social media presence. 25 | "Smooth" by Santana featuring Rob Thomas of Matchbox Twenty. 26 | The missing Trivial Pursuit pieces. 27 | The GameStop Employee of the Month. 28 | Original character (do not steal!) 29 | The illusion of privacy. 30 | Borrowed nostalgia for the unremembered '80s. 31 | "Bee Movie" Criterion Collection Edition. 32 | A craft beer that isn't another damn IPA. 33 | Beethoven (the dog, not the composer). 34 | Internet addiction. 35 | Vegan Garfield. 36 | Pre-hair-transplant Elon Musk. 37 | Dankey Kang. 38 | A "Skip Ad" button. You press it and nothing happens. 39 | Carmen Sandiego. 40 | None Pizza with Left Beef. 41 | Fifteen percent or more on car insurance. 42 | A lame impala. 43 | A handful of tan M&Ms. 44 | A cat who walks through walls. 45 | The Banach-Tarski Paradox. 46 | A dog's purpose. 47 | BLAST PROCESSING!!!! 48 | ....... *boop* 49 | Modified food starch. 50 | The onramp to the INFORMATION SUPERHIGHWAY. 51 | One hundred horse-sized ducks. 52 | One hundred duck-sized horses. 53 | CRAB BATTLE!!!! 54 | Mom's spaghetti. 55 | Here lies andy, peperony and chease. 56 | Modest "Mouse" Mussorgsky. 57 | TELEPHONE TELEPHONE TELEPHONE 58 | The means of production, just out of seizing reach. 59 | A copy of the official Internet Yellow Pages (sixth edition). 60 | The sign reads "NO LOAFING!" in large red letters. 61 | An orgone accumulator. 62 | Ten pounds of doodoo in a five pound bag. 63 | Today's hits and yesterday's favorites. 64 | A manganese mongoose. 65 | The Military Industrial Complex. (Free 2-hour parking!) 66 | A little red courgette. 67 | Taco pockets. 68 | Darkweb Duck. 69 | Jaggerbush! 70 | Abe Froman, the Sausage King of Chicago. 71 | An updated privacy policy. 72 | A pesseract. 73 | A secret parrot. 74 | A really good hug from David Byrne. 75 | A deep and dreamless slumber. 76 | A bag o' rags. 77 | A world of fragile cloned organisms highly vulnerable to extinction. 78 | A microhenry. 79 | Outback Snakehouse. 80 | The withered hand of evil. 81 | A vial of recreational morphine. 82 | The Mojave Phone Booth. 83 | Fresh pear, don't care. 84 | -----BEGIN RSA PRIVATE KEY----- 85 | Maliciously destroyed cabbages. 86 | The Aroma of Tacoma. 87 | Soggy tacos. 88 | MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! MOTH! 89 | The dreaded Girderfinger. 90 | You can't tell what it is, but it's artisanal and locally-sourced. 91 | Linux Biscuits. 92 | One gluten. 93 | Nine dairy alternatives. 94 | A Blockbuster Video, still in business. 95 | Doctor Wobbert. 96 | A flaming pile of cable boxes. 97 | The Great Everett Tire Fire of 1984. 98 | A firmware update. 99 | A paywall. 100 | Beans. 101 | One spaghetto. 102 | A real mouthy chat bot. 103 | A $50 gift card for Wipin' Willie's Toilet Paper Megastore. 104 | Boiled cheese. 105 | blue hedgehog 106 | Steamed hams. 107 | A heaping stack of freshly-baked Pecan Sandies. 108 | The shiniest ball bearing you've ever seen. 109 | It's Doug. 110 | A freshly shaved neckbeard. 111 | "To destroy special radio, press both buttons simultaneously." 112 | Mitochondria, the powerhouse of the cell. 113 | Mr. Paddles chases you away with a wooden oar. 114 | Hen Island. 115 | A disposable cup with that jazzy teal and purple design. 116 | A whiffenpoof. 117 | A hot tub time machine. 118 | "I wish to find kitten," you say to the genie. Nothing happens. 119 | A wib. (What's a wib?) 120 | In awe at the size of this lad. Absolute unit. 121 | A thousand cans of chowder and a thousand cans of beer. 122 | Someone playing the "Baker Street" saxophone solo on a kazoo. 123 | The goose honks at you. *HONK* 124 | You slip on a deposit slip! 125 | It's a Bobson Dugnutt baseball card. 126 | One Netflick. 127 | Your father's parentheses. Elegant weapons for a more civilized age. 128 | -------------------------------------------------------------------------------- /apps/rfk/nki-to-h.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Converts a regular NKI file (plain text strings, one per line) to a C header. 4 | # Inserts line breaks as necessary to wrap to a screen less than 80 columns wide. 5 | 6 | import sys 7 | import textwrap 8 | 9 | NKI_WRAP_WIDTH = 40 10 | LONG_NKI_LINES_EXCLUDE = 2 11 | 12 | C_ESCAPE_SEQUENCES = { 13 | 0x07: '\\a', 14 | 0x08: '\\b', 15 | 0x09: '\\t', 16 | 0x0A: '\\n', 17 | 0x0B: '\\v', 18 | 0x0C: '\\f', 19 | 0x0D: '\\r', 20 | 0x5C: '\\\\', 21 | 0x22: '\\"', 22 | 0x3F: '\\?', 23 | } 24 | 25 | # yucky but oh well 26 | def escape_c_char(ch): 27 | asciival = ord(ch) 28 | s = C_ESCAPE_SEQUENCES.get(asciival) 29 | if s is None: 30 | s = '\\x{:02x}'.format(asciival) if (asciival < 32 or asciival >= 127) else ch 31 | return s 32 | 33 | def c_string_literal_encode(s): 34 | return ''.join(escape_c_char(ch) for ch in s) 35 | 36 | def encode_nki(num, text): 37 | text = text.strip() 38 | lines = textwrap.wrap(text, NKI_WRAP_WIDTH) 39 | text = '\n'.join(lines) 40 | return 'static const char nki_{}[] PROGMEM = "{}";'.format(num, c_string_literal_encode(text)), len(lines) 41 | 42 | with open(sys.argv[1]) as f: 43 | num_nkis = 0 44 | max_lines = 0 45 | for line in f: 46 | text, nlines = encode_nki(num_nkis, line) 47 | if nlines > LONG_NKI_LINES_EXCLUDE: 48 | print >> sys.stderr, 'nki exceeds max lines, excluded: {}'.format(text) 49 | continue 50 | max_lines = max(max_lines, nlines) 51 | num_nkis += 1 52 | print text 53 | 54 | print '#define MAX_NKI_LINES {}'.format(max_lines) 55 | print '#define NUM_NKIS {}'.format(num_nkis) 56 | print '#define NKI_RAND_MASK {}'.format((1< 21 | struct vt_parser_state; 22 | 23 | typedef void (*vt_parser_hook)(uint8_t c); 24 | 25 | /* The specs recommend support for a minimum of 16 parameter values in a */ 26 | /* CSI sequence. If a smaller memory footprint is desired, this may be */ 27 | /* lowered. */ 28 | #ifndef VT_PARSER_MAX_PARAMS 29 | #define VT_PARSER_MAX_PARAMS 16 30 | #endif 31 | 32 | /* There's no official spec indicating the acceptable range of numeric */ 33 | /* parameters. [1] recommends 16383 as a maximum, but [2] recommends 255. */ 34 | /* On 8-bit and memory-constrained systems, two bytes per parameter may */ 35 | /* be excessive. If so, define VT_PARSER_SMALL_PARAMS to 1, and the parser */ 36 | /* will reserve only one byte for each parameter. */ 37 | /* Regardless of whether 8-bit or 16-bit parameters are used, there is no */ 38 | /* checking for integer overflow/wraparound. */ 39 | #ifndef VT_PARSER_SMALL_PARAMS 40 | #define VT_PARSER_SMALL_PARAMS 1 41 | #endif 42 | 43 | 44 | #if VT_PARSER_SMALL_PARAMS 45 | typedef int8_t vt_param; 46 | #else 47 | typedef int16_t vt_param; 48 | #endif 49 | 50 | struct vt_parser { 51 | const struct vt_parser_state *current; 52 | vt_param params[VT_PARSER_MAX_PARAMS]; 53 | uint8_t num_params; 54 | uint8_t intermediate_char; /* also stores the private marker */ 55 | /* User-supplied handlers */ 56 | vt_parser_hook print; 57 | vt_parser_hook execute_c0_c1; 58 | vt_parser_hook execute_esc; 59 | vt_parser_hook execute_csi; 60 | }; 61 | 62 | void vt_parser_init(struct vt_parser *vtp); 63 | 64 | void vt_parser_process(struct vt_parser *vtp, uint8_t c); 65 | 66 | vt_param vt_parser_get_param(struct vt_parser *vtp, uint8_t param_index, vt_param default_value); 67 | 68 | #endif -------------------------------------------------------------------------------- /apps/slideshow/slideshow.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Slideshow 3 | * Displays still images received over the serial port. (57600 baud) 4 | * Images should be raw bitmap data, 160x200 pixels at 8 bits per pixel 5 | * (16000 bytes total) using the 256-color palette. 6 | * 7 | * No synchronization and very little bounds checking. 8 | */ 9 | 10 | #include "app.h" 11 | 12 | static uint8_t screen[16000]; 13 | static volatile uint8_t *pixptr; 14 | 15 | /* just store the byte and advance the pointer; */ 16 | /* handle wraparound in main loop code. */ 17 | /* yes it's possible to get a buffer overflow here */ 18 | __attribute__((naked)) static void serial_rx_isr(void) { 19 | asm volatile( 20 | "lds ZL, pixptr\n" 21 | "lds ZH, pixptr+1\n" 22 | "st Z+, r2\n" 23 | "sts pixptr, ZL\n" 24 | "sts pixptr+1, ZH\n" 25 | "ret\n"); 26 | } 27 | 28 | 29 | APP_MAIN(slideshow) 30 | { 31 | struct serial_port serialconfig = { 32 | SERIAL_BAUD_57600, 33 | SERIAL_PARITY_NONE, 34 | SERIAL_DATA_BITS_8, 35 | SERIAL_STOP_BITS_1 36 | }; 37 | 38 | pixptr = screen; 39 | serial_init(serialconfig); 40 | serial_async_set_callback(serial_rx_isr); 41 | memset(screen, 0x55, sizeof(screen)); 42 | init_160x100x256_bitmap_mode(screen); 43 | video_start(); 44 | 45 | while (frame_sync()) { 46 | /* Handle pixel pointer wraparound. */ 47 | /* Don't need to do anything else, because the serial */ 48 | /* receive ISR puts pixel data directly into the screen buffer .*/ 49 | if (pixptr >= screen+sizeof(screen)) { 50 | pixptr = screen; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /apps/test_keys/test_keys.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Displays keyboard state and key-down events. 3 | */ 4 | #include "app.h" 5 | #include "keys.h" 6 | #include 7 | 8 | #define SCREEN_SIZE_BYTES 2000 9 | static char screen[SCREEN_SIZE_BYTES]; 10 | #define CELLADDR(x,y) (screen+((y)*40)+(x)) 11 | 12 | static void draw_key_row_state(char *pos, uint8_t bits, const char * PROGMEM legend) 13 | { 14 | bits |= 0b11000000; 15 | uint8_t bitmask = 0x80; 16 | strcpy_P(pos, legend); 17 | uint32_t *p = (uint32_t *)(pos+2); 18 | while (bitmask) { 19 | if ((bits & bitmask) == 0) { *p |= 0x80808080; } 20 | p++; 21 | bitmask >>= 1; 22 | } 23 | } 24 | 25 | 26 | APP_MAIN(test_keys) 27 | { 28 | init_40x25_mono_text_mode(screen, NULL); 29 | video_start(); 30 | struct keyreader kr = {0}; 31 | 32 | strcpy_P(CELLADDR(3,0), PSTR("7 6 5 4 3 2 1 0")); 33 | *CELLADDR(1,1) = 21; 34 | memset(CELLADDR(2,1), 19, 32); 35 | *CELLADDR(1,3) = 28; 36 | *CELLADDR(1,5) = 28; 37 | *CELLADDR(1,7) = 28; 38 | *CELLADDR(1,9) = 28; 39 | *CELLADDR(1,11) = 28; 40 | *CELLADDR(1,13) = 28; 41 | *CELLADDR(1,15) = 28; 42 | strcpy_P(CELLADDR(0, 18), PSTR("Last key down:")); 43 | strcpy_P(CELLADDR(0, 20), PSTR("ASCII value:")); 44 | while (frame_sync()) { 45 | #if KEYBOARD_LAYOUT_1_0 46 | draw_key_row_state(CELLADDR(0,2), keymatrixrows[0], PSTR("0\x1c O U T E Q ")); 47 | draw_key_row_state(CELLADDR(0,4), keymatrixrows[1], PSTR("1\x1c Rt Lf , = Sym ")); 48 | draw_key_row_state(CELLADDR(0,6), keymatrixrows[2], PSTR("2\x1c Up M B C Z ")); 49 | draw_key_row_state(CELLADDR(0,8), keymatrixrows[3], PSTR("3\x1c L J G D A ")); 50 | draw_key_row_state(CELLADDR(0,10), keymatrixrows[4], PSTR("4\x1c Del ' N V X Shf ")); 51 | draw_key_row_state(CELLADDR(0,12), keymatrixrows[5], PSTR("5\x1c P I Y R W Esc ")); 52 | draw_key_row_state(CELLADDR(0,14), keymatrixrows[6], PSTR("6\x1c Ent K H F S Ctrl")); 53 | draw_key_row_state(CELLADDR(0,16), keymatrixrows[7], PSTR("7\x1c / Dn . Spc - ")); 54 | #else 55 | draw_key_row_state(CELLADDR(0,2), keymatrixrows[0], PSTR("0\x1c 0 8 6 4 2 Esc ")); 56 | draw_key_row_state(CELLADDR(0,4), keymatrixrows[1], PSTR("1\x1c Rt Lf N V X ")); 57 | draw_key_row_state(CELLADDR(0,6), keymatrixrows[2], PSTR("2\x1c Dn M B C Z Sym ")); 58 | draw_key_row_state(CELLADDR(0,8), keymatrixrows[3], PSTR("3\x1c Up K H F S Shf ")); 59 | draw_key_row_state(CELLADDR(0,10), keymatrixrows[4], PSTR("4\x1c Spc O U T E Q ")); 60 | draw_key_row_state(CELLADDR(0,12), keymatrixrows[5], PSTR("5\x1c Del 9 7 5 3 1 ")); 61 | draw_key_row_state(CELLADDR(0,14), keymatrixrows[6], PSTR("6\x1c P I Y R W Ctrl")); 62 | draw_key_row_state(CELLADDR(0,16), keymatrixrows[7], PSTR("7\x1c Ent L J G D A ")); 63 | #endif 64 | key_code code = get_key_down(&kr); 65 | if (code.isdown) { 66 | *CELLADDR(15,18) = '0' + code.row + 128; 67 | *CELLADDR(16,18) = '0' + code.col + 128; 68 | } else { 69 | *CELLADDR(15,18) &= 0x7F; 70 | *CELLADDR(16,18) &= 0x7F; 71 | } 72 | char asc = key_code_to_ascii(code); 73 | if (asc) { 74 | *CELLADDR(15,20) = key_code_to_ascii(code); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /apps/test_patterns/test_patterns.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Displays various color test patterns. 3 | */ 4 | #include "app.h" 5 | #include 6 | 7 | static uint8_t screen[16016]; 8 | 9 | static const uint8_t nice_color_order[16] PROGMEM = { 10 | 0, 10, 4, 8, 1, 2, 6, 14, 12, 13, 9, 11, 7, 3, 5, 15 11 | }; 12 | 13 | static void test_pattern_16_color(void) 14 | { 15 | init_160x200x16_bitmap_mode(screen); 16 | /* 16 vertical stripes, one for each color, each 10 pixels wide. */ 17 | /* Do the first line and then replicate it. */ 18 | uint8_t *pixptr = screen; 19 | for (uint8_t c = 0; c < 16; c++) { 20 | uint8_t color = pgm_read_byte(nice_color_order+c); 21 | memset(pixptr, color|(color<<4), 4); 22 | pixptr[4] = 0; 23 | pixptr += 5; 24 | } 25 | 26 | /* leave last 8 rows black, so it goes nicer with the 256 color test */ 27 | for (uint8_t y = 0; y < 191; y++) { 28 | memcpy(pixptr, screen, 80); 29 | pixptr += 80; 30 | } 31 | } 32 | 33 | 34 | static void test_pattern_256_color(void) 35 | { 36 | init_160x100x256_bitmap_mode(screen); 37 | uint8_t *pixptr = screen; 38 | uint8_t color = 0; 39 | for (uint8_t gridrow = 0; gridrow < 16; gridrow++) { 40 | uint8_t *gridrowstart = pixptr; 41 | /* Do the first pixel row of each grid row */ 42 | for (uint8_t gridcol = 0; gridcol < 16; gridcol++) { 43 | uint8_t lo = color & 0xF; 44 | uint8_t hi = (color >> 4) & 0xF; 45 | uint8_t nice_color = pgm_read_byte(nice_color_order+lo) | (pgm_read_byte(nice_color_order+hi)<<4); 46 | memset(pixptr, nice_color, 9); 47 | pixptr[9] = 0; 48 | pixptr += 10; 49 | color++; 50 | } 51 | /* Then replicate this row for the remaining 11 pixel rows in this grid row */ 52 | for (uint8_t subrow = 0; subrow < 5; subrow++) { 53 | if (subrow != 4) { 54 | memcpy(pixptr, gridrowstart, 160); 55 | } else { 56 | memset(pixptr, 0, 160); 57 | } 58 | pixptr += 160; 59 | } 60 | } 61 | /* Blank the remaining four rows */ 62 | memset(pixptr, 0, 640); 63 | } 64 | 65 | 66 | static void test_pattern_256_color_grays_only(void) 67 | { 68 | init_160x100x256_bitmap_mode(screen); 69 | } 70 | 71 | 72 | APP_MAIN(test_patterns) 73 | { 74 | test_pattern_256_color(); 75 | video_start(); 76 | struct keyreader kr = {0}; 77 | uint8_t sound = 31; 78 | while (frame_sync()) { 79 | switch (get_key_ascii(&kr)) { 80 | case 'q': test_pattern_16_color(); break; 81 | case 'w': test_pattern_256_color(); break; 82 | case 'e': test_pattern_256_color_grays_only(); break; 83 | case 'r': sound++; break; 84 | case 't': sound--; break; 85 | } 86 | /* weird sound */ 87 | OCR0A+=sound; 88 | OCR0B = OCR0A>>2; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /apps/test_raster_interrupts/test_raster_interrupts.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Dynamic raster interrupt test. 3 | * 4 | * The text should be aqua, except for a sliding stripe of red. 5 | * Two raster interrupts are used to alternate between text colors. 6 | * The line numbers are also incremented, which causes the scrolling effect. 7 | */ 8 | #include "app.h" 9 | #include "raster_interrupt.h" 10 | 11 | static volatile uint8_t line1 = 0; 12 | static volatile uint8_t line2 = 0; 13 | static volatile uint8_t color1 = 0; 14 | static volatile uint8_t color2 = 0; 15 | static uint8_t screen[1000]; 16 | 17 | RASTER_INTERRUPT(test_raster_int); 18 | RASTER_INTERRUPT(test_raster_int_2) 19 | { 20 | RI_SET8(globalcolor, color2); 21 | RI_SET_NEXT_HANDLER(line2, test_raster_int); 22 | RI_RET(); 23 | } 24 | 25 | RASTER_INTERRUPT(test_raster_int) 26 | { 27 | RI_SET_TEXT_COLOR(color1); 28 | RI_SET_NEXT_HANDLER(line1, test_raster_int_2); 29 | RI_RET(); 30 | } 31 | 32 | 33 | APP_MAIN(test_raster_interrupts) 34 | { 35 | line1 = 128; 36 | line2 = 64; 37 | color1 = TEXT_COLOR_AQUA; 38 | color2 = TEXT_COLOR_RED; 39 | init_40x25_2color_text_mode(screen, NULL); 40 | set_raster_interrupt(line1, test_raster_int); 41 | 42 | for (unsigned i = 0; i < sizeof(screen); i++) { 43 | screen[i] = i&0xFF; 44 | } 45 | 46 | strcpy_P((char*)screen, PSTR("q/w: change color 1")); 47 | strcpy_P((char*)screen+40, PSTR("a/s: change color 2")); 48 | strcpy_P((char*)screen+80, PSTR("esc: reset colors ")); 49 | memset(screen+120, ' ', 20); 50 | 51 | struct keyreader kr = {0}; 52 | video_start(); 53 | while (frame_sync()) { 54 | switch (get_key_ascii(&kr)) { 55 | case 'q': color1++; break; 56 | case 'w': color1--; break; 57 | case 'a': color2++; break; 58 | case 's': color2--; break; 59 | case KC_ESC: color1 = TEXT_COLOR_AQUA; color2 = TEXT_COLOR_RED; break; 60 | } 61 | line1+=1; if (line1 >= 200) {line1 = 0;} 62 | line2+=1; if (line2 >= 200) {line2 = 0;} 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /apps/test_split_screen/test_split_screen.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Split-screen implementation by changing video modes using raster interrupts. 3 | */ 4 | #include "app.h" 5 | #include "raster_interrupt.h" 6 | #include 7 | 8 | static uint8_t screen[16000]; 9 | static volatile uint8_t splitline1; 10 | static volatile uint8_t splitline2; 11 | static volatile uint8_t splitline3; 12 | 13 | RASTER_INTERRUPT(split_enable_256color_mode); 14 | RASTER_INTERRUPT(split_enable_16color_mode); 15 | RASTER_INTERRUPT(split_enable_40col_text_mode); 16 | RASTER_INTERRUPT(split_enable_80col_text_mode); 17 | 18 | 19 | RASTER_INTERRUPT(split_enable_16color_mode) 20 | { 21 | RI_SET_VIDEO_MODE(VIDEO_MODE_160x200_16COLOR); 22 | RI_SET_VPTR(screen); 23 | RI_SET_NEXT_HANDLER(splitline2, split_enable_40col_text_mode); 24 | RI_RET(); 25 | } 26 | 27 | 28 | RASTER_INTERRUPT(split_enable_40col_text_mode) 29 | { 30 | RI_SET_VIDEO_MODE_FAST(VIDEO_MODE_40x25_TEXT); 31 | RI_SET_VPTR(screen); 32 | RI_SET_NEXT_HANDLER(splitline3, split_enable_80col_text_mode); 33 | RI_WITH_SAVED_SREG() { 34 | asm volatile( 35 | "mov r30, r3\n" 36 | "neg r30\n" 37 | "andi r30, 7\n" 38 | "sts vscroll, r30\n" 39 | ); 40 | } 41 | RI_RET(); 42 | } 43 | 44 | 45 | RASTER_INTERRUPT(split_enable_80col_text_mode) 46 | { 47 | RI_SET_VIDEO_MODE_FAST(VIDEO_MODE_80x25_TEXT); 48 | RI_SET_VPTR(screen); 49 | RI_SET_NEXT_HANDLER(0, split_enable_256color_mode); 50 | RI_WITH_SAVED_SREG() { 51 | asm volatile( 52 | "mov r30, r3\n" 53 | "neg r30\n" 54 | "andi r30, 7\n" 55 | "sts vscroll, r30\n" 56 | ); 57 | } 58 | RI_RET(); 59 | } 60 | 61 | 62 | RASTER_INTERRUPT(split_enable_256color_mode) 63 | { 64 | RI_SET_VIDEO_MODE(VIDEO_MODE_80x50_256COLOR); 65 | RI_SET_VPTR(screen); 66 | RI_SET_NEXT_HANDLER(splitline1, split_enable_16color_mode); 67 | RI_RET(); 68 | } 69 | 70 | 71 | 72 | APP_MAIN(test_split_screen) 73 | { 74 | struct keyreader kr = {0}; 75 | splitline1 = 144; 76 | splitline2 = 96; 77 | splitline3 = 48; 78 | 79 | set_tilemap(cp437_font_8x8); 80 | globalcolor = TEXT_COLOR_WHITE; 81 | for (unsigned i = 0; i < sizeof(screen); i++) { 82 | screen[i] = i; 83 | } 84 | strcpy_P((char*)screen, PSTR("q/w: move split 1 up/down one line ")); 85 | strcpy_P((char*)screen+40, PSTR("a/s: move split 2 up/down one line ")); 86 | strcpy_P((char*)screen+80, PSTR("z/x: move split 3 up/down one line ")); 87 | strcpy_P((char*)screen+120, PSTR("esc: reset split positions ")); 88 | 89 | /* Prepare the first video mode and register the first raster interrupt */ 90 | init_80x50x256_bitmap_mode(screen); 91 | set_raster_interrupt(splitline1, split_enable_16color_mode); 92 | 93 | video_start(); 94 | 95 | while (frame_sync()) { 96 | switch (get_key_ascii(&kr)) { 97 | case 'q': splitline1++; break; 98 | case 'w': splitline1--; break; 99 | case 'a': splitline2++; break; 100 | case 's': splitline2--; break; 101 | case 'z': splitline3++; break; 102 | case 'x': splitline3--; break; 103 | case KC_ESC: splitline1 = 144; splitline2 = 96; splitline3 = 48; break; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /apps/test_tiled_modes/test_tiled_modes.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | extern const TILEMAP test_tileset_8x8; 4 | 5 | #define SCREEN_SIZE_BYTES 1000 6 | static uint8_t screen[SCREEN_SIZE_BYTES]; 7 | 8 | APP_MAIN(test_tiled_modes) 9 | { 10 | for (int i = 0; i < SCREEN_SIZE_BYTES; i++) { 11 | screen[i] = i; 12 | } 13 | init_hires_tiled_mode(screen, test_tileset_8x8); 14 | video_start(); 15 | struct keyreader kr = {0}; 16 | 17 | while (frame_sync()) { 18 | switch (get_key_ascii(&kr)) { 19 | case 'q': vscroll++; break; 20 | case 'w': vscroll--; break; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /assets/amscii_font_8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/assets/amscii_font_8x8.png -------------------------------------------------------------------------------- /assets/crumbquadtable.S: -------------------------------------------------------------------------------- 1 | ; "crumb" is an obscure term for a 2-bit quantity 2 | .section .progmem.assets 3 | .balign 256 4 | .global crumbquadtable 5 | .type crumbquadtable, @object 6 | crumbquadtable: 7 | .rept 64 8 | .byte 0b00000000, 0b01010101, 0b10101010, 0b11111111 9 | .endr 10 | 11 | .global crumbmasktable 12 | .type crumbmasktable, @object 13 | crumbmasktable: 14 | .rept 64 15 | .byte 0b11000000, 0b00110000, 0b00001100, 0b00000011 16 | .endr 17 | 18 | .global bitmasktable 19 | .type bitmasktable, @object 20 | bitmasktable: 21 | .rept 32 22 | .byte 0b10000000, 0b01000000, 0b00100000, 0b00010000 23 | .byte 0b00001000, 0b00000100, 0b00000010, 0b00000001 24 | .endr 25 | 26 | 27 | ; fragments of horizontal lines, used in 2-bit-per-pixel bitmap modes 28 | ; The table is padded out because it's easier to combine 2 nibbles into a byte 29 | ; (2 cycles: SWAP+OR) than to combine two crumbs into a nibble (3 cycles: LSL+LSL+OR) 30 | .global lhlin_fragments 31 | .type lhlin_fragments, @object 32 | lhlin_fragments: 33 | ; Start bit: 0 34 | ; End bit: 0 1 2 3 35 | .byte 0b11000000, 0b11110000, 0b11111100, 0b11111111, 0,0,0,0,0,0,0,0,0,0,0,0 36 | ; Start bit: 1 37 | ; End bit: 0 1 2 3 38 | .byte 0b11110000, 0b00110000, 0b00111100, 0b00111111, 0,0,0,0,0,0,0,0,0,0,0,0 39 | ; Start bit: 2 40 | ; End bit: 0 1 2 3 41 | .byte 0b11111100, 0b00111100, 0b00001100, 0b00001111, 0,0,0,0,0,0,0,0,0,0,0,0 42 | ; Start bit: 3 43 | ; End bit: 0 1 2 3 44 | .byte 0b11111111, 0b00111111, 0b00001111, 0b00000011, 0,0,0,0,0,0,0,0,0,0,0,0 45 | .space 192 46 | 47 | ; fragments of vertical lines, used i1 2-bit-per-pixel bitmap modes 48 | ; The table is padded out because it's easier to combine 2 nibbles into a byte 49 | ; (2 cycles: SWAP+OR) than to combine two three-bit quantities into a byte 50 | ; (4 cycles: LSL+LSL+LSL+OR) 51 | .global bhlin_fragments 52 | .type bhlin_fragments, @object 53 | bhlin_fragments: 54 | ; Start bit: 0 55 | ; End bit: 0 1 2 3 4 5 6 7 56 | .byte 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110, 0b11111111, 0,0,0,0,0,0,0,0 57 | ; Start bit: 1 58 | ; End bit: 0 1 2 3 4 5 6 7 59 | .byte 0b11000000, 0b01000000, 0b01100000, 0b01110000, 0b01111000, 0b01111100, 0b01111110, 0b01111111, 0,0,0,0,0,0,0,0 60 | ; Start bit: 2 61 | ; End bit: 0 1 2 3 4 5 6 7 62 | .byte 0b11100000, 0b01100000, 0b00100000, 0b00110000, 0b00111000, 0b00111100, 0b00111110, 0b00111111, 0,0,0,0,0,0,0,0 63 | ; Start bit: 3 64 | ; End bit: 0 1 2 3 4 5 6 7 65 | .byte 0b11110000, 0b01110000, 0b00110000, 0b00010000, 0b00011000, 0b00011100, 0b00011110, 0b00011111, 0,0,0,0,0,0,0,0 66 | ; Start bit: 4 67 | ; End bit: 0 1 2 3 4 5 6 7 68 | .byte 0b11111000, 0b01111000, 0b00111000, 0b00011000, 0b00001000, 0b00001100, 0b00001110, 0b00001111, 0,0,0,0,0,0,0,0 69 | ; Start bit: 5 70 | ; End bit: 0 1 2 3 4 5 6 7 71 | .byte 0b11111100, 0b01111100, 0b00111100, 0b00011100, 0b00001100, 0b00000100, 0b00000110, 0b00000111, 0,0,0,0,0,0,0,0 72 | ; Start bit: 6 73 | ; End bit: 0 1 2 3 4 5 6 7 74 | .byte 0b11111110, 0b01111110, 0b00111110, 0b00011110, 0b00001110, 0b00000110, 0b00000010, 0b00000011, 0,0,0,0,0,0,0,0 75 | ; Start bit: 7 76 | ; End bit: 0 1 2 3 4 5 6 7 77 | .byte 0b11111111, 0b01111111, 0b00111111, 0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0,0,0,0,0,0,0,0 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /assets/hsvtable.S: -------------------------------------------------------------------------------- 1 | .section .progmem.assets 2 | .balign 256 3 | .global hsvtable 4 | .type hsvtable, @object 5 | hsvtable: 6 | .byte 240 7 | .byte 133 8 | .byte 133 9 | .byte 133 10 | .byte 133 11 | .byte 21 12 | .byte 21 13 | .byte 21 14 | .byte 21 15 | .byte 112 16 | .byte 112 17 | .byte 112 18 | .byte 69 19 | .byte 69 20 | .byte 69 21 | .byte 69 22 | .byte 148 23 | .byte 148 24 | .byte 133 25 | .byte 133 26 | .byte 176 27 | .byte 176 28 | .byte 176 29 | .byte 176 30 | .byte 97 31 | .byte 112 32 | .byte 112 33 | .byte 112 34 | .byte 224 35 | .byte 224 36 | .byte 224 37 | .byte 224 38 | .byte 192 39 | .byte 192 40 | .byte 208 41 | .byte 208 42 | .byte 193 43 | .byte 65 44 | .byte 65 45 | .byte 97 46 | .byte 97 47 | .byte 32 48 | .byte 32 49 | .byte 96 50 | .byte 96 51 | .byte 20 52 | .byte 20 53 | .byte 20 54 | .byte 192 55 | .byte 192 56 | .byte 40 57 | .byte 40 58 | .byte 24 59 | .byte 1 60 | .byte 1 61 | .byte 33 62 | .byte 48 63 | .byte 48 64 | .byte 32 65 | .byte 96 66 | .byte 96 67 | .byte 64 68 | .byte 64 69 | .byte 64 70 | .byte 197 71 | .byte 197 72 | .byte 213 73 | .byte 213 74 | .byte 213 75 | .byte 181 76 | .byte 181 77 | .byte 181 78 | .byte 53 79 | .byte 53 80 | .byte 37 81 | .byte 37 82 | .byte 101 83 | .byte 69 84 | .byte 69 85 | .byte 69 86 | .byte 148 87 | .byte 148 88 | .byte 45 89 | .byte 45 90 | .byte 149 91 | .byte 225 92 | .byte 225 93 | .byte 43 94 | .byte 195 95 | .byte 146 96 | .byte 130 97 | .byte 130 98 | .byte 52 99 | .byte 180 100 | .byte 180 101 | .byte 212 102 | .byte 44 103 | .byte 60 104 | .byte 13 105 | .byte 105 106 | .byte 105 107 | .byte 209 108 | .byte 241 109 | .byte 113 110 | .byte 131 111 | .byte 146 112 | .byte 2 113 | .byte 194 114 | .byte 116 115 | .byte 84 116 | .byte 84 117 | .byte 132 118 | .byte 12 119 | .byte 28 120 | .byte 232 121 | .byte 13 122 | .byte 73 123 | .byte 145 124 | .byte 177 125 | .byte 49 126 | .byte 147 127 | .byte 18 128 | .byte 18 129 | .byte 36 130 | .byte 36 131 | .byte 68 132 | .byte 68 133 | .byte 132 134 | .byte 158 135 | .byte 158 136 | .byte 143 137 | .byte 143 138 | .byte 159 139 | .byte 235 140 | .byte 235 141 | .byte 107 142 | .byte 107 143 | .byte 117 144 | .byte 117 145 | .byte 117 146 | .byte 101 147 | .byte 30 148 | .byte 30 149 | .byte 30 150 | .byte 124 151 | .byte 109 152 | .byte 109 153 | .byte 61 154 | .byte 61 155 | .byte 139 156 | .byte 11 157 | .byte 43 158 | .byte 195 159 | .byte 195 160 | .byte 210 161 | .byte 150 162 | .byte 46 163 | .byte 244 164 | .byte 244 165 | .byte 212 166 | .byte 44 167 | .byte 188 168 | .byte 77 169 | .byte 93 170 | .byte 121 171 | .byte 121 172 | .byte 27 173 | .byte 59 174 | .byte 211 175 | .byte 67 176 | .byte 2 177 | .byte 66 178 | .byte 134 179 | .byte 228 180 | .byte 228 181 | .byte 196 182 | .byte 12 183 | .byte 140 184 | .byte 156 185 | .byte 157 186 | .byte 217 187 | .byte 57 188 | .byte 25 189 | .byte 147 190 | .byte 19 191 | .byte 3 192 | .byte 18 193 | .byte 66 194 | .byte 100 195 | .byte 100 196 | .byte 196 197 | .byte 196 198 | .byte 158 199 | .byte 207 200 | .byte 223 201 | .byte 223 202 | .byte 159 203 | .byte 159 204 | .byte 191 205 | .byte 191 206 | .byte 63 207 | .byte 63 208 | .byte 215 209 | .byte 231 210 | .byte 111 211 | .byte 254 212 | .byte 190 213 | .byte 222 214 | .byte 108 215 | .byte 124 216 | .byte 237 217 | .byte 253 218 | .byte 219 219 | .byte 155 220 | .byte 187 221 | .byte 123 222 | .byte 243 223 | .byte 23 224 | .byte 242 225 | .byte 182 226 | .byte 246 227 | .byte 78 228 | .byte 78 229 | .byte 142 230 | .byte 236 231 | .byte 252 232 | .byte 237 233 | .byte 189 234 | .byte 189 235 | .byte 155 236 | .byte 27 237 | .byte 59 238 | .byte 115 239 | .byte 99 240 | .byte 34 241 | .byte 22 242 | .byte 198 243 | .byte 230 244 | .byte 78 245 | .byte 78 246 | .byte 204 247 | .byte 204 248 | .byte 220 249 | .byte 157 250 | .byte 249 251 | .byte 185 252 | .byte 185 253 | .byte 51 254 | .byte 51 255 | .byte 35 256 | .byte 50 257 | .byte 6 258 | .byte 70 259 | .byte 70 260 | .byte 228 261 | .byte 196 262 | -------------------------------------------------------------------------------- /assets/nibbledoubletable.S: -------------------------------------------------------------------------------- 1 | .section .progmem.assets 2 | .balign 256 3 | .global lowernibbledoubletable 4 | .type lowernibbledoubletable, @object 5 | lowernibbledoubletable: 6 | .rept 16 7 | .byte 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF 8 | .endr 9 | 10 | .global uppernibbledoubletable 11 | .type uppernibbledoubletable, @object 12 | uppernibbledoubletable: 13 | .space 16, 0x00 14 | .space 16, 0x11 15 | .space 16, 0x22 16 | .space 16, 0x33 17 | .space 16, 0x44 18 | .space 16, 0x55 19 | .space 16, 0x66 20 | .space 16, 0x77 21 | .space 16, 0x88 22 | .space 16, 0x99 23 | .space 16, 0xAA 24 | .space 16, 0xBB 25 | .space 16, 0xCC 26 | .space 16, 0xDD 27 | .space 16, 0xEE 28 | .space 16, 0xFF 29 | -------------------------------------------------------------------------------- /assets/test_tileset_8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/assets/test_tileset_8x8.png -------------------------------------------------------------------------------- /banned_stdlib_functions: -------------------------------------------------------------------------------- 1 | --wrap __prologue_saves__ 2 | --wrap __epilogue_restores__ 3 | --wrap __addvdi3 4 | --wrap __subvdi3 5 | --wrap __mulvsi3 6 | --wrap __mulvdi3 7 | --wrap __mulsc3 8 | --wrap __divsc3 9 | --wrap __divmoddi4 10 | --wrap __udivmoddi4 11 | --wrap __mulhelperdq 12 | --wrap __mulhelperda 13 | --wrap __mulhelperta 14 | --wrap __mulhelperudq 15 | --wrap __mulhelperuda 16 | --wrap __mulhelperuta 17 | --wrap __divhelpersq 18 | --wrap __divhelperdq 19 | --wrap __divhelpersa 20 | --wrap __divhelperda 21 | --wrap __divhelperta 22 | --wrap __divhelperusq 23 | --wrap __divhelperudq 24 | --wrap __divhelperusa 25 | --wrap __divhelperuda 26 | --wrap __divhelperuta 27 | --wrap __satfractqqdq2 28 | --wrap __satfractqqda 29 | --wrap __satfractqqta 30 | --wrap __satfracthqdq2 31 | --wrap __satfracthqda 32 | --wrap __satfracthqta 33 | --wrap __satfractsqdq2 34 | --wrap __satfractsqta 35 | --wrap __satfracthadq 36 | --wrap __satfracthada2 37 | --wrap __satfracthata2 38 | --wrap __satfractsadq 39 | --wrap __satfractsada2 40 | --wrap __satfractsata2 41 | --wrap __satfractdadq 42 | --wrap __satfractdata2 43 | --wrap __satfracttaqq 44 | --wrap __satfracttahq 45 | --wrap __satfracttasq 46 | --wrap __satfracttadq 47 | --wrap __satfracttaha2 48 | --wrap __satfractqidq 49 | --wrap __satfractqida 50 | --wrap __satfractqita 51 | --wrap __satfracthidq 52 | --wrap __satfracthida 53 | --wrap __satfracthita 54 | --wrap __satfractsidq 55 | --wrap __satfractsida 56 | --wrap __satfractsita 57 | --wrap __satfractdiqq 58 | --wrap __satfractdihq 59 | --wrap __satfractdisq 60 | --wrap __satfractdidq 61 | --wrap __satfractdiha 62 | --wrap __satfractdisa 63 | --wrap __satfractdida 64 | --wrap __satfractdita 65 | --wrap frame_downheap 66 | --wrap frame_heapsort 67 | --wrap classify_object_over_fdes 68 | --wrap linear_search_fdes 69 | --wrap add_fdes 70 | --wrap search_object 71 | --wrap _Unwind_RaiseException_Phase2 72 | --wrap _Unwind_ForcedUnwind_Phase2 73 | --wrap _Unwind_SjLj_RaiseException 74 | --wrap qsort 75 | --wrap realloc 76 | --wrap strtol 77 | --wrap strtoul 78 | --wrap setjmp 79 | --wrap longjmp 80 | --wrap moon_phase 81 | --wrap strftime 82 | --wrap vfprintf 83 | --wrap vfscanf 84 | --wrap vfprintf 85 | --wrap conv_brk 86 | --wrap conv_flt 87 | --wrap vfscanf 88 | --wrap vfscanf 89 | -------------------------------------------------------------------------------- /bootloader/README_bootloader.txt: -------------------------------------------------------------------------------- 1 | Optiboot support 2 | ================ 3 | 4 | This patch adds support for the Amethyst to Optiboot 5 | (https://github.com/Optiboot/optiboot), allowing it to be programmed 6 | over USB. 7 | 8 | Baud rate is fixed at 57600. 9 | 10 | -------------------------------------------------------------------------------- /bootloader/optiboot-amethyst-support.patch: -------------------------------------------------------------------------------- 1 | diff --git a/optiboot/bootloaders/optiboot/Makefile.1284 b/optiboot/bootloaders/optiboot/Makefile.1284 2 | index 78de8e6..113a632 100644 3 | --- a/optiboot/bootloaders/optiboot/Makefile.1284 4 | +++ b/optiboot/bootloaders/optiboot/Makefile.1284 5 | @@ -158,3 +158,27 @@ wildfirev3_isp: LFUSE ?= F7 6 | # 2.7V brownout 7 | wildfirev3_isp: EFUSE ?= FD 8 | wildfirev3_isp: isp 9 | + 10 | + 11 | + 12 | +HELPTEXT += "target amethyst - ATmega1284 (40 pin, 128k) at 14.31818MHz\n" 13 | +amethyst: TARGET = amethyst 14 | +amethyst: MCU_TARGET = atmega1284 15 | +amethyst: AVR_FREQ = 14318182L 16 | +amethyst: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe 17 | +amethyst: CFLAGS += -DLED_START_FLASHES=0 -DLED_DATA_FLASH=0 -DLED_START_ON=0 -DLED=B1 -DBIGBOOT 18 | +amethyst: CFLAGS += -DUART=1 -DBAUD_RATE=57600 $(UART_CMD) 19 | +amethyst: $(PROGRAM)_amethyst.hex 20 | +ifndef PRODUCTION 21 | +amethyst: $(PROGRAM)_amethyst.lst 22 | +endif 23 | + 24 | +amethyst_isp: amethyst 25 | +amethyst_isp: ISPTOOL ?= avrispmkII 26 | +amethyst_isp: ISPPORT ?= usb 27 | +amethyst_isp: TARGET = amethyst 28 | +amethyst_isp: MCU_TARGET = atmega1284 29 | +amethyst_isp: HFUSE ?= D6# preserve EEPROM through chip erase and enable bootloader 30 | +amethyst_isp: LFUSE ?= BF# enable CKOUT 31 | +amethyst_isp: EFUSE ?= FF 32 | +amethyst_isp: isp 33 | diff --git a/optiboot/bootloaders/optiboot/Makefile.isp b/optiboot/bootloaders/optiboot/Makefile.isp 34 | index 745729f..8eb6519 100644 35 | --- a/optiboot/bootloaders/optiboot/Makefile.isp 36 | +++ b/optiboot/bootloaders/optiboot/Makefile.isp 37 | @@ -57,7 +57,7 @@ endif 38 | # 39 | # avrdude commands to erase chip, unlock memory, and program fuses. 40 | # 41 | -ISPFUSES = -e -u -U lock:w:0x3f:m $(EFUSE_CMD) \ 42 | +ISPFUSES = -e -u -U lock:w:0xff:m $(EFUSE_CMD) \ 43 | -U hfuse:w:0x$(HFUSE):m -U lfuse:w:0x$(LFUSE):m 44 | 45 | 46 | @@ -66,7 +66,7 @@ ISPFUSES = -e -u -U lock:w:0x3f:m $(EFUSE_CMD) \ 47 | # space from accidental SPM writes. Note: "2f" allows boot section to be read 48 | # by the application, which is different than the arduino default. 49 | # 50 | -ISPFLASH = -U flash:w:$(PROGRAM)_$(TARGET).hex -U lock:w:0x2f:m 51 | +ISPFLASH = -U flash:w:$(PROGRAM)_$(TARGET).hex -U lock:w:0xcf:m 52 | 53 | # There are certain complicated caused by the fact that the default state 54 | # of a fuse is a "1" rather than a "0", especially with respect to fuse bits 55 | -------------------------------------------------------------------------------- /bootloader/optiboot_amethyst.hex: -------------------------------------------------------------------------------- 1 | :020000021000EC 2 | :10FC000001C010C1112484B7882369F0982F9A701D 3 | :10FC1000923049F081FF02C097EF94BF282E80E018 4 | :10FC2000ECD00C94000082E08093C80088E18093BF 5 | :10FC3000C90086E08093CA008EE18093CC008EE0FC 6 | :10FC4000DCD0219A299A43E0D42ECC24C39455E0E9 7 | :10FC5000952E61E1862EC3D0813479F4C0D0C82FAF 8 | :10FC6000D2D0C23811F480E004C088E0C13809F075 9 | :10FC700083E0AED080E1ACD0EECF823419F484E1E1 10 | :10FC8000CAD0F8CF853411F485E0FACF853581F4F8 11 | :10FC9000A6D0082FA4D0182F87FF07C08BB781608C 12 | :10FCA0008BBF000F111FAFD0E5CF8BB78E7FF8CF82 13 | :10FCB000863579F494D08D3451F491D0CBB78FD070 14 | :10FCC000C170880F8C2B8BBF81E0A5D0CCCF83E097 15 | :10FCD000FCCF843609F04AC082D0C82FD0E0DC2F98 16 | :10FCE000CC277DD0C82B7BD0782E5E01B394E12C3D 17 | :10FCF000FF24F39474D0F70181937F01EA15FB058B 18 | :10FD0000C9F781D0F5E47F1211C0F12CE12C08C0B5 19 | :10FD10006081C8018E0D9F1D9DD08FEFE81AF80AF3 20 | :10FD2000F701F395CE15DF0599F7A4CFF801D7BEFB 21 | :10FD3000E89507B600FCFDCF90E080E0F801E80F01 22 | :10FD4000F91FDC01B3952D913C910901C7BEE895DF 23 | :10FD500011240296C817D90789F7F80197BEE895CC 24 | :10FD600007B600FCFDCF87BEE89584CF843709F540 25 | :10FD700036D0C82FD0E0DC2FCC2731D0C82B2FD0E5 26 | :10FD8000B82E41D07801F5E4BF120CC0C00FD11FCE 27 | :10FD9000C70158D01DD08FEFE81AF80ACE15DF053D 28 | :10FDA000B9F768CFF70187917F0112D02197D1F77A 29 | :10FDB00061CF853739F427D08EE10AD087E908D0A2 30 | :10FDC00086E057CF813509F06ECF88E016D06BCF33 31 | :10FDD0009091C80095FFFCCF8093CE000895199AAA 32 | :10FDE0008091C80087FFFCCF8091C80084FD01C0CE 33 | :10FDF000A8958091CE00199A0895E0E6F0E098E188 34 | :10FE0000908380830895EBDF803219F088E0F5DF7E 35 | :10FE1000FFCF84E1DDCFCF93C82FE1DFC150E9F7F9 36 | :10FE2000CF91F1CFFC010A0167BFE895112407B615 37 | :10FE300000FCFDCF667029F0452B19F481E187BFE6 38 | :10FE4000E8950895F999FECF92BD81BDF89A99275A 39 | :10FE500080B50895262FF999FECF1FBA92BD81BDB6 40 | :10FE600020BD0FB6F894FA9AF99A0FBE019608953C 41 | :10FE700056657273696F6E3D382E30004F5054498D 42 | :10FE8000424F4F545F435553544F4D5645523D30AA 43 | :10FE9000004465766963653D61746D656761313203 44 | :10FEA000383400465F4350553D31343331383138B2 45 | :10FEB000324C00424947424F4F543D310042756930 46 | :10FEC0006C743A4F637420323220323031393A3117 47 | :10FED000393A31303A313400554152543D310042C3 48 | :10FEE0004155445F524154453D3537363030004C22 49 | :10FEF00045443D4231004C45445F53544152545FA8 50 | :10FF00004F4E3D30004C45445F444154415F464CA8 51 | :10FF10004153483D30004C45445F53544152545F77 52 | :0AFF2000464C41534845533D300064 53 | :02FFFE000008F9 54 | :040000031000FC00ED 55 | :00000001FF 56 | -------------------------------------------------------------------------------- /circuit/.gitignore: -------------------------------------------------------------------------------- 1 | *.gbr 2 | *.bak 3 | *-cache.lib 4 | *.drl 5 | *.net 6 | *.kicad_pcb-bak 7 | *.sch-bak 8 | fp-info-cache 9 | -------------------------------------------------------------------------------- /circuit/ChangeLog: -------------------------------------------------------------------------------- 1 | Rev 1.2 - December 17, 2019 2 | =========================== 3 | - Switch back to FT230X, didn't need the extra complexity 4 | - Move AVRISP/SPI1 switch, add more labels 5 | - Line up SPI0/SPI1 connectors with Analog connector 6 | 7 | Rev 1.1 - September 8, 2019 8 | =========================== 9 | - Ortholinear 47-key keyboard layout 10 | - Switch from FT230X to FT231X and connect /RESET to /DTR 11 | (circuit based on SparkFun's BOB-13263) 12 | - Connect /RESET to SPI1 via switch, allowing ISP programming without 13 | removing cover 14 | - Connect AVR PD1 to shift register /CE lines instead of video mixer 15 | - Changed mounting holes to M2.5 and repositioned 16 | - Moved +5V/GND testpoints 17 | - Moved power LED. Now vertically aligned with reset button 18 | - Empty board space near SPI0 and SPI1 ports may allow for internal 19 | expansion boards (SPI EEPROM, etc) 20 | 21 | Rev 1.0 - December 7, 2018 22 | ========================== 23 | Initial board revision 24 | 25 | -------------------------------------------------------------------------------- /circuit/DESIGN_FLAWS.txt: -------------------------------------------------------------------------------- 1 | The Amethyst's design isn't perfect. It has several shortcomings. These 2 | are some aspects of the design that could be improved: 3 | 4 | The luminance levels of the video signal are dependent on the supply voltage. 5 | Fluctuations and noise on the 5V supply can manifest as flickering changes of 6 | brightness in the image. This is exacerbated by the fact that the Amethyst has no onboard voltage regulator; it's entirely dependent on the 5V USB supply 7 | voltage. 8 | 9 | I did try to run the system at 3.3V using the 3V3 output of the FTDI chip. 10 | Technically, at 14.318 MHz, you're running the AVR outside its "safe operating 11 | area." (See Figure 29-1 in the ATmega1284 datasheet.) Most of the time the 12 | system would work fine, but writing to the internal EEPROM would cause the CPU 13 | to lock up or restart. (It is documented in several threads on the avrfreaks 14 | forums that EEPROM tends to fail when an AVR is overclocked.) However, this 15 | could have also been due to excessive current draw from the FT230X/FT231X's 16 | 3.3V regulator, which is only rated to deliver 50 mA. It's possible that 17 | everything might work fine using a dedicated 3.3V LDO. 18 | 19 | No fuse or reverse voltage protection on VUSB. Needs better filtering too, 20 | probably. 21 | 22 | Analog video and audio output stages are garbage. My focus was on minimizing 23 | part count instead of picture quality. I'm sure there's a lot of room for 24 | improvement here but I'm not much of an analog guy. 25 | 26 | No PAL support. I don't think artifact color works with PAL anyway. Your modern 27 | TV or capture device can probably decode NTSC, though. 28 | 29 | Derive the maximum video signal amplitude from the AVR's 2.56V/1.1V analog 30 | voltage reference at the AREF pin. Along with some 1% resistors, this might 31 | eliminate the need for the two trimpots VR1 and VR2. 32 | 33 | The ortholinear keyboard layout and lack of a spacebar take a while to get 34 | used to. This was done to minimize board real estate. Sorry if it's not your 35 | thing. 36 | 37 | A major drawback: the video signal is generated in software. The AVR has 38 | no DMA, which means the CPU needs to be stolen away to bitbang the image out 39 | to the shift registers during the active video portion of the frame. This only 40 | leaves about 25% of each frame for updating your program's logic. If you 41 | wanted to get crazy, you could probably build a dual-processor system. You 42 | could probably even have two AVRs sharing access to an external SRAM chip. You 43 | could use a smaller AVR as a 6845 CRT controller--it would only be responsible 44 | for VRAM address generation, and wouldn't need to be connected to the data 45 | lines. Or you could use another '1284 as a video coprocessor to accelerate 46 | blitting and line drawing operations. 47 | 48 | The biggest drawback: the AVR can't run native code from SRAM. If you don't 49 | want to use the Forth bytecode interpreter, loading a new native application 50 | requires reprogramming the flash memory, which only has a finite number of 51 | write cycles. An Arm would be better suited, but then (a) it wouldn't be 52 | "8-bit," and (b) it wouldn't be constructible on a breadboard. (Rest in peace, 53 | LPC1114FN28.) 54 | 55 | -------------------------------------------------------------------------------- /circuit/avr-colorvideo-board.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/circuit/avr-colorvideo-board.pdf -------------------------------------------------------------------------------- /circuit/avr-colorvideo-schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/circuit/avr-colorvideo-schematic.pdf -------------------------------------------------------------------------------- /circuit/board-changes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/circuit/board-changes.txt -------------------------------------------------------------------------------- /circuit/eeprom-board/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name 6-pin-smd)(type KiCad)(uri /Users/matt/Code/avr-colorvideo/circuit/6-pin-smd.pretty)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /circuit/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name avr-isp-6)(type KiCad)(uri ${KIPRJMOD}/avr-isp-6.pretty)(options "")(descr "")) 3 | (lib (name rca)(type KiCad)(uri ${KIPRJMOD}/rca.pretty)(options "")(descr "")) 4 | (lib (name 6-pin-smd)(type KiCad)(uri ${KIPRJMOD}/6-pin-smd.pretty)(options "")(descr "")) 5 | ) 6 | -------------------------------------------------------------------------------- /circuit/rca.pretty/RCA_Jack_Jameco_KY-006-1.kicad_mod: -------------------------------------------------------------------------------- 1 | (module RCA_Jack_Jameco_KY-006-1 (layer F.Cu) (tedit 5BF474A0) 2 | (fp_text reference REF** (at 0 0.5) (layer F.SilkS) 3 | (effects (font (size 1 1) (thickness 0.15))) 4 | ) 5 | (fp_text value RCA_Jack_Jameco_KY-006-1 (at 0 -0.5) (layer F.Fab) 6 | (effects (font (size 1 1) (thickness 0.15))) 7 | ) 8 | (fp_line (start -2.54 -5.5) (end -2.54 5.5) (layer F.Fab) (width 0.15)) 9 | (pad 2 thru_hole circle (at 0 0) (size 5 5) (drill 2.7) (layers *.Cu *.Mask)) 10 | (pad 1 thru_hole circle (at 5.1 0) (size 3 3) (drill 1.5) (layers *.Cu *.Mask)) 11 | (pad 2 thru_hole circle (at 5.1 5.3) (size 5 5) (drill 2.7) (layers *.Cu *.Mask)) 12 | (pad 2 thru_hole circle (at 5.1 -5.3) (size 5 5) (drill 2.7) (layers *.Cu *.Mask)) 13 | ) 14 | -------------------------------------------------------------------------------- /circuit/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | ) 3 | -------------------------------------------------------------------------------- /doc/Amethyst Specs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/doc/Amethyst Specs.pdf -------------------------------------------------------------------------------- /doc/Ramforth Reference Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/doc/Ramforth Reference Manual.pdf -------------------------------------------------------------------------------- /doc/asmtips.txt: -------------------------------------------------------------------------------- 1 | AVR assembler tips 2 | ================== 3 | 4 | The following instructions do NOT modify the SREG flags: 5 | - Any jump (JMP, RJMP, IJMP, EIJMP) 6 | - Any call (CALL, RCALL, ICALL, EICALL) 7 | - Any return (RET, RETI; RETI only modifies I) 8 | - Any conditional skip (CPSE, SBRC, SBRS, SBIC, SBIS) 9 | - Any branch instruction (BRxx) 10 | - Some register operations: LDI, MOV, MOVW, SER, SWAP 11 | - Any memory load/store (LD/LDS/LDD, LPM, ELPM, ST/STS/STD, SPM, PUSH, POP) 12 | - Any I/O register operation (IN, OUT, SBI, CBI) 13 | - BLD, BREAK, NOP, SLEEP, WDR 14 | 15 | The following instructions take 2 or more cycles: 16 | - SBI, CBI (these seem like they should be single-cycle, but they aren't!) 17 | - ADIW, SBIW 18 | - Any multiply (MUL, MULS, MULSU, FMUL, FMULS, FMULSU) 19 | - Any jump (RJMP, IJMP, EIJMP take 2 cycles; JMP takes 3 cycles) 20 | - Any call (RCALL, ICALL take 3 cycles; EICALL, CALL take 4 cycles) 21 | - Any RAM load/store (LD, LDS, LDD, ST, STS, STD, PUSH, POP) 22 | - Returns take 3 cycles (RET, RETI) 23 | - ROM loads take 3 cycles (LPM, ELPM) 24 | - Any branch if the condition is true (BRxx) 25 | - Any skip if the condition is true (CPSE, SBRC, SBRS, SBIC, SBRS) 26 | (3 cycles if condition is true and next instruction is 2 words) 27 | - SPM (see reference manual) 28 | 29 | The following instructions take 2 words: 30 | - CALL, JMP, LDS, STS 31 | 32 | The following instructions have register restrictions: 33 | - ADIW SBIW r25:r24, X, Y, Z 34 | - ANDI CBR CPI LDI MULS ORI SBCI SBR SER SUBI r16-r31 only 35 | - FMUL FMULS FMULSU MULSU r16-r23 only 36 | - IJMP EIJMP ICALL EICALL LPM Z implied 37 | - LDD STD Y and Z only for second operand 38 | - SPM see reference manual 39 | 40 | The following I/O registers can be accessed with IN, OUT, SBI, CBI: 41 | - PINA, DDRA, PORTA, PINB, DDRB, PORTB, PINC, DDRC, PORTC, PIND, DDRD, PORTD 42 | - TIFR0, TIFR1, TIFR2, TIFR3 43 | - PCIFR, EIFR, EIMSK, GPIOR0, EECR 44 | 45 | In addition to the previous I/O registers, the following I/O registers can be 46 | accessed with IN and OUT: 47 | - EEDR, EEAR 48 | - GTCCR 49 | - TCCR0A, TCCR0B, TCNT0, OCR0A, OCR0B 50 | - GPIOR1, GPIOR2 51 | - SPCR0, SPSR0, SPDR0 52 | - ACSR, OCDR, SMCR, MCUSR, MCUCR, SPMCSR, RAMPZ, SPL, SPH, SREG 53 | -------------------------------------------------------------------------------- /include/app.h: -------------------------------------------------------------------------------- 1 | #ifndef APP_H_ 2 | #define APP_H_ 3 | 4 | /* include the common headers */ 5 | #include "video.h" 6 | #include "audio.h" 7 | #include "keys.h" 8 | #include "colors.h" 9 | #include "serial.h" 10 | #include "spi.h" 11 | #include "fastrand.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | struct app_def { 21 | void (*entrypoint)(); 22 | uint16_t data_load_start; 23 | uint16_t data_load_start_xpage; /* only LSB is significant on atmega1284 but this member has to be 16 bits; if arithmetic is done on the address of an extern var (incl. downcasting) it is no longer a compile time constant */ 24 | uint16_t data_end; 25 | uint16_t bss_end; 26 | char appname[]; 27 | } __attribute__((packed)); 28 | 29 | #define APP_MAIN_NAMED(appid,namestr,...) \ 30 | extern char __data_load_start_app_##appid; \ 31 | extern char __data_load_start_xpage_app_##appid; \ 32 | extern char __data_end_app_##appid; \ 33 | extern char __bss_end_app_##appid; \ 34 | noreturn void APP_MAIN_##appid(void); \ 35 | __attribute__((used,section(".app_index" __VA_ARGS__))) const struct app_def APP_DEF_##appid = { \ 36 | .entrypoint = APP_MAIN_##appid, \ 37 | .data_load_start = (uint16_t)(&__data_load_start_app_##appid), \ 38 | .data_load_start_xpage = (uint16_t)(&__data_load_start_xpage_app_##appid), \ 39 | .data_end = (uint16_t)&__data_end_app_##appid, \ 40 | .bss_end = (uint16_t)&__bss_end_app_##appid, \ 41 | .appname = namestr \ 42 | }; \ 43 | noreturn void APP_MAIN_##appid(void) 44 | 45 | #define APP_MAIN(appid,...) APP_MAIN_NAMED(appid,#appid,__VA_ARGS__) 46 | 47 | 48 | #define APP_ALIAS_NAMED(aliasof,aliasid,namestr,...) \ 49 | noreturn void APP_MAIN_##aliasid(void); \ 50 | __attribute__((used,section(".app_index" __VA_ARGS__))) const struct app_def APP_DEF_##aliasid = { \ 51 | .entrypoint = APP_MAIN_##aliasid, \ 52 | .data_load_start = (uint16_t)(&__data_load_start_app_##aliasof), \ 53 | .data_load_start_xpage = (uint16_t)(&__data_load_start_xpage_app_##aliasof), \ 54 | .data_end = (uint16_t)&__data_end_app_##aliasof, \ 55 | .bss_end = (uint16_t)&__bss_end_app_##aliasof, \ 56 | .appname = namestr \ 57 | }; \ 58 | noreturn void APP_MAIN_##aliasid(void) 59 | 60 | #define APP_ALIAS(aliasof,aliasid,...) APP_ALIAS_NAMED(aliasof,aliasid,#aliasid,__VA_ARGS__) 61 | 62 | /** 63 | * Passing this option as an additional trailing argument to one of the APP_() 64 | * macros excludes this app from the index. It can still be invoked by passing a 65 | * a pointer to the struct app_def to app_launch(). 66 | */ 67 | #define APP_HIDDEN ".hidden" 68 | 69 | /** 70 | * Returns a pointer to the next app in the app index. 71 | * If the argument is NULL, returns a pointer to the first app in the index. 72 | * Returns NULL if the argument is the last app in the index. 73 | */ 74 | const struct app_def *app_index_next(const struct app_def *app); 75 | 76 | /** 77 | * Returns a pointer to the n'th app (zero-indexed) in the index. 78 | * Returns NULL if the number is invalid. 79 | */ 80 | const struct app_def *app_index_nth(uint8_t n); 81 | 82 | /** 83 | * Returns a pointer to the app's name (in progmem) as a null-terminated string. 84 | */ 85 | PGM_P app_name(const struct app_def *app); 86 | 87 | /** 88 | * Returns the load address (in ROM) of the data section for this app. 89 | */ 90 | uint32_t app_data_load_start(const struct app_def *app); 91 | 92 | /** 93 | * Returns the last address (in RAM) of the data section for this app. 94 | * This address is also the start of the app's bss section. 95 | */ 96 | uint16_t app_data_end(const struct app_def *app); 97 | 98 | /** 99 | * Returns the last address (in RAM) of the bss section for this app. 100 | */ 101 | uint16_t app_bss_end(const struct app_def *app); 102 | 103 | /** 104 | * Initializes and launches the given app. 105 | */ 106 | noreturn void app_launch(const struct app_def *app); 107 | 108 | /** 109 | * Quits the current app and soft-resets the system. 110 | */ 111 | noreturn void app_quit(void); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /include/audio.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_H_ 2 | #define AUDIO_H_ 3 | 4 | #include 5 | #include "note_numbers.h" 6 | 7 | void audio_init(void); 8 | void audio_off(void); 9 | 10 | /* Emit a pulse wave with the given duty cycle. (128 = 50% square wave) */ 11 | /* Higher period values produce lower-pitched sounds. */ 12 | enum { 13 | PULSE_WAVE_NARROW = 32, /* 12.5% */ 14 | PULSE_WAVE_WIDE = 64, /* 25% duty cycle */ 15 | SQUARE_WAVE = 128, 16 | }; 17 | void tone(uint16_t period, uint8_t duration, uint8_t duty_cycle); 18 | 19 | /* Valid note numbers are 0-127 inclusive. */ 20 | void note(enum note_number note_num, uint8_t duration, uint8_t duty_cycle); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/colors.h: -------------------------------------------------------------------------------- 1 | #ifndef COLORS_H_ 2 | #define COLORS_H_ 3 | 4 | #include 5 | 6 | #define NUM_TEXT_COLORS 32 7 | extern const uint8_t bitspreadtable[256*NUM_TEXT_COLORS]; 8 | extern const uint8_t lowernibbledoubletable[256]; 9 | extern const uint8_t uppernibbledoubletable[256]; 10 | 11 | /** 12 | * For 40x25 color text modes, the color byte for a character cell is actually 13 | * the upper 8 bits of the bitspread table to use when expanding the 8-pixel-wide 14 | * character pattern to 16 pixels during display. Different bitspread tables 15 | * produce different colors. 16 | */ 17 | 18 | //!!! TODO this is suboptimal and gcc can't always optimize this to a constant expression. 19 | //!!! If I can put the bitspread table at a fixed location in ROM then this would be better, 20 | //!!! but that requires messing with linker stuff 21 | #define TEXT_COLOR(c) ((uint8_t)((((uint16_t)(&bitspreadtable[256*c]))>>8)&0xFF)) 22 | #define TEXT_COLOR_BASE TEXT_COLOR(0) 23 | #define TEXT_COLOR_BLACK TEXT_COLOR(0) 24 | #define TEXT_COLOR_DK_GREEN TEXT_COLOR(1) 25 | #define TEXT_COLOR_DK_BLUE TEXT_COLOR(2) 26 | #define TEXT_COLOR_BLUE TEXT_COLOR(3) 27 | #define TEXT_COLOR_RED TEXT_COLOR(4) 28 | #define TEXT_COLOR_GRAY TEXT_COLOR(5) 29 | #define TEXT_COLOR_PURPLE TEXT_COLOR(6) 30 | #define TEXT_COLOR_LT_BLUE TEXT_COLOR(7) 31 | #define TEXT_COLOR_BROWN TEXT_COLOR(8) 32 | #define TEXT_COLOR_GREEN TEXT_COLOR(9) 33 | #define TEXT_COLOR_GREY TEXT_COLOR(10) /* same as 5 but different artifacting */ 34 | #define TEXT_COLOR_AQUA TEXT_COLOR(11) 35 | #define TEXT_COLOR_ORANGE TEXT_COLOR(12) 36 | #define TEXT_COLOR_YELLOW TEXT_COLOR(13) 37 | #define TEXT_COLOR_PINK TEXT_COLOR(14) 38 | #define TEXT_COLOR_WHITE TEXT_COLOR(15) 39 | 40 | #define TEXT_COLOR_BLUE_ON_DK_GREEN TEXT_COLOR(16) 41 | #define TEXT_COLOR_AQUA_ON_DK_GREEN TEXT_COLOR(17) 42 | #define TEXT_COLOR_YELLOW_ON_DK_GREEN TEXT_COLOR(18) 43 | #define TEXT_COLOR_WHITE_ON_DK_GREEN TEXT_COLOR(19) 44 | #define TEXT_COLOR_BLUE_ON_DK_BLUE TEXT_COLOR(20) 45 | #define TEXT_COLOR_AQUA_ON_DK_BLUE TEXT_COLOR(21) 46 | #define TEXT_COLOR_PINK_ON_DK_BLUE TEXT_COLOR(22) 47 | #define TEXT_COLOR_WHITE_ON_DK_BLUE TEXT_COLOR(23) 48 | #define TEXT_COLOR_BLUE_ON_RED TEXT_COLOR(24) 49 | #define TEXT_COLOR_YELLOW_ON_RED TEXT_COLOR(25) 50 | #define TEXT_COLOR_PINK_ON_RED TEXT_COLOR(26) 51 | #define TEXT_COLOR_WHITE_ON_RED TEXT_COLOR(27) 52 | #define TEXT_COLOR_AQUA_ON_BROWN TEXT_COLOR(28) 53 | #define TEXT_COLOR_YELLOW_ON_BROWN TEXT_COLOR(29) 54 | #define TEXT_COLOR_PINK_ON_BROWN TEXT_COLOR(30) 55 | #define TEXT_COLOR_WHITE_ON_BROWN TEXT_COLOR(31) 56 | #define TEXT_COLOR_WHITE_ON_BLUE TEXT_COLOR(32) 57 | #define TEXT_COLOR_WHITE_ON_GRAY TEXT_COLOR(33) 58 | #define TEXT_COLOR_WHITE_ON_PURPLE TEXT_COLOR(34) 59 | #define TEXT_COLOR_WHITE_ON_GREEN TEXT_COLOR(35) 60 | #define TEXT_COLOR_WHITE_ON_GREY TEXT_COLOR(36) /* same as 33 but different artifacting */ 61 | #define TEXT_COLOR_WHITE_ON_ORANGE TEXT_COLOR(37) 62 | 63 | 64 | #define HORIZ_RES_HALF ((uint8_t)((((uint16_t)(uppernibbledoubletable))>>8)&0xFF)) 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/console_macros.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Convenience macros when only a single console is used. 3 | */ 4 | 5 | #ifndef CONSOLE_MACROS_H_ 6 | #define CONSOLE_MACROS_H_ 7 | 8 | #include "console.h" 9 | 10 | #ifndef DEFAULT_CONSOLE 11 | #define DEFAULT_CONSOLE stdcon 12 | #endif 13 | 14 | #define clrhome(...) console_clrhome(&DEFAULT_CONSOLE, ##__VA_ARGS__) 15 | #define clrscroll(...) console_clrscroll(&DEFAULT_CONSOLE, ##__VA_ARGS__) 16 | #define clrline(...) console_clrline(&DEFAULT_CONSOLE, ##__VA_ARGS__) 17 | #define fillrange(...) console_fillrange(&DEFAULT_CONSOLE, ##__VA_ARGS__) 18 | #define xfrmrange(...) console_xfrmrange(&DEFAULT_CONSOLE, ##__VA_ARGS__) 19 | #define gohome(...) console_gohome(&DEFAULT_CONSOLE, ##__VA_ARGS__) 20 | #define gotoxy(...) console_gotoxy(&DEFAULT_CONSOLE, ##__VA_ARGS__) 21 | #define gotox(...) console_gotox(&DEFAULT_CONSOLE, ##__VA_ARGS__) 22 | #define gotoy(...) console_gotoy(&DEFAULT_CONSOLE, ##__VA_ARGS__) 23 | #define gotoscrollxy(...) console_gotoscrollxy(&DEFAULT_CONSOLE, ##__VA_ARGS__) 24 | #define gotoscrollx(...) console_gotoscrollx(&DEFAULT_CONSOLE, ##__VA_ARGS__) 25 | #define gotoscrolly(...) console_gotoscrolly(&DEFAULT_CONSOLE, ##__VA_ARGS__) 26 | #define movexy(...) console_movexy(&DEFAULT_CONSOLE, ##__VA_ARGS__) 27 | #define movex(...) console_movex(&DEFAULT_CONSOLE, ##__VA_ARGS__) 28 | #define movey(...) console_movey(&DEFAULT_CONSOLE, ##__VA_ARGS__) 29 | #define movesol(...) console_movesol(&DEFAULT_CONSOLE, ##__VA_ARGS__) 30 | #define erase(...) console_erase(&DEFAULT_CONSOLE, ##__VA_ARGS__) 31 | #define eraseline(...) console_eraseline(&DEFAULT_CONSOLE, ##__VA_ARGS__) 32 | #define setymargins(...) console_setymargins(&DEFAULT_CONSOLE, ##__VA_ARGS__) 33 | #define setxmargins(...) console_setxmargins(&DEFAULT_CONSOLE, ##__VA_ARGS__) 34 | #define resetmargins(...) console_resetmargins(&DEFAULT_CONSOLE, ##__VA_ARGS__) 35 | #define scrollup(...) console_scrollup(&DEFAULT_CONSOLE, ##__VA_ARGS__) 36 | #define scrolldown(...) console_scrolldown(&DEFAULT_CONSOLE, ##__VA_ARGS__) 37 | #define lback(...) console_lback(&DEFAULT_CONSOLE, ##__VA_ARGS__) 38 | #define lfwd(...) console_lfwd(&DEFAULT_CONSOLE, ##__VA_ARGS__) 39 | #define linefeed(...) console_linefeed(&DEFAULT_CONSOLE, ##__VA_ARGS__) 40 | #define cback(...) console_cback(&DEFAULT_CONSOLE, ##__VA_ARGS__) 41 | #define cfwd(...) console_cfwd(&DEFAULT_CONSOLE, ##__VA_ARGS__) 42 | #define backspace(...) console_backspace(&DEFAULT_CONSOLE, ##__VA_ARGS__) 43 | #define setch(...) console_setch(&DEFAULT_CONSOLE, ##__VA_ARGS__) 44 | #define addch(...) console_addch(&DEFAULT_CONSOLE, ##__VA_ARGS__) 45 | #define addch_raw(...) console_addch_raw(&DEFAULT_CONSOLE, ##__VA_ARGS__) 46 | #define addstr(...) console_addstr(&DEFAULT_CONSOLE, ##__VA_ARGS__) 47 | #define addstr_P(...) console_addstr_P(&DEFAULT_CONSOLE, ##__VA_ARGS__) 48 | #define xyaddch(...) console_xyaddch(&DEFAULT_CONSOLE, ##__VA_ARGS__) 49 | #define xyaddstr(...) console_xyaddstr(&DEFAULT_CONSOLE, ##__VA_ARGS__) 50 | #define xyaddstr_P(...) console_xyaddstr_P(&DEFAULT_CONSOLE, ##__VA_ARGS__) 51 | #define getch(...) console_getch(&DEFAULT_CONSOLE, ##__VA_ARGS__) 52 | #define getx(...) console_cx(&DEFAULT_CONSOLE, ##__VA_ARGS__) 53 | #define gety(...) console_cy(&DEFAULT_CONSOLE, ##__VA_ARGS__) 54 | #define getcols(...) console_cols(&DEFAULT_CONSOLE, ##__VA_ARGS__) 55 | #define getrows(...) console_rows(&DEFAULT_CONSOLE, ##__VA_ARGS__) 56 | #define maxcol(...) console_maxcol(&DEFAULT_CONSOLE, ##__VA_ARGS__) 57 | #define maxrow(...) console_maxrow(&DEFAULT_CONSOLE, ##__VA_ARGS__) 58 | #define scrollwidth(...) console_scrollwidth(&DEFAULT_CONSOLE, ##__VA_ARGS__) 59 | #define scrollheight(...) console_scrollheight(&DEFAULT_CONSOLE, ##__VA_ARGS__) 60 | #define topmargin(...) console_mtop(&DEFAULT_CONSOLE, ##__VA_ARGS__) 61 | #define botmargin(...) console_mbottom(&DEFAULT_CONSOLE, ##__VA_ARGS__) 62 | #define leftmargin(...) console_mleft(&DEFAULT_CONSOLE, ##__VA_ARGS__) 63 | #define rightmargin(...) console_mright(&DEFAULT_CONSOLE, ##__VA_ARGS__) 64 | #define scrollok(...) console_scrollok(&DEFAULT_CONSOLE, ##__VA_ARGS__) 65 | #define setscrollok(...) console_setscrollok(&DEFAULT_CONSOLE, ##__VA_ARGS__) 66 | #define showcursor(...) console_showcursor(&DEFAULT_CONSOLE, ##__VA_ARGS__) 67 | #define setshowcursor(...) console_setshowcursor(&DEFAULT_CONSOLE, ##__VA_ARGS__) 68 | #define setrvs(...) console_setrvs(&DEFAULT_CONSOLE, ##__VA_ARGS__) 69 | #endif 70 | -------------------------------------------------------------------------------- /include/defs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * defs.h 3 | * Global defines. 4 | */ 5 | #ifndef DEFS_H_ 6 | #define DEFS_H_ 7 | 8 | /* yes, this works with -std=gnu11 */ 9 | #define auto __auto_type 10 | 11 | /* Just an abbreviation to keep asm code narrow. */ 12 | #define IO_ _SFR_IO_ADDR 13 | 14 | #ifndef __ASSEMBLER__ 15 | 16 | /* thank u chrome */ 17 | #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 18 | 19 | #include 20 | #define PAGE_ALIGNED __attribute__((aligned(256))) 21 | typedef PAGE_ALIGNED uint8_t *page_aligned_ptr; 22 | typedef const PAGE_ALIGNED uint8_t *const_page_aligned_ptr; 23 | 24 | /* those golden oldies */ 25 | #define XPASTE_(x,y) x##y 26 | #define PASTE_(x,y) XPASTE_(x,y) 27 | #define STR_(x) #x 28 | #define XSTR_(x) STR_(x) 29 | 30 | /* assembler provides these as relocation modifiers, */ 31 | /* but they're handy from C as well */ 32 | #define ptr_to_int(n) ((uint16_t)(n)) 33 | #define lo8(n) (ptr_to_int(n)&0xFF) 34 | #define hi8(n) lo8(ptr_to_int(n)>>8) 35 | 36 | /* as above, but for 24-bit __memx pointers */ 37 | #define xptr_to_int(n) ((__uint24)(n)) 38 | #define xlo8(n) (xptr_to_int(n)&0xFF) 39 | #define xhi8(n) lo8(xptr_to_int(n)>>8) 40 | #define xhh8(n) lo8(xptr_to_int(n)>>16) 41 | 42 | /* a matter of style */ 43 | #define set_bits(n,b) ((n) |= (b)) 44 | #define clear_bits(n,b) ((n) &= ~(b)) 45 | #define set_bit(n,b) set_bits(n,_BV(b)) 46 | #define clear_bit(n,b) clear_bits(n,_BV(b)) 47 | 48 | #endif /* !__ASSEMBLER__ */ 49 | 50 | 51 | /* Hardware versions. */ 52 | 53 | /* First prototype; 43 keys, no number keys */ 54 | #define AMETHYST_HW_1_0 0x0100 55 | /* Second prototype: 47 keys, number keys added */ 56 | #define AMETHYST_HW_1_1 0x0101 57 | 58 | #define AMETHYST_HW_LATEST AMETHYST_HW_1_1 59 | 60 | #ifndef HW_VERSION 61 | #define HW_VERSION AMETHYST_HW_LATEST 62 | #endif 63 | 64 | #endif /* DEFS_H_ */ 65 | -------------------------------------------------------------------------------- /include/fastrand.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple but fast 16-bit pseudorandom number generator. 3 | * The default avr-libc implementation of rand() uses division/modulo, which 4 | * isn't quick, and requires bringing in parts of libgcc which otherwise might 5 | * not be needed. 6 | */ 7 | #ifndef FASTRAND_H_ 8 | #define FASTRAND_H_ 9 | 10 | int fastrand(void); 11 | void sfastrand(unsigned s); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/isr_reserved_registers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This header must be included by *ALL* translation units to tell the compiler 3 | * to leave these registers alone. It is recommended that this is done on the 4 | * command line (using an `-include` argument) instead of explicit include 5 | * statements in each file (which could be easily forgotten, leading to 6 | * potential register clobbering and very strange behavior or crashes) 7 | */ 8 | #ifndef ISR_RESERVED_REGISTERS_H_ 9 | #define ISR_RESERVED_REGISTERS_H_ 10 | 11 | #ifdef __ASSEMBLER__ 12 | 13 | /** 14 | * Reserve as many registers as we can for use in interrupt routines. 15 | * avr-libc documentation indicates that r2 through r7 are safe to be used for 16 | * this purpose. Unfortunately, some libc and libm functions use these and need 17 | * to be avoided. (see the file `banned_stdlib_functions`) 18 | */ 19 | 20 | /* Scratch register used in video interrupt routines. */ 21 | #define isr_tmp r2 22 | /* Line count. Decrements by 1 each line. 0 indicates the last line. */ 23 | #define linenum r3 24 | /* Pointer to video data. Persisted across lines. */ 25 | #define vptr_lo r4 26 | #define vptr_hi r5 27 | /* Used for saving ZL/ZH registers in video interrupt routines. */ 28 | /* movw is 4x as fast as pushing/popping a register pair. */ 29 | #define zsav_lo r6 30 | #define zsav_hi r7 31 | /* Address of the handler to invoke for each new line and frame. */ 32 | /* Using I/O registers instead of SRAM saves 2 cycles per line. */ 33 | #define linehandler_lo _SFR_IO_ADDR(GPIOR1) 34 | #define linehandler_hi _SFR_IO_ADDR(GPIOR2) 35 | /* Set bit 1 of DDRB after the current frame has finished drawing. */ 36 | /* The value of this bit is ignored when the CKOUT fuse is programmed, */ 37 | /* so we can use it as one extra bit of storage in the I/O address space. */ 38 | #define new_frame _SFR_IO_ADDR(DDRB) 39 | /* Bit 1 of PORTB enables the colorburst. */ 40 | /* The value of this bit is ignored when the CKOUT fuse is programmed, */ 41 | /* so we can use it as one extra bit of storage in the I/O address space. */ 42 | #define color_video _SFR_IO_ADDR(PORTB) 43 | /* Bit 2 of DDRD indicates if asynchronous USART RX is enabled. */ 44 | /* The value of this bit is ignored when the USART1 receiver is enabled, */ 45 | /* so we can use it as one extra bit of storage in the I/O address space. */ 46 | #define usart_async _SFR_IO_ADDR(DDRD) 47 | /* Bit 4 of PORTB indicates if the "simple tone generator" is enabled. */ 48 | /* The simple tone generator can output a pulse wave with a given frequency, */ 49 | /* duty cycle, and duration. If enabled, the simple tone generator updates */ 50 | /* once per frame. */ 51 | #define tone_enabled _SFR_IO_ADDR(PORTB) 52 | 53 | #else /* !ASSEMBLER */ 54 | 55 | register unsigned char isr_tmp asm("r2"); 56 | register unsigned char linenum asm("r3"); 57 | register unsigned char vptr_lo asm("r4"); 58 | register unsigned char vptr_hi asm("r5"); 59 | register unsigned char zsav_lo asm("r6"); 60 | register unsigned char zsav_hi asm("r7"); 61 | #define new_frame DDRB 62 | #define color_video PORTB 63 | #define usart_async DDRD 64 | #define tone_enabled PORTB 65 | #define linehandler_lo GPIOR1 66 | #define linehandler_hi GPIOR2 67 | #endif 68 | 69 | /* Reuse the pin-change interrupt registers, since it doesn't make sense to */ 70 | /* use these ports for interrupts */ 71 | #define tone_duration PCMSK0 72 | 73 | #define vptr vptr_lo 74 | #define zsav zsav_lo 75 | 76 | #define new_frame_bit 1 77 | #define color_video_bit 1 78 | #define usart_async_bit 2 79 | #define tone_enabled_bit 4 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/keys.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYS_H_ 2 | #define KEYS_H_ 3 | 4 | #include 5 | #include 6 | #include "keycodes.h" 7 | 8 | /* Keyboard matrix state. Only the lower 6 bits of each byte are significant. */ 9 | /* 0 bits indicate pressed keys. */ 10 | /* Updated during the first 10 scanlines of active video. */ 11 | extern uint8_t keymatrixrows[KEYMATRIX_ROWS]; 12 | #define key_is_pressed(k) (!(keymatrixrows[k##_ROW] & (1<<(k##_COL)))) 13 | 14 | 15 | struct keyreader { 16 | uint8_t lastkeys[KEYMATRIX_ROWS]; 17 | uint8_t repeat_count; 18 | }; 19 | 20 | /* col and row are 1-indexed. if 0, no key is down */ 21 | typedef union { 22 | struct { 23 | unsigned col:3; 24 | unsigned row:3; 25 | unsigned :1; 26 | unsigned isdown:1; 27 | }; 28 | struct { 29 | unsigned index:6; 30 | unsigned :2; 31 | }; 32 | uint8_t value; 33 | } __attribute__((packed)) key_code; 34 | 35 | bool key_down_pending(struct keyreader *kr); 36 | 37 | key_code get_key_down(struct keyreader *kr); 38 | 39 | /** 40 | * Returns the ASCII value of the key most recently pressed, taking the state 41 | * of the Shift and Symbol modifier keys into account. 42 | * If Ctrl is held down, the most significant bit of the return value is set. 43 | * This allows you to, for example, distinguish between Backspace and Ctrl-H, 44 | * using the CTRL() macro in keycodes.h. 45 | * If no non-modifier keys are pressed, returns 0. 46 | */ 47 | char get_key_ascii(struct keyreader *kr); 48 | #define CTRL(n) ((n) | 0x80) 49 | 50 | key_code wait_for_key(struct keyreader *kr); 51 | char wait_for_key_ascii(struct keyreader *kr); 52 | 53 | char key_code_to_ascii(key_code kc); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/minio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Much simpler (less-full-featured, non-POSIX-compliant) replacement for 3 | * avr-libc's stdio, which can be quite heavyweight with respect to RAM and 4 | * stack usage. 5 | */ 6 | #ifndef MINIO_H_ 7 | #define MINIO_H_ 8 | 9 | #include 10 | 11 | /* put-character function */ 12 | typedef void (*mio_putc_fn)(char c); 13 | /* get-character function. */ 14 | /* if chk == 0, return a flag indicating if a character is ready. */ 15 | /* otherwise, block until a character is ready. */ 16 | typedef int (*mio_getc_fn)(char chk); 17 | 18 | extern mio_putc_fn mio_putc; 19 | extern mio_getc_fn mio_getc; 20 | 21 | 22 | #define mio_hasc() mio_getc(1) 23 | #define mio_waitc() mio_getc(0) 24 | 25 | void mio_puts(const char *s); 26 | void mio_puts_P(const char * PROGMEM s); 27 | void mio_putns(const char *s, uint16_t nbytes); 28 | void mio_putns_P(const char * PROGMEM s, uint16_t nbytes); 29 | void mio_putsnl(const char *s); 30 | void mio_putsnl_P(const char * PROGMEM s); 31 | void mio_nl(void); 32 | void mio_bl(void); 33 | void mio_x8(uint8_t n); 34 | void mio_u8(uint8_t n); 35 | void mio_d8(int8_t n); 36 | void mio_x16(uint16_t n); 37 | void mio_u16(uint16_t n); 38 | void mio_d16(int16_t n); 39 | 40 | #define Pc mio_putc 41 | #define Ps mio_puts 42 | #define Psl(litstr) mio_puts_P(PSTR(litstr)) 43 | #define Psp mio_puts_P 44 | #define PS mio_putsnl 45 | #define PSl(litstr) mio_putsnl_P(PSTR(litstr)) 46 | #define PSp mio_putsnl_P 47 | #define Pnl mio_nl 48 | #define Pbl mio_bl 49 | #define Px8 mio_x8 50 | #define Pu8 mio_u8 51 | #define Pd8 mio_d8 52 | #define Px16 mio_x16 53 | #define Pu16 mio_u16 54 | #define Pd16 mio_d16 55 | #define Pp(p) mio_x16((uint16_t)(p)) 56 | #endif 57 | -------------------------------------------------------------------------------- /include/note_freqs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps MIDI note numbers to timer parameters. 3 | */ 4 | #ifndef NOTE_FREQS_H_ 5 | #define NOTE_FREQS_H_ 6 | 7 | #include 8 | #include 9 | 10 | typedef union { 11 | struct { 12 | uint8_t tccrb; 13 | uint8_t ocra; 14 | }; 15 | uint16_t n; 16 | } note_def; 17 | 18 | extern const note_def note_freqs[128] PROGMEM; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/note_numbers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIDI note numbers to be used with note(). 3 | */ 4 | #ifndef NOTE_NUMBERS_H_ 5 | #define NOTE_NUMBERS_H_ 6 | 7 | enum note_number { 8 | /* MIDI notes 0-20 are outside the frequency range of the tone generator */ 9 | NOTE_Am1 = 21, 10 | NOTE_Asm1 = 22, 11 | NOTE_Bm1 = 23, 12 | NOTE_C0 = 24, 13 | NOTE_Cs0 = 25, 14 | NOTE_D0 = 26, 15 | NOTE_Ds0 = 27, 16 | NOTE_E0 = 28, 17 | NOTE_F0 = 29, 18 | NOTE_Fs0 = 30, 19 | NOTE_G0 = 31, 20 | NOTE_Gs0 = 32, 21 | NOTE_A0 = 33, 22 | NOTE_As0 = 34, 23 | NOTE_B0 = 35, 24 | NOTE_C1 = 36, 25 | NOTE_Cs1 = 37, 26 | NOTE_D1 = 38, 27 | NOTE_Ds1 = 39, 28 | NOTE_E1 = 40, 29 | NOTE_F1 = 41, 30 | NOTE_Fs1 = 42, 31 | NOTE_G1 = 43, 32 | NOTE_Gs1 = 44, 33 | NOTE_A1 = 45, 34 | NOTE_As1 = 46, 35 | NOTE_B1 = 47, 36 | NOTE_C2 = 48, 37 | NOTE_Cs2 = 49, 38 | NOTE_D2 = 50, 39 | NOTE_Ds2 = 51, 40 | NOTE_E2 = 52, 41 | NOTE_F2 = 53, 42 | NOTE_Fs2 = 54, 43 | NOTE_G2 = 55, 44 | NOTE_Gs2 = 56, 45 | NOTE_A2 = 57, 46 | NOTE_As2 = 58, 47 | NOTE_B2 = 59, 48 | NOTE_C3 = 60, 49 | NOTE_Cs3 = 61, 50 | NOTE_D3 = 62, 51 | NOTE_Ds3 = 63, 52 | NOTE_E3 = 64, 53 | NOTE_F3 = 65, 54 | NOTE_Fs3 = 66, 55 | NOTE_G3 = 67, 56 | NOTE_Gs3 = 68, 57 | NOTE_A3 = 69, 58 | NOTE_As3 = 70, 59 | NOTE_B3 = 71, 60 | NOTE_C4 = 72, 61 | NOTE_Cs4 = 73, 62 | NOTE_D4 = 74, 63 | NOTE_Ds4 = 75, 64 | NOTE_E4 = 76, 65 | NOTE_F4 = 77, 66 | NOTE_Fs4 = 78, 67 | NOTE_G4 = 79, 68 | NOTE_Gs4 = 80, 69 | NOTE_A4 = 81, 70 | NOTE_As4 = 82, 71 | NOTE_B4 = 83, 72 | NOTE_C5 = 84, 73 | NOTE_Cs5 = 85, 74 | NOTE_D5 = 86, 75 | NOTE_Ds5 = 87, 76 | NOTE_E5 = 88, 77 | NOTE_F5 = 89, 78 | NOTE_Fs5 = 90, 79 | NOTE_G5 = 91, 80 | NOTE_Gs5 = 92, 81 | NOTE_A5 = 93, 82 | NOTE_As5 = 94, 83 | NOTE_B5 = 95, 84 | NOTE_C6 = 96, 85 | NOTE_Cs6 = 97, 86 | NOTE_D6 = 98, 87 | NOTE_Ds6 = 99, 88 | NOTE_E6 = 100, 89 | NOTE_F6 = 101, 90 | NOTE_Fs6 = 102, 91 | NOTE_G6 = 103, 92 | NOTE_Gs6 = 104, 93 | NOTE_A6 = 105, 94 | NOTE_As6 = 106, 95 | NOTE_B6 = 107, 96 | NOTE_C7 = 108, 97 | NOTE_Cs7 = 109, 98 | NOTE_D7 = 110, 99 | NOTE_Ds7 = 111, 100 | NOTE_E7 = 112, 101 | NOTE_F7 = 113, 102 | NOTE_Fs7 = 114, 103 | NOTE_G7 = 115, 104 | NOTE_Gs7 = 116, 105 | NOTE_A7 = 117, 106 | NOTE_As7 = 118, 107 | NOTE_B7 = 119, 108 | NOTE_C8 = 120, 109 | NOTE_Cs8 = 121, 110 | NOTE_D8 = 122, 111 | NOTE_Ds8 = 123, 112 | NOTE_E8 = 124, 113 | NOTE_F8 = 125, 114 | NOTE_Fs8 = 126, 115 | NOTE_G8 = 127, 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /include/numeric_io.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Simpler routines for numeric input/output for times when printf()/scanf() 3 | * are overkill. 4 | */ 5 | #ifndef NUMERIC_IO_H_ 6 | #define NUMERIC_IO_H_ 7 | 8 | #include 9 | 10 | /** 11 | * Write an 8-bit value as a 2-character hexadecimal number (with leading zero) 12 | * to the given stream. Nibble values of 10-15 are rendered with the uppercase 13 | * characters A-F. 14 | */ 15 | void fputhex8(uint8_t n, FILE *stream); 16 | 17 | /** 18 | * Write an 8-bit value as a 2-character hexadecimal number (with leading zero) 19 | * to stdout. Nibble values of 10-15 are rendered with the uppercase characters 20 | * A-F. 21 | */ 22 | void puthex8(uint8_t n); 23 | 24 | /** 25 | * Write a 16-bit value as a 4-character hexadecimal number (with leading zeros) 26 | * to the given stream. Nibble values of 10-15 are rendered with the uppercase 27 | * characters A-F. 28 | */ 29 | void fputhex16(uint16_t n, FILE *stream); 30 | 31 | /** 32 | * Write a 16-bit value as a 4-character hexadecimal number (with leading zeros) 33 | * to stdout. Nibble values of 10-15 are rendered with the uppercase characters 34 | * A-F. 35 | */ 36 | void puthex16(uint16_t n); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/readline.h: -------------------------------------------------------------------------------- 1 | #ifndef READLINE_H_ 2 | #define READLINE_H_ 3 | 4 | #include "minio.h" 5 | #include 6 | 7 | int readline(char *inbuf, int bufsize); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H_ 2 | #define SERIAL_H_ 3 | 4 | #include "defs.h" 5 | #include "minio.h" 6 | #include 7 | #include 8 | 9 | enum serial_baud_rate { 10 | SERIAL_BAUD_57600, /* default */ 11 | SERIAL_BAUD_300, 12 | SERIAL_BAUD_1200, 13 | SERIAL_BAUD_2400, 14 | SERIAL_BAUD_9600, 15 | SERIAL_BAUD_19200, 16 | SERIAL_BAUD_38400, 17 | }; 18 | #define SERIAL_BAUD_NUM_VALUES 7 19 | 20 | enum serial_parity { 21 | SERIAL_PARITY_NONE, 22 | SERIAL_PARITY_EVEN, 23 | SERIAL_PARITY_ODD, 24 | }; 25 | #define SERIAL_PARITY_NUM_VALUES 3 26 | 27 | enum serial_data_bits { 28 | SERIAL_DATA_BITS_8, /* default */ 29 | SERIAL_DATA_BITS_7, 30 | }; 31 | #define SERIAL_DATA_BITS_NUM_VALUES 2 32 | 33 | enum serial_stop_bits { 34 | SERIAL_STOP_BITS_1, 35 | SERIAL_STOP_BITS_2, 36 | }; 37 | #define SERIAL_STOP_BITS_NUM_VALUES 2 38 | 39 | struct serial_port { 40 | enum serial_baud_rate baudrate:3; 41 | enum serial_parity parity:2; 42 | enum serial_data_bits databits:1; 43 | enum serial_stop_bits stopbits:1; 44 | }; 45 | 46 | struct serial_port serial_port_with_options( 47 | enum serial_baud_rate baudrate, 48 | enum serial_parity parity, 49 | enum serial_data_bits databits, 50 | enum serial_stop_bits stopbits); 51 | 52 | void serial_init(struct serial_port port); 53 | 54 | /** 55 | * Enables asynchronous serial mode. 56 | * This causes bytes received over the USART to be written to the given buffer 57 | * without disturbing the video image. Characters can then be dequeued and 58 | * processed during vblank. 59 | * rxbuf must be a 256-byte-aligned pointer to a 256-byte array to use as the 60 | * receive buffer. 61 | */ 62 | void serial_async(page_aligned_ptr rxbuf); 63 | 64 | /** 65 | * Enables asynchronous serial mode. 66 | * When a byte is received over the USART, the given callback is invoked. 67 | * The callback does NOT use the standard calling convention and therefore 68 | * must be declared with __attribute__((naked)). 69 | * - The function is free to clobber ZL, ZH and GPIOR0 70 | * - The received byte is in r2 and the function is free to clobber it 71 | * - Any other registers (including SREG) must be saved 72 | */ 73 | void serial_async_set_callback(void (*cb)(void)); 74 | 75 | /* Not usable in async-callback mode. */ 76 | bool serial_bytes_available(void); 77 | 78 | /* Not usable in async-callback mode. */ 79 | uint8_t serial_get_byte(void); 80 | 81 | void serial_send_char(char c); 82 | 83 | void serial_send_str_P(const char *data); 84 | 85 | void serial_send_fmt_P(const char *fmt, ...); 86 | 87 | void serial_transmit_block(const uint8_t *data, uint16_t len); 88 | 89 | char serial_receive_char(void); 90 | 91 | uint16_t serial_receive_block(uint8_t *data, uint16_t maxlen); 92 | 93 | void serial_send_break(void); 94 | 95 | /* stdio-compatible putchar implementation. */ 96 | int serial_putchar(char c, FILE *stream); 97 | /* minio-compatible putc implementation. */ 98 | void serial_mio_putc(char c); 99 | 100 | /* stdio-compatible getchar implementation. Blocks until a character is */ 101 | /* received. Returns EOF if a break condition is received. */ 102 | int serial_getchar(FILE *stream); 103 | /* minio-compatible getc implementation. If chk==0, block until a character */ 104 | /* is received, then return that character. Otherwise, return a flag */ 105 | /* indicating if a character is ready. */ 106 | int serial_mio_getc(char chk); 107 | 108 | /* Equivalent of FDEV_SETUP_STREAM() that creates a stream attached to the 109 | * serial port. */ 110 | #define SERIAL_SETUP_STREAM_(p,g,f) { .put=p, .get=g, .flags=f } 111 | #define SERIAL_SETUP_STREAM_RDWR() SERIAL_SETUP_STREAM_(serial_putchar,serial_getchar,_FDEV_SETUP_RW) 112 | #define SERIAL_SETUP_STREAM_WRONLY() SERIAL_SETUP_STREAM_(serial_putchar,NULL,_FDEV_SETUP_WRITE) 113 | #define SERIAL_SETUP_STREAM_RDONLY() SERIAL_SETUP_STREAM_(NULL,serial_getchar,_FDEV_SETUP_READ) 114 | 115 | /* minio equivalent of FDEV_SETUP_STREAM() that connects the input and output */ 116 | /* vectors to the serial port. */ 117 | #define SERIAL_MIO_SETUP() ({ mio_putc=serial_mio_putc; mio_getc=serial_mio_getc; }) 118 | #define SERIAL_MIO_SETUP_WRONLY() ({ mio_putc=serial_mio_putc; mio_getc=NULL; }) 119 | #define SERIAL_MIO_SETUP_RDONLY() ({ mio_putc=NULL; mio_getc=serial_mio_getc; }) 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /include/spi.h: -------------------------------------------------------------------------------- 1 | #ifndef SPI_H_ 2 | #define SPI_H_ 3 | 4 | #define SPI0_CS_PORT PORTD 5 | #define SPI0_CS_DDR DDRD 6 | #define SPI0_CS_PIN 0 7 | 8 | #define SPI1_CS_PORT PORTB 9 | #define SPI1_CS_DDR DDRB 10 | #define SPI1_CS_PIN 0 11 | 12 | #define SPI2_CS_PORT PORTB 13 | #define SPI2_CS_DDR DDRB 14 | #define SPI2_CS_PIN 2 15 | 16 | #define SPI3_CS_PORT PORTB 17 | #define SPI3_CS_DDR DDRB 18 | #define SPI3_CS_PIN 3 19 | 20 | /* SPI EEPROM (AT25xxx compatible devices) status register bits */ 21 | #define SPI_EEPROM_WPEN 7 22 | #define SPI_EEPROM_IPL 6 23 | #define SPI_EEPROM_nPRESENT 5 24 | #define SPI_EEPROM_LIP 4 25 | #define SPI_EEPROM_BP1 3 26 | #define SPI_EEPROM_BP0 2 27 | #define SPI_EEPROM_WEL 1 28 | #define SPI_EEPROM_nRDY 0 29 | 30 | /* SPI EEPROM (AT25xxx compatible devices) commands */ 31 | #define SPI_EEPROM_WREN 0b00000110 /* Enable Write Operations */ 32 | #define SPI_EEPROM_WRDI 0b00000100 /* Disable Write Operations */ 33 | #define SPI_EEPROM_RDSR 0b00000101 /* Read Status Register */ 34 | #define SPI_EEPROM_WRSR 0b00000001 /* Write Status Register */ 35 | #define SPI_EEPROM_READ 0b00000011 /* Read Data from Memory */ 36 | #define SPI_EEPROM_WRITE 0b00000010 /* Write Data to Memory */ 37 | 38 | #define SPI_EEPROM_PAGE_SIZE 128 /* max number of bytes that can be written at a time */ 39 | #define SPI_EEPROM_PAGE_MASK ((SPI_EEPROM_PAGE_SIZE)-1) 40 | 41 | #ifndef __ASSEMBLER__ 42 | #include 43 | #include 44 | 45 | void spi_init(void); 46 | 47 | /** 48 | * SPI EEPROM (AT25xxx compatible devices) support on port 0 49 | */ 50 | 51 | uint8_t spi_eeprom_status(void); 52 | 53 | bool spi_eeprom_present(void); 54 | 55 | void spi_eeprom_set_status(uint8_t value); 56 | 57 | uint8_t spi_eeprom_read_byte(uint16_t addr); 58 | 59 | void spi_eeprom_write_byte(uint16_t addr, uint8_t value); 60 | 61 | void spi_eeprom_read_block(void *dst, uint16_t src, uint16_t n); 62 | 63 | void spi_eeprom_write_block(const void *src, uint16_t dst, uint16_t n); 64 | 65 | void spi_eeprom_memset(uint16_t dst, char c, uint16_t n); 66 | 67 | 68 | #endif /* !__ASSEMBLER__ */ 69 | #endif 70 | -------------------------------------------------------------------------------- /include/videodefs.h: -------------------------------------------------------------------------------- 1 | #ifndef VIDEODEFS_H_ 2 | #define VIDEODEFS_H_ 3 | 4 | #include "defs.h" 5 | 6 | /* Actual NTSC spec value is 227.5, but many computers and game consoles */ 7 | /* rounded up to 228 */ 8 | #define COLORBURST_PERIODS_PER_LINE 228 9 | /* approx. 3.91 usec. this is out of spec (NTSC wants 4.7 us) but I've tried */ 10 | /* numerous TVs (CRT and LCD) and capture/HD-converter devices and they are */ 11 | /* OK with it. Some displays are fine with hsync pulses as short as 3 us. */ 12 | #define COLORBURST_PERIODS_PER_HSYNC 14 13 | /* back porch duration; controls horizontal position of image */ 14 | #define COLORBURST_PERIODS_PER_BACK_PORCH 22 15 | /* "fake progressive scan" uses 262 lines/frame */ 16 | #define LINES_PER_FRAME 262 17 | #define ACTIVE_VIDEO_LINES 200 18 | /* change this number to adjust vertical positioning */ 19 | #define VBLANK_LINES 34 20 | /* change this number to adjust length of vsync */ 21 | /* some TVs are fine with values as high as 12, */ 22 | /* but some cheap NTSC-to-digital converters don't like that */ 23 | #define VSYNC_LINES 2 24 | 25 | #if F_CPU==14318182 26 | #define CYCLES_PER_COLORBURST 4 27 | #define COLOR_BITS 8 28 | #else 29 | #error Clock frequency must be 4x NTSC colorburst (14.3181818 MHz) 30 | #endif 31 | 32 | /* 912 AVR cycles per line */ 33 | #define CYCLES_PER_LINE ((COLORBURST_PERIODS_PER_LINE)*(CYCLES_PER_COLORBURST)) 34 | #define CYCLES_PER_HSYNC ((COLORBURST_PERIODS_PER_HSYNC)*(CYCLES_PER_COLORBURST)) 35 | #define CYCLES_PER_BACK_PORCH ((COLORBURST_PERIODS_PER_BACK_PORCH)*(CYCLES_PER_COLORBURST)) 36 | 37 | #define ACTIVE_VIDEO_BYTES_PER_LINE (20*COLOR_BITS) 38 | 39 | #define COLORBURST_PATTERN 0x88 40 | 41 | #ifndef __ASSEMBLER__ 42 | /* Tilemaps can be in ROM or RAM, so they need to be referenced */ 43 | /* by 24-bit pointers. */ 44 | typedef __memx PAGE_ALIGNED uint8_t TILEMAP[]; 45 | typedef const __memx PAGE_ALIGNED uint8_t *TILEMAP_PTR; 46 | #endif 47 | 48 | /* Bit number in tilemap_hh that indicates if the tilemap is in RAM or ROM. */ 49 | /* avr-gcc sets the high bit of a __memx pointer if it points to RAM. */ 50 | #define TILEMAP_IS_RAM_BIT 7 51 | 52 | #define VFMT_TEXT (0<<5) 53 | #define VFMT_256COLOR_BITMAP (1<<5) 54 | #define VFMT_16COLOR_BITMAP (2<<5) 55 | #define VFMT_4COLOR_BITMAP (3<<5) 56 | #define VFMT_MONO_BITMAP (5<<5) 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /palettes/2bit-bo-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/2bit-bo-preview.png -------------------------------------------------------------------------------- /palettes/2bit-bo.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 2-bit blue/orange 3 | Columns: 8 4 | 0 0 0 5 | 207 45 255 6 | 48 210 0 7 | 255 255 255 8 | -------------------------------------------------------------------------------- /palettes/2bit-gm-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/2bit-gm-preview.png -------------------------------------------------------------------------------- /palettes/2bit-gm.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 2-bit green/magenta 3 | Columns: 8 4 | 0 0 0 5 | 48 210 0 6 | 207 45 255 7 | 255 255 255 8 | -------------------------------------------------------------------------------- /palettes/2bit.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 2-bit 3 | Columns: 8 4 | 0 0 0 5 | 6 162 255 6 | 249 93 0 7 | 255 255 255 8 | -------------------------------------------------------------------------------- /palettes/4bit-bo-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/4bit-bo-preview.png -------------------------------------------------------------------------------- /palettes/4bit-bo.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 4-bit blue/orange 3 | Columns: 8 4 | 0 0 0 5 | 155 34 255 6 | 36 157 0 7 | 191 191 191 8 | 52 11 86 9 | 207 45 255 10 | 88 169 19 11 | 243 203 255 12 | 12 52 0 13 | 167 86 236 14 | 48 210 0 15 | 203 244 169 16 | 64 64 64 17 | 219 98 255 18 | 100 221 0 19 | 255 255 255 20 | -------------------------------------------------------------------------------- /palettes/4bit-gm-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/4bit-gm-preview.png -------------------------------------------------------------------------------- /palettes/4bit-gm.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 4-bit green/magenta 3 | Columns: 8 4 | 0 0 0 5 | 36 157 0 6 | 155 34 255 7 | 191 191 191 8 | 12 52 0 9 | 48 210 0 10 | 167 86 236 11 | 203 244 169 12 | 52 11 86 13 | 88 169 19 14 | 207 45 255 15 | 243 203 255 16 | 64 64 64 17 | 100 221 0 18 | 219 98 255 19 | 255 255 255 20 | -------------------------------------------------------------------------------- /palettes/4bit-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/4bit-preview.png -------------------------------------------------------------------------------- /palettes/4bit.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 4-bit 3 | Columns: 8 4 | 0 0 0 5 | 0 122 26 6 | 42 40 243 7 | 6 162 255 8 | 164 5 102 9 | 128 128 128 10 | 207 45 255 11 | 170 167 255 12 | 85 88 0 13 | 48 210 0 14 | 128 128 128 15 | 91 250 153 16 | 249 93 0 17 | 213 215 12 18 | 255 133 229 19 | 255 255 255 20 | -------------------------------------------------------------------------------- /palettes/8bit-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/74hc595/Amethyst/adf516a1329cd3dcfe483e4410ea5a5b1041a52a/palettes/8bit-preview.png -------------------------------------------------------------------------------- /palettes/8bit.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 8-bit blended 3 | Columns: 8 4 | 0 0 0 5 | 0 92 19 6 | 32 30 182 7 | 4 122 201 8 | 123 4 76 9 | 96 96 96 10 | 155 34 255 11 | 127 126 255 12 | 64 66 0 13 | 36 157 0 14 | 96 96 96 15 | 68 187 115 16 | 187 70 0 17 | 159 161 9 18 | 219 99 172 19 | 191 191 191 20 | 0 31 6 21 | 0 122 26 22 | 23 60 188 23 | 0 152 208 24 | 114 34 83 25 | 86 126 102 26 | 146 64 255 27 | 118 156 255 28 | 55 96 0 29 | 27 188 0 30 | 86 126 102 31 | 59 218 121 32 | 178 100 0 33 | 150 192 16 34 | 210 130 178 35 | 182 222 198 36 | 11 10 61 37 | 0 102 80 38 | 42 40 243 39 | 15 132 255 40 | 134 14 137 41 | 106 106 156 42 | 166 44 255 43 | 138 135 255 44 | 74 76 0 45 | 47 167 0 46 | 106 106 156 47 | 79 197 176 48 | 198 80 51 49 | 170 171 70 50 | 229 109 233 51 | 202 201 252 52 | 1 41 67 53 | 0 132 86 54 | 33 70 249 55 | 6 162 255 56 | 125 44 143 57 | 97 136 163 58 | 156 74 255 59 | 129 166 255 60 | 65 106 0 61 | 38 198 0 62 | 97 136 163 63 | 69 228 182 64 | 188 110 57 65 | 161 202 76 66 | 220 140 239 67 | 193 232 255 68 | 41 1 25 69 | 13 93 45 70 | 73 31 207 71 | 45 123 227 72 | 164 5 102 73 | 137 97 121 74 | 196 35 255 75 | 168 127 255 76 | 105 67 0 77 | 77 159 0 78 | 137 97 121 79 | 109 189 140 80 | 228 71 15 81 | 201 163 35 82 | 255 101 197 83 | 232 193 217 84 | 32 32 32 85 | 4 124 51 86 | 64 62 214 87 | 36 154 233 88 | 155 36 108 89 | 128 128 128 90 | 187 66 255 91 | 159 157 255 92 | 96 98 0 93 | 68 189 0 94 | 128 128 128 95 | 100 219 147 96 | 219 101 22 97 | 191 193 41 98 | 251 131 204 99 | 223 223 223 100 | 52 11 86 101 | 24 103 105 102 | 83 41 255 103 | 56 133 255 104 | 175 15 162 105 | 147 107 182 106 | 207 45 255 107 | 179 137 255 108 | 115 77 0 109 | 88 169 19 110 | 147 107 182 111 | 120 199 201 112 | 239 81 76 113 | 211 173 95 114 | 255 111 255 115 | 243 203 255 116 | 42 42 93 117 | 15 134 112 118 | 74 72 255 119 | 47 163 255 120 | 166 46 169 121 | 138 137 188 122 | 197 76 255 123 | 170 167 255 124 | 106 108 6 125 | 79 199 25 126 | 138 137 188 127 | 111 229 207 128 | 230 111 83 129 | 202 203 102 130 | 255 141 255 131 | 234 233 255 132 | 21 22 0 133 | 0 114 0 134 | 53 52 153 135 | 25 144 172 136 | 144 26 48 137 | 117 118 67 138 | 176 56 230 139 | 149 147 249 140 | 85 88 0 141 | 58 179 0 142 | 117 118 67 143 | 89 209 86 144 | 208 92 0 145 | 181 183 0 146 | 240 121 143 147 | 213 213 162 148 | 12 52 0 149 | 0 144 0 150 | 44 82 160 151 | 16 174 179 152 | 135 56 54 153 | 108 148 73 154 | 167 86 236 155 | 140 178 255 156 | 76 118 0 157 | 48 210 0 158 | 108 148 73 159 | 80 240 93 160 | 199 122 0 161 | 172 214 0 162 | 231 152 150 163 | 203 244 169 164 | 32 32 32 165 | 4 124 51 166 | 64 62 214 167 | 36 154 233 168 | 155 36 108 169 | 128 128 128 170 | 187 66 255 171 | 159 157 255 172 | 96 98 0 173 | 68 189 0 174 | 128 128 128 175 | 100 219 147 176 | 219 101 22 177 | 191 193 41 178 | 251 131 204 179 | 223 223 223 180 | 23 62 38 181 | 0 154 58 182 | 54 92 220 183 | 27 184 240 184 | 146 66 115 185 | 118 158 134 186 | 178 96 255 187 | 150 188 255 188 | 87 128 0 189 | 59 220 0 190 | 118 158 134 191 | 91 250 153 192 | 210 132 28 193 | 182 224 48 194 | 242 162 210 195 | 214 254 230 196 | 62 23 0 197 | 35 115 16 198 | 94 53 179 199 | 67 145 198 200 | 186 27 73 201 | 158 119 92 202 | 217 57 255 203 | 190 149 255 204 | 126 89 0 205 | 99 181 0 206 | 158 119 92 207 | 130 211 112 208 | 249 93 0 209 | 222 185 6 210 | 255 123 169 211 | 254 214 188 212 | 53 54 3 213 | 26 146 22 214 | 85 84 185 215 | 57 175 204 216 | 176 58 79 217 | 149 149 99 218 | 208 88 255 219 | 181 179 255 220 | 117 120 0 221 | 89 211 0 222 | 149 149 99 223 | 121 241 118 224 | 240 123 0 225 | 213 215 12 226 | 255 153 175 227 | 244 245 194 228 | 73 33 57 229 | 45 125 77 230 | 105 63 239 231 | 77 155 255 232 | 196 37 134 233 | 169 129 153 234 | 228 67 255 235 | 200 159 255 236 | 137 99 0 237 | 109 191 0 238 | 169 129 153 239 | 141 221 172 240 | 255 103 47 241 | 232 195 67 242 | 255 133 229 243 | 255 224 249 244 | 64 64 64 245 | 36 156 83 246 | 96 94 246 247 | 68 185 255 248 | 187 68 140 249 | 159 159 159 250 | 219 98 255 251 | 191 189 255 252 | 128 129 0 253 | 100 221 0 254 | 159 159 159 255 | 132 251 179 256 | 251 133 54 257 | 223 225 73 258 | 255 163 236 259 | 255 255 255 260 | -------------------------------------------------------------------------------- /palettes/hsv.gpl: -------------------------------------------------------------------------------- 1 | GIMP Palette 2 | Name: NTSC 8-bit HSV 3 | Columns: 8 4 | 64 64 64 5 | 117 118 67 6 | 117 118 67 7 | 117 118 67 8 | 117 118 67 9 | 86 126 102 10 | 86 126 102 11 | 86 126 102 12 | 86 126 102 13 | 42 42 93 14 | 42 42 93 15 | 42 42 93 16 | 137 97 121 17 | 137 97 121 18 | 137 97 121 19 | 137 97 121 20 | 135 56 54 21 | 135 56 54 22 | 117 118 67 23 | 117 118 67 24 | 23 62 38 25 | 23 62 38 26 | 23 62 38 27 | 23 62 38 28 | 24 103 105 29 | 42 42 93 30 | 42 42 93 31 | 42 42 93 32 | 73 33 57 33 | 73 33 57 34 | 73 33 57 35 | 73 33 57 36 | 62 23 0 37 | 62 23 0 38 | 53 54 3 39 | 53 54 3 40 | 35 115 16 41 | 13 93 45 42 | 13 93 45 43 | 24 103 105 44 | 24 103 105 45 | 11 10 61 46 | 11 10 61 47 | 52 11 86 48 | 52 11 86 49 | 114 34 83 50 | 114 34 83 51 | 114 34 83 52 | 62 23 0 53 | 62 23 0 54 | 74 76 0 55 | 74 76 0 56 | 55 96 0 57 | 0 92 19 58 | 0 92 19 59 | 0 102 80 60 | 1 41 67 61 | 1 41 67 62 | 11 10 61 63 | 52 11 86 64 | 52 11 86 65 | 41 1 25 66 | 41 1 25 67 | 41 1 25 68 | 158 119 92 69 | 158 119 92 70 | 149 149 99 71 | 149 149 99 72 | 149 149 99 73 | 118 158 134 74 | 118 158 134 75 | 118 158 134 76 | 97 136 163 77 | 97 136 163 78 | 106 106 156 79 | 106 106 156 80 | 147 107 182 81 | 137 97 121 82 | 137 97 121 83 | 137 97 121 84 | 135 56 54 85 | 135 56 54 86 | 170 171 70 87 | 170 171 70 88 | 108 148 73 89 | 45 125 77 90 | 45 125 77 91 | 79 197 176 92 | 67 145 198 93 | 44 82 160 94 | 53 52 153 95 | 53 52 153 96 | 125 44 143 97 | 146 66 115 98 | 146 66 115 99 | 176 58 79 100 | 198 80 51 101 | 188 110 57 102 | 159 161 9 103 | 88 169 19 104 | 88 169 19 105 | 26 146 22 106 | 36 156 83 107 | 15 134 112 108 | 25 144 172 109 | 44 82 160 110 | 32 30 182 111 | 94 53 179 112 | 166 46 169 113 | 155 36 108 114 | 155 36 108 115 | 144 26 48 116 | 187 70 0 117 | 178 100 0 118 | 137 99 0 119 | 159 161 9 120 | 77 159 0 121 | 0 144 0 122 | 0 154 58 123 | 0 132 86 124 | 16 174 179 125 | 23 60 188 126 | 23 60 188 127 | 134 14 137 128 | 134 14 137 129 | 164 5 102 130 | 164 5 102 131 | 144 26 48 132 | 231 152 150 133 | 231 152 150 134 | 213 213 162 135 | 213 213 162 136 | 203 244 169 137 | 141 221 172 138 | 141 221 172 139 | 120 199 201 140 | 120 199 201 141 | 138 137 188 142 | 138 137 188 143 | 138 137 188 144 | 147 107 182 145 | 210 130 178 146 | 210 130 178 147 | 210 130 178 148 | 230 111 83 149 | 211 173 95 150 | 211 173 95 151 | 161 202 76 152 | 161 202 76 153 | 89 209 86 154 | 68 187 115 155 | 79 197 176 156 | 67 145 198 157 | 67 145 198 158 | 85 84 185 159 | 167 86 236 160 | 229 109 233 161 | 187 68 140 162 | 187 68 140 163 | 176 58 79 164 | 198 80 51 165 | 210 132 28 166 | 201 163 35 167 | 191 193 41 168 | 79 199 25 169 | 79 199 25 170 | 59 218 121 171 | 69 228 182 172 | 57 175 204 173 | 45 123 227 174 | 32 30 182 175 | 73 31 207 176 | 176 56 230 177 | 196 37 134 178 | 196 37 134 179 | 186 27 73 180 | 187 70 0 181 | 208 92 0 182 | 199 122 0 183 | 172 214 0 184 | 89 211 0 185 | 38 198 0 186 | 27 188 0 187 | 16 174 179 188 | 0 152 208 189 | 4 122 201 190 | 23 60 188 191 | 73 31 207 192 | 175 15 162 193 | 175 15 162 194 | 186 27 73 195 | 186 27 73 196 | 231 152 150 197 | 254 214 188 198 | 244 245 194 199 | 244 245 194 200 | 203 244 169 201 | 203 244 169 202 | 214 254 230 203 | 214 254 230 204 | 193 232 255 205 | 193 232 255 206 | 181 179 255 207 | 200 159 255 208 | 243 203 255 209 | 255 163 236 210 | 242 162 210 211 | 255 153 175 212 | 239 81 76 213 | 230 111 83 214 | 232 195 67 215 | 223 225 73 216 | 121 241 118 217 | 80 240 93 218 | 91 250 153 219 | 111 229 207 220 | 68 185 255 221 | 118 156 255 222 | 96 94 246 223 | 178 96 255 224 | 219 98 255 225 | 255 101 197 226 | 255 101 197 227 | 240 121 143 228 | 255 103 47 229 | 251 133 54 230 | 232 195 67 231 | 182 224 48 232 | 182 224 48 233 | 80 240 93 234 | 59 218 121 235 | 69 228 182 236 | 47 163 255 237 | 56 133 255 238 | 42 40 243 239 | 146 64 255 240 | 217 57 255 241 | 228 67 255 242 | 255 101 197 243 | 255 101 197 244 | 249 93 0 245 | 249 93 0 246 | 240 123 0 247 | 172 214 0 248 | 100 221 0 249 | 59 220 0 250 | 59 220 0 251 | 6 162 255 252 | 6 162 255 253 | 15 132 255 254 | 33 70 249 255 | 155 34 255 256 | 196 35 255 257 | 196 35 255 258 | 196 37 134 259 | 186 27 73 260 | -------------------------------------------------------------------------------- /scripts/analyze-register-usage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Analyzes a set of AVR disassembly files (generated by avr-objdump) and reports 4 | # the set of registers used (r0 through r31) by each function. 5 | # Can be used to determine which registers are safe to reserve globally with the 6 | # `register asm()` construct: 7 | # https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_regbind.html 8 | # Can also be used to determine which standard library functions are safe to use 9 | # when a given set of registers is reserved globally. 10 | # 11 | # TODO: command line arg parsing, this is all hardcoded 12 | 13 | import re 14 | import operator 15 | import sys 16 | from functools import reduce 17 | from collections import defaultdict 18 | 19 | # in this codebase, r2 through r7 are "unsafe" because they're reserved for ISRs 20 | UNSAFE_REGS = (1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7) 21 | 22 | numbered_register_re = re.compile(r'r(\d{1,2})$') 23 | x_register_re = re.compile(r'-?X\+?$') 24 | y_register_re = re.compile(r'-?Y(\+\d{0,2})?$') 25 | z_register_re = re.compile(r'-?Z(\+\d{0,2})?$') 26 | fn_start_re = re.compile(r'[0-9a-fA-F]+ <(.+)>:$') 27 | 28 | def recognize_numbered_register(s: str) -> int: 29 | match = numbered_register_re.match(s) 30 | if match is None: 31 | return 0 32 | else: 33 | reg_num = int(match.group(1)) 34 | return 1< int: 37 | match = x_register_re.match(s) 38 | return (0b11<<26) if match is not None else 0 39 | 40 | def recognize_y_register(s: str) -> int: 41 | match = y_register_re.match(s) 42 | return (0b11<<28) if match is not None else 0 43 | 44 | def recognize_z_register(s: str) -> int: 45 | match = z_register_re.match(s) 46 | return (0b11<<30) if match is not None else 0 47 | 48 | RECOGNIZERS = ( 49 | recognize_numbered_register, 50 | recognize_x_register, 51 | recognize_y_register, 52 | recognize_z_register 53 | ) 54 | 55 | def recognize_registers_in_arg(s: str) -> int: 56 | return reduce(operator.__or__, (f(s) for f in RECOGNIZERS)) 57 | 58 | def recognize_registers_in_args(line: str) -> int: 59 | args = [s.strip() for s in line.split(',')] 60 | return reduce(operator.__or__, map(recognize_registers_in_arg, args)) 61 | 62 | def recognize_registers_in_line(line: str) -> int: 63 | try: 64 | addr, opcode, mnemonic, args, *comment = line.split('\t') 65 | return recognize_registers_in_args(args) 66 | except ValueError: 67 | return 0 68 | 69 | def make_table_row(fnname, regs): 70 | cells = ['{}'.format('unsafe' if (regs & UNSAFE_REGS) else '', fnname)] 71 | cells.extend('{}'.format('r'+str(n) if regs & (1<') 73 | return ''.join(cells) 74 | 75 | 76 | current_fn = None 77 | fn_register_usage = defaultdict(int) 78 | 79 | LST_FILES = ['libatmega1284','libgcc','libc','libm','libprintf_flt','libprintf_min','libscanf_flt','libscanf_min'] 80 | LST_SUFFIX = '_avr51.dis.lst' 81 | 82 | for filename in LST_FILES: 83 | with open(filename+LST_SUFFIX, 'r') as lstfile: 84 | for line in lstfile: 85 | # does this line start a function? 86 | fnmatch = fn_start_re.match(line) 87 | if fnmatch: 88 | current_fn = filename + ':' + fnmatch.group(1) 89 | else: 90 | regs = recognize_registers_in_line(line) 91 | fn_register_usage[current_fn] |= regs 92 | 93 | for fn, regs in fn_register_usage.items(): 94 | if regs & UNSAFE_REGS: 95 | print('--wrap '+fn.split(':')[1]) 96 | 97 | # print('') 98 | # print(make_table_row('library:function', 0xFFFFFFFF)) 99 | # for item in fn_register_usage.items(): 100 | # print(make_table_row(*item)) 101 | # print('
') -------------------------------------------------------------------------------- /scripts/colorbargen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def popcount(bitpattern, nbits): 4 | return bin(bitpattern & ((1<>= 1 28 | out |= bit 29 | return out 30 | 31 | lookuptable = sorted(xrange(0,256), key=lambda x: popcount(x,8)) 32 | 33 | 34 | remap_table = [ 35 | 0x0, 0x1, 0x2, 0x4, 36 | 0x8, 0x3, 0x6, 0xC, 37 | 0x9, 0x5, 0xA, 0xE, 38 | 0xD, 0xB, 0x7, 0xF, 39 | ] 40 | 41 | def eight_bit_pattern(): 42 | data = bytearray() 43 | width = 160 44 | for y in xrange(0, 192): 45 | for x in xrange(0, width): 46 | xblock = x / 10 47 | yblock = y / 12 48 | # lo = xblock << 4 49 | # lo_black = 0 50 | # lo_gray = 0b0101 << 4 51 | # lo_white = 0b1111 << 4 52 | # hi = xblock 53 | # hi_black = 0 54 | # hi_gray = 0b0101 55 | # hi_white = 0b1111 56 | # if yblock < 2: 57 | # c = hi_black + lo_gray 58 | # elif yblock < 4: 59 | # c = hi_black + lo_white 60 | # elif yblock < 6: 61 | # c = hi_gray + lo_black 62 | # elif yblock < 8: 63 | # c = hi_gray + lo_gray 64 | # elif yblock < 10: 65 | # c = hi_gray + lo_white 66 | # elif yblock < 12: 67 | # c = hi_white + lo_black 68 | # elif yblock < 14: 69 | # c = hi_white + lo_gray 70 | # else: 71 | # c = hi_white + lo_white 72 | c = yblock | (xblock << 4) 73 | # c = bit(c,0)*1 + bit(c,0)*2 + bit(c,1)*4 + bit(c,1)*8 + bit(c,2)*16 + bit(c,2)*32 + bit(c,3)*64 + bit(c,3)*128 74 | #c = revb(c,8) 75 | data.append(c) 76 | return data 77 | 78 | with open('4bit.bin', 'wb') as f: 79 | f.write(four_bit_pattern()) 80 | 81 | with open('8bit.bin', 'wb') as f: 82 | f.write(eight_bit_pattern()) -------------------------------------------------------------------------------- /scripts/colorize_16x8_font.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from os.path import basename, splitext 5 | from PIL import Image 6 | 7 | PATTERN_HEIGHT = 8 8 | PATTERN_WIDTHS = [4, 8, 12, 16] 9 | MAX_COL_WIDTH = max(PATTERN_WIDTHS) 10 | 11 | BIT_SPREAD_PATTERNS = [ 12 | (3, ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)), # white 13 | (1, (15, 0, 15, 1, 15, 2, 15, 3, 15, 4, 15, 5, 15, 6, 15, 7)), # gray 14 | (2, ( 0, 1, 2, 15, 3, 4, 5, 15, 6, 7, 8, 15, 9, 10, 11, 15)), # pink 15 | (1, ( 0, 1, 15, 15, 2, 3, 15, 15, 4, 5, 15, 15, 6, 7, 15, 15)), # red 16 | (2, ( 0, 1, 15, 2, 3, 4, 15, 5, 6, 7, 15, 8, 9, 10, 11, 15)), # yellow 17 | (1, ( 0, 15, 15, 1, 2, 15, 15, 3, 4, 15, 15, 5, 6, 15, 15, 7)), # green 18 | (2, ( 0, 15, 1, 2, 3, 15, 4, 5, 6, 15, 7, 8, 9, 15, 10, 11)), # aqua 19 | (2, (15, 0, 1, 2, 15, 3, 4, 5, 15, 6, 7, 8, 15, 9, 10, 11)), # light blue 20 | (1, (15, 15, 0, 1, 15, 15, 2, 3, 15, 15, 4, 5, 15, 15, 6, 7)), # blue 21 | (1, (15, 0, 1, 15, 15, 2, 3, 15, 15, 4, 5, 15, 15, 6, 7, 15)), # purple 22 | (0, (15, 0, 15, 15, 15, 1, 15, 15, 15, 2, 15, 15, 15, 3, 15, 15)), # dark red 23 | (0, ( 0, 15, 15, 15, 1, 15, 15, 15, 2, 15, 15, 15, 3, 15, 15, 15)), # dark orange 24 | (0, (15, 15, 15, 0, 15, 15, 15, 1, 15, 15, 15, 2, 15, 15, 15, 3)), # dark green 25 | (0, (15, 15, 0, 15, 15, 15, 1, 15, 15, 15, 2, 15, 15, 15, 3, 15)), # dark blue 26 | (0, (15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15)), 27 | (0, (15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15)), 28 | ] 29 | 30 | 31 | def extract_subimage(img, x, row, width): 32 | y = row*PATTERN_HEIGHT 33 | return img.crop((x, y, x+width, y+PATTERN_HEIGHT)) 34 | 35 | def spread_bits(img, spreadpattern): 36 | out = Image.new('L', (MAX_COL_WIDTH,PATTERN_HEIGHT)) 37 | for x in xrange(0,MAX_COL_WIDTH): 38 | dstcol = spreadpattern[x] 39 | col = img.crop((dstcol,0,dstcol+1,PATTERN_HEIGHT)) 40 | out.paste(col,(x,0,x+1,PATTERN_HEIGHT)) 41 | return out 42 | 43 | 44 | infile = sys.argv[1] 45 | outfile = splitext(infile)[0]+'_color.png' 46 | img = Image.open(infile) 47 | if img.size[0] % MAX_COL_WIDTH != 0: 48 | raise Exception('image width must be a multiple of {}'.format(MAX_COL_WIDTH)) 49 | if img.size[1] != PATTERN_HEIGHT*len(PATTERN_WIDTHS): 50 | raise Exception('image height must be {}'.format(PATTERN_HEIGHT*len(PATTERN_WIDTHS))) 51 | 52 | # slice up input image 53 | chars = [] 54 | for px in xrange(0, img.size[0], MAX_COL_WIDTH): 55 | src_images = map(lambda (r,w): extract_subimage(img,px,r,MAX_COL_WIDTH), enumerate(PATTERN_WIDTHS)) 56 | chars.append(src_images) 57 | 58 | # create output image 59 | num_cols = len(chars) 60 | num_rows = len(BIT_SPREAD_PATTERNS) 61 | outimg = Image.new('L', (num_cols*MAX_COL_WIDTH, num_rows*PATTERN_HEIGHT), 0) 62 | 63 | for col, char in enumerate(chars): 64 | for row, spr in enumerate(BIT_SPREAD_PATTERNS): 65 | outx = col*MAX_COL_WIDTH 66 | outy = row*PATTERN_HEIGHT 67 | src_img = char[spr[0]] 68 | spread_img = spread_bits(src_img, spr[1]) 69 | outimg.paste(spread_img, (outx, outy, outx+src_img.size[0], outy+src_img.size[1])) 70 | 71 | outimg.save(outfile) 72 | -------------------------------------------------------------------------------- /scripts/generate-app-linker-script-fragment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SECTION=$1 4 | shift 5 | echo "OVERLAY ADDR(.${SECTION}) + SIZEOF(.${SECTION}) : {" 6 | for app in $@ 7 | do 8 | echo ".app_${app}_${SECTION} { KEEP(build/apps/${app}/*(.${SECTION}*)) }" 9 | done 10 | echo "} > data" 11 | -------------------------------------------------------------------------------- /scripts/generate-app-linker-script-overlay.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SECTION=$1 4 | shift 5 | START="ADDR(${SECTION})+SIZEOF(${SECTION})" 6 | DATA_AT="AT(LOADADDR(.data)+SIZEOF(.data))" 7 | for app in $@ 8 | do 9 | echo ".app_${app}_data ${START} : ${DATA_AT} { " 10 | echo " build/apps/${app}/*(.data)" 11 | echo " build/apps/${app}/*(.data*)" 12 | echo " build/apps/${app}/*(.gnu.linkonce.d*)" 13 | echo " build/apps/${app}/*(.rodata)" 14 | echo " build/apps/${app}/*(.rodata*)" 15 | echo " build/apps/${app}/*(.gnu.linkonce.r*)" 16 | echo "} > data" 17 | echo "__data_load_start_app_${app} = LOADADDR(.app_${app}_data);" 18 | echo "__data_load_start_xpage_app_${app} = LOADADDR(.app_${app}_data) >> 16;" 19 | echo "__data_start_app_${app} = ADDR(.app_${app}_data);" 20 | echo "__data_end_app_${app} = ADDR(.app_${app}_data)+SIZEOF(.app_${app}_data);" 21 | echo ".app_${app}_bss ADDR(.app_${app}_data)+SIZEOF(.app_${app}_data) (NOLOAD) : { KEEP(build/apps/${app}/*(.bss*)) } > data" 22 | echo "__bss_end_app_${app} = ADDR(.app_${app}_bss)+SIZEOF(.app_${app}_bss);" 23 | echo ".app_${app}_noinit ADDR(.app_${app}_bss)+SIZEOF(.app_${app}_bss) (NOLOAD) : { KEEP(build/apps/${app}/*(.noinit*)) } > data" 24 | DATA_AT="AT(LOADADDR(.app_${app}_data)+SIZEOF(.app_${app}_data))" 25 | done 26 | 27 | -------------------------------------------------------------------------------- /scripts/generate-bitspread-table.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | O = 7 # indicates bit will always be zero 4 | I = 6 # indicates bit will always be one 5 | BIT_SPREAD_PATTERNS = [ 6 | (O, O, O, O, O, O, O, O), # 0 black 7 | (O, O, O, 2, O, O, O, 0), # 1 dark green 8 | (O, O, 2, O, O, O, 0, O), # 2 dark blue 9 | (O, O, 3, 2, O, O, 1, 0), # 3 blue 10 | (O, 2, O, O, O, 0, O, O), # 4 red 11 | (O, 3, O, 2, O, 1, O, 0), # 5 gray 12 | (O, 3, 2, O, O, 1, 0, O), # 6 purple 13 | (O, 3, 2, 2, O, 1, 0, 0), # 7 light blue 14 | (2, O, O, O, 0, O, O, O), # 8 brown 15 | (3, O, O, 2, 1, O, O, 0), # 9 green 16 | (3, O, 2, O, 1, O, 0, O), # 10 grey 17 | (3, O, 3, 2, 1, O, 1, 0), # 11 aqua 18 | (3, 2, O, O, 1, 0, O, O), # 12 orange 19 | (3, 2, O, 2, 1, 0, O, 0), # 13 yellow 20 | (3, 2, 2, O, 1, 0, 0, O), # 14 pink 21 | (3, 3, 2, 2, 1, 1, 0, 0), # 15 white 22 | (O, 3, 2, I, O, 1, 0, I), # 16 blue on dark green 23 | (3, O, 2, I, 1, O, 0, I), # 17 aqua on dark green 24 | (3, 2, O, I, 1, 0, O, I), # 18 yellow on dark green 25 | (3, 2, 2, I, 1, 0, 0, I), # 19 white on dark green 26 | (O, 3, I, 2, O, 1, I, 0), # 20 blue on dark blue 27 | (3, O, I, 2, 1, O, I, 0), # 21 aqua on dark blue 28 | (3, 2, I, O, 1, 0, I, O), # 22 pink on dark blue 29 | (3, 2, I, 2, 1, 0, I, 0), # 23 white on dark blue 30 | (O, I, 3, 2, O, I, 1, 0), # 24 blue on red 31 | (3, I, O, 2, 1, I, O, 0), # 25 yellow on red 32 | (3, I, 2, O, 1, I, 0, O), # 26 pink on red 33 | (3, I, 3, 2, 1, I, 1, 0), # 27 white on red 34 | (I, O, 3, 2, I, O, 1, 0), # 28 aqua on brown 35 | (I, 3, O, 2, I, 1, O, 0), # 29 yellow on brown 36 | (I, 3, 2, O, I, 1, 0, O), # 30 pink on brown 37 | (I, 3, 2, 2, I, 1, 0, 0), # 31 white on brown 38 | (3, 2, I, I, 1, 0, I, I), # 32 white on blue 39 | (I, 3, I, 2, I, 1, I, 0), # 33 white on gray 40 | (3, I, I, 2, 1, I, I, 0), # 34 white on purple 41 | (I, 3, 2, I, I, 1, 0, I), # 35 white on green 42 | (3, I, 2, I, 1, I, 0, I), # 36 white on grey 43 | (I, I, 3, 2, I, I, 1, 0), # 37 white on orange 44 | ] 45 | 46 | 47 | 48 | def set_bit(n, value, bitnum): 49 | return n | (value<>4)&0xF) 63 | 64 | def generate_table_for_bitspread_pattern(pattern, invert=False): 65 | lines = [] 66 | for i in xrange(0,256): 67 | j = nibbleswap(i) 68 | nibble = (j if not invert else j^15) & 15 69 | byte = spread_nibble(nibble, pattern) 70 | lines.append('.byte 0b{:08b}'.format(byte)) 71 | lines.append('') 72 | return '\n'.join(lines) 73 | 74 | 75 | print '.text' 76 | print '.balign 256' 77 | print '.global bitspreadtable' 78 | print '.type bitspreadtable, @object' 79 | print 'bitspreadtable:' 80 | for pattern in BIT_SPREAD_PATTERNS: 81 | print generate_table_for_bitspread_pattern(pattern) 82 | print '.global bitspreadtable_numpatterns' 83 | print '.equ bitspreadtable_numpatterns, {}'.format(len(BIT_SPREAD_PATTERNS)) -------------------------------------------------------------------------------- /scripts/genfont.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | import sys 5 | from os.path import basename, splitext 6 | from PIL import Image 7 | 8 | # TODO: parse command line args 9 | PATTERN_WIDTH = 8 10 | PATTERN_HEIGHT = 8 11 | PACK = False 12 | Y_FLIP = 1 13 | X_FLIP = 0 14 | DEINTERLEAVE_16X8_HALVES = False 15 | 16 | # reverse bits 17 | def revb(bitpattern, nbits): 18 | out = 0 19 | for _ in xrange(0, nbits): 20 | out <<= 1 21 | bit = bitpattern & 1 22 | bitpattern >>= 1 23 | out |= bit 24 | return out 25 | 26 | def to_row(img, sx, sy): 27 | n = 0 28 | for x in xrange(sx, sx+PATTERN_WIDTH): 29 | n >>= 1 30 | color = img.getpixel((x, sy)) 31 | if color >= 128: 32 | n |= 0x80 33 | n = (n >> (8-PATTERN_WIDTH)) & 0xFF # align if width < 8 34 | return n 35 | 36 | def row_to_c_code(row): 37 | return ' 0b{:08b},'.format(revb(row,8)) 38 | 39 | def row_to_asm_code(row): 40 | return '.byte 0b{:08b}'.format(revb(row,8)) 41 | 42 | def to_c_code(pattern_num, rows): 43 | if pattern_num >= 32 and pattern_num < 127: 44 | comment = ' /* {} {} */\n'.format(pattern_num, chr(pattern_num)) 45 | else: 46 | comment = ' /* {} */\n'.format(pattern_num) 47 | code_rows = [row_to_c_code(r) for r in rows] 48 | code_rows = reversed(code_rows) if Y_FLIP else code_rows 49 | return comment + '\n'.join(code_rows) 50 | 51 | def to_asm_code(pattern_num, rows): 52 | if pattern_num >= 32 and pattern_num < 127: 53 | comment = ' ; {} {}\n'.format(pattern_num, chr(pattern_num)) 54 | else: 55 | comment = '; {}\n'.format(pattern_num) 56 | return comment + '\n'.join( 57 | [row_to_asm_code(r) for r in rows]) 58 | 59 | if sys.argv[1] == '-a': 60 | del sys.argv[1] 61 | genfunc = to_asm_code 62 | row_genfunc = row_to_asm_code 63 | extension = '.S' 64 | header = lambda fontname: '.text\n.balign 256\n.global '+fontname+'\n.type '+fontname+', @object\n'+fontname+':\n' 65 | footer = '\n' 66 | else: 67 | genfunc = to_c_code 68 | row_genfunc = row_to_c_code 69 | extension = '.c' 70 | header = lambda fontname: '#include \nconst uint8_t '+fontname+'[] __attribute__((aligned(256))) PROGMEM = {\n' 71 | footer = '\n};\n' 72 | 73 | infile = sys.argv[1] 74 | outfile = sys.argv[2] if len(sys.argv) >= 3 else splitext(infile)[0]+extension 75 | fontname = splitext(basename(outfile))[0] 76 | 77 | # filename suffix can be used to specify pattern width/height 78 | match = re.search(r"_(\d+)x(\d+)$", fontname) 79 | if match: 80 | PATTERN_WIDTH, PATTERN_HEIGHT = int(match.group(1)), int(match.group(2)) 81 | 82 | if PATTERN_WIDTH < 1 or PATTERN_HEIGHT < 1: 83 | raise Exception('pattern width and height must be greater than 0') 84 | if PATTERN_WIDTH > 8: 85 | raise Exception('pattern width cannot be greater than 8') 86 | 87 | img = Image.open(infile) 88 | if img.size[0] % PATTERN_WIDTH != 0: 89 | raise Exception('image width must be a multiple of {}'.format(PATTERN_WIDTH)) 90 | if img.size[1] % PATTERN_HEIGHT != 0: 91 | raise Exception('image height must be a multiple of {}'.format(PATTERN_HEIGHT)) 92 | 93 | pattern_strs = [] 94 | row_strs = ['']*PATTERN_HEIGHT 95 | 96 | if DEINTERLEAVE_16X8_HALVES: 97 | alt_row_strs = ['']*PATTERN_HEIGHT 98 | else: 99 | alt_row_strs = [] 100 | 101 | 102 | img = img.convert(mode='L') 103 | pattern_num = 0 104 | for py in xrange(0, img.size[1], PATTERN_HEIGHT): 105 | for px in xrange(0, img.size[0], PATTERN_WIDTH): 106 | rows = [to_row(img, px, py+n) for n in xrange(0, PATTERN_HEIGHT)] 107 | for r in xrange(0,PATTERN_HEIGHT): 108 | row = rows[r] 109 | row = revb(row,8) if X_FLIP else row 110 | row_str = row_genfunc(row)+'\n' 111 | if not DEINTERLEAVE_16X8_HALVES: 112 | row_strs[r] += row_str 113 | else: 114 | if px%(PATTERN_WIDTH*2) == PATTERN_WIDTH: 115 | alt_row_strs[r] += row_str 116 | else: 117 | row_strs[r] += row_str 118 | pattern_strs.append(genfunc(pattern_num, rows)) 119 | pattern_num += 1 120 | 121 | 122 | output = header(fontname) #'#include \nconst uint8_t '+fontname+'[] __attribute__((aligned(8))) = {\n' 123 | # output += '\n'.join(pattern_strs) 124 | if not PACK: 125 | output += '\n'.join(reversed(row_strs)) 126 | output += '\n'.join(reversed(alt_row_strs)) 127 | else: 128 | output += '\n'.join(pattern_strs) 129 | output += footer 130 | 131 | with open(outfile, 'w') as f: 132 | f.write(output) 133 | -------------------------------------------------------------------------------- /scripts/genhsvtable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from img2bin import load_colors_from_gimp_palette 4 | import colorsys 5 | 6 | rgb_colors = load_colors_from_gimp_palette('palettes/8bit.gpl') 7 | 8 | ihsvrgb_colors = [] 9 | 10 | def color_distance(ihsvrgb, h2, s2, v2): 11 | _, h1, s1, v1, _, _, _ = ihsvrgb 12 | return (2*(h2-h1))**2 + (s2-s1)**2 + (v2-v1)**2 13 | 14 | for i, c in enumerate(rgb_colors): 15 | r, g, b = c 16 | h, s, v = colorsys.rgb_to_hsv(r/255.,g/255.,b/255.) 17 | ihsvrgb_colors.append((i, h, s, v, r, g, b)) 18 | 19 | 20 | h_vals = [0., 1/15., 2/15., 3/15., 4/15., 5/15., 6/15., 7/15., 21 | 8/15., 9/15., 10/15., 11/15., 12/15., 13/15., 14/15., 1] 22 | s_vals = [0.3, 0.6, 0.8, 1.0] 23 | v_vals = [0.3, 0.6, 0.8, 1.0] 24 | 25 | # print 'GIMP Palette' 26 | # print 'Name: NTSC 8-bit HSV' 27 | # print 'Columns: 8' 28 | # for v in v_vals: 29 | # for s in s_vals: 30 | # for h in h_vals: 31 | # i, _, _, _, r, g, b = min(ihsvrgb_colors, key=lambda x: color_distance(x,h,s,v)) 32 | # print '{} {} {}'.format(r,g,b) 33 | 34 | print ".section .progmem.assets" 35 | print ".balign 256" 36 | print ".global hsvtable" 37 | print ".type hsvtable, @object" 38 | print "hsvtable:" 39 | for v in v_vals: 40 | for s in s_vals: 41 | for h in h_vals: 42 | i, _, _, _, r, g, b = min(ihsvrgb_colors, key=lambda x: color_distance(x,h,s,v)) 43 | print ' .byte {}'.format(i) 44 | -------------------------------------------------------------------------------- /scripts/gentrigtable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from math import pi, cos 4 | 5 | print ".section .progmem.assets" 6 | print ".balign 256" 7 | print ".global cosinetable" 8 | print ".type cosinetable, @object" 9 | print "cosinetable:" 10 | for i in xrange(0,256): 11 | radians = (i/256.)*2*pi 12 | cosval = 32767*cos(radians) 13 | cosintval = int(round(cosval)) 14 | print " .byte lo8({:6}) ; i={:3} theta={:.5f} cos(theta)*32767={:.3f}".format(cosintval, i, radians, cosval) 15 | for i in xrange(0,256): 16 | radians = (i/256.)*2*pi 17 | cosval = 32767*cos(radians) 18 | cosintval = int(round(cosval)) 19 | print " .byte hi8({:6}) ; i={:3} theta={:.5f} cos(theta)*32767={:.3f}".format(cosintval, i, radians, cosval) 20 | 21 | -------------------------------------------------------------------------------- /scripts/serial-slideshow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import serial 4 | import time 5 | import glob 6 | import os 7 | 8 | from img2bin import load_gimp_palette, image_file_to_bin 9 | 10 | DIR = '/Users/matt/slideshow/' 11 | images = [] 12 | images.extend(glob.glob(DIR+'*.jpg')) 13 | images.extend(glob.glob(DIR+'*.jpeg')) 14 | images.extend(glob.glob(DIR+'*.png')) 15 | images.extend(glob.glob(DIR+'*.gif')) 16 | images = sorted(images) 17 | print len(images) 18 | 19 | BAUD = 57600 20 | 21 | palette, num_colors = load_gimp_palette('palettes/8bit.gpl') 22 | 23 | def tobin(filename): 24 | aspectcrop = not os.path.splitext(filename)[0].endswith('-nocrop') 25 | image, _ = image_file_to_bin( 26 | palette=palette, 27 | num_colors=num_colors, 28 | infile=filename, 29 | outsize=(160,100), 30 | pxaspectratio=1, 31 | aspectcrop=aspectcrop, 32 | revbits=False, 33 | revpixels=False) 34 | return image 35 | 36 | 37 | with serial.Serial('/dev/cu.usbserial-DN05KLWU', BAUD) as port: 38 | n = 0 39 | raw_input('press enter when ready:') 40 | while True: 41 | bdata = tobin(images[n]) 42 | port.write(bdata) 43 | n += 1 44 | time.sleep(1.5) 45 | -------------------------------------------------------------------------------- /scripts/serial-stress-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import serial 4 | import time 5 | 6 | BAUD = 57600 7 | 8 | with serial.Serial('/dev/cu.usbserial-DN05KLWU', BAUD) as port: 9 | time.sleep(5.0) 10 | n = 0 11 | while True: 12 | port.write(bytearray([n & 0xFF])) 13 | n += 1 14 | 15 | -------------------------------------------------------------------------------- /scripts/serialconsole.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | Label 36 | serialconsole 37 | ProgramArguments 38 | 39 | /usr/libexec/getty 40 | usbserial 41 | cu.usbserial-DN03LPEQ 42 | 43 | KeepAlive 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /scripts/sortpalette.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import colorsys 4 | import sys 5 | from math import sqrt, pow 6 | from collections import namedtuple 7 | from operator import attrgetter 8 | from img2bin import load_colors_from_gimp_palette 9 | 10 | HSYI = namedtuple('HSYI', 'h s y index') 11 | 12 | def write_palette_preview(colors, filename): 13 | from PIL import Image 14 | boxw, boxh = 16, 16 15 | cols = int(sqrt(len(colors))) 16 | preview = Image.new('RGB', (cols*boxw, cols*boxh)) 17 | for n, rgb in enumerate(colors): 18 | box = Image.new('RGB', (boxw, boxh), tuple(rgb)) 19 | preview.paste(box, ((n%cols)*boxw, (n/cols)*boxh)) 20 | preview.save(filename) 21 | 22 | hsv_colors = [] 23 | if __name__ == '__main__': 24 | colors = load_colors_from_gimp_palette(sys.argv[1]) 25 | for n, rgb in enumerate(colors): 26 | normalized_rgb = tuple(c/255. for c in rgb) 27 | h, s, _ = colorsys.rgb_to_hsv(*normalized_rgb) 28 | y, _, _ = colorsys.rgb_to_yiq(*normalized_rgb) 29 | hsv_colors.append(HSYI(h,s,y,n)) 30 | 31 | hsv_colors = sorted(hsv_colors, key=attrgetter('h')) 32 | 33 | for r in xrange(0,16): 34 | i, j = r*16, (r+1)*16 35 | hsv_colors[i:j] = sorted(hsv_colors[i:j], key=attrgetter('s')) 36 | hsv_colors[i:j] = sorted(hsv_colors[i:j], key=attrgetter('y')) 37 | 38 | sorted_colors = [[0,0,0]]*256 39 | sorted_color_indexes = [0]*256 40 | for i, hsvi in enumerate(hsv_colors): 41 | sorted_colors[i] = colors[hsvi.index] 42 | sorted_color_indexes[i] = hsvi.index 43 | 44 | print sorted_color_indexes 45 | write_palette_preview(sorted_colors, 'palprev.png') 46 | -------------------------------------------------------------------------------- /scripts/to16colorbin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | from PIL import Image 6 | 7 | # Initialize palette 8 | # These are the "updated" values from the "Apple II Video Display Theory" 9 | # spreadsheet: 10 | # https://docs.google.com/spreadsheets/d/1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/ 11 | 12 | PALETTE = [ 13 | 0, 0, 0, # Black 14 | 167, 11, 64, # Red 15 | 64, 28, 247, # Dark Blue 16 | 230, 40, 255, # Purple 17 | 0, 116, 64, # Dark Green 18 | 128, 128, 128, # Gray 1 19 | 25, 144, 255, # Medium Blue 20 | 191, 156, 255, # Light Blue 21 | 64, 99, 0, # Brown 22 | 230, 111, 0, # Orange 23 | 128, 128, 128, # Gray 2 24 | 255, 139, 191, # Pink 25 | 25, 215, 0, # Light Green 26 | 191, 227, 8, # Yellow 27 | 88, 244, 191, # Aqua 28 | 255, 255, 255, # White 29 | ] + [0,0,0]*240 30 | pal = Image.new("P", (1,1), 0) 31 | pal.putpalette(PALETTE) 32 | 33 | infile = sys.argv[1] 34 | parts = os.path.splitext(infile) 35 | previewfile = parts[0] + '-16color.png' 36 | binfile = parts[0] + '.bin' 37 | 38 | # Load image 39 | image = Image.open(infile) 40 | image = image.convert("RGB") 41 | width, height = image.size 42 | aspect = width / float(height) 43 | 44 | # Crop/letterbox to 1.6:1 aspect ratio 45 | target_width = 320 46 | target_height = 200 47 | target_aspect = target_width / float(target_height) 48 | if aspect > target_aspect: 49 | new_width = int(target_aspect * height) 50 | offset = (width - new_width) / 2 51 | resize = (offset, 0, width - offset, height) 52 | else: 53 | new_height = int(width / target_aspect) 54 | offset = (height - new_height) / 2 55 | resize = (0, offset, width, height - offset) 56 | 57 | # Reduce horizontal resolution by half 58 | # (effective chroma resolution is 160 pixels) 59 | resized = image.crop(resize).resize((target_width/2, target_height), Image.ANTIALIAS) 60 | 61 | # Quantize to color palette 62 | quantized = resized.quantize(palette=pal) 63 | 64 | # Save preview file: expand preview back to full width 65 | preview = quantized.resize((target_width, target_height), Image.NEAREST) 66 | preview.save(previewfile) 67 | 68 | # Convert to binary 69 | binary = bytearray() 70 | w, h = quantized.size 71 | for y in xrange(0,h): 72 | for x in xrange(0,w,2): 73 | p0 = quantized.getpixel((x,y)) 74 | p1 = quantized.getpixel((x+1,y)) 75 | b = ((p1 & 0xF) << 4) | (p0 & 0xF) 76 | binary.append(b) 77 | with open(binfile, 'wb') as f: 78 | f.write(binary) 79 | 80 | -------------------------------------------------------------------------------- /scripts/tone-freqs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # freq = Fcpu / (P * (2*n)), where P = 1, 8, 64, 256, or 1024 4 | # n = Fcpu / (2*freq*P) 5 | 6 | Fcpu = 14318182. 7 | 8 | PRESCALERS = [8, 64, 256, 1024] 9 | NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] 10 | 11 | # see datasheet, Table 16-10 12 | PRESCALER_BIT_PATTERNS = { 13 | 0: '0', 14 | 1: '_BV(CS00)', 15 | 8: '_BV(CS01)', 16 | 64: '_BV(CS01)|_BV(CS00)', 17 | 256: '_BV(CS02)', 18 | 1024: '_BV(CS02)|_BV(CS00)' 19 | } 20 | 21 | def note_to_freq(n): 22 | return (2**((n-69)/12.)) * 440. 23 | 24 | def freq_to_timer_params(freq): 25 | for P in PRESCALERS: 26 | n = Fcpu / (2*freq*P) 27 | if n >= 1.5 and n <= 255.5: 28 | return (P, int(round(n))) 29 | return (0,0) 30 | 31 | def timer_params_to_freq(P, n): 32 | if P==0 or n==0: 33 | return 0 34 | return Fcpu / (P * (2.*n)) 35 | 36 | def percent_error(exact, approx): 37 | return 100.*(abs(approx-exact)/exact) 38 | 39 | def note_name(n): 40 | octave = -2 + (n/12) 41 | octavestr = str(octave) 42 | name = NOTE_NAMES[n%12] 43 | return name+octavestr 44 | 45 | def tone_to_timer_params(period): 46 | if period < 256: 47 | return 8, period 48 | elif period < 480: 49 | return 64, period-224 50 | elif period < 672: 51 | return 256, period-416 52 | else: 53 | return 1024, period-608 54 | 55 | for notenum in xrange(0,128): 56 | exact_freq = note_to_freq(notenum) 57 | P, n = freq_to_timer_params(exact_freq) 58 | approx_freq = timer_params_to_freq(P, n) 59 | if P == 0 or n == 0: 60 | print ' {{ .tccrb=_BV(WGM02), .ocra=0 }}, /* {} ({}) */'.format(notenum, note_name(notenum)) 61 | else: 62 | print ' {{ .tccrb=_BV(WGM02)|{}, .ocra={} }}, /* {} ({}), approx {:.3f} Hz ({:.2f}% error) */'.format( 63 | PRESCALER_BIT_PATTERNS[P], n, notenum, note_name(notenum), 64 | approx_freq, percent_error(exact=exact_freq, approx=approx_freq)) 65 | 66 | # for t in xrange(0,864): 67 | # P, n = tone_to_timer_params(t) 68 | # print '{},{}'.format(t, timer_params_to_freq(P, n)) 69 | 70 | # for P in PRESCALERS: 71 | # for n in xrange(0,256): 72 | # freq = timer_params_to_freq(P,n) 73 | # print '{:10.3f}'.format(freq), '\t', P, '\t', n 74 | -------------------------------------------------------------------------------- /sim/Makefile: -------------------------------------------------------------------------------- 1 | # Basic UART-only simulator for Amethyst, using simavr 2 | # (https://github.com/buserror/simavr) 3 | # Starts a picocom session hooked up to USART1 on the simulated AVR, allowing 4 | # interaction via serial. Works well with the `forth_serial` app. 5 | PROGRAM_NAME = amsim 6 | SRC_OBJ = uart_pty.o sim.o 7 | 8 | OBJDIR = build 9 | INCLUDE_DIRS = /usr/local/include/simavr ./include 10 | LIB_DIRS = 11 | RPATH = 12 | LIBS = elf simavr 13 | 14 | OPTIMIZE_FLAGS += -Os 15 | DEBUG_FLAGS += -g3 -fno-omit-frame-pointer 16 | CPPFLAGS += -DDEBUG=1 17 | 18 | CFLAGS_COMMON = $(OPTIMIZE_FLAGS) $(DEBUG_FLAGS) -Wall -Wno-missing-braces -MMD -MP 19 | CFLAGS += $(CFLAGS_COMMON) -std=c11 20 | CXXFLAGS += $(CFLAGS_COMMON) -std=c++17 21 | 22 | CPPFLAGS += $(foreach includedir,$(INCLUDE_DIRS),-I$(includedir)) 23 | 24 | LDFLAGS += $(foreach libdir,$(LIB_DIRS),-L$(libdir)) 25 | LDFLAGS += $(foreach lib,$(LIBS),-l$(lib)) 26 | LDFLAGS += $(RPATH) 27 | LDFLAGS += $(DEBUG_FLAGS) 28 | 29 | OBJ = $(addprefix $(OBJDIR)/,$(SRC_OBJ)) 30 | DEPS = $(OBJ:.o=.d) 31 | 32 | .PHONY: all clean 33 | 34 | all: $(PROGRAM_NAME) 35 | 36 | $(PROGRAM_NAME): $(OBJ) 37 | $(CC) $(OBJ) $(LDFLAGS) -o $(PROGRAM_NAME) 38 | 39 | $(OBJ): | $(OBJDIR) 40 | 41 | $(OBJDIR)/%.o: %.c 42 | $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< 43 | 44 | $(OBJDIR)/%.o: %.cpp 45 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $< 46 | 47 | $(OBJDIR): 48 | mkdir -p $(OBJDIR) 49 | 50 | clean: 51 | rm -f $(PROGRAM_NAME) 52 | rm -rf $(OBJDIR) 53 | rm -rf *.dSYM 54 | 55 | -include $(DEPS) 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /sim/uart_pty.h: -------------------------------------------------------------------------------- 1 | /* 2 | uart_pty.h 3 | 4 | Copyright 2012 Michel Pollet 5 | 6 | This file is part of simavr. 7 | 8 | simavr is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | simavr is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with simavr. If not, see . 20 | */ 21 | 22 | 23 | #ifndef __UART_PTY_H___ 24 | #define __UART_PTY_H___ 25 | 26 | #include 27 | #include "sim_irq.h" 28 | #include "fifo_declare.h" 29 | 30 | enum { 31 | IRQ_UART_PTY_BYTE_IN = 0, 32 | IRQ_UART_PTY_BYTE_OUT, 33 | IRQ_UART_PTY_COUNT 34 | }; 35 | 36 | DECLARE_FIFO(uint8_t,uart_pty_fifo, 512); 37 | 38 | typedef struct uart_pty_port_t { 39 | int tap : 1, crlf : 1; 40 | int s; // socket we chat on 41 | char slavename[64]; 42 | uart_pty_fifo_t in; 43 | uart_pty_fifo_t out; 44 | uint8_t buffer[512]; 45 | size_t buffer_len, buffer_done; 46 | } uart_pty_port_t, *uart_pty_port_p; 47 | 48 | typedef struct uart_pty_t { 49 | avr_irq_t * irq; // irq list 50 | struct avr_t *avr; // keep it around so we can pause it 51 | 52 | pthread_t thread; 53 | int xon; 54 | 55 | union { 56 | struct { 57 | uart_pty_port_t pty; 58 | uart_pty_port_t tap; 59 | }; 60 | uart_pty_port_t port[2]; 61 | }; 62 | } uart_pty_t; 63 | 64 | void 65 | uart_pty_init( 66 | struct avr_t * avr, 67 | uart_pty_t * b); 68 | void 69 | uart_pty_stop(uart_pty_t * p); 70 | 71 | void 72 | uart_pty_connect( 73 | uart_pty_t * p, 74 | char uart); 75 | 76 | #endif /* __UART_PTY_H___ */ 77 | -------------------------------------------------------------------------------- /system/app.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /* Exposed by the linker */ 8 | extern const struct app_def __app_index_start; 9 | extern const struct app_def __app_index_end; 10 | 11 | const struct app_def *app_index_next(const struct app_def *app) 12 | { 13 | if (!app) { 14 | return &__app_index_start; 15 | } else if (app >= &__app_index_end) { 16 | return NULL; 17 | } else { 18 | const char *app_byteptr = (const char *)app; 19 | /* advance past the fixed-length fields */ 20 | app_byteptr += sizeof(struct app_def); 21 | /* advance past the name; add 1 for the null byte */ 22 | app_byteptr += strlen_P(app_byteptr)+1; 23 | /* return NULL if there are no more apps */ 24 | if (app_byteptr >= (const char *)&__app_index_end) { 25 | return NULL; 26 | } else { 27 | return (const struct app_def *)app_byteptr; 28 | } 29 | } 30 | } 31 | 32 | 33 | const struct app_def *app_index_nth(uint8_t n) 34 | { 35 | const struct app_def *app = NULL; 36 | do { 37 | app = app_index_next(app); 38 | } while (n--); 39 | return app; 40 | } 41 | 42 | 43 | PGM_P app_name(const struct app_def *app) { 44 | return (PGM_P)&(app->appname); 45 | } 46 | 47 | 48 | uint32_t app_data_load_start(const struct app_def *app) { 49 | return pgm_read_dword(&(app->data_load_start)); 50 | } 51 | 52 | 53 | uint16_t app_data_end(const struct app_def *app) { 54 | return pgm_read_word(&(app->data_end)); 55 | } 56 | 57 | 58 | uint16_t app_bss_end(const struct app_def *app) { 59 | return pgm_read_word(&(app->bss_end)); 60 | } 61 | 62 | 63 | /** 64 | * Soft-reset the system. 65 | * (assumes the startup code and/or bootloader will disable the wdt on reset) 66 | */ 67 | void app_quit(void) { 68 | cli(); 69 | wdt_enable(WDTO_15MS); 70 | while (1) {} 71 | } 72 | -------------------------------------------------------------------------------- /system/app_launch.S: -------------------------------------------------------------------------------- 1 | ; This function does not return, so it doesn't care about clobbering the 2 | ; caller-saved registers. 3 | ; Much of the implementation is adapted from libgcc's __do_clear_bss and 4 | ; __do_copy_data. 5 | #include 6 | .text 7 | .global app_launch 8 | app_launch: 9 | cli ; interrupts must be reenabled by the app 10 | clr r1 ; make sure the zero register is clear 11 | ; get entry point 12 | movw Z, r24 13 | lpm r8, Z+ ; entry point low byte 14 | lpm r9, Z+ ; entry point high byte 15 | ; get data load start 16 | lpm r10, Z+ ; data load start address low byte 17 | lpm r11, Z+ ; data load start address middle byte 18 | lpm r0, Z+ ; data load start address high byte 19 | adiw Z, 1 ; skip padding byte 20 | ; get data end 21 | lpm r12, Z+ ; data end address low byte 22 | lpm r13, Z+ ; data end address high byte 23 | ; get bss end 24 | lpm r14, Z+ ; bss end address low byte 25 | lpm r15, Z+ ; bss end address high byte 26 | ; copy data from flash to RAM 27 | ldi XL, lo8(__app_data_start) 28 | ldi XH, hi8(__app_data_start) 29 | out _SFR_IO_ADDR(RAMPZ), r0 30 | movw Z, r10 31 | rjmp .copy_data_start 32 | .copy_data_loop: 33 | elpm r0, Z+ 34 | st X+, r0 35 | .copy_data_start: 36 | cp XL, r12 ; compare current address with end address 37 | cpc XH, r13 38 | brne .copy_data_loop 39 | ; clear bss 40 | rjmp .clear_bss_start 41 | .clear_bss_loop: 42 | st X+, r1 43 | .clear_bss_start: 44 | cp XL, r14 45 | cpc XH, r15 46 | brne .clear_bss_loop 47 | ; clear SREG and reset stack/frame pointers 48 | out _SFR_IO_ADDR(SREG), r1 49 | out _SFR_IO_ADDR(RAMPZ), r1 50 | ldi YL, lo8(__stack) 51 | ldi YH, hi8(__stack) 52 | out _SFR_IO_ADDR(SPL), YL 53 | out _SFR_IO_ADDR(SPH), YH 54 | ; off we go! 55 | movw Z, r8 56 | ijmp 57 | -------------------------------------------------------------------------------- /system/asm_macros.inc: -------------------------------------------------------------------------------- 1 | #define ALWAYS_ZERO_IOREG _SFR_IO_ADDR(DDRA) 2 | 3 | ; Clear a register without affecting SREG flags. 4 | ; Relies on keeping one I/O register always set to zero. 5 | .macro clrnf reg 6 | in \reg, ALWAYS_ZERO_IOREG 7 | .endm 8 | 9 | ; "Returns" from a video mode handler. 10 | .macro fhret 11 | rjmp framehandler_end 12 | .endm 13 | 14 | .macro lhret 15 | rjmp linehandler_end 16 | .endm 17 | 18 | ; A 2-cycle nop that only takes up one word of flash. 19 | .macro short_nop2 20 | rjmp . 21 | .endm 22 | 23 | ; A dummy lpm can be used as a 3-cycle nop that only takes up one word of flash. 24 | ; Clobbers ZL. Does not affect flags 25 | .macro short_nop3 26 | lpm ZL, Z 27 | .endm 28 | 29 | ; A 3-cycle nop that takes up two words of flash. 30 | .macro nop3 31 | jmp . 32 | .endm 33 | 34 | ; A 4-cycle nop that takes up two words of flash. 35 | .macro short_nop4 36 | short_nop2 37 | short_nop2 38 | .endm 39 | 40 | ; A 5-cycle nop that takes up two words of flash. 41 | .macro short_nop5 42 | short_nop3 43 | short_nop2 44 | .endm 45 | -------------------------------------------------------------------------------- /system/audio.c: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | #include "defs.h" 3 | #include "isr_reserved_registers.h" 4 | #include "note_freqs.h" 5 | 6 | void audio_init() 7 | { 8 | /* Timer0 and OC0B is used to generate audio */ 9 | DDRB |= _BV(4); 10 | OCR0A = 0; 11 | OCR0B = 0; 12 | TCCR0A = _BV(COM0B1) | _BV(WGM00); 13 | TCCR0B = _BV(WGM02) | _BV(CS02); /* default to 1/256 prescaler */ 14 | } 15 | 16 | 17 | void audio_off() 18 | { 19 | /* disable audio pin output, but keep the timer configured */ 20 | DDRB &= ~_BV(4); 21 | } 22 | 23 | 24 | void tone(uint16_t period, uint8_t duration, uint8_t duty_cycle) 25 | { 26 | /* Calculate prescaler and OCR0A values. */ 27 | uint8_t tccrb, ocra, ocrb; 28 | /* Frequencies 0-255 use 1/8 prescaler */ 29 | if (period < 256) { 30 | tccrb = _BV(WGM02) | _BV(CS01); 31 | ocra = period; 32 | } 33 | /* Frequencies 256-479 use 1/64 prescaler */ 34 | else if (period < 480) { 35 | tccrb = _BV(WGM02) | _BV(CS01) | _BV(CS00); 36 | ocra = period-224; 37 | } 38 | /* Frequencies 480-671 use 1/256 prescaler */ 39 | else if (period < 672) { 40 | tccrb = _BV(WGM02) | _BV(CS02); 41 | ocra = period-416; 42 | } 43 | /* Frequencies 672-863 use 1/1024 prescaler */ 44 | /* Behavior of frequencies 864 and above is undefined */ 45 | else { 46 | tccrb = _BV(WGM02) | _BV(CS02) | _BV(CS00); 47 | ocra = period-608; 48 | } 49 | 50 | /* Convert duty cycle percentage to OCR0B value */ 51 | ocrb = (ocra*duty_cycle) >> 8; 52 | 53 | /* Execution order is important here. The duration needs to be set first. */ 54 | /* Otherwise, if frequency is set first, and the tone generator is already */ 55 | /* active, and you get a frame interrupt immediately afterward that causes */ 56 | /* tone_duration to be decremented to zero, OCR0A and OCR0B will */ 57 | /* immediately get overwritten with zeros, causing no sound output. */ 58 | tone_duration = duration; 59 | TCCR0B = tccrb; 60 | OCR0A = ocra; 61 | OCR0B = ocrb; 62 | set_bit(tone_enabled, tone_enabled_bit); 63 | } 64 | 65 | 66 | void note(enum note_number note_num, uint8_t duration, uint8_t duty_cycle) 67 | { 68 | note_num &= 0x7f; /* ignore high bit */ 69 | note_def nd = { .n = pgm_read_word(note_freqs+note_num) }; 70 | uint8_t ocrb = (nd.ocra*duty_cycle) >> 8; 71 | tone_duration = duration; 72 | TCCR0B = nd.tccrb; 73 | OCR0A = nd.ocra; 74 | OCR0B = ocrb; 75 | set_bit(tone_enabled, tone_enabled_bit); 76 | } 77 | -------------------------------------------------------------------------------- /system/fastrand.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple but fast 16-bit pseudorandom number generator. 3 | * The default avr-libc implementation of rand() uses division/modulo, which 4 | * isn't quick, and requires bringing in parts of libgcc which otherwise might 5 | * not be needed. 6 | */ 7 | #include 8 | 9 | extern uint16_t seed; 10 | 11 | /* From Leo Brodie's "Starting Forth." */ 12 | int fastrand(void) { 13 | seed = (seed * 31421) + 6927; 14 | return seed; 15 | } 16 | 17 | void sfastrand(unsigned s) { 18 | seed = s; 19 | } 20 | -------------------------------------------------------------------------------- /system/main.c: -------------------------------------------------------------------------------- 1 | #include "app.h" 2 | #include 3 | 4 | #ifdef STARTUP_APP 5 | #define STARTUP_APP_DEF PASTE_(APP_DEF_,STARTUP_APP) 6 | #else 7 | #error No STARTUP_APP defined 8 | #endif 9 | 10 | extern const struct app_def STARTUP_APP_DEF; 11 | 12 | /* for simavr */ 13 | #include "avr_mcu_section.h" 14 | AVR_MCU(F_CPU, "atmega1284"); 15 | 16 | /* Disable watchdog timer early in startup, so soft-resets don't lock the AVR */ 17 | /* (see avr-libc FAQ) */ 18 | void wdt_init(void) __attribute__((naked)) __attribute__((section(".init3"))); 19 | void wdt_init(void) { 20 | MCUSR = 0; 21 | wdt_disable(); 22 | } 23 | 24 | 25 | int main() 26 | { 27 | /* disable unused peripherals */ 28 | PRR0 = _BV(PRTWI)|_BV(PRADC); 29 | ACSR = _BV(ACD); 30 | video_init(); 31 | audio_init(); 32 | spi_init(); 33 | app_launch(&STARTUP_APP_DEF); 34 | } 35 | -------------------------------------------------------------------------------- /system/minio.c: -------------------------------------------------------------------------------- 1 | #include "minio.h" 2 | 3 | void mio_nl(void) { 4 | mio_putc('\n'); 5 | } 6 | 7 | 8 | void mio_bl(void) { 9 | mio_putc(' '); 10 | } 11 | 12 | 13 | void mio_puts(const char *s) { 14 | char c; 15 | while ((c = *s++)) { mio_putc(c); } 16 | } 17 | 18 | 19 | void mio_puts_P(const char * PROGMEM s) { 20 | char c; 21 | while ((c = pgm_read_byte(s++))) { mio_putc(c); } 22 | } 23 | 24 | 25 | void mio_putsnl(const char *s) { 26 | mio_puts(s); 27 | mio_nl(); 28 | } 29 | 30 | 31 | void mio_putsnl_P(const char * PROGMEM s) { 32 | mio_puts_P(s); 33 | mio_nl(); 34 | } 35 | 36 | 37 | void mio_putns(const char *s, uint16_t nbytes) { 38 | while (nbytes--) { mio_putc(*s++); } 39 | } 40 | 41 | void mio_putns_P(const char *s, uint16_t nbytes) { 42 | while (nbytes--) { mio_putc(pgm_read_byte(s++)); } 43 | } 44 | 45 | 46 | void mio_x8(uint8_t n) { 47 | uint8_t lonib = n & 0b00001111; 48 | uint8_t hinib = (n & 0b11110000) >> 4; 49 | lonib += (lonib <= 9) ? 0 : 7; 50 | hinib += (hinib <= 9) ? 0 : 7; 51 | mio_putc('0'+hinib); 52 | mio_putc('0'+lonib); 53 | } 54 | 55 | 56 | void mio_x16(uint16_t n) { 57 | mio_x8(n>>8); 58 | mio_x8(n&0xFF); 59 | } 60 | -------------------------------------------------------------------------------- /system/minio_decimal.S: -------------------------------------------------------------------------------- 1 | ; Minimal functions for printing 8- and 16-bit integers as signed or 2 | ; unsigned decimal. 3 | .text 4 | 5 | .global mio_d8 6 | mio_d8: 7 | tst r24 8 | brpl mio_u8 9 | push r28 10 | mov r28, r24 11 | neg r28 12 | ldi r24, '-' 13 | lds ZL, mio_putc 14 | lds ZH, mio_putc+1 15 | icall 16 | rjmp .mio_u8 17 | 18 | .global mio_u8 19 | mio_u8: 20 | push r28 21 | mov r28, r24 22 | .mio_u8: 23 | cpi r28, 10 24 | brlo .ones 25 | cpi r28, 100 26 | brlo .tens 27 | .hundreds_u8: 28 | ldi r24, '/' 29 | 1: inc r24 30 | subi r28, 100 31 | brsh 1b 32 | lds ZL, mio_putc 33 | lds ZH, mio_putc+1 34 | icall 35 | subi r28, -100 36 | .tens: 37 | ldi r24, '/' 38 | 2: inc r24 39 | subi r28, 10 40 | brsh 2b 41 | lds ZL, mio_putc 42 | lds ZH, mio_putc+1 43 | icall 44 | subi r28, -10 45 | .ones: 46 | mov r24, r28 47 | subi r24, -'0' 48 | pop r28 49 | lds ZL, mio_putc 50 | lds ZH, mio_putc+1 51 | ijmp 52 | 53 | .global mio_d16 54 | mio_d16: 55 | tst r25 56 | breq mio_u8 ; defer to 8-bit function if <= 255 57 | brpl .mio_u16 ; nothing to do if already positive 58 | ; otherwise, print minus sign and take two's complement of value 59 | com r25 60 | neg r24 61 | sbci r25, -1 62 | push r24 63 | push r25 64 | ldi r24, '-' ; print minus sign 65 | lds ZL, mio_putc 66 | lds ZH, mio_putc+1 67 | icall 68 | pop r25 69 | pop r24 70 | rjmp mio_d16 71 | 72 | .global mio_u16 73 | mio_u16: 74 | ; defer to 8-bit function if <= 255 75 | tst r25 76 | breq mio_u8 77 | .mio_u16: 78 | push r28 79 | push r29 80 | movw r28, r24 81 | ldi r25, hi8(1000) 82 | cpi r28, lo8(1000) 83 | cpc r29, r25 84 | brlo .hundreds_u16 85 | ldi r25, hi8(10000) 86 | cpi r28, lo8(10000) 87 | cpc r29, r25 88 | brlo .thousands 89 | .tenthousands: 90 | ldi r24, '/' 91 | 3: inc r24 92 | subi r28, lo8(10000) 93 | sbci r29, hi8(10000) 94 | brsh 3b 95 | lds ZL, mio_putc 96 | lds ZH, mio_putc+1 97 | icall 98 | subi r28, lo8(-10000) 99 | sbci r29, hi8(-10000) 100 | .thousands: 101 | ldi r24, '/' 102 | 4: inc r24 103 | subi r28, lo8(1000) 104 | sbci r29, hi8(1000) 105 | brsh 4b 106 | lds ZL, mio_putc 107 | lds ZH, mio_putc+1 108 | icall 109 | subi r28, lo8(-1000) 110 | sbci r29, hi8(-1000) 111 | .hundreds_u16: 112 | ldi r24, '/' 113 | 5: inc r24 114 | subi r28, lo8(100) 115 | sbci r29, hi8(100) 116 | brsh 5b 117 | lds ZL, mio_putc 118 | lds ZH, mio_putc+1 119 | icall 120 | subi r28, lo8(-100) 121 | sbci r29, hi8(-100) 122 | pop r29 123 | rjmp .tens 124 | -------------------------------------------------------------------------------- /system/minio_handlers.S: -------------------------------------------------------------------------------- 1 | .global mio_putc 2 | .global mio_getc 3 | .comm mio_putc, 2 4 | .comm mio_getc, 2 5 | -------------------------------------------------------------------------------- /system/randseed.S: -------------------------------------------------------------------------------- 1 | ; 16 bits in the reserved system RAM region that can be used for the state of a 2 | ; random number generator. 3 | .global seed 4 | .comm seed, 2 5 | -------------------------------------------------------------------------------- /system/readline.c: -------------------------------------------------------------------------------- 1 | #include "readline.h" 2 | #include 3 | 4 | int readline(char *inbuf, int bufsize) 5 | { 6 | int len = 0; 7 | while (1) { 8 | char c = mio_waitc(); 9 | switch (c) { 10 | /* Newline finishes */ 11 | case '\r': 12 | case '\n': 13 | inbuf[len] = '\0'; 14 | return len; 15 | /* Esc aborts */ 16 | case '\e': 17 | return -1; 18 | /* Form feed (Ctrl-L) clears screen and aborts */ 19 | case '\f': 20 | Pc(c); 21 | return -2; 22 | /* Backspace and DEL delete a character */ 23 | case '\b': 24 | case '\x7f': 25 | if (len) { 26 | Psl("\b \b"); 27 | len--; 28 | inbuf[len] = 0; 29 | } 30 | break; 31 | /* Otherwise, add character to the input buffer and print it */ 32 | default: 33 | if (len < bufsize) { 34 | inbuf[len++] = c; 35 | Pc(c); 36 | } 37 | } 38 | } 39 | } 40 | --------------------------------------------------------------------------------