
; QR code generator, image size is 21x21 pixels
; version 1.0

format PE console
entry start

include 'include\win32a.inc'

SIZE_QRDATA = 32
SIZE_QRECC  = 16

macro nalign value
{
  rb (value-1) - (RVA $ + value-1) mod value
}

macro calign value
{
  times ((value-1) - (RVA $ + value-1) mod value) db 0x90
}

macro pushext {
  push	ebx
  push	ebp
  push	esi
  push	edi
}

macro popext {
  pop	edi
  pop	esi
  pop	ebp
  pop	ebx
}

section '.code' code readable executable

    rs_init_gf:			; ecx = poly
	pushext

	mov	ebx, 1		; ebx = b = 1
	xor	eax, eax	; eax = m
	mov	edx, ebx	; edx = 1
      .L1:
	cmp	ebx, ecx	; if b > poly then
	jg	@f		;   break the loop
	inc	eax		; Inc (m)
	shl	ebx, 1		; b = b shl 1
	jmp	.L1
      @@:
				; since there are no calls in this subroutine,
				; it's possible to use the stack directly

	mov	[esp-4], ecx	; save ecx on stack
	dec	eax		; Dec (m)
	shr	ebx, 1		; b = b shr 1

	mov	ecx, eax	; ecx = m
	shl	edx, cl		; edx = (1 shl m)
	mov	ecx, [esp-4]	; restore ecx
	dec	edx		; edx = logmod = (1 shl m) - 1
	mov	[esp-4], ebx	; save b
	mov	eax, 1		; eax = p
	mov	[logmod], edx	; store global variable
	xor	ebx, ebx	; ebx = v
	mov	esi, logt
	mov	edi, alog

      .L2:
	cmp	ebx, edx		; if v >= logmod then
	jge	.L9			;   return
	mov	[edi+4*ebx], eax	; alog[v] = p
	mov	[esi+4*eax], ebx	; logt[p] = v
	shl	eax, 1			; p = p shl 1
	mov	ebp, [esp-4]		; load b from stack
	and	ebp, eax		; if (p and b) = 0 then
	jz	 @f			;   do nothing
	xor	eax, ecx		; else p:=p xor poly
      @@:
	inc	ebx			; Inc (v)
	jmp	.L2

      .L9:
	popext
	ret


    calign 16
    rs_init_code:		; ecx = nsym, edx = index
	pushext
	mov	esi, 1			; esi = i
	mov	[esp-4], ecx		; save nsym
	mov	[esp-8], edx		; save index
	mov	edi, rspoly
	mov	[rlen], ecx		; store global variable
	mov	dword [edi], 1		; rspoly[0] = 1

      .L1:
	cmp	esi, [esp-4]		; if i > nsym then
	jg	.L9                     ;   return
	mov	dword [edi+4*esi], 1	; rspoly[i] = 1
	lea	ebx, [esi-1]		; ebx = k = i - 1
      .L2:
	cmp	ebx, 0			; if k <= 0 then
	jle	.L4			;   break the loop
	mov	ecx, [edi+4*ebx]	; ecx = rspoly[k]
	cmp	ecx, 0
	jz	@f			; if rspoly[k]=0 then jump

	mov	eax, [logt+4*ecx]	; eax = logt[rspoly[k]]
	xor	edx, edx		; prepare edx for division
	add	eax, [esp-8]		; eax = eax + index
	idiv	dword [logmod]
	mov	eax, [alog+4*edx]	; eax = alog [ ... ]
	mov	[edi+4*ebx], eax	; rspoly[k] = eax

      @@:
	mov	eax, [edi+4*ebx-4]	; eax = rspoly[k - 1]
	xor	eax, [edi+4*ebx]	; eax = eax xor rspoly[k]
	mov	[edi+4*ebx], eax        ; rspoly[k] = eax
	dec	ebx			; Dec (k)
	jmp	.L2

      .L4:
	mov	ecx, [edi]		; ecx = rspoly[0]
	mov	eax, [logt+4*ecx]	; eax = logt[rspoly[0]]
	xor	edx, edx		; prepare edx for division
	add	eax, [esp-8]		; eax = eax + index
	idiv	dword [logmod]		
	inc	dword [esp-8]           ; Inc (index)
	mov	eax, [alog+4*edx]	; eax = alog [ ... ]
	inc	esi			; Inc (i)
	mov	[edi], eax		; rspoly[0] = eax
	jmp	.L1

      .L9:
	popext
	ret


    calign 16
    rs_encode:			; ecx = len, edx = data, eax = res
	pushext
	mov	[esp-4], ecx		; save len
	mov	[esp-8], edx		; save data
	mov	esi, eax		; esi = res
	xor	edi, edi		; edi = i = 0

      .L1:
	or	ebx, -1			; same as "mov ebx, -1" but shorter
	cmp	edi, [esp-4]		; if i >= len then
	jge	.L9			;   return
	xor	eax, eax		; going to use eax and al simultaneously, so must clear it first
	mov	edx, [esp-8]		; edx = data
	add	ebx, [rlen]		; ebx = k = rlen - 1
	mov	al, [edx+edi]		; al = data[i]
	xor	al, [esi+ebx]		; al = al xor res[k]
	mov	ebp, eax		; ebp = m

      .L2:
	cmp	ebx, 0			; if k <= 0 then
	jle	.L6			;   break the loop
	xor	eax, eax		; using carry flag to test ((m<>0) and (rspoly[k]<>0))
	mov	ecx, [rspoly+4*ebx]
	cmp	ebp, 1			; set carry flag if m=0
	adc	eax, 0			; inc eax if m=0
	cmp	ecx, 1                  ; set carry flag if rspoly[k]=0
	adc	eax, 0			; inc eax if rspoly[k]=0
	jnz	@f

	mov	eax, [logt+4*ebp]
	xor	edx, edx		; prepare edx for division
	add	eax, [logt+4*ecx]
	idiv	dword [logmod]
	mov	eax, [alog+4*edx]
	xor	al, [esi+ebx-1]
	jmp	.L4

      @@:
	mov	al, [esi+ebx-1]

      .L4:
	mov	[esi+ebx], al
	dec	ebx			; Dec (k)
	jmp	.L2

      .L6:
	xor	eax, eax		; using carry flag to test ((m<>0) and (rspoly[0]<>0))
	mov	ecx, [rspoly]
	cmp	ebp, 1
	adc	eax, 0
	cmp	ecx, 1
	adc	eax, 0
	jnz	@f

	mov	eax, [logt+4*ebp]
	xor	edx, edx		; prepare edx for division
	add	eax, [logt+4*ecx]
	idiv	dword [logmod]
	mov	eax, [alog+4*edx]
	jmp	.L8

      @@:
	xor	eax, eax
      .L8:
	mov	[esi], al
	inc	edi			; Inc (i)
	jmp	.L1

      .L9:
	popext
	ret


    calign 16
    bit:				; al = char, edx = table
	pushext
	mov	esi, edx		; esi = table
	xor	edx, edx		; edx = i-1

      .L1:
	mov	cl, byte [esi]		; cl = vert
	xor	ebx, ebx
	and	cl, 1
	or	bl, cl
	mov	cl, byte [esi+2]	; cl = up
	shl	bl, 1
	and	cl, 1
	or	bl, cl			; 4 possible combinations of vert and up
	mov	edi, [vu_case+4*ebx]	; select the right one from a table

	mov	ebx, 1			; bl = (Byte(c) and (1 shl i)) shr i
	mov	ecx, edx
	shl	bl, cl
	and	bl, al
	shr	bl, cl
	xor	ebp, ebp

      .L4:				; if i in [ ... ] then
	cmp	dl, [edi+ebp]
	jnz	@f
	xor	bl, 1			; flip the bit
      @@:
	inc	ebp
	cmp	ebp, 4			; 4 entries in vu_xx tables
	jl	.L4
	
	lea	ecx, [edx+2]		; ecx = edx + 2
	mov	cx, word [esi+2*ecx]	; get position
	mov	[qr_image+ecx-2], bl	; modify appropriate pixel in qr_image
	inc	edx
	cmp	edx, 8
	jl	.L1

	popext
	ret
	

    calign 16
    start:
	sub	esp, 8

	mov	ecx, 285
	call	rs_init_gf
	mov	ecx, 7
	mov	edx, 0
	call	rs_init_code

	mov	[qrdata], 0x40		; qrdata and qrecc are already zeroed
	mov	[qrdata+1], 0xE0	; = 14 shl 4
	xor	ecx, ecx

      .init_qrdata:
	mov	al, byte [string+ecx]
	cmp	al, 0
	jz	.encode
	mov	dl, al
	shr	al, 4
	shl	dl, 4
	or	byte [qrdata+ecx+1], al
	mov	byte [qrdata+ecx+2], dl
 	inc	ecx
	jmp	.init_qrdata

      .encode:
	mov	ecx, 19
	mov	edx, qrdata
	mov	eax, qrecc
	call	rs_encode

      .bit_length:	
	mov	al, 14
	mov	edx, qr_tbl00
	call	bit

	xor	eax, eax
	
      .bit_string:
	mov	[esp], eax
	mov	ecx, eax
	imul	eax, 2*10
	lea	edx, [qr_tbl01+eax]
	mov	al, [string+ecx]
	call	bit
	mov	eax, [esp]
	inc	eax
	cmp	eax, 14
	jl	.bit_string

	mov	eax, 6	
      .bit_ecc:
	mov	[esp], eax
	mov	ecx, eax
	imul	eax, 2*10
	lea	edx, [qr_tble0+eax]
	mov	al, [qrecc+ecx]
	call	bit
	mov	eax, [esp]
	dec	eax
	jns	.bit_ecc

      .save_bmp:
	invoke	CreateFile,image,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,\
			0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
	cmp	eax, INVALID_HANDLE_VALUE
	jz	.exit
	mov	[esp+4], eax
	mov	edx, esp
	invoke	WriteFile,eax,qr_bmp,size_qr_bmp,edx,0
	mov	eax, [esp+4]
	invoke	CloseHandle,eax

      .exit:
	add	esp, 8
	invoke	ExitProcess, 0


section '.data' data readable writeable

  logmod   dd 0
  rlen     dd 0

  nalign   16
  string   db 'Hello World!',0
  times    16-($-string) db 0		; fill the rest of the string with null bytes

  image    db 'QRCLI.BMP',0

  nalign   16
  vu_case  dd vu_ff, vu_ft, vu_tf, vu_tt
  vu_tt    db 2,3,6,7
  vu_tf    db 0,1,4,5
  vu_ft    db 2,3,4,5
  vu_ff    db 0,1,6,7

  qr_tbl00 dw  TRUE,  TRUE, 24*17-2, 24*17-1, 24*18-2, 24*18-1, 24*19-2, 24*19-1, 24*20-2, 24*20-1
  qr_tbl01 dw  TRUE,  TRUE, 24*13-2, 24*13-1, 24*14-2, 24*14-1, 24*15-2, 24*15-1, 24*16-2, 24*16-1
  qr_tbl02 dw FALSE, FALSE, 24*12-4, 24*12-3, 24*11-4, 24*11-3, 24*11-2, 24*11-1, 24*12-2, 24*12-1
  qr_tbl03 dw  TRUE, FALSE, 24*16-4, 24*16-3, 24*15-4, 24*15-3, 24*14-4, 24*14-3, 24*13-4, 24*13-3
  qr_tbl04 dw  TRUE, FALSE, 24*20-4, 24*20-3, 24*19-4, 24*19-3, 24*18-4, 24*18-3, 24*17-4, 24*17-3
  qr_tbl05 dw FALSE,  TRUE, 24*21-6, 24*21-5, 24*22-6, 24*22-5, 24*22-4, 24*22-3, 24*21-4, 24*21-3
  qr_tbl06 dw  TRUE,  TRUE, 24*17-6, 24*17-5, 24*18-6, 24*18-5, 24*19-6, 24*19-5, 24*20-6, 24*20-5
  qr_tbl07 dw  TRUE,  TRUE, 24*13-6, 24*13-5, 24*14-6, 24*14-5, 24*15-6, 24*15-5, 24*16-6, 24*16-5
  qr_tbl08 dw FALSE, FALSE, 24*12-8, 24*12-7, 24*11-8, 24*11-7, 24*11-6, 24*11-5, 24*12-6, 24*12-5
  qr_tbl09 dw  TRUE, FALSE, 24*16-8, 24*16-7, 24*15-8, 24*15-7, 24*14-8, 24*14-7, 24*13-8, 24*13-7
  qr_tbl10 dw  TRUE, FALSE, 24*20-8, 24*20-7, 24*19-8, 24*19-7, 24*18-8, 24*18-7, 24*17-8, 24*17-7
  qr_tbl11 dw FALSE,  TRUE, 24*21-10, 24*21-9, 24*22-10, 24*22-9, 24*22-8, 24*22-7, 24*21-8, 24*21-7
  qr_tbl12 dw  TRUE,  TRUE, 24*17-10, 24*17-9, 24*18-10, 24*18-9, 24*19-10, 24*19-9, 24*20-10, 24*20-9
  qr_tbl13 dw  TRUE,  TRUE, 24*13-10, 24*13-9, 24*14-10, 24*14-9, 24*15-10, 24*15-9, 24*16-10, 24*16-9
  qr_tbl14 dw  TRUE,  TRUE, 24*9-10, 24*9-9, 24*10-10, 24*10-9, 24*11-10, 24*11-9, 24*12-10, 24*12-9
  qr_tble0 dw  TRUE, FALSE, 24*14-21, 24*14-20, 24*13-21, 24*13-20, 24*12-21, 24*12-20, 24*11-21, 24*11-20
  qr_tble1 dw  TRUE,  TRUE, 24*11-19, 24*11-18, 24*12-19, 24*12-18, 24*13-19, 24*13-18, 24*14-19, 24*14-18
  qr_tble2 dw  TRUE, FALSE, 24*14-17, 24*14-16, 24*13-17, 24*13-16, 24*12-17, 24*12-16, 24*11-17, 24*11-16
  qr_tble3 dw  TRUE,  TRUE, 24*11-14, 24*11-13, 24*12-14, 24*12-13, 24*13-14, 24*13-13, 24*14-14, 24*14-13
  qr_tble4 dw  TRUE, FALSE, 24*22-12, 24*22-11, 24*21-12, 24*21-11, 24*20-12, 24*20-11, 24*19-12, 24*19-11
  qr_tble5 dw  TRUE, FALSE, 24*18-12, 24*18-11, 24*17-12, 24*17-11, 24*16-12, 24*16-11, 24*15-12, 24*15-11
  qr_tble6 dw  TRUE, FALSE, 24*14-12, 24*14-11, 24*13-12, 24*13-11, 24*12-12, 24*12-11, 24*11-12, 24*11-11

  nalign 16
  qr_bmp	db 'BM'		; bmp header
		dd size_qr_bmp
		dd 0
		dd 14+40+4*256
		dd 40
		dd 23		; image width
		dd -23		; image height is negative in order to draw the image from top to bottom
		dw 1
		dw 8		; 8 bits per pixel
		dd 0
		dd 24*23
		dd 0
		dd 0
		dd 2
		dd 2
		; palette entries
		db 0xFF, 0xFF, 0xFF, 0x00	; white
		db 0x00, 0xFF, 0x00, 0x00	; green
		times 254 dd 0

		; in fact the image is 24x23 pixels, because bmp format requires padding
  qr_image      db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,\
		   0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,\
		   0,1,0,1,1,1,0,1,0,0,1,1,1,1,0,1,0,1,1,1,0,1,0,0,\
		   0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,\
		   0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,0,\
		   0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,\
		   0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,0,\
		   0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,1,1,0,0,1,1,0,1,1,1,0,0,1,1,1,1,0,0,1,1,0,0,\
		   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
		   0,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,\
		   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

  size_qr_bmp = $ - qr_bmp	

  nalign   16
  alog     rd 512		; reserve 2 KB
  logt	   rd 512
  rspoly   rd 32
  qrdata   rb SIZE_QRDATA
  qrecc    rb SIZE_QRECC

section '.idata' import data readable writeable

  library kernel ,'KERNEL32.DLL'

  import kernel,\
	 CloseHandle,'CloseHandle',\
	 CreateFile,'CreateFileA',\
	 ExitProcess,'ExitProcess',\
	 GetStdHandle,'GetStdHandle',\
	 WriteFile,'WriteFile'

section '.reloc' fixups data readable discardable
