mirror of
https://github.com/thrust26/6502-QR-code-generator.git
synced 2024-12-27 19:29:35 +00:00
1021 lines
24 KiB
PHP
1021 lines
24 KiB
PHP
;===============================================================================
|
|
; Q R C O D E C O N S T A N T S
|
|
;===============================================================================
|
|
|
|
; Do NOT change these constants!
|
|
|
|
IFNCONST QR_OVERLAP
|
|
QR_OVERLAP = 0
|
|
ENDIF
|
|
IFNCONST QR_SINGLE_MASK
|
|
QR_SINGLE_MASK = 0
|
|
ENDIF
|
|
IFNCONST QR_DIRECT_DRAW
|
|
QR_DIRECT_DRAW = 0
|
|
ENDIF
|
|
|
|
IF QR_VERSION = 1 ;{
|
|
; 21x21 QR code
|
|
IF QR_LEVEL = 0
|
|
QR_DEGREE = 7 ; for version 1, level L QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 1
|
|
QR_DEGREE = 10 ; for version 1, level M QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 2
|
|
QR_DEGREE = 13 ; for version 1, level Q QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 3
|
|
QR_DEGREE = 17 ; for version 1, level H QR codes
|
|
ENDIF
|
|
ENDIF ;}
|
|
|
|
IF QR_VERSION = 2
|
|
; 25x25 QR code
|
|
IF QR_LEVEL = 0
|
|
QR_DEGREE = 10 ; for version 2, level L QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 1
|
|
QR_DEGREE = 16 ; for version 2, level M QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 2
|
|
QR_DEGREE = 22 ; for version 2, level Q QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 3
|
|
QR_DEGREE = 28 ; for version 2, level H QR codes
|
|
ENDIF
|
|
ENDIF
|
|
|
|
IF QR_VERSION = 3 ;{
|
|
; 29x29 QR code
|
|
IF QR_LEVEL = 0
|
|
QR_DEGREE = 15 ; for version 3, level L QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 1
|
|
QR_DEGREE = 26 ; for version 3, level M QR codes
|
|
ENDIF
|
|
IF QR_LEVEL = 2 || QR_LEVEL = 3
|
|
ECHO ""
|
|
ECHO "ERROR: Version 3, Level", [QR_LEVEL]d, "not supported"
|
|
ERR
|
|
ENDIF
|
|
ENDIF ;}
|
|
|
|
QR_SIZE = 17 + QR_VERSION * 4
|
|
|
|
; Calculate capacity based on version
|
|
_QR_VAL SET (QR_VERSION * 16 + 128) * QR_VERSION + 64
|
|
IF QR_VERSION >= 2
|
|
_QR_NUM_ALIGN = QR_VERSION / 7 + 2
|
|
_QR_VAL SET _QR_VAL - ((25 * _QR_NUM_ALIGN - 10) * _QR_NUM_ALIGN - 55)
|
|
IF QR_VERSION >= 7
|
|
_QR_VAL SET _QR_VAL - 36
|
|
ENDIF
|
|
ENDIF
|
|
QR_CAPACITY = _QR_VAL / 8
|
|
|
|
QR_MAX_DATA = QR_CAPACITY - QR_DEGREE
|
|
|
|
QR_MODE = %0100 ; byte mode
|
|
QR_POLY = $11d ; GF(2^8) is based on 9 bit polynomial
|
|
; x^8 + x^4 + x^3 + x^2 + 1 = 0x11d
|
|
QR_FORMATS = 15 ; 15 type information bits
|
|
|
|
QR_MAX_MSG = QR_MAX_DATA - 2
|
|
QR_TOTAL = QR_MAX_DATA + QR_DEGREE ; 44
|
|
|
|
|
|
;===============================================================================
|
|
; Q R C O D E M A C R O S
|
|
;===============================================================================
|
|
|
|
; The following code has been partially converted from the C code of the
|
|
; QR code generator found at https://github.com/nayuki/QR-Code-generator
|
|
|
|
;-----------------------------------------------------------
|
|
; Returns the product of the two given field elements modulo GF(2^8/0x11D).
|
|
; All inputs are valid.
|
|
MAC _RS_MULT
|
|
;-----------------------------------------------------------
|
|
; Russian peasant multiplication (x * y)
|
|
; Input: A = x, Y = y
|
|
; Result: A
|
|
.x = tmpVars
|
|
.y = tmpVars+1
|
|
|
|
sta .x
|
|
sty .y
|
|
; uint8_t z = 0;
|
|
lda #0
|
|
; for (int i = 7; i >= 0; i--) {
|
|
ldy #7
|
|
.loopI
|
|
; z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D));
|
|
asl
|
|
bcc .skipEorPoly
|
|
eor #<QR_POLY
|
|
.skipEorPoly
|
|
; z ^= ((y >> i) & 1) * x;
|
|
asl .y
|
|
bcc .skipEorX
|
|
eor .x
|
|
.skipEorX
|
|
; }
|
|
dey
|
|
bpl .loopI
|
|
ENDM
|
|
|
|
IF QR_GENERATE
|
|
; Computes a Reed-Solomon ECC generator polynomial for degree 16, storing in result[0 : 16-1].
|
|
; This is implemented as a lookup table too (Generator)
|
|
;-----------------------------------------------------------
|
|
MAC _RS_DIVISOR
|
|
;-----------------------------------------------------------
|
|
; E.g. g(x)=(x+1)(x+?)(x+?^2)(x+?^3)...(x+?^15)
|
|
; = x^16+3Bx^15+0Dx^14+68x^13+BDx^12+44x^11+d1x^10+1e^x9+08x^8
|
|
; +A3x^7+41x^6+29x^5+E5x^4+62x^3+32x^2+24x+3B
|
|
.root = tmpVars+2
|
|
.i = tmpVars+3
|
|
|
|
; memset(qrGenerator, 1, 16);
|
|
ldx #QR_DEGREE-1
|
|
stx .i
|
|
ldy #0
|
|
.loopClear
|
|
sty qrGenerator,x
|
|
dex
|
|
bne .loopClear
|
|
; qrGenerator[0] = 1; // Start off with the monomial x^0
|
|
iny
|
|
sty qrGenerator
|
|
; uint8_t root = 1;
|
|
sty .root
|
|
; for (int i = 0; i < degree; i++) {
|
|
.loopI
|
|
; // Multiply the current product by (x - r^i)
|
|
; for (int j = degree-1; j >= 0; j--) {
|
|
ldx #QR_DEGREE-1
|
|
.loopJ
|
|
; qrGenerator[j] = reedSolomonMultiply(qrGenerator[j], root);
|
|
lda qrGenerator,x
|
|
ldy .root
|
|
jsr RSMult
|
|
; if (j)
|
|
dex
|
|
bmi .skipEor
|
|
; qrGenerator[j] ^= qrGenerator[j - 1];
|
|
eor qrGenerator,x
|
|
.skipEor
|
|
sta qrGenerator+1,x
|
|
txa
|
|
bpl .loopJ
|
|
; root = reedSolomonMultiply(root, 0x02);
|
|
lda .root
|
|
ldy #$02
|
|
jsr RSMult
|
|
sta .root
|
|
dec .i
|
|
bpl .loopI
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
RSMult SUBROUTINE
|
|
_RS_MULT
|
|
rts
|
|
;-----------------------------------------------------------
|
|
ENDIF ; /QR_GENERATE
|
|
|
|
;-----------------------------------------------------------
|
|
MAC _RS_REMAINDER
|
|
;-----------------------------------------------------------
|
|
.i = tmpVars+2
|
|
.factor = tmpVars+3
|
|
|
|
; memset(result, 0, 16); // done in START_TEXT
|
|
; for (int i = dataLen-1; i >= 0; i--) { // Polynomial division
|
|
ldx #QR_MAX_DATA-1
|
|
.loopI
|
|
stx .i
|
|
; uint8_t factor = data[i] ^ result[degree - 1];
|
|
lda msgData,x
|
|
eor remainder + QR_DEGREE - 1
|
|
sta .factor
|
|
; memmove(&result[1], &result[0], (size_t)(16 - 1) * sizeof(result[0]));
|
|
ldx #QR_DEGREE-1
|
|
.loopMove
|
|
lda remainder-1,x
|
|
sta remainder,x
|
|
dex
|
|
bne .loopMove
|
|
; result[0] = 0;
|
|
lda #0
|
|
sta remainder
|
|
; for (int j = 16-1; j >= 0; j--)
|
|
ldx #QR_DEGREE-1
|
|
.loopJ
|
|
; result[j] ^= reedSolomonMultiply(generator[j], factor);
|
|
ldy .factor
|
|
IF QR_GENERATE
|
|
lda qrGenerator,x
|
|
jsr RSMult
|
|
ELSE
|
|
lda Generator,x
|
|
_RS_MULT
|
|
ENDIF
|
|
eor remainder,x
|
|
sta remainder,x
|
|
; }
|
|
dex
|
|
bpl .loopJ
|
|
; }
|
|
ldx .i
|
|
dex
|
|
bpl .loopI
|
|
ENDM
|
|
|
|
IF QR_DIRECT_DRAW = 0
|
|
;-----------------------------------------------------------
|
|
MAC _BLACK_FUNC
|
|
;-----------------------------------------------------------
|
|
; Blacks all function/alignment and timing pattern areas
|
|
|
|
; assume bitmap is clear, only draw the pattern
|
|
ldy #4+2 ; pattern + timing lines
|
|
.loopPattern
|
|
lda #$ff ; fill pattern
|
|
jsr DrawPattern
|
|
bpl .loopPattern
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
BlackFunc SUBROUTINE
|
|
;-----------------------------------------------------------
|
|
_BLACK_FUNC
|
|
rts
|
|
|
|
;-----------------------------------------------------------
|
|
MAC _DRAW_FUNC
|
|
;-----------------------------------------------------------
|
|
; Draws all function/alignment and timing pattern over existing codewords
|
|
|
|
; blacken functions pattern first
|
|
jsr BlackFunc
|
|
|
|
; draw function pattern
|
|
ldy #4
|
|
.loopPattern
|
|
lda #$00 ; draw pattern
|
|
jsr DrawPattern
|
|
bne .loopPattern
|
|
|
|
; draw horizontal timing pattern
|
|
ldy #QR_SIZE-1 - 9
|
|
ldx #QR_SIZE-1 - 6
|
|
.loopTimingH
|
|
jsr InvertPixel
|
|
dey
|
|
dey
|
|
cpy #9
|
|
bcs .loopTimingH
|
|
|
|
; draw vertical timing pattern
|
|
ldy #6
|
|
ldx #QR_SIZE-1 - 9
|
|
.loopTimingV
|
|
jsr InvertPixel
|
|
dex
|
|
dex
|
|
cpx #9
|
|
bcs .loopTimingV
|
|
ENDM ;
|
|
|
|
;---------------------------------------------------------------
|
|
DrawPattern SUBROUTINE
|
|
;---------------------------------------------------------------
|
|
; Y = index, A = fill
|
|
.index = tmpVars
|
|
.width = tmpVars+1
|
|
.height = tmpVars+2
|
|
.offset = tmpVars+3
|
|
.tmpPat = tmpVars+4
|
|
.fill = tmpVars+5
|
|
|
|
sty .index
|
|
sta .fill
|
|
ldx PatternY,y
|
|
lda PatternHeight,y
|
|
sta .height
|
|
lda PatternOffset,y
|
|
tay
|
|
.loopRow
|
|
sty .offset
|
|
lda PatternStart,y
|
|
ora .fill
|
|
sta .tmpPat
|
|
ldy .index
|
|
lda PatternWidth,y
|
|
sta .width
|
|
lda PatternX,y
|
|
tay
|
|
; draw one row
|
|
.loopBit
|
|
sec ; allows for >8 black pixel
|
|
ror .tmpPat
|
|
bcc .clear
|
|
;.set
|
|
jsr CheckPixel
|
|
bne .skipInvert
|
|
.invert
|
|
jsr InvertPixel
|
|
.skipInvert
|
|
iny
|
|
dec .width
|
|
bne .loopBit
|
|
dex
|
|
ldy .offset
|
|
iny
|
|
dec .height
|
|
bne .loopRow
|
|
.exit
|
|
ldy .index
|
|
dey
|
|
rts
|
|
|
|
.clear
|
|
jsr CheckPixel
|
|
bne .invert
|
|
beq .skipInvert
|
|
ENDIF ; /QR_DIRECT_DRAW = 0
|
|
|
|
;-----------------------------------------------------------
|
|
; Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of
|
|
; the QR Code to be black at function modules and white at codeword modules (including unused remainder bits).
|
|
MAC _DRAW_CODEWORDS
|
|
;-----------------------------------------------------------
|
|
; Note: This part has the maximum RAM usage
|
|
.vert = tmpVars+0
|
|
.j = tmpVars+1
|
|
.y = tmpVars+2
|
|
IF QR_DIRECT_DRAW
|
|
.iBit = tmpVars+3
|
|
.iByte = tmpVars+4
|
|
.right1 = tmpVars+5
|
|
ELSE
|
|
; must not overlap with DrawPattern space
|
|
.iBit = tmpVars+6
|
|
.iByte = tmpVars+7
|
|
.right1 = tmpVars+8
|
|
ENDIF
|
|
|
|
; blacken the (right) function modules in the bitmap
|
|
IF QR_OVERLAP
|
|
_BLACK_RIGHT ; returns with X = 0
|
|
ELSE
|
|
IF QR_DIRECT_DRAW
|
|
_BLACK_FUNC ; returns with X = 0
|
|
ELSE
|
|
jsr BlackFunc
|
|
ldx #0
|
|
ENDIF
|
|
ENDIF
|
|
; int i = 0; // Bit index into the data
|
|
; ldx #0
|
|
; 2600 code has data in reversed order
|
|
stx .iBit
|
|
lda #QR_TOTAL-1
|
|
sta .iByte
|
|
; // Do the funny zigzag scan
|
|
; Note: 2600 code has .right1 increased by 1
|
|
; for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair
|
|
ldy #QR_SIZE-1+1
|
|
.loopRight
|
|
; if (right == 6)
|
|
cpy #6+1
|
|
bne .not6
|
|
; right = 5;
|
|
dey ; skip the timing column
|
|
.not6
|
|
sty .right1
|
|
IF QR_OVERLAP
|
|
; overwrite shared data
|
|
cpy #16+1
|
|
bne .skipBlackMiddle
|
|
; blacken the middle function modules in the bitmap
|
|
_BLACK_MIDDLE
|
|
IF QR_DIRECT_DRAW = 0
|
|
ldy .right1 ; DrawPattern overwrites Y
|
|
ENDIF
|
|
.skipBlackMiddle
|
|
cpy #8+1
|
|
bne .skipBlackLeft
|
|
; blacken the left function modules in the bitmap
|
|
_BLACK_LEFT
|
|
.skipBlackLeft
|
|
ENDIF
|
|
; for (int vert = 0; vert < qrsize; vert++) { // Vertical counter
|
|
ldy #QR_SIZE-1
|
|
.loopVert
|
|
sty .vert
|
|
; bool upward = ((right + 1) & 2) != 0; // 2600 code works in reverse
|
|
lda .right1
|
|
and #$02
|
|
bne .notUp
|
|
; int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate
|
|
lda #QR_SIZE-1;+1
|
|
sec
|
|
sbc .vert
|
|
tay
|
|
.notUp
|
|
sty .y
|
|
; for (int j = 0; j < 2; j++) {
|
|
; some tricky code with .j here
|
|
ldy .right1
|
|
BIT_B
|
|
.loopJ
|
|
dey
|
|
sty .j
|
|
; int x = right - j; // Actual x coordinate
|
|
dey
|
|
; if (!getModule(qrcode, x, y) && i < dataLen * 8) {
|
|
; ldy .x
|
|
ldx .y
|
|
jsr CheckPixel
|
|
bne .skipPixel
|
|
; bool black = getBit(data[i >> 3], 7 - (i & 7));
|
|
ldx .iByte
|
|
lda data,x
|
|
ldx .iBit
|
|
and BitMask,x
|
|
beq .skipInv
|
|
; setModule(qrcode, x, y, black);
|
|
; ldy .x
|
|
ldx .y
|
|
jsr InvertPixel
|
|
ldx .iBit
|
|
.skipInv
|
|
; i++;
|
|
inx
|
|
txa
|
|
and #$07
|
|
sta .iBit
|
|
bne .skipByte
|
|
dec .iByte
|
|
bmi .exitDraw ; 2600 code exits here!
|
|
.skipByte
|
|
; }
|
|
.skipPixel
|
|
ldy .j
|
|
cpy .right1
|
|
beq .loopJ
|
|
; } // for j
|
|
ldy .vert
|
|
dey
|
|
bpl .loopVert
|
|
; } // for vert
|
|
ldy .right1
|
|
dey
|
|
dey
|
|
; bpl .loopRight ; unconditional!
|
|
jmp .loopRight ; unconditional!
|
|
; } // for right
|
|
.exitDraw
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
MAC _APPLY_MASK
|
|
;-----------------------------------------------------------
|
|
IF QR_SINGLE_MASK
|
|
.y = tmpVars
|
|
ldx #QR_SIZE - 1
|
|
.loopY
|
|
stx .y
|
|
ldy #QR_SIZE - 1
|
|
.loopX
|
|
; 0: (x + y) % 2 == 0
|
|
tya
|
|
eor .y
|
|
lsr
|
|
bcs .skipInvert
|
|
jsr InvertPixel
|
|
.skipInvert
|
|
dey
|
|
bpl .loopX
|
|
dex
|
|
bpl .loopY
|
|
ELSE
|
|
.y = tmpVars
|
|
;.x = tmpVars+2
|
|
.xMod3 = tmpVars+1
|
|
.yMod3 = tmpVars+2
|
|
.xDiv3 = tmpVars+3
|
|
.tmp = tmpVars+4
|
|
|
|
lda #0
|
|
sta .yMod3
|
|
ldx #QR_SIZE - 1
|
|
.loopY
|
|
stx .y
|
|
lda #0
|
|
sta .xMod3
|
|
sta .xDiv3
|
|
ldy #QR_SIZE - 1
|
|
.loopX
|
|
; sty .x
|
|
lda qrPattern
|
|
bne .not0
|
|
; 0: (x + y) % 2 == 0
|
|
tya
|
|
eor .y
|
|
bpl .checkMod2
|
|
|
|
.not0
|
|
cmp #4
|
|
bne .not4
|
|
; 4: (x / 3 + y / 2) % 2 == 0
|
|
lda .y
|
|
lsr
|
|
adc .xDiv3
|
|
bpl .checkMod2
|
|
|
|
.not4
|
|
bcs .above4
|
|
|
|
lsr
|
|
bcs .not2
|
|
; 2: x % 3 == 0
|
|
lda .xMod3
|
|
beq .invert
|
|
bne .skipInvert
|
|
|
|
.not2
|
|
bne .is3
|
|
; 1: y % 2 == 0
|
|
txa
|
|
bpl .checkMod2
|
|
|
|
.is3
|
|
; 3: (x + y) % 3 == 0
|
|
lda .xMod3
|
|
; sec
|
|
sbc .yMod3
|
|
beq .invert
|
|
bne .skipInvert
|
|
|
|
.above4
|
|
cmp #6
|
|
beq .is6
|
|
bcs .is7
|
|
.is6
|
|
php
|
|
; 5: x * y % 2 + x * y % 3 == 0
|
|
; 6: (x * y % 2 + x * y % 3) % 2 == 0
|
|
lda .xMod3
|
|
beq .modEven56
|
|
lda .yMod3
|
|
beq .modEven56
|
|
clc
|
|
adc .xMod3
|
|
BIT_W
|
|
.modEven56
|
|
lda #0
|
|
sta .tmp
|
|
tya
|
|
lsr
|
|
bcc .even56
|
|
txa
|
|
lsr
|
|
bcc .even56
|
|
inc .tmp
|
|
.even56
|
|
plp
|
|
lda .tmp
|
|
bcs .checkMod2
|
|
beq .invert
|
|
bne .skipInvert
|
|
|
|
.is7
|
|
; 7: ((x + y) % 2 + x * y % 3) % 2 == 0
|
|
tya
|
|
eor .y
|
|
sta .tmp
|
|
lda .xMod3
|
|
beq .modEven7
|
|
lda .yMod3
|
|
beq .modEven7
|
|
clc
|
|
adc .xMod3
|
|
adc .tmp
|
|
sta .tmp
|
|
.modEven7
|
|
lda .tmp
|
|
.checkMod2
|
|
lsr
|
|
bcs .skipInvert
|
|
.invert
|
|
jsr InvertPixel
|
|
.skipInvert
|
|
; next X
|
|
dec .xMod3
|
|
bpl .xMod3OK
|
|
lda #2
|
|
sta .xMod3
|
|
inc .xDiv3
|
|
.xMod3OK
|
|
dey
|
|
bpl .loopX
|
|
; next Y
|
|
dec .yMod3
|
|
bpl .yMod3OK
|
|
lda #2
|
|
sta .yMod3
|
|
.yMod3OK
|
|
dex
|
|
bmi .exitLoopY
|
|
jmp .loopY
|
|
|
|
.exitLoopY
|
|
ENDIF ; !QR_SINGLE_MASK
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
MAC _DRAW_FORMAT
|
|
;-----------------------------------------------------------
|
|
IF QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
|
|
.idx = tmpVars
|
|
|
|
ldy #QR_FORMATS-1
|
|
.loopFormat
|
|
sty .idx
|
|
cpy #8
|
|
ldx qrPattern
|
|
lda FormatLo,x
|
|
and BitMask,y
|
|
bcc .lowFormat
|
|
lda FormatHi,x
|
|
and BitMask-8,y
|
|
.lowFormat
|
|
IF QR_DIRECT_DRAW
|
|
beq .skipFormat
|
|
ELSE
|
|
bne .skipFormat
|
|
ENDIF
|
|
ldx FormatY1,y
|
|
lda FormatX1,y
|
|
tay
|
|
jsr InvertPixel
|
|
ldy .idx
|
|
ldx FormatY2,y
|
|
lda FormatX2,y
|
|
tay
|
|
jsr InvertPixel
|
|
ldy .idx
|
|
.skipFormat
|
|
dey
|
|
bpl .loopFormat
|
|
ENDIF
|
|
ENDM
|
|
|
|
; ********** The user macros start here: **********
|
|
|
|
;-----------------------------------------------------------
|
|
MAC START_MSG
|
|
;-----------------------------------------------------------
|
|
; A = message length
|
|
; add mode and length to message data
|
|
tax
|
|
lsr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
ora #(QR_MODE << 4)
|
|
; (QR_MODE << 4) | (MSG_LEN >> 4)
|
|
sta msgData + QR_MAX_DATA - 1
|
|
txa
|
|
asl
|
|
asl
|
|
asl
|
|
asl
|
|
; (MSG_LEN << 4)
|
|
sta msgData + QR_MAX_DATA - 2
|
|
lda #QR_MAX_DATA - 3
|
|
sta msgIdx
|
|
; clear the remaining data buffer
|
|
ldx #QR_TOTAL-3
|
|
lda #0
|
|
.loopClear
|
|
sta data,x
|
|
dex
|
|
bpl .loopClear
|
|
ENDM
|
|
|
|
;---------------------------------------------------------------
|
|
MAC ADD_MSG_BYTE
|
|
;---------------------------------------------------------------
|
|
; A = byte to add
|
|
ldx msgIdx
|
|
pha
|
|
lsr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
ora msgData + 1,x
|
|
sta msgData + 1,x
|
|
pla
|
|
asl
|
|
asl
|
|
asl
|
|
asl
|
|
sta msgData,x
|
|
dec msgIdx
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
MAC STOP_MSG
|
|
;-----------------------------------------------------------
|
|
IF QR_PADDING
|
|
.msgLen = tmpVars
|
|
; pad with optional filler bytes (QR code works without too)
|
|
lda #QR_MAX_MSG - 1
|
|
sec
|
|
sbc .msgLen
|
|
bcc .noPadding
|
|
tax
|
|
.loopPadding
|
|
lda #$ec ; defined by QR standard
|
|
sta msgData,x
|
|
dex
|
|
bmi .noPadding
|
|
lda #$11 ; defined by QR standard
|
|
sta msgData,x
|
|
dex
|
|
bpl .loopPadding
|
|
.noPadding
|
|
ENDIF
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
MAC GEN_QR_CODE
|
|
;-----------------------------------------------------------
|
|
; This is the main macro to use!
|
|
QRCodeCode
|
|
|
|
IF QR_GENERATE
|
|
; generate the Reed-Solomon ECC generator polynomial
|
|
RSDevisor
|
|
_RS_DIVISOR
|
|
ENDIF
|
|
; calculate the ECC
|
|
RSRemainder
|
|
_RS_REMAINDER
|
|
; draw the code words onto the bitmap
|
|
DrawCodes
|
|
_DRAW_CODEWORDS
|
|
; apply the pattern mask
|
|
ApplyMask
|
|
_APPLY_MASK
|
|
; blacken the function modules in the bitmap again
|
|
; and draw the function modules in the bitmap
|
|
DrawFunc
|
|
_DRAW_FUNC
|
|
; draw the format bits
|
|
DrawFormat
|
|
_DRAW_FORMAT
|
|
|
|
ECHO "QR Code encoding code:", [. - QRCodeCode]d, "bytes"
|
|
_QR_TOTAL SET _QR_TOTAL + . - QRCodeCode
|
|
ENDM
|
|
|
|
;-----------------------------------------------------------
|
|
MAC QR_CODE_DATA
|
|
;-----------------------------------------------------------
|
|
; Add this to your code's data area
|
|
QRCodeData
|
|
|
|
IF QR_DIRECT_DRAW = 0
|
|
; Function pattern parameters
|
|
PatternX ; left
|
|
.byte 6 ; left, vertical timing line
|
|
.byte 0, 0, QR_SIZE-8 ; eye pattern
|
|
.byte QR_SIZE-8, QR_SIZE-9 ; alignment pattern (right/middle part)
|
|
.byte 9 ; top, horizontal timing line
|
|
PatternY ; top
|
|
.byte QR_SIZE - 10 ; left, vertical timing line
|
|
.byte QR_SIZE-1, 7, QR_SIZE-1 ; eye pattern
|
|
.byte 8, 8 ; alignment pattern (right/middle part)
|
|
.byte QR_SIZE - 7 ; top, horizontal timing line
|
|
PatternOffset = . - 1
|
|
.byte EyeLeftTop-PatternStart ; 1
|
|
.byte EyeLeftBottom-PatternStart ; 2
|
|
.byte EyeRightTop-PatternStart ; 3
|
|
.byte AlignRightBottom-PatternStart
|
|
.byte AlignRightBottom-PatternStart
|
|
; timing lines need no pattern, always drawn filled here
|
|
PatternWidth
|
|
.byte 1 ; left, vertical timing line
|
|
.byte 9, 9, 8 ; eye pattern
|
|
.byte 4, 1 ; alignment pattern (right/middle part)
|
|
.byte QR_SIZE - 9 - 8 ; top, horizontal timing line
|
|
PatternHeight
|
|
.byte QR_SIZE - 9 - 8 ; left, vertical timing line
|
|
.byte 9, 8, 9 ; eye pattern
|
|
.byte 5, 5 ; alignment pattern (right/middle part)
|
|
.byte 1 ; top, horizontal timing line
|
|
|
|
PatternStart
|
|
EyeRightTop
|
|
.byte %11111110
|
|
.byte %10000010
|
|
.byte %10111010
|
|
.byte %10111010
|
|
.byte %10111010
|
|
.byte %10000010
|
|
.byte %11111110
|
|
.byte %00000000
|
|
.byte %11111111
|
|
AlignRightBottom = . - 1
|
|
; .byte %1111
|
|
.byte %1000
|
|
.byte %1010
|
|
.byte %1000
|
|
.byte %1111
|
|
|
|
EyeLeftBottom
|
|
.byte %00000000
|
|
; .byte %01111111
|
|
; .byte %01000001
|
|
; .byte %01011101
|
|
; .byte %01011101
|
|
; .byte %01011101
|
|
; .byte %01000001
|
|
; .byte %01111111
|
|
EyeLeftTop
|
|
.byte %01111111
|
|
.byte %01000001
|
|
.byte %01011101
|
|
.byte %01011101
|
|
.byte %01011101
|
|
.byte %01000001
|
|
.byte %01111111
|
|
.byte %00000000
|
|
.byte %11111111
|
|
ENDIF ; /QR_DIRECT_DRAW
|
|
|
|
; Format Information Strings
|
|
IF QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
|
|
IF QR_LEVEL = 0 ; L
|
|
FormatLo
|
|
.byte %11101111
|
|
.byte %11100101
|
|
.byte %11111011
|
|
.byte %11110001
|
|
.byte %11001100
|
|
.byte %11000110
|
|
.byte %11011000
|
|
.byte %11010010
|
|
FormatHi
|
|
.byte %10001000
|
|
.byte %11100110
|
|
.byte %01010100
|
|
.byte %00111010
|
|
.byte %01011110
|
|
.byte %00110000
|
|
.byte %10000010
|
|
.byte %11101100
|
|
ENDIF
|
|
IF QR_LEVEL = 1 ; M
|
|
FormatLo
|
|
.byte %10101000
|
|
.byte %10100010
|
|
.byte %10111100
|
|
.byte %10110110
|
|
.byte %10001011
|
|
.byte %10000001
|
|
.byte %10011111
|
|
.byte %10010101
|
|
FormatHi
|
|
.byte %00100100
|
|
.byte %01001010
|
|
.byte %11111000
|
|
.byte %10010110
|
|
.byte %11110010
|
|
.byte %10011100
|
|
.byte %00101110
|
|
.byte %01000000
|
|
ENDIF
|
|
IF QR_LEVEL = 2 ; Q
|
|
FormatLo
|
|
.byte %01101010
|
|
.byte %01100000
|
|
.byte %01111110
|
|
.byte %01110100
|
|
.byte %01001001
|
|
.byte %01000011
|
|
.byte %01011101
|
|
.byte %01010111
|
|
FormatHi
|
|
.byte %10111110
|
|
.byte %11010000
|
|
.byte %01100010
|
|
.byte %00001100
|
|
.byte %01101000
|
|
.byte %00000110
|
|
.byte %10110100
|
|
.byte %11011010
|
|
ENDIF
|
|
IF QR_LEVEL = 3 ; H
|
|
FormatLo
|
|
.byte %00101101
|
|
.byte %00100111
|
|
.byte %00111001
|
|
.byte %00110011
|
|
.byte %00001110
|
|
.byte %00000100
|
|
.byte %00011010
|
|
.byte %00010000
|
|
FormatHi
|
|
.byte %00010010
|
|
.byte %01111100
|
|
.byte %11001110
|
|
.byte %10100000
|
|
.byte %11000100
|
|
.byte %10101010
|
|
.byte %00011000
|
|
.byte %01110110
|
|
ENDIF
|
|
|
|
; positions of the 15 type information bits
|
|
FormatX1
|
|
.byte 0, 1, 2, 3, 4, 5, 7, 8
|
|
.byte 8, 8, 8, 8, 8, 8, 8
|
|
FormatY2
|
|
.byte 0, 1, 2, 3, 4, 5, 6
|
|
; ds 8, QR_SIZE-9 ; shared
|
|
FormatY1
|
|
ds 8, QR_SIZE-9
|
|
.byte QR_SIZE-8, QR_SIZE-6, QR_SIZE-5, QR_SIZE-4
|
|
.byte QR_SIZE-3, QR_SIZE-2, QR_SIZE-1
|
|
FormatX2
|
|
ds 7, 8
|
|
.byte QR_SIZE-8, QR_SIZE-7, QR_SIZE-6, QR_SIZE-5
|
|
.byte QR_SIZE-4, QR_SIZE-3, QR_SIZE-2, QR_SIZE-1
|
|
ENDIF ; /QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
|
|
|
|
BitMask
|
|
.byte $80, $40, $20, $10, $8, $4, $2, $1
|
|
|
|
IF QR_GENERATE = 0
|
|
Generator ; data in reversed order!
|
|
IF QR_DEGREE = 7
|
|
.byte $75, $44, $0b, $a4, $9a, $7a, $7f
|
|
ENDIF
|
|
IF QR_DEGREE = 10
|
|
.byte $c1, $9d, $71, $5f, $5e, $c7, $6f, $9f
|
|
.byte $c2, $d8
|
|
ENDIF
|
|
IF QR_DEGREE = 13
|
|
.byte $87, $84, $53, $2b, $2e, $0d, $34, $11
|
|
.byte $b1, $11, $e3, $49, $89
|
|
ENDIF
|
|
IF QR_DEGREE = 15
|
|
.byte $1a, $86, $20, $97, $84, $8b, $69, $69
|
|
.byte $0a, $4a, $70, $a3, $6f, $c4, $1d
|
|
ENDIF
|
|
IF QR_DEGREE = 16
|
|
; Reed-Solomon ECC generator polynomial for degree 16
|
|
; g(x)=(x+1)(x+?)(x+?^2)(x+?^3)...(x+?^15)
|
|
; = x^16+3bx^15+0dx^14+68x^13+bdx^12+44x^11+d1x^10+1e^x9+08x^8
|
|
; +a3x^7+41x^6+29x^5+e5x^4+62x^3+32x^2+24x+3b
|
|
.byte $3b, $24, $32, $62, $e5, $29, $41, $a3
|
|
.byte $08, $1e, $d1, $44, $bd, $68, $0d, $3b
|
|
ENDIF
|
|
IF QR_DEGREE = 17
|
|
.byte $4f, $63, $7d, $35, $55, $86, $8f, $29
|
|
.byte $f9, $53, $c5, $16, $77, $78, $53, $42
|
|
.byte $77
|
|
ENDIF
|
|
IF QR_DEGREE = 22
|
|
.byte $f5, $91, $1a, $e6, $da, $56, $fd, $43
|
|
.byte $7b, $1d, $89, $1c, $28, $45, $bd, $13
|
|
.byte $f4, $b6, $b0, $83, $b3, $59
|
|
ENDIF
|
|
IF QR_DEGREE = 26
|
|
.byte $5e, $2b, $4d, $92, $90, $46, $44, $87
|
|
.byte $2a, $e9, $75, $d1, $28, $91, $18, $ce
|
|
.byte $38, $4d, $98, $c7, $62, $88, $04, $b7
|
|
.byte $44, $f6
|
|
ENDIF
|
|
IF QR_DEGREE = 28
|
|
.byte $c5, $3a, $4a, $b0, $93, $79, $64, $b5
|
|
.byte $7f, $e9, $77, $75, $38, $f7, $0c, $a7
|
|
.byte $29, $64, $ae, $67, $96, $d0, $fb, $12
|
|
.byte $0d, $1c, $09, $fc
|
|
ENDIF
|
|
|
|
QR_DEGREE = . - Generator ; verify data size
|
|
|
|
ENDIF ; /QR_GENERATE
|
|
|
|
ECHO "QR Code encoding data:", [. - QRCodeData]d, "bytes"
|
|
_QR_TOTAL SET _QR_TOTAL + . - QRCodeData
|
|
|
|
ENDM |