Add files via upload

This commit is contained in:
Thomas Jentzsch 2021-04-11 10:25:59 +02:00 committed by GitHub
parent 0bbab8727d
commit 3be452938c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1553 additions and 0 deletions

136
GenPoly.asm Normal file
View File

@ -0,0 +1,136 @@
processor 6502
BASE_ADR = $f800
DEGREE = 26
POLY = $11d ; GF(2^8) is based on 9 bit polynomial
; x^8 + x^4 + x^3 + x^2 + 1 = 0x11d
;===============================================================================
; Z P - V A R I A B L E S
;===============================================================================
SEG.U variables
ORG $80
tmpVars ds 4
ALIGN 16
result ds DEGREE
;===============================================================================
; R O M - C O D E
;===============================================================================
SEG Bank0
ORG BASE_ADR
;---------------------------------------------------------------
Start SUBROUTINE
;---------------------------------------------------------------
cld ; Clear BCD math bit.
lda #0
tax
dex
txs
.clearLoop:
tsx
pha
bne .clearLoop
RS_DIVISOR
.wait
jmp .wait
; Computes a Reed-Solomon ECC generator polynomial for degree 16, storing in result[0 : 16-1].
; This is now implemented as a lookup table (Generator)
; 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
MAC RS_DIVISOR
.root = tmpVars+2
.i = tmpVars+3
; memset(result, 0, 16);
ldx #DEGREE-2
ldy #0
.loopClear
sty result,x
dex
bpl .loopClear
; result[16 - 1] = 1; // Start off with the monomial x^0
iny
sty result + DEGREE - 1
; uint8_t root = 1;
sty .root
; for (int i = 0; i < 16; i++) {
lda #DEGREE-1 ; just loop 16 times
sta .i
.loopI
; // Multiply the current product by (x - r^i)
; for (int j = 0; j < 16; j++) {
ldx #0
.loopJ
; result[j] = reedSolomonMultiply(result[j], root);
lda result,x
ldy .root
; RS_MULT
jsr RSMult
; if (j != 16 - 1)
cpx #DEGREE - 1
bcs .skipEor
; result[j] ^= result[j + 1];
eor result+1,x
.skipEor
sta result,x
inx
cpx #DEGREE
bcc .loopJ
; root = reedSolomonMultiply(root, 0x02);
lda .root
ldy #$02
; RS_MULT
jsr RSMult
sta .root
dec .i
bpl .loopI
ENDM
; Returns the product of the two given field elements modulo GF(2^8/0x11D).
; All inputs are valid.
RSMult SUBROUTINE
; 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 #<POLY
.skipEorPoly
; z ^= ((y >> i) & 1) * x;
asl .y
bcc .skipEorX
eor .x
.skipEorX
dey
bpl .loopI
rts
.byte "JTZ"
org BASE_ADR + $7fc
.word Start
.word Start

600
QRCodeGen.inc Normal file
View File

@ -0,0 +1,600 @@
; 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
;-----------------------------------------------------------
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 #MAX_DATA-1
.loopI
stx .i
; uint8_t factor = data[i] ^ result[degree - 1];
lda msgData,x
eor remainder + DEGREE - 1
sta .factor
; memmove(&result[1], &result[0], (size_t)(16 - 1) * sizeof(result[0]));
ldx #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 #DEGREE-1
.loopJ
; result[j] ^= reedSolomonMultiply(generator[j], factor);
lda Generator,x
ldy .factor
_RS_MULT
eor remainder,x
sta remainder,x
; }
dex
bpl .loopJ
; }
ldx .i
dex
bpl .loopI
ENDM
;-----------------------------------------------------------
; 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 #<POLY
.skipEorPoly
; z ^= ((y >> i) & 1) * x;
asl .y
bcc .skipEorX
eor .x
.skipEorX
; }
dey
bpl .loopI
ENDM
;-----------------------------------------------------------
; 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
.right = tmpVars+0
.vert = tmpVars+1
.j = tmpVars+2
.y = tmpVars+3
.iBit = tmpVars+4
.iByte = tmpVars+5
; blacken the (right) function modules in the bitmap
IF QR_OVERLAP
_BLACK_RIGHT ; returns with X = 0
ELSE
_BLACK_FUNC ; returns with X = 0
ENDIF
; int i = 0; // Bit index into the data
; 2600 code has data in reversed order
stx .iBit
lda #TOTAL_LEN-1
sta .iByte
; // Do the funny zigzag scan
; for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair
ldy #QR_SIZE - 1
.loopRight
; if (right == 6)
cpy #6
bne .not6
; right = 5;
dey ; skip the timing column
.not6
sty .right
IF QR_OVERLAP
; overwrite shared data
cpy #16
bne .skipBlackMiddle
; blacken the middle function modules in the bitmap
_BLACK_MIDDLE
.skipBlackMiddle
cpy #8
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
; for (int j = 0; j < 2; j++) {
ldy #-1
.loopJ
iny
sty .j
; bool upward = ((right + 1) & 2) != 0; // 2600 code works in reverse
ldx .vert
lda .right
clc
adc #1
and #$2
bne .notUp
; int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate
lda #QR_SIZE-1
sec
sbc .vert
tax
.notUp
stx .y
; int x = right - j; // Actual x coordinate
lda .right
sec
sbc .j
; sta .x
tay
; if (!getModule(qrcode, x, y) && i < dataLen * 8) {
; ldy .x
; ldx .y
jsr CheckPixel
bne .skipPixel
ldx .iByte
bmi .skipPixel
; bool black = getBit(data[i >> 3], 7 - (i & 7));
lda data,x
ldx .iBit
and BitMask,x
beq .skipInv
; setModule(qrcode, x, y, black);
; ldy .x
ldx .y
jsr InvertPixel
.skipInv
; i++;
ldy .iBit
iny
tya
and #$07
sta .iBit
bne .skipByte
dec .iByte
.skipByte
; }
.skipPixel
ldy .j
beq .loopJ
; } // for j
ldy .vert
dey
bpl .loopVert
; } // for vert
ldy .right
dey
dey
bpl .loopRight
; } // for right
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
;-----------------------------------------------------------
.idx = tmpVars
ldy #NUM_FORMAT-1
.loopFormat
sty .idx
cpy #8
IF QR_SINGLE_MASK
lda #%10101000
and BitMask,y
bcc .lowFormat
lda #%00100100
ELSE
ldx qrPattern
lda FormatLo,x
and BitMask,y
bcc .lowFormat
lda FormatHi,x
ENDIF
and BitMask-8,y
.lowFormat
beq .skipFormat
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
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 #(MODE << 4)
; (MODE << 4) | (MSG_LEN >> 4)
sta msgData + MAX_DATA - 1
txa
asl
asl
asl
asl
; (MSG_LEN << 4)
sta msgData + MAX_DATA - 2
lda #MAX_DATA - 3
sta msgIdx
; clear the remaining data buffer
ldx #TOTAL_LEN-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 #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
; 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
; Format Information Strings
IF QR_SINGLE_MASK = 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
; TODO: levels Q and H
ENDIF
; position 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
BitMask
.byte $80, $40, $20, $10, $8, $4, $2, $1
Generator ; data in reversed order!
IF DEGREE = 7
.byte $75, $44, $0b, $a4, $9a, $7a, $7f
ENDIF
IF DEGREE = 10
.byte $c1, $9d, $71, $5f, $5e, $c7, $6f, $9f
.byte $c2, $d8
ENDIF
IF DEGREE = 15
.byte $1a, $86, $20, $97, $84, $8b, $69, $69
.byte $0a, $4a, $70, $a3, $6f, $c4, $1d
ENDIF
IF 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 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
DEGREE = . - Generator ; verify data
ECHO "QR Code encoding data:", [. - QRCodeData]d, "bytes"
_QR_TOTAL SET _QR_TOTAL + . - QRCodeData
ENDM

817
QRCodeGenDemo.asm Normal file
View File

@ -0,0 +1,817 @@
; QR Code generator demo V0.3
; (C) 2021 Thomas Jentzsch
; TODOs
; + get it working!
; + reduce RAM usage
; + reverse data
; + overlap data with QR code
; + multiple pattern formats
; + apply pattern
; x evaluate pattern (very slow!)
; - support multiple QR code versions
; o support multiple QR code levels
; - try to optimize function pattern (SetPixel)
; x add logo (does NOT work for such small sizes)
;---------------------------------------------------------------
; QR code data bytes (version 2):
; - 13 into right sprite column
; - 17 until horizontal timer line
; - 36 into right and middle sprite column
; - 8 in left sprite column
processor 6502
LIST OFF
include vcs.h
LIST ON
;===============================================================================
; A S S E M B L E R - S W I T C H E S
;===============================================================================
BASE_ADR = $f000
NTSC = 1
; QR Switches
QR_VERSION = 2 ; 1, 2 or 3 (TODO 1 and 3)
QR_LEVEL = 1 ; 0 (L) or 1 (M) (TODO Q and H)
QR_OVERLAP = 1 ; overlaps input and output data to save RAM (0 not tested!)
QR_SINGLE_MASK = 0 ; (-156) if 1 uses only 1 of the 8 mask pattern
QR_PADDING = 1 ; (+22) add padding bytes
;===============================================================================
; C O N S T A N T S
;===============================================================================
; QR code constants
IF QR_VERSION = 1 ;{
QR_SIZE = 21 ; 21x21 QR code
IF QR_LEVEL = 0
DEGREE = 7 ; for version 1, level L QR codes
MAX_DATA = 17+2
ELSE
DEGREE = 10 ; for version 1, level M QR codes
MAX_DATA = 14+2
ENDIF
ENDIF ;}
IF QR_VERSION = 2
QR_SIZE = 25 ; 25x25 QR code
IF QR_LEVEL = 0
DEGREE = 10 ; for version 2, level L QR codes
MAX_DATA = 32+2
ELSE
DEGREE = 16 ; for version 2, level M QR codes
MAX_DATA = 26+2
ENDIF
ENDIF
IF QR_VERSION = 3 ;{
QR_SIZE = 29 ; 29x29 QR code
IF QR_LEVEL = 0
DEGREE = 15 ; for version 3, level L QR codes
MAX_DATA = 53+2
ELSE
DEGREE = 26 ; for version 3, level M QR codes
MAX_DATA = 42+2
ENDIF
ENDIF ;}
IF QR_VERSION = 1 || QR_VERSION = 3
ECHO ""
ECHO "ERROR: Version", [QR_VERSION]d, "unsupported by demo code"
ERR
ENDIF
MODE = %0100 ; byte mode
POLY = $11d ; GF(2^8) is based on 9 bit polynomial
; x^8 + x^4 + x^3 + x^2 + 1 = 0x11d
NUM_FORMAT = 15 ; 15 type information bits
MAX_MSG = MAX_DATA - 2
TOTAL_LEN = MAX_DATA + DEGREE ; 44
NUM_FIRST = 1 ; left top 9 and bottom 8 bits are fixed!
; other constants
RND_EOR_VAL = $b4
_QR_TOTAL SET 0
;===============================================================================
; Z P - V A R I A B L E S
;===============================================================================
SEG.U variables
ORG $80
random .byte
;---------------------------------------
; QR code variables
; all byte counts based on version 2, level M QR code
tmpVars ds 6
msgIdx = tmpVars + 3
IF QR_SINGLE_MASK = 0
qrPattern .byte
ENDIF
;---------------------------------------
data ds TOTAL_LEN ; 44 bytes
remainder = data ; (DEGREE = 16 bytes)
msgData = data + DEGREE ; (MAX_DATA = 28 bytes)
;- - - - - - - - - - - - - - - - - - - -
; The QR code overlaps the data! It overwrites the data while being drawn.
IF QR_OVERLAP
qrCodeLst = data + 6 ; all but 6 bytes overlap (version 2 only!)
ds NUM_FIRST + QR_SIZE*3 - TOTAL_LEN + 6 ; 38 bytes
ELSE
qrCodeLst ds NUM_FIRST + QR_SIZE*3 ; 76 bytes
ENDIF
grp0LLst = qrCodeLst + QR_SIZE * 0
firstMsl = qrCodeLst + QR_SIZE * 1
grp1Lst = qrCodeLst + NUM_FIRST + QR_SIZE * 1
grp0RLst = qrCodeLst + NUM_FIRST + QR_SIZE * 2
QR_LST_SIZE = . - qrCodeLst
;---------------------------------------
; QR code total = 89/127 bytes
ECHO "RAM:", [$100 - .]d, "bytes free"
ECHO ""
;===============================================================================
; M A C R O S
;===============================================================================
MAC BIT_W
.byte $2c
ENDM
MAC SLEEP
IF {1} = 1
ECHO "ERROR: SLEEP 1 not allowed !"
END
ENDIF
IF {1} & 1
nop $00
REPEAT ({1}-3)/2
nop
REPEND
ELSE
REPEAT ({1})/2
nop
REPEND
ENDIF
ENDM
;-----------------------------------------------------------
MAC NEXT_RANDOM
;-----------------------------------------------------------
; update random value:
lda random ; 3
lsr ; 2
bcc .skipEOR ; 2/3
eor #RND_EOR_VAL ; 2
.skipEOR
sta random ; 3 = 14/19
ENDM
; Platform specific macros
IF QR_OVERLAP = 0
;-----------------------------------------------------------
MAC _BLACK_FUNC
;-----------------------------------------------------------
ldx #QR_LST_SIZE-1
.loopBlack
lda BlackGfx,x
sta qrCodeLst,x
dex
bpl .loopBlack
ENDM
ELSE
;-----------------------------------------------------------
MAC _BLACK_LEFT
;-----------------------------------------------------------
ldx #NUM_FIRST + QR_SIZE-1-8
.loopBlackLeft
lda LeftBlack+8,x
sta qrCodeLst+8,x
dex
bpl .loopBlackLeft
ENDM
;-----------------------------------------------------------
MAC _BLACK_MIDDLE
;-----------------------------------------------------------
ldx #QR_SIZE-1
.loopBlackMiddle
lda GRP1Black,x
sta grp1Lst,x
dex
bpl .loopBlackMiddle
ENDM
;-----------------------------------------------------------
MAC _BLACK_RIGHT
;-----------------------------------------------------------
ldx #QR_SIZE
.loopBlackRight
lda GRP0RBlack-1,x
sta grp0RLst-1,x
dex
bne .loopBlackRight
ENDM
ENDIF ; /QR_OVERLAP
;-----------------------------------------------------------
MAC _DRAW_FUNC
;-----------------------------------------------------------
ldx #QR_LST_SIZE-1
.loopBlack
lda qrCodeLst,x
ora BlackGfx,x
eor EorGfx,x
sta qrCodeLst,x
dex
bpl .loopBlack
ENDM
include QRCodeGen.inc
;===============================================================================
; R O M - C O D E
;===============================================================================
SEG Bank0
ORG BASE_ADR
;---------------------------------------------------------------
DrawScreen SUBROUTINE
;---------------------------------------------------------------
ldx #227
.waitTim:
lda INTIM
bne .waitTim
sta WSYNC
sta VBLANK
stx TIM64T
;---------------------------------------------------------------
ldx #3
bit SWCHB
bvs .skipCentering
; some vertical centering
ldx #(192-QR_SIZE*2)/2
.skipCentering
.waitTop
sta WSYNC
dex
bne .waitTop
ldx #QR_SIZE-1
lda #%1 ; 1st top left fixed pixel
bne .enterLoop
.tmpFirst = tmpVars
; the QR code kernel
.loopKernel ; @55
lda FirstIdxTbl,x ; 4*
bne .newFirst ; 2/3
lsr .tmpFirst ; 5
bpl .endFirst ; 3 = 14 unconditional
.newFirst ; @62
; $bf | $01 | $fe
bmi .enterLoop ; 2/3
lda firstMsl ; 3
.enterLoop
sta .tmpFirst ; 3 = 7
.endFirst ; @69
ldy #2 ; 2
.loopBlock
sta WSYNC ; 3 @74
;---------------------------------------
;M1-P0-P1-P0
lda .tmpFirst ; 3
asl ; 2
sta ENAM1 ; 3 = 8
lda grp1Lst,x ; 4
sta GRP1 ; 3
lda grp0LLst,x ; 4
sta GRP0 ; 3
SLEEP 17 ;17
lda grp0RLst,x ; 4
dey ; 2
sta GRP0 ; 3 = 40 @48
bne .loopBlock ; 3/2
dex ; 2
bpl .loopKernel ; 3/2=7/6
sta WSYNC
;---------------------------------------------------------------
sty ENAM1
sty GRP1
sty GRP0
ldx #2
.waitScreen:
lda INTIM
bne .waitScreen
sta WSYNC
stx VBLANK
rts
; DrawScreen
;---------------------------------------------------------------
Start SUBROUTINE
;---------------------------------------------------------------
cld ; Clear BCD math bit.
lda #0
tax
dex
txs
.clearLoop:
tsx
pha
bne .clearLoop
lda INTIM
ora #$10
sta random
jsr InitDemo
.mainLoop:
jsr VerticalBlank
jsr DrawScreen
jsr OverScan
jmp .mainLoop
;---------------------------------------------------------------
InitDemo SUBROUTINE
;---------------------------------------------------------------
sta WSYNC
;---------------------------------------
lda #$0e
sta COLUBK
lda #$00
sta COLUP0
sta COLUP1
lda #%001
sta NUSIZ0
sta VDELP1
ldx #$3f
stx HMP0
inx
stx HMP1
lda #$a0
sta HMM1
SLEEP 3
sta RESM1
sta RESP0
sta RESP1
sta WSYNC
;---------------------------------------
sta HMOVE
jmp GenerateQR
; GameInit
;---------------------------------------------------------------
VerticalBlank SUBROUTINE
;---------------------------------------------------------------
lda #%00001110
.loopVSync:
sta WSYNC
sta VSYNC
lsr
bne .loopVSync
IF NTSC
lda #44
ELSE
lda #77
ENDIF
sta TIM64T
bit INPT4
bmi .skipRegen
jsr GenerateQR
.skipRegen
NEXT_RANDOM
rts
; VerticalBlank
;---------------------------------------------------------------
OverScan SUBROUTINE
;---------------------------------------------------------------
IF NTSC
lda #36
ELSE
lda #63
ENDIF
sta TIM64T
.waitTim:
lda INTIM
bne .waitTim
rts
; OverScan
;---------------------------------------------------------------
GenerateQR SUBROUTINE
;---------------------------------------------------------------
; *** Generate QR code from message ***
IF QR_SINGLE_MASK = 0
lda random
and #$07
; lda #0
sta qrPattern
ENDIF
MessageCode
; convert the message into a data stream
.msgLen = tmpVars
.msgPtr = tmpVars+1
lda random
lsr
lsr
lsr
and #$0f
tay
; ldy #0
lda MessagePtrLo,y
sta .msgPtr
lda MessagePtrHi,y
sta .msgPtr+1
lda MessagePtrLo+1,y
sec
sbc .msgPtr
sta .msgLen
START_MSG
ldy #0
.loopMsg
lda (.msgPtr),y
ADD_MSG_BYTE
iny
cpy .msgLen
bcc .loopMsg
STOP_MSG
ECHO "QR Code message code:", [. - MessageCode]d, "bytes"
_QR_TOTAL SET _QR_TOTAL + . - MessageCode
GEN_QR_CODE
rts
BitMapCode
;---------------------------------------------------------------
CheckPixel SUBROUTINE
;---------------------------------------------------------------
; Platform specific code. Must NOT change X and Y registers!
; X = y; Y = x
; determine 8 bit column (0..2) or missile columns
tya
bne .notMissile
; check if single missile byte is affected
cpx #8
bcc .alwaysSet
cpx #8*2
bcs .alwaysSet
lda firstMsl
and BitMask-8,x
rts
.alwaysSet
lda #1
rts
.notMissile
cpy #1+8
bcs .notGRP0L
IF QR_OVERLAP
cpx #8 ; bottom left eye (partially) shared with data!
bcc .alwaysSet
ENDIF
lda grp0LLst,x
and BitMask-1,y
rts
.notGRP0L
cpy #1+8*2
bcs .notGRP1
lda grp1Lst,x
and BitMask-1-8,y
rts
.notGRP1
; must be GRP0R then
lda grp0RLst,x
and BitMask-1-8*2,y
rts
;---------------------------------------------------------------
InvertPixel SUBROUTINE
;---------------------------------------------------------------
; Platform specific code. Must NOT change X and Y registers!
; X = y; Y = x
; determine 8 bit column (0..2) or missile column
tya
bne .notMissile
; check if single missile byte is affected
cpx #8
bcc .ignore
cpx #8*2
bcs .ignore
lda BitMask-8,x
eor firstMsl
sta firstMsl
.ignore
rts
.notMissile
cpy #1+8
bcs .notGRP0L
lda grp0LLst,x
eor BitMask-1,y
sta grp0LLst,x
rts
.notGRP0L
cpy #1+8*2
bcs .notGRP1
lda grp1Lst,x
eor BitMask-1-8,y
sta grp1Lst,x
rts
.notGRP1
; must be GRP0R then
lda grp0RLst,x
eor BitMask-1-8*2,y
sta grp0RLst,x
rts
ECHO "QR Code bitmap code:", [. - BitMapCode]d, "bytes"
_QR_TOTAL SET _QR_TOTAL + . - BitMapCode
;===============================================================================
; R O M - T A B L E S (Bank 0)
;===============================================================================
org BASE_ADR + $600
FunctionModulesData
; Platform and version specific function module data definition
BlackGfx
LeftBlack
GRP0LBlack
.byte %11111111 ; constant, bit 0 of 2nd format copy, level
.byte %11111111 ; constant, bit 1 of 2nd format copy, level
.byte %11111111 ; constant, bit 2 of 2nd format copy, pattern
.byte %11111111 ; constant, bit 3 of 2nd format copy, pattern
.byte %11111111 ; constant, bit 4 of 2nd format copy, pattern
.byte %11111111 ; constant, bit 5 of 2nd format copy, ECC
.byte %11111111 ; constant, bit 6 of 2nd format copy, ECC
.byte %11111111 ; constant, 1 (dark module)
.byte %00000100
.byte %00000100
.byte %00000100
.byte %00000100
.byte %00000100
.byte %00000100
.byte %00000100
.byte %00000100
.byte %11111111 ; constant, bits 1..7 of 1st format copy
.byte %11111111 ; constant, bit 8 of 1st format copy, ECC
.byte %11111111 ; constant, 1 (timing bit)
.byte %11111111 ; constant, bit 9 of 1st format copy, ECC
.byte %11111111 ; constant, bit 10 of 1st format copy, ECC
.byte %11111111 ; constant, bit 11 of 1st format copy, ECC
.byte %11111111 ; constant, bit 12 of 1st format copy, ECC
.byte %11111111 ; constant, bit 13 of 1st format copy, ECC
.byte %11111111 ; constant, bit 14 of 1st format copy, ECC
;FirstBlack
.byte %00000000
GRP1Black
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000001
.byte %00000001
.byte %00000001
.byte %00000001
.byte %00000001
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %11111111
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
GRP0RBlack
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %11110000
.byte %11110000
.byte %11110000
.byte %11110000
.byte %11110000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %11111111 ; constant, bits 7..14 of 2nd format copy
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
.byte %11111111 ; constant
EorGfx
;GRP0LEor
.byte %00000011 ; constant, bit 0 of 2nd format copy, level
.byte %11111011 ; constant, bit 1 of 2nd format copy, level
.byte %10001011 ; constant, bit 2 of 2nd format copy, pattern
.byte %10001011 ; constant, bit 3 of 2nd format copy, pattern
.byte %10001011 ; constant, bit 4 of 2nd format copy, pattern
.byte %11111011 ; constant, bit 5 of 2nd format copy, ECC
.byte %00000011 ; constant, bit 6 of 2nd format copy, ECC
.byte %11111110 ; constant, 1 (dark module)
.byte %00000000
.byte %00000100
.byte %00000000
.byte %00000100
.byte %00000000
.byte %00000100
.byte %00000000
.byte %00000100
.byte %11111011 ; constant, bits 1..7 of 1st format copy
.byte %11111111 ; constant, bit 8 of 1st format copy, ECC
.byte %00000010 ; constant, 1 (timing bit)
.byte %11111011 ; constant, bit 9 of 1st format copy, ECC
.byte %10001011 ; constant, bit 10 of 1st format copy, ECC
.byte %10001011 ; constant, bit 11 of 1st format copy, ECC
.byte %10001011 ; constant, bit 12 of 1st format copy, ECC
.byte %11111011 ; constant, bit 13 of 1st format copy, ECC
.byte %00000011 ; constant, bit 14 of 1st format copy, ECC
;FirstEor
.byte %00000000
;GRP1Eor
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %10101010
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
;GRP0REor
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %11100000
.byte %10100000
.byte %11100000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %11111111 ; constant, bits 7..14 of 2nd format copy
.byte %11111111 ; constant
.byte %10000000 ; constant
.byte %10111110 ; constant
.byte %10100010 ; constant
.byte %10100010 ; constant
.byte %10100010 ; constant
.byte %10111110 ; constant
.byte %10000000 ; constant
FirstIdxTbl ; for 25 pixel
ds 7, 0
.byte $fe
ds 7, 0
.byte $01
ds 7, 0
.byte $bf
ECHO "QR Code function modules data:", [. - FunctionModulesData]d, "bytes"
_QR_TOTAL SET _QR_TOTAL + . - FunctionModulesData
QR_CODE_DATA
.byte " QR Code Generator Demo V0.3 - (C)2021 Thomas Jentzsch "
; messages MUST not be longer than 26 bytes for version 2, level M!
; Galadriel:
MessageTbl
Message0
.byte "It began with the forging"
; .byte "AtariAge/?s=_1X<|>[]*#"
Message1
.byte "of the Great Rings. Three"
Message2
.byte "were given to the Elves,"
Message3
.byte "immortal, wisest and"
Message4
.byte "fairest of all beings."
Message5
.byte "Seven to the Dwarf lords,"
Message6
.byte "great miners and craftsmen"
Message7
.byte "of the mountain halls. And"
Message8
.byte "nine, nine rings were"
Message9
.byte "gifted to the race of men,"
Message10
.byte "who, above all else,"
Message11
.byte "desire power. But they"
Message12
.byte "were, all of them,"
Message13
.byte "deceived, for another Ring"
Message14
.byte "was made. In the land of"
Message15
.byte "Mordor, in the fires of..."
MessageEnd
; .byte "..the single hardest thing"
MessagePtrLo
.byte <Message0, <Message1, <Message2, <Message3
.byte <Message4, <Message5, <Message6, <Message7
.byte <Message8, <Message9, <Message10, <Message11
.byte <Message12, <Message13, <Message14, <Message15
.byte <MessageEnd
MessagePtrHi
.byte >Message0, >Message1, >Message2, >Message3
.byte >Message4, >Message5, >Message6, >Message7
.byte >Message8, >Message9, >Message10, >Message11
.byte >Message12, >Message13, >Message14, >Message15
.byte "JTZ"
org BASE_ADR + $ffc
.word Start
.word Start
ECHO "----------------------------------------"
ECHO "QR Code total:", [_QR_TOTAL]d, "bytes"
ECHO ""
ECHO "QR Code Version, Level (Degree): ", [QR_VERSION]d, ",", [QR_LEVEL]d, "(", [DEGREE]d, ")"