├── i25.png ├── qrc1.png ├── qrc11.png ├── i25zxs.png ├── qrc1zxs.png ├── src ├── debug │ ├── pr11.bin │ ├── Makefile │ ├── encode.asm │ ├── proxyud.c │ ├── access.lua │ ├── qrc11.lua │ ├── pr11.lua │ ├── imgcreate.c │ ├── z80dasm.h │ └── z80.c ├── qrc1_ram.asm ├── zx81 │ ├── qrc1.bas │ ├── Makefile │ ├── i25.bas │ ├── i25.asm │ ├── buildp.lua │ ├── plot.asm │ └── qrc1.asm ├── spectrum │ ├── qrc1.bas │ ├── qrc11.bas │ ├── i25.bas │ ├── Makefile │ ├── i25.asm │ ├── qrc1.asm │ ├── qrc11.asm │ ├── buildtap.lua │ └── plot.asm ├── qrc11_ram.asm ├── i25_rom.asm ├── qrc1_rom.asm └── qrc11_rom.asm ├── etc ├── Makefile ├── writeall.lua ├── readall.lua ├── lstsym.lua ├── baspp.lua ├── qrc1.c └── zxtext2p.c ├── .gitignore ├── LICENSE └── README.md /i25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/i25.png -------------------------------------------------------------------------------- /qrc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/qrc1.png -------------------------------------------------------------------------------- /qrc11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/qrc11.png -------------------------------------------------------------------------------- /i25zxs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/i25zxs.png -------------------------------------------------------------------------------- /qrc1zxs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/qrc1zxs.png -------------------------------------------------------------------------------- /src/debug/pr11.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiradel/qrc1/HEAD/src/debug/pr11.bin -------------------------------------------------------------------------------- /etc/Makefile: -------------------------------------------------------------------------------- 1 | all: qrc1 zxtext2p 2 | 3 | qrc1: qrc1.c 4 | gcc -O2 -o $@ $+ -lm 5 | 6 | zxtext2p: zxtext2p.c 7 | gcc -O2 -o $@ $+ 8 | 9 | clean: FORCE 10 | rm -f qrc1 zxtext2p 11 | 12 | .PHONY: FORCE 13 | -------------------------------------------------------------------------------- /src/debug/Makefile: -------------------------------------------------------------------------------- 1 | ZASM=../../../zasm/Linux/zasm 2 | 3 | all: debug 4 | 5 | debug: debug.c debug.h 6 | gcc -O2 -o $@ $+ 7 | 8 | debug.h: debug.bin 9 | xxd -i $< $@ 10 | 11 | debug.bin: debug.asm 12 | $(ZASM) -uwy $< $@ 13 | 14 | clean: FORCE 15 | rm -f debug debug.h debug.bin 16 | 17 | .PHONY: FORCE 18 | -------------------------------------------------------------------------------- /etc/writeall.lua: -------------------------------------------------------------------------------- 1 | return function(path, contents) 2 | if not path or #path == 0 then 3 | return nil, 'invalid file path' 4 | end 5 | 6 | local file, err = io.open(path, 'wb') 7 | 8 | if not file then 9 | return nil, err 10 | end 11 | 12 | file:write(contents) 13 | file:close() 14 | return true 15 | end 16 | -------------------------------------------------------------------------------- /etc/readall.lua: -------------------------------------------------------------------------------- 1 | return function(path) 2 | if not path or #path == 0 then 3 | return nil, 'invalid file path' 4 | end 5 | 6 | local file, err = io.open(path, 'rb') 7 | 8 | if not file then 9 | return nil, err 10 | end 11 | 12 | local contents = file:read('*a') 13 | file:close() 14 | return contents 15 | end 16 | -------------------------------------------------------------------------------- /src/qrc1_ram.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ; The ECC level M polynomial. 4 | qrc1_ecc_poly: 5 | db 1, 216, 194, 159, 111, 199, 94, 95, 113, 157, 193 6 | db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 7 | 8 | ; The message, it'll be encoded in place. 9 | qrc1_message: 10 | db 0 ; Message length 11 | ds 15 ; Message 12 | ds 10 ; Computed ECC 13 | 14 | ; Some scratch bytes. 15 | qrc1_scratch: 16 | ds 16 17 | -------------------------------------------------------------------------------- /src/zx81/qrc1.bas: -------------------------------------------------------------------------------- 1 | 10 REM ${#@temp.bin} 2 | 20 FAST 3 | 30 CLS 4 | 40 PRINT AT 0,0;"ENTER MESSAGE (MAX 14 CHARS)" 5 | 50 INPUT M$ 6 | 60 LET L=LEN M$ 7 | 70 IF L=0 THEN GOTO 50 8 | 80 PRINT AT 1,0;M$;" " 9 | 90 IF L>14 THEN LET L=14 10 | 100 LET O=${qrc1_message} 11 | 110 POKE O,L 12 | 120 FOR I=1 TO L 13 | 130 POKE O+I,CODE M$(I) 14 | 140 NEXT I 15 | 150 FOR I=6 TO 16 16 | 160 PRINT AT I,10;" " 17 | 170 NEXT I 18 | 180 RAND USR ${main} 19 | 190 GOTO 40 20 | -------------------------------------------------------------------------------- /src/spectrum/qrc1.bas: -------------------------------------------------------------------------------- 1 | 10 CLEAR ${ramtop} 2 | 20 LOAD "" CODE 3 | 30 CLS 4 | 40 PRINT AT 0,0;"Enter message (max 14 chars)" 5 | 50 INPUT M$ 6 | 60 LET L=LEN M$ 7 | 70 IF L=0 THEN GOTO 50 8 | 80 PRINT AT 1,0;M$;" " 9 | 90 IF L>14 THEN LET L=14 10 | 100 LET O=${qrc1_message} 11 | 110 POKE O,L 12 | 120 FOR I=1 TO L 13 | 130 POKE O+I,CODE M$(I) 14 | 140 NEXT I 15 | 150 FOR I=6 TO 16 16 | 160 PRINT AT I,10;" " 17 | 170 NEXT I 18 | 180 RANDOMIZE USR ${main} 19 | 190 GOTO 40 20 | -------------------------------------------------------------------------------- /src/spectrum/qrc11.bas: -------------------------------------------------------------------------------- 1 | 10 CLEAR ${ramtop} 2 | 20 LOAD "" CODE 3 | 30 CLS 4 | 40 PRINT AT 0,0;"Enter message (max 251 chars)" 5 | 50 INPUT M$ 6 | 60 LET L=LEN M$ 7 | 70 IF L=0 THEN GOTO 50 8 | 80 PRINT AT 1,0;M$;" " 9 | 90 IF L>251 THEN LET L=251 10 | 100 LET O=${qrc11_message} 11 | 110 POKE O,L 12 | 120 FOR I=1 TO L 13 | 130 POKE O+I,CODE M$(I) 14 | 140 NEXT I 15 | 150 FOR I=5 TO 20 16 | 160 PRINT AT I,8;" " 17 | 170 NEXT I 18 | 180 RANDOMIZE USR ${main} 19 | 190 GOTO 40 20 | -------------------------------------------------------------------------------- /src/zx81/Makefile: -------------------------------------------------------------------------------- 1 | ZXTEXT2P=../../etc/zxtext2p 2 | PASMO=../../../pasmo-0.5.3/pasmo 3 | 4 | all: qrc1.p i25.p 5 | 6 | qrc1.p: buildp.lua qrc1.bas qrc1.asm plot.asm ../qrc1_rom.asm ../qrc1_ram.asm 7 | LUA_PATH=';;../../etc/?.lua' lua buildp.lua qrc1.bas qrc1.asm $@ "$(PASMO)" "$(ZXTEXT2P)" 8 | 9 | i25.p: buildp.lua i25.bas i25.asm plot.asm ../i25_rom.asm 10 | LUA_PATH=';;../../etc/?.lua' lua buildp.lua i25.bas i25.asm $@ "$(PASMO)" "$(ZXTEXT2P)" 11 | 12 | clean: FORCE 13 | rm -f qrc1.p i25.p 14 | 15 | .PHONY: FORCE 16 | -------------------------------------------------------------------------------- /src/zx81/i25.bas: -------------------------------------------------------------------------------- 1 | 10 REM ${#@temp.bin} 2 | 20 FAST 3 | 30 CLS 4 | 40 PRINT AT 0,0;"Enter message (max 8 digits)" 5 | 50 INPUT M$ 6 | 60 LET L=LEN M$ 7 | 70 IF L=0 THEN GOTO 50 8 | 80 PRINT AT 1,0;M$;" " 9 | 90 IF L>8 THEN LET L=8 10 | 100 LET W=L*28+32 11 | 110 LET C=(256-W)/2 12 | 120 POKE ${column},C/8 13 | 130 LET O=${i25_message} 14 | 140 POKE O,L 15 | 150 FOR I=1 TO L 16 | 160 POKE O+I,CODE M$(I)+20 17 | 170 NEXT I 18 | 180 FOR I=5 TO 15 19 | 190 PRINT AT I,0;" " 20 | 200 NEXT I 21 | 210 RANDOMIZE USR ${main} 22 | 220 GOTO 40 23 | -------------------------------------------------------------------------------- /src/spectrum/i25.bas: -------------------------------------------------------------------------------- 1 | 10 CLEAR ${ramtop} 2 | 20 LOAD "" CODE 3 | 30 CLS 4 | 40 PRINT AT 0,0;"Enter message (max 16 digits)" 5 | 50 INPUT M$ 6 | 60 LET L=LEN M$ 7 | 70 IF L=0 THEN GOTO 50 8 | 80 PRINT AT 1,0;M$;" " 9 | 90 IF L>16 THEN LET L=16 10 | 100 LET W=L*14+16 11 | 110 LET C=(256-W)/2 12 | 120 POKE ${column},C AND 248 13 | 130 LET O=${i25_message} 14 | 140 POKE O,L 15 | 150 FOR I=1 TO L 16 | 160 POKE O+I,CODE M$(I) 17 | 170 NEXT I 18 | 180 FOR I=5 TO 10 19 | 190 PRINT AT I,0;" " 20 | 200 NEXT I 21 | 210 RANDOMIZE USR ${main} 22 | 220 GOTO 40 23 | -------------------------------------------------------------------------------- /src/zx81/i25.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | org 16514 4 | 5 | db $76, $76 ; two new lines to hide the machine code in LIST 6 | 7 | main: 8 | ; Set HL to the top-left character where the code will be printed. 9 | ld hl, ($400c) 10 | column equ $ + 1 11 | ld de, 0 12 | add hl, de 13 | ld de, 33 * 6 + 1 14 | add hl, de 15 | 16 | ; Set C to the upper-left pixel in the character. 17 | ld c, 1 18 | 19 | ; Print it onto the screen. 20 | jp i25_print 21 | 22 | i25_message: 23 | ds 9 24 | 25 | include "plot.asm" 26 | include "../i25_rom.asm" 27 | -------------------------------------------------------------------------------- /etc/lstsym.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Reads the listing file produced by the Pasmo assembler and returns a table with 3 | all the symbols in the listing with their values. 4 | ]] 5 | 6 | return function(path) 7 | if not path or #path == 0 then 8 | return nil, 'invalid file path' 9 | end 10 | 11 | local file, err = io.open(path, 'r') 12 | 13 | if not file then 14 | return nil, err 15 | end 16 | 17 | local symbols = {} 18 | 19 | for line in file:lines() do 20 | local symbol, value = line:match('([^%s]+)%s+EQU%s+(%x+)H') 21 | 22 | if symbol then 23 | symbols[symbol] = tonumber(value, 16) 24 | end 25 | end 26 | 27 | file:close() 28 | return symbols 29 | end 30 | -------------------------------------------------------------------------------- /src/spectrum/Makefile: -------------------------------------------------------------------------------- 1 | ZMAKEBAS=../../../zmakebas/zmakebas 2 | PASMO=../../../pasmo-0.5.3/pasmo 3 | 4 | all: qrc1.tap qrc11.tap i25.tap 5 | 6 | qrc1.tap: buildtap.lua qrc1.bas qrc1.asm plot.asm ../qrc1_rom.asm ../qrc1_ram.asm 7 | LUA_PATH=';;../../etc/?.lua' lua buildtap.lua qrc1.bas qrc1.asm $@ "$(PASMO)" "$(ZMAKEBAS)" 8 | 9 | qrc11.tap: buildtap.lua qrc11.bas qrc11.asm plot.asm ../qrc11_rom.asm ../qrc11_ram.asm 10 | LUA_PATH=';;../../etc/?.lua' lua buildtap.lua qrc11.bas qrc11.asm $@ "$(PASMO)" "$(ZMAKEBAS)" 11 | 12 | i25.tap: buildtap.lua i25.bas i25.asm plot.asm ../i25_rom.asm 13 | LUA_PATH=';;../../etc/?.lua' lua buildtap.lua i25.bas i25.asm $@ "$(PASMO)" "$(ZMAKEBAS)" 14 | 15 | clean: FORCE 16 | rm -f qrc1.tap qrc11.tap 17 | 18 | .PHONY: FORCE 19 | -------------------------------------------------------------------------------- /src/spectrum/i25.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | qrc_pixel_up equ qrc_pixel_up_2 4 | qrc_pixel_down equ qrc_pixel_down_2 5 | qrc_pixel_left equ qrc_pixel_left_2 6 | qrc_pixel_right equ qrc_pixel_right_2 7 | qrc_invert_pixel equ qrc_invert_pixel_2 8 | start_pixel equ $c0 9 | 10 | org 24576 11 | 12 | ramtop equ $ - 1 13 | 14 | main: 15 | ; Set H and L to the top-left pixel where the code will be printed, H is Y 16 | ; and L is X & %11111000. The low bits of X are encoded in register C. 17 | column equ $ + 1 18 | ld hl, 48 << 8 19 | 20 | ; Set C to the first pixel in the byte. 21 | ld c, start_pixel 22 | 23 | ; Print it onto the screen. 24 | jp i25_print 25 | 26 | i25_message: 27 | ds 17 28 | 29 | include "plot.asm" 30 | include "../i25_rom.asm" 31 | -------------------------------------------------------------------------------- /src/spectrum/qrc1.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | qrc_pixel_up equ qrc_pixel_up_4 4 | qrc_pixel_down equ qrc_pixel_down_4 5 | qrc_pixel_left equ qrc_pixel_left_4 6 | qrc_pixel_right equ qrc_pixel_right_4 7 | qrc_invert_pixel equ qrc_invert_pixel_4 8 | start_pixel equ $f0 9 | 10 | org 24576 11 | 12 | ramtop equ $ - 1 13 | 14 | main: 15 | ; Encode the message. 16 | call qrc1_encmessage 17 | 18 | ; Set H and L to the top-left pixel where the code will be printed, H is Y 19 | ; and L is X & %11111000. The low bits of X are encoded in register C. 20 | ld hl, 48 << 8 | 10 << 3 21 | 22 | ; Set C to the first pixel in the byte. 23 | ld c, start_pixel 24 | 25 | ; Print it onto the screen. 26 | call qrc1_print 27 | 28 | ret 29 | 30 | include "plot.asm" 31 | include "../qrc1_rom.asm" 32 | include "../qrc1_ram.asm" 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | etc/zxtext2p 2 | etc/zxtext2p.exe 3 | 4 | etc/qrc1 5 | etc/qrc1.exe 6 | 7 | src/debug/debug 8 | src/debug/debug.exe 9 | src/debug/debug.bin 10 | src/debug/debug.h 11 | src/debug/debug.png 12 | 13 | *.p 14 | *.tap 15 | 16 | # Assembler files 17 | *.lst 18 | 19 | # VS Code 20 | .vscode/* 21 | 22 | # Prerequisites 23 | *.d 24 | 25 | # Object files 26 | *.o 27 | *.ko 28 | *.obj 29 | *.elf 30 | 31 | # Linker output 32 | *.ilk 33 | *.map 34 | *.exp 35 | 36 | # Precompiled Headers 37 | *.gch 38 | *.pch 39 | 40 | # Libraries 41 | *.lib 42 | *.a 43 | *.la 44 | *.lo 45 | 46 | # Shared objects (inc. Windows DLLs) 47 | *.dll 48 | *.so 49 | *.so.* 50 | *.dylib 51 | 52 | # Executables 53 | *.exe 54 | *.out 55 | *.app 56 | *.i*86 57 | *.x86_64 58 | *.hex 59 | 60 | # Debug files 61 | *.dSYM/ 62 | *.su 63 | *.idb 64 | *.pdb 65 | 66 | # Kernel Module Compile Results 67 | *.mod* 68 | *.cmd 69 | .tmp_versions/ 70 | modules.order 71 | Module.symvers 72 | Mkfile.old 73 | dkms.conf 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andre Leiradella 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/qrc11_ram.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ; The ECC version 11 level M polynomial. 4 | qrc11_ecc_poly: 5 | db 1, 212, 246, 77, 73, 195, 192, 75, 98, 5, 70, 103, 177, 22, 217, 138 6 | db 51, 181, 246, 72, 25, 18, 46, 228, 74, 216, 195, 11, 106, 130, 150 7 | qrc11_ecc_poly_extra: 8 | ds 51 9 | 10 | ; The message, it'll be encoded in place. 11 | qrc11_block1: 12 | db $40 13 | qrc11_message: 14 | db 0 ; Message length 15 | ds 48 ; Message source 16 | qrc11_block2: 17 | ds 51 ; Message source 18 | qrc11_block3: 19 | ds 51 ; Message source 20 | qrc11_block4: 21 | ds 51 ; Message source 22 | qrc11_block5: 23 | ds 51 ; Message source 24 | 25 | ; Extra space for encoded message 26 | ds 30 * 5 27 | 28 | qrc11_b1: 29 | ds 50 ; Message target 30 | qrc11_b1_ecc: 31 | ds 30 ; Computed ECC 32 | qrc11_b2: 33 | ds 51 ; Message target 34 | qrc11_b2_ecc: 35 | ds 30 ; Computed ECC 36 | qrc11_b3: 37 | ds 51 ; Message target 38 | qrc11_b3_ecc: 39 | ds 30 ; Computed ECC 40 | qrc11_b4: 41 | ds 51 ; Message target 42 | qrc11_b4_ecc: 43 | ds 30 ; Computed ECC 44 | qrc11_b5: 45 | ds 51 ; Message target 46 | qrc11_b5_ecc: 47 | ds 30 ; Computed ECC 48 | 49 | qrc11_plot_direction: 50 | db 0 51 | -------------------------------------------------------------------------------- /src/spectrum/qrc11.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ;qrc_pixel_up equ qrc_pixel_up_1 4 | ;qrc_pixel_down equ qrc_pixel_down_1 5 | ;qrc_pixel_left equ qrc_pixel_left_1 6 | ;qrc_pixel_right equ qrc_pixel_right_1 7 | ;qrc_invert_pixel equ qrc_invert_pixel_1 8 | ;start_pixel equ $80 9 | 10 | qrc_pixel_up equ qrc_pixel_up_2 11 | qrc_pixel_down equ qrc_pixel_down_2 12 | qrc_pixel_left equ qrc_pixel_left_2 13 | qrc_pixel_right equ qrc_pixel_right_2 14 | qrc_invert_pixel equ qrc_invert_pixel_2 15 | start_pixel equ $c0 16 | 17 | ;qrc_pixel_up equ qrc_pixel_up_4 18 | ;qrc_pixel_down equ qrc_pixel_down_4 19 | ;qrc_pixel_left equ qrc_pixel_left_4 20 | ;qrc_pixel_right equ qrc_pixel_right_4 21 | ;qrc_invert_pixel equ qrc_invert_pixel_4 22 | ;start_pixel equ $f0 23 | 24 | org 24576 25 | 26 | ramtop equ $ - 1 27 | 28 | main: 29 | ; Encode the message. 30 | call qrc11_encmessage 31 | 32 | ; Set H and L to the top-left pixel where the code will be printed, H is Y 33 | ; and L is X & %11111000. The low bits of X are encoded in register C. 34 | ld hl, 40 << 8 | 8 << 3 35 | 36 | ; Set C to the first pixel in the byte. 37 | ld c, start_pixel 38 | 39 | ; Print it onto the screen. 40 | call qrc11_print 41 | 42 | ret 43 | 44 | include "plot.asm" 45 | include "../qrc11_rom.asm" 46 | include "../qrc11_ram.asm" 47 | -------------------------------------------------------------------------------- /etc/baspp.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | Preprocess a BASIC source file. Changes: 4 | 5 | * `${symbol}` for the corresponding value in the provided symbols file 6 | * `${#symbol}` for the length of the corresponding value in the provided symbols file 7 | * `${#@path}` for the size of the file 8 | 9 | ]] 10 | 11 | local readall = require 'readall' 12 | 13 | return function(basic, symbols) 14 | local errors = {} 15 | 16 | -- Substitute ${#@path} by the size of the file. 17 | basic = basic:gsub('%${#@(.-)}', function(path) 18 | local contents, err = readall(path) 19 | 20 | if contents then 21 | return string.rep('.', #contents) 22 | else 23 | errors[#errors + 1] = string.format('error reading from "%s": %s', path, err) 24 | return '' 25 | end 26 | end) 27 | 28 | -- Substitute ${#symbol} by the length of the symbol. 29 | basic = basic:gsub('%${#(.-)}', function(symbol) 30 | if symbols[symbol] then 31 | return string.rep('.', #symbols[symbol]) 32 | else 33 | errors[#errors + 1] = string.format('symbol not found: %s', symbol) 34 | return '' 35 | end 36 | end) 37 | 38 | -- Substitute ${symbol} by the symbol itself. 39 | basic = basic:gsub('%${(.-)}', symbols) 40 | 41 | -- Return the result. 42 | return (#errors == 0 and basic or nil), errors 43 | end 44 | -------------------------------------------------------------------------------- /src/debug/encode.asm: -------------------------------------------------------------------------------- 1 | CMD_PORT equ $de 2 | CMD_PIXEL_LEFT equ 0 3 | CMD_PIXEL_RIGHT equ 1 4 | CMD_PIXEL_UP equ 2 5 | CMD_PIXEL_DOWN equ 3 6 | CMD_SET_PIXEL equ 4 7 | CMD_MESSAGE equ 5 8 | CMD_PRINT_WORD equ 6 9 | CMD_INVERT_PIXEL equ 7 10 | CMD_SET_XY equ 8 11 | 12 | org 0 13 | 14 | ld sp, 0 15 | 16 | call print 17 | db "copying message to encode", 0 18 | 19 | ld hl, message 20 | ld de, qrc11_message + 1 21 | ld b, 0 22 | ld c, (hl) 23 | inc c 24 | ldir 25 | 26 | call print 27 | db "encoding message", 0 28 | 29 | call qrc11_encmessage 30 | 31 | call print 32 | db "printing qr code", 0 33 | 34 | call qrc11_print 35 | 36 | call print 37 | db "done", 0 38 | 39 | halt 40 | 41 | print: 42 | pop hl 43 | push af 44 | ld a, CMD_MESSAGE 45 | out (CMD_PORT), a 46 | 47 | print_loop: 48 | ld a, (hl) 49 | inc hl 50 | 51 | out (CMD_PORT), a 52 | or a 53 | jr nz, print_loop 54 | 55 | pop af 56 | jp (hl) 57 | 58 | message: 59 | db 8, "leiradel" 60 | 61 | qrc_pixel_left: 62 | ld a, CMD_PIXEL_LEFT 63 | out (CMD_PORT), a 64 | ret 65 | 66 | qrc_pixel_right: 67 | ld a, CMD_PIXEL_RIGHT 68 | out (CMD_PORT), a 69 | ret 70 | 71 | qrc_pixel_up: 72 | ld a, CMD_PIXEL_UP 73 | out (CMD_PORT), a 74 | ret 75 | 76 | qrc_pixel_down: 77 | ld a, CMD_PIXEL_DOWN 78 | out (CMD_PORT), a 79 | ret 80 | 81 | qrc_invert_pixel: 82 | ld a, CMD_INVERT_PIXEL 83 | out (CMD_PORT), a 84 | ret 85 | 86 | #include "../qrc11.asm" 87 | -------------------------------------------------------------------------------- /src/zx81/buildp.lua: -------------------------------------------------------------------------------- 1 | local basic_path = arg[1] 2 | local assembly_path = arg[2] 3 | local p_path = arg[3] 4 | local pasmo = arg[4] 5 | local zxtext2p = arg[5] 6 | 7 | -- Set up temporary paths. 8 | local tmp_bin_path = 'temp.bin' 9 | local tmp_lst_path = 'temp.lst' 10 | local tmp_bas_path = 'temp.bas' 11 | local tmp_p_path = 'temp.p' 12 | 13 | local function build() 14 | local lstsym = require 'lstsym' 15 | local readall = require 'readall' 16 | local baspp = require 'baspp' 17 | local writeall = require 'writeall' 18 | 19 | local function execute(format, ...) 20 | local cmd = string.format(format, ...) 21 | assert(os.execute(cmd)) 22 | end 23 | 24 | -- Assemble. 25 | execute('%s --bin %s %s %s', pasmo, assembly_path, tmp_bin_path, tmp_lst_path) 26 | 27 | -- Get symbols from the assembled file. 28 | local symbols = assert(lstsym(tmp_lst_path)) 29 | 30 | -- Create the BASIC list. 31 | local basic = assert(readall(basic_path)) 32 | basic = assert(baspp(basic, symbols)) 33 | 34 | -- Generate the P file from the BASIC list. 35 | assert(writeall(tmp_bas_path, basic)) 36 | execute('%s -o %s %s', zxtext2p, tmp_p_path, tmp_bas_path) 37 | 38 | -- Read and combine the generated files. 39 | local bin = assert(readall(tmp_bin_path)) 40 | local p = assert(readall(tmp_p_path)) 41 | 42 | p = p:sub(1, 0x79) .. bin .. p:sub(0x79 + #bin + 1, -1) 43 | assert(writeall(p_path, p)) 44 | 45 | return true 46 | end 47 | 48 | local ok, err = pcall(build) 49 | 50 | -- Clean up. 51 | os.remove(tmp_bin_path) 52 | os.remove(tmp_lst_path) 53 | os.remove(tmp_bas_path) 54 | os.remove(tmp_p_path) 55 | 56 | if not ok then 57 | io.stderr:write(string.format('%s\n', err)) 58 | os.exit(1) 59 | end 60 | -------------------------------------------------------------------------------- /src/spectrum/buildtap.lua: -------------------------------------------------------------------------------- 1 | local basic_path = arg[1] 2 | local assembly_path = arg[2] 3 | local tap_path = arg[3] 4 | local pasmo = arg[4] 5 | local zmakebas = arg[5] 6 | 7 | -- Set up temporary paths. 8 | local tmp_bin_path = 'temp.bin' 9 | local tmp_lst_path = 'temp.lst' 10 | local tmp_bas_path = 'temp.bas' 11 | local tmp_tap1_path = 'temp1.tap' 12 | local tmp_tap2_path = 'temp2.tap' 13 | 14 | 15 | local function build() 16 | local lstsym = require 'lstsym' 17 | local readall = require 'readall' 18 | local baspp = require 'baspp' 19 | local writeall = require 'writeall' 20 | 21 | local function execute(format, ...) 22 | local cmd = string.format(format, ...) 23 | assert(os.execute(cmd)) 24 | end 25 | 26 | -- Assemble 27 | execute('%s --tap %s %s %s', pasmo, assembly_path, tmp_tap2_path, tmp_lst_path) 28 | 29 | -- Get symbols from the assembled file. 30 | local symbols = assert(lstsym(tmp_lst_path)) 31 | 32 | -- Create the BASIC list 33 | local basic = assert(readall(basic_path)) 34 | basic = assert(baspp(basic, symbols)) 35 | 36 | -- Generate the TAP file from the BASIC list. 37 | assert(writeall(tmp_bas_path, basic)) 38 | execute('%s -a 10 -n barcode -o %s %s', zmakebas, tmp_tap1_path, tmp_bas_path) 39 | 40 | -- Read and combine the generated files 41 | local tap1 = assert(readall(tmp_tap1_path)) 42 | local tap2 = assert(readall(tmp_tap2_path)) 43 | assert(writeall(tap_path, tap1 .. tap2)) 44 | end 45 | 46 | local ok, err = pcall(build) 47 | 48 | -- Clean up 49 | os.remove(tmp_bin_path) 50 | os.remove(tmp_lst_path) 51 | os.remove(tmp_bas_path) 52 | os.remove(tmp_tap1_path) 53 | os.remove(tmp_tap2_path) 54 | 55 | if not ok then 56 | io.stderr:write(string.format('%s\n', err)) 57 | os.exit(1) 58 | end 59 | -------------------------------------------------------------------------------- /src/zx81/plot.asm: -------------------------------------------------------------------------------- 1 | ; Moves the cursor one pixel to the left. 2 | qrc_pixel_left: 3 | ; 1 -> 2 | 0001 -> 0010 dec hl 4 | ; 4 -> 8 | 0100 -> 1000 dec hl 5 | ; 2 -> 1 | 0010 -> 0001 6 | ; 8 -> 4 | 1000 -> 0100 7 | ld a, 5 8 | and c 9 | jr z, qrc_dont_dec 10 | sla c 11 | dec hl 12 | ret 13 | qrc_dont_dec: 14 | srl c 15 | ret 16 | 17 | ; Moves the cursor one pixel to the right. 18 | qrc_pixel_right: 19 | ; 1 -> 2 | 0001 -> 0010 20 | ; 4 -> 8 | 0100 -> 1000 21 | ; 2 -> 1 | 0010 -> 0001 inc hl 22 | ; 8 -> 4 | 1000 -> 0100 inc hl 23 | ld a, 5 24 | and c 25 | jr nz, qrc_dont_inc 26 | srl c 27 | inc hl 28 | ret 29 | qrc_dont_inc: 30 | sla c 31 | ret 32 | 33 | ; Moves the cursor one pixel up. 34 | qrc_pixel_up: 35 | ; 1 -> 4 | 0001 -> 0100 sub hl, 33 36 | ; 2 -> 8 | 0010 -> 1000 sub hl, 33 37 | ; 4 -> 1 | 0100 -> 0001 38 | ; 8 -> 2 | 1000 -> 0010 39 | ld a, 3 40 | and c 41 | jr z, qrc_dont_sub 42 | sla c 43 | sla c 44 | ld a, l 45 | sub 33 46 | ld l, a 47 | ld a, h 48 | sbc a, 0 49 | ld h, a 50 | ret 51 | qrc_dont_sub: 52 | srl c 53 | srl c 54 | ret 55 | 56 | ; Moves the cursor one pixel down. 57 | qrc_pixel_down: 58 | ; 1 -> 4 | 0001 -> 0100 59 | ; 2 -> 8 | 0010 -> 1000 60 | ; 4 -> 1 | 0100 -> 0001 add hl, 33 61 | ; 8 -> 2 | 1000 -> 0010 add hl, 33 62 | ld a, 3 63 | and c 64 | jr nz, qrc_dont_add 65 | srl c 66 | srl c 67 | ld a, l 68 | add a, 33 69 | ld l, a 70 | ld a, h 71 | adc a, 0 72 | ld h, a 73 | ret 74 | qrc_dont_add: 75 | sla c 76 | sla c 77 | ret 78 | 79 | qrc_invert_pixel: 80 | ld a, (hl) 81 | bit 7, a 82 | jr z, qrc_invert1 83 | xor $8f 84 | qrc_invert1: 85 | xor c 86 | bit 3, a 87 | jr z, qrc_invert2 88 | xor $8f 89 | qrc_invert2: 90 | ld (hl), a 91 | ret 92 | -------------------------------------------------------------------------------- /src/debug/proxyud.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static int l_new(lua_State* const L) { 5 | int const top = lua_gettop(L); 6 | 7 | // Create an empty full userdata object. 8 | lua_newuserdata(L, 0); 9 | 10 | // The first parameter to new is the value that will be associated to the 11 | // userdata. 12 | if (top >= 1) { 13 | lua_pushvalue(L, 1); 14 | lua_setuservalue(L, -2); 15 | } 16 | 17 | // The second parameter to new, if present, is the metatable for the 18 | // userdata. 19 | if (top >= 2 && lua_type(L, 2) == LUA_TTABLE) { 20 | lua_pushvalue(L, 2); 21 | lua_setmetatable(L, -2); 22 | } 23 | 24 | // Return the userdata object. 25 | return 1; 26 | } 27 | 28 | static int l_get(lua_State* const L) { 29 | // Check that the first argument is a full userdata. 30 | luaL_checktype(L, 1, LUA_TUSERDATA); 31 | 32 | // Return the value associated with it. 33 | lua_getuservalue(L, 1); 34 | return 1; 35 | } 36 | 37 | LUAMOD_API int luaopen_proxyud(lua_State* L) { 38 | static luaL_Reg const functions[] = { 39 | {"new", l_new}, 40 | {"get", l_get}, 41 | {NULL, NULL} 42 | }; 43 | 44 | static struct {char const* const name; char const* const value;} const info[] = { 45 | {"_COPYRIGHT", "Copyright (c) 2020 Andre Leiradella"}, 46 | {"_LICENSE", "MIT"}, 47 | {"_VERSION", "1.0.0"}, 48 | {"_NAME", "proxyud"}, 49 | {"_URL", "https://github.com/leiradel/luamods/proxyud"}, 50 | {"_DESCRIPTION", "Creates proxy userdata objects"} 51 | }; 52 | 53 | size_t const functions_count = sizeof(functions) / sizeof(functions[0]) - 1; 54 | size_t const info_count = sizeof(info) / sizeof(info[0]); 55 | 56 | lua_createtable(L, 0, functions_count + info_count); 57 | luaL_setfuncs(L, functions, 0); 58 | 59 | for (size_t i = 0; i < info_count; i++) { 60 | lua_pushstring(L, info[i].value); 61 | lua_setfield(L, -2, info[i].name); 62 | } 63 | 64 | return 1; 65 | } 66 | -------------------------------------------------------------------------------- /src/zx81/qrc1.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | org 16514 4 | 5 | db $76, $76 ; two new lines to hide the machine code in LIST 6 | 7 | 8 | main: 9 | ; Translate the character set from ZX81 to ASCII. 10 | call to_ascii 11 | 12 | ; Encode the message. 13 | call qrc1_encmessage 14 | 15 | ; Set HL to the top-left character where the code will be printed. 16 | ld hl, ($400c) 17 | ld de, 33 * 6 + 11 18 | add hl, de 19 | 20 | ; Set C to the upper-left pixel in the character. 21 | ld c, 1 22 | 23 | ; Print it onto the screen. 24 | call qrc1_print 25 | 26 | ret 27 | 28 | to_ascii: 29 | ; Make HL point to the unencoded message. 30 | ld hl, qrc1_message 31 | 32 | ; B has the message length. 33 | ld b, (hl) 34 | inc hl 35 | 36 | xlate_loop: 37 | ; Put the ZX81 character in A, making sure it's valid and non-inverted. 38 | ld a, (hl) 39 | and $3f 40 | 41 | ; Get the corresponding ASCII character in the translation table. 42 | add a, xlate_table & $ff 43 | ld e, a 44 | ld a, 0 45 | adc a, xlate_table >> 8 46 | ld d, a 47 | ld a, (de) 48 | 49 | ; Overwrite with the ASCII char. 50 | ld (hl), a 51 | inc hl 52 | djnz xlate_loop 53 | ret 54 | 55 | xlate_table: 56 | ; spc gra gra gra gra gra gra gra gra gra gra " £ $ : ? 57 | db ' ', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '"', '?', '$', ':', '?' 58 | 59 | ; ( ) > < = + - * / ; , . 0 1 2 3 60 | db '(', ')', '>', '<', '=', '+', '-', '*', '/', ';', ',', '.', '0', '1', '2', '3' 61 | 62 | ; 4 5 6 7 8 9 A B C D E F G H I J 63 | db '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' 64 | 65 | ; K L M N O P Q R S T U V W X Y Z 66 | db 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 67 | 68 | include "plot.asm" 69 | include "../qrc1_rom.asm" 70 | include "../qrc1_ram.asm" 71 | -------------------------------------------------------------------------------- /src/i25_rom.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ; i25_message contains the message length followed by the message. 4 | i25_print: 5 | ld iy, i25_message 6 | ld a, (iy) 7 | inc iy 8 | 9 | ; Divide the message length by 2. 10 | and a 11 | rra 12 | 13 | ; Return if the length is odd. 14 | ret c 15 | 16 | ld d, a 17 | 18 | call i25_narrow 19 | call qrc_pixel_right 20 | call i25_narrow 21 | call qrc_pixel_right 22 | 23 | i25_print_loop: 24 | push de 25 | push hl 26 | 27 | ld a, (iy + 0) 28 | sub $30 29 | ld e, a 30 | ld d, 0 31 | ld hl, i25_patterns 32 | add hl, de 33 | ld d, (hl) 34 | push de 35 | 36 | ld a, (iy + 1) 37 | sub $30 38 | ld e, a 39 | ld d, 0 40 | ld hl, i25_patterns 41 | add hl, de 42 | pop de 43 | ld e, (hl) 44 | pop hl 45 | 46 | inc iy 47 | inc iy 48 | 49 | call i25_draw_pair 50 | call i25_draw_pair 51 | call i25_draw_pair 52 | call i25_draw_pair 53 | call i25_draw_pair 54 | 55 | pop de 56 | dec d 57 | jr nz, i25_print_loop 58 | 59 | call i25_wide 60 | call qrc_pixel_right 61 | jp i25_narrow 62 | 63 | i25_draw_pair: 64 | sla d 65 | jr c, i25_draw_wide 66 | 67 | call i25_narrow 68 | jr i25_next 69 | 70 | i25_draw_wide: 71 | call i25_wide 72 | 73 | i25_next: 74 | call qrc_pixel_right 75 | 76 | sla e 77 | jp c, qrc_pixel_right 78 | ret 79 | 80 | i25_narrow: 81 | ld b, 20 82 | i25_narrow_loop1: 83 | call qrc_invert_pixel 84 | call qrc_pixel_down 85 | djnz i25_narrow_loop1 86 | 87 | i25_up: 88 | ld b, 20 89 | i25_narrow_loop2: 90 | call qrc_pixel_up 91 | djnz i25_narrow_loop2 92 | 93 | jp qrc_pixel_right 94 | 95 | i25_wide: 96 | ld b, 20 97 | i25_wide_loop1: 98 | call qrc_invert_pixel 99 | call qrc_pixel_right 100 | call qrc_invert_pixel 101 | call qrc_pixel_left 102 | call qrc_pixel_down 103 | djnz i25_wide_loop1 104 | 105 | call qrc_pixel_right 106 | jr i25_up 107 | 108 | i25_patterns: 109 | db %00110 * 8 110 | db %10001 * 8 111 | db %01001 * 8 112 | db %11000 * 8 113 | db %00101 * 8 114 | db %10100 * 8 115 | db %01100 * 8 116 | db %00011 * 8 117 | db %10010 * 8 118 | db %01010 * 8 119 | -------------------------------------------------------------------------------- /src/spectrum/plot.asm: -------------------------------------------------------------------------------- 1 | ; Routines that use 1x1 square pixels for the modules. 2 | 3 | qrc_pixel_up_1: 4 | dec h 5 | ret 6 | 7 | qrc_pixel_down_1: 8 | inc h 9 | ret 10 | 11 | qrc_pixel_left_1: 12 | rlc c 13 | ret nc 14 | ld a, l 15 | sub $08 16 | ld l, a 17 | ret 18 | 19 | qrc_pixel_right_1: 20 | rrc c 21 | ret nc 22 | ld a, l 23 | add a, $08 24 | ld l, a 25 | ret 26 | 27 | qrc_invert_pixel_1: 28 | push hl 29 | push hl 30 | 31 | ld a, h 32 | add a, high_y & $ff 33 | ld l, a 34 | ld a, 0 35 | adc a, high_y >> 8 36 | ld h, a 37 | ld a, (hl) 38 | 39 | pop hl 40 | rra 41 | rr l 42 | rra 43 | rr l 44 | rra 45 | rr l 46 | 47 | and $1f 48 | or $40 49 | ld h, a 50 | 51 | ld a, (hl) 52 | xor c 53 | ld (hl), a 54 | pop hl 55 | ret 56 | 57 | high_y: 58 | ; This table is indexed with the Y coordinate [0, 191] and provides a byte 59 | ; encoded as (y[6-7] << 6) | (y[0-2] << 3) | y[3-5] which can be used to 60 | ; assemble the screen address of the corresponding line. 61 | db $00, $08, $10, $18, $20, $28, $30, $38, $01, $09, $11, $19, $21, $29, $31, $39 62 | db $02, $0a, $12, $1a, $22, $2a, $32, $3a, $03, $0b, $13, $1b, $23, $2b, $33, $3b 63 | db $04, $0c, $14, $1c, $24, $2c, $34, $3c, $05, $0d, $15, $1d, $25, $2d, $35, $3d 64 | db $06, $0e, $16, $1e, $26, $2e, $36, $3e, $07, $0f, $17, $1f, $27, $2f, $37, $3f 65 | db $40, $48, $50, $58, $60, $68, $70, $78, $41, $49, $51, $59, $61, $69, $71, $79 66 | db $42, $4a, $52, $5a, $62, $6a, $72, $7a, $43, $4b, $53, $5b, $63, $6b, $73, $7b 67 | db $44, $4c, $54, $5c, $64, $6c, $74, $7c, $45, $4d, $55, $5d, $65, $6d, $75, $7d 68 | db $46, $4e, $56, $5e, $66, $6e, $76, $7e, $47, $4f, $57, $5f, $67, $6f, $77, $7f 69 | db $80, $88, $90, $98, $a0, $a8, $b0, $b8, $81, $89, $91, $99, $a1, $a9, $b1, $b9 70 | db $82, $8a, $92, $9a, $a2, $aa, $b2, $ba, $83, $8b, $93, $9b, $a3, $ab, $b3, $bb 71 | db $84, $8c, $94, $9c, $a4, $ac, $b4, $bc, $85, $8d, $95, $9d, $a5, $ad, $b5, $bd 72 | db $86, $8e, $96, $9e, $a6, $ae, $b6, $be, $87, $8f, $97, $9f, $a7, $af, $b7, $bf 73 | 74 | ; Routines that use 2x2 square pixels for the modules. 75 | 76 | qrc_pixel_up_2: 77 | dec h 78 | dec h 79 | ret 80 | 81 | qrc_pixel_down_2: 82 | inc h 83 | inc h 84 | ret 85 | 86 | qrc_pixel_left_2: 87 | rlc c 88 | rlc c 89 | ret nc 90 | ld a, l 91 | sub $08 92 | ld l, a 93 | ret 94 | 95 | qrc_pixel_right_2: 96 | rrc c 97 | rrc c 98 | ret nc 99 | ld a, l 100 | add a, $08 101 | ld l, a 102 | ret 103 | 104 | qrc_invert_pixel_2: 105 | call qrc_invert_pixel_1 106 | inc h 107 | call qrc_invert_pixel_1 108 | dec h 109 | ret 110 | 111 | ; Routines that use 4x4 square pixels for the modules. 112 | 113 | qrc_pixel_up_4: 114 | dec h 115 | dec h 116 | dec h 117 | dec h 118 | ret 119 | 120 | qrc_pixel_down_4: 121 | inc h 122 | inc h 123 | inc h 124 | inc h 125 | ret 126 | 127 | qrc_pixel_left_4: 128 | ld a, c 129 | rlca 130 | rlca 131 | rlca 132 | rlca 133 | ld c, a 134 | ret nc 135 | ld a, l 136 | sub $08 137 | ld l, a 138 | ret 139 | 140 | qrc_pixel_right_4: 141 | ld a, c 142 | rrca 143 | rrca 144 | rrca 145 | rrca 146 | ld c, a 147 | ret nc 148 | ld a, l 149 | add a, $08 150 | ld l, a 151 | ret 152 | 153 | qrc_invert_pixel_4: 154 | call qrc_invert_pixel_1 155 | inc h 156 | call qrc_invert_pixel_1 157 | inc h 158 | call qrc_invert_pixel_1 159 | inc h 160 | call qrc_invert_pixel_1 161 | dec h 162 | dec h 163 | dec h 164 | ret 165 | -------------------------------------------------------------------------------- /src/debug/access.lua: -------------------------------------------------------------------------------- 1 | local proxyud = require 'proxyud' 2 | local proxyud_new = proxyud.new 3 | local proxyud_get = proxyud.get 4 | 5 | -- The metatable for readonly tables. 6 | local constmt = { 7 | __index = function(ud, key) 8 | -- Get the table for the userdata. 9 | local table = proxyud_get(ud) 10 | -- Get the value in the table. 11 | local value = table[key] 12 | 13 | -- Return the value if there's one. 14 | if value ~= nil then 15 | return value 16 | end 17 | 18 | -- Unknown key. 19 | error(string.format('cannot access unknown field \'%s\' in table', key)) 20 | end, 21 | 22 | __newindex = function(ud, key, value) 23 | -- Const tables don't support assignments. 24 | error(string.format('cannot assign to field \'%s\' in constant table', key)) 25 | end, 26 | 27 | __add = function(ud, other) return proxyud_get(ud) + other end, 28 | __sub = function(ud, other) return proxyud_get(ud) - other end, 29 | __mul = function(ud, other) return proxyud_get(ud) * other end, 30 | __div = function(ud, other) return proxyud_get(ud) / other end, 31 | __mod = function(ud, other) return proxyud_get(ud) % other end, 32 | __pow = function(ud, other) return proxyud_get(ud) ^ other end, 33 | __unm = function(ud) return -proxyud_get(ud) end, 34 | __idiv = function(ud, other) return proxyud_get(ud) // other end, 35 | __band = function(ud, other) return proxyud_get(ud) & other end, 36 | __bor = function(ud, other) return proxyud_get(ud) | other end, 37 | __bxor = function(ud, other) return proxyud_get(ud) ~ other end, 38 | __bnot = function(ud) return ~proxyud_get(ud) end, 39 | __shl = function(ud, other) return proxyud_get(ud) << other end, 40 | __shr = function(ud, other) return proxyud_get(ud) >> other end, 41 | __concat = function(ud, other) return proxyud_get(ud) .. other end, 42 | __len = function(ud) return #proxyud_get(ud) end, 43 | __eq = function(ud, other) return proxyud_get(ud) == other end, 44 | __lt = function(ud, other) return proxyud_get(ud) < other end, 45 | __le = function(ud, other) return proxyud_get(ud) <= other end, 46 | __call = function(ud, ...) return proxyud_get(ud)(...) end 47 | } 48 | 49 | -- The metatable for sealed tables. 50 | local sealmt = { 51 | __index = constmt.__index, 52 | 53 | __newindex = function(ud, key, value) 54 | -- Get the table for the userdata. 55 | local table = proxyud_get(ud) 56 | 57 | -- Only assign if the field exists. 58 | if table[key] ~= nil then 59 | table[key] = value 60 | return 61 | end 62 | 63 | -- Unknown key. 64 | error(string.format('cannot assign to unknown field \'%s\' in sealed table', key)) 65 | end, 66 | 67 | __add = constmt.__add, 68 | __sub = constmt.__sub, 69 | __mul = constmt.__mul, 70 | __div = constmt.__div, 71 | __mod = constmt.__mod, 72 | __pow = constmt.__pow, 73 | __unm = constmt.__unm, 74 | __idiv = constmt.__idiv, 75 | __band = constmt.__band, 76 | __bor = constmt.__bor, 77 | __bxor = constmt.__bxor, 78 | __bnot = constmt.__bnot, 79 | __shl = constmt.__shl, 80 | __shr = constmt.__shr, 81 | __concat = constmt.__concat, 82 | __len = constmt.__len, 83 | __eq = constmt.__eq, 84 | __lt = constmt.__lt, 85 | __le = constmt.__le, 86 | __call = constmt.__call 87 | } 88 | 89 | local function const(table) 90 | -- Create an userdata with the table and the constmt metatable. 91 | local ud = proxyud_new(table, constmt) 92 | -- Return the userdata proxy. 93 | return ud 94 | end 95 | 96 | local function seal(table) 97 | local ud = proxyud_new(table, sealmt) 98 | return ud 99 | end 100 | 101 | -- The module. 102 | return const { 103 | _COPYRIGHT = 'Copyright (c) 2020 Andre Leiradella', 104 | _LICENSE = 'MIT', 105 | _VERSION = '1.0.0', 106 | _NAME = 'proxyud', 107 | _URL = 'https://github.com/leiradel/luamods/access', 108 | _DESCRIPTION = 'Creates constant and sealed objects', 109 | 110 | const = const, 111 | seal = seal 112 | } 113 | -------------------------------------------------------------------------------- /src/debug/qrc11.lua: -------------------------------------------------------------------------------- 1 | local z80 = require 'z80' 2 | local imgcreate = require 'imgcreate' 3 | 4 | local CMD_PORT = 0xde 5 | local CMD_PIXEL_LEFT = 0 6 | local CMD_PIXEL_RIGHT = 1 7 | local CMD_PIXEL_UP = 2 8 | local CMD_PIXEL_DOWN = 3 9 | local CMD_SET_PIXEL = 4 10 | local CMD_MESSAGE = 5 11 | local CMD_PRINT_WORD = 6 12 | local CMD_INVERT_PIXEL = 7 13 | local CMD_SET_XY = 8 14 | 15 | local memory = {} 16 | local done = false 17 | 18 | local width, height = 0,0 19 | local x, y = 0, 0 20 | local pixels = {} 21 | 22 | local command; command = function(data) 23 | if data == CMD_PIXEL_LEFT then 24 | x = x - 1 25 | elseif data == CMD_PIXEL_RIGHT then 26 | x = x + 1 27 | elseif data == CMD_PIXEL_UP then 28 | y = y - 1 29 | elseif data == CMD_PIXEL_DOWN then 30 | y = y + 1 31 | elseif data == CMD_SET_PIXEL then 32 | if x >= width then 33 | width = x + 1 34 | end 35 | 36 | if y >= height then 37 | for i = height, y do 38 | pixels[i] = {} 39 | end 40 | 41 | height = y + 1 42 | end 43 | 44 | pixels[y][x] = true 45 | elseif data == CMD_MESSAGE then 46 | local old = command 47 | local message = '' 48 | 49 | command = function(data) 50 | if data ~= 0 then 51 | message = message .. string.char(data) 52 | else 53 | print(message) 54 | command = old 55 | end 56 | end 57 | elseif data == CMD_PRINT_WORD then 58 | local old = command 59 | local word, step = 0, 1 60 | 61 | command = function(data) 62 | if step == 1 then 63 | word = data 64 | elseif step == 2 then 65 | word = word | data << 8 66 | print(string.format('%04x (%u)', word, word)) 67 | command = old 68 | end 69 | 70 | step = step + 1 71 | end 72 | elseif data == CMD_INVERT_PIXEL then 73 | if x >= width then 74 | width = x + 1 75 | end 76 | 77 | if y >= height then 78 | for i = height, y do 79 | pixels[i] = {} 80 | end 81 | 82 | height = y + 1 83 | end 84 | 85 | pixels[y][x] = not pixels[y][x] 86 | elseif data == CMD_SET_XY then 87 | local old = command 88 | local step = 1 89 | 90 | command = function(data) 91 | if step == 1 then 92 | x = data 93 | elseif step == 2 then 94 | y = data 95 | command = old 96 | end 97 | 98 | step = step + 1 99 | end 100 | end 101 | end 102 | 103 | do 104 | local file = assert(io.open('qrc11.bin', 'rb')) 105 | local bin = file:read('*a') 106 | file:close() 107 | 108 | for i = 1, #bin do 109 | memory[i - 1] = bin:byte(i) 110 | end 111 | end 112 | 113 | local cpu = z80.init(function(cpu, num_ticks, pins) 114 | done = (pins & z80.HALT) ~= 0 115 | 116 | if (pins & z80.M1) ~= 0 then 117 | local insn = z80.dasm(cpu:pc(), memory, cpu:pc()) 118 | insn = insn:gsub('(%x%x%x%x)h', function(value) return '0000h' end) 119 | insn = insn:gsub('IY', 'IX') 120 | --print(string.format('%s ; A=%02x DE=%04x', insn, cpu:a(), cpu:de())) 121 | end 122 | 123 | if (pins & z80.MREQ) ~= 0 then 124 | local address = z80.GET_ADDR(pins) 125 | 126 | if (pins & z80.RD) ~= 0 then 127 | pins = z80.SET_DATA(pins, memory[address]) 128 | elseif (pins & z80.WR) ~= 0 then 129 | memory[address] = z80.GET_DATA(pins) 130 | 131 | if address < 0xf000 and cpu:pc() ~= 0x002a then 132 | --print(string.format('memory[0x%04x] = {data = 0x%02x, pc = 0x%04x} -- %d', address, z80.GET_DATA(pins), cpu:pc(), address - 0x0318)) 133 | end 134 | end 135 | elseif (pins & z80.IORQ) ~= 0 then 136 | local address = z80.GET_ADDR(pins) 137 | 138 | if (address & 0xff) == CMD_PORT and (pins & z80.WR) ~= 0 then 139 | local data = z80.GET_DATA(pins) 140 | command(data) 141 | end 142 | end 143 | 144 | return pins 145 | end) 146 | 147 | while not done do 148 | cpu:exec(1) 149 | end 150 | 151 | --[[for i = 0x0317, 0x0317 + 4095, 16 do 152 | io.write(string.format('%04x ', i)) 153 | 154 | for j = i, i + 15 do 155 | if j <= 0x0317 + 4095 then 156 | io.write(string.format(' %02x', memory[j])) 157 | end 158 | end 159 | 160 | io.write(' ') 161 | 162 | for j = i, i + 15 do 163 | if j <= 0x0317 + 4095 then 164 | io.write(string.format('%c', (memory[j] >= 32 and memory[j] < 127) and memory[j] or string.byte('.'))) 165 | end 166 | end 167 | 168 | io.write('\n') 169 | end]] 170 | 171 | do 172 | local png = imgcreate.png(width, height, 1, function(x, y) 173 | return pixels[y][x] and 0x00 or 0xff 174 | end) 175 | 176 | local file = assert(io.open('qrc11.png', 'wb')) 177 | file:write(png) 178 | file:close() 179 | end 180 | -------------------------------------------------------------------------------- /src/debug/pr11.lua: -------------------------------------------------------------------------------- 1 | local z80 = require 'z80' 2 | local imgcreate = require 'imgcreate' 3 | 4 | local CMD_PORT = 0xde 5 | local CMD_PIXEL_LEFT = 0 6 | local CMD_PIXEL_RIGHT = 1 7 | local CMD_PIXEL_UP = 2 8 | local CMD_PIXEL_DOWN = 3 9 | local CMD_SET_PIXEL = 4 10 | local CMD_MESSAGE = 5 11 | local CMD_PRINT_WORD = 6 12 | local CMD_INVERT_PIXEL = 7 13 | local CMD_SET_XY = 8 14 | 15 | local memory = {} 16 | local done = false 17 | 18 | local width, height = 0,0 19 | local x, y = 0, 0 20 | local pixels = {} 21 | 22 | local command; command = function(data) 23 | if data == CMD_PIXEL_LEFT then 24 | x = x - 1 25 | elseif data == CMD_PIXEL_RIGHT then 26 | x = x + 1 27 | elseif data == CMD_PIXEL_UP then 28 | y = y - 1 29 | elseif data == CMD_PIXEL_DOWN then 30 | y = y + 1 31 | elseif data == CMD_SET_PIXEL then 32 | if x >= width then 33 | width = x + 1 34 | end 35 | 36 | if y >= height then 37 | for i = height, y do 38 | pixels[i] = {} 39 | end 40 | 41 | height = y + 1 42 | end 43 | 44 | pixels[y][x] = true 45 | elseif data == CMD_MESSAGE then 46 | local old = command 47 | local message = '' 48 | 49 | command = function(data) 50 | if data ~= 0 then 51 | message = message .. string.char(data) 52 | else 53 | print(message) 54 | command = old 55 | end 56 | end 57 | elseif data == CMD_PRINT_WORD then 58 | local old = command 59 | local word, step = 0, 1 60 | 61 | command = function(data) 62 | if step == 1 then 63 | word = data 64 | elseif step == 2 then 65 | word = word | data << 8 66 | print(string.format('%04x (%u)', word, word)) 67 | command = old 68 | end 69 | 70 | step = step + 1 71 | end 72 | elseif data == CMD_INVERT_PIXEL then 73 | if x >= width then 74 | width = x + 1 75 | end 76 | 77 | if y >= height then 78 | for i = height, y do 79 | pixels[i] = {} 80 | end 81 | 82 | height = y + 1 83 | end 84 | 85 | pixels[y][x] = not pixels[y][x] 86 | elseif data == CMD_SET_XY then 87 | local old = command 88 | local step = 1 89 | 90 | command = function(data) 91 | if step == 1 then 92 | x = data 93 | elseif step == 2 then 94 | y = data 95 | command = old 96 | end 97 | 98 | step = step + 1 99 | end 100 | end 101 | end 102 | 103 | do 104 | local file = assert(io.open('pr11.bin', 'rb')) 105 | local bin = file:read('*a') 106 | file:close() 107 | 108 | for i = 1, #bin do 109 | memory[i - 1] = bin:byte(i) 110 | end 111 | end 112 | 113 | local cpu = z80.init(function(cpu, num_ticks, pins) 114 | done = (pins & z80.HALT) ~= 0 115 | 116 | if (pins & z80.M1) ~= 0 then 117 | local insn = z80.dasm(cpu:pc(), memory, cpu:pc()) 118 | insn = insn:gsub('(%x%x%x%x)h', function(value) return '0000h' end) 119 | --print(string.format('%s ; A=%02x DE=%04x', insn, cpu:a(), cpu:de())) 120 | end 121 | 122 | if (pins & z80.MREQ) ~= 0 then 123 | local address = z80.GET_ADDR(pins) 124 | 125 | if (pins & z80.RD) ~= 0 then 126 | pins = z80.SET_DATA(pins, memory[address]) 127 | elseif (pins & z80.WR) ~= 0 then 128 | memory[address] = z80.GET_DATA(pins) 129 | 130 | if address >= 0xc350 then 131 | --print(string.format('memory[0x%04x] = {data = 0x%02x, pc = 0x%04x} -- %d', address, z80.GET_DATA(pins), cpu:pc(), address - 0xc4ee)) 132 | end 133 | end 134 | elseif (pins & z80.IORQ) ~= 0 then 135 | local address = z80.GET_ADDR(pins) 136 | 137 | if (address & 0xff) == CMD_PORT and (pins & z80.WR) ~= 0 then 138 | local data = z80.GET_DATA(pins) 139 | command(data) 140 | end 141 | end 142 | 143 | return pins 144 | end) 145 | 146 | cpu:set_pc(0xc36f) 147 | cpu:set_sp(0xc338) 148 | 149 | local module = { 150 | 0x3E, 0x08, -- [ 7] ld a, CMD_SET_XY 151 | 0xD3, 0xDE, -- [18] out (CMD_PORT), a 152 | 0x3E, 0x3D, -- [25] ld a, 61 153 | 0x90, -- [29] sub a, b 154 | 0xD3, 0xDE, -- [40] out (CMD_PORT), a 155 | 0x3E, 0x3D, -- [47] ld a, 61 156 | 0x91, -- [51] sub a, c 157 | 0xD3, 0xDE, -- [62] out (CMD_PORT), a 158 | 0x3E, 0x04, -- [ 7] ld a, CMD_SET_PIXEL 159 | 0xD3, 0xDE, -- [18] out (CMD_PORT), a 160 | 0xC9, -- [28] ret 161 | } 162 | 163 | for i = 1, #module do 164 | memory[0xc816 + i - 1] = module[i] 165 | end 166 | 167 | memory[0xc432] = 0x76 168 | 169 | --memory[0xc3ff] = 0x76 -- to end right before printing 170 | 171 | while not done do 172 | cpu:exec(1) 173 | end 174 | 175 | --[[for i = 0xc4ed, 0xc4ed + 4095, 16 do 176 | io.write(string.format('%04x ', i)) 177 | 178 | for j = i, i + 15 do 179 | if j <= 0xc4ed + 4095 then 180 | io.write(string.format(' %02x', memory[j])) 181 | end 182 | end 183 | 184 | io.write(' ') 185 | 186 | for j = i, i + 15 do 187 | if j <= 0xc4ed + 4095 then 188 | io.write(string.format('%c', (memory[j] >= 32 and memory[j] < 127) and memory[j] or string.byte('.'))) 189 | end 190 | end 191 | 192 | io.write('\n') 193 | end]] 194 | 195 | do 196 | local png = imgcreate.png(width, height, 1, function(x, y) 197 | return pixels[y][x] and 0x00 or 0xff 198 | end) 199 | 200 | local file = assert(io.open('pr11.png', 'wb')) 201 | file:write(png) 202 | file:close() 203 | end 204 | -------------------------------------------------------------------------------- /src/debug/imgcreate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | /*---------------------------------------------------------------------------*/ 8 | /* stb_image_write config and inclusion */ 9 | #define STB_IMAGE_WRITE_IMPLEMENTATION 10 | #define STBIW_ASSERT(x) 11 | #define STBI_WRITE_NO_STDIO 12 | #define STB_IMAGE_WRITE_STATIC 13 | #include "stb_image_write.h" 14 | /*---------------------------------------------------------------------------*/ 15 | 16 | typedef enum { 17 | PNG, 18 | BMP, 19 | TGA, 20 | JPEG 21 | } 22 | Format; 23 | 24 | static void const* get_pixels( 25 | lua_State* const L, 26 | int const width, 27 | int const height, 28 | int const components, 29 | int const get_pixel_index) { 30 | 31 | uint8_t* const pixels = (uint8_t*)malloc(width * height * components); 32 | 33 | if (pixels == NULL) { 34 | luaL_error(L, "could not allocate the pixel buffer"); 35 | 36 | // Let the compiler know luaL_error doesn't return. 37 | return NULL; 38 | } 39 | 40 | uint8_t* aux = pixels; 41 | 42 | for (int y = 0; y < height; y++) { 43 | for (int x = 0; x < width; x++) { 44 | lua_pushvalue(L, get_pixel_index); 45 | lua_pushinteger(L, x); 46 | lua_pushinteger(L, y); 47 | 48 | lua_call(L, 2, components); 49 | 50 | for (int i = -components; i <= -1; i++) { 51 | if (!lua_isinteger(L, i)) { 52 | luaL_error(L, "'get_pixel' must return integer components"); 53 | return NULL; 54 | } 55 | 56 | *aux++ = lua_tointeger(L, i); 57 | } 58 | 59 | lua_pop(L, components); 60 | } 61 | } 62 | 63 | return (void*)pixels; 64 | } 65 | 66 | static void write_to_buffer(void* const context, void* const data, int const size) { 67 | luaL_Buffer* const buffer = (luaL_Buffer*)context; 68 | luaL_addlstring(buffer, (char const*)data, (size_t)size); 69 | } 70 | 71 | static int l_create(lua_State* const L, Format const format) { 72 | lua_Integer const width = luaL_checkinteger(L, 1); 73 | lua_Integer const height = luaL_checkinteger(L, 2); 74 | lua_Integer const components = luaL_checkinteger(L, 3); 75 | 76 | int const get_pixel_index = format == JPEG ? 5 : 4; 77 | 78 | luaL_checktype(L, get_pixel_index, LUA_TFUNCTION); 79 | 80 | // Validate common arguments. 81 | if (width <= 0) { 82 | return luaL_error(L, "invalid width, must be greater than zero"); 83 | } 84 | 85 | if (height <= 0) { 86 | return luaL_error(L, "invalid height, must be greater than zero"); 87 | } 88 | 89 | if (components < 1 || components > 4) { 90 | return luaL_error(L, "invalid number of components, must be between 1 and 4 for Y, YA, RGB, or RGBA"); 91 | } 92 | 93 | // Generate the pixels buffer. 94 | void const* const pixels = get_pixels(L, width, height, components, get_pixel_index); 95 | 96 | // The image will be written to a Lua buffer. 97 | luaL_Buffer buffer; 98 | luaL_buffinit(L, &buffer); 99 | 100 | // Create the image. 101 | int result = 0; 102 | 103 | switch (format) { 104 | case PNG: { 105 | int const stride_in_bytes = width * components; 106 | result = stbi_write_png_to_func(write_to_buffer, (void*)&buffer, width, height, components, pixels, stride_in_bytes); 107 | break; 108 | } 109 | 110 | case BMP: 111 | result = stbi_write_bmp_to_func(write_to_buffer, (void*)&buffer, width, height, components, pixels); 112 | break; 113 | 114 | case TGA: 115 | result = stbi_write_tga_to_func(write_to_buffer, (void*)&buffer, width, height, components, pixels); 116 | break; 117 | 118 | case JPEG: { 119 | lua_Integer const quality = luaL_checkinteger(L, 4); 120 | 121 | // Validate the quality. 122 | if (quality < 1 || quality > 100) { 123 | return luaL_error(L, "invalid quality, must be between 1 and 100"); 124 | } 125 | 126 | result = stbi_write_jpg_to_func(write_to_buffer, (void*)&buffer, width, height, components, pixels, quality); 127 | break; 128 | } 129 | } 130 | 131 | // Free the pixels. 132 | free((void*)pixels); 133 | 134 | // Error creating image. 135 | if (result == 0) { 136 | return luaL_error(L, "error creating image"); 137 | } 138 | 139 | // Success, return a string with the created image. 140 | luaL_pushresult(&buffer); 141 | return 1; 142 | } 143 | 144 | static int l_png(lua_State* const L) { 145 | return l_create(L, PNG); 146 | } 147 | 148 | static int l_bmp(lua_State* const L) { 149 | return l_create(L, BMP); 150 | } 151 | 152 | static int l_tga(lua_State* const L) { 153 | return l_create(L, TGA); 154 | } 155 | 156 | static int l_jpeg(lua_State* const L) { 157 | return l_create(L, JPEG); 158 | } 159 | 160 | LUAMOD_API int luaopen_imgcreate(lua_State* const L) { 161 | static const luaL_Reg functions[] = { 162 | {"png", l_png}, 163 | {"bmp", l_bmp}, 164 | {"tga", l_tga}, 165 | {"jpeg",l_jpeg}, 166 | {NULL, NULL} 167 | }; 168 | 169 | static struct {char const* const name; char const* const value;} const info[] = { 170 | {"_COPYRIGHT", "Copyright (c) 2020 Andre Leiradella"}, 171 | {"_LICENSE", "MIT"}, 172 | {"_VERSION", "1.0.0"}, 173 | {"_NAME", "imgcreate"}, 174 | {"_URL", "https://github.com/leiradel/luamods/imgcreate"}, 175 | {"_DESCRIPTION", "Writes images to the file system"} 176 | }; 177 | 178 | size_t const functions_count = sizeof(functions) / sizeof(functions[0]) - 1; 179 | size_t const info_count = sizeof(info) / sizeof(info[0]); 180 | 181 | lua_createtable(L, 0, functions_count + info_count); 182 | luaL_setfuncs(L, functions, 0); 183 | 184 | for (size_t i = 0; i < info_count; i++) { 185 | lua_pushstring(L, info[i].value); 186 | lua_setfield(L, -2, info[i].name); 187 | } 188 | 189 | return 1; 190 | } 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qrc1 2 | 3 | QR Code versions 1 and 11 and Code 2 of 5 generators written in Z80 assembly. 4 | 5 | ## Source Code 6 | 7 | The generators are in: 8 | 9 | * QR Code version 1 10 | * `src/qrc1_rom.asm` 11 | * `src/qrc1_ram.asm` 12 | * QR Code version 11 13 | * `src/qrc11_rom.asm` 14 | * `src/qrc11_ram.asm` 15 | * Code 2 of 5 16 | * `src/i25_rom.asm` 17 | 18 | They don't output anything by themselves, to actually see something some platform specific code must be written that encodes the message and translates it to pixels on the screen. 19 | 20 | The assembly files should assemble with virtually any Z80 assembler since it doesn't use anything but quite standard features found everywhere. If you do find issues when assembling please let me know and I'll try to fix them. 21 | 22 | The QR Code version 11 encoder was contributed by [Daniel A. Nagy](https://github.com/nagydani), with some changes from me to make the plotter code smaller. 23 | 24 | ## Using 25 | 26 | ### QR Code Version 1 27 | 28 | 1. Include the files `src/qrc1_rom.asm` and `src/qrc1_ram.asm` in your project; the former can be in ROM but the later must be in RAM 29 | 1. Implement the following routines for your platform: 30 | * `qrc_pixel_up`: move the current pixel cursor up 31 | * `qrc_pixel_down`: move the current pixel cursor down 32 | * `qrc_pixel_left`: move the current pixel cursor to the left 33 | * `qrc_pixel_right`: move the current pixel cursor to the right 34 | * `qrc_invert_pixel`: invert the current pixel 35 | * These routines are free to use `A`, `C`, `H`, `L`, and `IX`. `A` is changed by the caller so it should be used only for temporary values, but `C`, `H`, `L`, and `IX` are **not** changed and can be used for persistent values. Do not change any other registers. 36 | 1. Set the byte at `qrc1_message` to the message length (maximum 14 bytes) 37 | 1. Write the message bytes after the length 38 | 1. Call `qrc1_encmessage` to encode the message 39 | 1. Make sure the screen area that will receive the pixels for the QR Code is filled with white pixels (platform dependent) 40 | 1. Set `C`, `H`, `L`, and `IX` to represent the top-left pixel of the QR Code in the screen (platform dependent) 41 | 1. Call `qrc1_print` 42 | 43 | ### QR Code Version 11 44 | 45 | 1. Include the files `src/qrc11_rom.asm` and `src/qrc11_ram.asm` in your project; the former can be in ROM but the later must be in RAM 46 | 1. Implement the same routines for your platform as the version 1 47 | 1. Set the byte at `qrc11_message` to the message length (maximum 251 bytes) 48 | 1. Write the message bytes after the length 49 | 1. Call `qrc11_encmessage` to encode the message 50 | 1. Make sure the screen area that will receive the pixels for the QR Code is filled with white pixels (platform dependent) 51 | 1. Set `C`, `H`, `L`, and `IX` to represent the top-left pixel of the QR Code in the screen (platform dependent) 52 | 1. Call `qrc11_print` 53 | 54 | ### Code 2 of 5 55 | 56 | 1. Include the file `src/i25_rom.asm` in your project; it can be in ROM 57 | 1. Define `i25_message` in RAM with 1 plus the maximum message size that will be needed 58 | 1. Implement the same routines for your platform as the QR Code version 1 59 | 1. Set the byte at `i25_message` to the message length; it must be even and the maximum length is only limited by the screen size though 254 is a hard limit imposed by the byte 60 | 1. Write the message ASCII digits after the length 61 | 1. Make sure the screen area that will receive the pixels for the QR Code is filled with white pixels (platform dependent) 62 | 1. Set `C`, `H`, `L`, and `IX` to represent the top-left pixel of the QR Code in the screen (platform dependent) 63 | 1. Call `i25_print` 64 | 65 | Notice that Code 2 of 5 doesn't have a separate encoding routine. 66 | 67 | ## Examples 68 | 69 | ### ZX81 70 | 71 | ![https://cutt.ly/QRC1](https://raw.githubusercontent.com/leiradel/qrc1/master/qrc1.png) 72 | 73 | In the `src/zx81/qrc1.asm` file there is code that plots the encoded message onto the ZX81 screen. It must be used together with `src/zx81/qrc1.bas`, which takes care of user input reading, poking the message into the appropriate memory location for the encoder to do its job, and calling into the machine code routine that will encode the message and draw it onto the screen. 74 | 75 | The Makefile in the `src/zx81` folder uses [Pasmo](http://pasmo.speccy.org/) to assemble the assembly file, and [zxtext2p](http://freestuff.grok.co.uk/zxtext2p/index.html) to convert the BASIC file to a `.p` file. A [Lua](https://www.lua.org/) script will orchestrate everything and produce the final `.p` file to use with an emulator. Just go into `src/zx81` and run `make`. 76 | 77 | Notice that the program must run in FAST mode, as it uses the `IY` register. While it would be possible not to use it, at the expense of performance, there's not really much to see on the screen while the message is encoded and the barcode printed onto the screen. 78 | 79 | > `zxtext2p` has been slightly changed to auto-run the generated program at the first BASIC line. 80 | 81 | ![https://cutt.ly/QRC1](https://raw.githubusercontent.com/leiradel/qrc1/master/i25.png) 82 | 83 | `src/zx81/i25.asm` and `src/zx81/i25.bas` implement code to plot Code 2 of 5 barcodes. 84 | 85 | ### ZX Spectrum 86 | 87 | ![https://cutt.ly/QRC1](https://raw.githubusercontent.com/leiradel/qrc1/master/qrc1zxs.png) 88 | 89 | Similarly to the ZX81, `src/spectrum/qrc1.asm` has code to encode and plot messages as QR Code version 1 for the ZX Spectrum. The `src/spectrum/qrc1.bas` file has a loader for the binary part of the program, and BASIC commands that will read the message, poke it to the appropriate memory location, and call the encoder and plotter. 90 | 91 | [zmakebas](https://github.com/z00m128/zmakebas) was used to convert the BASIC program to a `.tap` file, and a Lua script will take care of building everything and producing the final tape. 92 | 93 | There are three different plotters for the ZX Spectrum, each one using a different size for the barcode modules: 1x1 square pixels, 2x2, and 4x4. It can be easily selected by commenting and uncommenting the appropriate lines at the top of `src/spectrum/zxs1.asm`. 94 | 95 | ![https://cutt.ly/QRC1](https://raw.githubusercontent.com/leiradel/qrc1/master/qrc11.png) 96 | 97 | `src/spectrum/qrc11.asm` and `src/spectrum/qrc11.bas` implement code to plot messages as QR Code version 11. 98 | 99 | ![https://cutt.ly/QRC1](https://raw.githubusercontent.com/leiradel/qrc1/master/i25zxs.png) 100 | 101 | `src/spectrum/i25.asm` and `src/spectrum/i25.bas` implement code to plot Code 2 of 5 barcodes. 102 | 103 | ## Limitations 104 | 105 | The generators are hardcoded to QR Code versions 1 and 11, binary data encoding, ECC level M, and mask 0. While I think a generator capable of encoding messages with any combination of the QR Code capabilities is possible for the Z80, the amount of memory would make it too expensive to embed into other programs that may want to generate them. 106 | 107 | Even with those limits, the generated QR Code version 1 has 14 bytes worth of data which makes it practical for a range of applications. If 14 bytes are not enough, version 11 provides a capacity of up to 251 bytes. 108 | 109 | Code 2 of 5, being a linear barcode, cannot encode as much data as QR Code, but it needs much less code to implement. Using 2x2 pixels on a ZX Spectrum, it can fit 16 decimal digits in the screen. Notice that some barcode readers won't recognize small Code 2 of 5 barcodes with only 2 or 4 digits. Some may be configured to accept such small barcodes. 110 | 111 | ## Etc 112 | 113 | There's a C version at `etc/qrc1.c`, which I wrote only to validate that I could really understand the original JavaScript that encodes the message (available in the second issue of the [Paged Out!](https://pagedout.institute/) magazine). 114 | 115 | There's also a modified version of **zxtext2p** that will auto-run the generated program starting at the first line. 116 | 117 | ## Links 118 | 119 | * [ISO/IEC18004](https://www.swisseduc.ch/informatik/theoretische_informatik/qr_codes/docs/qr_standard.pdf) 120 | * [Paged Out! magazine issue #2](https://pagedout.institute/download/PagedOut_002_beta2.pdf) (page 20) 121 | * [Website for the article published on PagedOut!](https://www.quaxio.com/an_artisanal_qr_code.html) 122 | * [Thonky QR Code Tutorial](https://www.thonky.com/qr-code-tutorial/) 123 | * [Wikipedia QR Code article](https://en.wikipedia.org/wiki/QR_code) 124 | 125 | ## License 126 | 127 | MIT, enjoy. 128 | -------------------------------------------------------------------------------- /src/qrc1_rom.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ; qrc1_message contains the message length followed by the message (maximum 14 4 | ; bytes). 5 | qrc1_encmessage: 6 | ; ------------------------------------------------------------------------ 7 | ; Encode the message. 8 | ; ------------------------------------------------------------------------ 9 | 10 | ; Use byte encoding (0b0100 << 4), length high nibble is always 0 since 11 | ; len <= 14. 12 | ld hl, qrc1_message 13 | ld a, (hl) 14 | ld (hl), $40 15 | inc hl 16 | 17 | ; Save message length for later for the padding. 18 | ld c, a 19 | 20 | ; Shift the message to the right by four bits. 21 | ld b, a 22 | qrc1_shift_msg: 23 | rrd 24 | inc hl 25 | djnz qrc1_shift_msg 26 | 27 | ; A has the low nibble of the last message byte, shift it to the high 28 | ; nibble and set the low nibble to 0, which is the end of message mark. 29 | ld (hl), b ; B is zero here 30 | rrd 31 | inc hl 32 | 33 | ; Pad the rest of the message with $ec and $11. 34 | ld a, 14 35 | sub c 36 | jr z, qrc1_no_padding 37 | 38 | ld b, a 39 | ld a, $ec 40 | qrc1_pad_msg: 41 | ld (hl), a 42 | inc hl 43 | xor $fd 44 | djnz qrc1_pad_msg 45 | 46 | qrc1_no_padding: 47 | 48 | ; ------------------------------------------------------------------------ 49 | ; Calculate the message ECC. 50 | ; ------------------------------------------------------------------------ 51 | 52 | ; Copy the original encoded message to the scratch buffer, the ECC 53 | ; evaluation will overwrite it so we need to restore it at the end. 54 | ld hl, qrc1_message 55 | ld de, qrc1_scratch 56 | ld bc, 16 57 | ldir 58 | 59 | ; Zero the 10 bytes where the ECC will be stored. 60 | xor a 61 | ld b, 10 62 | qrc1_zero_ecc: 63 | ld (hl), a 64 | inc hl 65 | djnz qrc1_zero_ecc 66 | 67 | ; HL is the polynomial A. 68 | ld hl, qrc1_message 69 | 70 | ; IYL is the outer loop counter (i) for the length of A. 71 | ld iyl, 16 72 | qrc1_loop_i: 73 | ; Save HL as it'll be incremented in the inner loop. 74 | push hl 75 | 76 | ; Save A[i] in B to be used inside the inner loop. 77 | ld b, (hl) 78 | 79 | ; DE is the polynomial B. 80 | ld de, qrc1_ecc_poly 81 | 82 | ; Evaluate the inner loop count limit. 83 | ld a, 11 84 | add a, iyl 85 | dec a 86 | 87 | ; IYH is inner loop counter (j) up to length(A) - i. 88 | ld iyh, a 89 | qrc1_loop_j: 90 | ; A is B[j] 91 | ld a, (de) 92 | 93 | ; Save DE as we'll use D and E in the gf_mod loop. 94 | push de 95 | 96 | ; D is A[i], E is the gf_mod result. 97 | ld d, b 98 | ld e, 0 99 | 100 | ; A is x, D is y, E is r, C is a scratch register. 101 | jr qrc1_test_y 102 | 103 | qrc1_xor_res: 104 | ; y had the 0th bit set, r ^= x. 105 | ld c, a 106 | xor e 107 | ld e, a 108 | ld a, c 109 | qrc1_dont_xor: 110 | ; x <<= 1, set carry if x >= 256. 111 | add a, a 112 | jr nc, qrc1_test_y 113 | 114 | ; x was >= 256, xor it with the module. 115 | xor 285 & $ff 116 | qrc1_test_y: 117 | ; y >>= 1, update r if the 0th bit is set, end the loop if 118 | ; it's zero. 119 | srl d 120 | jr c, qrc1_xor_res 121 | jr nz, qrc1_dont_xor 122 | 123 | ; A[i + j] ^= gf_mod(...) 124 | ld a, (hl) 125 | xor e 126 | ld (hl), a 127 | 128 | ; Restore DE. 129 | pop de 130 | 131 | ; Update HL and DE to point to the next bytes of A and B. 132 | inc hl 133 | inc de 134 | 135 | ; Inner loop test. 136 | dec iyh 137 | jr nz, qrc1_loop_j 138 | 139 | ; Restore HL since it was changed in the inner loop, and make it point 140 | ; to the next byte in A. 141 | pop hl 142 | inc hl 143 | 144 | ; Outer loop test. 145 | dec iyl 146 | jr nz, qrc1_loop_i 147 | 148 | ; Restore the original encoded message, since the loops above zeroed it. 149 | ld hl, qrc1_scratch 150 | ld de, qrc1_message 151 | ld bc, 16 152 | ldir 153 | 154 | ; All done, there's no need to perform the masking step because the mask is 155 | ; embedded in the fixed modules binary pattern so it's automatically 156 | ; applied when the modules are plotted. 157 | ret 158 | 159 | qrc1_print: 160 | ; Draw the fixed modules. 161 | ld de, qrc1_fixed_modules 162 | 163 | ; 21 lines. 164 | ld iyl, 21 165 | 166 | qrc1_print_line: 167 | ; Each line has 3 bytes to cover 21 modules. 168 | ld iyh, 3 169 | 170 | qrc1_print_byte: 171 | ld a, (de) 172 | inc de 173 | 174 | ; Set the module depending on the bit pattern. 175 | ld b, 8 176 | 177 | qrc1_print_bits: 178 | rla 179 | 180 | push af 181 | call c, qrc_invert_pixel 182 | call qrc_pixel_right 183 | pop af 184 | djnz qrc1_print_bits 185 | 186 | dec iyh 187 | jr nz, qrc1_print_byte 188 | 189 | ; When all rows have been printed, leave the cursor one module to the 190 | ; right of the bottom-right module. 191 | dec iyl 192 | jr z, qrc1_print_end 193 | 194 | ; Otherwise, go down on pixel and left 24 pixels to start a new line. 195 | call qrc_pixel_down 196 | ld b, 12 197 | 198 | qrc1_print_cr: 199 | call qrc1_pixel_left_2 200 | djnz qrc1_print_cr 201 | 202 | jr qrc1_print_line 203 | 204 | qrc1_print_end: 205 | 206 | ; Move the cursor to the first module of the encoded message. 207 | call qrc1_pixel_left_2 208 | call qrc1_pixel_left_2 209 | 210 | ; Message bits. 211 | ld de, qrc1_message 212 | ld b, $80 ; Most significant bit 213 | 214 | ; Print command sequence. 215 | ld iy, qrc1_print_cmds 216 | 217 | qrc1_print_loop: 218 | ; Run the command in the upper nibble. 219 | ld a, (iy) 220 | rra 221 | rra 222 | rra 223 | rra 224 | call qrc1_run_command 225 | 226 | ; Run the command in the lower nibble. 227 | ld a, (iy) 228 | inc iy 229 | call qrc1_run_command 230 | 231 | ; The qrc1_cmd_print_ended command discards the top address in the stack 232 | ; and returns, meaning that it will end this loop and return to the caller 233 | ; of qrc1_print. 234 | jr qrc1_print_loop 235 | 236 | qrc1_run_command: 237 | ; Runs the command in the lower nibble of A. This seems to be more lengthy 238 | ; than using a lookup table (LUT), but the code to use the LUT plus the LUT 239 | ; itself are bigger than the code here. 240 | and $0f 241 | jr z, qrc1_pixel_up_1 242 | dec a 243 | jr z, qrc1_pixel_down_1 244 | dec a 245 | jr z, qrc1_pixel_left_2 246 | dec a 247 | jr z, qrc1_pixel_left_1 248 | dec a 249 | jr z, qrc1_uturn_up 250 | dec a 251 | jr z, qrc1_uturn_down 252 | dec a 253 | jr z, qrc1_pixel_up_9 254 | dec a 255 | jr z, qrc1_bit_up_28 256 | dec a 257 | jr z, qrc1_bit_up_24 258 | dec a 259 | jr z, qrc1_bit_up_12 260 | dec a 261 | jr z, qrc1_bit_up_8 262 | dec a 263 | jr z, qrc1_bit_down_28 264 | dec a 265 | jr z, qrc1_bit_down_24 266 | dec a 267 | jr z, qrc1_bit_down_12 268 | dec a 269 | jr z, qrc1_bit_down_8 270 | 271 | ; This handles the qrc1_cmd_print_ended command. 272 | pop af 273 | ret 274 | 275 | ; Skips two pixels to the left. 276 | qrc1_pixel_left_2: 277 | call qrc_pixel_left 278 | ; fallthrough 279 | 280 | ; Skips one pixel to the left. 281 | qrc1_pixel_left_1: 282 | jp qrc_pixel_left 283 | 284 | ; Makes a U-turn to start going up. 285 | qrc1_uturn_up: 286 | call qrc_pixel_left 287 | call qrc_pixel_left 288 | ; fallthrough 289 | 290 | ; Skips one pixel up. 291 | qrc1_pixel_up_1: 292 | jp qrc_pixel_up 293 | 294 | ; Makes an U-turn to start going down. 295 | qrc1_uturn_down: 296 | call qrc_pixel_left 297 | call qrc_pixel_left 298 | ; fallthrough 299 | 300 | ; Skips one pixel down. 301 | qrc1_pixel_down_1: 302 | jp qrc_pixel_down 303 | 304 | ; Skips nine pixels up. 305 | qrc1_pixel_up_9: 306 | push iy 307 | ld iyl, 9 308 | qrc1_pixel_up_9_loop: 309 | call qrc_pixel_up 310 | dec iyl 311 | jr nz, qrc1_pixel_up_9_loop 312 | pop iy 313 | ret 314 | 315 | ; Plots 28 modules up in zig-zag. 316 | qrc1_bit_up_28: 317 | call qrc1_bit_up_4 318 | ; fallthrough 319 | 320 | ; Plots 24 modules up in zig-zag. 321 | qrc1_bit_up_24: 322 | call qrc1_bit_up_12 323 | ; fallthrough 324 | 325 | ; Plots 12 moudles up in zig-zag. 326 | qrc1_bit_up_12: 327 | call qrc1_bit_up_4 328 | ; fallthrough 329 | 330 | ; Plots 8 modules up in zig-zag. 331 | qrc1_bit_up_8: 332 | call qrc1_bit_up_4 333 | ; fallthrough 334 | 335 | ; Plots 4 modules up in zig-zag. 336 | qrc1_bit_up_4: 337 | call qrc1_set_pixel_if 338 | call qrc_pixel_left 339 | call qrc1_set_pixel_if 340 | call qrc_pixel_up 341 | call qrc_pixel_right 342 | call qrc1_set_pixel_if 343 | call qrc_pixel_left 344 | call qrc1_set_pixel_if 345 | call qrc_pixel_up 346 | jp qrc_pixel_right 347 | 348 | ; Plots 28 modules down in zig-zag. 349 | qrc1_bit_down_28: 350 | call qrc1_bit_down_4 351 | ; fallthrough 352 | 353 | ; Plots 24 modules down in zig-zag. 354 | qrc1_bit_down_24: 355 | call qrc1_bit_down_12 356 | ; fallthrough 357 | 358 | ; Plots 12 modules down in zig-zag. 359 | qrc1_bit_down_12: 360 | call qrc1_bit_down_4 361 | ; fallthrough 362 | 363 | ; Plots 8 modules down in zig-zag. 364 | qrc1_bit_down_8: 365 | call qrc1_bit_down_4 366 | ; fallthrough 367 | 368 | ; Plots 4 modules down in zig-zag. 369 | qrc1_bit_down_4: 370 | call qrc1_set_pixel_if 371 | call qrc_pixel_left 372 | call qrc1_set_pixel_if 373 | call qrc_pixel_down 374 | call qrc_pixel_right 375 | call qrc1_set_pixel_if 376 | call qrc_pixel_left 377 | call qrc1_set_pixel_if 378 | call qrc_pixel_down 379 | jp qrc_pixel_right 380 | 381 | ; Plots a module if the corresponding bit in the encoded message is set. 382 | qrc1_set_pixel_if: 383 | ld a, (de) 384 | and b 385 | call nz, qrc_invert_pixel 386 | 387 | ; Skip to the next bit. 388 | rrc b 389 | ret nc 390 | 391 | ; Increment the pointer to the encoded message if the bit in B wrapped. 392 | inc de 393 | ret 394 | 395 | ; Print commands constants to encode the print commands sequence below. 396 | qrc1_cmd_pixel_up_1 equ 0 397 | qrc1_cmd_pixel_down_1 equ 1 398 | qrc1_cmd_pixel_left_2 equ 2 399 | qrc1_cmd_pixel_left_1 equ 3 400 | qrc1_cmd_uturn_up equ 4 401 | qrc1_cmd_uturn_down equ 5 402 | qrc1_cmd_pixel_up_9 equ 6 403 | qrc1_cmd_bit_up_28 equ 7 404 | qrc1_cmd_bit_up_24 equ 8 405 | qrc1_cmd_bit_up_12 equ 9 406 | qrc1_cmd_bit_up_8 equ 10 407 | qrc1_cmd_bit_down_28 equ 11 408 | qrc1_cmd_bit_down_24 equ 12 409 | qrc1_cmd_bit_down_12 equ 13 410 | qrc1_cmd_bit_down_8 equ 14 411 | qrc1_cmd_print_ended equ 15 412 | 413 | ; Print commands sequence that drives the printing of the QR Code. 414 | qrc1_print_cmds: 415 | db qrc1_cmd_bit_up_24 << 4 | qrc1_cmd_uturn_down 416 | db qrc1_cmd_bit_down_24 << 4 | qrc1_cmd_uturn_up 417 | db qrc1_cmd_bit_up_24 << 4 | qrc1_cmd_uturn_down 418 | db qrc1_cmd_bit_down_24 << 4 | qrc1_cmd_uturn_up 419 | db qrc1_cmd_bit_up_28 << 4 | qrc1_cmd_pixel_up_1 420 | db qrc1_cmd_bit_up_12 << 4 | qrc1_cmd_uturn_down 421 | db qrc1_cmd_bit_down_12 << 4 | qrc1_cmd_pixel_down_1 422 | db qrc1_cmd_bit_down_28 << 4 | qrc1_cmd_pixel_left_2 423 | db qrc1_cmd_pixel_up_9 << 4 | qrc1_cmd_bit_up_8 424 | db qrc1_cmd_uturn_down << 4 | qrc1_cmd_pixel_left_1 425 | db qrc1_cmd_bit_down_8 << 4 | qrc1_cmd_uturn_up 426 | db qrc1_cmd_bit_up_8 << 4 | qrc1_cmd_uturn_down 427 | db qrc1_cmd_bit_down_8 << 4 | qrc1_cmd_print_ended 428 | 429 | ; The fixed modules encoded in binary. The checkerboard mask is already 430 | ; included, so there's no separate masking operation. 431 | qrc1_fixed_modules: 432 | db $fe, $2b, $f8 433 | db $82, $d2, $08 434 | db $ba, $2a, $e8 435 | db $ba, $52, $e8 436 | db $ba, $aa, $e8 437 | db $82, $52, $08 438 | db $fe, $ab, $f8 439 | db $00, $50, $00 440 | db $aa, $28, $90 441 | db $55, $55, $50 442 | db $aa, $aa, $a8 443 | db $55, $55, $50 444 | db $aa, $aa, $a8 445 | db $00, $d5, $50 446 | db $fe, $2a, $a8 447 | db $82, $55, $50 448 | db $ba, $aa, $a8 449 | db $ba, $55, $50 450 | db $ba, $aa, $a8 451 | db $82, $55, $50 452 | db $fe, $aa, $a8 453 | -------------------------------------------------------------------------------- /etc/qrc1.c: -------------------------------------------------------------------------------- 1 | // https://github.com/leiradel/qrc1 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // An encoded message and it's checkerboard mask. 8 | typedef struct { 9 | uint8_t bytes[16]; 10 | uint8_t mask[16]; 11 | } 12 | qrc1_Message; 13 | 14 | // The ECC level M for the message. 15 | typedef struct { 16 | uint8_t bytes[10]; 17 | } 18 | qrc1_Ecc; 19 | 20 | // The format info with the ECC level and mask number. 21 | typedef struct { 22 | uint8_t bytes[15]; 23 | } 24 | qrc1_FormatInfo; 25 | 26 | // A stream of bits to read the message and the mask. 27 | typedef struct { 28 | uint8_t const* bytes; 29 | uint8_t bit; 30 | } 31 | qrc1_Bitstream; 32 | 33 | // A pixel cursor in a 1 bpp image. 34 | typedef struct { 35 | uint8_t* byte; 36 | uint8_t bit; 37 | } 38 | qrc1_Cursor; 39 | 40 | // A QR Code 1 bpp image. 41 | typedef struct { 42 | uint8_t bytes[21 * 3]; 43 | } 44 | qrc1_QRCode; 45 | 46 | static void qrc1_encmessage(uint8_t const* const data, size_t const length, qrc1_Message* message) { 47 | // Maximum message size is 14 because we need 4 bits for the encoding 48 | // type, 4 bits for the end of message mark, and 8 bits for the message 49 | // length. 50 | size_t const maxlen = 14; 51 | size_t const len = length > maxlen ? maxlen : length; 52 | 53 | // Use byte encoding (0b0100 << 4), the length high nibble is always 0 54 | // since len <= 14. 55 | message->bytes[0] = 0x40; 56 | // Length low nibble. 57 | message->bytes[1] = ((uint8_t)len & 15) << 4; 58 | 59 | // Copy the data taking care of the correct nibbles; the second byte 60 | // already has the length high nibble. 61 | for (size_t i = 0; i < len; i++) { 62 | message->bytes[i + 1] |= data[i] >> 4; 63 | message->bytes[i + 2] = (data[i] & 15) << 4; 64 | } 65 | 66 | // The last data nibble was written to the high nibble of the last message 67 | // byte, so the last byte already has a zeroed low nibble which is the 68 | // end of message indicator. 69 | 70 | // Pad the unused bytes of the message alternating 0xec and 0x11. 71 | { 72 | uint8_t padding = 0xec; 73 | 74 | for (size_t i = len; i < 14; i++, padding ^= 0xfd) { 75 | message->bytes[i + 2] = padding; 76 | } 77 | } 78 | 79 | // The checkerboard mask, with bits corresponding to the message bits. The 80 | // first nibble is 0 because it corresponds to the encoding type in the 81 | // encoded message (0b0100, byte encoding) which is not masked. 82 | static uint8_t const mask[16] = { 83 | 0x09, 0x99, 0x99, 0x66, 0x66, 0x66, 0x99, 0x99, 84 | 0x99, 0x66, 0x66, 0x66, 0x99, 0x99, 0x99, 0x96 85 | }; 86 | 87 | // Set the bits for the message bits where the mask must be applied, which 88 | // excludes the encoding and the end mark 89 | 90 | // Copy the mask to the destination, and zero the four bits that 91 | // correspond to the end of message mark in the encoded message which 92 | // are not masked. 93 | memcpy(message->mask, mask, 16); 94 | message->mask[len + 1] &= 0xf0; 95 | } 96 | 97 | // Galois Field multiplication, it's a black box. 98 | static uint8_t qrc1_gfmul(uint16_t x, uint8_t y, uint16_t const mod) { 99 | uint8_t r = 0; 100 | 101 | while (y != 0) { 102 | if (y & 1) { 103 | r ^= (uint8_t)x; 104 | } 105 | 106 | y >>= 1; 107 | x <<= 1; 108 | 109 | if (x > 255) { 110 | x ^= mod; 111 | } 112 | } 113 | 114 | return r; 115 | } 116 | 117 | // Polynomial modulus, it's also a black box. 118 | static void qrc1_polymod(uint8_t* const a, 119 | size_t const lena, 120 | uint8_t const* const b, 121 | size_t const lenb, 122 | uint16_t const mod) { 123 | 124 | // Zero the part of A that will contain the modulus. 125 | memset(a + lena, 0, lenb - 1); 126 | size_t const maxlena = lena + lenb - 1; 127 | 128 | // Perform the modulus. 129 | for (size_t i = 0; i < lena; i++) { 130 | uint8_t const f = a[i]; 131 | 132 | for (size_t j = 0; j < (maxlena - i); j++) { 133 | a[i + j] ^= qrc1_gfmul(j < lenb ? b[j] : 0, f, mod); 134 | } 135 | } 136 | } 137 | 138 | // Evaluates the ECC leve M for the encoded message. 139 | static void qrc1_messageecc(qrc1_Message const* const message, qrc1_Ecc* const ecc) { 140 | // Append 10 zeroes to the message to open space for the ECC. 141 | uint8_t msg[26]; 142 | memcpy(msg, message->bytes, 16); 143 | memset(msg + 16, 0, 10); 144 | 145 | // Pre-calculated polynomial for ECC level M. 146 | static uint8_t const generator[11] = {1, 216, 194, 159, 111, 199, 94, 95, 113, 157, 193}; 147 | 148 | // Evaluate the ECC and copy to the destination. 149 | qrc1_polymod(msg, 16, generator, 11, 285); 150 | memcpy(ecc->bytes, msg + 16, 10); 151 | } 152 | 153 | // Evaluates the format info, this could be hardcoded since we only support 154 | // ECC level M and the checkerboard mask, but it's useful to have it here and 155 | // be able to generate other format infos if necessary. 156 | static void qrc1_formatinfo(qrc1_FormatInfo* const fmtinfo) { 157 | // Create the format info: 0b00 for ECC level M, 0b000 for the 158 | // checkerboard mask, 10 zeroed bytes for the ECC of the format info. 159 | static uint8_t const info[5] = {0, 0, 0, 0, 0}; 160 | memcpy(fmtinfo->bytes, info, 5); 161 | memset(fmtinfo->bytes + 5, 0, 10); 162 | 163 | // Polynomial for the format info ECC. 164 | static uint8_t const generator[11] = {1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1}; 165 | 166 | // Evaluate the ECC, the modulus argument is not important for 167 | // qrc1_polymod here because both polynomials only have 0 and 1 in them. 168 | qrc1_polymod(fmtinfo->bytes, 5, generator, 11, 0 /*1335*/); 169 | 170 | // Put back the ECC level and the mask index (qrc1_polymod zeroes them). 171 | memcpy(fmtinfo->bytes, info, 5); 172 | 173 | // Apply the format info mask. 174 | for (size_t i = 0; i < 15; i++) { 175 | static uint8_t const mask[15] = {1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; 176 | fmtinfo->bytes[i] ^= mask[i]; 177 | } 178 | } 179 | 180 | // Returns the next bit from the bit stream. 181 | static uint8_t qrc1_getbit(qrc1_Bitstream* const bs) { 182 | uint8_t const bit = -((*bs->bytes & bs->bit) != 0); 183 | bs->bit >>= 1; 184 | 185 | if (bs->bit == 0) { 186 | bs->bit = 0x80; 187 | bs->bytes++; 188 | } 189 | 190 | return bit; 191 | } 192 | 193 | // Moves the cursor one pixel to the left. 194 | static void qrc1_cursorleft(qrc1_Cursor* const cursor) { 195 | cursor->bit <<= 1; 196 | 197 | if (cursor->bit == 0) { 198 | cursor->bit = 0x01; 199 | cursor->byte -= 1; 200 | } 201 | } 202 | 203 | // Moves the cursor one pixel to the right. 204 | static void qrc1_cursorright(qrc1_Cursor* const cursor) { 205 | cursor->bit >>= 1; 206 | 207 | if (cursor->bit == 0) { 208 | cursor->bit = 0x80; 209 | cursor->byte += 1; 210 | } 211 | } 212 | 213 | // Moves the cursor one pixel up. 214 | static void qrc1_cursorup(qrc1_Cursor* const cursor) { 215 | cursor->byte -= 3; 216 | } 217 | 218 | // Moves the cursor one pixel down. 219 | static void qrc1_cursordown(qrc1_Cursor* const cursor) { 220 | cursor->byte += 3; 221 | } 222 | 223 | // Set the current pixel depending on the mask. 224 | static void qrc1_setblock(qrc1_Cursor const* const cursor, uint8_t const mask) { 225 | *cursor->byte |= cursor->bit & mask; 226 | } 227 | 228 | // Draws one nibble from the given bitstream, using the given mask, moving up. 229 | static void qrc1_nibbleup(qrc1_Cursor* const cursor, qrc1_Bitstream* const bits, qrc1_Bitstream* const mask) { 230 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 231 | qrc1_cursorleft(cursor); 232 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 233 | qrc1_cursorup(cursor); qrc1_cursorright(cursor); 234 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 235 | qrc1_cursorleft(cursor); 236 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 237 | qrc1_cursorup(cursor); qrc1_cursorright(cursor); 238 | } 239 | 240 | // Draws one nibble from the given bitstream, using the given mask, moving down. 241 | static void qrc1_nibbledown(qrc1_Cursor* const cursor, qrc1_Bitstream* const bits, qrc1_Bitstream* const mask) { 242 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 243 | qrc1_cursorleft(cursor); 244 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 245 | qrc1_cursordown(cursor); qrc1_cursorright(cursor); 246 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 247 | qrc1_cursorleft(cursor); 248 | qrc1_setblock(cursor, qrc1_getbit(bits) ^ qrc1_getbit(mask)); 249 | qrc1_cursordown(cursor); qrc1_cursorright(cursor); 250 | } 251 | 252 | // Generate the QR Code for the given encoded message, ECC, and format info. 253 | static void qrc1_generate(qrc1_Message const* const message, 254 | qrc1_Ecc const* const ecc, 255 | qrc1_FormatInfo const* const fmtinfo, 256 | qrc1_QRCode* const qrcode) { 257 | 258 | // The fixed blocks of a QR Code version 1. 259 | static uint8_t const fixed[21 * 3] = { 260 | 0xfe, 0x03, 0xf8, // #######. ......## #####... 261 | 0x82, 0x02, 0x08, // #.....#. ......#. ....#... 262 | 0xba, 0x02, 0xe8, // #.###.#. ......#. ###.#... 263 | 0xba, 0x02, 0xe8, // #.###.#. ......#. ###.#... 264 | 0xba, 0x02, 0xe8, // #.###.#. ......#. ###.#... 265 | 0x82, 0x02, 0x08, // #.....#. ......#. ....#... 266 | 0xfe, 0xab, 0xf8, // #######. #.#.#.## #####... 267 | 0x00, 0x00, 0x00, // ........ ........ ........ 268 | 0x02, 0x00, 0x00, // ......#. ........ ........ 269 | 0x00, 0x00, 0x00, // ........ ........ ........ 270 | 0x02, 0x00, 0x00, // ......#. ........ ........ 271 | 0x00, 0x00, 0x00, // ........ ........ ........ 272 | 0x02, 0x00, 0x00, // ......#. ........ ........ 273 | 0x00, 0x80, 0x00, // ........ #....... ........ 274 | 0xfe, 0x00, 0x00, // #######. ........ ........ 275 | 0x82, 0x00, 0x00, // #.....#. ........ ........ 276 | 0xba, 0x00, 0x00, // #.###.#. ........ ........ 277 | 0xba, 0x00, 0x00, // #.###.#. ........ ........ 278 | 0xba, 0x00, 0x00, // #.###.#. ........ ........ 279 | 0x82, 0x00, 0x00, // #.....#. ........ ........ 280 | 0xfe, 0x00, 0x00, // #######. ........ ........ 281 | }; 282 | 283 | // Overwrite the image with the fixed part. 284 | memcpy(qrcode->bytes, fixed, 21 * 3); 285 | 286 | // Draw the format info. 287 | qrc1_Cursor cursor; 288 | cursor.byte = qrcode->bytes + 8 * 3; 289 | cursor.bit = 0x80; 290 | 291 | for (int i = 0; i < 6; i++) { 292 | qrc1_setblock(&cursor, -fmtinfo->bytes[i]); 293 | qrc1_cursorright(&cursor); 294 | } 295 | 296 | qrc1_cursorright(&cursor); 297 | qrc1_setblock(&cursor, -fmtinfo->bytes[6]); 298 | qrc1_cursorright(&cursor); 299 | qrc1_setblock(&cursor, -fmtinfo->bytes[7]); 300 | qrc1_cursorup(&cursor); 301 | qrc1_setblock(&cursor, -fmtinfo->bytes[8]); 302 | qrc1_cursorup(&cursor); 303 | 304 | for (int i = 0; i < 6; i++) { 305 | qrc1_cursorup(&cursor); 306 | qrc1_setblock(&cursor, -fmtinfo->bytes[i + 9]); 307 | } 308 | 309 | cursor.byte = qrcode->bytes + 20 * 3 + 1; 310 | cursor.bit = 0x80; 311 | 312 | for (int i = 0; i < 7; i++) { 313 | qrc1_setblock(&cursor, -fmtinfo->bytes[i]); 314 | qrc1_cursorup(&cursor); 315 | } 316 | 317 | for (int i = 0; i < 5; i++) { 318 | qrc1_cursorup(&cursor); 319 | qrc1_cursorright(&cursor); 320 | } 321 | 322 | for (int i = 0; i < 8; i++) { 323 | qrc1_setblock(&cursor, -fmtinfo->bytes[i + 7]); 324 | qrc1_cursorright(&cursor); 325 | } 326 | 327 | // Draw the encoded message using the mask. 328 | qrc1_Bitstream bs; 329 | bs.bit = 0x80; 330 | bs.bytes = message->bytes; 331 | 332 | qrc1_Bitstream mask; 333 | mask.bit = 0x80; 334 | mask.bytes = message->mask; 335 | 336 | cursor.byte = qrcode->bytes + 21 * 3 - 1; 337 | cursor.bit = 0x08; 338 | 339 | for (int i = 0; i < 6; i++) { 340 | qrc1_nibbleup(&cursor, &bs, &mask); 341 | } 342 | 343 | qrc1_cursordown(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 344 | 345 | for (int i = 0; i < 6; i++) { 346 | qrc1_nibbledown(&cursor, &bs, &mask); 347 | } 348 | 349 | qrc1_cursorup(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 350 | 351 | for (int i = 0; i < 6; i++) { 352 | qrc1_nibbleup(&cursor, &bs, &mask); 353 | } 354 | 355 | qrc1_cursordown(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 356 | 357 | for (int i = 0; i < 6; i++) { 358 | qrc1_nibbledown(&cursor, &bs, &mask); 359 | } 360 | 361 | qrc1_cursorup(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 362 | 363 | for (int i = 0; i < 7; i++) { 364 | qrc1_nibbleup(&cursor, &bs, &mask); 365 | } 366 | 367 | qrc1_cursorup(&cursor); 368 | qrc1_nibbleup(&cursor, &bs, &mask); 369 | 370 | // Draw the ECC using the mask. 371 | static uint8_t const eccmask[10] = {0x66, 0x99, 0x96, 0x66, 0x66, 0x66, 0x99, 0x99, 0x66, 0x99}; 372 | 373 | bs.bit = 0x80; 374 | bs.bytes = ecc->bytes; 375 | 376 | mask.bit = 0x80; 377 | mask.bytes = eccmask; 378 | 379 | qrc1_nibbleup(&cursor, &bs, &mask); 380 | qrc1_nibbleup(&cursor, &bs, &mask); 381 | qrc1_cursordown(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 382 | 383 | qrc1_nibbledown(&cursor, &bs, &mask); 384 | qrc1_nibbledown(&cursor, &bs, &mask); 385 | qrc1_nibbledown(&cursor, &bs, &mask); 386 | qrc1_cursordown(&cursor); 387 | 388 | for (int i = 0; i < 7; i++) { 389 | qrc1_nibbledown(&cursor, &bs, &mask); 390 | } 391 | 392 | cursor.byte = qrcode->bytes + 12 * 3 + 1; 393 | cursor.bit = 0x80; 394 | 395 | qrc1_nibbleup(&cursor, &bs, &mask); 396 | qrc1_nibbleup(&cursor, &bs, &mask); 397 | qrc1_cursordown(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 398 | qrc1_cursorleft(&cursor); 399 | 400 | qrc1_nibbledown(&cursor, &bs, &mask); 401 | qrc1_nibbledown(&cursor, &bs, &mask); 402 | qrc1_cursorup(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 403 | 404 | qrc1_nibbleup(&cursor, &bs, &mask); 405 | qrc1_nibbleup(&cursor, &bs, &mask); 406 | qrc1_cursordown(&cursor); qrc1_cursorleft(&cursor); qrc1_cursorleft(&cursor); 407 | 408 | qrc1_nibbledown(&cursor, &bs, &mask); 409 | qrc1_nibbledown(&cursor, &bs, &mask); 410 | } 411 | 412 | #include 413 | #include 414 | 415 | int main(int const argc, char const* const argv[]) { 416 | if (argc != 2) { 417 | return EXIT_FAILURE; 418 | } 419 | 420 | // Encode the message. 421 | qrc1_Message message; 422 | qrc1_encmessage(argv[1], strlen(argv[1]), &message); 423 | 424 | // Evaluate the message ECC. 425 | qrc1_Ecc ecc; 426 | qrc1_messageecc(&message, &ecc); 427 | 428 | // Encode the format info along with its ECC. 429 | qrc1_FormatInfo fmtinfo; 430 | qrc1_formatinfo(&fmtinfo); 431 | 432 | // Draw the resulting QR Code. 433 | qrc1_QRCode qrcode; 434 | qrc1_generate(&message, &ecc, &fmtinfo, &qrcode); 435 | 436 | // Output the QR Code. 437 | for (int y = 0; y < 3; y++) { 438 | for (int x = 0; x < 54; x++) { 439 | printf("\u2588"); 440 | } 441 | 442 | printf("\n"); 443 | } 444 | 445 | for (int y = 0; y < 21; y++) { 446 | printf("\u2588\u2588\u2588\u2588\u2588\u2588"); 447 | 448 | for (int x = 0; x < 3; x++) { 449 | uint8_t const b = qrcode.bytes[y * 3 + x]; 450 | 451 | for (uint8_t m = 0x80; m != 0; m >>= 1) { 452 | printf("%s", b & m ? " " : "\u2588\u2588"); 453 | } 454 | } 455 | 456 | printf("\n"); 457 | } 458 | 459 | for (int y = 0; y < 3; y++) { 460 | for (int x = 0; x < 54; x++) { 461 | printf("\u2588"); 462 | } 463 | 464 | printf("\n"); 465 | } 466 | 467 | return EXIT_SUCCESS; 468 | } 469 | -------------------------------------------------------------------------------- /src/debug/z80dasm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /*# 3 | # z80dasm.h 4 | 5 | A stateless Z80 disassembler that doesn't call any CRT functions. 6 | 7 | Do this: 8 | ~~~C 9 | #define CHIPS_IMPL 10 | ~~~ 11 | before you include this file in *one* C or C++ file to create the 12 | implementation. 13 | 14 | Optionally provide the following macros with your own implementation 15 | 16 | ~~~C 17 | CHIPS_ASSERT(c) 18 | ~~~ 19 | your own assert macro (default: assert(c)) 20 | 21 | ## Usage 22 | 23 | There's only one function to call which consumes a stream of instruction bytes 24 | and produces a stream of ASCII characters for exactly one instruction: 25 | 26 | ~~~C 27 | uint16_t z80dasm_op(uint16_t pc, z80dasm_input_t in_cb, z80dasm_output_t out_cb, void* user_data) 28 | ~~~ 29 | 30 | pc - the current 16-bit program counter, this is used to compute 31 | absolute target addresses for relative jumps 32 | in_cb - this function is called when the disassembler needs the next 33 | instruction byte: uint8_t in_cb(void* user_data) 34 | out_cb - (optional) this function is called when the disassembler produces a single 35 | ASCII character: void out_cb(char c, void* user_data) 36 | user_data - a user-provided context pointer for the callbacks 37 | 38 | z80dasm_op() returns the new program counter (pc), this should be 39 | used as input arg when calling z80dasm_op() for the next instruction. 40 | 41 | NOTE that the output callback will never be called with a null character, 42 | you need to terminate the resulting string yourself if needed. 43 | 44 | All undocumented instructions are supported, but are currently 45 | not marked as such. 46 | 47 | ## Links 48 | 49 | The disassembler uses this decoding strategy: 50 | 51 | http://www.z80.info/decoding.htm 52 | 53 | ## zlib/libpng license 54 | 55 | Copyright (c) 2018 Andre Weissflog 56 | This software is provided 'as-is', without any express or implied warranty. 57 | In no event will the authors be held liable for any damages arising from the 58 | use of this software. 59 | Permission is granted to anyone to use this software for any purpose, 60 | including commercial applications, and to alter it and redistribute it 61 | freely, subject to the following restrictions: 62 | 1. The origin of this software must not be misrepresented; you must not 63 | claim that you wrote the original software. If you use this software in a 64 | product, an acknowledgment in the product documentation would be 65 | appreciated but is not required. 66 | 2. Altered source versions must be plainly marked as such, and must not 67 | be misrepresented as being the original software. 68 | 3. This notice may not be removed or altered from any source 69 | distribution. 70 | #*/ 71 | #include 72 | 73 | #ifdef __cplusplus 74 | extern "C" { 75 | #endif 76 | 77 | /* the input callback type */ 78 | typedef uint8_t (*z80dasm_input_t)(void* user_data); 79 | /* the output callback type */ 80 | typedef void (*z80dasm_output_t)(char c, void* user_data); 81 | 82 | /* disassemble a single Z80 instruction into a stream of ASCII characters */ 83 | uint16_t z80dasm_op(uint16_t pc, z80dasm_input_t in_cb, z80dasm_output_t out_cb, void* user_data); 84 | 85 | #ifdef __cplusplus 86 | } /* extern "C" */ 87 | #endif 88 | 89 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 90 | #ifdef CHIPS_IMPL 91 | #ifndef CHIPS_ASSERT 92 | #include 93 | #define CHIPS_ASSERT(c) assert(c) 94 | #endif 95 | 96 | /* fetch unsigned 8-bit value and track pc */ 97 | #ifdef _FETCH_U8 98 | #undef _FETCH_U8 99 | #endif 100 | #define _FETCH_U8(v) v=in_cb(user_data);pc++; 101 | /* fetch signed 8-bit value and track pc */ 102 | #ifdef _FETCH_I8 103 | #undef _FETCH_I8 104 | #endif 105 | #define _FETCH_I8(v) v=(int8_t)in_cb(user_data);pc++; 106 | /* fetch unsigned 16-bit value and track pc */ 107 | #ifdef _FETCH_U16 108 | #undef _FETCH_U16 109 | #endif 110 | #define _FETCH_U16(v) v=in_cb(user_data);v|=in_cb(user_data)<<8;pc+=2; 111 | /* output character */ 112 | #ifdef _CHR 113 | #undef _CHR 114 | #endif 115 | #define _CHR(c) if (out_cb) { out_cb(c,user_data); } 116 | /* output string */ 117 | #ifdef _STR 118 | #undef _STR 119 | #endif 120 | #define _STR(s) _z80dasm_str(s,out_cb,user_data); 121 | /* output offset as signed 8-bit string (decimal) */ 122 | #ifdef _STR_D8 123 | #undef _STR_D8 124 | #endif 125 | #define _STR_D8(d8) _z80dasm_d8((int8_t)(d8),out_cb,user_data); 126 | /* output number as unsigned 8-bit string (hex) */ 127 | #ifdef _STR_U8 128 | #undef _STR_U8 129 | #endif 130 | #define _STR_U8(u8) _z80dasm_u8((uint8_t)(u8),out_cb,user_data); 131 | /* output number number as unsigned 16-bit string (hex) */ 132 | #ifdef _STR_U16 133 | #undef _STR_U16 134 | #endif 135 | #define _STR_U16(u16) _z80dasm_u16((uint16_t)(u16),out_cb,user_data); 136 | /* (HL)/(IX+d)/(IX+d) */ 137 | #ifdef _M 138 | #undef _M 139 | #endif 140 | #define _M() _STR(r[6]);if(pre){_FETCH_I8(d);_STR_D8(d);_CHR(')');} 141 | /* same as _M, but with given offset byte */ 142 | #ifdef _Md 143 | #undef _Md 144 | #endif 145 | #define _Md(d) _STR(r[6]);if(pre){_STR_D8(d);_CHR(')');} 146 | /* (HL)/(IX+d)/(IX+d) or r */ 147 | #ifdef _MR 148 | #undef _MR 149 | #endif 150 | #define _MR(i) if(i==6){_M();}else{_STR(r[i]);} 151 | /* same as _MR, but with given offset byte */ 152 | #ifdef _MRd 153 | #undef _MRd 154 | #endif 155 | #define _MRd(i,d) _STR(r[i]);if(i==6 && pre){_STR_D8(d);_CHR(')');} 156 | /* output 16-bit immediate operand */ 157 | #ifdef _IMM16 158 | #undef _IMM16 159 | #endif 160 | #define _IMM16() _FETCH_U16(u16); _STR_U16(u16); 161 | /* output 8-bit immediate operand */ 162 | #ifdef _IMM8 163 | #undef _IMM8 164 | #endif 165 | #define _IMM8() _FETCH_U8(u8); _STR_U8(u8); 166 | 167 | static const char* _z80dasm_r[8] = { "B", "C", "D", "E", "H", "L", "(HL)", "A" }; 168 | static const char* _z80dasm_rix[8] = { "B", "C", "D", "E", "IXH", "IXL", "(IX", "A" }; 169 | static const char* _z80dasm_riy[8] = { "B", "C", "D", "E", "IYH", "IYL", "(IY", "A" }; 170 | static const char* _z80dasm_rp[4] = { "BC", "DE", "HL", "SP" }; 171 | static const char* _z80dasm_rpix[4] = { "BC", "DE", "IX", "SP" }; 172 | static const char* _z80dasm_rpiy[4] = { "BC", "DE", "IY", "SP" }; 173 | static const char* _z80dasm_rp2[4] = { "BC", "DE", "HL", "AF"}; 174 | static const char* _z80dasm_rp2ix[4] = { "BC", "DE", "IX", "AF"}; 175 | static const char* _z80dasm_rp2iy[4] = { "BC", "DE", "IY", "AF"}; 176 | static const char* _z80dasm_cc[8] = { "NZ", "Z", "NC", "C", "PO", "PE", "P", "M" }; 177 | static const char* _z80dasm_alu[8] = { "ADD A,", "ADC A,", "SUB ", "SBC A,", "AND ", "XOR ", "OR ", "CP " }; 178 | static const char* _z80dasm_rot[8] = { "RLC ", "RRC ", "RL ", "RR ", "SLA ", "SRA ", "SLL ", "SRL " }; 179 | static const char* _z80dasm_x0z7[8] = { "RLCA", "RRCA", "RLA", "RRA", "DAA", "CPL", "SCF", "CCF" }; 180 | static const char* _z80dasm_edx1z7[8] = { "LD I,A", "LD R,A", "LD A,I", "LD A,R", "RRD", "RLD", "NOP (ED)", "NOP (ED)" }; 181 | static const char* _z80dasm_im[8] = { "0", "0", "1", "2", "0", "0", "1", "2" }; 182 | static const char* _z80dasm_bli[4][4] = { 183 | { "LDI", "CPI", "INI", "OUTI" }, 184 | { "LDD", "CPD", "IND", "OUTD" }, 185 | { "LDIR", "CPIR", "INIR", "OTIR" }, 186 | { "LDDR", "CPDR", "INDR", "OTDR" } 187 | }; 188 | static const char* _z80dasm_oct = "01234567"; 189 | static const char* _z80dasm_dec = "0123456789"; 190 | static const char* _z80dasm_hex = "0123456789ABCDEF"; 191 | 192 | static const uint32_t _z80dasm_pre_valid[8] = { 193 | UINT32_C(0x02000200), UINT32_C(0x02707e7e), UINT32_C(0x70707070), UINT32_C(0x70b7ffff), 194 | UINT32_C(0x70707070), UINT32_C(0x70707070), UINT32_C(0x00800800), UINT32_C(0x0200022a) 195 | }; 196 | 197 | /* output a string */ 198 | static void _z80dasm_str(const char* str, z80dasm_output_t out_cb, void* user_data) { 199 | if (out_cb) { 200 | char c; 201 | while (0 != (c = *str++)) { 202 | out_cb(c, user_data); 203 | } 204 | } 205 | } 206 | 207 | /* output a signed 8-bit offset value as decimal string */ 208 | static void _z80dasm_d8(int8_t val, z80dasm_output_t out_cb, void* user_data) { 209 | if (out_cb) { 210 | if (val < 0) { 211 | out_cb('-', user_data); 212 | val = -val; 213 | } 214 | else { 215 | out_cb('+', user_data); 216 | } 217 | if (val >= 100) { 218 | out_cb('1', user_data); 219 | val -= 100; 220 | } 221 | if ((val/10) != 0) { 222 | out_cb(_z80dasm_dec[val/10], user_data); 223 | } 224 | out_cb(_z80dasm_dec[val%10], user_data); 225 | } 226 | } 227 | 228 | /* output an unsigned 8-bit value as hex string */ 229 | static void _z80dasm_u8(uint8_t val, z80dasm_output_t out_cb, void* user_data) { 230 | if (out_cb) { 231 | for (int i = 1; i >= 0; i--) { 232 | out_cb(_z80dasm_hex[(val>>(i*4)) & 0xF], user_data); 233 | } 234 | out_cb('h',user_data); 235 | } 236 | } 237 | 238 | /* output an unsigned 16-bit value as hex string */ 239 | static void _z80dasm_u16(uint16_t val, z80dasm_output_t out_cb, void* user_data) { 240 | if (out_cb) { 241 | for (int i = 3; i >= 0; i--) { 242 | out_cb(_z80dasm_hex[(val>>(i*4)) & 0xF], user_data); 243 | } 244 | out_cb('h',user_data); 245 | } 246 | } 247 | 248 | /* main disassembler function */ 249 | uint16_t z80dasm_op(uint16_t pc, z80dasm_input_t in_cb, z80dasm_output_t out_cb, void* user_data) { 250 | CHIPS_ASSERT(in_cb); 251 | uint8_t op = 0, pre = 0, u8 = 0; 252 | int8_t d = 0; 253 | uint16_t u16 = 0; 254 | const char** cc = _z80dasm_cc; 255 | const char** alu = _z80dasm_alu; 256 | const char** r = _z80dasm_r; 257 | const char** rp = _z80dasm_rp; 258 | const char** rp2 = _z80dasm_rp2; 259 | 260 | /* fetch the first instruction byte */ 261 | _FETCH_U8(op); 262 | /* prefixed op? */ 263 | if ((0xFD == op) || (0xDD == op)) { 264 | pre = op; 265 | _FETCH_U8(op); 266 | if ((_z80dasm_pre_valid[op / 32] & (UINT32_C(1) << (op & 31))) == 0) { 267 | /* if the next instruction doesn't use the prefix, report it as a NOP */ 268 | _STR("NOP ("); 269 | _STR(pre == 0xFD ? "FD" : "DD"); 270 | _STR(")"); 271 | return pc - 1; 272 | } 273 | /* if prefixed op, use register tables that replace HL with IX/IY */ 274 | if (pre == 0xDD) { 275 | r = _z80dasm_rix; 276 | rp = _z80dasm_rpix; 277 | rp2 = _z80dasm_rp2ix; 278 | } 279 | else if (pre == 0xFD) { 280 | r = _z80dasm_riy; 281 | rp = _z80dasm_rpiy; 282 | rp2 = _z80dasm_rp2iy; 283 | } 284 | } 285 | 286 | /* parse the opcode */ 287 | uint8_t x = (op >> 6) & 3; 288 | uint8_t y = (op >> 3) & 7; 289 | uint8_t z = op & 7; 290 | uint8_t p = y >> 1; 291 | uint8_t q = y & 1; 292 | if (x == 1) { 293 | /* 8-bit load block */ 294 | if (y == 6) { 295 | if (z == 6) { 296 | /* special case LD (HL),(HL) */ 297 | _STR("HALT"); 298 | } 299 | else { 300 | /* LD (HL),r; LD (IX+d),r; LD (IY+d),r */ 301 | _STR("LD "); _M(); _CHR(','); 302 | if (pre && ((z == 4) || (z == 5))) { 303 | /* special case LD (IX+d),L/H (don't use IXL/IXH) */ 304 | _STR(_z80dasm_r[z]); 305 | } 306 | else { 307 | _STR(r[z]); 308 | } 309 | } 310 | } 311 | else if (z == 6) { 312 | /* LD r,(HL); LD r,(IX+d); LD r,(IY+d) */ 313 | _STR("LD "); 314 | if (pre && ((y == 4) || (y == 5))) { 315 | /* special case LD H/L,(IX+d) (don't use IXL/IXH) */ 316 | _STR(_z80dasm_r[y]); 317 | } 318 | else { 319 | _STR(r[y]); 320 | } 321 | _CHR(','); _M(); 322 | } 323 | else { 324 | /* regular LD r,s */ 325 | _STR("LD "); _STR(r[y]); _CHR(','); _STR(r[z]); 326 | } 327 | } 328 | else if (x == 2) { 329 | /* 8-bit ALU block */ 330 | _STR(alu[y]); _MR(z); 331 | } 332 | else if (x == 0) { 333 | switch (z) { 334 | case 0: 335 | switch (y) { 336 | case 0: _STR("NOP"); break; 337 | case 1: _STR("EX AF,AF'"); break; 338 | case 2: _STR("DJNZ "); _FETCH_I8(d); _STR_U16(pc+d); break; 339 | case 3: _STR("JR "); _FETCH_I8(d); _STR_U16(pc+d); break; 340 | default: _STR("JR "); _STR(cc[y-4]); _CHR(','); _FETCH_I8(d); _STR_U16(pc+d); break; 341 | } 342 | break; 343 | case 1: 344 | if (q == 0) { 345 | _STR("LD "); _STR(rp[p]); _CHR(','); _IMM16(); 346 | } 347 | else { 348 | _STR("ADD "); _STR(rp[2]); _CHR(','); _STR(rp[p]); 349 | } 350 | break; 351 | case 2: 352 | { 353 | _STR("LD "); 354 | switch (y) { 355 | case 0: _STR("(BC),A"); break; 356 | case 1: _STR("A,(BC)"); break; 357 | case 2: _STR("(DE),A"); break; 358 | case 3: _STR("A,(DE)"); break; 359 | case 4: _STR("("); _IMM16(); _STR("),"); _STR(rp[2]); break; 360 | case 5: _STR(rp[2]); _STR(",("); _IMM16(); _STR(")"); break; 361 | case 6: _STR("("); _IMM16(); _STR("),A"); break; 362 | case 7: _STR("A,("); _IMM16(); _STR(")"); break; 363 | } 364 | } 365 | break; 366 | case 3: _STR(q==0?"INC ":"DEC "); _STR(rp[p]); break; 367 | case 4: _STR("INC "); _MR(y); break; 368 | case 5: _STR("DEC "); _MR(y); break; 369 | case 6: _STR("LD "); _MR(y); _CHR(','); _IMM8(); break; 370 | case 7: _STR(_z80dasm_x0z7[y]); break; 371 | } 372 | } 373 | else { 374 | switch (z) { 375 | case 0: _STR("RET "); _STR(cc[y]); break; 376 | case 1: 377 | if (q == 0) { 378 | _STR("POP "); _STR(rp2[p]); 379 | } 380 | else { 381 | switch (p) { 382 | case 0: _STR("RET"); break; 383 | case 1: _STR("EXX"); break; 384 | case 2: _STR("JP "); _CHR('('); _STR(rp[2]); _CHR(')'); break; 385 | case 3: _STR("LD SP,"); _STR(rp[2]); break; 386 | } 387 | } 388 | break; 389 | case 2: _STR("JP "); _STR(cc[y]); _CHR(','); _IMM16(); break; 390 | case 3: 391 | switch (y) { 392 | case 0: _STR("JP "); _IMM16(); break; 393 | case 2: _STR("OUT ("); _IMM8(); _CHR(')'); _STR(",A"); break; 394 | case 3: _STR("IN A,("); _IMM8(); _CHR(')'); break; 395 | case 4: _STR("EX (SP),"); _STR(rp[2]); break; 396 | case 5: _STR("EX DE,HL"); break; 397 | case 6: _STR("DI"); break; 398 | case 7: _STR("EI"); break; 399 | case 1: /* CB prefix */ 400 | if (pre) { 401 | _FETCH_I8(d); 402 | } 403 | _FETCH_U8(op); 404 | x = (op >> 6) & 3; 405 | y = (op >> 3) & 7; 406 | z = op & 7; 407 | if (x == 0) { 408 | /* rot and shift instructions */ 409 | _STR(_z80dasm_rot[y]); _MRd(z,d); 410 | } 411 | else { 412 | /* bit instructions */ 413 | if (x == 1) { _STR("BIT "); } 414 | else if (x == 2) { _STR("RES "); } 415 | else { _STR("SET "); } 416 | _CHR(_z80dasm_oct[y]); 417 | if (pre) { 418 | _CHR(','); _Md(d); 419 | } 420 | if (!pre || (z != 6)) { 421 | _CHR(','); _STR(r[z]); 422 | } 423 | } 424 | break; 425 | } 426 | break; 427 | case 4: _STR("CALL "); _STR(cc[y]); _CHR(','); _IMM16(); break; 428 | case 5: 429 | if (q == 0) { 430 | _STR("PUSH "); _STR(rp2[p]); 431 | } 432 | else { 433 | switch (p) { 434 | case 0: _STR("CALL "); _IMM16(); break; 435 | case 2: /* ED prefix */ 436 | _FETCH_U8(op); 437 | x = (op >> 6) & 3; 438 | y = (op >> 3) & 7; 439 | z = op & 7; 440 | p = y >> 1; 441 | q = y & 1; 442 | if ((x == 0) || (x == 3)) { 443 | _STR("NOP (ED)"); 444 | } 445 | else if (x == 2) { 446 | if ((y >= 4) && (z <= 3)) { 447 | /* block instructions */ 448 | _STR(_z80dasm_bli[y-4][z]); 449 | } 450 | else { 451 | _STR("NOP (ED)"); 452 | } 453 | } 454 | else { 455 | switch (z) { 456 | case 0: _STR("IN "); if(y!=6){_STR(r[y]);_CHR(',');} _STR("(C)"); break; 457 | case 1: _STR("OUT (C),"); _STR(y==6?"0":r[y]); break; 458 | case 2: _STR(q==0?"SBC":"ADC"); _STR(" HL,"); _STR(rp[p]); break; 459 | case 3: 460 | _STR("LD "); 461 | if (q == 0) { 462 | _CHR('('); _IMM16(); _STR("),"); _STR(rp[p]); 463 | } 464 | else { 465 | _STR(rp[p]); _STR(",("); _IMM16(); _CHR(')'); 466 | } 467 | break; 468 | case 4: _STR("NEG"); break; 469 | case 5: _STR(y==1?"RETI":"RETN"); break; 470 | case 6: _STR("IM "); _STR(_z80dasm_im[y]); break; 471 | case 7: _STR(_z80dasm_edx1z7[y]); break; 472 | } 473 | } 474 | break; 475 | } 476 | } 477 | break; 478 | case 6: _STR(alu[y]); _IMM8(); break; /* ALU n */ 479 | case 7: _STR("RST "); _STR_U8(y*8); break; 480 | } 481 | } 482 | return pc; 483 | } 484 | 485 | #undef _FETCH_U8 486 | #undef _FETCH_I8 487 | #undef _FETCH_U16 488 | #undef _CHR 489 | #undef _STR 490 | #undef _STR_D8 491 | #undef _STR_U8 492 | #undef _STR_U16 493 | #undef _M 494 | #undef _Md 495 | #undef _MR 496 | #undef _MRd 497 | #undef _IMM16 498 | #undef _IMM8 499 | #endif /* CHIPS_IMPL */ 500 | -------------------------------------------------------------------------------- /src/qrc11_rom.asm: -------------------------------------------------------------------------------- 1 | ; https://github.com/leiradel/qrc1 2 | 3 | ; qrc11_message contains the message length followed by the message (maximum 4 | ; 251 bytes). 5 | qrc11_encmessage: 6 | ; ------------------------------------------------------------------------ 7 | ; Encode the message. 8 | ; ------------------------------------------------------------------------ 9 | 10 | ; Insert a 0000 nibble to before the length 11 | ld hl, qrc11_message 12 | ld c, (hl) 13 | xor a 14 | 15 | ; Shift the message to the right by four bits. 16 | ld b, c 17 | inc b 18 | 19 | qrc11_shift_msg: 20 | rrd 21 | inc hl 22 | djnz qrc11_shift_msg 23 | 24 | ; A has the low nibble of the last message byte, shift it to the high 25 | ; nibble and set the low nibble to 0, which is the end of message mark. 26 | ld (hl), 0 27 | rrd 28 | inc hl 29 | 30 | ; Pad the rest of the message with $ec and $11. 31 | ld a, 251 32 | sub c 33 | jr z, qrc11_no_padding 34 | 35 | ld b, a 36 | ld a, $ec 37 | qrc11_pad_msg: 38 | ld (hl), a 39 | inc hl 40 | xor $fd 41 | djnz qrc11_pad_msg 42 | 43 | qrc11_no_padding: 44 | 45 | ; ------------------------------------------------------------------------ 46 | ; Calculate the message ECC. 47 | ; ------------------------------------------------------------------------ 48 | 49 | ; Zero the space after the ECC polynomial. 50 | ld hl, qrc11_ecc_poly_extra 51 | ld de, qrc11_ecc_poly_extra + 1 52 | ld bc, 50 53 | ld (hl), 0 54 | ldir 55 | 56 | ; Copy each block of the original encoded message to the target buffer, 57 | ; the ECC evaluation will overwrite it so we need to restore it at the end. 58 | ld hl, qrc11_block1 59 | ld de, qrc11_b1 60 | ld bc, 50 61 | call qrc11_ecc 62 | 63 | ld hl, qrc11_block2 64 | ld de, qrc11_b2 65 | ld bc, 51 66 | call qrc11_ecc 67 | 68 | ld hl, qrc11_block3 69 | ld de, qrc11_b3 70 | ld bc, 51 71 | call qrc11_ecc 72 | 73 | ld hl, qrc11_block4 74 | ld de, qrc11_b4 75 | ld bc, 51 76 | call qrc11_ecc 77 | 78 | ld hl, qrc11_block5 79 | ld de, qrc11_b5 80 | ld bc, 51 81 | call qrc11_ecc 82 | 83 | ; ------------------------------------------------------------------------ 84 | ; Interleave message and ecc blocks. 85 | ; ------------------------------------------------------------------------ 86 | 87 | qrc11_interleave: 88 | ld hl, qrc11_b1 89 | ld de, qrc11_message - 1 90 | 91 | ld a, 50 92 | qrc11_dintl: 93 | call qrc11_dint 94 | ld bc, qrc11_b1 - qrc11_b5 95 | add hl, bc 96 | dec a 97 | jr nz, qrc11_dintl 98 | 99 | ld hl, qrc11_b2 + 50 100 | ldi 101 | ld hl, qrc11_b3 + 50 102 | ldi 103 | ld hl, qrc11_b4 + 50 104 | ldi 105 | ld hl, qrc11_b5 + 50 106 | ldi 107 | 108 | ld hl, qrc11_b1_ecc 109 | 110 | ld a, 30 111 | qrc11_eintl: 112 | call qrc11_eint 113 | ld bc, qrc11_b1_ecc - qrc11_b5_ecc 114 | add hl, bc 115 | dec a 116 | jr nz, qrc11_eintl 117 | 118 | ret 119 | 120 | ; ------------------------------------------------------------------------ 121 | ; Interleave bytes. 122 | ; ------------------------------------------------------------------------ 123 | 124 | qrc11_eint: 125 | ldi 126 | ld bc, 50 + 30 127 | jr qrc11_int 128 | 129 | qrc11_dint: 130 | ldi 131 | ld bc, 49 + 30 132 | 133 | qrc11_int: 134 | add hl, bc 135 | ldi 136 | ld c, 50 + 30 137 | add hl, bc 138 | ldi 139 | ld c, 50 + 30 140 | add hl, bc 141 | ldi 142 | ld c, 50 + 30 143 | add hl, bc 144 | ldi 145 | ret 146 | 147 | ; ------------------------------------------------------------------------ 148 | ; Calculate the block ECC. 149 | ; ------------------------------------------------------------------------ 150 | 151 | qrc11_ecc: 152 | ; Save block parameters for restoring 153 | push hl 154 | push de 155 | push bc 156 | 157 | ; Save message block length for later 158 | push bc 159 | 160 | ; Save message block address for later 161 | push de 162 | ldir 163 | 164 | ; Zero the 30 bytes where the ECC will be stored. 165 | xor a 166 | 167 | ld b, 30 168 | qrc11_zero_ecc: 169 | ld (de), a 170 | inc de 171 | djnz qrc11_zero_ecc 172 | 173 | ; HL is the polynomial A. 174 | pop hl 175 | 176 | ; IYL is the outer loop counter (i) for the length of A. 177 | pop iy 178 | qrc11_loop_i: 179 | ; Save HL as it'll be incremented in the inner loop. 180 | push hl 181 | 182 | ; Save A[i] in B to be used inside the inner loop. 183 | ld b, (hl) 184 | 185 | ; DE is the polynomial B. 186 | ld de, qrc11_ecc_poly 187 | 188 | ; Evaluate the inner loop count limit. 189 | ld a, 31 190 | add a, iyl 191 | dec a 192 | 193 | ; IYH is inner loop counter (j) up to length(A) - i. 194 | ld iyh, a 195 | qrc11_loop_j: 196 | ; A is B[j] 197 | ld a, (de) 198 | 199 | ; Save DE as we'll use D and E in the gf_mod loop. 200 | push de 201 | 202 | ; D is A[i], E is the gf_mod result. 203 | ld d, b 204 | ld e, 0 205 | 206 | ; A is x, D is y, E is r, C is a scratch register. 207 | jr qrc11_test_y 208 | 209 | qrc11_xor_res: 210 | ; y had the 0th bit set, r ^= x. 211 | ld c, a 212 | xor e 213 | ld e, a 214 | ld a, c 215 | qrc11_dont_xor: 216 | ; x <<= 1, set carry if x >= 256. 217 | add a, a 218 | jr nc, qrc11_test_y 219 | 220 | ; x was >= 256, xor it with the module. 221 | xor 285 & $ff 222 | qrc11_test_y: 223 | ; y >>= 1, update r if the 0th bit is set, end the loop if 224 | ; it's zero. 225 | srl d 226 | jr c, qrc11_xor_res 227 | jr nz, qrc11_dont_xor 228 | 229 | ; A[i + j] ^= gf_mod(...) 230 | ld a, (hl) 231 | xor e 232 | ld (hl), a 233 | 234 | ; Restore DE. 235 | pop de 236 | 237 | ; Update HL and DE to point to the next bytes of A and B. 238 | inc hl 239 | inc de 240 | 241 | ; Inner loop test. 242 | dec iyh 243 | jr nz, qrc11_loop_j 244 | 245 | ; Restore HL since it was changed in the inner loop, and make it point 246 | ; to the next byte in A. 247 | pop hl 248 | inc hl 249 | 250 | ; Outer loop test. 251 | dec iyl 252 | jr nz, qrc11_loop_i 253 | 254 | ; Restore the original encoded message, since the loops above zero it. 255 | pop bc 256 | pop de 257 | pop hl 258 | ldir 259 | 260 | ; All done, there's no need to perform the masking step because the mask is 261 | ; embedded in the fixed modules binary pattern so it's automatically 262 | ; applied when the modules are plotted. 263 | ret 264 | 265 | qrc11_print: 266 | ; IY points to the RLE fixed module data. 267 | ld iy, qrc11_fixed 268 | 269 | ; Print loop, D is the X coordinate and E is the Y coordinate, both count 270 | ; towards zero. 271 | ld de, 61 << 8 | 61 272 | 273 | qrc11_print_row: 274 | ; Get RLE code. 275 | ld a, (iy) 276 | inc iy 277 | 278 | ; Save in B because we'll need the lower six bits later. 279 | ld b, a 280 | 281 | ; Mask the RLE command. 282 | and $c0 283 | ; Zero means print black pixels. 284 | jr z, qrc11_print_black 285 | 286 | ; 0x40 means print white pixels. 287 | cp $40 288 | jr z, qrc11_print_white 289 | 290 | ; 0x80 means print mask pixels, put pixel count in B. 291 | ld a, b 292 | and $3f 293 | 294 | ld b, a 295 | qrc11_print_mask_loop: 296 | ; Only set the pixel if (i+j)%2==0. 297 | ld a, d 298 | xor e 299 | rra 300 | call nc, qrc_invert_pixel 301 | 302 | ; Move cursor to the right. 303 | call qrc_pixel_right 304 | dec d 305 | djnz qrc11_print_mask_loop 306 | 307 | ; Continue line. 308 | 309 | qrc11_print_continue: 310 | ; Check if X is zero, meaning the end of the row was reached. 311 | ld a, d 312 | or a 313 | jr nz, qrc11_print_row 314 | 315 | ; Row ended, check for end of data. 316 | dec e 317 | jr z, qrc11_print_modules 318 | 319 | ; Continue to the next row, set D to 61 for the new row. 320 | ld d, 61 321 | 322 | ; Move the cursor to the beginning of the next row. 323 | call qrc_pixel_down 324 | 325 | ld b, d 326 | qrc11_print_cr: 327 | call qrc_pixel_left 328 | djnz qrc11_print_cr 329 | 330 | ; Continue on the next line. 331 | jr qrc11_print_row 332 | 333 | qrc11_print_black: 334 | ; Pixel count in already in B. 335 | 336 | qrc11_print_black_loop: 337 | ; Set the pixel and move the cursor to the right. 338 | call qrc_invert_pixel 339 | call qrc_pixel_right 340 | dec d 341 | djnz qrc11_print_black_loop 342 | 343 | ; Continue line. 344 | jr qrc11_print_continue 345 | 346 | qrc11_print_white: 347 | ; Put pixel count in B. 348 | ld a, b 349 | and $3f 350 | ld b, a 351 | 352 | qrc11_print_white_loop: 353 | ; Move the cursor to the right without setting the pixel. 354 | call qrc_pixel_right 355 | dec d 356 | djnz qrc11_print_white_loop 357 | 358 | ; Continue line. 359 | jr qrc11_print_continue 360 | 361 | qrc11_print_modules: 362 | ; Move the cursor to the bottom-right pixel. 363 | call qrc_pixel_left 364 | 365 | ; Message bits. 366 | ld de, qrc11_message - 1 367 | ld b, $80 ; Most significant bit 368 | 369 | ; Set the current plot direction to up. 370 | xor a 371 | ld (qrc11_plot_direction), a 372 | 373 | ; IY points to the RLE module data. 374 | ld iy, qrc11_modules 375 | 376 | qrc11_modules_loop: 377 | ; Run the command in the upper nibble. 378 | ld a, (iy) 379 | rra 380 | rra 381 | rra 382 | rra 383 | call qrc11_module_command 384 | 385 | ; Run the command in the lower nibble. 386 | ld a, (iy) 387 | inc iy 388 | call qrc11_module_command 389 | 390 | ; Check if IY is at the end of the RLE commands. 391 | ld a, iyl 392 | cp qrc11_modules_end & $ff 393 | jr nz, qrc11_modules_loop 394 | ld a, iyh 395 | cp qrc11_modules_end >> 8 396 | jr nz, qrc11_modules_loop 397 | 398 | ret 399 | 400 | qrc11_module_command: 401 | ; Runs the command in the lower nibble of A. This seems to be more lengthy 402 | ; than using a lookup table (LUT), but the code to use the LUT plus the LUT 403 | ; itself are bigger than the code here. 404 | 405 | ; * 0: u-turn (29 usages) 406 | and $0f 407 | jr z, qrc11_uturn 408 | 409 | ; * 1: skip 1 (20 usages) 410 | dec a 411 | jr z, qrc11_skip_1 412 | 413 | ; * 2: pairs 54 (18 usages) 414 | dec a 415 | jr z, qrc11_pairs_54 416 | 417 | ; * 3: pairs 6 (17 usages) 418 | dec a 419 | jr z, qrc11_pairs_6 420 | 421 | ; * 4: pairs 19 (13 usages) 422 | dec a 423 | jr z, qrc11_pairs_19 424 | 425 | ; * 5: skip 5 (12 usages) 426 | dec a 427 | jr z, qrc11_skip_5 428 | 429 | ; * 6: pairs 4 (10 usages) 430 | dec a 431 | jr z, qrc11_pairs_4 432 | 433 | ; * 7: left 1 (7 usages) 434 | dec a 435 | jp z, qrc_pixel_left 436 | 437 | ; * 8: right 1 (6 usages) 438 | dec a 439 | jp z, qrc_pixel_right 440 | 441 | ; * 9: bits 1 (5 usages) 442 | dec a 443 | jr z, qrc11_bits_1 444 | 445 | ; * 10: bits 5 (5 usages) 446 | dec a 447 | jr z, qrc11_bits_5 448 | 449 | ; * 11: pairs 17 (2 usages) 450 | dec a 451 | jr z, qrc11_pairs_17 452 | 453 | ; * 12: pairs 41 (2 usages) 454 | dec a 455 | jr z, qrc11_pairs_41 456 | 457 | ; * 13: pairs 52 (2 usages) 458 | dec a 459 | jr z, qrc11_pairs_52 460 | 461 | ; * 14: skip 7 (2 usages) 462 | dec a 463 | jr z, qrc11_skip_7 464 | 465 | ; * 15: pairs 20 (1 usages) 466 | jr qrc11_pairs_20 467 | 468 | ; Makes an U-turn and inverts the direction. 469 | qrc11_uturn: 470 | call qrc_pixel_left 471 | call qrc_pixel_left 472 | 473 | ld a, (qrc11_plot_direction) 474 | xor 1 475 | ld (qrc11_plot_direction), a 476 | ; fallthrough 477 | 478 | ; Skips one pixel. 479 | qrc11_skip_1: 480 | ld a, (qrc11_plot_direction) 481 | and 1 482 | 483 | jp nz, qrc_pixel_down 484 | jp qrc_pixel_up 485 | 486 | ; Plots 108 modules in zig-zag. 487 | qrc11_pairs_54: 488 | call qrc11_pairs_2 489 | ; fallthrough 490 | 491 | qrc11_pairs_52: 492 | call qrc11_pairs_11 493 | ; fallthrough 494 | 495 | qrc11_pairs_41: 496 | call qrc11_pairs_20 497 | ; fallthrough 498 | 499 | qrc11_pairs_21: 500 | call qrc11_pairs_1 501 | ; fallthrough 502 | 503 | qrc11_pairs_20: 504 | call qrc11_pairs_1 505 | ; fallthrough 506 | 507 | qrc11_pairs_19: 508 | call qrc11_pairs_2 509 | ; fallthrough 510 | 511 | qrc11_pairs_17: 512 | call qrc11_pairs_6 513 | ; fallthrough 514 | 515 | qrc11_pairs_11: 516 | call qrc11_pairs_5 517 | ; fallthrough 518 | 519 | qrc11_pairs_6: 520 | call qrc11_pairs_1 521 | ; fallthrough 522 | 523 | qrc11_pairs_5: 524 | call qrc11_pairs_1 525 | ; fallthrough 526 | 527 | qrc11_pairs_4: 528 | call qrc11_pairs_2 529 | ; fallthrough 530 | 531 | qrc11_pairs_2: 532 | call qrc11_pairs_1 533 | ; fallthrough 534 | 535 | qrc11_pairs_1: 536 | call qrc11_set_pixel_if 537 | call qrc_pixel_left 538 | call qrc11_set_pixel_if 539 | call qrc_pixel_right 540 | jp qrc11_skip_1 541 | 542 | qrc11_skip_7: 543 | call qrc11_skip_2 544 | ; fallthrough 545 | 546 | qrc11_skip_5: 547 | call qrc11_skip_1 548 | ; fallthrough 549 | 550 | qrc11_skip_4: 551 | call qrc11_skip_2 552 | ; fallthrough 553 | 554 | qrc11_skip_2: 555 | call qrc11_skip_1 556 | jp qrc11_skip_1 557 | 558 | qrc11_bits_5: 559 | call qrc11_bits_1 560 | ; fallthrough 561 | 562 | qrc11_bits_4: 563 | call qrc11_bits_2 564 | ; fallthrough 565 | 566 | qrc11_bits_2: 567 | call qrc11_bits_1 568 | ; fallthrough 569 | 570 | qrc11_bits_1: 571 | call qrc11_set_pixel_if 572 | jp qrc11_skip_1 573 | 574 | ; Plots a module if the corresponding bit in the encoded message is set. 575 | qrc11_set_pixel_if: 576 | ld a, (de) 577 | and b 578 | call nz, qrc_invert_pixel 579 | 580 | ; Skip to the next bit. 581 | rrc b 582 | ret nc 583 | 584 | ; Increment the pointer to the encoded message if the bit in B wrapped. 585 | inc de 586 | ret 587 | 588 | ; RLE data to plot the fixed modules plus the checkerboard. RLE commands are 589 | ; encoded in the highest two bits of each byte, with the remaining six holding 590 | ; the data for the command: 591 | ; 592 | ; * 0x00: print black pixels, data is the number of pixels 593 | ; * 0x40: print white pixels, data is the number of pixels 594 | ; * 0x80: print mask pixels, data is the number of pixels 595 | ; * 0xc0: not used 596 | qrc11_fixed: 597 | db $07, $42, $80 | 41, $41, $02, $41, $07 598 | db $01, $45, $01, $41, $01, $80 | 41, $41, $02, $41, $01, $45, $01 599 | db $01, $41, $03, $41, $01, $42, $80 | 41, $03, $41, $01, $41, $03, $41, $01 600 | db $01, $41, $03, $41, $01, $42, $80 | 41, $01, $41, $01, $41, $01, $41, $03, $41, $01 601 | db $01, $41, $03, $41, $01, $41, $01, $80 | 19, $05, $80 | 17, $02, $42, $01, $41, $03, $41, $01 602 | db $01, $45, $01, $42, $80 | 19, $01, $43, $01, $80 | 17, $01, $43, $01, $45, $01 603 | 604 | ; The following line uses the mask RLE command to draw the horizontal 605 | ; timing pattern. If the mask used in the QR Code wasn't the checkerboard, 606 | ; the line would have to be encoded using alternating $01 and $41 commands 607 | ; to draw the black and white pixels for the timing, which would have been 608 | ; terrible. 609 | db $07, $41, $01, $80 | 19, $01, $41, $01, $41, $01, $80 | 20, $41, $07 610 | 611 | db $49, $80 | 19, $01, $43, $01, $80 | 20, $48 612 | db $01, $41, $01, $41, $01, $41, $01, $42, $80 | 19, $05, $80 | 20, $43, $01, $42, $01, $41 613 | db $86, $41, $80 | 54 614 | db $86, $01, $80 | 54 615 | db $86, $41, $80 | 54 616 | db $86, $01, $80 | 54 617 | db $86, $41, $80 | 54 618 | db $86, $01, $80 | 54 619 | db $86, $41, $80 | 54 620 | db $86, $01, $80 | 54 621 | db $86, $41, $80 | 54 622 | db $86, $01, $80 | 54 623 | db $86, $41, $80 | 54 624 | db $86, $01, $80 | 54 625 | db $86, $41, $80 | 54 626 | db $86, $01, $80 | 54 627 | db $86, $41, $80 | 54 628 | db $86, $01, $80 | 54 629 | db $86, $41, $80 | 54 630 | db $86, $01, $80 | 54 631 | db $86, $41, $80 | 54 632 | db $84, $05, $80 | 19, $05, $80 | 19, $05, $84 633 | db $84, $01, $43, $01, $80 | 19, $01, $43, $01, $80 | 19, $01, $43, $01, $84 634 | db $84, $01, $41, $01, $41, $01, $80 | 19, $01, $41, $01, $41, $01, $80 | 19, $01, $41, $01, $41, $01, $84 635 | db $84, $01, $43, $01, $80 | 19, $01, $43, $01, $80 | 19, $01, $43, $01, $84 636 | db $84, $05, $80 | 19, $05, $80 | 19, $05, $84 637 | db $86, $41, $80 | 54 638 | db $86, $01, $80 | 54 639 | db $86, $41, $80 | 54 640 | db $86, $01, $80 | 54 641 | db $86, $41, $80 | 54 642 | db $86, $01, $80 | 54 643 | db $86, $41, $80 | 54 644 | db $86, $01, $80 | 54 645 | db $86, $41, $80 | 54 646 | db $86, $01, $80 | 54 647 | db $86, $41, $80 | 54 648 | db $86, $01, $80 | 54 649 | db $86, $41, $80 | 54 650 | db $86, $01, $80 | 54 651 | db $86, $41, $80 | 54 652 | db $86, $01, $80 | 54 653 | db $86, $41, $80 | 54 654 | db $42, $05, $80 | 54 655 | db $03, $41, $01, $42, $80 | 54 656 | db $04, $42, $01, $80 | 21, $05, $80 | 19, $05, $84 657 | db $48, $01, $80 | 19, $01, $43, $01, $80 | 19, $01, $43, $01, $84 658 | db $07, $42, $80 | 19, $01, $41, $01, $41, $01, $80 | 19, $01, $41, $01, $41, $01, $84 659 | db $01, $45, $01, $42, $80 | 19, $01, $43, $01, $80 | 19, $01, $43, $01, $84 660 | db $01, $41, $03, $41, $01, $41, $01, $80 | 19, $05, $80 | 19, $05, $84 661 | db $01, $41, $03, $41, $01, $42, $80 | 52 662 | db $01, $41, $03, $41, $01, $41, $01, $80 | 52 663 | db $01, $45, $01, $42, $80 | 52 664 | db $07, $41, $01, $80 | 52 665 | 666 | ; RLE data to plot the message modules. Each nibble is one of the following 667 | ; comands: 668 | ; 669 | ; * 0: u-turn (29 usages) 670 | ; * 1: skip 1 (20 usages) 671 | ; * 2: pairs 54 (18 usages) 672 | ; * 3: pairs 6 (17 usages) 673 | ; * 4: pairs 19 (13 usages) 674 | ; * 5: skip 5 (12 usages) 675 | ; * 6: pairs 4 (10 usages) 676 | ; * 7: left 1 (7 usages) 677 | ; * 8: right 1 (6 usages) 678 | ; * 9: bits 1 (5 usages) 679 | ; * 10: bits 5 (5 usages) 680 | ; * 11: pairs 17 (2 usages) 681 | ; * 12: pairs 41 (2 usages) 682 | ; * 13: pairs 52 (2 usages) 683 | ; * 14: skip 7 (2 usages) 684 | ; * 15: pairs 20 (1 usages) 685 | qrc11_modules: 686 | db $d0 ; pairs 52 + u-turn 687 | db $d0 ; pairs 52 + u-turn 688 | db $65 ; pairs 4 + skip 5 689 | db $45 ; pairs 19 + skip 5 690 | db $40 ; pairs 19 + u-turn 691 | db $45 ; pairs 19 + skip 5 692 | db $45 ; pairs 19 + skip 5 693 | db $60 ; pairs 4 + u-turn 694 | db $67 ; pairs 4 + left 1 695 | db $a8 ; bits 5 + right 1 696 | db $47 ; pairs 19 + left 1 697 | db $a8 ; bits 5 + right 1 698 | db $b6 ; pairs 17 + pairs 4 699 | db $e0 ; skip 7 + u-turn 700 | db $7a ; left 1 + bits 5 701 | db $91 ; bits 1 + skip 1 702 | db $82 ; right 1 + pairs 54 703 | db $02 ; u-turn + pairs 54 704 | db $13 ; skip 1 + pairs 6 705 | db $03 ; u-turn + pairs 6 706 | db $12 ; skip 1 + pairs 54 707 | db $02 ; u-turn + pairs 54 708 | db $13 ; skip 1 + pairs 6 709 | db $03 ; u-turn + pairs 6 710 | db $12 ; skip 1 + pairs 54 711 | db $02 ; u-turn + pairs 54 712 | db $13 ; skip 1 + pairs 6 713 | db $03 ; u-turn + pairs 6 714 | db $12 ; skip 1 + pairs 54 715 | db $02 ; u-turn + pairs 54 716 | db $13 ; skip 1 + pairs 6 717 | db $03 ; u-turn + pairs 6 718 | db $12 ; skip 1 + pairs 54 719 | db $06 ; u-turn + pairs 4 720 | db $54 ; skip 5 + pairs 19 721 | db $54 ; skip 5 + pairs 19 722 | db $56 ; skip 5 + pairs 4 723 | db $06 ; u-turn + pairs 4 724 | db $54 ; skip 5 + pairs 19 725 | db $54 ; skip 5 + pairs 19 726 | db $56 ; skip 5 + pairs 4 727 | db $06 ; u-turn + pairs 4 728 | db $7a ; left 1 + bits 5 729 | db $84 ; right 1 + pairs 19 730 | db $7a ; left 1 + bits 5 731 | db $84 ; right 1 + pairs 19 732 | db $79 ; left 1 + bits 1 733 | db $91 ; bits 1 + skip 1 734 | db $99 ; bits 1 + bits 1 735 | db $86 ; right 1 + pairs 4 736 | db $03 ; u-turn + pairs 6 737 | db $12 ; skip 1 + pairs 54 738 | db $02 ; u-turn + pairs 54 739 | db $13 ; skip 1 + pairs 6 740 | db $03 ; u-turn + pairs 6 741 | db $12 ; skip 1 + pairs 54 742 | db $02 ; u-turn + pairs 54 743 | db $13 ; skip 1 + pairs 6 744 | db $03 ; u-turn + pairs 6 745 | db $12 ; skip 1 + pairs 54 746 | db $02 ; u-turn + pairs 54 747 | db $13 ; skip 1 + pairs 6 748 | db $03 ; u-turn + pairs 6 749 | db $12 ; skip 1 + pairs 54 750 | db $02 ; u-turn + pairs 54 751 | db $13 ; skip 1 + pairs 6 752 | db $03 ; u-turn + pairs 6 753 | db $12 ; skip 1 + pairs 54 754 | db $0e ; u-turn + skip 7 755 | db $1f ; skip 1 + pairs 20 756 | db $54 ; skip 5 + pairs 19 757 | db $07 ; u-turn + left 1 758 | db $45 ; pairs 19 + skip 5 759 | db $b0 ; pairs 17 + u-turn 760 | db $c0 ; pairs 41 + u-turn 761 | db $c0 ; pairs 41 + u-turn 762 | qrc11_modules_end: 763 | -------------------------------------------------------------------------------- /etc/zxtext2p.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | ZXText2P.c - ASCII text file to ZX81 emulator .P file 3 | 4 | By Chris Cowley 5 | 6 | Portions of this code were taken from zmakebas 7 | (public domain by Russell Marks) 8 | 9 | Copyright (c)2002 Chris Cowley. 10 | 11 | This program is free software; you can redistribute it and/or 12 | modify it under the terms of the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 14 | of the License, or (at your option) any later version. 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 | 24 | 25 | COMPILING 26 | gcc - Use "gcc -lm zxtext2p.c". 27 | VC++ - Open zxtext2p.c, create a default workspace, press F7 to 28 | build. 29 | Turbo C++ - Open zxtext2p.c, change MAX_LABELS to 512, set Memory Model 30 | to "Small" select Compile->Make EXE file. 31 | 32 | *****************************************************************************/ 33 | 34 | #define DEFAULT_OUT_FILE "out.p" 35 | #define MAX_LABEL_LEN 32 36 | #define MAX_LABELS 2000 37 | 38 | 39 | #include 40 | #include 41 | #include 42 | #ifdef HAVE_GETOPT 43 | #include 44 | #endif 45 | #include 46 | #include 47 | 48 | #ifndef TRUE 49 | #define TRUE -1 50 | #endif 51 | 52 | #ifndef FALSE 53 | #define FALSE 0 54 | #endif 55 | 56 | int iStartLineNum = 10, iLineInc=2; 57 | char sInFile[256], sOutFile[256]; 58 | int UseLabels = FALSE; 59 | int labelend=0; 60 | unsigned char labels[MAX_LABELS][MAX_LABEL_LEN+1]; 61 | int label_lines[MAX_LABELS]; 62 | unsigned char filebuf[32768]; 63 | unsigned char sysvarbuf[116]; /* Buffer for holding the system variables */ 64 | 65 | 66 | #define REM_TOKEN_NUM 234 67 | 68 | char *tokens[]= { 69 | "copy", "", 70 | "return", "", 71 | "clear", "", 72 | "unplot", "", 73 | "cls", "", 74 | "if", "", 75 | "randomize", "rand", 76 | "save", "", 77 | "run", "", 78 | "plot", "", 79 | "print", "", 80 | "poke", "", 81 | "next", "", 82 | "pause", "", 83 | "let", "", 84 | "list", "", 85 | "load", "", 86 | "input", "", 87 | "go sub", "gosub", 88 | "go to", "goto", 89 | "for", "", 90 | "rem", "", 91 | "dim", "", 92 | "continue", "cont", 93 | "scroll", "", 94 | "new", "", 95 | "fast", "", 96 | "slow", "", 97 | "stop", "", 98 | "llist", "", 99 | "lprint", "", 100 | "step", "", 101 | "to", "", 102 | "then", "", 103 | "<>", "", 104 | ">=", "", 105 | "<=", "", 106 | "and", "", 107 | "or", "", 108 | "**", "", 109 | "not", "", 110 | "chr$", "", 111 | "str$","", 112 | "usr","", 113 | "peek", "", 114 | "abs", "", 115 | "sgn", "", 116 | "sqr", "", 117 | "int", "", 118 | "exp", "", 119 | "ln", "", 120 | "atn", "", 121 | "acs", "", 122 | "asn", "", 123 | "tan", "", 124 | "cos", "", 125 | "sin", "", 126 | "len", "", 127 | "val", "", 128 | "code", "", 129 | "", "", /* token 195 - not used */ 130 | "tab", "", 131 | "at", "", 132 | "", "", /* ZX81 escaped-quote char */ 133 | "pi", "", /* The codes for PI, INKEY$ and RND are just placeholders. */ 134 | "inkey$", "", /* They get translated into the appropriate ZX81 tokens by */ 135 | "rnd", "", /* the ASCIIToZXChar() function */ 136 | NULL 137 | }; 138 | 139 | 140 | #ifndef HAVE_GETOPT 141 | /* 142 | * getopt() 143 | * 144 | * If there is no getopt function available, use this simple 145 | * work-alike (taken from zmakebas, public domain by Russell Marks 146 | */ 147 | int optopt=0, opterr=0, optind=1; 148 | char *optarg=NULL; 149 | 150 | /* holds offset in current argv[] value */ 151 | static unsigned int optpos=1; 152 | 153 | /* This routine assumes that the caller is pretty sane and doesn't 154 | * try passing an invalid 'optstring' or varying argc/argv. 155 | */ 156 | int getopt(int argc,char *argv[],char *optstring) 157 | { 158 | char *ptr; 159 | 160 | /* check for end of arg list */ 161 | if(optind==argc || *(argv[optind])!='-' || strlen(argv[optind])<=1) 162 | return(-1); 163 | 164 | if((ptr=strchr(optstring,argv[optind][optpos]))==NULL) 165 | return('?'); /* error: unknown option */ 166 | else 167 | { 168 | optopt=*ptr; 169 | if(ptr[1]==':') 170 | { 171 | if(optind==argc-1) return(':'); /* error: missing option */ 172 | optarg=argv[optind+1]; 173 | optpos=1; 174 | optind+=2; 175 | return(optopt); /* return early, avoiding the normal increment */ 176 | } 177 | } 178 | 179 | /* now increment position ready for next time. 180 | * no checking is done for the end of args yet - this is done on 181 | * the next call. 182 | */ 183 | optpos++; 184 | if(optpos>=strlen(argv[optind])) 185 | { 186 | optpos=1; 187 | optind++; 188 | } 189 | 190 | return(optopt); /* return the found option */ 191 | } 192 | #endif /* !HAVE_GETOPT */ 193 | 194 | 195 | /* 196 | * ShowUsage() 197 | * 198 | * Displays usage text in response to the '-h' help option 199 | * 200 | */ 201 | void ShowUsage() 202 | { 203 | printf("\nusage: zxtext2p [-h] [-i incr] [-l] [-o output-file] [-s line] [input-file]\n\n"); 204 | 205 | printf(" -h show this help information.\n"); 206 | printf(" -i in labels mode, set line number increment. (default = 2).\n"); 207 | printf(" -l use labels rather than line numbers.\n"); 208 | printf(" -o specify output file (default `%s').\n",DEFAULT_OUT_FILE); 209 | printf(" -s in labels mode, set starting line number (default = 10).\n"); 210 | } 211 | 212 | 213 | /* 214 | * ParseOptions() 215 | * 216 | * Process the command line options 217 | * 218 | */ 219 | void ParseOptions(int argc,char *argv[]) 220 | { 221 | int done = 0; 222 | 223 | opterr=0; 224 | 225 | do { 226 | switch(getopt(argc,argv,"a:hi:ln:o:rs:")) 227 | { 228 | case 'h': /* usage help */ 229 | ShowUsage(); 230 | exit(1); 231 | case 'i': /* increment (for labels mode) */ 232 | iLineInc = atoi(optarg); 233 | if ( iLineInc<1 || iLineInc>9990 ) { 234 | fprintf(stderr,"Label line incr. must be in the range 1 to 9990.\n"); 235 | exit(1); 236 | } 237 | break; 238 | case 'l': /* enable labels mode (off by default) */ 239 | UseLabels = TRUE; 240 | break; 241 | case 'o': /* specify output file */ 242 | strcpy(sOutFile,optarg); 243 | break; 244 | case 's': /* start line number (for labels mode) */ 245 | iStartLineNum = atoi(optarg); 246 | if(iStartLineNum<0 || iStartLineNum>9999) { 247 | fprintf(stderr,"Label start line must be in the range 0 to 9999.\n"); 248 | exit(1); 249 | } 250 | break; 251 | case '?': 252 | switch(optopt) { 253 | case 'i': 254 | fprintf(stderr,"The `i' option takes a line increment argument.\n"); 255 | break; 256 | case 'o': 257 | fprintf(stderr,"The `o' option takes a filename argument.\n"); 258 | break; 259 | case 's': 260 | fprintf(stderr,"The `s' option takes a line number argument.\n"); 261 | break; 262 | default: 263 | fprintf(stderr,"Option `%c' not recognised.\n",optopt); 264 | } 265 | exit(1); 266 | case -1: 267 | done = TRUE; 268 | break; 269 | } 270 | } while(!done); 271 | 272 | if(optind10 ) v+=117; 321 | 322 | return(v); 323 | } 324 | 325 | 326 | /* 327 | * dbl2zx81() 328 | * 329 | * Converts a double to an inline-basic-style ZX81 floating point number. 330 | * 331 | * IN: num - Number to convert 332 | * OUT: &exp - Exponent 333 | * OUT: &man - Mantissa 334 | * RETURN: 1 if ok, 0 if exponent too big 335 | * 336 | */ 337 | int dbl2zx81(double num,int *pexp,unsigned long *pman) 338 | { 339 | int exp; 340 | unsigned long man; 341 | 342 | /* Special case for zero */ 343 | if (num == 0) { 344 | exp = man = 0; 345 | *pexp=exp; 346 | *pman=man; 347 | return(1); 348 | } 349 | 350 | /* If negative work with the absolute value */ 351 | if (num < 0) num = -num; 352 | 353 | exp = (int)floor( log(num)/log(2) ); 354 | 355 | if (exp < -129 || exp > 126) return 0; 356 | 357 | man = (unsigned long) floor ( (num/pow(2,exp) - 1) * 0x80000000 + 0.5); 358 | /* The sign bit is always reset, even for negative numbers, 359 | which seems like a bit of waste */ 360 | man = man & 0x7fffffff; 361 | 362 | /* Adjust the exponent for storage */ 363 | exp += 129; 364 | 365 | *pexp=exp; *pman=man; 366 | return(1); 367 | } 368 | 369 | 370 | /* 371 | * ASCIIToZXChar() 372 | * 373 | * IN: c - ASCII character to convert 374 | * IN: iLineNum - Line number of input file, used when reporting errors 375 | * RETURN: ZX81 character code 376 | * 377 | */ 378 | unsigned char ASCIIToZXChar(unsigned char c,int iLineNum) 379 | { 380 | c = tolower(c); 381 | if ( c >= 'a' && c <= 'z' ) return (c-59); /* A to Z */ 382 | if ( c >= '0' && c <= '9' ) return (c-20); /* 0 to 9 */ 383 | 384 | switch( tolower(c) ) { 385 | case ' ': return 0; 386 | case '"': return 11; 387 | case '#': return 12; /* map '#' to the ZX81 pound symbol */ 388 | case '$': return 13; 389 | case '(': return 16; 390 | case ')': return 17; 391 | case '*': return 23; 392 | case '+': return 21; 393 | case ',': return 26; 394 | case '-': return 22; 395 | case '.': return 27; 396 | case '/': return 24; 397 | case ':': return 14; 398 | case ';': return 25; 399 | case '<': return 19; 400 | case '=': return 20; 401 | case '>': return 18; 402 | case '?': return 15; 403 | case 191: return 66; /* Replace the PI placeholder */ 404 | case 190: return 65; /* Replace the INKEY$ placeholder */ 405 | case 189: return 64; /* Replace the RND placeholder */ 406 | 407 | case '!': case '%': case '&': 408 | case 39: case '@': 409 | fprintf(stderr,"line %d: Non-mappable character in input\n",iLineNum); 410 | exit(1); 411 | } 412 | 413 | return (c); /* It's a token, return the code unmodified */ 414 | } 415 | 416 | 417 | /* 418 | * main() 419 | * 420 | * Process any command line options 421 | * Open the input file 422 | * Read and process it, writing the tokenized output to filebuf 423 | * Write out a p file using the contents of filebuf 424 | * 425 | */ 426 | int main(int argc,char *argv[]) 427 | { 428 | FILE *fIn=stdin, *fOut=stdout; 429 | int iInputLineNum = 0; 430 | int iPassNum = 1; 431 | static unsigned char buf[1024],lcasebuf[1024],outbuf[2048]; 432 | int iLineNum = 0, iLastLineNum = 0, iFirstLineNum = -1; 433 | int iTemp, in_quotes, toknum, toklen, alttok, in_rem; 434 | unsigned char *ptr, *linestart, *ptrLowCase, *remptr, *outptr, *fileptr; 435 | char **tarrptr; 436 | 437 | double num; 438 | int num_exp; 439 | unsigned long num_mantissa; 440 | 441 | strcpy(sInFile,""); 442 | strcpy(sOutFile,DEFAULT_OUT_FILE); 443 | 444 | fileptr=filebuf; 445 | 446 | ParseOptions(argc,argv); 447 | 448 | if( strcmp(sInFile,"") != 0 && (fIn=fopen(sInFile,"r"))==NULL) { 449 | fprintf(stderr,"Could not open input file.\n"); 450 | exit(1); 451 | } 452 | 453 | /* Read and parse the input file 454 | * 455 | * There is one pass made if using line numbers 456 | * and two passes if using labels 457 | */ 458 | do { 459 | if( UseLabels ) iLineNum = iStartLineNum - iLineInc; 460 | 461 | if( iPassNum>1 && fseek(fIn,0L,SEEK_SET) !=0 ) { 462 | fprintf(stderr,"Need seekable input for label support\n"); 463 | exit(1); 464 | } 465 | 466 | while( fgets(buf+1,sizeof(buf)-1,fIn) != NULL ) { 467 | buf[0]=32; /* just in case, for all the ptr[-1] stuff */ 468 | iInputLineNum++; 469 | iLastLineNum=iLineNum; 470 | 471 | if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0; 472 | 473 | /* allow for (shell-style) comments which don't appear in the program, 474 | * and also ignore blank lines. 475 | */ 476 | if(buf[1]==0 || buf[1]=='#') continue; 477 | 478 | /* check for line continuation */ 479 | while(buf[strlen(buf)-1]=='\\') { 480 | iTemp = strlen(buf)-1; 481 | fgets(buf+iTemp,sizeof(buf) - iTemp, fIn); 482 | iInputLineNum++; 483 | if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0; 484 | } 485 | 486 | if(strlen(buf)>=sizeof(buf)-MAX_LABEL_LEN-1) { 487 | /* this is nasty, but given how the label substitution works it's 488 | * probably the safest thing to do. 489 | */ 490 | fprintf(stderr,"line %d: line too big for input buffer\n",iInputLineNum); 491 | exit(1); 492 | } 493 | 494 | /* get line number (or assign one) */ 495 | if(UseLabels) { 496 | linestart = buf; 497 | /* assign a line number */ 498 | iLineNum += iLineInc; 499 | 500 | /* Remember the first line number for use when writing out the sysvars */ 501 | if (iFirstLineNum == -1) iFirstLineNum = iLineNum; 502 | 503 | if(iLineNum>9999) { 504 | fprintf(stderr,"Generated line number is >9999 - %s\n", 505 | (iLineInc>1) ? "try using `-s 1 -i 1'" : "too many lines!"); 506 | exit(1); 507 | } 508 | } 509 | else { 510 | ptr=buf; 511 | while ( isspace(*ptr) ) ptr++; /* Trim any leading spaces from the line */ 512 | if ( !isdigit(*ptr) ) { 513 | fprintf(stderr,"line %d: missing line number\n",iInputLineNum); 514 | exit(1); 515 | } 516 | iLineNum = strtol(ptr,(char **)&linestart,10); 517 | 518 | /* Remember the first line number for use when writing out the sysvars */ 519 | if (iFirstLineNum == -1) iFirstLineNum = iLineNum; 520 | 521 | if(iLineNum<=iLastLineNum) { 522 | fprintf(stderr,"line %d: line no. not greater than previous one\n", 523 | iInputLineNum); 524 | exit(1); 525 | } 526 | } 527 | 528 | if( iLineNum<0 || iLineNum>9999 ) { 529 | fprintf(stderr,"line %d: line no. out of range\n",iInputLineNum); 530 | exit(1); 531 | } 532 | 533 | /* lose any spaces between line number and the start of the BASIC statement */ 534 | while(isspace(*linestart)) linestart++; 535 | 536 | /* check there's no line numbers on label-using programs */ 537 | if(UseLabels && isdigit(*linestart)) { 538 | fprintf(stderr,"line %d: line number used in labels mode\n",iInputLineNum); 539 | exit(1); 540 | } 541 | 542 | /* If this is a label-defining line... */ 543 | if ( UseLabels && *linestart=='@' ) { 544 | if ( (ptr=strchr(linestart,':'))==NULL ) { 545 | fprintf(stderr,"line %d: incomplete label definition\n",iInputLineNum); 546 | exit(1); 547 | } 548 | 549 | if(ptr-linestart-1>MAX_LABEL_LEN) { 550 | fprintf(stderr,"line %d: label too long\n",iInputLineNum); 551 | exit(1); 552 | } 553 | 554 | if(iPassNum==1) { 555 | *ptr=0; 556 | label_lines[labelend]=iLineNum; 557 | strcpy(labels[labelend++],linestart+1); 558 | if(labelend>=MAX_LABELS) { 559 | fprintf(stderr,"line %d: too many labels\n",iInputLineNum); 560 | exit(1); 561 | } 562 | for(iTemp=0;iTemp, <=, >=. 640 | */ 641 | if ( (*tarrptr)[0]=='<' || (*tarrptr)[1]=='=' || 642 | (!isalpha(ptr[-1]) && !isalpha(ptr[toklen])) ) { 643 | ptrLowCase=linestart+(ptr-lcasebuf); 644 | /* the token text is overwritten in the lcase copy too, to 645 | * avoid problems with e.g. go to/to. 646 | */ 647 | *ptrLowCase=*ptr=toknum; 648 | for(iTemp = 1; iTemp < toklen; iTemp++) ptrLowCase[iTemp] = ptr[iTemp] = 1; 649 | /* absorb trailing spaces too */ 650 | while(ptrLowCase[iTemp]==' ') ptrLowCase[iTemp++]=1; 651 | } 652 | ptr+=toklen; 653 | } 654 | } 655 | 656 | 657 | /* Okay, now both the buf, and lcasebuf are tokenized */ 658 | 659 | if(UseLabels) { 660 | /* replace @label with matching number. 661 | * this expands labels in strings too, since: 662 | * 1. it seems reasonable to assume you might want this; 663 | * 2. it makes the code a bit simpler :-) ; 664 | * 3. you can use the escape `\@' to get a literal `@' anyway. 665 | */ 666 | ptr=linestart; 667 | while((ptr=strchr(ptr,'@'))!=NULL) { 668 | if(ptr[-1]=='\\') { 669 | ptr++; 670 | continue; 671 | } 672 | 673 | /* the easiest way to spot them is to try matching against 674 | * each label in turn. It's gross, but at least it's sane 675 | * and doesn't restrict what you can have as a label. 676 | * We also test that the char after a match is not a printable 677 | * ascii char (other than space or colon), to prevent matches 678 | * against the shorter of two possibilities. 679 | */ 680 | ptr++; 681 | for(iTemp=0; iTemp126 || ptr[len]==':')) { 685 | unsigned char numbuf[20]; 686 | 687 | /* this could be optimised to use a single memmove(), but 688 | * at least this way it's clear(er) what's happening. 689 | */ 690 | /* switch text for label. first, remove text */ 691 | memmove(ptr-1,ptr+len,strlen(ptr+len)+1); 692 | /* make number string */ 693 | sprintf(numbuf,"%d",label_lines[iTemp]); 694 | len=strlen(numbuf); 695 | /* insert room for number string */ 696 | ptr--; 697 | memmove(ptr+len,ptr,strlen(ptr)+1); 698 | memcpy(ptr,numbuf,len); 699 | ptr+=len; 700 | break; 701 | } 702 | } 703 | if( iTemp==labelend ) { 704 | fprintf(stderr,"line %d: undefined label\n",iInputLineNum); 705 | exit(1); 706 | } 707 | } 708 | } 709 | 710 | if(remptr) *remptr=REM_TOKEN_NUM; 711 | 712 | /* remove 0x01s, deal with escapes, and add numbers */ 713 | ptr=linestart; 714 | outptr=outbuf; 715 | in_rem=in_quotes=0; 716 | 717 | while(*ptr) { 718 | if(outptr>outbuf+sizeof(outbuf)-10) { 719 | fprintf(stderr,"line %d: line too big\n",iInputLineNum); 720 | exit(1); 721 | } 722 | 723 | if(*ptr=='"') in_quotes=!in_quotes; 724 | 725 | /* as well as 0x01 chars, we skip tabs. */ 726 | if(*ptr==1 || *ptr==9 || (!in_quotes && !in_rem && *ptr==' ')) { 727 | ptr++; 728 | continue; 729 | } 730 | 731 | if(*ptr==REM_TOKEN_NUM) in_rem=1; 732 | 733 | /* Handle inverse video escape (prefix '%') */ 734 | if(!in_rem && *ptr=='%') { 735 | *outptr ++= ASCIIToZXChar(ptr[1],iInputLineNum) | 128; 736 | ptr+=2; 737 | continue; 738 | } 739 | 740 | 741 | /* Handle escaped characters (block graphics and quote char) */ 742 | if(!in_rem && *ptr=='\\') { 743 | switch(ptr[1]) { 744 | case '"': *outptr++=192; break; 745 | case '\'': case '.': case ':': case ' ': /* block graphics char */ 746 | case '~': case ',': case '#': case '@': case ';': case '!': 747 | *outptr++=BlockGraphic(ptr,iInputLineNum); 748 | ptr++; 749 | break; 750 | default: 751 | fprintf(stderr,"line %d: warning: unknown escape `%c', inserting literally\n", 752 | iInputLineNum,ptr[1]); 753 | *outptr++=ptr[1]; 754 | } 755 | ptr+=2; 756 | continue; 757 | } 758 | 759 | /* spot any numbers (because we have to add the inline FP 760 | * representation). We do this largely by relying on strtod(), 761 | * so that we only have to find the start - i.e. a non-alpha char, 762 | * an optional `-' or `+', an optional `.', then a digit. 763 | */ 764 | if( !in_rem && !in_quotes && !isalpha(ptr[-1]) && 765 | (isdigit(*ptr) || 766 | ((*ptr=='-' || *ptr=='+' || *ptr=='.') && isdigit(ptr[1])) || 767 | ((*ptr=='-' || *ptr=='+') && ptr[1]=='.' && isdigit(ptr[2])))) 768 | { 769 | /* we have a number. parse with strtod(). */ 770 | num=strtod(ptr,(char **)&ptrLowCase); 771 | 772 | /* output text of number */ 773 | while (ptr>24); 784 | *outptr ++= (unsigned char) (num_mantissa>>16); 785 | *outptr ++= (unsigned char) (num_mantissa>>8); 786 | *outptr ++= (unsigned char) (num_mantissa&255); 787 | ptr = ptrLowCase; 788 | } 789 | else { 790 | /* if not number, just output char */ 791 | *outptr++=ASCIIToZXChar(*ptr++,iInputLineNum); 792 | } 793 | } 794 | 795 | *outptr++=0x76; /* add terminating NEWLINE */ 796 | 797 | 798 | /* output line */ 799 | iTemp=outptr-outbuf; 800 | if(fileptr+4+iTemp>=filebuf+sizeof(filebuf)) { 801 | /* the program would be too big to load into a ZX81 long before 802 | * you'd get this error, but FWIW... 803 | */ 804 | fprintf(stderr,"program too big!\n"); 805 | exit(1); 806 | } 807 | *fileptr++=(iLineNum>>8); 808 | *fileptr++=(iLineNum&255); 809 | *fileptr++=(iTemp&255); 810 | *fileptr++=(iTemp>>8); 811 | memcpy(fileptr,outbuf,iTemp); 812 | fileptr+=iTemp; 813 | 814 | 815 | } /* while */ 816 | 817 | iPassNum++; 818 | } while( UseLabels && iPassNum<=2); /* end of do..while() pass loop */ 819 | 820 | if ( fIn != stdin ) fclose(fIn); 821 | 822 | 823 | /* write the output file */ 824 | 825 | if( strcmp(sOutFile,"-") != 0 && (fOut=fopen(sOutFile,"wb")) == NULL ) { 826 | fprintf(stderr,"Couldn't open output file.\n"); 827 | exit(1); 828 | } 829 | 830 | 831 | /* 832 | ** make sysvars: 116 bytes from 0x4009 (16393) to 16509 833 | */ 834 | iTemp = fileptr-filebuf + 0x407D; /* iTemp = Address of D_FILE */ 835 | memset(sysvarbuf,0,116); 836 | 837 | /* E_PPC - Line number of line which has the edit cursor */ 838 | sysvarbuf[1]=iFirstLineNum & 255; 839 | sysvarbuf[2]=iFirstLineNum>>8; /* E_PPC high */ 840 | /* D_FILE - Address of display file */ 841 | sysvarbuf[3]=iTemp & 255; 842 | sysvarbuf[4]=iTemp++>>8; 843 | /* DF_CC - Address of print position in display file */ 844 | sysvarbuf[5]=iTemp & 255; 845 | sysvarbuf[6]=iTemp>>8; 846 | iTemp += 792; /* iTemp = Address of VARS area */ 847 | /* VARS - Address of program variables */ 848 | sysvarbuf[7]=iTemp & 255; 849 | sysvarbuf[8]=iTemp++>>8; 850 | /* E_LINE - Pointer to line edit buffer */ 851 | sysvarbuf[11]=iTemp & 255; 852 | sysvarbuf[12]=iTemp++>>8; 853 | /* CH_ADD - Address of the next character to be interpreted */ 854 | sysvarbuf[13]=iTemp & 255; 855 | sysvarbuf[14]=iTemp++>>8; 856 | /* STKBOT - Points to the bottom of the calculator stack AND... 857 | STKEND - Points to the end of the calculator stack */ 858 | sysvarbuf[17]=sysvarbuf[19]=iTemp & 255; 859 | sysvarbuf[18]=sysvarbuf[20]=iTemp>>8; 860 | /* MEM - Address of area used for calculator's memory (we point this at MEMBOT 0x405D */ 861 | sysvarbuf[22]=0x5d; 862 | sysvarbuf[23]=0x40; 863 | /* DF_SZ - Number of lines (including 1 blank line) in lower part of the screen */ 864 | sysvarbuf[25]=2; 865 | /* LAST_K - Shows which keys are pressed */ 866 | sysvarbuf[28]=255; 867 | sysvarbuf[29]=255; 868 | /* DEBOUNCE - Debounce status of keyboard */ 869 | sysvarbuf[30]=0x0f; 870 | /* MARGIN - Number of scanlines before start of display (55=PAL, 31=NTSC) */ 871 | sysvarbuf[31]=55; 872 | sysvarbuf[32]=0x7d; /*sysvarbuf[3]; /* NXTLIN low - Address of next program line to be executed */ 873 | sysvarbuf[33]=0x40; /*sysvarbuf[4]; /* NXTLIN high */ 874 | /* T_ADDR - Address of next item in syntax table */ 875 | sysvarbuf[39]=0x6b; 876 | sysvarbuf[40]=0x0c; 877 | /* FRAMES - Seed for random number generation. Set by RAND */ 878 | sysvarbuf[43]=0x7d; 879 | sysvarbuf[44]=0xfd; 880 | /* PR_CC - Least significant byte of address of next position for LPRINT to print */ 881 | sysvarbuf[47]=0xbc; 882 | /* S_POSN x - Column number for print position */ 883 | sysvarbuf[48]=0x21; 884 | /* S_POSN y - Row number for print position */ 885 | sysvarbuf[49]=0x18; 886 | /* CDFLAG - Various flags. Bit 7 is set during SLOW mode, Bit 6 is the true fast/slow flag */ 887 | sysvarbuf[50]=0x40; 888 | /* PRBUF - Printer buffer (33 bytes, 33rd is NEWLINE) */ 889 | sysvarbuf[83]=118; 890 | 891 | 892 | /* write the system variables */ 893 | fwrite(sysvarbuf,1,116,fOut); 894 | 895 | /* write the program data */ 896 | fwrite(filebuf,1,fileptr-filebuf,fOut); 897 | 898 | /* Write an empty DFILE area */ 899 | memset(sysvarbuf,0,33); 900 | sysvarbuf[0]=118; 901 | sysvarbuf[33]=118; 902 | fwrite (sysvarbuf,1,1,fOut); /* Initial HALT */ 903 | 904 | for(iTemp=1;iTemp<25;iTemp++) 905 | fwrite(&sysvarbuf[1],1,33,fOut); /* 24 lines of D_FILE data */ 906 | 907 | 908 | /* Write and empty VARS area */ 909 | sysvarbuf[0]=0x80; 910 | sysvarbuf[1]=0x76; 911 | fwrite (sysvarbuf,1,2,fOut); 912 | 913 | 914 | if(fOut!=stdout) fclose(fOut); 915 | 916 | return(0); 917 | } 918 | -------------------------------------------------------------------------------- /src/debug/z80.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /*---------------------------------------------------------------------------*/ 9 | /* z80.h and z80dasm.h config and inclusion */ 10 | #define CHIPS_IMPL 11 | #define CHIPS_ASSERT(c) 12 | #include "z80.h" 13 | #include "z80dasm.h" 14 | /*---------------------------------------------------------------------------*/ 15 | 16 | static bool l_checkboolean(lua_State* const L, int const index) { 17 | luaL_checktype(L, index, LUA_TBOOLEAN); 18 | return lua_toboolean(L, index) == 0 ? false : true; 19 | } 20 | 21 | #define Z80_STATE_MT "Z80State" 22 | 23 | typedef struct { 24 | z80_t z80; 25 | lua_State* L; 26 | int tick_ref; 27 | int trap_cb_ref; 28 | } 29 | Z80State; 30 | 31 | static Z80State* z80_check(lua_State* const L, int const index) { 32 | return (Z80State*)luaL_checkudata(L, index, Z80_STATE_MT); 33 | } 34 | 35 | static uint64_t tick(int const num_ticks, uint64_t const pins, void* const user_data) { 36 | Z80State* const self = (Z80State*)user_data; 37 | lua_State* const L = self->L; 38 | 39 | lua_rawgeti(L, LUA_REGISTRYINDEX, self->tick_ref); 40 | lua_pushvalue(L, 1); 41 | lua_pushinteger(L, num_ticks); 42 | lua_pushinteger(L, pins); 43 | 44 | lua_call(L, 3, 1); 45 | 46 | if (!lua_isinteger(L, -1)) { 47 | return luaL_error(L, "'tick' must return an integer"); 48 | } 49 | 50 | lua_Integer const new_pins = lua_tointeger(L, -1); 51 | lua_pop(L, 1); 52 | return new_pins; 53 | } 54 | 55 | static int trap_cb(uint16_t const pc, uint32_t const ticks, uint64_t const pins, void* const trap_user_data) { 56 | Z80State* const self = (Z80State*)trap_user_data; 57 | lua_State* const L = self->L; 58 | 59 | lua_rawgeti(L, LUA_REGISTRYINDEX, self->trap_cb_ref); 60 | lua_pushvalue(L, 1); 61 | lua_pushinteger(L, pc); 62 | lua_pushinteger(L, ticks); 63 | lua_pushinteger(L, pins); 64 | 65 | lua_call(L, 4, 1); 66 | 67 | if (!lua_isinteger(L, -1)) { 68 | return luaL_error(L, "'trap_cb' must return an integer"); 69 | } 70 | 71 | lua_Integer const trap_id = lua_tointeger(L, -1); 72 | lua_pop(L, 1); 73 | return trap_id; 74 | } 75 | 76 | static int l_a(lua_State* const L) { 77 | Z80State* const self = z80_check(L, 1); 78 | lua_pushinteger(L, z80_a(&self->z80)); 79 | return 1; 80 | } 81 | 82 | static int l_af(lua_State* const L) { 83 | Z80State* const self = z80_check(L, 1); 84 | lua_pushinteger(L, z80_af(&self->z80)); 85 | return 1; 86 | } 87 | 88 | static int l_af_(lua_State* const L) { 89 | Z80State* const self = z80_check(L, 1); 90 | lua_pushinteger(L, z80_af_(&self->z80)); 91 | return 1; 92 | } 93 | 94 | static int l_b(lua_State* const L) { 95 | Z80State* const self = z80_check(L, 1); 96 | lua_pushinteger(L, z80_b(&self->z80)); 97 | return 1; 98 | } 99 | 100 | static int l_bc(lua_State* const L) { 101 | Z80State* const self = z80_check(L, 1); 102 | lua_pushinteger(L, z80_bc(&self->z80)); 103 | return 1; 104 | } 105 | 106 | static int l_bc_(lua_State* const L) { 107 | Z80State* const self = z80_check(L, 1); 108 | lua_pushinteger(L, z80_bc_(&self->z80)); 109 | return 1; 110 | } 111 | 112 | static int l_c(lua_State* const L) { 113 | Z80State* const self = z80_check(L, 1); 114 | lua_pushinteger(L, z80_c(&self->z80)); 115 | return 1; 116 | } 117 | 118 | static int l_d(lua_State* const L) { 119 | Z80State* const self = z80_check(L, 1); 120 | lua_pushinteger(L, z80_d(&self->z80)); 121 | return 1; 122 | } 123 | 124 | static int l_de(lua_State* const L) { 125 | Z80State* const self = z80_check(L, 1); 126 | lua_pushinteger(L, z80_de(&self->z80)); 127 | return 1; 128 | } 129 | 130 | static int l_de_(lua_State* const L) { 131 | Z80State* const self = z80_check(L, 1); 132 | lua_pushinteger(L, z80_de_(&self->z80)); 133 | return 1; 134 | } 135 | 136 | static int l_e(lua_State* const L) { 137 | Z80State* const self = z80_check(L, 1); 138 | lua_pushinteger(L, z80_e(&self->z80)); 139 | return 1; 140 | } 141 | 142 | static int l_ei_pending(lua_State* const L) { 143 | Z80State* const self = z80_check(L, 1); 144 | lua_pushboolean(L, z80_ei_pending(&self->z80)); 145 | return 1; 146 | } 147 | 148 | static int l_exec(lua_State* const L) { 149 | Z80State* const self = z80_check(L, 1); 150 | lua_Integer const ticks = luaL_checkinteger(L, 2); 151 | 152 | self->L = L; 153 | uint32_t const executed_ticks = z80_exec(&self->z80, ticks); 154 | self->L = NULL; 155 | 156 | lua_pushinteger(L, executed_ticks); 157 | return 1; 158 | } 159 | 160 | static int l_f(lua_State* const L) { 161 | Z80State* const self = z80_check(L, 1); 162 | lua_pushinteger(L, z80_f(&self->z80)); 163 | return 1; 164 | } 165 | 166 | static int l_fa(lua_State* const L) { 167 | Z80State* const self = z80_check(L, 1); 168 | lua_pushinteger(L, z80_fa(&self->z80)); 169 | return 1; 170 | } 171 | 172 | static int l_fa_(lua_State* const L) { 173 | Z80State* const self = z80_check(L, 1); 174 | lua_pushinteger(L, z80_fa_(&self->z80)); 175 | return 1; 176 | } 177 | 178 | static int l_h(lua_State* const L) { 179 | Z80State* const self = z80_check(L, 1); 180 | lua_pushinteger(L, z80_h(&self->z80)); 181 | return 1; 182 | } 183 | 184 | static int l_hl(lua_State* const L) { 185 | Z80State* const self = z80_check(L, 1); 186 | lua_pushinteger(L, z80_hl(&self->z80)); 187 | return 1; 188 | } 189 | 190 | static int l_hl_(lua_State* const L) { 191 | Z80State* const self = z80_check(L, 1); 192 | lua_pushinteger(L, z80_hl_(&self->z80)); 193 | return 1; 194 | } 195 | 196 | static int l_i(lua_State* const L) { 197 | Z80State* const self = z80_check(L, 1); 198 | lua_pushinteger(L, z80_i(&self->z80)); 199 | return 1; 200 | } 201 | 202 | static int l_iff1(lua_State* const L) { 203 | Z80State* const self = z80_check(L, 1); 204 | lua_pushboolean(L, z80_iff1(&self->z80)); 205 | return 1; 206 | } 207 | 208 | static int l_iff2(lua_State* const L) { 209 | Z80State* const self = z80_check(L, 1); 210 | lua_pushboolean(L, z80_iff2(&self->z80)); 211 | return 1; 212 | } 213 | 214 | static int l_im(lua_State* const L) { 215 | Z80State* const self = z80_check(L, 1); 216 | lua_pushinteger(L, z80_im(&self->z80)); 217 | return 1; 218 | } 219 | 220 | static int l_ix(lua_State* const L) { 221 | Z80State* const self = z80_check(L, 1); 222 | lua_pushinteger(L, z80_ix(&self->z80)); 223 | return 1; 224 | } 225 | 226 | static int l_iy(lua_State* const L) { 227 | Z80State* const self = z80_check(L, 1); 228 | lua_pushinteger(L, z80_iy(&self->z80)); 229 | return 1; 230 | } 231 | 232 | static int l_l(lua_State* const L) { 233 | Z80State* const self = z80_check(L, 1); 234 | lua_pushinteger(L, z80_l(&self->z80)); 235 | return 1; 236 | } 237 | 238 | static int l_opdone(lua_State* const L) { 239 | Z80State* const self = z80_check(L, 1); 240 | lua_pushboolean(L, z80_opdone(&self->z80)); 241 | return 1; 242 | } 243 | 244 | static int l_pc(lua_State* const L) { 245 | Z80State* const self = z80_check(L, 1); 246 | lua_pushinteger(L, z80_pc(&self->z80)); 247 | return 1; 248 | } 249 | 250 | static int l_r(lua_State* const L) { 251 | Z80State* const self = z80_check(L, 1); 252 | lua_pushinteger(L, z80_r(&self->z80)); 253 | return 1; 254 | } 255 | 256 | static int l_reset(lua_State* const L) { 257 | Z80State* const self = z80_check(L, 1); 258 | z80_reset(&self->z80); 259 | return 0; 260 | } 261 | 262 | static int l_set_a(lua_State* const L) { 263 | Z80State* const self = z80_check(L, 1); 264 | lua_Integer const value = luaL_checkinteger(L, 2); 265 | z80_set_a(&self->z80, value); 266 | return 1; 267 | } 268 | 269 | static int l_set_af(lua_State* const L) { 270 | Z80State* const self = z80_check(L, 1); 271 | lua_Integer const value = luaL_checkinteger(L, 2); 272 | z80_set_af(&self->z80, value); 273 | return 1; 274 | } 275 | 276 | static int l_set_af_(lua_State* const L) { 277 | Z80State* const self = z80_check(L, 1); 278 | lua_Integer const value = luaL_checkinteger(L, 2); 279 | z80_set_af_(&self->z80, value); 280 | return 1; 281 | } 282 | 283 | static int l_set_b(lua_State* const L) { 284 | Z80State* const self = z80_check(L, 1); 285 | lua_Integer const value = luaL_checkinteger(L, 2); 286 | z80_set_b(&self->z80, value); 287 | return 1; 288 | } 289 | 290 | static int l_set_bc(lua_State* const L) { 291 | Z80State* const self = z80_check(L, 1); 292 | lua_Integer const value = luaL_checkinteger(L, 2); 293 | z80_set_bc(&self->z80, value); 294 | return 1; 295 | } 296 | 297 | static int l_set_bc_(lua_State* const L) { 298 | Z80State* const self = z80_check(L, 1); 299 | lua_Integer const value = luaL_checkinteger(L, 2); 300 | z80_set_bc_(&self->z80, value); 301 | return 1; 302 | } 303 | 304 | static int l_set_c(lua_State* const L) { 305 | Z80State* const self = z80_check(L, 1); 306 | lua_Integer const value = luaL_checkinteger(L, 2); 307 | z80_set_c(&self->z80, value); 308 | return 1; 309 | } 310 | 311 | static int l_set_d(lua_State* const L) { 312 | Z80State* const self = z80_check(L, 1); 313 | lua_Integer const value = luaL_checkinteger(L, 2); 314 | z80_set_d(&self->z80, value); 315 | return 1; 316 | } 317 | 318 | static int l_set_de(lua_State* const L) { 319 | Z80State* const self = z80_check(L, 1); 320 | lua_Integer const value = luaL_checkinteger(L, 2); 321 | z80_set_de(&self->z80, value); 322 | return 1; 323 | } 324 | 325 | static int l_set_de_(lua_State* const L) { 326 | Z80State* const self = z80_check(L, 1); 327 | lua_Integer const value = luaL_checkinteger(L, 2); 328 | z80_set_de_(&self->z80, value); 329 | return 1; 330 | } 331 | 332 | static int l_set_e(lua_State* const L) { 333 | Z80State* const self = z80_check(L, 1); 334 | lua_Integer const value = luaL_checkinteger(L, 2); 335 | z80_set_e(&self->z80, value); 336 | return 1; 337 | } 338 | 339 | static int l_set_ei_pending(lua_State* const L) { 340 | Z80State* const self = z80_check(L, 1); 341 | bool const value = l_checkboolean(L, 2); 342 | z80_set_ei_pending(&self->z80, value); 343 | return 1; 344 | } 345 | 346 | static int l_set_f(lua_State* const L) { 347 | Z80State* const self = z80_check(L, 1); 348 | lua_Integer const value = luaL_checkinteger(L, 2); 349 | z80_set_f(&self->z80, value); 350 | return 1; 351 | } 352 | 353 | static int l_set_fa(lua_State* const L) { 354 | Z80State* const self = z80_check(L, 1); 355 | lua_Integer const value = luaL_checkinteger(L, 2); 356 | z80_set_fa(&self->z80, value); 357 | return 1; 358 | } 359 | 360 | static int l_set_fa_(lua_State* const L) { 361 | Z80State* const self = z80_check(L, 1); 362 | lua_Integer const value = luaL_checkinteger(L, 2); 363 | z80_set_fa_(&self->z80, value); 364 | return 1; 365 | } 366 | 367 | static int l_set_h(lua_State* const L) { 368 | Z80State* const self = z80_check(L, 1); 369 | lua_Integer const value = luaL_checkinteger(L, 2); 370 | z80_set_h(&self->z80, value); 371 | return 1; 372 | } 373 | 374 | static int l_set_hl(lua_State* const L) { 375 | Z80State* const self = z80_check(L, 1); 376 | lua_Integer const value = luaL_checkinteger(L, 2); 377 | z80_set_hl(&self->z80, value); 378 | return 1; 379 | } 380 | 381 | static int l_set_hl_(lua_State* const L) { 382 | Z80State* const self = z80_check(L, 1); 383 | lua_Integer const value = luaL_checkinteger(L, 2); 384 | z80_set_hl_(&self->z80, value); 385 | return 1; 386 | } 387 | 388 | static int l_set_i(lua_State* const L) { 389 | Z80State* const self = z80_check(L, 1); 390 | lua_Integer const value = luaL_checkinteger(L, 2); 391 | z80_set_i(&self->z80, value); 392 | return 1; 393 | } 394 | 395 | static int l_set_iff1(lua_State* const L) { 396 | Z80State* const self = z80_check(L, 1); 397 | bool const value = l_checkboolean(L, 2); 398 | z80_set_iff1(&self->z80, value); 399 | return 1; 400 | } 401 | 402 | static int l_set_iff2(lua_State* const L) { 403 | Z80State* const self = z80_check(L, 1); 404 | lua_Integer const value = l_checkboolean(L, 2); 405 | z80_set_iff2(&self->z80, value); 406 | return 1; 407 | } 408 | 409 | static int l_set_im(lua_State* const L) { 410 | Z80State* const self = z80_check(L, 1); 411 | lua_Integer const value = luaL_checkinteger(L, 2); 412 | z80_set_im(&self->z80, value); 413 | return 1; 414 | } 415 | 416 | static int l_set_ix(lua_State* const L) { 417 | Z80State* const self = z80_check(L, 1); 418 | lua_Integer const value = luaL_checkinteger(L, 2); 419 | z80_set_ix(&self->z80, value); 420 | return 1; 421 | } 422 | 423 | static int l_set_iy(lua_State* const L) { 424 | Z80State* const self = z80_check(L, 1); 425 | lua_Integer const value = luaL_checkinteger(L, 2); 426 | z80_set_iy(&self->z80, value); 427 | return 1; 428 | } 429 | 430 | static int l_set_l(lua_State* const L) { 431 | Z80State* const self = z80_check(L, 1); 432 | lua_Integer const value = luaL_checkinteger(L, 2); 433 | z80_set_l(&self->z80, value); 434 | return 1; 435 | } 436 | 437 | static int l_set_pc(lua_State* const L) { 438 | Z80State* const self = z80_check(L, 1); 439 | lua_Integer const value = luaL_checkinteger(L, 2); 440 | z80_set_pc(&self->z80, value); 441 | return 1; 442 | } 443 | 444 | static int l_set_r(lua_State* const L) { 445 | Z80State* const self = z80_check(L, 1); 446 | lua_Integer const value = luaL_checkinteger(L, 2); 447 | z80_set_r(&self->z80, value); 448 | return 1; 449 | } 450 | 451 | static int l_set_sp(lua_State* const L) { 452 | Z80State* const self = z80_check(L, 1); 453 | lua_Integer const value = luaL_checkinteger(L, 2); 454 | z80_set_sp(&self->z80, value); 455 | return 1; 456 | } 457 | 458 | static int l_set_wz(lua_State* const L) { 459 | Z80State* const self = z80_check(L, 1); 460 | lua_Integer const value = luaL_checkinteger(L, 2); 461 | z80_set_wz(&self->z80, value); 462 | return 1; 463 | } 464 | 465 | static int l_sp(lua_State* const L) { 466 | Z80State* const self = z80_check(L, 1); 467 | lua_pushinteger(L, z80_sp(&self->z80)); 468 | return 1; 469 | } 470 | 471 | static int l_trap_cb(lua_State* const L) { 472 | Z80State* const self = z80_check(L, 1); 473 | 474 | if (self->trap_cb_ref != LUA_NOREF) { 475 | luaL_unref(L, LUA_REGISTRYINDEX, self->trap_cb_ref); 476 | self->trap_cb_ref = LUA_NOREF; 477 | 478 | z80_trap_cb(&self->z80, NULL, NULL); 479 | } 480 | 481 | if (lua_type(L, 2) != LUA_TNIL) { 482 | luaL_checktype(L, 2, LUA_TFUNCTION); 483 | lua_pushvalue(L, 2); 484 | self->trap_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); 485 | 486 | z80_trap_cb(&self->z80, trap_cb, (void*)self); 487 | } 488 | 489 | return 1; 490 | } 491 | 492 | static int l_trap_id(lua_State* const L) { 493 | Z80State* const self = z80_check(L, 1); 494 | lua_pushinteger(L, self->z80.trap_id); 495 | return 1; 496 | } 497 | 498 | static int l_wz(lua_State* const L) { 499 | Z80State* const self = z80_check(L, 1); 500 | lua_pushinteger(L, z80_wz(&self->z80)); 501 | return 1; 502 | } 503 | 504 | static int l_gc(lua_State* const L) { 505 | Z80State* const self = (Z80State*)lua_touserdata(L, 1); 506 | luaL_unref(L, LUA_REGISTRYINDEX, self->tick_ref); 507 | 508 | if (self->trap_cb_ref != LUA_NOREF) { 509 | luaL_unref(L, LUA_REGISTRYINDEX, self->trap_cb_ref); 510 | } 511 | 512 | return 0; 513 | } 514 | 515 | static int l_init(lua_State* const L) { 516 | luaL_checktype(L, 1, LUA_TFUNCTION); 517 | 518 | Z80State* const self = (Z80State*)lua_newuserdata(L, sizeof(Z80State)); 519 | 520 | z80_init(&self->z80, &(z80_desc_t){ 521 | .tick_cb = tick, 522 | .user_data = (void*)self 523 | }); 524 | 525 | lua_pushvalue(L, 1); 526 | self->tick_ref = luaL_ref(L, LUA_REGISTRYINDEX); 527 | self->trap_cb_ref = LUA_NOREF; 528 | self->L = NULL; 529 | 530 | if (luaL_newmetatable(L, Z80_STATE_MT) != 0) { 531 | static luaL_Reg const methods[] = { 532 | {"a", l_a}, 533 | {"af", l_af}, 534 | {"af_", l_af_}, 535 | {"b", l_b}, 536 | {"bc", l_bc}, 537 | {"bc_", l_bc_}, 538 | {"c", l_c}, 539 | {"d", l_d}, 540 | {"de", l_de}, 541 | {"de_", l_de_}, 542 | {"e", l_e}, 543 | {"ei_pending", l_ei_pending}, 544 | {"exec", l_exec}, 545 | {"f", l_f}, 546 | {"fa", l_fa}, 547 | {"fa_", l_fa_}, 548 | {"h", l_h}, 549 | {"hl", l_hl}, 550 | {"hl_", l_hl_}, 551 | {"i", l_i}, 552 | {"iff1", l_iff1}, 553 | {"iff2", l_iff2}, 554 | {"im", l_im}, 555 | {"ix", l_ix}, 556 | {"iy", l_iy}, 557 | {"l", l_l}, 558 | {"opdone", l_opdone}, 559 | {"pc", l_pc}, 560 | {"r", l_r}, 561 | {"reset", l_reset}, 562 | {"set_a", l_set_a}, 563 | {"set_af", l_set_af}, 564 | {"set_af_", l_set_af_}, 565 | {"set_b", l_set_b}, 566 | {"set_bc", l_set_bc}, 567 | {"set_bc_", l_set_bc_}, 568 | {"set_c", l_set_c}, 569 | {"set_d", l_set_d}, 570 | {"set_de", l_set_de}, 571 | {"set_de_", l_set_de_}, 572 | {"set_e", l_set_e}, 573 | {"set_ei_pending", l_set_ei_pending}, 574 | {"set_f", l_set_f}, 575 | {"set_fa", l_set_fa}, 576 | {"set_fa_", l_set_fa_}, 577 | {"set_h", l_set_h}, 578 | {"set_hl", l_set_hl}, 579 | {"set_hl_", l_set_hl_}, 580 | {"set_i", l_set_i}, 581 | {"set_iff1", l_set_iff1}, 582 | {"set_iff2", l_set_iff2}, 583 | {"set_im", l_set_im}, 584 | {"set_ix", l_set_ix}, 585 | {"set_iy", l_set_iy}, 586 | {"set_l", l_set_l}, 587 | {"set_pc", l_set_pc}, 588 | {"set_r", l_set_r}, 589 | {"set_sp", l_set_sp}, 590 | {"set_wz", l_set_wz}, 591 | {"sp", l_sp}, 592 | {"trap_cb", l_trap_cb}, 593 | {"trap_id", l_trap_id}, 594 | {"wz", l_wz} 595 | }; 596 | 597 | luaL_newlib(L, methods); 598 | lua_setfield(L, -2, "__index"); 599 | 600 | lua_pushcfunction(L, l_gc); 601 | lua_setfield(L, -2, "__gc"); 602 | } 603 | 604 | lua_setmetatable(L, -2); 605 | return 1; 606 | } 607 | 608 | static int l_make_pins(lua_State* const L) { 609 | lua_Integer const ctrl = luaL_checkinteger(L, 1); 610 | lua_Integer const addr = luaL_checkinteger(L, 2); 611 | lua_Integer const data = luaL_checkinteger(L, 3); 612 | 613 | lua_Integer const pins = Z80_MAKE_PINS(ctrl, addr, data); 614 | lua_pushinteger(L, pins); 615 | return 1; 616 | } 617 | 618 | static int l_get_addr(lua_State* const L) { 619 | lua_Integer const pins = luaL_checkinteger(L, 1); 620 | lua_Integer const addr = Z80_GET_ADDR(pins); 621 | lua_pushinteger(L, addr); 622 | return 1; 623 | } 624 | 625 | static int l_set_addr(lua_State* const L) { 626 | lua_Integer const pins = luaL_checkinteger(L, 1); 627 | lua_Integer const addr = luaL_checkinteger(L, 2); 628 | 629 | lua_Integer new_pins = pins; 630 | Z80_SET_ADDR(new_pins, addr); 631 | lua_pushinteger(L, new_pins); 632 | return 1; 633 | } 634 | 635 | static int l_get_data(lua_State* const L) { 636 | lua_Integer const pins = luaL_checkinteger(L, 1); 637 | lua_Integer const data = Z80_GET_DATA(pins); 638 | lua_pushinteger(L, data); 639 | return 1; 640 | } 641 | 642 | static int l_set_data(lua_State* const L) { 643 | lua_Integer const pins = luaL_checkinteger(L, 1); 644 | lua_Integer const data = luaL_checkinteger(L, 2); 645 | 646 | lua_Integer new_pins = pins; 647 | Z80_SET_DATA(new_pins, data); 648 | lua_pushinteger(L, new_pins); 649 | return 1; 650 | } 651 | 652 | static int l_get_wait(lua_State* const L) { 653 | lua_Integer const pins = luaL_checkinteger(L, 1); 654 | lua_Integer const wait = Z80_GET_WAIT(pins); 655 | lua_pushinteger(L, wait); 656 | return 1; 657 | } 658 | 659 | static int l_set_wait(lua_State* const L) { 660 | lua_Integer const pins = luaL_checkinteger(L, 1); 661 | lua_Integer const wait = luaL_checkinteger(L, 2); 662 | 663 | lua_Integer new_pins = pins; 664 | Z80_SET_WAIT(new_pins, wait); 665 | lua_pushinteger(L, new_pins); 666 | return 1; 667 | } 668 | 669 | typedef struct { 670 | int input_cb_index; 671 | lua_State* L; 672 | luaL_Buffer B; 673 | int count; 674 | uint8_t bytes[5]; 675 | } 676 | Z80DasmState; 677 | 678 | static void info(uint8_t const* const opcode, uint8_t* const cycles, uint16_t* const flags) { 679 | // 64 is for DJNZ: 13 when it jumps, 8 when it doesn't. 680 | // 65 is for JR cc: 12 when it jumps, 7 when it doesn't. 681 | // 66 is for RET cc: 11 when it returns, 5 when it doesn't. 682 | // 67 is for CALL cc: 17 when it jumps, 10 when it doesn't. 683 | // 68 is for block transfers: 21 when it repeats, 16 when it doesn't. 684 | static uint8_t const cycles_main[256] = { 685 | 4, 10, 7, 6, 4, 4, 7, 4, 4, 11, 7, 6, 4, 4, 7, 4, 686 | 64, 10, 7, 6, 4, 4, 7, 4, 12, 11, 7, 6, 4, 4, 7, 4, 687 | 65, 10, 16, 6, 4, 4, 7, 4, 65, 11, 16, 6, 4, 4, 7, 4, 688 | 65, 10, 13, 6, 11, 11, 10, 4, 65, 11, 13, 6, 4, 4, 7, 4, 689 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 690 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 691 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 692 | 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, 693 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 694 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 695 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 696 | 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, 697 | 66, 10, 10, 10, 67, 11, 7, 11, 66, 10, 10, 0, 67, 17, 7, 11, 698 | 66, 10, 10, 11, 67, 11, 7, 11, 66, 4, 10, 11, 67, 0, 7, 11, 699 | 66, 10, 10, 19, 67, 11, 7, 11, 66, 4, 10, 4, 67, 0, 7, 11, 700 | 66, 10, 10, 4, 67, 11, 7, 11, 66, 6, 10, 4, 67, 0, 7, 11, 701 | }; 702 | 703 | static uint8_t const cycles_ed[128] = { 704 | 12, 12, 15, 20, 8, 14, 8, 9, 12, 8, 15, 8, 8, 14, 8, 9, 705 | 12, 8, 15, 8, 8, 14, 8, 9, 12, 8, 15, 8, 8, 14, 8, 9, 706 | 12, 8, 15, 8, 8, 14, 8, 18, 12, 8, 15, 8, 8, 14, 8, 18, 707 | 12, 8, 15, 8, 8, 14, 8, 8, 12, 8, 15, 8, 8, 14, 8, 8, 708 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 709 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 710 | 16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8, 711 | 68, 68, 68, 68, 8, 8, 8, 8, 68, 68, 68, 68, 8, 8, 8, 8, 712 | }; 713 | 714 | static uint8_t const cycles_ddfd[256] = { 715 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 716 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 717 | 0, 14, 20, 10, 8, 8, 11, 0, 0, 15, 20, 10, 8, 8, 11, 0, 718 | 0, 0, 0, 0, 23, 23, 19, 0, 0, 15, 0, 0, 0, 0, 0, 0, 719 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 720 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 721 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 722 | 19, 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 8, 8, 19, 0, 723 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 724 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 725 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 726 | 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, 727 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 728 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 729 | 0, 14, 0, 23, 0, 15, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 730 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 731 | }; 732 | 733 | // Two bits per flag, in the following order (MSB to LSB): SZ5H3PNV 734 | // 0b00: flag is unchanged 735 | // 0b01: flag is set 736 | // 0b10: flag is reset 737 | // 0b11: flag is changed depending on the CPU state 738 | static uint16_t const flags_main[256] = { 739 | 0x0000, 0x0000, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0ecb, 740 | 0xffff, 0x0fcb, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0ecb, 741 | 0x0000, 0x0000, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0ecb, 742 | 0x0000, 0x0fcb, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0ecb, 743 | 0x0000, 0x0000, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0xfff3, 744 | 0x0000, 0x0fcb, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0dc4, 745 | 0x0000, 0x0000, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0ec9, 746 | 0x0000, 0x0fcb, 0x0000, 0x0000, 0xfffc, 0xfffc, 0x0000, 0x0fcb, 747 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 748 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 749 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 750 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 751 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 752 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 753 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 754 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 755 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 756 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 757 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 758 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 759 | 0xfdfa, 0xfdfa, 0xfdfa, 0xfdfa, 0xfdfa, 0xfdfa, 0xfdfa, 0xfdfa, 760 | 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 761 | 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 0xfefa, 762 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 763 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 764 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 765 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 766 | 0x0000, 0x0000, 0x0000, 0xfef8, 0x0000, 0x0000, 0xffff, 0x0000, 767 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfdfa, 0x0000, 768 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfefa, 0x0000, 769 | 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xfefa, 0x0000, 770 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 771 | }; 772 | 773 | static uint16_t const flags_ed[128] = { 774 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0x0000, 775 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0x0000, 776 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0xfef8, 777 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0xfef8, 778 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0xfef8, 779 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0xfef8, 780 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0x0000, 781 | 0xfef8, 0x0000, 0xffff, 0x0000, 0xfff7, 0x0000, 0x0000, 0x0000, 782 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 783 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 784 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 785 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 786 | 0x0ef8, 0xfff4, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 787 | 0x0ef8, 0xfff4, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 788 | 0x0ef8, 0xfff4, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 789 | 0x0ef8, 0xfff4, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 790 | }; 791 | 792 | if (opcode[0] == 0xcb) { 793 | // Bit instructions, all shift and rotate instructions affect the flags 794 | // in the same way. Same thing for all bit test instructions. Set and 795 | // reset instructions don't affect the flags. 796 | uint8_t const op = opcode[1]; 797 | *flags = op < 0x40 ? 0xfefb : op < 0x80 ? 0xfdf8 : 0x0000; 798 | } 799 | else if (opcode[0] == 0xed) { 800 | // Valid extended instructions change the flags according to the 801 | // flags_ed table. Invalid ones are NOPs and don't change the flags. 802 | uint8_t const op = opcode[1] - 0x40; 803 | *flags = (op < 0x80) ? flags_ed[op] : 0x0000; 804 | } 805 | else if (opcode[0] == 0xdd || opcode[0] == 0xfd) { 806 | // IX or IY. 807 | if (opcode[1] == 0xcb) { 808 | // The index prefix don't change how flags are affected by the bit 809 | // instructions. 810 | uint8_t const op = opcode[3]; 811 | *flags = op < 0x40 ? 0xfefb : op < 0x80 ? 0xfdf8 : 0x0000; 812 | } 813 | else if (opcode[1] == 0xed || opcode[1] == 0xdd || opcode[1] == 0xfd) { 814 | // A prefix followed by ED or by another prefix executes as a NOP, 815 | // which doesn't affect the flags. 816 | *flags = 0x0000; 817 | } 818 | else if (cycles_ddfd[opcode[1]] == 0) { 819 | // A prefix followed by an instruction that doesn't use IX or IY 820 | // is also executed as a NOP. 821 | *flags = 0x0000; 822 | } 823 | else { 824 | // Use the flags_main table since the prefix will affect the flags 825 | // in the same way as the unprefixed instructions. 826 | *flags = flags_main[opcode[1]]; 827 | } 828 | } 829 | else { 830 | // Use the flags_main table directly. 831 | *flags = flags_main[opcode[0]]; 832 | } 833 | 834 | if (opcode[0] == 0xcb) { 835 | // Bit instructions. 836 | uint8_t const op = opcode[1] & 0xc7; 837 | 838 | if (op == 0x06 || op == 0x86 || op == 0xc6) { 839 | // Shift, rotate, set, and reset instructions that use (HL) take 840 | // 15 cycles. 841 | *cycles = 15; 842 | } else if (op == 0x46) { 843 | // Bit test instructions that use (HL) take 12 cycles. 844 | *cycles = 12; 845 | } else { 846 | // All other bit instructions take 8 cycles. 847 | *cycles = 8; 848 | } 849 | } 850 | else if (opcode[0] == 0xed) { 851 | // Valid extended instructions consume cycles according to the 852 | // cycles_ed table. Invalid ones are NOPs that take 8 cycles. 853 | uint8_t const op = opcode[1] - 0x40; 854 | *cycles = (op < 0x80) ? cycles_ed[op] : 8; 855 | } 856 | else if (opcode[0] == 0xdd || opcode[0] == 0xfd) { 857 | // IX or IY. 858 | if (opcode[1] == 0xcb) { 859 | // Bit instructions. 860 | uint8_t const op = opcode[3] & 0xc0; 861 | 862 | if (op == 0x40) { 863 | // Bit test instructions take 20 cycles. 864 | *cycles = 20; 865 | } else { 866 | // All other bit instructions take 23 cycles. 867 | *cycles = 23; 868 | } 869 | } 870 | else if (opcode[1] == 0xed || opcode[1] == 0xdd || opcode[1] == 0xfd) { 871 | // A prefix followed by ED or by another prefix executes as a NOP, 872 | // which takes 4 cycles. 873 | *cycles = 4; 874 | } 875 | else if (cycles_ddfd[opcode[1]] == 0) { 876 | // A prefix followed by an instruction that doesn't use IX or IY 877 | // is also executed as a NOP. 878 | *cycles = 4; 879 | } 880 | else { 881 | // Use the cycles_ddfd table for valid uses of the prefix. 882 | *cycles = cycles_ddfd[opcode[1]]; 883 | } 884 | } 885 | else { 886 | // Use the cycles_main table directly. 887 | *cycles = cycles_main[opcode[0]]; 888 | } 889 | } 890 | 891 | static uint8_t in_cb(void* const user_data) { 892 | Z80DasmState* const ud = (Z80DasmState*)user_data; 893 | 894 | // Push and call the input callback. 895 | lua_pushvalue(ud->L, ud->input_cb_index); 896 | lua_call(ud->L, 0, 1); 897 | 898 | // Oops. 899 | if (!lua_isinteger(ud->L, -1)) { 900 | return luaL_error(ud-> L, "'dasm' input callback must return an integer"); 901 | } 902 | 903 | // Get the returned byte, save it, remove it from the stack, and return. 904 | uint8_t const byte = (uint8_t)lua_tointeger(ud->L, -1); 905 | ud->bytes[ud->count++] = byte; 906 | lua_pop(ud->L, 1); 907 | return byte; 908 | } 909 | 910 | static void out_cb(char const c, void* const user_data) { 911 | // Just add the character to the buffer. 912 | Z80DasmState* const ud = (Z80DasmState*)user_data; 913 | luaL_addchar(&ud->B, c); 914 | } 915 | 916 | static int flag_is(lua_State* const L, uint8_t const condition) { 917 | uint16_t const flags = *(uint16_t*)luaL_checkudata(L, 1, "z80flags"); 918 | 919 | size_t length = 0; 920 | char const* const flag = luaL_checklstring(L, 2, &length); 921 | 922 | if (length != 1) { 923 | unknown: 924 | return luaL_error(L, "unknown Z80 flag %s", flag); 925 | } 926 | 927 | int result = 0; 928 | 929 | switch (*flag) { 930 | case 'S': case 's': result = ((flags >> 14) & 3) == condition; break; 931 | case 'Z': case 'z': result = ((flags >> 12) & 3) == condition; break; 932 | case 'Y': case 'y': case '5': result = ((flags >> 10) & 3) == condition; break; 933 | case 'H': case 'h': result = ((flags >> 8) & 3) == condition; break; 934 | case 'X': case 'x': case '3': result = ((flags >> 6) & 3) == condition; break; 935 | case 'P': case 'p': result = ((flags >> 4) & 3) == condition; break; 936 | case 'N': case 'n': result = ((flags >> 2) & 3) == condition; break; 937 | case 'C': case 'c': result = ((flags >> 0) & 3) == condition; break; 938 | default: goto unknown; 939 | } 940 | 941 | lua_pushboolean(L, result); 942 | return 1; 943 | } 944 | 945 | static int l_flag_unchanged(lua_State* const L) { 946 | return flag_is(L, 0); 947 | } 948 | 949 | static int l_flag_set(lua_State* const L) { 950 | return flag_is(L, 1); 951 | } 952 | 953 | static int l_flag_reset(lua_State* const L) { 954 | return flag_is(L, 2); 955 | } 956 | 957 | static int l_flag_changed(lua_State* const L) { 958 | return flag_is(L, 3); 959 | } 960 | 961 | static int l_flag_tostring(lua_State* const L) { 962 | static char const statuses[] = "-10*"; 963 | 964 | uint16_t const flags = *(uint16_t*)luaL_checkudata(L, 1, "z80flags"); 965 | char result[8]; 966 | 967 | for (int i = 0; i < 8; i++) { 968 | int const shift_amount = 14 - i * 2; 969 | uint8_t const flag_status = (flags >> shift_amount) & 3; 970 | result[i] = statuses[flag_status]; 971 | } 972 | 973 | lua_pushlstring(L, result, 8); 974 | return 1; 975 | } 976 | 977 | static int l_dasm(lua_State* L) { 978 | Z80DasmState ud; 979 | ud.input_cb_index = 1; 980 | 981 | // Get an optional address in the first argument. 982 | uint16_t address = 0; 983 | 984 | if (lua_isinteger(L, 1)) { 985 | address = (uint16_t)lua_tointeger(L, 1); 986 | ud.input_cb_index = 2; 987 | } 988 | 989 | // Make sure we have an input callback. 990 | luaL_checktype(L, ud.input_cb_index, LUA_TFUNCTION); 991 | 992 | // Prepare the userdata. 993 | ud.L = L; 994 | luaL_buffinitsize(L, &ud.B, 32); 995 | ud.count = 0; 996 | 997 | // Disassemble the instruction. 998 | uint16_t const next_address = z80dasm_op(address, in_cb, out_cb, (void*)&ud); 999 | 1000 | // Get cycle and flags information. 1001 | uint8_t cycles = 0; 1002 | uint16_t flags = 0; 1003 | info(ud.bytes, &cycles, &flags); 1004 | 1005 | // Push the results: disassembled instruction, next address. 1006 | luaL_pushresult(&ud.B); 1007 | lua_pushinteger(L, next_address); 1008 | 1009 | // Push affected flags. 1010 | uint16_t* const flags_ud = (uint16_t*)lua_newuserdata(L, sizeof(uint16_t)); 1011 | *flags_ud = flags; 1012 | 1013 | // Set the flags userdata's metatable. 1014 | if (luaL_newmetatable(L, "z80flags")) { 1015 | static const luaL_Reg methods[] = { 1016 | {"unchanged", l_flag_unchanged}, 1017 | {"set", l_flag_set}, 1018 | {"reset", l_flag_reset}, 1019 | {"changed", l_flag_changed}, 1020 | {NULL, NULL} 1021 | }; 1022 | 1023 | luaL_newlib(L, methods); 1024 | lua_setfield(L, -2, "__index"); 1025 | 1026 | lua_pushcfunction(L, l_flag_tostring); 1027 | lua_setfield(L, -2, "__tostring"); 1028 | } 1029 | 1030 | lua_setmetatable(L, -2); 1031 | 1032 | // Push cycles. 1033 | switch (cycles) { 1034 | case 64: lua_pushinteger(L, 13); lua_pushinteger(L, 8); break; 1035 | case 65: lua_pushinteger(L, 12); lua_pushinteger(L, 7); break; 1036 | case 66: lua_pushinteger(L, 11); lua_pushinteger(L, 5); break; 1037 | case 67: lua_pushinteger(L, 17); lua_pushinteger(L, 10); break; 1038 | case 68: lua_pushinteger(L, 21); lua_pushinteger(L, 16); break; 1039 | 1040 | default: lua_pushinteger(L, cycles); lua_pushnil(L); break; 1041 | } 1042 | 1043 | return 5; 1044 | } 1045 | 1046 | LUAMOD_API int luaopen_z80(lua_State* const L) { 1047 | static const luaL_Reg functions[] = { 1048 | {"init", l_init}, 1049 | {"MAKE_PINS", l_make_pins}, 1050 | {"GET_ADDR", l_get_addr}, 1051 | {"SET_ADDR", l_set_addr}, 1052 | {"GET_DATA", l_get_data}, 1053 | {"SET_DATA", l_set_data}, 1054 | {"GET_WAIT", l_get_wait}, 1055 | {"SET_WAIT", l_set_wait}, 1056 | {"dasm", l_dasm}, 1057 | {NULL, NULL} 1058 | }; 1059 | 1060 | static struct {char const* const name; uint64_t const value;} const constants[] = { 1061 | {"A0", Z80_A0}, 1062 | {"A1", Z80_A1}, 1063 | {"A2", Z80_A2}, 1064 | {"A3", Z80_A3}, 1065 | {"A4", Z80_A4}, 1066 | {"A5", Z80_A5}, 1067 | {"A6", Z80_A6}, 1068 | {"A7", Z80_A7}, 1069 | {"A8", Z80_A8}, 1070 | {"A9", Z80_A9}, 1071 | {"A10", Z80_A10}, 1072 | {"A11", Z80_A11}, 1073 | {"A12", Z80_A12}, 1074 | {"A13", Z80_A13}, 1075 | {"A14", Z80_A14}, 1076 | {"A15", Z80_A15}, 1077 | {"D0", Z80_D0}, 1078 | {"D1", Z80_D1}, 1079 | {"D2", Z80_D2}, 1080 | {"D3", Z80_D3}, 1081 | {"D4", Z80_D4}, 1082 | {"D5", Z80_D5}, 1083 | {"D6", Z80_D6}, 1084 | {"D7", Z80_D7}, 1085 | {"M1", Z80_M1}, 1086 | {"MREQ", Z80_MREQ}, 1087 | {"IORQ", Z80_IORQ}, 1088 | {"RD", Z80_RD}, 1089 | {"WR", Z80_WR}, 1090 | {"RFSH", Z80_RFSH}, 1091 | {"CTRL_MASK", Z80_CTRL_MASK}, 1092 | {"HALT", Z80_HALT}, 1093 | {"INT", Z80_INT}, 1094 | {"NMI", Z80_NMI}, 1095 | {"WAIT0", Z80_WAIT0}, 1096 | {"WAIT1", Z80_WAIT1}, 1097 | {"WAIT2", Z80_WAIT2}, 1098 | {"WAIT_SHIFT", Z80_WAIT_SHIFT}, 1099 | {"WAIT_MASK", Z80_WAIT_MASK}, 1100 | {"IEIO", Z80_IEIO}, 1101 | {"RETI", Z80_RETI}, 1102 | {"PIN_MASK", Z80_PIN_MASK} 1103 | }; 1104 | 1105 | static struct {char const* const name; char const* const value;} const info[] = { 1106 | {"_COPYRIGHT", "Copyright (c) 2020 Andre Leiradella"}, 1107 | {"_LICENSE", "MIT"}, 1108 | {"_VERSION", "1.0.0"}, 1109 | {"_NAME", "z80"}, 1110 | {"_URL", "https://github.com/leiradel/luamods/z80"}, 1111 | {"_DESCRIPTION", "Bindings for Andre Weissflog's Z80 emulator"} 1112 | }; 1113 | 1114 | size_t const functions_count = sizeof(functions) / sizeof(functions[0]) - 1; 1115 | size_t const constants_count = sizeof(constants) / sizeof(constants[0]); 1116 | size_t const info_count = sizeof(info) / sizeof(info[0]); 1117 | 1118 | lua_createtable(L, 0, functions_count + constants_count + info_count); 1119 | luaL_setfuncs(L, functions, 0); 1120 | 1121 | for (size_t i = 0; i < constants_count; i++) { 1122 | lua_pushinteger(L, constants[i].value); 1123 | lua_setfield(L, -2, constants[i].name); 1124 | } 1125 | 1126 | for (size_t i = 0; i < info_count; i++) { 1127 | lua_pushstring(L, info[i].value); 1128 | lua_setfield(L, -2, info[i].name); 1129 | } 1130 | 1131 | // Enrich z80.dasm. 1132 | int const load_res = luaL_loadstring(L, 1133 | "return function(z80)\n" 1134 | " local dasm0 = z80.dasm\n" 1135 | "\n" 1136 | " z80.dasm = function(pc, input_cb, start_index)\n" 1137 | " local dasm\n" 1138 | "\n" 1139 | " if type(pc) == 'number' then\n" 1140 | " dasm = function(input_cb) return dasm0(pc, input_cb) end\n" 1141 | " else\n" 1142 | " dasm = function(input_cb) return dasm0(input_cb) end\n" 1143 | " start_index = input_cb\n" 1144 | " input_cb = pc\n" 1145 | " end\n" 1146 | "\n" 1147 | " if type(input_cb) == 'string' then\n" 1148 | " local index = start_index or 1\n" 1149 | "\n" 1150 | " return dasm(function()\n" 1151 | " local byte = input_cb:byte(index)\n" 1152 | " index = index + 1\n" 1153 | " return byte\n" 1154 | " end)\n" 1155 | " elseif type(input_cb) == 'table' then\n" 1156 | " local index = start_index or 1\n" 1157 | "\n" 1158 | " return dasm(function()\n" 1159 | " local byte = input_cb[index]\n" 1160 | " index = index + 1\n" 1161 | " return byte\n" 1162 | " end)\n" 1163 | " else\n" 1164 | " return dasm(input_cb)\n" 1165 | " end\n" 1166 | " end\n" 1167 | "end\n" 1168 | ); 1169 | 1170 | if (load_res != LUA_OK) { 1171 | return lua_error(L); 1172 | } 1173 | 1174 | // Call the chunk to get the function, and then call it with the module. 1175 | lua_call(L, 0, 1); 1176 | lua_pushvalue(L, -2); 1177 | lua_call(L, 1, 0); 1178 | 1179 | return 1; 1180 | } 1181 | --------------------------------------------------------------------------------