``````;name: daysinmonth.asm
;
;build: nasm -felf64 daysinmonth.asm -o daysinmonth.o
;
;description: Allthough easy solvable with a table of monthnumber as index and the days as values,
;             I like demonstrate the calculation of the number of days in a month the binary way
;             using one register only.  My inspiration comes from the book Hacker's Delight.
;             Using vector registers we can put in all months and calculate the days in all months
;             at once.  Therefor this algorithm is nice because it's branch free.
;
;remark: February has in this program always 28 days. Combined with a leapyear you can add one day.
;        I don't have a nice formula for this algorithm, it's merely a trial and error combined with
;        binary logic.  The ame was to create branch free routines without looking up values in a table.
;
; The first phase figures out if a month has at least 28 or sure 29 days.  Only february has 28 days.
; We can't just rely on a sudden bit because, when we look at the month values in a cyclic manner, we see
; that december,january and july and august have 31 days. An xor with bit 3 however give the right value
; in bit 0 as indication that a month has one day less or more than it's predecesor.  Adding 28 gives us
; a cycle of 29-28-29-28-29-28-29-29-28-29-28-29.
;
; month         nr     al     ah = al   ah >> 3   ah = ah xor al  ah = ah and 1   ah = ah or 00011100  days
; ----------   ---  --------  --------  --------  --------------  --------------  -------------------  ----
; january       1   00000001  00000001  00000000     00000001       00000001          00011101          29
; february      2   00000010  00000010  00000000     00000010       00000000          00011100          28
; march         3   00000011  00000011  00000000     00000011       00000001          00011101          29
; april         4   00000100  00000100  00000000     00000100       00000000          00011100          28
; may           5   00000101  00000101  00000000     00000101       00000001          00011101          29
; june          6   00000110  00000110  00000000     00000110       00000000          00011100          28
; july          7   00000111  00000111  00000000     00000111       00000001          00011101          29
; august        8   00001000  00001000  00000001     00001001       00000001          00011101          29
; september     9   00001001  00001001  00000001     00001000       00000000          00011100          28
; october      10   00001010  00001010  00000001     00001011       00000001          00011101          29
; november     11   00001011  00001011  00000001     00001010       00000000          00011100          28
; december     12   00001100  00001100  00000001     00001101       00000001          00011101          29
;
; The second phase eliminates the month february which doesn't need two additional days.  Doing so we got for each
; month its number of days in rax in return.
;
; month         nr     al     al = al - 2   al or 0xF0     al - 1   al >> 3    value in ah   al and 2  ah = ah or al  days
; ----------   ---  --------  ------------  -----------   --------  ---------  -----------   --------  -------------  ----
; january       1   00000001    11111111     11111111     11111110  00011111     00011101    00000010    00011111      31
; february      2   00000010    00000000     11110000     11101111  00011101     00011100    00000000    00011100      28
; march         3   00000011    00000001     11110001     11110000  00011110     00011101    00000010    00011111      31
; april         4   00000100    00000010     11110010     11110001  00011110     00011100    00000010    00011110      30
; may           5   00000101    00000011     11110011     11110010  00011110     00011101    00000010    00011111      31
; june          6   00000110    00000100     11110100     11110011  00011110     00011100    00000010    00011110      30
; july          7   00000111    00000101     11110101     11110100  00011110     00011101    00000010    00011111      31
; august        8   00001000    00000110     11110110     11110101  00011110     00011101    00000010    00011111      31
; september     9   00001001    00000111     11110111     11110110  00011110     00011100    00000010    00011110      30
; october      10   00001010    00001000     11111000     11110111  00011110     00011101    00000010    00011111      31
; november     11   00001011    00001001     11111001     11111000  00011111     00011100    00000010    00011110      30
; december     12   00001100    00001010     11111010     11111001  00011111     00011101    00000010    00011111      31

bits 64

section .text

global daysinmonth

daysinmonth:
;calculates the number of days in a month, february counts 28 days.
;phase one: figure out if we have more than 28 days
mov     rax,rdi
mov     ah,al               ;monthnumber in ah
shr     ah,3                ;shift bit 3 to position 0 zeroing out all other bits
xor     ah,al               ;xor with month number
and     ah,1                ;mask bit 0 from temp result
;we got now 0 or 1 in ah, indicating that a month has 31 or 30 days
or      ah,28               ;adjust to number of days, ah has 29 or 28
;phase two: find out if we need to add two more days or not
dec     al                  ;decrement month with two
dec     al
or      al,0xF0             ;erase lowest nibble
dec     al                  ;decrement al
shr     al,3                ;bit 4 of al in postion 0
and     al,2                ;elimante all bits except the one on position 1
or      ah,al               ;or this bit in number of days
;ah has now 28,30 or 31 for number of days
shr     ax,8                ;shift result in al
ret                         ;return number of days in AL``````
``````;name: leapyear.asm
;
;Build:    nasm "-felf64" leapyear.asm -l leapyear.lst -o leapyear.o
;
;description: the function calculates if a year in rdi (hexadecimal) is leap or not.
;             rax returns 0 if not leap otherwise 1

bits 64

global leapyear

section .text

leapyear:
push	rbx                ; save used registers
push	rcx
push	rdx
mov		rax, rdi
xor		rcx, rcx           ; assume not leap, rcx = 0
test	rax, 3             ; last two bits 0?
jnz		.@1                ; if not year is not disible by 4 -> no leapyear
inc		rcx                ; assume year is a leapyear, rcx = 1
xor		rdx, rdx           ; prepare rdx for division
mov		rbx, 100           ; year / 100
div		rbx
and		rdx, rdx           ; remainder = 0?
jnz		.@1                ; no, no leapyear
; multiples of 100 aren't leap years except if last two bits
; are zero 0 (divisible by 4) then also divisible by 400
test	rax, 3
jz		.@1                ; yes, leap year
dec		rcx                ; no, not leap year, rcx = 0
.@1:
mov		rax, rcx           ; mov result in RAX
pop		rdx
pop		rcx
pop		rbx
ret``````
``````;name: semester.asm
;
;build: nasm -felf64 semester.asm -o semester.o
;
;description: calculating in which semester a month is.
;             semester: A period or term of six months
;
; month         nr  binary in AL  AL > 3
; ----------   ---  ------------  ------------  -------------
; january       1     00000001      00000010       00000000
; february      2     00000010      00000011       00000000
; march         3     00000011      00000100       00000000
; april         4     00000100      00000101       00000000
; may           5     00000101      00000110       00000000
; june          6     00000110      00000111       00000000
; july          7     00000111      00001000       00000001
; august        8     00001000      00001001       00000001
; september     9     00001001      00001010       00000001
; october      10     00001010      00001011       00000001
; november     11     00001011      00001100       00000001
; december     12     00001100      00001101       00000001
;
; incrementing the value in al gives us the value of the semester of that month.

bits 64

section .text

global semester

semester:
mov     rax,rdi
inc     al              ;s = month + 1
shr     al,3            ;s = s div 8
inc     al              ;s = s + 1
ret                     ;return semester in rax``````
``````;name: quadrimester.asm
;
;
;description: calculating in which quadrimester a month is.
;
; month         nr  binary in al  al > 2
; ----------   ---  ------------  ------------  -------------
; january       1     00000001      00000000       00000000
; february      2     00000010      00000001       00000000
; march         3     00000011      00000010       00000000
; april         4     00000100      00000011       00000000
; may           5     00000101      00000100       00000001
; june          6     00000110      00000101       00000001
; july          7     00000111      00000110       00000001
; august        8     00001000      00000111       00000001
; september     9     00001001      00001000       00000010
; october      10     00001010      00001001       00000010
; november     11     00001011      00001010       00000010
; december     12     00001100      00001011       00000010
;
; incrementing the value in al by one and erasing all bits in ah gives the value of the semester of that month.

bits 64

section .text

; calculates the quadrimester number of a month in rdi
mov     rax,rdi
dec     al              ;q = month - 1
shr     al,2            ;q = q idiv 4
inc     al              ;q = q + 1
``````;name: shiftedmonth.asm
;
;build: nasm -felf64 shiftedmonth.asm -o shiftedmonth.o
;
;description: Calculates the shifted month from a given month in rdi, which can be used to calculate
;             Easter sundays (and most probably more than only Easter sundays).
;             The routine doesn't check for legal month numbers, it takes out the lower four bits to calculate
;             with.
;             Returns shifted month number in rax if rdi is a montnumber otherwise rubish.
;
;  ax <- month number      ax = ax - 3       ax and 111 0100 0000 1111           not al           and al,ah               inc al
;                                                                                                                  = shifted month number
; -------------------  -------------------  ---------------------------  -------------------  -------------------  ----------------------
; 0000 0000 0000 0001  1111 1111 1111 1110     1111 0100 0000 1110       0000 1011 0000 1110  0000 1011 0000 1010   0000 1011 0000 1011
; 0000 0000 0000 0010  1111 1111 1111 1111     1111 0100 0000 1111       0000 1011 0000 1111  0000 1011 0000 1011   0000 1011 0000 1100
; 0000 0000 0000 0011  0000 0000 0000 0000     0000 0000 0000 0000       1111 1111 0000 0000  1111 1111 0000 0000   1111 1111 0000 0001
; 0000 0000 0000 0100  0000 0000 0000 0001     0000 0000 0000 0001       1111 1111 0000 0001  1111 1111 0000 0001   1111 1111 0000 0010
; 0000 0000 0000 0101  0000 0000 0000 0010     0000 0000 0000 0010       1111 1111 0000 0010  1111 1111 0000 0010   1111 1111 0000 0011
; 0000 0000 0000 0110  0000 0000 0000 0011     0000 0000 0000 0011       1111 1111 0000 0011  1111 1111 0000 0011   1111 1111 0000 0100
; 0000 0000 0000 0111  0000 0000 0000 0100     0000 0000 0000 0100       1111 1111 0000 0100  1111 1111 0000 0100   1111 1111 0000 0101
; 0000 0000 0000 1000  0000 0000 0000 0101     0000 0000 0000 0101       1111 1111 0000 0101  1111 1111 0000 0101   1111 1111 0000 0110
; 0000 0000 0000 1001  0000 0000 0000 0110     0000 0000 0000 0110       1111 1111 0000 0110  1111 1111 0000 0110   1111 1111 0000 0111
; 0000 0000 0000 1010  0000 0000 0000 0111     0000 0000 0000 0111       1111 1111 0000 0111  1111 1111 0000 0111   1111 1111 0000 1000
; 0000 0000 0000 1011  0000 0000 0000 1000     0000 0000 0000 1000       1111 1111 0000 1000  1111 1111 0000 1000   1111 1111 0000 1001
; 0000 0000 0000 1100  0000 0000 0000 1001     0000 0000 0000 1001       1111 1111 0000 1001  1111 1111 0000 1001   1111 1111 0000 1010
;
; Looking at the values in al, we notice the shifted mont number. Clearing the other meaningless bits gives this number in rax.

bits 64

section .text

global shiftedmonth

shiftedmonth:
mov     rax,rdi
and     rax,1111b              ;take only lower 4 bits in concern
dec     ax                     ;minus 3
dec     ax
dec     ax
and     ax,1111010000001111b
not     ah
and     al,ah
inc     al
and     ax,1111b               ;only lower 4 bits are relevant for the result
ret``````