As I've mentioned in IRC, I'm currently working on a QR Code generator for the z80-based monochrome TI calculators (focusing on the 84+ since that's what I have available, but I'll be try to keep it as portable as I can for reasons explained below).

On and off over the past 5 years, I've made some progress on a TI-Basic version (and the work I did with it contributed directly to my final project in my VisualBasic CS class in highschool), but the code is scattered around multiple, poorly named files, and TI-Basic doesn't lend itself well to writing inline documentation, so most of it is unreadable to me. Additionally, given that the underlying Reed–Solomon encoding is based on GF(256), many of the relevant algorithms appear to be well suited for 8-bit arithmetic. (And now that it's five years later and I've graduated as a math & physics major and I've taken both abstract algebra and logic design, I actually know what that means, rather than just knowing that it's some fancy math I'll need to get around.)

I also saw in the file archives that SopaXorzTaker has already made a limited TI-Basic version (according to the readme, not dissimilar in scope to the VB version I made in highschool: only the 21×21 size version, and only a constant masking code)—props to them, btw, bc this problem is not suited for TI-Basic. I hope that, with the lighter memory footprint that comes with using z80, I'll be able to generate larger-sized QR codes.

(For example, a number of GF(256) operations are aided by use of a table of discrete logarithms; each of 255 entries is an integer between 0 and 254 — an unsigned 8-bit integer. An appvar can store these as-is, taking 255 bytes + header, but a TI-Basic list interprets the entries as 9-byte reals, raising the memory footprint by an order of magnitude.)

I do expect to be reusing the front end I had made in Basic. It makes sense to have a front end in a language that is designed to be a front end, and a back end in a language designed to be a back end.

The reason I want to try to keep it as portable as I can is that if there's any use for this apart from the "coolness" factor, it's for transferring data from a monochrome calculator that has only a 2.5mm I/O port in situations where lugging the linking cable around is unfeasible. This project may make it possible to transfer data from your otherwise air-gapped calculator to the clipboard on your smartphone.

It's 3am right now, but expect an edit with code and links when I wake up

Edit: Code:

On and off over the past 5 years, I've made some progress on a TI-Basic version (and the work I did with it contributed directly to my final project in my VisualBasic CS class in highschool), but the code is scattered around multiple, poorly named files, and TI-Basic doesn't lend itself well to writing inline documentation, so most of it is unreadable to me. Additionally, given that the underlying Reed–Solomon encoding is based on GF(256), many of the relevant algorithms appear to be well suited for 8-bit arithmetic. (And now that it's five years later and I've graduated as a math & physics major and I've taken both abstract algebra and logic design, I actually know what that means, rather than just knowing that it's some fancy math I'll need to get around.)

I also saw in the file archives that SopaXorzTaker has already made a limited TI-Basic version (according to the readme, not dissimilar in scope to the VB version I made in highschool: only the 21×21 size version, and only a constant masking code)—props to them, btw, bc this problem is not suited for TI-Basic. I hope that, with the lighter memory footprint that comes with using z80, I'll be able to generate larger-sized QR codes.

(For example, a number of GF(256) operations are aided by use of a table of discrete logarithms; each of 255 entries is an integer between 0 and 254 — an unsigned 8-bit integer. An appvar can store these as-is, taking 255 bytes + header, but a TI-Basic list interprets the entries as 9-byte reals, raising the memory footprint by an order of magnitude.)

I do expect to be reusing the front end I had made in Basic. It makes sense to have a front end in a language that is designed to be a front end, and a back end in a language designed to be a back end.

The reason I want to try to keep it as portable as I can is that if there's any use for this apart from the "coolness" factor, it's for transferring data from a monochrome calculator that has only a 2.5mm I/O port in situations where lugging the linking cable around is unfeasible. This project may make it possible to transfer data from your otherwise air-gapped calculator to the clipboard on your smartphone.

It's 3am right now, but expect an edit with code and links when I wake up

Edit: Code:

**Code:**```
; Program Name: QRAVMAK
```

; Author(s): ReGuess

; Description: AppVar Wrapper. TODO: Find a better name

; QR code AppVar Make? (QRAVMAK ?)

.nolist

#include "ti83plus.inc"

.list

.org userMem-2

.db $BB,$6D

; Program code goes here

;get variable name

ld HL, expName ; AppVarName

CALL findOrCreateAppVar

push HL ; EXP.sym -> Stack[0] ;Notation: Each 'element' of Stack is 2 Bytes

push DE ; EXP.data -> Stack[1] ;

CALL c, fillExpTbl ; CALL c, ...

ld HL, logName

CALL findOrCreateAppVar

ex (SP), HL ; EXP.data -> HL; LOG.sym -> Stack[1]

push HL ; EXP.data -> Stack[2]

push DE ; LOG.data -> Stack[3]

CALL c, fillLogTbl

pop DE ; I expect I'll eventually use these, so not getting rid of this yet

;call printTable

pop HL

pop DE

pop HL

RET

; Significantly modified from the TI-83+ Dev Guide Sys Routine Docs:

; https://education.ti.com/~/media/01E6AF2CADF84171B6F2E2039357BAAC

; page 12-3 (or p. 351) of 83psysroutines.pdf

; (the example for ChkFindSym)

;

; Inputs:

; HL: Pointer to variable name

; Outputs:

; OP1: Variable name

; HL: pointer to sym entry

; DE: pointer to (size bytes of) data

; BC: pointer to variable name

; Flags:

; Carry set if (and only if) variable was created

; Z set if variable found; otherwise, unknown?

; Destroyed:

; A

; OP2

; possibly OP3 and OP4 (if the variable was found archived)

findOrCreateAppVar:

push HL ; ptr to var name

rst rMov9ToOP1 ; OP1 = variable name

BCALL(_ChkFindSym) ; look up

jr nc, VarExists ; jump if it exists

ld HL, $00fe ; $00ff ; size to create (hard-coded as 254 bytes)

BCALL(_CreateAppVar) ; create it

push HL ; pointer to sym entry

push DE ; pointer to data

BCALL(_OP4ToOP1) ; OP1 = name

pop DE ; pointer to data

pop HL ; pointer to sym entry

scf ; set carry flag

jr done

VarExists:

ld A,B ; check for archived

or A ; in RAM? (also, clears carry flag)

jr z, done ; yes

BCALL(_Arc_Unarc) ; unarchive if enough RAM

pop HL ; pointer to variable name

jr findOrCreateAppVar ; look up pointers again in RAM now

done:

pop BC ; pointer to variable name

ret

; Uses the Russian Peasant algorithm for GF(256) multiplication,

; significantly optimized for multiplication by 2

; Inputs:

; DE: pointer to (size bytes of) data

; Destroys:

; A, B, C, D, E

fillExpTbl:

; putting $ff into B for the loop counter

; putting $1d into C because XOR C is smaller & faster than XOR $1d

; actually $1c now that I've optimized the sla A into an rlca

; (in general, it's faster to read a register

; than it is to read the next byte from program memory)

ld BC, $ff1c ; ld b, $ff

ld A, 1

inc DE ; move DE past the size bytes

peasantLoop:

inc DE

ld (DE), A

rlca ; sla A ; A <<= 1; (A.7 -> carry)

; this could probably be optimized with RLCA and some fancy math

jr nc, skip_xor_1d

xor C ; XOR $1d ; generator (0x11d)'s LSB

skip_xor_1d:

djnz peasantLoop

ret

; Inputs:

; HL: Pointer to EXP.data (Already filled; to be Read) [to be SEARCHED]

; DE: Pointer to LOG.data (Empty; to be Written to)

fillLogTbl:

inc DE ; move DE past the size bytes

;inc DE ; LOG.data+2

inc HL ; move HL past the size bytes

;inc HL ; EXP.data+2

ld A, 1 ; for A in range(1, 256):

log_loop:

push HL ; EXP.data+2 -> Stack(+1) ; Notation: Stack(+n) means w.r.t. start of call

ld BC, $00FF

cpir ; BC == C == 255(?) - index(A) (BC==C because B==0)

;HL is now pointer to location of value A in the variable, not that we care

ld H,B ;ld H,0

ld L,A

add HL,DE

ld B,A ; for storage

ld A,C ; for manipulation

cpl ;neg

dec A ;sub 2 ; might not need this if I play around w/ the `ld BC` above

ld (HL),A

pop HL

ld A,B

inc A

jr nz, log_loop

ret

;Inputs: DE: ptr to data

printTable:

ex de,hl

inc hl ; move DE past the size bytes ; data+2

ld B, $ff ; for A in range(1, 256):

print_loop:

inc hl

ld a, (hl)

push bc

push hl

ld h,0

ld l,a

bcall(_DispHL)

pop bc

push bc

bit 0,c

call z, wait

bcall(_NewLine) \ bcall(_NewLine)

pop hl

pop bc

djnz print_loop

ret

; Copied from https://www.plutiedev.com/z80-add-8bit-to-16bit#add-unsigned

; Inputs: A, HL: Addends

; Outputs: HL: sum

; Destroyed: A

addAtoHL:

add a, l ; A = A+L

ld l, a ; L = A+L

adc a, h ; A = A+L+H+carry

sub l ; A = H+carry

ld h, a ; H = H+carry

ret

; Props to Zeroko and Iambian, via Cemetech IRC

addMod255:

add a,b \ adc a,1 \ ret z \ dec a \ ret ; Iambian

;add a,b \ adc a,1 \ jp z,$+1 \ dec a ; Zeroko: in-line

;add A,B \ adc a,0 \ cp 255 \ ret nz \ xor a \ ret ; ReGuess

; there was another version that used inc a, but it left the chat before I had the chance to copy it

; A <- (A-B)mod 255

subMod255:

sub B

sbc A,0 ; ret nc \ dec A ; add a, 255

ret

; Inputs:

; B, C: multiplicands

; HL: Pointer to EXP.data

; DE: Pointer to LOG.data

; Outputs: A: Product

; Destroys: HL, DE

gf_mul:

xor A

cp B

ret z

cp C

ret z

; TODO

;push bc

inc hl \ inc hl ; todo: do this as soon as these pointers are found, instead of repeating it each time

inc de \ inc de

;push de

push hl

ld a, b

call addAtoHL

ld b,(hl)

pop hl

;push hl

ld a,c

call addAtoHL

ld a,(hl)

add a,b

jr nc, lbl_206

dec a ; modulo 255

lbl_206:

ex de,hl

call addAtoHL

ld a,(hl)

;pop hl

;pop de

;pop bc

ret

; Waits about 1 second. For debugging purposes.

; WARNING: DOES NOT CHECK IF MACHINE IS 84+ SERIES. DO NOT USE ON 83+

wait:

ld c, $45

in a,(45h)

waitloop:

in b,(c)

cp b

jr z, waitloop

ret

;data section

expName:

.db AppVarObj, "QREXPTBL"

logName:

.db AppVarObj, "QRLOGTBL" ; 8 bytes: no need for null termination?

string1: .db "Not yet implemented",0

; misc. code, not needed:

; comment out if we want (EXP.data - LOG.data)

;ex DE,HL ;if we want (LOG.data - EXP.data)

;xor A ; need to reset carry; might as well reset A, too.

;sbc HL,DE ; HL is now the difference (LOG.data - EXP.data)

;ex DE,HL ; now DE is the difference, and HL is now EXP.data+2

;inc A