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.

» Go to Registration page
Page 1 of 1
» 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

 

Advertisement