577 lines
17 KiB
NASM
577 lines
17 KiB
NASM
; SPDX-License-Identifier: GPL-2.0-only
|
||
;
|
||
; Piano - Toy Piano
|
||
; Copyright (C) 2019-2025 KolibriOS team
|
||
;
|
||
; Authors: Antonio and Leency
|
||
|
||
; ====================================================================
|
||
|
||
use32
|
||
org 0
|
||
|
||
; ====================================================================
|
||
|
||
db "MENUET01"
|
||
dd 1
|
||
dd START
|
||
dd I_END
|
||
dd MEM
|
||
dd STACKTOP
|
||
dd 0
|
||
dd app_path_buf
|
||
|
||
; ====================================================================
|
||
|
||
include "../../macros.inc"
|
||
include "../../KOSfuncs.inc"
|
||
include "../../encoding.inc"
|
||
|
||
; ====================================================================
|
||
|
||
START:
|
||
mcall SF_KEYBOARD, SSF_SET_INPUT_MODE, 1
|
||
call build_keymap_path
|
||
call load_keymap
|
||
jmp redraw
|
||
|
||
; ====================================================================
|
||
|
||
still:
|
||
mcall SF_WAIT_EVENT
|
||
|
||
cmp al, EV_REDRAW
|
||
je redraw
|
||
cmp al, EV_KEY
|
||
je key
|
||
cmp al, EV_BUTTON
|
||
je button
|
||
|
||
jmp still
|
||
|
||
; ====================================================================
|
||
|
||
key:
|
||
mcall SF_GET_KEY
|
||
|
||
; drop keys release
|
||
test ah, 0x80
|
||
jnz still
|
||
|
||
; open help window on Escape
|
||
cmp ah, KEY_ESC
|
||
je .help
|
||
|
||
; if Shift is pressed - increase note_id by 0x40
|
||
movzx ecx, ah
|
||
mcall SF_KEYBOARD, SSF_GET_CONTROL_KEYS
|
||
xor edx, edx
|
||
|
||
test al, KEY_SHIFT
|
||
jz .key_map_play
|
||
|
||
mov dl, INC_SHIFT
|
||
|
||
; play note if valid key pressed
|
||
.key_map_play:
|
||
mov ah, [keymap + ecx]
|
||
cmp ah, BAD_NOTE
|
||
je still
|
||
|
||
add ah, dl
|
||
jmp play
|
||
|
||
; help window thread creation
|
||
.help:
|
||
cmp [hw_tid], 0
|
||
jne still
|
||
|
||
sub esp, 1024
|
||
mov ebx, esp
|
||
mcall SF_THREAD_INFO, ebx, -1
|
||
mov eax, [esp + process_information.box.left]
|
||
mov edx, [esp + process_information.box.top]
|
||
add esp, 1024
|
||
shl eax, 16
|
||
mov ax, dx
|
||
mov [mw_pos], eax
|
||
|
||
mcall SF_CREATE_THREAD, 1, help_thread, help_stack_top
|
||
cmp eax, -1
|
||
je still
|
||
|
||
mov [hw_tid], eax
|
||
jmp still
|
||
|
||
button:
|
||
mcall SF_GET_BUTTON
|
||
|
||
cmp ah, 1
|
||
je .exit
|
||
|
||
; note_id is always button_id - 0x10
|
||
sub ah, 0x10
|
||
mov dl, ah
|
||
|
||
; if Shift is pressed - increase note_id by 0x40
|
||
mcall SF_KEYBOARD, SSF_GET_CONTROL_KEYS
|
||
test al, KEY_SHIFT
|
||
jz .no_shift
|
||
add dl, INC_SHIFT
|
||
|
||
.no_shift:
|
||
mov ah, dl
|
||
jmp play
|
||
|
||
.exit:
|
||
mcall SF_TERMINATE_PROCESS
|
||
|
||
; =======================================================================
|
||
|
||
play:
|
||
mov [melody + 1], ah
|
||
mcall SF_SPEAKER_PLAY, SF_SPEAKER_PLAY, , , melody
|
||
|
||
jmp still
|
||
|
||
; =======================================================================
|
||
|
||
build_keymap_path:
|
||
; build path: app_dir + 'piano.map'
|
||
lea esi, [app_path_buf + 2]
|
||
lea edi, [app_path_buf]
|
||
.copy:
|
||
lodsb
|
||
stosb
|
||
test al, al
|
||
jnz .copy
|
||
; edi is one past '\0'; scan backward to the last '/'
|
||
.find_slash:
|
||
dec edi
|
||
cmp byte [edi], '/'
|
||
jne .find_slash
|
||
inc edi
|
||
|
||
; add 'piano.map' to the path
|
||
mov eax, 'pian'
|
||
stosd
|
||
mov eax, 'o.ma'
|
||
stosd
|
||
mov eax, 'p'
|
||
stosd
|
||
ret
|
||
|
||
; =======================================================================
|
||
|
||
load_keymap:
|
||
mcall SF_FILE, keymap_info
|
||
test eax, eax
|
||
jnz .notify
|
||
cmp ebx, 256
|
||
je .done
|
||
.notify:
|
||
mcall SF_FILE, notify_info
|
||
.done:
|
||
ret
|
||
|
||
; =======================================================================
|
||
|
||
redraw:
|
||
mcall SF_REDRAW, SSF_BEGIN_DRAW
|
||
mcall SF_STYLE_SETTINGS, SSF_GET_COLORS, sc, sizeof.system_colors
|
||
mcall , SSF_GET_SKIN_HEIGHT,
|
||
|
||
mov ecx, eax
|
||
add ecx, WN_MAIN.Y shl 16 + WN_MAIN.H
|
||
mov edx, [sc.work]
|
||
or edx, 0x34000000 ; skin, no resize, caption, client area
|
||
mcall SF_CREATE_WINDOW, <WN_MAIN.X, WN_MAIN.W>, , , , cp_main
|
||
|
||
; white buttons rows
|
||
mov eax, SF_DEFINE_BUTTON ; constant syscall number
|
||
mov ebx, BT_WHITE.X shl 16 + BT_WHITE.W ; packed <x, width>, x starts at 0
|
||
mov ecx, BT_WHITE.Y shl 16 + BT_WHITE.H ; packed <y, height>, constant
|
||
mov edi, white_button_colors
|
||
mov ebp, white_button_ids
|
||
|
||
.white_buttons_loop:
|
||
cmp edi, end_white_button_colors
|
||
jae .black_buttons_setup
|
||
|
||
movzx edx, byte[ebp]
|
||
mov esi, [edi]
|
||
mcall
|
||
|
||
mov ecx, (BT_WHITE.Y + BT_WHITE.H) shl 16 + BT_WHITE.H
|
||
movzx edx, byte[ebp + 1]
|
||
mov esi, [edi + 4]
|
||
mcall
|
||
|
||
mov ecx, BT_WHITE.Y shl 16 + BT_WHITE.H
|
||
add ebx, BT_WHITE.W shl 16
|
||
|
||
add edi, WT_SIZE
|
||
add ebp, 2
|
||
|
||
jmp .white_buttons_loop
|
||
|
||
; black buttons rows
|
||
.black_buttons_setup:
|
||
|
||
mov esi, 0x00221100 ; constant color
|
||
mov edi, black_buttons
|
||
|
||
.black_buttons_loop:
|
||
cmp edi, black_buttons_end
|
||
jae .black_buttons_loop_end
|
||
|
||
movzx ebx, byte [edi]
|
||
imul ebx, BT_WHITE.W
|
||
add ebx, BT_BLACK.X
|
||
shl ebx, 16
|
||
mov bx, BT_BLACK.W
|
||
|
||
movzx edx, byte [edi + 1]
|
||
|
||
mov ecx, (BT_WHITE.Y + BT_BLACK.Y) shl 16 + BT_BLACK.H ; top row
|
||
add edx, 0x20
|
||
mcall
|
||
|
||
mov ecx, (BT_WHITE.Y + BT_WHITE.H + BT_BLACK.Y) shl 16 + BT_BLACK.H ; bottom row
|
||
sub edx, 0x20
|
||
mcall
|
||
|
||
add edi, BL_SIZE
|
||
jmp .black_buttons_loop
|
||
|
||
.black_buttons_loop_end:
|
||
|
||
; notes labels
|
||
mcall SF_DRAW_TEXT, <RT_NOTES.X, RT_NOTES.Y>, 0x90FFFFFF, lb_notes
|
||
|
||
mcall SF_REDRAW, SSF_END_DRAW
|
||
jmp still
|
||
|
||
; =======================================================================
|
||
|
||
help_thread:
|
||
mcall SF_SET_EVENTS_MASK, EVM_REDRAW + EVM_BUTTON
|
||
jmp .redraw
|
||
|
||
.still:
|
||
mcall SF_WAIT_EVENT
|
||
|
||
cmp al, EV_REDRAW
|
||
je .redraw
|
||
cmp al, EV_BUTTON
|
||
je .button
|
||
|
||
jmp .still
|
||
|
||
.button:
|
||
mcall SF_GET_BUTTON
|
||
cmp ah, 1
|
||
jne .still
|
||
|
||
mov dword [hw_tid], 0
|
||
mcall SF_TERMINATE_PROCESS
|
||
|
||
.redraw:
|
||
mcall SF_REDRAW, SSF_BEGIN_DRAW
|
||
mcall SF_STYLE_SETTINGS, SSF_GET_COLORS, sc, sizeof.system_colors
|
||
mcall , SSF_GET_SKIN_HEIGHT,
|
||
|
||
mov ebp, eax
|
||
mov ebx, [mw_pos]
|
||
mov ecx, ebx
|
||
shr ebx, 16
|
||
and ecx, 0xFFFF
|
||
|
||
add ebx, WN_HELP.X
|
||
shl ebx, 16
|
||
mov bx, WN_HELP.W
|
||
|
||
add ecx, WN_HELP.Y
|
||
add ecx, ebp
|
||
shl ecx, 16
|
||
mov cx, WN_HELP.H
|
||
|
||
mov edx, [sc.work]
|
||
or edx, 0x34000000 ; skin, no resize, caption, client area
|
||
mcall SF_CREATE_WINDOW, , , , , cp_help
|
||
|
||
mov eax, SF_DRAW_TEXT
|
||
mov ecx, [sc.work_text]
|
||
or ecx, 0x90000000 ; null terminator, 8x16 OLE
|
||
mov ebx, RT_HELP.X shl 16 + RT_HELP.Y
|
||
mov edi, help_texts
|
||
|
||
; draw all help text in rows
|
||
.help_text_loop:
|
||
mov edx, [edi]
|
||
test edx, edx
|
||
jz .help_text_loop_done
|
||
|
||
mcall ; draw text
|
||
|
||
add ebx, RT_HELP.H
|
||
add edi, 4
|
||
jmp .help_text_loop
|
||
|
||
.help_text_loop_done:
|
||
mcall SF_REDRAW, SSF_END_DRAW
|
||
jmp .still
|
||
|
||
; =======================================================================
|
||
|
||
sc system_colors
|
||
|
||
mw_pos dd 0
|
||
hw_tid dd 0
|
||
|
||
WN_MAIN RECT 32, 32, BT_WHITE.W * 15 + 10, BT_WHITE.H * 2 + (5 + 4)
|
||
|
||
BT_WHITE RECT 0, 4, 48, 100
|
||
BT_BLACK RECT 33, 0, 30, 50
|
||
|
||
RT_HELP RECT 8, 8, 0, 24
|
||
|
||
KEY_ESC = 0x01
|
||
KEY_SHIFT = 0x03
|
||
INC_SHIFT = 0x40
|
||
|
||
; =======================================================================
|
||
|
||
if lang eq ru_RU
|
||
|
||
cp_main cp866 "Детское Пианино [Escape - Открыть Справку]", 0
|
||
cp_help cp866 "Детское Пианино - Справка", 0
|
||
|
||
lb_help1 cp866 "Клавиши от 1 до = и от Q до ] - верхний ряд.", 0
|
||
lb_help2 cp866 "Клавиши от A до Enter и от Z до /, \ и Backspace - нижний ряд.", 0
|
||
lb_help3 cp866 "Удерживайте Shift для повышения октавы на 4.", 0
|
||
lb_help4 cp866 "Раскладку клавиатуры можно изменить через файл piano.map.", 0
|
||
lb_help5 cp866 "Он должен содержать 256 байт, каждый индекс - скан-код, а значение - нота.", 0
|
||
lb_help6 cp866 "Должен звучать встроенный динамик ПК (не колонки).", 0
|
||
|
||
lb_notes cp866 "ДО РЕ МИ ФА СОЛЬ ЛЯ СИ ДО", 0
|
||
|
||
nf_map cp866 "'Детское Пианино\npiano.map отсутствует или повреждён, используется раскладка по умолчанию' -tW", 0
|
||
|
||
WN_HELP RECT 14, 13, 617, 178
|
||
RT_NOTES RECT 16, 183, 0, 0
|
||
|
||
else if lang eq es_ES
|
||
|
||
cp_main cp850 "Piano de Juguete [Escape - Abrir Ayuda]", 0
|
||
cp_help cp850 "Piano de Juguete - Ayuda", 0
|
||
|
||
lb_help1 cp850 "Teclas de 1 a = y de Q a ] - fila superior.", 0
|
||
lb_help2 cp850 "Teclas de A a Enter y de Z a /, \ y Backspace - fila inferior.", 0
|
||
lb_help3 cp850 "Mantenga Shift para subir 4 octavas.", 0
|
||
lb_help4 cp850 "La distribución del teclado se puede cambiar mediante el archivo piano.map.", 0
|
||
lb_help5 cp850 "Debe contener 256 bytes: cada índice es un código de escaneo y el valor es una nota.", 0
|
||
lb_help6 cp850 "Deberias oir el altavoz interno del PC (no los externos).", 0
|
||
|
||
lb_notes cp850 "DO RE MI FA SOL LA SI DO", 0
|
||
|
||
nf_map cp850 "'Piano de Juguete\nFalta piano.map o es inválido; se usa el mapa de teclas predeterminado' -tW", 0
|
||
|
||
WN_HELP RECT 14, 13, 697, 178
|
||
RT_NOTES RECT 16, 183, 0, 0
|
||
|
||
else
|
||
|
||
cp_main db "Toy Piano [Escape - Open Help]", 0
|
||
cp_help db "Toy Piano - Help", 0
|
||
|
||
lb_help1 db "Keys from 1 to = and from Q to ] - top row.", 0
|
||
lb_help2 db "Keys from A to Enter and from Z to /, \ and Backspace - bottom row.", 0
|
||
lb_help3 db "Hold Shift to raise the octave by 4.", 0
|
||
lb_help4 db "Keyboard layout can be changed via the piano.map file.", 0
|
||
lb_help5 db "It must contain 256 bytes - each index is scancode, and value is note.", 0
|
||
lb_help6 db "You should hear the built-in PC speaker (not external speakers).", 0
|
||
|
||
lb_notes db "C D E F G A B C", 0
|
||
|
||
nf_map db "'Toy Piano\npiano.map is missing or invalid, using default keymap' -tW", 0
|
||
|
||
WN_HELP RECT 14, 13, 585, 178
|
||
RT_NOTES RECT 20, 183, 0, 0
|
||
|
||
end if
|
||
|
||
; =======================================================================
|
||
|
||
WT_SIZE = 8 ; white buttons entry size
|
||
BL_SIZE = 2 ; black buttons entry size
|
||
|
||
; white button colors
|
||
white_button_colors: ; 1 top, 1 bottom, 2 top, 2 bottom, N top, N bottom
|
||
dd 0xFF7A74, 0x702050
|
||
dd 0x907040, 0x683638
|
||
dd 0xA08050, 0x784648
|
||
dd 0xB09060, 0x885658
|
||
dd 0xC0A070, 0x986668
|
||
dd 0xD0B080, 0xA87678
|
||
dd 0xE0C090, 0xB88688
|
||
|
||
dd 0xFFA97C, 0x880040
|
||
dd 0xAF8D8D, 0x90622B
|
||
dd 0xBF9D9D, 0xA0723B
|
||
dd 0xCFADAD, 0xB0824B
|
||
dd 0xDFBDBD, 0xC0925B
|
||
dd 0xEFCDCD, 0xD0A26B
|
||
dd 0xFFDDDD, 0xE0B27B
|
||
|
||
dd 0xFFE558, 0xFF7A74
|
||
end_white_button_colors:
|
||
|
||
; white button ids
|
||
white_button_ids: ; 1 top, 1 bottom, 2 top, 2 bottom, N top, N bottom
|
||
db 0x31, 0x11
|
||
db 0x33, 0x13
|
||
db 0x35, 0x15
|
||
db 0x36, 0x16
|
||
db 0x38, 0x18
|
||
db 0x3A, 0x1A
|
||
db 0x3C, 0x1C
|
||
|
||
db 0x41, 0x21
|
||
db 0x43, 0x23
|
||
db 0x45, 0x25
|
||
db 0x46, 0x26
|
||
db 0x48, 0x28
|
||
db 0x4A, 0x2A
|
||
db 0x4C, 0x2C
|
||
|
||
db 0x51, 0x31 ; ?
|
||
end_white_button_ids:
|
||
|
||
; parent white button index in row and black button ids
|
||
black_buttons:
|
||
db 0x00, 0x12
|
||
db 0x01, 0x14
|
||
db 0x03, 0x17
|
||
db 0x04, 0x19
|
||
db 0x05, 0x1B
|
||
db 0x07, 0x22
|
||
db 0x08, 0x24
|
||
db 0x0A, 0x27
|
||
db 0x0B, 0x29
|
||
db 0x0C, 0x2B
|
||
black_buttons_end:
|
||
|
||
; help string labels
|
||
help_texts:
|
||
dd lb_help1
|
||
dd lb_help2
|
||
dd lb_help3
|
||
dd lb_help4
|
||
dd lb_help5
|
||
dd lb_help6
|
||
dd 0
|
||
|
||
; =======================================================================
|
||
|
||
BAD_NOTE = 0xFF
|
||
|
||
melody db 0x90, 0x30, 0
|
||
|
||
keymap:
|
||
db BAD_NOTE ; 0x00
|
||
db BAD_NOTE ; 0x01
|
||
db 0x21 ; 0x02 '1' -> note 0x31
|
||
db 0x22 ; 0x03 '2' -> note 0x32
|
||
db 0x23 ; 0x04 '3' -> note 0x33
|
||
db 0x24 ; 0x05 '4' -> note 0x34
|
||
db 0x25 ; 0x06 '5' -> note 0x35
|
||
db 0x26 ; 0x07 '6' -> note 0x36
|
||
db 0x27 ; 0x08 '7' -> note 0x37
|
||
db 0x28 ; 0x09 '8' -> note 0x38
|
||
db 0x29 ; 0x0A '9' -> note 0x39
|
||
db 0x2A ; 0x0B '0' -> note 0x3A
|
||
db 0x2B ; 0x0C '-' -> note 0x3B
|
||
db 0x2C ; 0x0D '=' -> note 0x3C
|
||
db 0x1C ; 0x0E Backspace -> note 0x2C
|
||
db BAD_NOTE ; 0x0F
|
||
|
||
db 0x31 ; 0x10 'q' -> note 0x41
|
||
db 0x32 ; 0x11 'w' -> note 0x42
|
||
db 0x33 ; 0x12 'e' -> note 0x43
|
||
db 0x34 ; 0x13 'r' -> note 0x44
|
||
db 0x35 ; 0x14 't' -> note 0x45
|
||
db 0x36 ; 0x15 'y' -> note 0x46
|
||
db 0x37 ; 0x16 'u' -> note 0x47
|
||
db 0x38 ; 0x17 'i' -> note 0x48
|
||
db 0x39 ; 0x18 'o' -> note 0x49
|
||
db 0x3A ; 0x19 'p' -> note 0x4A
|
||
db 0x3B ; 0x1A '[' -> note 0x4B
|
||
db 0x3C ; 0x1B ']' -> note 0x4C
|
||
db 0x0C ; 0x1C Enter -> note 0x1C
|
||
db BAD_NOTE ; 0x1D
|
||
|
||
db 0x01 ; 0x1E 'a' -> note 0x11
|
||
db 0x02 ; 0x1F 's' -> note 0x12
|
||
db 0x03 ; 0x20 'd' -> note 0x13
|
||
db 0x04 ; 0x21 'f' -> note 0x14
|
||
db 0x05 ; 0x22 'g' -> note 0x15
|
||
db 0x06 ; 0x23 'h' -> note 0x16
|
||
db 0x07 ; 0x24 'j' -> note 0x17
|
||
db 0x08 ; 0x25 'k' -> note 0x18
|
||
db 0x09 ; 0x26 'l' -> note 0x19
|
||
db 0x0A ; 0x27 ';' -> note 0x1A
|
||
db 0x0B ; 0x28 ''' -> note 0x1B
|
||
db BAD_NOTE ; 0x29
|
||
db BAD_NOTE ; 0x2A
|
||
|
||
db 0x1B ; 0x2B '\' -> note 0x2B
|
||
db 0x11 ; 0x2C 'z' -> note 0x21
|
||
db 0x12 ; 0x2D 'x' -> note 0x22
|
||
db 0x13 ; 0x2E 'c' -> note 0x23
|
||
db 0x14 ; 0x2F 'v' -> note 0x24
|
||
db 0x15 ; 0x30 'b' -> note 0x25
|
||
db 0x16 ; 0x31 'n' -> note 0x26
|
||
db 0x17 ; 0x32 'm' -> note 0x27
|
||
db 0x18 ; 0x33 ',' -> note 0x28
|
||
db 0x19 ; 0x34 '.' -> note 0x29
|
||
db 0x1A ; 0x35 '/' -> note 0x2A
|
||
|
||
times 256-($-keymap) db BAD_NOTE
|
||
|
||
|
||
|
||
; =======================================================================
|
||
|
||
notify_info:
|
||
dd SSF_START_APP
|
||
dd 0
|
||
dd nf_map
|
||
dd 0
|
||
dd 0
|
||
db '/sys/@notify', 0
|
||
|
||
; =======================================================================
|
||
|
||
keymap_info:
|
||
dd SSF_READ_FILE
|
||
dd 0
|
||
dd 0
|
||
dd 256
|
||
dd keymap
|
||
|
||
; =======================================================================
|
||
|
||
I_END:
|
||
|
||
app_path rb 1024 ; = keymap_info + 20: acts as SF_FILE path field
|
||
|
||
help_stack:
|
||
rb 512
|
||
help_stack_top:
|
||
rb 1024
|
||
align 512
|
||
|
||
STACKTOP:
|
||
MEM:
|