; CALENDAR FOR MENUET v1.0
; Written in pure assembler by Ivushkin Andrey aka Willow
;
;
; Created:      November 1, 2004
; Last changed: January 13, 2005
;
; COMPILE WITH FASM

WIN_X equ (150 shl 16+270)
WIN_Y equ (100 shl 16+300)

LINE1	  equ 27 shl 16+16
B_MONTH_X equ 10 shl 16+158
B_Y	  equ LINE1
B_MONTH   equ 63 shl 16+32

B_WBAR_X  equ 10 shl 16+250
B_WBAR_Y  equ 64 shl 16+20
B_WEEK	  equ 30 shl 16+70
B_WX_SHIFT equ 32 shl 16

B_DBAR_X  equ B_WBAR_X
B_DBAR_Y  equ 85 shl 16+190

B_DROP	  equ B_MONTH+16
B_DAYS_Y  equ 100
B_DAYS_SHIFT equ 30

B_YEAR_X  equ 173 shl 16+58
B_YEAR	  equ 188 shl 16+32

B_TODAY_X equ 25 shl 16
B_TODAY_Y equ 48 shl 16+10
B_TODAY   equ 30 shl 16+50

B_SPIN_WIDTH equ 13
B_SPIN_X  equ 234 shl 16+B_SPIN_WIDTH
B_SPIN	  equ 238 shl 16+32

B_DATE_X  equ 26 shl 16+60
B_DATE_Y  equ 275 shl 16+16
B_DATE_BSHIFT equ 80 shl 16
B_DATE	  equ 32 shl 16+280
B_DATE_SHIFT equ 80 shl 16

B_NS_X	  equ 185 shl 16+75
B_NS_Y	  equ 48 shl 16+10
B_NS	  equ 190 shl 16+50

FOCUSABLE equ 5
SKIP	  equ 1

use32		     ; включить 32-битный режим ассемблера

  org	 0x0	     ; адресация с нуля

  db	 'MENUET01'  ; 8-байтный идентификатор MenuetOS
  dd	 0x01	     ; версия заголовка (всегда 1)
  dd	 start	     ; адрес метки, с которой начинается выполнение программ
  dd	 I_END	     ; размер программы
  dd	 0x1000      ; количество памяти
  dd	 0x1000      ; адрес вершины стэка
  dd	 0x0	     ; адрес буфера для строки параметров (не используется)
  dd	 0x0	     ; зарезервировано
include 'lang.inc'
include 'macros.inc' ; уменьшает размер программы
;include 'debug.inc'


macro  ShowFocus field,reg
{
   local  .nofocus, .exit
     cmp  [focus],field
     jne  .nofocus
   if reg eq
     mov  ecx,0x10e7c750;0x10ddeeff
   else
     mov  reg,0x10e7c750;0x10ddeeff
   end if
     jmp  .exit
   .nofocus:
   if reg eq
     mov  ecx,0x10000000
   else
     mov  reg,0x10000000
   end if
   .exit:
}

month_name:
if lang eq ru
     db   8
     db   'Январь  '
     db   'Февраль '
     db   'Март    '
     db   'Апрель  '
     db   'Май     '
     db   'Июнь    '
     db   'Июль    '
     db   'Август  '
     db   'Сентябрь'
     db   'Октябрь '
     db   'Ноябрь  '
     db   'Декабрь '
else if lang eq de
     db   9
     db   'Januar   '
     db   'Februar  '
     db   'M┴rz     '
     db   'April    '
     db   'Mai      '
     db   'Juni     '
     db   'Juli     '
     db   'August   '
     db   'September'
     db   'Oktober  '
     db   'November '
     db   'Dezember '
else if lang eq fr
     db   9
     db   'Janvier  '
     db   'Fevrier  '
     db   'Mars     '
     db   'Avril    '
     db   'Mai      '
     db   'Juin     '
     db   'Juliet   '
     db   'Aout     '
     db   'Septembre'
     db   'Octobre  '
     db   'Novembre '
     db   'Decembre '
else if lang eq fi
     db   9
     db   'Tammikuu '
     db   'Helmikuu '
     db   'Maaliskuu'
     db   'Huhtikuu '
     db   'Toukokuu '
     db   'Kes┴kuu  '
     db   'Hein┴kuu '
     db   'Elokuu   '
     db   'Syyskuu  '
     db   'Lokakuu  '
     db   'Marraskuu'
     db   'Joulukuu '
else
     db   9
     db   'January  '
     db   'February '
     db   'March    '
     db   'April    '
     db   'May      '
     db   'June     '
     db   'July     '
     db   'August   '
     db   'September'
     db   'October  '
     db   'November '
     db   'December '
end if
spinner db '< >'
week_days:
if lang eq ru
     db   2
     db   1
     db   'Пн'
     db   'Вт'
     db   'Ср'
     db   'Чт'
     db   'Пт'
     db   'Сб'
     db   'Вс'
else if lang eq de
     db   2
     db   7
     db   'So'
     db   'Mo'
     db   'Di'
     db   'Mi'
     db   'Do'
     db   'Fr'
     db   'Sa'
else if lang eq fr
     db   3
     db   7
     db   'Dim'
     db   'Lun'
     db   'Mar'
     db   'Mer'
     db   'Jeu'
     db   'Ven'
     db   'Sam'
else if lang eq fi
     db   2
     db   7
     db   'Su'
     db   'Ma'
     db   'Ti'
     db   'Ke'
     db   'To'
     db   'Pe'
     db   'La'
else
     db   3
     db   7
     db   'Sun'
     db   'Mon'
     db   'Tue'
     db   'Wen'
     db   'Thi'
     db   'Fri'
     db   'Sat'
end if

str2int:
    xor  eax,eax
    lodsb
    mov  ebx,eax
    shr  ebx,4
    and  eax,0xf
    imul ebx,10
    add  al,bl
    ret

start:
    mcall 29
    mov  [datestr],eax
    mov  esi,datestr
    call str2int
    add  eax,1900
    mov  [Year],eax
    call str2int
    dec  eax
    mov  [Month],eax
    call str2int
    mov  [day_sel],eax
    test byte[esi],0
    jnz  .no2000
    add  [Year],100
  .no2000:
    jmp  upd		; здесь начинается выполнение программы
red:			; перерисовать окно

    call draw_window	; вызываем процедуру отрисовки окна

still:			; ГЛАВНЫЙ ЦИКЛ ПРОГРАММЫ

    mov  eax,10 	; функция 10 - ждать события
    int  0x40		; вызываем систему
  .evt:
    mov  ebp,[focus]
    cmp  eax,1		; перерисовать окно ?
    je	 red		; если да - на метку red
    cmp  eax,2		; нажата клавиша ?
    je	 key		; если да - на key
    cmp  eax,3		; нажата кнопка ?
    je	 button 	; если да - на button

    jmp  still		; если другое событие - в начало цикла

  key:			; нажата клавиша на клавиатуре
    mov  eax,2		; функция 2 - считать код символа
    int  0x40		; вызов системы
    cmp  ah,9
    jne  no_tab
  .tab:
    cmp  ebp,FOCUSABLE
    je	 foc_cycle
    inc  [focus]
  upd:
    call calculate
    jmp  red
  foc_cycle:
    mov  [focus],2
    jmp  upd
  no_tab:
    push eax
    shr  eax,8
    mov  ecx,12
    mov  edi,Fkeys
    repne scasb
    pop  eax
    jnz  .noFkey
    sub  edi,Fkeys+1
    mov  [Month],edi
    jmp  upd
  .noFkey:
    cmp  ebp,4
    jne  no_spinner
    cmp  ah,176
    je	 year_dec
    cmp  ah,179
    je	 year_inc
  no_spinner:
    cmp  ebp,2
    jne  .nomonth
    cmp  ah,177
    je	 noclose.drop
    jmp  still
  .nomonth:
    cmp  ebp,3
    je	 noy_up.year_evt
    cmp  ebp,5
    jne  still
    mov  ebx,[day_sel]
    cmp  ah,176 	; left arrow
    jb	 still
    cmp  ah,179
    ja	 still
    shr  eax,8
    sub  eax,176
    movsx ecx,byte[day_bounds+eax*2]
    movzx eax,byte[day_bounds+eax*2+1]
    add  ecx,ebx
    test eax,eax
    jz	 .chk0
    cmp  ecx,eax
    ja	 still
  .ok:
    mov  [day_sel],ecx
    call draw_days
    jmp  still		; вернуться к началу цикла
  .chk0:
    cmp  ecx,eax
    jle  still
    jmp  .ok

day_bounds db -1,0,7,0,-7,0,1,0 ; left,down,up,right

  button:		; нажата кнопка в окне программы
    mov  eax,17 	; 17 - получить идентификатор нажатой кнопки
    int  0x40		; вызов системы
    movzx ebx,ah
    cmp  ah,200
    jbe  nodayselect
    sub  ah,200
    mov  byte[day_sel],ah
    cmp  ebp,5
    jne  .redraw
    call draw_days
    jmp  still
  .redraw:
    mov  [focus],5
    jmp  red
  nodayselect:
    cmp  ah,100
    jb	 no_list
    sub  ah,100
    mov  byte[Month],ah
    mov  [focus],2
    jmp  upd
  no_list:
    cmp  ah,1		; идентификатор == 1 ?
    jne  noclose	; если нет - иди вперёд на noclose
  close:
    or	 eax,-1 	; выход из программы
    int  0x40		; вызов системы

  noclose:
    cmp  ah,2		; drop down list
    jne  no_dropdn
  .drop:
    mov  [focus],2
    cmp  [dropped],al	; ==0
    jne  red
    call draw_window
    mov  edx,1 shl 31+231
    mov  ecx,31
    mov  eax,8
  .bremove:
    int  0x40
    dec  edx
    loop .bremove
    call draw_dropdown
    jmp  still
  no_dropdn:
    cmp  ah,3		; year -1
    jne  noy_dn
  year_dec:
    dec  [Year]
    mov  [focus],4
    jmp  upd
  noy_dn:
    cmp  ah,4		; year+1
    jne  noy_up
  year_inc:
    inc  [Year]
    mov  [focus],4
    jmp  upd
  noy_up:
    cmp  ah,5
    jne  noy_click
    mov  [focus],3
    call draw_window
  .still:
    mcall 10
    cmp  eax,2
    jne  still.evt
    mcall 2
  .year_evt:
    mov  ebx,10
    cmp  ah,9
    je	 key.tab
    cmp  ah,8		; backspace
    jne  .nobsp
    mov  eax,[Year]
    xor  edx,edx
    div  ebx
  .ch_year:
    mov  [Year],eax
    call draw_year
    jmp  .still
  .nobsp:
    cmp  ah,13		; enter
    je	 upd
    cmp  ah,182
    jne  .noclear	; del
    xor  eax,eax
    jmp  .ch_year
  .noclear:
    cmp  ah,48
    jb	 .still
    cmp  ah,57
    ja	 .still
    cmp  [Year],1000
    jae  .still
    shr  eax,8
    lea  ecx,[eax-48]
    mov  eax,[Year]
    imul eax,ebx
    add  eax,ecx
    jmp  .ch_year
  noy_click:
    cmp  ah,10
    jne  start
    xor  [new_style],1
    jmp  upd


;   *********************************************
;   *******  ОПРЕДЕЛЕНИЕ И ОТРИСОВКА ОКНА *******
;   *********************************************

draw_window:

    mov  eax,12 		   ; функция 12: сообщить ОС об отрисовке окна
    mov  ebx,1			   ; 1 - начинаем рисовать
    int  0x40
				   ; СОЗДАЁМ ОКНО
    xor  eax,eax		   ; функция 0 : определить и отрисовать окно
    mov  ebx,WIN_X
  if SKIP eq 0
    mov  ecx,WIN_Y
  else
    mov  ecx,WIN_Y-15
  end if
    mov  edx,0x03aabbcc 	   ; цвет рабочей области  RRGGBB,8->color gl
    mov  esi,0x805080d0 	   ; цвет полосы заголовка RRGGBB,8->color gl
    mov  edi,0x005080d0 	   ; цвет рамки            RRGGBB
    int  0x40
    call draw_week

    mov  eax,8
    mov  esi,0x05080d0
  if SKIP eq 0
    mov  ebx,B_DATE_X
    mov  ecx,B_DATE_Y
    mov  edx,eax
    int  0x40
    inc  edx
    add  ebx,B_DATE_BSHIFT
    int  0x40
    inc  edx
  else
    mov  edx,10
  end if
    or	 edx,1 shl 29+1 shl 30
    mov  ebx,B_NS_X
    mov  ecx,B_NS_Y
    int  0x40
    add  edx,1-1 shl 29
    mov  ebx,B_TODAY_X+8*(today_end-today_msg)
    mov  ecx,B_TODAY_Y
    int  0x40
    mov  ecx,B_Y
    mov  ebx,B_MONTH_X
    mov  edx,2
    int  0x40
    mov  ebx,B_SPIN_X
    inc  edx
    int  0x40
    add  ebx,B_SPIN_WIDTH shl 16
    inc  edx
    int  0x40
    call draw_days

				   ; ЗАГОЛОВОК ОКНА
    mov  eax,4			   ; функция 4 : написать в окне текст
    mov  ebx,8*65536+8		   ; [x] *65536 + [y]
    mov  ecx,0x10ddeeff 	   ; шрифт 1 и цвет ( 0xF0RRGGBB )
    mov  edx,zagolovok		   ; адрес строки
    mov  esi,zag_konets-zagolovok  ; и её длина
    int  0x40

 if SKIP eq 0
    mov  ebx,B_DATE
    mov  edx,datebut
    mov  esi,9
    btc  ecx,28
    int  0x40
    add  ebx,B_DATE_SHIFT
    add  edx,esi
    int  0x40
 end if
    mov  edx,n_style
    mov  esi,ns_end-n_style
    mov  ebx,B_NS
    cmp  [new_style],1
    je	 .high
    mov  ecx,0xa0a0a0
    jmp  .int
  .high:
    mov  ecx,0xac0000;d048c8
  .int:
    int  0x40

    mov  ecx,0xd048c8
    mov  edx,today_msg
    mov  ebx,B_TODAY
    mov  esi,today_end-today_msg
    int  0x40

    mov  ebx,B_SPIN
    mov  edx,spinner
    mov  esi,3
    ShowFocus 4
    int  0x40

    mov  edx,[Month]
    movzx  esi,byte[month_name]
    imul edx,esi
    add  edx,month_name+1
    mov  ebx,B_MONTH
    ShowFocus 2
    int  0x40

    call draw_year
    mov  [dropped],0
    mov  eax,12 		   ; функция 12: сообщить ОС об отрисовке окна
    mov  ebx,2			   ; 2, закончили рисовать
    int  0x40
    ret 			   ; выходим из процедуры

draw_year:
    mcall 8,B_YEAR_X,B_Y,5,0x05080d0
    ShowFocus 3,esi
    mcall 47,0x40001,Year,B_YEAR
    ret

draw_dropdown:
    mov  [dropped],1
    push [Month]
    pop  [focus]
    add  [focus],100
    mov  ecx,12
    mov  edx,100
    push dword month_name+1
    push dword B_DROP
    push dword B_Y+16 shl 16
  .ddd_loop:
    mov  edi,edx
    push ecx
    mov  ebx,B_MONTH_X
    mov  ecx,[esp+4]
    mov  esi,0x6f9fef
    mov  eax,8
    int  0x40
    shr  eax,1
    mov  ebx,[esp+8]
    xchg edx,[esp+12]
    movzx esi,byte[month_name]
    ShowFocus edi
    int  0x40
    add  edx,esi
    xchg edx,[esp+12]
    add  dword[esp+8],16
    add  dword[esp+4],16 shl 16
    inc  edx
    pop  ecx
    loop .ddd_loop
    add  esp,12
    ret

draw_week:
    mov  eax,13
    mov  ebx,B_WBAR_X
    mov  ecx,B_WBAR_Y
    mov  edx,0x90a0b0
    int  0x40
    movzx esi,byte[week_days]
    movzx edi,byte[week_days+1]
    mov  ebx,B_WEEK
    mov  ecx,7
    mov  edx,week_days+2
    mov  eax,4
  .week:
    push ecx
    cmp  ecx,edi
    je	 .holiday
    mov  ecx,0x10000000
    jmp  .noholiday
  .holiday:
    mov  ecx,0x10cc1010
  .noholiday:
    int  0x40
    add  edx,esi
    add  ebx,B_WX_SHIFT
    pop  ecx
    loop .week
    ret

draw_days:
    mov  eax,13
    mov  ebx,B_DBAR_X
    mov  ecx,B_DBAR_Y
    mov  edx,0xe0e0e0
    int  0x40
    call count_days
    cmp  ecx,[day_sel]
    jae  .ok
    mov  [day_sel],ecx
  .ok:
    mov  [number],0
    mov  eax,47
    mov  edx,B_DAYS_Y
    mov  ebx,0x20001
    mov  edi,[firstday]
  .dayloop:
    push ecx
    movzx edx,dx
    mov  esi,edi
    shl  esi,21
    lea  edx,[edx+esi+30 shl 16]
    mov  ecx,edi
    add  cl,[week_days+1]
    cmp  ecx,7
    je	 .holiday
    mov  esi,0x10000000
    jmp  .noholiday
  .holiday:
    mov  esi,0x10cc1010
  .noholiday:
    mov  ecx,number
    inc  dword[ecx]
    pusha
    mov  ebx,edx
    mov  bx,20
    sub  ebx,3 shl 16
    shrd ecx,edx,16
    mov  cx,20
    sub  ecx,7 shl 16
    mov  edx,[number]
    cmp  edx,[day_sel]
    je	 .draw_sel
    mov  esi,0xe0e0e0
    jmp  .draw_but
  .draw_sel:
    mov  esi,0x5080d0
    cmp  [focus],5
    jne  .draw_but
    mov  esi,0xef7840;0xe26830
  .draw_but:
    add  edx,200+1 shl 29
    mov  eax,8
    int  0x40
    popa
    int  0x40
    pop  ecx
    inc  edi
    cmp  edi,7
    jne  .nowrap
    xor  edi,edi
    add  dx,B_DAYS_SHIFT
  .nowrap:
    loop .eloop
    jmp  .ex
  .eloop:
    jmp  .dayloop
  .ex:
    ret

count_days:    ; ecx -days in month
    call is_leap_year
    mov  ecx,[Month]
    mov  eax,1
    movzx ecx,byte[day_count+ecx]
    add  ecx,28
    cmp  eax,[leap_year]
    jne  .noleap
    cmp  eax,[Month]
    jne  .noleap
    inc  ecx
  .noleap:
    mov  [day_bounds+3],cl
    mov  [day_bounds+7],cl
    ret

is_leap_year:
    mov  [leap_year],0
    mov  eax,[Year]
    mov  bl,100
    div  bl	     ; ah=Year mod 100, al=Year%100
    test ah,ah
    jz	.century
    shr  ax,8	     ; ax - last 2 digits
  .century:
    test al,11b
    jnz  .noleap
    inc  [leap_year]
  .noleap:
    ret

calculate:
    mov  ebx,[Year]
    mov  eax,[Month]
    sub  eax,2
    jge  .nojf
    dec  ebx
    add  eax,12
  .nojf:
    add  eax,4
    xor  edx,edx
    mov  ecx,153
    imul cx
    mov  ecx,5
    div  cx
    inc  eax
    mov  ecx,365
    imul ecx,ebx
    add  eax,ecx
    mov  ecx,ebx
    shr  ecx,2
    add  eax,ecx
    dec  eax
    cmp  [new_style],0
    je	 .nonew
    add  eax,2
    xchg eax,ebx
    mov  ecx,100
    xor  edx,edx
    div  cx
    sub  ebx,eax
    shr  eax,2
    add  ebx,eax
  .nonew:
    add  ebx,5
    mov  eax,ebx
    xor  edx,edx
    movzx ebx,byte[week_days+1]
    sub  eax,ebx
    inc  eax
    mov  ebx,7
    div  bx
    mov  [firstday],edx
    ret

; Здесь находятся данные программы:

; интерфейс программы двуязычный - задайте язык в macros.inc
day_count db 3,0,3,2,3,2,3,3,2,3,2,3
Fkeys	  db 210,211,212,213,214,215,216,217,208,209,228,159

zagolovok:		 ; строка заголовка
if lang eq ru
     db   'КАЛЕНДАРЬ'
else if lang eq ge
     db   'KALENDER'
else if lang eq fr
     db   'CALENDRIER'
else
     db   'CALENDAR'
end if
zag_konets:		 ; и её конец
if SKIP eq 0
datebut:
if lang eq ru
     db   '1-я дата '
     db   '2-я дата '
else if lang eq fr
     db   '1ere date'
     db   '2eme date'
else if lang eq ge
     db   ' Datum 1 '
     db   ' Datum 2 '
else
     db   '1st date '
     db   '2nd date '
end if
end if
n_style:
if lang eq ru
     db   'Новый стиль'
else if lang eq de
     db   'Neuer Stil'
else if lang eq fr
     db   'Nouveau'
else
     db   'New style'
end if
ns_end:
today_msg:
if lang eq ru
     db   'Сегодня'
else if lang eq ge
     db   'Heute'
else if lang eq fr
     db   "Aujourd'hui"
else
     db   'Today'
end if
today_end:
focus dd  3
new_style dd 1
dropped db 0

I_END:	; конец программы
firstday  dd ?
Year dd   ?
Month dd  ?
day_sel   dd ?
all_days  dd ?

datestr   dd  ?
leap_year dd ?
number	  dd ?
year_input dd ?
