1 Commits

Author SHA1 Message Date
8d235ce49b Libraries: fixed a bug in lib_init functions (#274)
added verification of the second initialization attempt

Reviewed-on: #274
2025-08-02 18:00:18 +02:00
14 changed files with 409 additions and 1542 deletions

View File

@@ -162,7 +162,6 @@ min=23
nes=23
sna=23
snes=23
rom=23
bat=24
sh=24
sys=25

View File

@@ -68,7 +68,6 @@ sna=/kolibrios/emul/e80/e80
gb=/kolibrios/emul/gameboy
gbc=/kolibrios/emul/gameboy
min=/kolibrios/emul/pokemini
rom=/kolibrios/emul/uxn
nc=/kolibrios/utils/cnc_editor/cnc_editor
kf=/sys/KF_VIEW
csv=/sys/table

View File

@@ -192,7 +192,6 @@ nc=/kolibrios/utils/cnc_editor/cnc_editor
ch8=/kolibrios/emul/chip8/chip8
md=/kolibrios/emul/dgen/dgen
gen=/kolibrios/emul/dgen/dgen
rom=/kolibrios/emul/uxn
zip=$Unz
7z=$Unz

View File

@@ -100,6 +100,10 @@ lib_init: ;//////////////////////////////////////////////////////////////////;;
mov [mem.alloc], eax
mov [mem.free], ebx
mov [mem.realloc], ecx
cmp [dll.load], edx
je .ok
mov [dll.load], edx
invoke dll.load, @IMPORT
@@ -115,6 +119,7 @@ lib_init: ;//////////////////////////////////////////////////////////////////;;
invoke ini.get_str, inifile, sec_proxy, key_password, proxyPassword, 256, proxyPassword
popa
.ok:
DEBUGF 1, "HTTP library: init OK\n"
xor eax, eax
ret

View File

@@ -78,6 +78,10 @@ proc lib_init ;///////////////////////////////////////////////////////////////;;
mov [mem.alloc], eax
mov [mem.free], ebx
mov [mem.realloc], ecx
cmp [dll.load], edx
je .ok
mov [dll.load], edx
or edx, edx

View File

@@ -34,21 +34,25 @@ proc libini._.init ;////////////////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< eax = 1 (fail) / 0 (ok) (library initialization result) ;;
;;================================================================================================;;
mov [mem.alloc], eax
mov [mem.free], ebx
mov [mem.realloc], ecx
mov [dll.load], edx
mov [mem.alloc], eax
mov [mem.free], ebx
mov [mem.realloc], ecx
invoke dll.load, @IMPORT
or eax, eax
jz .ok
cmp [dll.load], edx
je .ok
xor eax, eax
inc eax
ret
mov [dll.load], edx
.ok: xor eax,eax
ret
invoke dll.load, @IMPORT
or eax, eax
jz .ok
xor eax, eax
inc eax
ret
.ok: xor eax,eax
ret
endp
;;================================================================================================;;
@@ -60,16 +64,16 @@ proc libini._.unget_char _f ;///////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push eax ecx
mov ecx, [_f]
inc [ecx + IniFile.cnt]
dec esi
mov eax, [ecx + IniFile.bsize]
cmp [ecx + IniFile.cnt], eax
jle @f
stdcall libini._.unload_block, [_f]
@@: pop ecx eax
ret
push eax ecx
mov ecx, [_f]
inc [ecx + IniFile.cnt]
dec esi
mov eax, [ecx + IniFile.bsize]
cmp [ecx + IniFile.cnt], eax
jle @f
stdcall libini._.unload_block, [_f]
@@: pop ecx eax
ret
endp
;;================================================================================================;;
@@ -81,13 +85,13 @@ proc libini._.get_char _f ;/////////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
mov ecx, [_f]
dec [ecx + IniFile.cnt]
jns @f
stdcall libini._.preload_block, [_f]
dec [ecx + IniFile.cnt]
mov ecx, [_f]
dec [ecx + IniFile.cnt]
jns @f
stdcall libini._.preload_block, [_f]
dec [ecx + IniFile.cnt]
@@: lodsb
ret
ret
endp
;;================================================================================================;;
@@ -99,22 +103,22 @@ proc libini._.skip_nonblanks _f ;///////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
mov ecx, [_f]
mov ecx, [_f]
@@: stdcall libini._.get_char, [_f]
cmp al, 32
je @b
cmp al, 13
je @b
cmp al, 10
je @b
cmp al, 9
je @b
cmp al, ini.COMMENT_CHAR
jne @f
stdcall libini._.skip_line, [_f]
jmp @b
cmp al, 32
je @b
cmp al, 13
je @b
cmp al, 10
je @b
cmp al, 9
je @b
cmp al, ini.COMMENT_CHAR
jne @f
stdcall libini._.skip_line, [_f]
jmp @b
@@: stdcall libini._.unget_char, [_f]
ret
ret
endp
;;================================================================================================;;
@@ -126,14 +130,14 @@ proc libini._.skip_spaces _f ;//////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
mov ecx, [_f]
mov ecx, [_f]
@@: stdcall libini._.get_char, [_f]
cmp al, 32
je @b
cmp al, 9
je @b
cmp al, 32
je @b
cmp al, 9
je @b
@@: stdcall libini._.unget_char, [_f]
ret
ret
endp
;;================================================================================================;;
@@ -145,16 +149,16 @@ proc libini._.skip_line _f ;////////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
mov ecx, [_f]
mov ecx, [_f]
@@: stdcall libini._.get_char, [_f]
or al, al
jz @f
cmp al, 13
je @f
cmp al, 10
jne @b
or al, al
jz @f
cmp al, 13
je @f
cmp al, 10
jne @b
@@: stdcall libini._.unget_char, [_f]
ret
ret
endp
;;================================================================================================;;
@@ -166,16 +170,16 @@ proc libini._.unload_block _f ;/////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push eax ebx ecx
mov ebx, [_f]
mov eax, [ebx + IniFile.pos]
add eax, -ini.BLOCK_SIZE
invoke file.seek, [ebx + IniFile.fh], eax, SEEK_SET
stdcall libini._.preload_block, ebx
add esi, eax
mov [ebx + IniFile.cnt], 0
pop ecx ebx eax
ret
push eax ebx ecx
mov ebx, [_f]
mov eax, [ebx + IniFile.pos]
add eax, -ini.BLOCK_SIZE
invoke file.seek, [ebx + IniFile.fh], eax, SEEK_SET
stdcall libini._.preload_block, ebx
add esi, eax
mov [ebx + IniFile.cnt], 0
pop ecx ebx eax
ret
endp
;;================================================================================================;;
@@ -187,25 +191,25 @@ proc libini._.preload_block _f ;////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push eax ebx ecx
mov ebx, [_f]
@@: mov esi, [ebx + IniFile.buf]
push edi
mov edi, esi
mov ecx, ini.BLOCK_SIZE / 4
xor eax, eax
rep stosd
pop edi
invoke file.tell, [ebx + IniFile.fh]
mov [ebx + IniFile.pos], eax
invoke file.read, [ebx + IniFile.fh], esi, ini.BLOCK_SIZE
mov esi,[ebx + IniFile.buf]
cmp eax,ini.BLOCK_SIZE
jl @f
@@: mov [ebx + IniFile.cnt], eax
mov [ebx + IniFile.bsize], eax
pop ecx ebx eax
ret
push eax ebx ecx
mov ebx, [_f]
@@: mov esi, [ebx + IniFile.buf]
push edi
mov edi, esi
mov ecx, ini.BLOCK_SIZE / 4
xor eax, eax
rep stosd
pop edi
invoke file.tell, [ebx + IniFile.fh]
mov [ebx + IniFile.pos], eax
invoke file.read, [ebx + IniFile.fh], esi, ini.BLOCK_SIZE
mov esi,[ebx + IniFile.buf]
cmp eax,ini.BLOCK_SIZE
jl @f
@@: mov [ebx + IniFile.cnt], eax
mov [ebx + IniFile.bsize], eax
pop ecx ebx eax
ret
endp
;;================================================================================================;;
@@ -217,18 +221,18 @@ proc libini._.reload_block _f ;/////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push eax ebx ecx
mov ebx, [_f]
push [ebx + IniFile.bsize]
push esi [ebx + IniFile.cnt]
invoke file.seek, [ebx + IniFile.fh], [ebx + IniFile.pos], SEEK_SET
stdcall libini._.preload_block, ebx
pop [ebx + IniFile.cnt] esi
pop eax
sub eax,[ebx + IniFile.bsize]
sub [ebx + IniFile.cnt], eax
pop ecx ebx eax
ret
push eax ebx ecx
mov ebx, [_f]
push [ebx + IniFile.bsize]
push esi [ebx + IniFile.cnt]
invoke file.seek, [ebx + IniFile.fh], [ebx + IniFile.pos], SEEK_SET
stdcall libini._.preload_block, ebx
pop [ebx + IniFile.cnt] esi
pop eax
sub eax,[ebx + IniFile.bsize]
sub [ebx + IniFile.cnt], eax
pop ecx ebx eax
ret
endp
; f_info - contains current file block number
@@ -249,91 +253,91 @@ locals
buf dd ?
endl
xor eax, eax
cmp [_delta], 0
je .skip
xor eax, eax
cmp [_delta], 0
je .skip
push ebx ecx
invoke mem.alloc, ini.BLOCK_SIZE
or eax, eax
jz .fail
mov [buf], eax
push ebx ecx
invoke mem.alloc, ini.BLOCK_SIZE
or eax, eax
jz .fail
mov [buf], eax
cmp [_delta], 0
jl .down
cmp [_delta], 0
jl .down
mov ebx, [_f]
mov ecx, [ebx + IniFile.cnt]
mov ebx, [ebx + IniFile.fh]
invoke file.tell, ebx
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_SET
@@: invoke file.seek, ebx, [_delta], SEEK_CUR
invoke file.eof?, ebx
or eax, eax
jnz .done
invoke file.read, ebx, [buf], ini.BLOCK_SIZE
mov ecx, eax
mov eax, [_delta]
neg eax
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_CUR
push ecx
invoke file.write, ebx, [buf], ecx
pop ecx
cmp eax, ecx
jz @b
mov ebx, [_f]
mov ecx, [ebx + IniFile.cnt]
mov ebx, [ebx + IniFile.fh]
invoke file.tell, ebx
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_SET
@@: invoke file.seek, ebx, [_delta], SEEK_CUR
invoke file.eof?, ebx
or eax, eax
jnz .done
invoke file.read, ebx, [buf], ini.BLOCK_SIZE
mov ecx, eax
mov eax, [_delta]
neg eax
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_CUR
push ecx
invoke file.write, ebx, [buf], ecx
pop ecx
cmp eax, ecx
jz @b
.fail:
or eax, -1
pop ecx ebx
ret
or eax, -1
pop ecx ebx
ret
.done:
mov eax, [_delta]
neg eax
invoke file.seek, ebx, eax, SEEK_CUR
invoke file.seteof, ebx
stdcall libini._.reload_block, [_f]
invoke mem.free, [buf]
pop ecx ebx
mov eax, [_delta]
neg eax
invoke file.seek, ebx, eax, SEEK_CUR
invoke file.seteof, ebx
stdcall libini._.reload_block, [_f]
invoke mem.free, [buf]
pop ecx ebx
.skip:
ret
ret
.down:
neg [_delta]
neg [_delta]
mov ebx, [_f]
mov ecx, [ebx + IniFile.cnt]
mov ebx, [ebx + IniFile.fh]
invoke file.tell, ebx
sub eax, ecx
lea edx, [eax - 1]
push edx
@@: invoke file.seek, ebx, edx, SEEK_SET
invoke file.eof?, ebx
or eax, eax
jnz @f
add edx, ini.BLOCK_SIZE
jmp @b
@@: cmp edx, [esp]
je .skip.2
add edx, -ini.BLOCK_SIZE
cmp edx, [esp]
jl @f
invoke file.seek, ebx, edx, SEEK_SET
invoke file.read, ebx, [buf], ini.BLOCK_SIZE
mov ecx, eax
mov eax, [_delta]
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_CUR
invoke file.write, ebx, [buf], ecx
jmp @b
mov ebx, [_f]
mov ecx, [ebx + IniFile.cnt]
mov ebx, [ebx + IniFile.fh]
invoke file.tell, ebx
sub eax, ecx
lea edx, [eax - 1]
push edx
@@: invoke file.seek, ebx, edx, SEEK_SET
invoke file.eof?, ebx
or eax, eax
jnz @f
add edx, ini.BLOCK_SIZE
jmp @b
@@: cmp edx, [esp]
je .skip.2
add edx, -ini.BLOCK_SIZE
cmp edx, [esp]
jl @f
invoke file.seek, ebx, edx, SEEK_SET
invoke file.read, ebx, [buf], ini.BLOCK_SIZE
mov ecx, eax
mov eax, [_delta]
sub eax, ecx
invoke file.seek, ebx, eax, SEEK_CUR
invoke file.write, ebx, [buf], ecx
jmp @b
@@:
.skip.2:
add esp, 4
stdcall libini._.reload_block, [_f]
invoke mem.free, [buf]
pop ecx ebx
ret
add esp, 4
stdcall libini._.reload_block, [_f]
invoke mem.free, [buf]
pop ecx ebx
ret
endp
;;================================================================================================;;
@@ -345,25 +349,25 @@ proc libini._.get_value_length _f ;/////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push ebx ecx edx eax
mov ebx, [_f]
invoke file.tell, [ebx + IniFile.fh]
push esi [ebx + IniFile.cnt] [ebx + IniFile.pos]
sub eax, [ebx + IniFile.cnt]
mov edx, eax
push ebx ecx edx eax
mov ebx, [_f]
invoke file.tell, [ebx + IniFile.fh]
push esi [ebx + IniFile.cnt] [ebx + IniFile.pos]
sub eax, [ebx + IniFile.cnt]
mov edx, eax
stdcall libini._.skip_line, [_f]
invoke file.tell, [ebx + IniFile.fh]
sub eax, [ebx + IniFile.cnt]
sub eax, edx
mov [esp + 4 * 3], eax
stdcall libini._.skip_line, [_f]
invoke file.tell, [ebx + IniFile.fh]
sub eax, [ebx + IniFile.cnt]
sub eax, edx
mov [esp + 4 * 3], eax
pop eax
invoke file.seek, [ebx + IniFile.fh], eax, SEEK_SET
stdcall libini._.preload_block, [_f]
pop [ebx + IniFile.cnt] esi
pop eax edx ecx ebx
ret
pop eax
invoke file.seek, [ebx + IniFile.fh], eax, SEEK_SET
stdcall libini._.preload_block, [_f]
pop [ebx + IniFile.cnt] esi
pop eax edx ecx ebx
ret
endp
;;================================================================================================;;
@@ -376,10 +380,10 @@ proc libini._.string_copy ;/////////////////////////////////////////////////////
;< --- TBD --- ;;
;;================================================================================================;;
@@: lodsb
or al, al
jz @f
stosb
jmp @b
or al, al
jz @f
stosb
jmp @b
@@: ret
endp
@@ -392,26 +396,26 @@ proc libini._.find_next_section _f ;////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push ebx edi
push ebx edi
@@: stdcall libini._.skip_nonblanks, [_f]
cmp al, '['
je @f
or al, al
jz .exit_error
stdcall libini._.skip_line, [_f]
or al, al
jz .exit_error
jmp @b
cmp al, '['
je @f
or al, al
jz .exit_error
stdcall libini._.skip_line, [_f]
or al, al
jz .exit_error
jmp @b
@@:
pop edi ebx
xor eax, eax
ret
pop edi ebx
xor eax, eax
ret
.exit_error:
pop edi ebx
or eax, -1
ret
pop edi ebx
or eax, -1
ret
endp
;;================================================================================================;;
@@ -425,50 +429,50 @@ proc libini._.find_section _f, _sec_name ;//////////////////////////////////////
;< eax = -1 (fail) / 0 (ok) ;;
;< [_f.pos] = new cursor position (right after ']' char if eax = 0, at the end of file otherwise) ;;
;;================================================================================================;;
push ebx edi
push ebx edi
mov ecx, [_f]
invoke file.seek, [ecx + IniFile.fh], 0, SEEK_SET
stdcall libini._.preload_block, [_f]
mov ecx, [_f]
invoke file.seek, [ecx + IniFile.fh], 0, SEEK_SET
stdcall libini._.preload_block, [_f]
.next_section:
stdcall libini._.find_next_section, [_f]
or eax, eax
jnz .exit_error
stdcall libini._.find_next_section, [_f]
or eax, eax
jnz .exit_error
stdcall libini._.get_char, [_f]
stdcall libini._.skip_spaces, [_f]
mov edi, [_sec_name]
stdcall libini._.get_char, [_f]
stdcall libini._.skip_spaces, [_f]
mov edi, [_sec_name]
@@: stdcall libini._.get_char, [_f]
cmp al, ']'
je @f
or al, al
jz .exit_error
cmp al, 13
je .next_section
cmp al, 10
je .next_section
scasb
je @b
cmp byte[edi - 1], 0
jne .next_section
dec edi
stdcall libini._.unget_char, [_f]
stdcall libini._.skip_spaces, [_f]
stdcall libini._.get_char, [_f]
cmp al, ']'
jne .next_section
cmp al, ']'
je @f
or al, al
jz .exit_error
cmp al, 13
je .next_section
cmp al, 10
je .next_section
scasb
je @b
cmp byte[edi - 1], 0
jne .next_section
dec edi
stdcall libini._.unget_char, [_f]
stdcall libini._.skip_spaces, [_f]
stdcall libini._.get_char, [_f]
cmp al, ']'
jne .next_section
@@:
cmp byte[edi], 0
jne .next_section
pop edi ebx
xor eax, eax
ret
cmp byte[edi], 0
jne .next_section
pop edi ebx
xor eax, eax
ret
.exit_error:
pop edi ebx
or eax, -1
ret
pop edi ebx
or eax, -1
ret
endp
;;================================================================================================;;
@@ -483,44 +487,44 @@ proc libini._.find_key _f, _key_name ;//////////////////////////////////////////
;< [_f.pos] = new cursor position (right after '=' char if eax = 0, at the end of file or right ;;
;< before '[' char otherwise) ;;
;;================================================================================================;;
push ebx edi
push ebx edi
.next_value:
mov edi, [_key_name]
stdcall libini._.skip_line, [_f]
stdcall libini._.skip_nonblanks, [_f]
or al, al
jz .exit_error
cmp al, '['
je .exit_error
mov edi, [_key_name]
stdcall libini._.skip_line, [_f]
stdcall libini._.skip_nonblanks, [_f]
or al, al
jz .exit_error
cmp al, '['
je .exit_error
@@: stdcall libini._.get_char, [_f]
or al, al
jz .exit_error
cmp al, '='
je @f
scasb
je @b
cmp byte[edi - 1], 0
jne .next_value
dec edi
stdcall libini._.unget_char, [_f]
stdcall libini._.skip_spaces, [_f]
stdcall libini._.get_char, [_f]
cmp al, '='
je @f
jmp .next_value
or al, al
jz .exit_error
cmp al, '='
je @f
scasb
je @b
cmp byte[edi - 1], 0
jne .next_value
dec edi
stdcall libini._.unget_char, [_f]
stdcall libini._.skip_spaces, [_f]
stdcall libini._.get_char, [_f]
cmp al, '='
je @f
jmp .next_value
@@:
cmp byte[edi], 0
jne .next_value
cmp byte[edi], 0
jne .next_value
pop edi ebx
xor eax, eax
ret
pop edi ebx
xor eax, eax
ret
.exit_error:
pop edi ebx
or eax, -1
ret
pop edi ebx
or eax, -1
ret
endp
;;================================================================================================;;
@@ -532,31 +536,31 @@ proc libini._.low.read_value _f_addr, _buffer, _buf_len ;///////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push edi eax
mov edi, [_buffer]
stdcall libini._.skip_spaces, [_f_addr]
@@: dec [_buf_len]
jz @f
stdcall libini._.get_char, [_f_addr]
cmp al, 13
je @f
cmp al, 10
je @f
stosb
or al, al
jnz @b
push edi eax
mov edi, [_buffer]
stdcall libini._.skip_spaces, [_f_addr]
@@: dec [_buf_len]
jz @f
stdcall libini._.get_char, [_f_addr]
cmp al, 13
je @f
cmp al, 10
je @f
stosb
or al, al
jnz @b
@@: stdcall libini._.unget_char, [_f_addr]
mov byte[edi], 0
dec edi
@@: cmp edi, [_buffer]
jb @f
cmp byte[edi], 32
ja @f
mov byte[edi], 0
dec edi
jmp @b
@@: pop eax edi
ret
mov byte[edi], 0
dec edi
@@: cmp edi, [_buffer]
jb @f
cmp byte[edi], 32
ja @f
mov byte[edi], 0
dec edi
jmp @b
@@: pop eax edi
ret
endp
;;================================================================================================;;
@@ -568,25 +572,25 @@ proc libini._.str_to_int ;//////////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< eax = binary number representation (no overflow checks made) ;;
;;================================================================================================;;
push edx
push edx
xor eax, eax
xor edx, edx
xor eax, eax
xor edx, edx
@@: lodsb
cmp al, '0'
jb @f
cmp al, '9'
ja @f
add eax, -'0'
imul edx, 10
add edx, eax
jmp @b
cmp al, '0'
jb @f
cmp al, '9'
ja @f
add eax, -'0'
imul edx, 10
add edx, eax
jmp @b
@@: dec esi
mov eax, edx
pop edx
ret
@@: dec esi
mov eax, edx
pop edx
ret
endp
;;================================================================================================;;
@@ -600,29 +604,29 @@ proc libini._.int_to_str ;//////////////////////////////////////////////////////
;;------------------------------------------------------------------------------------------------;;
;< --- TBD --- ;;
;;================================================================================================;;
push ecx edx
push ecx edx
or eax, eax
jns @f
mov byte[edi], '-'
inc edi
@@: call .recurse
pop edx ecx
ret
or eax, eax
jns @f
mov byte[edi], '-'
inc edi
@@: call .recurse
pop edx ecx
ret
.recurse:
cmp eax,ecx
jb @f
xor edx,edx
div ecx
push edx
call .recurse
pop eax
@@: cmp al,10
sbb al,0x69
das
stosb
retn
cmp eax,ecx
jb @f
xor edx,edx
div ecx
push edx
call .recurse
pop eax
@@: cmp al,10
sbb al,0x69
das
stosb
retn
endp
;;================================================================================================;;
@@ -635,50 +639,50 @@ proc libini._.ascii_to_scan ;_ascii_code ;//////////////////////////////////////
;< eax = 0 (error) / scancode (success) ;;
;;================================================================================================;;
; /sys/keymap.key
sub esp, 256
mov eax, esp
push ebx
push 'key'
push 'map.'
push '/key'
push '/sys'
push eax ; buffer in the stack
push 0x100 ; read 0x100 bytes
push 0
push 0 ; from position zero
push 0 ; subfunction: read
mov ebx, esp
push 70
pop eax
mcall
add esp, 36
pop ebx
test eax, eax
jnz .die
mov al, [esp+256+4] ; get ASCII code
push edi
sub esp, 256
mov eax, esp
push ebx
push 'key'
push 'map.'
push '/key'
push '/sys'
push eax ; buffer in the stack
push 0x100 ; read 0x100 bytes
push 0
push 0 ; from position zero
push 0 ; subfunction: read
mov ebx, esp
push 70
pop eax
mcall
add esp, 36
pop ebx
test eax, eax
jnz .die
mov al, [esp+256+4] ; get ASCII code
push edi
; first keytable - no modifiers pressed
; check scancodes from 1 to 36h (inclusive)
lea edi, [esp+4+1]
mov edx, edi
mov ecx, 36h
repnz scasb
jz .found
lea edi, [esp+4+1]
mov edx, edi
mov ecx, 36h
repnz scasb
jz .found
; second keytable - Shift pressed
lea edi, [esp+4+128+1]
mov edx, edi
mov ecx, 36h
repnz scasb
jz .found
pop edi
lea edi, [esp+4+128+1]
mov edx, edi
mov ecx, 36h
repnz scasb
jz .found
pop edi
.die:
xor eax, eax
jmp .ret
xor eax, eax
jmp .ret
.found:
mov eax, edi
sub eax, edx
pop edi
mov eax, edi
sub eax, edx
pop edi
.ret:
add esp, 256
ret 4
add esp, 256
ret 4
endp

View File

@@ -33,38 +33,39 @@ use_ColorDialog
;--------------------------------------------------
align 16
lib_init:
ret
xor eax, eax
ret
;--------------------------------------------------
align 16
EXPORTS:
dd sz_init, lib_init
dd sz_version, 0x00000001
dd sz_init, lib_init
dd sz_version, 0x00000001
dd sz_OpenDialog_init, OpenDialog.init
dd sz_OpenDialog_start, OpenDialog.start
dd sz_OpenDialog_set_file_name, OpenDialog.set_file_name
dd sz_OpenDialog_set_file_ext, OpenDialog.set_file_ext
dd szVersion_OpenDialog, 0x00010001
dd sz_OpenDialog_init, OpenDialog.init
dd sz_OpenDialog_start, OpenDialog.start
dd sz_OpenDialog_set_file_name, OpenDialog.set_file_name
dd sz_OpenDialog_set_file_ext, OpenDialog.set_file_ext
dd szVersion_OpenDialog, 0x00010001
dd sz_ColorDialog_init, ColorDialog.init
dd sz_ColorDialog_start, ColorDialog.start
dd szVersion_ColorDialog, 0x00010001
dd sz_ColorDialog_init, ColorDialog.init
dd sz_ColorDialog_start, ColorDialog.start
dd szVersion_ColorDialog, 0x00010001
dd 0,0
dd 0,0
;-----------------------------------------------------------------------------
sz_init db 'lib_init',0
sz_version db 'version',0
sz_init db 'lib_init',0
sz_version db 'version',0
sz_OpenDialog_init db 'OpenDialog_init',0
sz_OpenDialog_start db 'OpenDialog_start',0
sz_OpenDialog_set_file_name db 'OpenDialog_set_file_name',0
sz_OpenDialog_set_file_ext db 'OpenDialog_set_file_ext',0
szVersion_OpenDialog db 'Version_OpenDialog',0
sz_OpenDialog_init db 'OpenDialog_init',0
sz_OpenDialog_start db 'OpenDialog_start',0
sz_OpenDialog_set_file_name db 'OpenDialog_set_file_name',0
sz_OpenDialog_set_file_ext db 'OpenDialog_set_file_ext',0
szVersion_OpenDialog db 'Version_OpenDialog',0
sz_ColorDialog_init db 'ColorDialog_init',0
sz_ColorDialog_start db 'ColorDialog_start',0
szVersion_ColorDialog db 'Version_ColorDialog',0
sz_ColorDialog_init db 'ColorDialog_init',0
sz_ColorDialog_start db 'ColorDialog_start',0
szVersion_ColorDialog db 'Version_ColorDialog',0
;-----------------------------------------------------------------------------

View File

@@ -1,6 +0,0 @@
# SPDX-FileCopyrightText: 2025 iyzsong@envs.net
#
# SPDX-License-Identifier: MPL-2.0
zig-out
.zig-cache

View File

@@ -1,17 +0,0 @@
// SPDX-FileCopyrightText: 2025 iyzsong@envs.net
//
// SPDX-License-Identifier: MPL-2.0
Uxn/Varvara emulator for Kolibri OS
Based on https://github.com/chmod222/zuxn
compile: zig build --release=fast
result: zig-out/bin/uxn
run: uxn SOME.rom
control:
Up/Down/Left/Right
Ctrl/Shift/Alt/Home
F1: change scale (1x, 2x, 3x)
TODO: file/directory stat, audio latency, open dialog?

View File

@@ -1,36 +0,0 @@
// SPDX-FileCopyrightText: 2025 iyzsong@envs.net
//
// SPDX-License-Identifier: MPL-2.0
const std = @import("std");
pub fn build(b: *std.Build) void {
const target_query = std.Target.Query{
.cpu_arch = std.Target.Cpu.Arch.x86,
.os_tag = std.Target.Os.Tag.freestanding,
.abi = std.Target.Abi.none,
.cpu_model = std.Target.Query.CpuModel{ .explicit = &std.Target.x86.cpu.i586 },
};
const target = b.resolveTargetQuery(target_query);
const optimize = b.standardOptimizeOption(.{});
const zuxn = b.dependency("zuxn", .{
.target = target,
.optimize = optimize,
});
const elf = b.addExecutable(.{
.name = "uxn.elf",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.unwind_tables = .none,
});
elf.root_module.addImport("uxn-core", zuxn.module("uxn-core"));
elf.root_module.addImport("uxn-varvara", zuxn.module("uxn-varvara"));
elf.setLinkerScript(b.path("src/linker.ld"));
const bin = elf.addObjCopy(.{
.format = .bin,
});
const install_bin = b.addInstallBinFile(bin.getOutput(), "uxn");
b.getInstallStep().dependOn(&install_bin.step);
b.installArtifact(elf);
}

View File

@@ -1,21 +0,0 @@
// SPDX-FileCopyrightText: 2025 iyzsong@envs.net
//
// SPDX-License-Identifier: MPL-2.0
.{
.name = .uxn_kolibrios,
.version = "0.0.0",
.fingerprint = 0x3aef20f25c0a0218,
.minimum_zig_version = "0.14.0",
.dependencies = .{
.zuxn = .{
.url = "git+https://github.com/chmod222/zuxn.git#fc3a76724fa87dd08039438b56fc326ac3d51e4d",
.hash = "zuxn-0.0.1-XnoOpbqsAgD-fU6rv_AoLffA1utIzXuae2cmnHj6SzE6",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@@ -1,604 +0,0 @@
// SPDX-FileCopyrightText: 2025 iyzsong@envs.net
//
// SPDX-License-Identifier: MPL-2.0
const std = @import("std");
pub const SYS = enum(i32) {
terminate_process = -1,
create_window = 0,
put_pixel = 1,
get_key = 2,
get_sys_time = 3,
draw_text = 4,
sleep = 5,
put_image = 7,
define_button = 8,
thread_info = 9,
wait_event = 10,
check_event = 11,
redraw = 12,
draw_rect = 13,
get_screen_size = 14,
get_button = 17,
system = 18,
screen_put_image = 25,
system_get = 26,
get_sys_date = 29,
mouse_get = 37,
set_events_mask = 40,
style_settings = 48,
create_thread = 51,
board = 63,
keyboard = 66,
change_window = 67,
sys_misc = 68,
file = 70,
blitter = 73,
};
pub const Event = enum(u32) {
none = 0,
redraw = 1,
key = 2,
button = 3,
background = 5,
mouse = 6,
ipc = 7,
};
pub inline fn syscall0(number: SYS) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
);
}
pub inline fn syscall1(number: SYS, arg1: u32) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
[arg1] "{ebx}" (arg1),
);
}
pub inline fn syscall2(number: SYS, arg1: u32, arg2: u32) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
[arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2),
);
}
pub inline fn syscall3(number: SYS, arg1: u32, arg2: u32, arg3: u32) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
[arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3),
);
}
pub inline fn syscall4(number: SYS, arg1: u32, arg2: u32, arg3: u32, arg4: u32) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
[arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3),
[arg4] "{esi}" (arg4),
);
}
pub inline fn syscall5(number: SYS, arg1: u32, arg2: u32, arg3: u32, arg4: u32, arg5: u32) u32 {
return asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (@intFromEnum(number)),
[arg1] "{ebx}" (arg1),
[arg2] "{ecx}" (arg2),
[arg3] "{edx}" (arg3),
[arg4] "{esi}" (arg4),
[arg5] "{edi}" (arg5),
);
}
pub fn terminateProcess() noreturn {
_ = syscall0(SYS.terminate_process);
unreachable;
}
pub const WindowFlags = struct {
skinned: bool = true,
fixed: bool = true,
no_title: bool = false,
relative_coordinates: bool = false,
no_fill: bool = false,
unmovable: bool = false,
};
pub fn createWindow(x: u16, y: u16, width: u16, height: u16, bgcolor: u24, flags: WindowFlags, title: [*:0]const u8) void {
var f1: u32 = 0x00000000;
if (flags.no_fill)
f1 |= 0x40000000;
if (flags.relative_coordinates)
f1 |= 0x20000000;
if (!flags.no_title)
f1 |= 0x10000000;
if (flags.skinned) {
if (flags.fixed) {
f1 |= 0x04000000;
} else {
f1 |= 0x03000000;
}
} else {
f1 |= 0x01000000;
}
var f2: u32 = 0x00000000;
if (flags.unmovable)
f2 = 0x01000000;
_ = syscall5(SYS.create_window, @as(u32, x) * 0x10000 + width, @as(u32, y) * 0x10000 + height, f1 | @as(u32, bgcolor), f2 | @as(u32, bgcolor), @intFromPtr(title));
}
pub fn putPixel(x: u16, y: u16, color: u24) void {
_ = syscall3(SYS.put_pixel, x, y, color);
}
pub fn invertPixel(x: u16, y: u16) void {
_ = syscall3(SYS.put_pixel, x, y, 0x01000000);
}
pub const Key = packed struct(u32) {
_unused: u8 = 0,
key: u8 = 0,
scancode: u8 = 0,
empty: u8 = 1,
pub fn pressed(self: *const Key) bool {
return self.key & 0x80 > 0;
}
};
pub fn getKey() Key {
return @bitCast(syscall0(SYS.get_key));
}
pub fn getSysTime() u24 {
return @intCast(syscall0(SYS.get_sys_time));
}
pub fn getButton() u32 {
return syscall0(SYS.get_button);
}
pub fn terminateThreadId(id: u32) void {
_ = syscall2(SYS.system, 18, id);
}
pub fn drawText(x: u16, y: u16, color: u24, text: [*:0]const u8) void {
_ = syscall5(SYS.draw_text, @as(u32, x) * 0x10000 + y, 0x80000000 | @as(u32, color), @intFromPtr(text), 0, 0);
}
pub fn sleep(centisecond: u32) void {
_ = syscall1(SYS.sleep, centisecond);
}
pub fn beginDraw() void {
_ = syscall1(SYS.redraw, 1);
}
pub fn endDraw() void {
_ = syscall1(SYS.redraw, 2);
}
pub fn putImage(image: [*]const u8, width: u16, height: u16, x: u16, y: u16) void {
_ = syscall3(SYS.put_image, @intFromPtr(image), @as(u32, width) * 0x10000 + height, @as(u32, x) * 0x10000 + y);
}
pub fn drawRect(x: u16, y: u16, width: u16, height: u16, color: u24) void {
_ = syscall3(SYS.draw_rect, @as(u32, x) * 0x10000 + width, @as(u32, y) * 0x10000 + height, color);
}
pub fn getScreenSize() packed struct(u32) { height: u16, width: u16 } {
return @bitCast(syscall0(SYS.get_screen_size));
}
pub fn waitEvent() Event {
return @enumFromInt(syscall0(SYS.wait_event));
}
pub fn checkEvent() Event {
return @enumFromInt(syscall0(SYS.check_event));
}
pub fn createThread(entry: *const fn () void, stack: []u8) u32 {
return syscall3(SYS.create_thread, 1, @intFromPtr(entry), @intFromPtr(stack.ptr) + stack.len);
}
pub fn debugWrite(byte: u8) void {
_ = syscall2(SYS.board, 1, byte);
}
pub fn debugWriteText(bytes: []const u8) void {
for (bytes) |byte| {
debugWrite(byte);
}
}
pub const EventsMask = packed struct(u32) {
redraw: bool = true, // 0
key: bool = true,
button: bool = true,
_reserved: bool = false,
background: bool = false,
mouse: bool = false,
ipc: bool = false,
network: bool = false,
debug: bool = false,
_unused: u23 = 0,
};
pub fn setEventsMask(mask: EventsMask) EventsMask {
return @bitCast(syscall1(SYS.set_events_mask, @bitCast(mask)));
}
pub fn getSkinHeight() u16 {
return @intCast(syscall1(SYS.style_settings, 4));
}
pub fn screenPutImage(image: [*]const u32, width: u16, height: u16, x: u16, y: u16) void {
_ = syscall3(SYS.screen_put_image, @intFromPtr(image), @as(u32, width) * 0x10000 + height, @as(u32, x) * 0x10000 + y);
}
pub fn systemGetTimeCount() u32 {
return syscall1(SYS.system_get, 9);
}
pub fn getSysDate() u24 {
return @intCast(syscall0(SYS.get_sys_date));
}
pub fn mouseGetScreenPosition() packed struct(u32) { y: u16, x: u16 } {
return @bitCast(syscall1(SYS.mouse_get, 0));
}
pub fn mouseGetWindowPosition() packed struct(u32) { y: u16, x: u16 } {
return @bitCast(syscall1(SYS.mouse_get, 1));
}
pub fn loadCursorIndirect(image: *const [32 * 32]u32, spotx: u5, spoty: u5) u32 {
return syscall3(SYS.mouse_get, 4, @intFromPtr(image), 0x0002 | (@as(u32, spotx) << 24) | (@as(u32, spoty) << 16));
}
pub fn setCursor(cursor: u32) u32 {
return syscall2(SYS.mouse_get, 5, cursor);
}
pub const MouseEvents = packed struct(u32) {
left_hold: bool = false,
right_hold: bool = false,
middle_hold: bool = false,
button4_hold: bool = false,
button5_hold: bool = false,
_unused0: u3 = 0,
left_pressed: bool = false,
right_pressed: bool = false,
middle_pressed: bool = false,
_unused1: u4 = 0,
vertical_scroll: bool = false,
left_released: bool = false,
right_released: bool = false,
middle_released: bool = false,
_unused2: u4 = 0,
horizontal_scroll: bool = false,
left_double_clicked: bool = false,
_unused3: u7 = 0,
};
pub fn mouseGetEvents() MouseEvents {
return @bitCast(syscall1(SYS.mouse_get, 3));
}
pub fn heapInit() u32 {
return syscall1(SYS.sys_misc, 11);
}
pub fn memAlloc(size: u32) *anyopaque {
return @ptrFromInt(syscall2(SYS.sys_misc, 12, size));
}
pub fn memFree(ptr: *anyopaque) void {
_ = syscall2(SYS.sys_misc, 13, @intFromPtr(ptr));
}
pub fn memRealloc(size: u32, ptr: *anyopaque) *anyopaque {
return @ptrFromInt(syscall3(SYS.sys_misc, 20, size, @intFromPtr(ptr)));
}
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
_ = ctx;
_ = alignment;
_ = ret_addr;
return @ptrCast(memAlloc(len));
}
fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
_ = ctx;
_ = alignment;
_ = ret_addr;
memFree(@ptrCast(memory.ptr));
}
fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
_ = ctx;
_ = alignment;
_ = ret_addr;
_ = memRealloc(new_len, @ptrCast(memory.ptr));
return true;
}
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
_ = ctx;
_ = memory;
_ = alignment;
_ = new_len;
_ = ret_addr;
return null;
}
pub const allocator: std.mem.Allocator = .{
.ptr = undefined,
.vtable = &.{
.alloc = alloc,
.free = free,
.resize = resize,
.remap = remap,
},
};
pub fn loadDriver(name: [*:0]const u8) u32 {
return syscall2(SYS.sys_misc, 16, @intFromPtr(name));
}
pub fn controlDriver(drv: u32, func: u32, in: ?[]const u32, out: ?[]const *anyopaque) u32 {
const ioctl: packed struct(u192) {
drv: u32,
func: u32,
inptr: u32,
insize: u32,
outptr: u32,
outsize: u32,
} = .{
.drv = drv,
.func = func,
.inptr = if (in) |v| @intFromPtr(v.ptr) else 0,
.insize = if (in) |v| v.len * 4 else 0,
.outptr = if (out) |v| @intFromPtr(v.ptr) else 0,
.outsize = if (out) |v| v.len * 4 else 0,
};
return syscall2(SYS.sys_misc, 17, @intFromPtr(&ioctl));
}
pub const Signal = packed struct(u192) {
kind: u32,
data0: u32,
data1: u32,
data2: u32,
data3: u32,
data4: u32,
};
pub fn waitSignal(sig: *Signal) void {
_ = syscall2(SYS.sys_misc, 14, @intFromPtr(sig));
}
pub const Sound = struct {
drv: u32,
pub const Buffer = struct {
drv: u32,
handle: u32,
pub fn play(self: *const Buffer, flags: u32) void {
_ = controlDriver(self.drv, 10, &.{ self.handle, flags }, null);
}
pub fn set(self: *const Buffer, src: []u8, offset: u32) void {
_ = controlDriver(self.drv, 8, &.{ self.handle, @intFromPtr(src.ptr), offset, src.len }, null);
}
};
pub fn init() Sound {
return .{
.drv = loadDriver("INFINITY"),
};
}
pub fn createBuffer(self: *const Sound, format: u32, size: u32) Buffer {
var ret: u32 = 0;
_ = controlDriver(self.drv, 1, &.{ format, size }, &.{&ret});
return .{
.drv = self.drv,
.handle = ret,
};
}
};
pub const InputMode = enum(u32) {
normal = 0,
scancodes = 1,
};
pub fn setInputMode(mode: InputMode) void {
_ = syscall2(SYS.keyboard, 1, @intFromEnum(mode));
}
pub fn changeWindow(x: u32, y: u32, width: u32, height: u32) void {
_ = syscall4(SYS.change_window, x, y, width, height);
}
pub const ControlKeys = packed struct(u32) {
left_shift: bool,
right_shift: bool,
left_ctrl: bool,
right_ctrl: bool,
left_alt: bool,
right_alt: bool,
caps_lock: bool,
num_lock: bool,
scroll_lock: bool,
left_win: bool,
right_win: bool,
_unused: u21,
};
pub fn getControlKeys() ControlKeys {
return @bitCast(syscall1(SYS.keyboard, 3));
}
const FileInfo = packed struct(u200) {
subfn: u32,
offset: u64,
size: u32,
buffer: u32,
path0: u8 = 0,
pathptr: *const u8,
};
pub fn readFile(pathname: [*:0]const u8, offset: u64, buffer: []u8) !u32 {
const info: FileInfo = .{
.subfn = 0,
.offset = offset,
.size = buffer.len,
.buffer = @intFromPtr(buffer.ptr),
.pathptr = @ptrCast(pathname),
};
const err = asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (SYS.file),
[info] "{ebx}" (&info),
);
const size = asm volatile (""
: [ret] "={ebx}" (-> u32),
);
return switch (err) {
0 => size,
10 => error.AccessDenied,
6 => size,
else => error.Unexpected,
};
}
pub fn createFile(pathname: [*:0]const u8, buffer: []u8) !u32 {
const info: FileInfo = .{
.subfn = 2,
.offset = 0,
.size = buffer.len,
.buffer = @intFromPtr(buffer.ptr),
.pathptr = @ptrCast(pathname),
};
const err = asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (SYS.file),
[info] "{ebx}" (&info),
);
const size = asm volatile (""
: [ret] "={ebx}" (-> u32),
);
return switch (err) {
0 => size,
10 => error.AccessDenied,
8 => size,
else => error.Unexpected,
};
}
pub fn writeFile(pathname: [*:0]const u8, offset: u64, buffer: []u8) !u32 {
const info: FileInfo = .{
.subfn = 3,
.offset = offset,
.size = buffer.len,
.buffer = @intFromPtr(buffer.ptr),
.pathptr = @ptrCast(pathname),
};
const err = asm volatile ("int $0x40"
: [ret] "={eax}" (-> u32),
: [number] "{eax}" (SYS.file),
[info] "{ebx}" (&info),
);
const size = asm volatile (""
: [ret] "={ebx}" (-> u32),
);
return switch (err) {
0 => size,
10 => error.AccessDenied,
5 => error.FileNotFound,
else => error.Unexpected,
};
}
pub fn fileGetSize(pathname: [*:0]const u8) !u64 {
var ret: [10]u32 = undefined;
const info: FileInfo = .{
.subfn = 5,
.offset = 0,
.size = 0,
.buffer = @intFromPtr(&ret),
.pathptr = @ptrCast(pathname),
};
if (syscall1(SYS.file, @intFromPtr(&info)) != 0)
return error.Unexpected;
return ret[8] | (@as(u64, ret[9]) << 32);
}
pub fn deleteFile(pathname: [*:0]const u8) !void {
const info: FileInfo = .{
.subfn = 8,
.offset = 0,
.size = 0,
.buffer = 0,
.pathptr = @ptrCast(pathname),
};
const err = syscall1(SYS.file, @intFromPtr(&info));
if (err != 0)
return error.Unexpected;
}
pub const File = struct {
pathname: [*:0]const u8,
offset: u64 = 0,
pub fn reader(file: *File) std.io.GenericReader(*File, anyerror, struct {
fn read(context: *File, buffer: []u8) !usize {
const size = try readFile(context.pathname, context.offset, buffer);
context.offset += size;
return size;
}
}.read) {
return .{ .context = file };
}
};
pub const BlitterFlags = packed struct(u32) {
rop: u4 = 0,
background: bool = false,
transparent: bool = false,
reserved1: u23 = 0,
client_relative: bool = true,
reserved2: u2 = 0,
};
pub fn blitter(dstx: u32, dsty: u32, dstw: u32, dsth: u32, srcx: u32, srcy: u32, srcw: u32, srch: u32, src: *const u8, pitch: u32, flags: BlitterFlags) void {
_ = syscall2(SYS.blitter, @bitCast(flags), @intFromPtr(&[_]u32{ dstx, dsty, dstw, dsth, srcx, srcy, srcw, srch, @intFromPtr(src), pitch }));
}
pub const DebugWriter = std.io.GenericWriter(void, anyerror, struct {
fn write(context: void, bytes: []const u8) !usize {
_ = context;
debugWriteText(bytes);
return bytes.len;
}
}.write);
pub const debug_writer: DebugWriter = .{ .context = {} };

View File

@@ -1,42 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 iyzsong@envs.net
*
* SPDX-License-Identifier: MPL-2.0
*/
SECTIONS
{
.text 0x00000000 :
{
/* MENUET01 header */
LONG(0x554e454d);
LONG(0x31305445);
LONG(1);
LONG(_start);
LONG(_image_end);
LONG(_memory_end);
LONG(_stack_top);
LONG(_cmdline);
LONG(0);
*(.text)
*(.text.*)
}
.rodata : ALIGN(8)
{
*(.rodata)
*(.rodata.*)
}
.data : ALIGN(8)
{
*(.data)
}
_image_end = .;
.bss : ALIGN(8)
{
*(.bss)
. = . + 4K;
_stack_top = .;
}
_memory_end = .;
}

View File

@@ -1,418 +0,0 @@
// SPDX-FileCopyrightText: 2025 iyzsong@envs.net
//
// SPDX-License-Identifier: MPL-2.0
const std = @import("std");
const kos = @import("kolibri.zig");
const uxn = @import("uxn-core");
const varvara = @import("uxn-varvara");
const allocator = kos.allocator;
export var _cmdline: [1024]u8 = undefined;
pub const std_options: std.Options = .{
.log_level = .info,
.logFn = struct {
fn log(comptime level: std.log.Level, comptime scope: @Type(.enum_literal), comptime format: []const u8, args: anytype) void {
_ = level;
_ = scope;
kos.debug_writer.print(format, args) catch return;
}
}.log,
};
const VarvaraDefault = varvara.VarvaraSystem(kos.DebugWriter, kos.DebugWriter);
const emu = struct {
var cpu: uxn.Cpu = undefined;
var sys: VarvaraDefault = undefined;
var rom: *[0x10000]u8 = undefined;
var pixels: []u8 = undefined;
var screen_width: u32 = undefined;
var screen_height: u32 = undefined;
var null_cursor: u32 = undefined;
var hide_cursor: bool = false;
var audio_thread: ?u32 = null;
var scale: u4 = 1;
fn init(rompath: [*:0]const u8) !void {
const screen = &emu.sys.screen_device;
var rom_file = kos.File{ .pathname = rompath };
emu.rom = try uxn.loadRom(allocator, rom_file.reader());
emu.cpu = uxn.Cpu.init(emu.rom);
emu.sys = try VarvaraDefault.init(allocator, kos.debug_writer, kos.debug_writer);
emu.cpu.device_intercept = struct {
var file_offsets: [2]u64 = .{ 0, 0 };
fn bcd8(x: u8) u8 {
return (x & 0xf) + 10 * ((x & 0xf0) >> 4);
}
fn getFilePortSlice(dev: *varvara.file.File, comptime port: comptime_int) []u8 {
const ports = varvara.file.ports;
const ptr: usize = dev.loadPort(u16, &cpu, port);
return if (port == ports.name)
std.mem.sliceTo(cpu.mem[ptr..], 0x00)
else
return cpu.mem[ptr..ptr +| dev.loadPort(u16, &cpu, ports.length)];
}
pub fn intercept(self: *uxn.Cpu, addr: u8, kind: uxn.Cpu.InterceptKind, data: ?*anyopaque) !void {
_ = data;
const port: u4 = @truncate(addr & 0x0f);
if (audio_thread == null and addr >= 0x30 and addr < 0x70) {
audio_thread = kos.createThread(&audio, allocator.alloc(u8, 32 * 1024) catch unreachable);
}
switch (addr >> 4) {
0xa, 0xb => {
if (kind != .output)
return;
const idx = (addr >> 4) - 0xa;
const dev = &sys.file_devices[idx];
const ports = varvara.file.ports;
switch (port) {
ports.stat + 1 => {
// TODO: file/directory stat
dev.storePort(u16, &cpu, ports.success, 0);
},
ports.delete => {
const name_slice = getFilePortSlice(dev, ports.name);
_ = kos.deleteFile(@ptrCast(name_slice)) catch {};
dev.storePort(u16, &cpu, ports.success, 0);
},
ports.name + 1 => {
file_offsets[idx] = 0;
dev.storePort(u16, &cpu, ports.success, 1);
},
ports.read + 1 => {
const name_slice = getFilePortSlice(dev, ports.name);
const data_slice = getFilePortSlice(dev, ports.read);
const ret: u32 = kos.readFile(@ptrCast(name_slice), file_offsets[idx], data_slice) catch 0;
file_offsets[idx] += ret;
dev.storePort(u16, &cpu, ports.success, @intCast(ret));
},
ports.write + 1 => {
const append = dev.loadPort(u8, &cpu, ports.append) == 0x01;
const pathname: [*:0]const u8 = @ptrCast(getFilePortSlice(dev, ports.name));
const buffer = getFilePortSlice(dev, ports.write);
var ret: u32 = 0;
if (append) {
const offset: u32 = @intCast(kos.fileGetSize(pathname) catch 0);
ret = kos.writeFile(pathname, offset, buffer) catch |err| blk: {
if (err == error.FileNotFound) {
break :blk kos.createFile(pathname, buffer) catch 0;
} else {
break :blk 0;
}
};
} else {
ret = kos.createFile(pathname, buffer) catch 0;
}
dev.storePort(u16, &cpu, ports.success, @intCast(ret));
},
else => {},
}
},
0xc => {
if (kind != .input)
return;
const dev = &sys.datetime_device;
const date = kos.getSysDate();
const time = kos.getSysTime();
switch (port) {
0x0, 0x1 => {
const year: u8 = bcd8(@truncate(date & 0xff));
dev.storePort(u16, &cpu, 0x0, @as(u16, 2000) + bcd8(year));
},
0x02 => {
const month: u8 = bcd8(@truncate((date & 0xff00) >> 8));
dev.storePort(u8, &cpu, port, month);
},
0x03 => {
const day: u8 = bcd8(@truncate((date & 0xff0000) >> 16));
dev.storePort(u8, &cpu, port, day);
},
0x04 => {
const hour: u8 = bcd8(@truncate(time & 0xff));
dev.storePort(u8, &cpu, port, hour);
},
0x05 => {
const minute: u8 = bcd8(@truncate((time & 0xff00) >> 8));
dev.storePort(u8, &cpu, port, minute);
},
0x06 => {
const second: u8 = bcd8(@truncate((time & 0xff0000) >> 16));
dev.storePort(u8, &cpu, port, second);
},
else => {},
}
},
else => try emu.sys.intercept(self, addr, kind),
}
}
}.intercept;
emu.cpu.output_intercepts = varvara.full_intercepts.output;
emu.cpu.input_intercepts = varvara.full_intercepts.input;
try emu.cpu.evaluateVector(0x0100);
screen_width = screen.width * emu.scale;
screen_height = screen.height * emu.scale;
emu.pixels = try allocator.alloc(u8, @as(usize, 4) * screen_width * screen_height);
}
fn exit() void {
if (audio_thread) |tid| {
kos.terminateThreadId(tid);
}
kos.terminateProcess();
}
fn audio() void {
var samples: [8192]i16 = undefined;
var sig: kos.Signal = undefined;
const buf = kos.Sound.init().createBuffer(3 | 0x10000000, 0);
buf.play(0);
while (true) {
kos.waitSignal(&sig);
if (sig.kind != 0xFF000001) continue;
@memset(&samples, 0);
for (0..samples.len / 512) |i| {
const w = samples[i * 512 .. (i + 1) * 512];
for (&sys.audio_devices) |*poly| {
if (poly.duration <= 0) {
poly.evaluateFinishVector(&cpu) catch unreachable;
}
poly.updateDuration();
poly.renderAudio(w);
}
}
for (0..samples.len) |i| {
samples[i] <<= 6;
}
buf.set(@ptrCast(&samples), sig.data2);
}
}
fn update() !void {
const screen = &sys.screen_device;
const colors = &sys.system_device.colors;
if (sys.system_device.exit_code) |_| {
exit();
}
if (screen_width != screen.width * scale or screen_height != screen.height * scale) {
const skin_height = kos.getSkinHeight();
allocator.free(emu.pixels);
screen_width = screen.width * scale;
screen_height = screen.height * scale;
emu.pixels = try allocator.alloc(u8, @as(usize, 4) * screen_width * screen_height);
kos.changeWindow(100, 100, screen_width + 9, screen_height + skin_height + 4);
}
try screen.evaluateFrame(&cpu);
if (screen.dirty_region) |region| {
for (region.y0..region.y1) |y| {
for (region.x0..region.x1) |x| {
const idx = y * screen.width + x;
const pal = (@as(u4, screen.foreground[idx]) << 2) | screen.background[idx];
const color = colors[if ((pal >> 2) > 0) (pal >> 2) else (pal & 0x3)];
for (0..scale) |sy| {
for (0..scale) |sx| {
pixels[4 * ((y * scale + sy) * screen.width * scale + (x * scale + sx)) + 2] = color.r;
pixels[4 * ((y * scale + sy) * screen.width * scale + (x * scale + sx)) + 1] = color.g;
pixels[4 * ((y * scale + sy) * screen.width * scale + (x * scale + sx)) + 0] = color.b;
}
}
}
}
const ix = region.x0 * scale;
const iy = region.y0 * scale;
const iw = (region.x1 - region.x0) * scale;
const ih = (region.y1 - region.y0) * scale;
kos.blitter(ix, iy, iw, ih, ix, iy, iw, ih, @ptrCast(emu.pixels.ptr), screen.width * scale * 4, .{});
screen.dirty_region = null;
}
}
fn changeScale() void {
const screen = &sys.screen_device;
scale = switch (scale) {
1 => 2,
2 => 3,
3 => 1,
else => 1,
};
screen.forceRedraw();
}
};
export fn _start() noreturn {
const cursor: [32 * 32]u32 = .{0} ** (32 * 32);
var counter: u32 = 0;
var last_tick = kos.systemGetTimeCount();
_ = kos.heapInit();
_ = kos.setEventsMask(.{ .mouse = true });
kos.setInputMode(.scancodes);
emu.null_cursor = kos.loadCursorIndirect(&cursor, 0, 0);
emu.init(@ptrCast(&_cmdline)) catch unreachable;
const screen = &emu.sys.screen_device;
const controller = &emu.sys.controller_device;
const callbacks = struct {
fn redraw() void {
const skin_height = kos.getSkinHeight();
kos.beginDraw();
kos.createWindow(300, 300, screen.width * emu.scale + 9, screen.height * emu.scale + skin_height + 4, 0x000000, .{ .skinned = true, .no_fill = true, .relative_coordinates = true }, "UXN");
kos.endDraw();
}
fn key() void {
const symtab: [0x80]u8 = .{
// 0x0*
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, '\t',
// 0x1*
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\r', 0, 'a', 's',
// 0x2*
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
// 0x3*
'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0,
// 0x00* + SHIFT
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8, '\t',
// 0x10* + SHIFT
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\r', 0, 'A', 'S',
// 0x20* + SHIFT
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
// 0x30* + SHIFT,
'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0,
};
const scancode1 = kos.getKey().key;
const input: ?union(enum) {
button: struct {
flags: varvara.controller.ButtonFlags,
pressed: bool,
},
key: u8,
} = switch (scancode1) {
0xe0 => blk: {
const scancode2 = kos.getKey().key;
break :blk switch (scancode2) {
0x1d => .{ .button = .{ .flags = .{ .ctrl = true }, .pressed = true } },
0x9d => .{ .button = .{ .flags = .{ .ctrl = true }, .pressed = false } },
0x38 => .{ .button = .{ .flags = .{ .alt = true }, .pressed = true } },
0xb8 => .{ .button = .{ .flags = .{ .alt = true }, .pressed = false } },
0x47 => .{ .button = .{ .flags = .{ .start = true }, .pressed = true } },
0xc7 => .{ .button = .{ .flags = .{ .start = true }, .pressed = false } },
0x48 => .{ .button = .{ .flags = .{ .up = true }, .pressed = true } },
0xc8 => .{ .button = .{ .flags = .{ .up = true }, .pressed = false } },
0x50 => .{ .button = .{ .flags = .{ .down = true }, .pressed = true } },
0xd0 => .{ .button = .{ .flags = .{ .down = true }, .pressed = false } },
0x4b => .{ .button = .{ .flags = .{ .left = true }, .pressed = true } },
0xcb => .{ .button = .{ .flags = .{ .left = true }, .pressed = false } },
0x4d => .{ .button = .{ .flags = .{ .right = true }, .pressed = true } },
0xcd => .{ .button = .{ .flags = .{ .right = true }, .pressed = false } },
else => null,
};
},
0x3b => blk: {
emu.changeScale();
break :blk null;
},
0x1d => .{ .button = .{ .flags = .{ .ctrl = true }, .pressed = true } },
0x9d => .{ .button = .{ .flags = .{ .ctrl = true }, .pressed = false } },
0x38 => .{ .button = .{ .flags = .{ .alt = true }, .pressed = true } },
0xb8 => .{ .button = .{ .flags = .{ .alt = true }, .pressed = false } },
0x2a, 0x36 => .{ .button = .{ .flags = .{ .shift = true }, .pressed = true } },
0xaa, 0xb6 => .{ .button = .{ .flags = .{ .shift = true }, .pressed = false } },
else => blk: {
if (scancode1 > 0x40) {
break :blk null;
}
const ctrls = kos.getControlKeys();
const k = if (ctrls.left_shift or ctrls.right_shift) symtab[scancode1 + 0x40] else symtab[scancode1];
break :blk if (k > 0) .{ .key = k } else null;
},
};
if (input) |v| {
switch (v) {
.button => {
if (v.button.pressed) {
controller.pressButtons(&emu.cpu, v.button.flags, 0) catch unreachable;
} else {
controller.releaseButtons(&emu.cpu, v.button.flags, 0) catch unreachable;
}
},
.key => {
controller.pressKey(&emu.cpu, v.key) catch unreachable;
},
}
}
}
fn button() void {
const btn = kos.getButton();
if (btn >> 8 == 1)
emu.exit();
}
fn mouse() void {
const dev = &emu.sys.mouse_device;
const pos = kos.mouseGetWindowPosition();
const events = kos.mouseGetEvents();
const mouse_pressed: varvara.mouse.ButtonFlags = .{
.left = events.left_pressed,
.middle = events.middle_pressed,
.right = events.right_pressed,
._unused = 0,
};
const mouse_released: varvara.mouse.ButtonFlags = .{
.left = events.left_released,
.middle = events.middle_released,
.right = events.right_released,
._unused = 0,
};
if (emu.hide_cursor and pos.y > emu.screen_height) {
_ = kos.setCursor(0);
emu.hide_cursor = false;
}
if (!emu.hide_cursor and pos.y < emu.screen_height) {
_ = kos.setCursor(emu.null_cursor);
emu.hide_cursor = true;
}
dev.updatePosition(&emu.cpu, pos.x / emu.scale, pos.y / emu.scale) catch unreachable;
if (@as(u8, @bitCast(mouse_pressed)) > 0) {
dev.pressButtons(&emu.cpu, mouse_pressed) catch unreachable;
}
if (@as(u8, @bitCast(mouse_released)) > 0) {
dev.releaseButtons(&emu.cpu, mouse_released) catch unreachable;
}
}
};
callbacks.redraw();
while (true) {
while (true) {
const event = kos.checkEvent();
switch (event) {
.none => break,
.redraw => callbacks.redraw(),
.key => callbacks.key(),
.button => callbacks.button(),
.mouse => callbacks.mouse(),
else => {},
}
}
const tick = kos.systemGetTimeCount();
counter += (tick - last_tick) * 3;
last_tick = tick;
if (counter > 5) {
counter -= 5;
emu.update() catch unreachable;
} else {
kos.sleep(1);
}
}
}