added function module drawing

This commit is contained in:
thrust26 2021-04-14 23:23:39 +02:00
parent d150cbc5af
commit 77263c915c
5 changed files with 351 additions and 65 deletions

View File

@ -162,16 +162,3 @@ EorGfx
.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
IF QR_LEVEL = 0 || QR_LEVEL = 1
.byte $bf ; 1st format bit is 1
ELSE
.byte $3f ; 1st format bit is 0
ENDIF

View File

@ -171,15 +171,3 @@ GRP0RBlack
IF QR_LEVEL = 1
EOR_GFX ~%10101000, ~%00100100
ENDIF
FirstIdxTbl ; for 25 pixel
ds 7, 0
.byte $fe
ds 7, 0
.byte $01
ds 7, 0
IF QR_LEVEL = 0 || QR_LEVEL = 1
.byte $bf ; 1st format bit is 1
ELSE
.byte $3f ; 1st format bit is 0
ENDIF

View File

@ -2,8 +2,20 @@
; 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 ;{
QR_SIZE = 21 ; 21x21 QR code
; 21x21 QR code
IF QR_LEVEL = 0
QR_DEGREE = 7 ; for version 1, level L QR codes
ENDIF
@ -19,7 +31,7 @@ QR_DEGREE = 17 ; for version 1, level H QR codes
ENDIF ;}
IF QR_VERSION = 2
QR_SIZE = 25 ; 25x25 QR code
; 25x25 QR code
IF QR_LEVEL = 0
QR_DEGREE = 10 ; for version 2, level L QR codes
ENDIF
@ -35,7 +47,7 @@ QR_DEGREE = 28 ; for version 2, level H QR codes
ENDIF
IF QR_VERSION = 3 ;{
QR_SIZE = 29 ; 29x29 QR code
; 29x29 QR code
IF QR_LEVEL = 0
QR_DEGREE = 15 ; for version 3, level L QR codes
ENDIF
@ -49,6 +61,8 @@ QR_DEGREE = 26 ; for version 3, level M QR codes
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
@ -219,24 +233,150 @@ RSMult SUBROUTINE
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
.right1 = tmpVars+0
.vert = tmpVars+1
.j = tmpVars+2
.y = tmpVars+3
.iBit = tmpVars+4
.iByte = tmpVars+5
.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
@ -262,6 +402,9 @@ RSMult SUBROUTINE
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
@ -332,7 +475,8 @@ RSMult SUBROUTINE
ldy .right1
dey
dey
bpl .loopRight ; unconditional!
; bpl .loopRight ; unconditional!
jmp .loopRight ; unconditional!
; } // for right
.exitDraw
ENDM
@ -496,7 +640,7 @@ RSMult SUBROUTINE
;-----------------------------------------------------------
MAC _DRAW_FORMAT
;-----------------------------------------------------------
IF QR_SINGLE_MASK = 0
IF QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
.idx = tmpVars
ldy #QR_FORMATS-1
@ -510,7 +654,11 @@ RSMult SUBROUTINE
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
@ -643,8 +791,77 @@ _QR_TOTAL SET _QR_TOTAL + . - QRCodeCode
; 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
IF QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
IF QR_LEVEL = 0 ; L
FormatLo
.byte %11101111
@ -725,7 +942,6 @@ FormatHi
.byte %00011000
.byte %01110110
ENDIF
ENDIF ; /QR_SINGLE_MASK = 0
; positions of the 15 type information bits
FormatX1
@ -742,7 +958,7 @@ 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
ENDIF ; /QR_SINGLE_MASK = 0 || QR_DIRECT_DRAW = 0
BitMask
.byte $80, $40, $20, $10, $8, $4, $2, $1

View File

@ -35,21 +35,31 @@
BASE_ADR = $f000
NTSC = 1 ; 0 = PAL50
ATARI_2600 = 1 ; enable for Atari 2600 specific code
; QR Code Generator Switches
QR_VERSION = 2 ; 1, 2 or 3 (TODO 1 and 3)
QR_LEVEL = 1 ; 0 (L), 1 (M), 2 (Q) and 3 (H))
QR_PADDING = 1 ; (+22 bytes) add padding bytes (optional)
QR_GENERATE = 1 ; (+~12 bytes) generates Reed-Solomon ECC generator polynomial on-the-fly
QR_PADDING = 1 ; (+ 22 bytes) add padding bytes (optional)
QR_GENERATE = 0 ; (+~12 bytes) generates Reed-Solomon ECC generator polynomial on-the-fly
; else uses built-in table
; Atari 2600 specific QR settings (set to 0 for other platforms)
; Atari 2600 specific QR settings (keep set to 0 for other platforms!)
IFCONST ATARI_2600
QR_OVERLAP = 1 ; overlaps input and output data to save RAM (defined for version 2 only!)
QR_SINGLE_MASK = 0 ; (-255 bytes) if 1 uses only 1 of the 8 mask pattern
QR_DIRECT_DRAW = 0 ; (+ 45 bytes) draw byte columns instead of individual pixel
ENDIF
IF QR_VERSION = 1 || QR_VERSION = 3
IF QR_VERSION != 2
ECHO ""
ECHO "ERROR: Version", [QR_VERSION]d, "unsupported by demo code"
ECHO "*** ERROR: Version", [QR_VERSION]d, "unsupported by demo code! ***"
ERR
ENDIF
IF QR_SINGLE_MASK = 1 && QR_DIRECT_DRAW = 0
ECHO ""
ECHO "*** ERROR: Unsupported assembler switches combination! ***"
ERR
ENDIF
@ -76,7 +86,11 @@ random .byte
;---------------------------------------
; QR code variables
; all byte counts based on version 2, level M QR code
IF QR_DIRECT_DRAW
tmpVars ds 6
ELSE
tmpVars ds 9
ENDIF
msgIdx = tmpVars + 3
IF QR_SINGLE_MASK = 0
@ -89,11 +103,16 @@ msgData = data + QR_DEGREE ; (QR_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 - QR_TOTAL + 6 ; 38 bytes
IF QR_DIRECT_DRAW
QR_NON_OVER = 6
ELSE
QR_NON_OVER = 8
ENDIF
qrCodeLst = data + QR_NON_OVER ; all but 6/8 bytes overlap (version 2 only!)
ds NUM_FIRST + QR_SIZE*3 - QR_TOTAL + QR_NON_OVER ; 38/40 bytes
ELSE
qrCodeLst ds NUM_FIRST + QR_SIZE*3 ; 76 bytes
ENDIF
qrCodeLst ds NUM_FIRST + QR_SIZE*3 ; 76 bytes
ENDIF ; /QR_OVERLAP
grp0LLst = qrCodeLst + QR_SIZE * 0
firstMsl = qrCodeLst + QR_SIZE * 1
grp1Lst = qrCodeLst + NUM_FIRST + QR_SIZE * 1
@ -150,62 +169,124 @@ qrGenerator ds QR_DEGREE
sta random ; 3 = 14/19
ENDM
; Platform specific macros
; Atari 2600 specific macros
IF QR_OVERLAP = 0
IF QR_DIRECT_DRAW
;-----------------------------------------------------------
MAC _BLACK_FUNC
;-----------------------------------------------------------
; blacks all function/alignment and timing pattern
ldx #CODE_LST_SIZE-1
ldx #CODE_LST_SIZE
.loopBlack
lda BlackGfx,x
sta qrCodeLst,x
lda BlackGfx-1,x
sta qrCodeLst-1,x
dex
bpl .loopBlack
bne .loopBlack
; X = 0!
ENDM
ENDIF
ELSE
ELSE ; QR_OVERLAP
;-----------------------------------------------------------
MAC _BLACK_LEFT
;-----------------------------------------------------------
; blacks all function/alignment and timing pattern of the left column
; Blacks all function/alignment and timing pattern areas of the left sprite column
IF QR_DIRECT_DRAW
ldx #NUM_FIRST + QR_SIZE-1-8
.loopBlackLeft
lda LeftBlack+8,x
sta qrCodeLst+8,x
dex
bpl .loopBlackLeft
ELSE
; clear the bitmap column first...
ldx #NUM_FIRST + QR_SIZE-1-8
lda #0
.loopBlackLeft
sta qrCodeLst+8,x
dex
bpl .loopBlackLeft
; ...then draw the pattern
IF QR_DIRECT_DRAW
ldy #1 ; left top pattern/vertical timing line
ELSE
ldy #2
ENDIF
.loopPattern
lda #$ff ; fill pattern
jsr DrawPattern
bpl .loopPattern
ENDIF
ENDM
;-----------------------------------------------------------
MAC _BLACK_MIDDLE
;-----------------------------------------------------------
; blacks all function/alignment and timing pattern of the middle column
; Blacks all function/alignment and timing pattern areas of the middle sprite column
IF QR_DIRECT_DRAW
ldx #QR_SIZE-1
.loopBlackMiddle
lda GRP1Black,x
sta grp1Lst,x
dex
bpl .loopBlackMiddle
ELSE
; clear the bitmap column first...
ldx #QR_SIZE-1
lda #0
.loopBlackMiddle
sta grp1Lst,x
dex
bpl .loopBlackMiddle
; ...then draw the pattern
ldy #6 ; align pattern
.loopPattern
lda #$ff ; fill pattern
jsr DrawPattern
cpy #5
bcs .loopPattern
ENDIF
ENDM
;-----------------------------------------------------------
MAC _BLACK_RIGHT
;-----------------------------------------------------------
; blacks all function/alignment and timing pattern of the right column
; Blacks all function/alignment and timing pattern areas of the right sprite column
IF QR_DIRECT_DRAW
ldx #QR_SIZE
.loopBlackRight
lda GRP0RBlack-1,x
sta grp0RLst-1,x
dex
bne .loopBlackRight
ELSE
; clear the bitmap column first...
ldx #QR_SIZE-1
lda #$00
.loopBlackRight
sta grp0RLst,x
dex
bpl .loopBlackRight
; ...then draw the pattern
ldy #4 ; right pattern
.loopPattern
lda #$ff ; fill pattern
jsr DrawPattern
cpy #3
bcs .loopPattern
ldx #0
ENDIF
; X = 0!
ENDM
ENDIF ; /QR_OVERLAP
IF QR_DIRECT_DRAW
;-----------------------------------------------------------
MAC _DRAW_FUNC
;-----------------------------------------------------------
; Draws all function/alignment and timing pattern over existing codewords
ldx #CODE_LST_SIZE-1
.loopBlack
lda qrCodeLst,x
@ -222,6 +303,7 @@ qrGenerator ds QR_DEGREE
jsr InvertPixel
ENDIF
ENDM
ENDIF
;===============================================================================
@ -436,7 +518,7 @@ MessageCode
lsr
and #$0f
tay
; ldy #0
; ldy #3
lda MessagePtrLo,y
sta .msgPtr
lda MessagePtrHi,y
@ -486,7 +568,7 @@ CheckPixel SUBROUTINE
.notMissile
cpy #1+8
bcs .notGRP0L
IF QR_OVERLAP
IF QR_OVERLAP & QR_DIRECT_DRAW
cpx #8 ; bottom left eye (partially) shared with data!
bcc .alwaysSet
ENDIF
@ -560,13 +642,14 @@ _QR_TOTAL SET _QR_TOTAL + . - BitMapCode
FunctionModulesData
; Platform and version specific function module data definition
IF QR_DIRECT_DRAW
IF QR_VERSION = 1
ERR ; TODO
ENDIF
IF QR_VERSION = 2
IF QR_SINGLE_MASK
include FuncDataV2S.inc ; TODO: special pattern
include FuncDataV2S.inc ; special pattern
ELSE
include FuncDataV2.inc
ENDIF
@ -574,6 +657,19 @@ FunctionModulesData
IF QR_VERSION = 3
ERR ; TODO
ENDIF
ENDIF
FirstIdxTbl ; for 25 pixel
ds 7, 0
.byte $fe
ds 7, 0
.byte $01
ds 7, 0
IF QR_LEVEL = 0 || QR_LEVEL = 1
.byte $bf ; 1st format bit is 1
ELSE
.byte $3f ; 1st format bit is 0
ENDIF
ECHO "QR Code function modules data:", [. - FunctionModulesData]d, "bytes"
@ -584,12 +680,11 @@ _QR_TOTAL SET _QR_TOTAL + . - FunctionModulesData
.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 "2002 - Thrust+ Platinum"
Message1
.byte "2001 - Jammed "
.byte "2001 - Jammed"
Message2
.byte "2005 - SWOOPS!"
Message3

View File

@ -15,14 +15,14 @@ If you make use of my code or have questions, please let me know.
- code size optimized for minimal RAM and ROM space
- all eight mask pattern supported
- Atari 2600 demo code (randomly generates some Atari 2600 related messages)
- generator code for Reed-Solomon ECC generator polygons accompanied (can be integrated to compute on-the-fly)
- generator code for Reed-Solomon ECC generator polygons accompanied (can also be integrated to compute on-the-fly)
## Limitations
- only small, single block QR codes supported
- only small (versions 1, 2 and 3), single block QR codes supported
- only byte mode supported
- no automatic mask pattern evaluation
- tested only for version 2 QR codes (25x25)
- memory organization and pixel checking/drawing has to be implemented platform specific; this includes the finder, alignment and timing pattern
- memory organization has to be implemented platform specific
## License
Copyright © 2021 Thomas Jentzsch. (GPLV3 License)