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:

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```
I kinda started over again about two weeks ago. As stated on IRC:
ReGuess1997 at 2020 July 27, 1:15 AM wrote:
So for about the past two weeks, I've been working on a QR code generator in z80 asm. And I've been following the guide on wikiversity more closely than the one on thonky, since it's easier to read, and it has working python code that I can use to make sure I get the right results…
…And I've finally realized, the lookup tables for the exponents and logarithms… aren't actually unnecessary for an encoder, since the GF(256) operations that cannot be done without them, are only found in the decoder
Thank God for the Russian peasant algorithm

Also notable was Zeroko's suggestion to implement structured append, given the rectangularity of the LCD, which is now on my to-do list.

Anyway, here's my progress on GitHub.

Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

»
» All times are GMT - 5 Hours

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum