Meta: Add files to repository

This commit is contained in:
Alec Murphy 2025-03-25 07:32:23 -04:00
parent 80a0428b66
commit 39198164cd
1029 changed files with 78311 additions and 0 deletions

28
System/Api/Dns.HC Normal file
View file

@ -0,0 +1,28 @@
#define DNS_REQUEST_PTR 0x300010
MemSet(DNS_REQUEST_PTR, NULL, sizeof(U64));
class DnsRequest {
U64 host;
U64 pointer_to_u32;
};
U32 @dns_query(U8* host)
{
U32 res = 0;
if (!host)
return U32_MAX;
if (!StrICmp("catbox.moe", host)) {
return 0x0a1400fe;
}
DnsRequest* request = CAlloc(sizeof(DnsRequest), Fs->code_heap);
request->host = StrNew(host, erythros_mem_task);
request->pointer_to_u32 = &res;
U64* request_ptr = DNS_REQUEST_PTR;
while (*request_ptr)
Sleep(1);
LXchgU32(request_ptr, request);
while (!res)
Sleep(1);
return res;
}

32
System/Api/Icmp.HC Normal file
View file

@ -0,0 +1,32 @@
#define ICMP_REQUEST_PTR 0x300020
MemSet(ICMP_REQUEST_PTR, NULL, sizeof(U64));
class IcmpRequest {
U64 addr;
U64 iden;
U64 seq;
U64 pointer_to_u32;
};
U32 @icmp_echo_request(U32 addr, U16 iden, U16 seq, IcmpRequest* request, I64 count)
{
U32 res = 0; // low 16 = ttl, hi 16 = payload size
request->addr = addr;
request->iden = iden;
request->seq = seq;
request->pointer_to_u32 = &res;
I64 start_jiffies = cnts.jiffies;
U64* request_ptr = ICMP_REQUEST_PTR;
if (!count)
*request_ptr = NULL;
while (*request_ptr) {
if (!(cnts.jiffies < start_jiffies + 1000))
return res;
Sleep(1);
}
LXchgU32(request_ptr, request);
while (!res && cnts.jiffies < start_jiffies + 1000)
Sleep(1);
return res;
}

9
System/Api/Ipv4.HC Normal file
View file

@ -0,0 +1,9 @@
U32 @ipv4_address(I64 o3, I64 o2, I64 o1, I64 o0)
{
U32 addr = NULL;
addr.u8[3] = o3;
addr.u8[2] = o2;
addr.u8[1] = o1;
addr.u8[0] = o0;
return addr;
}

144
System/Api/MD5.HC Normal file
View file

@ -0,0 +1,144 @@
/*
* Simple MD5 implementation
*
* https://gist.github.com/creationix/4710780
*/
U32 md5_r[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
// Use binary integer part of the sines of integers (in radians) as constants//
// Initialize variables:
U32 md5_k[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
// leftrotate function
U32 LEFTROTATE(U32 x, U32 c) { return (((x) << (c)) | ((x) >> (32 - (c)))); }
U0 md5(U8* initial_msg, U32 initial_len, U32* md5_h)
{
// These vars will contain the hash
U32 md5_h0, md5_h1, md5_h2, md5_h3;
// Message (to prepare)
U8* msg = NULL;
// Note: All variables are unsigned 32 bit and wrap modulo 2^32 when
// calculating
// r specifies the per-round shift amounts
md5_h0 = 0x67452301;
md5_h1 = 0xefcdab89;
md5_h2 = 0x98badcfe;
md5_h3 = 0x10325476;
// Pre-processing: adding a single 1 bit
// append "1" bit to message
/* Notice: the input bytes are considered as bits strings,
where the first bit is the most significant bit of the byte.[37] */
// Pre-processing: padding with zeros
// append "0" bit until message length in bit ≡ 448 (mod 512)
// append length mod (2 pow 64) to message
U32 new_len;
for (new_len = initial_len * 8 + 1; new_len % 512 != 448; new_len++)
;
new_len /= 8;
msg = CAlloc(new_len + 64, erythros_mem_task); // also appends "0" bits
// (we alloc also 64 extra bytes...)
MemCpy(msg, initial_msg, initial_len);
msg[initial_len] = 128; // write the "1" bit
U32 bits_len = 8 * initial_len; // note, we append the len
MemCpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer
// Process the message in successive 512-bit chunks:
// for each 512-bit chunk of message:
U32 offset;
for (offset = 0; offset < new_len; offset += (512 / 8)) {
// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
U32* w = (msg + offset)(U32*);
// Initialize hash value for this chunk:
U32 a = md5_h0;
U32 b = md5_h1;
U32 c = md5_h2;
U32 d = md5_h3;
// Main loop:
U32 i;
for (i = 0; i < 64; i++) {
U32 f, g;
if (i < 16) {
f = (b & c) | ((~b) & d);
g = i;
} else if (i < 32) {
f = (d & b) | ((~d) & c);
g = (5 * i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7 * i) % 16;
}
U32 temp = d;
d = c;
c = b;
// printf("rotateLeft(%x + %x + %x + %x, %d)\n", a, f, k[i], w[g], r[i]);
b = b + LEFTROTATE((a + f + md5_k[i] + w[g]), md5_r[i]);
a = temp;
}
// Add this chunk's hash to result so far:
md5_h0 += a;
md5_h1 += b;
md5_h2 += c;
md5_h3 += d;
}
md5_h[0] = md5_h0;
md5_h[1] = md5_h1;
md5_h[2] = md5_h2;
md5_h[3] = md5_h3;
// cleanup
Free(msg);
}
U8* md5_string(U8* buf, I64 size)
{
U32 md5_h[4];
md5(buf, size, &md5_h[0]);
U8* str = CAlloc(33, erythros_mem_task);
StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[0].u8[0],
md5_h[0].u8[1], md5_h[0].u8[2], md5_h[0].u8[3]);
StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[1].u8[0],
md5_h[1].u8[1], md5_h[1].u8[2], md5_h[1].u8[3]);
StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[2].u8[0],
md5_h[2].u8[1], md5_h[2].u8[2], md5_h[2].u8[3]);
StrPrint(str + StrLen(str), "%02x%02x%02x%02x", md5_h[3].u8[0],
md5_h[3].u8[1], md5_h[3].u8[2], md5_h[3].u8[3]);
return str;
}

32
System/Api/NetInfo.HC Normal file
View file

@ -0,0 +1,32 @@
#define NETINFO_REQUEST_PTR 0x300030
MemSet(NETINFO_REQUEST_PTR, NULL, sizeof(U64));
class NetInfoRequest {
U64 mac_address;
U64 ipv4_address;
U64 ipv4_netmask;
U64 ipv4_network;
U64 ipv4_gateway;
U64 dns_server_address;
U64 dns_server_port;
U64 rx_bytes;
U64 rx_frames;
U64 tx_bytes;
U64 tx_frames;
U64 pointer_to_u32;
};
NetInfoRequest* @net_info_request()
{
U32 res = 0;
NetInfoRequest* req = CAlloc(sizeof(NetInfoRequest), Fs->code_heap);
req->pointer_to_u32 = &res;
U64* request_ptr = NETINFO_REQUEST_PTR;
while (*request_ptr)
Sleep(1);
LXchgU32(request_ptr, req);
while (!res)
Sleep(1);
return req;
}

210
System/Api/Tcp.HC Normal file
View file

@ -0,0 +1,210 @@
#define TCP_SOCKET_REQUEST_PTR 0x300000
#define TCP_BIND_REQUEST_PTR 0x300040
#define TCP_ACCEPT_REQUEST_PTR 0x300050
MemSet(TCP_SOCKET_REQUEST_PTR, NULL, sizeof(U64));
// TcpSocket states
#define TCP_SOCKET_STATE_IDLE 0
#define TCP_SOCKET_STATE_ESTABLISHED 1
#define TCP_SOCKET_STATE_CLOSED 2
#define TCP_SOCKET_STATE_CONNECTING 4
class TcpSocket {
U64 remote_addr;
U64 remote_port;
U64 state;
U64 receive_buffer_ptr; // Pointer to receive buffer in physical memory
U64 receive_buffer_size;
U64 receive_buffer_filled; // Number of bytes Net has put into buffer
U64 receive_buffer_kick; // Net sets this to 1 when it has data available for
// us, we set back to 0 when ready to receive
U64 send_buffer_ptr;
U64 send_buffer_size;
U64 send_buffer_filled;
U64 send_buffer_kick; // We set this to 1 when we have data available to net,
// Net sets back to 0 when ready to receive
U0(*close)
();
U64(*receive)
(U64 buf, U64 length);
U0(*send)
(U64 buf, U64 length);
};
class TcpBind {
U64 port;
U64 function;
U64 response_code;
};
U8 @tcp_close_wrapper_function[16]
= { 0x55, 0x48, 0x8B, 0xEC, 0x68, 0x78,
0x56, 0x34, 0x12, 0xE8, 0x02, 0x6D,
0x02, 0x00, 0x5D, 0xC3 };
U8 @tcp_receive_wrapper_function[32] = {
0x55, 0x48, 0x8B, 0xEC, 0x56, 0x57, 0x48, 0x8B, 0x75, 0x18, 0x48,
0x8B, 0x7D, 0x10, 0x56, 0x57, 0x68, 0x78, 0x56, 0x34, 0x12, 0xE8,
0x5E, 0x62, 0x02, 0x00, 0x5F, 0x5E, 0x5D, 0xC2, 0x10, 0x00
};
U8 @tcp_send_wrapper_function[32] = {
0x55, 0x48, 0x8B, 0xEC, 0x56, 0x57, 0x48, 0x8B, 0x75, 0x18, 0x48,
0x8B, 0x7D, 0x10, 0x56, 0x57, 0x68, 0x78, 0x56, 0x34, 0x12, 0xE8,
0x5E, 0x62, 0x02, 0x00, 0x5F, 0x5E, 0x5D, 0xC2, 0x10, 0x00
};
U0 @tcp_socket_send(TcpSocket* s, U64 buf, U64 length)
{
while (s->send_buffer_kick)
Sleep(1);
U64 pos = 0;
U64 bytes_to_send = 0;
while (pos < length) {
if ((length - pos) > s->send_buffer_size)
bytes_to_send = s->send_buffer_size;
else
bytes_to_send = length - pos;
MemCpy(s->send_buffer_ptr, buf + pos, bytes_to_send);
s->send_buffer_filled = bytes_to_send;
s->send_buffer_kick = 1;
pos += bytes_to_send;
while (s->send_buffer_kick)
Sleep(1);
}
}
U64 @tcp_socket_receive(TcpSocket* s, U64 buf, U64 size)
{
s->receive_buffer_size = size;
s->receive_buffer_kick = 0;
while (!s->receive_buffer_kick) {
if (s->state == TCP_SOCKET_STATE_CLOSED)
return NULL;
Sleep(1);
}
U64 bytes_received = s->receive_buffer_filled;
if (bytes_received > 0) {
MemCpy(buf, s->receive_buffer_ptr, bytes_received);
}
return bytes_received;
}
U0 @tcp_wait_for_connection_established(TcpSocket* s)
{
while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
Sleep(1);
}
U0 @tcp_socket_close(TcpSocket* s)
{
if (s->close)
Free(s->close);
if (s->receive)
Free(s->receive);
if (s->send)
Free(s->send);
s->state = TCP_SOCKET_STATE_CLOSED;
}
TcpSocket* @tcp_socket_create(U8* host, U64 port)
{
U64 addr = @dns_query(host);
TcpSocket* s = CAlloc(sizeof(TcpSocket), erythros_mem_task->code_heap);
s->remote_addr = addr;
s->remote_port = port;
U64 a;
s->close = MAlloc(16, erythros_mem_task->code_heap);
MemCpy(s->close, @tcp_close_wrapper_function, 16);
a = s->close;
a += 0x05;
MemSetU32(a, s, 1);
a = s->close;
a += 0x09;
@patch_call_rel32(a, &@tcp_socket_close);
s->receive = MAlloc(25, erythros_mem_task->code_heap);
MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
a = s->receive;
a += 0x11;
MemSetU32(a, s, 1);
a = s->receive;
a += 0x15;
@patch_call_rel32(a, &@tcp_socket_receive);
s->send = MAlloc(32, erythros_mem_task->code_heap);
MemCpy(s->send, @tcp_send_wrapper_function, 32);
a = s->send;
a += 0x11;
MemSetU32(a, s, 1);
a = s->send;
a += 0x15;
@patch_call_rel32(a, &@tcp_socket_send);
U64* request_ptr = TCP_SOCKET_REQUEST_PTR;
while (*request_ptr)
Sleep(1);
LXchgU32(request_ptr, s);
return s;
}
U64 @tcp_socket_bind(U64 port, U64 function)
{
if (!port || !function)
return NULL;
TcpBind* b = CAlloc(sizeof(TcpBind), erythros_mem_task->code_heap);
b->port = port;
b->function = function; // U0 my_spawn_wrapper_function(TcpSocket* s)
U64* request_ptr = TCP_BIND_REQUEST_PTR;
while (*request_ptr)
Sleep(1);
LXchgU32(request_ptr, b);
while (*request_ptr)
Sleep(1);
U64 res = b->response_code;
Free(b);
return res;
}
TcpSocket* @tcp_socket_accept(TcpSocket* s)
{
if (!s || !s->remote_addr || !s->remote_port)
return NULL;
U64 a;
s->close = MAlloc(16, erythros_mem_task->code_heap);
MemCpy(s->close, @tcp_close_wrapper_function, 16);
a = s->close;
a += 0x05;
MemSetU32(a, s, 1);
a = s->close;
a += 0x09;
@patch_call_rel32(a, &@tcp_socket_close);
s->receive = MAlloc(25, erythros_mem_task->code_heap);
MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
a = s->receive;
a += 0x11;
MemSetU32(a, s, 1);
a = s->receive;
a += 0x15;
@patch_call_rel32(a, &@tcp_socket_receive);
s->send = MAlloc(32, erythros_mem_task->code_heap);
MemCpy(s->send, @tcp_send_wrapper_function, 32);
a = s->send;
a += 0x11;
MemSetU32(a, s, 1);
a = s->send;
a += 0x15;
@patch_call_rel32(a, &@tcp_socket_send);
return s;
}

99
System/Api/Tls.HC Normal file
View file

@ -0,0 +1,99 @@
#define TLS_CONNECT_TASK_STACK_SIZE 524288
#define TLS_CLIENT_MESSAGE_BUFFER_SIZE 0xFFFF
class TlsSocket : TcpSocket {
U64 ctx;
U8 client_message[TLS_CLIENT_MESSAGE_BUFFER_SIZE];
};
U0 @tls_send_pending(TlsSocket* s)
{
U32 out_buffer_len = 0;
U8* out_buffer = @tls_get_write_buffer(s->ctx, &out_buffer_len);
if (out_buffer && out_buffer_len) {
@tcp_socket_send(s, out_buffer, out_buffer_len);
@tls_buffer_clear(s->ctx);
}
}
U0 @tls_socket_send(TlsSocket* s, U64 buf, U64 size)
{
@tls_write(s->ctx, buf, size);
@tls_send_pending(s);
}
U64 @tls_socket_receive(TlsSocket* s, U8* buf, I64 size)
{
I64 len = @tcp_socket_receive(s, s->client_message, TLS_CLIENT_MESSAGE_BUFFER_SIZE);
if (len) {
@tls_consume_stream(s->ctx, s->client_message, len, NULL);
@tls_send_pending(s);
}
return @tls_read(s->ctx, buf, size);
}
U0 @tls12_connect(TlsSocket* s)
{
I64 len;
@tls_client_connect(s->ctx);
@tls_send_pending(s);
while (!@tls_established(s->ctx)) {
len = @tcp_socket_receive(s, &s->client_message, TLS_CLIENT_MESSAGE_BUFFER_SIZE);
if (len) {
@tls_consume_stream(s->ctx, &s->client_message, len, NULL);
@tls_send_pending(s);
}
Sleep(1);
}
}
TlsSocket* @tls_socket_create(U8* server_name, U64 port = 443)
{
U64 addr = @dns_query(server_name);
TlsSocket* s = CAlloc(sizeof(TlsSocket), erythros_mem_task->code_heap);
s->remote_addr = addr;
s->remote_port = port;
U64 a;
s->close = MAlloc(16, erythros_mem_task->code_heap);
MemCpy(s->close, @tcp_close_wrapper_function, 16);
a = s->close;
a += 0x05;
MemSetU32(a, s, 1);
a = s->close;
a += 0x09;
@patch_call_rel32(a, &@tcp_socket_close);
s->receive = MAlloc(25, erythros_mem_task->code_heap);
MemCpy(s->receive, @tcp_receive_wrapper_function, 32);
a = s->receive;
a += 0x11;
MemSetU32(a, s, 1);
a = s->receive;
a += 0x15;
@patch_call_rel32(a, &@tls_socket_receive);
s->send = MAlloc(32, erythros_mem_task->code_heap);
MemCpy(s->send, @tcp_send_wrapper_function, 32);
a = s->send;
a += 0x11;
MemSetU32(a, s, 1);
a = s->send;
a += 0x15;
@patch_call_rel32(a, &@tls_socket_send);
U64* request_ptr = TCP_SOCKET_REQUEST_PTR;
while (*request_ptr)
Sleep(1);
LXchgU32(request_ptr, s);
while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
Sleep(1);
s->ctx = @tls_create_context(0, TLS_V12);
@tls_sni_set(s->ctx, StrNew(server_name, erythros_mem_task->code_heap));
Spawn(&@tls12_connect, s, , , , TLS_CONNECT_TASK_STACK_SIZE);
return s;
}

10
System/Config/Net.json Normal file
View file

@ -0,0 +1,10 @@
{
"tcpip.ipv4_address": "10.20.0.10",
"tcpip.ipv4_netmask": "255.255.255.0",
"tcpip.ipv4_network": "10.20.0.0",
"tcpip.ipv4_gateway": "10.20.0.254",
"tcpip.ipv4_dns_server_address": "8.8.8.8",
"tcpip.ipv4_dns_server_port": "53",
"tcpip.mss_size": "1360",
"eof": "eof"
}

1112
System/Core/Compositor.HC Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
"filesystem ";

67
System/Core/Menu.HC Normal file
View file

@ -0,0 +1,67 @@
// Core component for Menu functions
#define MENU_ITEM_MIN_HEIGHT 24
#define MENU_ITEM_MIN_WIDTH 256
I64 @menu_get_items_count(Window* win)
{
I64 count = 0;
@window_widgets_list* wl = win->widget;
while (wl) {
if (wl->widget) {
if (wl->widget->type == WIDGET_TYPE_MENU_ITEM)
count++;
}
wl = wl->next;
}
return count;
}
MenuItemWidget* @menu_add_item(Window* win, U8* text, Context2D* icon,
U64 callback, U8* path = NULL,
Window* submenu = NULL)
{
I64 items_count = @menu_get_items_count(win);
win->height = 8;
win->height += MENU_ITEM_MIN_HEIGHT * (items_count + 1);
MenuItemWidget* item = Gui.CreateWidget(
win, WIDGET_TYPE_MENU_ITEM, 0, MENU_ITEM_MIN_HEIGHT * items_count,
MENU_ITEM_MIN_WIDTH, MENU_ITEM_MIN_HEIGHT);
Gui.Widget.SetText(item, text);
if (icon)
item->icon = icon;
if (path)
item->path = StrNew(path);
if (submenu)
item->submenu = submenu;
if (callback)
Gui.Widget.SetCallback(item, "clicked", callback);
return item;
}
Window* @menu_new(U8* title = NULL)
{
Window* menu = Compositor.CreateWindow(
0, 0, MENU_ITEM_MIN_WIDTH, MENU_ITEM_MIN_HEIGHT,
WIN_FLAGS_NOHILIGHT | WIN_FLAGS_SKIP | WIN_FLAGS_MENU);
Gui.Window.Hide(menu);
if (title)
Gui.Window.SetTitle(menu, title);
else
Gui.Window.SetTitle(menu, "");
return menu;
}
class @menu
{
MenuItemWidget* (*AddItem)(Window* win, U8* text, Context2D* icon,
U64 callback, U8* path = NULL,
Window* submenu = NULL);
Window* (*New)(U8* title);
};
@menu Menu;
Menu.AddItem = &@menu_add_item;
Menu.New = &@menu_new;
"menu ";

108
System/Core/MessageBox.HC Normal file
View file

@ -0,0 +1,108 @@
// Core component for MessageBox functions
Context2D* MESSAGEBOX_ICON_ERR = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_critical.png");
Context2D* MESSAGEBOX_ICON_INFO = Image.FileToContext2D("/Media/Themes/Umami/Icon/status/messagebox_info.png");
Context2D* MESSAGEBOX_ICON_WARN = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_warning.png");
Context2D* MESSAGEBOX_WIN_ICON_ERR = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_critical_16x16.png");
Context2D* MESSAGEBOX_WIN_ICON_INFO = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_info_16x16.png");
Context2D* MESSAGEBOX_WIN_ICON_WARN = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_warning_16x16.png");
#define MESSAGEBOX_TYPE_ALERT 0
#define MESSAGEBOX_TYPE_ERROR 1
#define MESSAGEBOX_TYPE_INFO 2
U0 @messagebox_close_window(Window* window)
{
Compositor.DestroyWindow(window);
}
U0 @messagebox_close_widget(Widget* widget)
{
Compositor.DestroyWindow(widget->parent_win);
}
U0 @messagebox_msg(U8* str, I64 type, Context2D* win_icon, Context2D* icon,
U8* options = NULL, U64 callback = NULL)
{
U64 flags = WIN_FLAGS_MOVABLE | WIN_FLAGS_ICON | WIN_FLAGS_TITLE_BAR | WIN_FLAGS_CLOSE_BUTTON;
Window* win = Compositor.CreateWindow(0, 0, 320, 192, flags);
Gui.Window.SetIcon(win, win_icon);
switch (type) {
case MESSAGEBOX_TYPE_ALERT:
Gui.Window.SetTitle(win, "Alert");
break;
case MESSAGEBOX_TYPE_ERROR:
Gui.Window.SetTitle(win, "Error");
break;
case MESSAGEBOX_TYPE_INFO:
Gui.Window.SetTitle(win, "Info");
break;
}
Context2DWidget* ctx_icon = Gui.CreateWidget(win, WIDGET_TYPE_CONTEXT2D, 8, 16, 24, 24);
ctx_icon->ctx = icon;
TextLabelWidget* lbl_text = Gui.CreateWidget(win, WIDGET_TYPE_LABEL, 40, 16, 192, 96);
ButtonWidget* btn_ok = NULL;
ButtonWidget* btn_cancel = NULL;
Gui.Widget.SetText(lbl_text, str);
Gui.Window.SetCallback(win, "close", callback);
if (options) {
btn_ok = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) - 80,
win->height - 60, 64, 24);
btn_cancel = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) + 16,
win->height - 60, 64, 24);
Gui.Widget.SetText(btn_ok, " OK ");
Gui.Widget.SetText(btn_cancel, " Cancel ");
Gui.Widget.SetCallback(btn_ok, "clicked", callback);
Gui.Widget.SetCallback(btn_cancel, "clicked", callback);
btn_ok->tag = TRUE;
btn_cancel->tag = FALSE;
} else {
btn_ok = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) - 32,
win->height - 60, 64, 24);
Gui.Widget.SetText(btn_ok, " OK ");
Gui.Widget.SetCallback(btn_ok, "clicked", callback);
btn_ok->tag = TRUE;
}
Gui.Window.Center(win);
Gui.Window.SetFocus(win);
}
U0 @messagebox_alert(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_ALERT, MESSAGEBOX_WIN_ICON_WARN,
MESSAGEBOX_ICON_WARN, options, callback);
}
U0 @messagebox_error(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_ERROR, MESSAGEBOX_WIN_ICON_ERR,
MESSAGEBOX_ICON_ERR, options, callback);
}
U0 @messagebox_info(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_INFO, MESSAGEBOX_WIN_ICON_INFO,
MESSAGEBOX_ICON_INFO, options, callback);
}
class @messagebox
{
U0* (*Alert)(U8* str, U8* options = NULL, U64 callback = NULL);
U0* (*Error)(U8* str, U8* options = NULL, U64 callback = NULL);
U0* (*Info)(U8* str, U8* options = NULL, U64 callback = NULL);
};
@messagebox MessageBox;
MessageBox.Alert = &@messagebox_alert;
MessageBox.Error = &@messagebox_error;
MessageBox.Info = &@messagebox_info;
"messagebox ";

79
System/Core/Scheduler.HC Normal file
View file

@ -0,0 +1,79 @@
/* clang-format off */
U0 @scheduler_restore_page_table(U64 i) {
U64 reg R15 _pt = i.u32[1];
if (_pt) { // Use CTask's page table entries
asm {
MOV RAX, R15
MOV_CR3_RAX
}
} else { // Use identity mapping
asm {
MOV EAX, [MEM_PML4]
MOV_CR3_RAX
}
}
}
asm {
ERYTHROS_TASK_CONTEXT_RESTORE_START::
XOR RAX,RAX
INC U64 GS:CCPU.swap_cnter[RAX]
MOV RSI,FS:CTask.addr[RAX]
BT U32 CTask.rflags[RSI],RFLAGf_INT
JNC @@05
BTS U32 GS:CCPU.cpu_flags[RAX],CPUf_RAN_A_TASK
@@05: BT U64 CTask.task_flags[RSI],TASKf_DISABLE_BPTS
JC @@15
MOV RDX,U64 CTask.bpt_lst[RSI]
@@10: TEST RDX,RDX
JZ @@15
MOV RDI,U64 CBpt.addr[RDX]
MOV U8 [RDI],OC_BPT
MOV RDX,U64 CBpt.next[RDX]
JMP @@10
@@15: INC U64 CTask.swap_cnter[RSI]
MOV RAX, U64 CTask.user_data[RSI]
PUSH RAX
CALL I32 &@scheduler_restore_page_table
MOV RAX,U64 CTask.fpu_mmx[RSI]
FXRSTOR U64 [RAX]
MOV RAX,RSP
LEA RSP,U64 CTask.rcx[RSI]
POP RCX
POP RDX
POP RBX
POP RBP
POP RDI
POP R8
POP R9
POP R10
POP R11
POP R12
POP R13
POP R14
POP R15
MOV RSP,RAX
MOV RAX,U64 CTask.rax[RSI]
PUSH CGDT.ds
PUSH U64 CTask.rsp[RSI]
PUSH U64 CTask.rflags[RSI]
PUSH CGDT.cs64
PUSH U64 CTask.rip[RSI]
MOV RSI,U64 CTask.rsi[RSI]
IRET
ERYTHROS_TASK_CONTEXT_RESTORE_END::
NOP
//************************************
}
/* clang-format on */
Function.Patch(_TASK_CONTEXT_RESTORE, ERYTHROS_TASK_CONTEXT_RESTORE_START);
"scheduler ";

282
System/Core/Shell.HC Normal file
View file

@ -0,0 +1,282 @@
#define SHELL_OPTS_ERR_INVALID_OPT -1
#define SHELL_OPTS_ERR_EXTRA_OPD -2
U0 @shell_free_args(I64 argc, U8** argv)
{
I64 i;
for (i = 0; i < argc; i++)
Free(argv[i]);
if (argv)
Free(argv);
}
U8** @shell_parse_args(@shell* sh, U8* str,
I64* argc)
{ // Return argc, argv from str.
Bool quoted = FALSE;
I64 _argc = 0;
U8** _argv = NULL;
U8** _tmp = CAlloc(sizeof(U64) * StrLen(str));
I64 i = 0;
I64 s = 0;
I64 len;
while (i < StrLen(str) + 1) {
switch (str[i]) {
case 0:
case ' ':
if (!quoted) {
len = (str + i) - (str + s - 1);
if (str[i - 1] == '"')
len--;
if (len - 1) {
_tmp[_argc] = CAlloc(len);
MemCpy(_tmp[_argc], str + s, len - 1);
_argc++;
}
s = i + 1;
}
break;
case '"':
quoted = !quoted;
if (quoted)
s = i + 1;
break;
default:
break;
}
i++;
}
*argc = _argc;
_argv = CAlloc(sizeof(U64) * _argc);
MemCpy(_argv, _tmp, sizeof(U64) * _argc);
Free(_tmp);
return _argv;
}
I64 @shell_parse_opts(@shell* sh, U8* op_lst, I64 argc, U8** argv, I64* flags,
U64* op_err, Bool ignore_extra_opd = FALSE)
{
I64 i, j;
U8 op_chr[2];
op_chr[1] = 0;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
for (j = 1; j < StrLen(argv[i]); j++) {
op_chr[0] = argv[i][j];
if (StrFind(&op_chr, op_lst))
*flags |= 1 << (StrFind(&op_chr, op_lst) - op_lst);
else {
*op_err = StrNew(&op_chr);
return SHELL_OPTS_ERR_INVALID_OPT;
}
}
} else {
if (!ignore_extra_opd) {
*op_err = StrNew(argv[i]);
return SHELL_OPTS_ERR_EXTRA_OPD;
}
}
}
return 0;
}
U8* @shell_expand_relative_path(@shell* sh, U8* path)
{
if (!path || !sh)
return NULL;
if (StrLen(path) < 1)
return NULL;
switch (path[0]) {
case '/':
return StrNew(path);
break;
default:
U8* abs_path = CAlloc(StrLen(path) + StrLen(&sh->cwd) + 4);
StrPrint(abs_path, "%s/%s", &sh->cwd, path);
return abs_path;
break;
}
}
U8* @shell_get_env_var(@shell* sh, U8* key)
{
@shell_env_var* var = sh->env->next;
while (var) {
if (StrLen(&var->key) && StrLen(&var->value))
if (!StrCmp(&var->key, key))
return &var->value;
var = var->next;
}
return "";
}
U0 @shell_set_env_var(@shell* sh, U8* key, U8* value)
{
if (!sh || !key || !value)
return;
@shell_env_var* var = sh->env->next;
while (var->next) {
if (!StrCmp(&var->key, key)) {
StrCpy(&var->value, value);
return;
}
var = var->next;
}
@shell_env_var* new = CAlloc(sizeof(@shell_env_var));
StrCpy(&new->key, key);
StrCpy(&new->value, value);
new->prev = var;
var->next = new;
}
U0 @shell_unset_env_var(@shell* sh, U8* key)
{
@shell_env_var* var = sh->env->next;
@shell_env_var* prev = NULL;
@shell_env_var* next = NULL;
while (var) {
if (!StrCmp(&var->key, key)) {
prev = var->prev;
next = var->next;
if (prev)
prev->next = next;
if (next)
next->prev = prev;
Free(var);
return;
}
var = var->next;
}
}
U0 @shell_history_append(@shell* sh, U8* str)
{
if (!sh || !str)
return;
if (!StrCmp(str, ""))
return;
I64 i;
sh->history.entries[sh->history.pos] = StrNew(str);
sh->history.pos++;
if (sh->history.pos > SHELL_HISTORY_LIMIT - 1) {
Free(sh->history.entries[0]);
for (i = 0; i < SHELL_HISTORY_LIMIT; i++)
sh->history.entries[i] = sh->history.entries[i + 1];
sh->history.pos--;
}
}
U0 @shell_process_args(@shell* sh, I64 argc, U8** argv)
{
if (argc < 1 || !argv)
return;
I64 i;
U8 buf[256];
for (i = 0; i < argc; i++) {
if (argv[i][0] == '\d') {
switch (argv[i][1]) {
case '?':
Free(argv[i]);
StrPrint(&buf, "%d", sh->answer);
argv[i] = StrNew(&buf);
break;
case '0':
Free(argv[i]);
argv[i] = StrNew("esh");
break;
default:
Free(argv[i]);
argv[i] = StrNew(@shell_get_env_var(sh, argv[i] + 1));
break;
}
}
if (!StrCmp(argv[i], "~")) {
Free(argv[i]);
argv[i] = StrNew(&sh->session->home);
}
}
}
U0 @shell_update_prompts(@shell* sh)
{
U8 buf[512];
U8 buf2[512];
StrCpy(&buf, &sh->cwd);
StrPrint(buf2, "/home/%s", &sh->session->user.name);
if (!StrCmp(&buf, &buf2))
StrCpy(&buf, "~");
else
StrCpy(&buf, StrLastOcc(&buf, "/") + 1);
StrPrint(&sh->PS1, "[%s@%s %s]\d ", &sh->session->user.name,
&sh->session->hostname, &buf);
}
I64 @shell_input_loop(@shell* sh)
{
CHashFun* cmd;
I64 argc;
U8** argv;
U8 buf[4096];
Bool exit = FALSE;
I64 i;
I64 (*@shell_exec)(@shell* sh, I64 argc, U8** argv);
while (!exit) {
@shell_update_prompts(sh);
Stdio.WriteLine(sh, &sh->PS1);
Stdio.ReadLine(sh, &buf);
@shell_history_append(sh, &buf);
argv = @shell_parse_args(sh, &buf, &argc);
if (argc) {
if (!StrCmp(argv[0], "exit")) {
exit = TRUE;
goto @shell_exit;
}
@shell_process_args(sh, argc, argv);
StrPrint(&buf, "@shell_cmd_%s", argv[0]);
cmd = HashFind(&buf, adam_task->hash_table, HTT_FUN);
if (cmd) {
@shell_exec = cmd->exe_addr;
sh->answer = @shell_exec(sh, argc, argv);
sh->break = FALSE;
FifoU8Flush(sh->input);
} else {
StrPrint(&buf, "%s: command not found\n", argv[0]);
Stdio.WriteLine(sh, &buf);
sh->answer = 0;
}
}
@shell_exit : @shell_free_args(argc, argv);
}
return 0;
}
U0 @shell_instance(@shell* sh)
{
@shell_input_loop(sh);
sh->exit = TRUE;
}
U0 @shell_init(@shell* sh)
{
sh->env = CAlloc(sizeof(@shell_env_var));
sh->history.limit = SHELL_HISTORY_LIMIT;
sh->history.pos = 0;
sh->history.entries = CAlloc(sizeof(U64) * SHELL_HISTORY_LIMIT);
sh->input = FifoU8New(SHELL_INPUT_FIFO_SIZE);
sh->task = Spawn(&@shell_instance, sh);
}
@shell* @shell_new(Bool headless = FALSE)
{
@shell* sh = CAlloc(sizeof(@shell));
if (!headless)
@shell_init(sh);
StrCpy(&sh->cwd, &Compositor.session.home);
return sh;
}
"shell ";

View file

@ -0,0 +1,13 @@
extern I64 @systemstarter_open(@shell* sh, I64 argc, U8** argv);
CDirEntry* sc_de = FilesFind("M:/System/Shell/Commands/*.HC");
CDirEntry* sc_de2 = sc_de;
while (sc_de2) {
if (!(!StrCmp(sc_de2->name, ".") && !StrCmp(sc_de2->name, ".."))) {
ExeDoc(DocRead(sc_de2->full_name));
}
sc_de2 = sc_de2->next;
}
DirTreeDel(sc_de);
"shellcommands ";

View file

@ -0,0 +1,214 @@
#define SYSSTART_MSG_NULL 0
#define SYSSTART_MSG_SPAWN 1
#define SYSSTART_MSG_KILL 2
class @systemtask
{
U8* path;
U8* name;
};
class @systemstarter
{
CTask* task;
U0 (*CreateTask)(U8* path, U8* name);
U0 (*Init)();
U0 (*Spawn)(U8* path, U8* name);
U0 (*Task)();
};
@systemstarter SystemStarter;
U32 @systemstarter_ext(U8* path)
{
U32 res = NULL;
U8* ext = FileSystem.GetFileExtension(path);
MemCpy(&res, ext, StrLen(ext));
return res;
}
U0 @systemstarter_init() { }
I64 @systemstarter_open(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
U8* path = @shell_expand_relative_path(sh, argv[1]);
if (!FileSystem.PathExists(path)) {
Stdio.WriteLine(sh, "error: path does not exist: ");
Stdio.WriteLine(sh, path);
Stdio.WriteLine(sh, "\n");
Free(path);
return 1;
}
switch (@systemstarter_ext(path)) {
case 'app':
StrCpy(&buf, "");
String.Append(&buf, "I64 @exe_doc_buf_size = NULL; ");
String.Append(&buf,
"U8 *@exe_doc_buf = FileSystem.ReadFile(\"%s/Run.HC\", "
"&@exe_doc_buf_size); ",
path);
String.Append(&buf, "ExePutS(@exe_doc_buf); ");
String.Append(&buf, "Free(@exe_doc_buf); ");
// System.Log(Fs, &buf);
CTask* task = User;
TaskExe(task, NULL, "Raw(ON);\n", 0);
TaskExe(task, NULL, &buf, 0);
return 0;
break;
default:
Stdio.WriteLine(sh, "error: unknown or unsupported file type.\n");
// Free(path);
return 1;
break;
};
}
U0 @systemstarter_spawn(U8* path, U8* name)
{
CTask* task = Spawn(&UserCmdLine);
Sleep(Rand * 100);
U8 change_path_str[512];
StrPrint(task->task_name, name);
StrPrint(change_path_str, "Cd(\"%s\");\n", path);
TaskExe(task, NULL, "Raw(ON);\n", 0);
TaskExe(task, NULL, change_path_str, 0);
TaskExe(task, NULL, "ExeFile(\"Run.HC\");\n", 0);
Sleep(Rand * 100);
}
U0 @systemstarter_load_applets()
{
U8 applet_name[512];
CDirEntry* de = FilesFind("M:/Applets/*.applet");
CDirEntry* tmpde = de;
while (tmpde) {
StrCpy(&applet_name, StrLastOcc(tmpde->full_name, "/") + 1);
*(StrFirstOcc(&applet_name, ".")) = NULL;
SystemStarter.Spawn(tmpde->full_name, &applet_name);
tmpde = tmpde->next;
}
DirTreeDel(de);
}
U0 @systemstarter_play_user_startup_sound()
{
U8 path[512];
StrPrint(&path, "/home/%s/.sounds/startup.wav",
&Compositor.session.user.name);
U8** argv = CAlloc(sizeof(U64) * 2);
argv[0] = "aplay";
argv[1] = &path;
@shell* sh = @shell_new(TRUE);
sh->session = &Compositor.session;
//@shell_cmd_aplay(sh, 2, argv);
Free(sh);
}
U0 @systemstarter_set_user_wallpaper()
{
U8 path[512];
StrPrint(&path, "/home/%s/.wallpaper/wallpaper.png",
&Compositor.session.user.name);
U8** argv = CAlloc(sizeof(U64) * 2);
argv[0] = "wpset";
argv[1] = &path;
//@shell_cmd_wpset(NULL, 2, argv);
}
U0 @systemstarter_user_startup()
{
// Set User wallpaper
@systemstarter_set_user_wallpaper();
// Play User startup sound
@systemstarter_play_user_startup_sound();
}
U0 @systemstarter_startup()
{
// Set user-specific startup preferences
Spawn(&@systemstarter_user_startup);
// Initialize Clipboard
Clipboard.Init();
// Spawn Clipboard Task
Spawn(Clipboard.Task, , "Clipboard", T(mp_cnt > 3, 2, 1));
// Initialize SystemTray
SystemTray.Init();
// Spawn SystemTray Task
Spawn(SystemTray.Task, , "SystemTray", T(mp_cnt > 3, 2, 1));
SystemStarter.Spawn("M:/Applications/OS/Wallpaper.app", "Wallpaper");
SystemStarter.Spawn("M:/Applications/OS/MenuBar.app", "MenuBar");
SystemStarter.Spawn("M:/Applications/OS/TaskSwitcher.app",
"TaskSwitcher");
// Load SystemTray Applets
@systemstarter_load_applets;
}
U0 @systemstarter_ipc_queue_process()
{
IpcMessage* msg;
@systemtask* st = NULL;
msg = Ipc.MsgRecv();
if (msg) {
switch (msg->type) {
case SYSSTART_MSG_SPAWN:
if (msg->payload) {
st = msg->payload;
if (st->path && st->name) {
SystemStarter.Spawn(st->path, st->name);
System.Log(Fs, "Received message ← CreateTask (%s)", st->name);
Free(st->name);
Free(st->path);
}
Free(st);
}
break;
case SYSSTART_MSG_KILL:
break;
default:
break;
}
Free(msg);
}
}
U0 @systemstarter_task()
{
Ipc.InitQueue(Fs);
System.Log(Fs, "Task running at 0x%08x", Fs);
SystemStarter.task = Fs;
Spawn(&@systemstarter_startup, , , T(mp_cnt, 1, 0));
while (1) {
@systemstarter_ipc_queue_process;
Sleep(1);
}
}
U0 @systemstarter_create_task(U8* path, U8* name)
{
@systemtask* st = CAlloc(sizeof(@systemtask));
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
st->path = StrNew(path);
st->name = StrNew(name);
msg->client = NULL;
msg->type = SYSSTART_MSG_SPAWN;
msg->payload = st;
Ipc.MsgSend(SystemStarter.task, msg);
}
SystemStarter.CreateTask = &@systemstarter_create_task;
SystemStarter.Init = &@systemstarter_init;
SystemStarter.Spawn = &@systemstarter_spawn;
SystemStarter.Task = &@systemstarter_task;
"systemstarter ";

139
System/Core/SystemTray.HC Normal file
View file

@ -0,0 +1,139 @@
#define SYSTRAY_MSG_NULL 0x0
#define SYSTRAY_MSG_REGISTER 0x1
#define SYSTRAY_MSG_UNREGISTER 0x2
class @systemtray
{
CTask* task;
@window_widgets_list* item;
U0 (*Init)();
U0 (*Task)();
Context2DWidget* (*RegisterItem)();
U0 (*UnregisterItem)(Widget* widget);
U0 (*SetIcon)(Context2DWidget* widget, U8* path);
};
@systemtray SystemTray;
U0 @systemtray_register_item(I64 addr)
{
Context2DWidget* item = Gui.CreateWidget(
Compositor.menubar.win, WIDGET_TYPE_CONTEXT2D, -24, 0, 24, 24);
item->ctx = NewContext2D(item->width, item->height);
Fill2D(item->ctx, Color(0, 0, 0, 0));
MemSetI64(addr, item, 1);
}
U0 @systemtray_unregister_item(Widget* item)
{
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = CPZ_MSG_WIN_WIDGET_DESTROY;
msg->payload = item;
System.Log(Fs, "Sent message → WidgetDestroy");
Ipc.MsgSend(Compositor.menubar.task, msg);
}
U0 @systemtray_reindex_items()
{
I64 x = Display.Width() - 100;
@window_widgets_list* item = Compositor.menubar.win->widget;
while (item->next)
item = item->next;
while (item->widget->type == WIDGET_TYPE_CONTEXT2D) {
x -= item->widget->width + 4;
item->widget->x = x;
item = item->prev;
}
Gui.Window.Refresh(Compositor.menubar.win);
}
U0 @systemtray_ipc_queue_process()
{
IpcMessage* msg;
msg = Ipc.MsgRecv();
if (msg) {
switch (msg->type) {
case SYSTRAY_MSG_REGISTER:
@systemtray_register_item(msg->payload);
@systemtray_reindex_items;
break;
case SYSTRAY_MSG_UNREGISTER:
@systemtray_unregister_item(msg->payload);
@systemtray_reindex_items;
break;
default:
break;
}
Free(msg);
}
}
U0 @systemtray_init() { }
U0 @systemtray_task()
{
Ipc.InitQueue(Fs);
SystemTray.task = Fs;
System.Log(Fs, "Task running at 0x%08x", Fs);
while (!Compositor.menubar.win) // Wait for instance
Sleep(1);
while (1) {
@systemtray_ipc_queue_process();
Sleep(1);
}
}
Context2DWidget* @systemtray_client_register_item()
{
Context2DWidget* item = NULL;
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = SYSTRAY_MSG_REGISTER;
msg->payload = &item;
System.Log(Fs, "Sent message → SystrayRegisterItem");
Ipc.MsgSend(SystemTray.task, msg);
while (!item)
Sleep(1);
return item;
}
U0 @systemtray_client_unregister_item(Widget* item)
{
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = SYSTRAY_MSG_UNREGISTER;
msg->payload = item;
System.Log(Fs, "Sent message → SystrayUnRegisterItem");
Ipc.MsgSend(SystemTray.task, msg);
while (item->type)
Sleep(1);
@systemtray_reindex_items;
}
U0 @systemtray_set_icon(Context2DWidget* widget, U8* path)
{
if (!widget || !path)
return;
U8 full_path[512];
if (2 == 3) // FIXME
StrCpy(&full_path, path);
else {
StrCpy(&full_path, "M:/Media/Themes/Umami/Icon/");
String.Append(&full_path, path);
}
if (!FileFind(&full_path))
return;
Context2D* icon = Image.FileToContext2D(&full_path);
CopyRect2D(widget->ctx, 0, 0, icon);
DelContext2D(icon);
Gui.Window.Refresh(Compositor.menubar.win);
}
SystemTray.Init = &@systemtray_init;
SystemTray.RegisterItem = &@systemtray_client_register_item;
SystemTray.SetIcon = &@systemtray_set_icon;
SystemTray.Task = &@systemtray_task;
SystemTray.UnregisterItem = &@systemtray_client_unregister_item;
"systemtray ";

153
System/Drivers/AC97.HC Normal file
View file

@ -0,0 +1,153 @@
#define INT_LAST_VALID_ENTRY 1 << 2
#define INT_IOC 1 << 3
#define INT_FIFO_ERR 1 << 4
#define BDL_BUF_SIZE 2044
#define PCM_BUF_SIZE 2048
#define MAX_BDLS 32
#define PCM_IN 0
#define PCM_OUT 1
#define MIC_IN 2
// Native Audio Mixer registers (all U16)
#define RESET 0x00 // Reset Register
#define MASTER_VOL 0x02 // Set Master Output Volume
#define MIC_VOL 0x0E // Set Microphone Volume
#define PCM_VOL 0x18 // Set Output Volume of PCM patterns
#define REC_SLC 0x1A // Select Input Device
#define REC_GAIN 0x1C // Set Input Gain
#define MIC_GAIN 0x1E // Set Gain of Microphone
#define EXT_ID 0x28 // Supported extended functions
#define EXT_CTRL 0x2A // Enabling extended functions
#define EXT_FRONT_RATE 0x2C // Sample rate of front speaker
// Native Audio Bus Master registers
#define PCM_INPUT_REG_BOX \
0x00 // NABM register box for PCM IN (sizeof NABM register box)
#define PCM_OUTPUT_REG_BOX \
0x10 // NABM register box for PCM OUT (sizeof NABM register box)
#define MIC_INPUT_REG_BOX \
0x20 // NABM register box for Microphone (sizeof NABM register box)
#define GLOBAL_CTL 0x2C // Global Control Register (U32)
#define GLOBAL_STS 0x30 // Global Status Register (U32)
// NABM register box registers
#define BUFFER_DSC_ADDR 0x00 // Physical Address of Buffer Descriptor List (U32)
#define CUR_ENTRY_VAL \
0x04 // Number of Actual Processed Buffer Descriptor Entry (U8)
#define LAST_VALID_ENTRY 0x05 // Number of all Descriptor Entries (U8)
#define TRANSFER_STS 0x06 // Status of Transferring Data (U16)
#define CUR_IDX_PROC_SAMPLES \
0x08 // Number of Transferred Samples in Actual Processed Entry (U16)
#define PRCSD_ENTRY 0x0A // Number of Actual Processed Buffer Entry (U8)
#define BUFFER_CNT \
0x0B // Most Important Register for controlling Transfers (U8)
class @ac97_bdl_entry
{
U32 addr;
U16 length; // length - 1
U16 flags;
};
class @ac97_bdl
{
@ac97_bdl_entry entries[32];
};
class @ac97
{
@pci_info pci;
@ac97_bdl* bdl[3];
U16 nam;
U16 nabm;
};
@ac97 AC97;
U0 @ac97_fill_buffer()
{
I64 idx = InU8(AC97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY);
U32* buf = AC97.bdl[PCM_OUT]->entries[idx].addr;
@audio_mix_output(buf, BDL_BUF_SIZE);
OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY, ++idx);
}
U0 @ac97_int_handler()
{
U16 status = InU16(AC97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS);
if (status & INT_IOC) {
@ac97_fill_buffer;
OutU16(AC97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS, 0x1C);
}
}
I64 @ac97_init()
{
I64 i;
I64 j;
// Scan for device
j = PCIClassFind(0x040100, 0);
if (j < 0) {
device_not_found:
AdamLog("\n[AC'97] Device not found\n");
return -1;
}
@get_pci_info(j, &AC97.pci);
if (AC97.pci.vendor_id != 0x8086 || AC97.pci.device_id != 0x2415)
goto device_not_found;
AC97.nam = AC97.pci.bar[0] & 0xFFFFFF00;
AC97.nabm = AC97.pci.bar[1] & 0xFFFFFF00;
// Enable port IO, disable MMIO
PCIWriteU8(j.u8[2], j.u8[1], j.u8[0], 0x4, 5);
OutU32(AC97.nabm + GLOBAL_CTL, 0x03);
OutU16(AC97.nam + RESET, 0xFFFF);
// Set PCM Output to Max volume
OutU16(AC97.nam + PCM_VOL, 0x0000);
// Allocate Buffer Descriptor Lists
AC97.bdl[PCM_IN] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap);
AC97.bdl[PCM_OUT] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap);
AC97.bdl[MIC_IN] = CAllocAligned(sizeof(@ac97_bdl), 4096, Fs->code_heap);
for (i = 0; i < MAX_BDLS; i++) {
AC97.bdl[PCM_OUT]->entries[i].addr = CAllocAligned(PCM_BUF_SIZE, 4096, Fs->code_heap);
AC97.bdl[PCM_OUT]->entries[i].length = BDL_BUF_SIZE / 2;
AC97.bdl[PCM_OUT]->entries[i].flags = 1 << 15;
}
// Set addresses of Buffer Descriptor Lists
// OutU32(AC97.nabm + PCM_INPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[PCM_IN]);
OutU32(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[PCM_OUT]);
// OutU32(AC97.nabm + MIC_INPUT_REG_BOX + BUFFER_DSC_ADDR, AC97.bdl[MIC_IN]);
// Set Master Volume
OutU16(AC97.nam + MASTER_VOL, 0x0F0F);
// Stop playing sound
OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 0);
// Fill one buffers
@ac97_fill_buffer;
// Enable interrupt handler
@pci_register_int_handler(&@ac97_int_handler);
// Start playing sound
OutU8(AC97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 1);
return 0;
}
@ac97_init;
"ac97 ";

117
System/Drivers/Audio.HC Normal file
View file

@ -0,0 +1,117 @@
extern U0 (*fp_snd_fill_buf)(U32* buf, I64 buf_num);
#define AUDIO_MAX_STREAMS 16
#define AUDIO_OUTPUT_BUFFER_SIZE 1024
#define AUDIO_STREAM_FIFO_SIZE 1048576
#define AUDIO_STREAM_TYPE_INPUT 0
#define AUDIO_STREAM_TYPE_OUTPUT 1
class Sound {
// For simplicity, all samples will be converted to 44100 Hz, 2 channels, 16
// bit when they are loaded.
I64 rate;
I64 channels;
I64 bits;
U32* data;
I64 length; // in samples
};
class @audio_device
{
Bool enabled;
};
class @audio_mixer
{
I64 left;
I64 right;
};
class @audio_stream
{
I64 type;
I64 rate;
I64 channels;
I64 bits;
CFifoI64* data;
};
class @audio_wave_generator
{
F64 duration;
I64 frequency;
};
class @audio
{
I64 driver;
@audio_device device;
@audio_mixer mixer;
@audio_stream output[AUDIO_MAX_STREAMS + 1];
@audio_wave_generator wavegen;
U0 (*Beep)();
U0 (*Init)();
U0 (*MixOutput)(U64 buf, I64);
Sound (*SoundFromFile)(U8* filename);
U0 (*FreeSound)(Sound* snd);
I64 (*PlaySound)(Sound* snd);
};
@audio Audio;
U0 @audio_mix_output(U32* buf, I64 length = NULL)
{
I64 i;
I64 j;
I64 acc_sample_L = 0;
I64 acc_sample_R = 0;
I64 acc_streams = 0;
U32 sample;
if (!length)
length = AUDIO_OUTPUT_BUFFER_SIZE;
for (i = 0; i < length / 4; i++) {
acc_sample_L = 0;
acc_sample_R = 0;
acc_streams = 0;
if (Audio.wavegen.frequency) {
sample.i16[0] = T(Sin(Audio.wavegen.frequency * Audio.wavegen.duration) >= 0.0,
I16_MAX / 8, I16_MIN / 8);
sample.i16[1] = sample.i16[0];
FifoI64Ins(Audio.output[AUDIO_MAX_STREAMS].data, sample);
Audio.wavegen.duration += 6.4 / 48000.0;
}
for (j = 0; j < AUDIO_MAX_STREAMS + 1; j++) {
if (FifoI64Cnt(Audio.output[j].data)) {
FifoI64Rem(Audio.output[j].data, &sample);
acc_streams++;
acc_sample_L += sample.i16[0];
acc_sample_R += sample.i16[1];
}
}
buf[i].i16[0] = ToI64(acc_sample_L / Sqrt(acc_streams) * Audio.mixer.left / 100);
buf[i].i16[1] = ToI64(acc_sample_R / Sqrt(acc_streams) * Audio.mixer.right / 100);
}
}
U0 @audio_init()
{
I64 i = 0;
for (i = 0; i < AUDIO_MAX_STREAMS + 1; i++)
Audio.output[i].data = FifoI64New(AUDIO_STREAM_FIFO_SIZE);
Audio.mixer.left = 100;
Audio.mixer.right = 100;
Audio.wavegen.duration = 0.0;
Audio.wavegen.frequency = 0;
Audio.device.enabled = TRUE;
}
Audio.driver = NULL;
Audio.Init = &@audio_init;
Audio.MixOutput = &@audio_mix_output;
// Initialize Audio
Audio.Init();
"audio ";

26
System/Drivers/Display.HC Normal file
View file

@ -0,0 +1,26 @@
class @display
{
I64 width;
I64 height;
I64 bpp;
I64 driver;
U64 fb;
U0(*Init)
(I64 width, I64 height, I64 bpp, I64 driver);
I64(*Width)
();
I64(*Height)
();
I64(*Bpp)
();
I64(*Driver)
();
U0 (*Update)();
};
@display Display;
Display.driver = NULL;
Display.Update = NULL;
"display ";

111
System/Drivers/Mouse.HC Normal file
View file

@ -0,0 +1,111 @@
// FIXME: This should be an Input driver which contains both Keyboard/Mouse
// classes.
extern U0 @vmsvga_mouse_pointer_set(U32* pointer, I64 width, I64 height);
#define MI_QEMU 0x01
#define MI_VBOX 0x02
#define MS_UPDATE_INTERVAL 10
#define MS_UP 0
#define MS_DOWN 1
class @mouse
{
I64 x;
I64 y;
I64 z;
I64 delta_z;
I64 wheel_sensitivity;
U32* pointer;
I64 integration_type;
Bool left;
Bool right;
Bool natural_scroll;
I64 (*X)();
I64 (*Y)();
U0 (*PointerSet)(U32* pointer, I64 width, I64 height);
U0 (*Init)();
U0 (*Update)();
U0 (*Task)();
};
class @keyboard
{
I64 active_key;
I64 active_key_tS;
I64 last_key_tS;
U0 (*Update)();
};
@mouse Mouse;
@keyboard Keyboard;
U0 @keyboard_update()
{
I64 sc;
if (FifoI64Rem(kbd.scan_code_fifo, &sc)) {
if (!(sc & SCF_KEY_UP)) {
Keyboard.active_key = sc(U8);
Keyboard.active_key_tS = cnts.jiffies;
return;
}
}
Keyboard.active_key = 0;
}
Keyboard.Update = &@keyboard_update;
I64 @mouse_x() { return Mouse.x; }
I64 @mouse_y() { return Mouse.y; }
U0 @mouse_integration_type_set(I64 type) { Mouse.integration_type = type; }
U0 @mouse_pointer_set(U32* pointer, I64 width, I64 height)
{
if (Mouse.pointer != pointer) {
Mouse.pointer = pointer;
if (Mouse.integration_type == MI_VBOX) {
@vmsvga_mouse_pointer_set(pointer, width, height);
}
}
}
U0 @mouse_init()
{
Mouse.x = Display.Width() / 2;
Mouse.y = Display.Height() / 2;
Mouse.z = ms.pos.z;
Mouse.wheel_sensitivity = 2;
Mouse.pointer = NULL;
Mouse.left = OFF;
Mouse.right = OFF;
}
U0 @mouse_task()
{
while (1) {
WinMsUpdate;
KbdMsHndlr(0, 0);
Keyboard.Update();
if (Mouse.Update)
Mouse.Update();
if (!Mouse.Update) {
// Mouse.x = ms.pos.x;
// Mouse.y = ms.pos.y;
Mouse.left = ms.lb > 0;
Mouse.right = ms.rb > 0;
}
Mouse.z = ms.pos.z;
Sleep(MS_UPDATE_INTERVAL);
}
}
Mouse.X = &@mouse_x;
Mouse.Y = &@mouse_y;
Mouse.PointerSet = &@mouse_pointer_set;
Mouse.Init = &@mouse_init;
Mouse.Update = NULL;
Mouse.Task = &@mouse_task;
"mouse ";

98
System/Drivers/Pci.HC Normal file
View file

@ -0,0 +1,98 @@
#define PCI_INTH_MAX 16
U64 @pci_int_handler[PCI_INTH_MAX];
class @pci_info
{
U16 vendor_id;
U16 device_id;
U16 command;
U16 status;
U32 _class;
U32 bar[6];
U32 cap_pointer;
};
class @pci_cap
{
U8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */
U8 cap_next; /* Generic PCI field: next ptr. */
U8 cap_len; /* Generic PCI field: capability length */
U8 cfg_type; /* Identifies the structure. */
U8 bar; /* Where to find it. */
U8 padding[3]; /* Pad to full dword. */
U32 offset; /* Offset within bar. */
U32 length; /* Length of the structure, in bytes. */
};
U0 @get_pci_info(I64 i, @pci_info* pci)
{
I64 j;
pci->vendor_id = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x0) & 0xFFFF;
pci->device_id = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x0) >> 16;
pci->command = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x4) & 0xFFFF;
pci->status = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x4) >> 16;
pci->_class = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x8) >> 24;
for (j = 0; j < 6; j++)
pci->bar[j] = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x10 + (0x04 * j));
}
U0 @get_pci_cap(I64 i, @pci_cap* cap, I64 idx)
{
I64 base = 0x40 + (idx * 16);
U32 u32;
u32 = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base);
cap->cap_vndr = u32.u8[0];
cap->cap_next = u32.u8[1];
cap->cap_len = u32.u8[2];
cap->cfg_type = u32.u8[3];
u32 = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x04);
cap->bar = u32.u8[0];
cap->offset = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x08);
cap->length = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x0c);
}
U0 @pci_reroute_interrupts(I64 base, I64 cpu)
{
I64 i;
U8* da = dev.uncached_alias + IOAPIC_REG;
U32* _d = dev.uncached_alias + IOAPIC_DATA;
for (i = 0; i < 4; i++) {
*da = IOREDTAB + i * 2 + 1;
*_d = dev.mp_apic_ids[cpu] << 24;
*da = IOREDTAB + i * 2;
*_d = 0x4000 + base + i;
}
}
I64 @pci_register_int_handler(U64 handler)
{
if (!handler)
return -1;
I64 i = 0;
while (@pci_int_handler[i])
i++;
if (i > PCI_INTH_MAX - 1)
return -1;
@pci_int_handler[i] = handler;
return 0;
}
interrupt U0 @pci_interrupt_handler()
{
I64 i;
for (i = 0; i < PCI_INTH_MAX; i++)
if (@pci_int_handler[i])
Call(@pci_int_handler[i]);
*(dev.uncached_alias + LAPIC_EOI)(U32*) = 0;
}
MemSet(&@pci_int_handler, NULL, sizeof(U64) * PCI_INTH_MAX);
IntEntrySet(0x40, &@pci_interrupt_handler, IDTET_IRQ);
IntEntrySet(0x41, &@pci_interrupt_handler, IDTET_IRQ);
IntEntrySet(0x42, &@pci_interrupt_handler, IDTET_IRQ);
IntEntrySet(0x43, &@pci_interrupt_handler, IDTET_IRQ);
@pci_reroute_interrupts(0x40, 0);
"pci ";

255
System/Drivers/VMSVGA.HC Normal file
View file

@ -0,0 +1,255 @@
class @vmsvga_info
{
U16 io_base;
U32* fifo;
U16 width;
U16 height;
U16 bpp;
U64 fb;
U32 capabilities;
};
#define VMWGFX_FIFO_STATIC_SIZE (1024 * 1024)
#define VMSVGA_MAGIC 0x900000
#define VMSVGA_ID_2 (VMSVGA_MAGIC << 8 | 2)
#define VMSVGA_MOUSE_ID 1
#define VMSVGA_CAP_GMR 0x00100000
#define VMSVGA_CMD_INVALID_CMD 0
#define VMSVGA_CMD_UPDATE 1
#define VMSVGA_CMD_RECT_COPY 3
#define VMSVGA_CMD_DEFINE_CURSOR 19
#define VMSVGA_CMD_DEFINE_ALPHA_CURSOR 22
#define VMSVGA_CMD_UPDATE_VERBOSE 25
#define VMSVGA_CMD_FRONT_ROP_FILL 29
#define VMSVGA_CMD_FENCE 30
#define VMSVGA_CMD_ESCAPE 33
#define VMSVGA_CMD_DEFINE_SCREEN 34
#define VMSVGA_CMD_DESTROY_SCREEN 35
#define VMSVGA_CMD_DEFINE_GMRFB 36
#define VMSVGA_CMD_BLIT_GMRFB_TO_SCREEN 37
#define VMSVGA_CMD_BLIT_SCREEN_TO_GMRFB 38
#define VMSVGA_CMD_ANNOTATION_FILL 39
#define VMSVGA_CMD_ANNOTATION_COPY 40
#define VMSVGA_CMD_DEFINE_GMR2 41
#define VMSVGA_CMD_REMAP_GMR2 42
#define VMSVGA_REG_ID 0
#define VMSVGA_REG_ENABLE 1
#define VMSVGA_REG_WIDTH 2
#define VMSVGA_REG_HEIGHT 3
#define VMSVGA_REG_MAX_WIDTH 4
#define VMSVGA_REG_MAX_HEIGHT 5
#define VMSVGA_REG_DEPTH 6
#define VMSVGA_REG_BITS_PER_PIXEL 7 /* Current bpp in the guest */
#define VMSVGA_REG_PSEUDOCOLOR 8
#define VMSVGA_REG_RED_MASK 9
#define VMSVGA_REG_GREEN_MASK 10
#define VMSVGA_REG_BLUE_MASK 11
#define VMSVGA_REG_BYTES_PER_LINE 12
#define VMSVGA_REG_FB_START 13 /* (Deprecated) */
#define VMSVGA_REG_FB_OFFSET 14
#define VMSVGA_REG_VRAM_SIZE 15
#define VMSVGA_REG_FB_SIZE 16
/* ID 0 implementation only had the above registers then the palette */
#define VMSVGA_REG_CAPABILITIES 17
#define VMSVGA_REG_MEM_START 18 /* (Deprecated) */
#define VMSVGA_REG_MEM_SIZE 19
#define VMSVGA_REG_CONFIG_DONE 20 /* Set when memory area configured */
#define VMSVGA_REG_SYNC 21 /* See "FIFO Synchronization Registers" */
#define VMSVGA_REG_BUSY 22 /* See "FIFO Synchronization Registers" */
#define VMSVGA_REG_GUEST_ID 23 /* Set guest OS identifier */
#define VMSVGA_REG_CURSOR_ID 24 /* (Deprecated) */
#define VMSVGA_REG_CURSOR_X 25 /* (Deprecated) */
#define VMSVGA_REG_CURSOR_Y 26 /* (Deprecated) */
#define VMSVGA_REG_CURSOR_ON 27 /* (Deprecated) */
#define VMSVGA_REG_HOST_BITS_PER_PIXEL 28 /* (Deprecated) */
#define VMSVGA_REG_SCRATCH_SIZE 29 /* Number of scratch registers */
#define VMSVGA_REG_MEM_REGS 30 /* Number of FIFO registers */
#define VMSVGA_REG_NUM_DISPLAYS 31 /* (Deprecated) */
#define VMSVGA_REG_PITCHLOCK 32 /* Fixed pitch for all modes */
#define VMSVGA_REG_IRQMASK 33 /* Interrupt mask */
/* Legacy multi-monitor support */
#define VMSVGA_REG_NUM_GUEST_DISPLAYS \
34 /* Number of guest displays in X/Y direction */
#define VMSVGA_REG_DISPLAY_ID \
35 /* Display ID for the following display attributes */
#define VMSVGA_REG_DISPLAY_IS_PRIMARY \
36 /* Whether this is a primary display \
*/
#define VMSVGA_REG_DISPLAY_POSITION_X 37 /* The display position x */
#define VMSVGA_REG_DISPLAY_POSITION_Y 38 /* The display position y */
#define VMSVGA_REG_DISPLAY_WIDTH 39 /* The display's width */
#define VMSVGA_REG_DISPLAY_HEIGHT 40 /* The display's height */
/* See "Guest memory regions" below. */
#define VMSVGA_REG_GMR_ID 41
#define VMSVGA_REG_GMR_DESCRIPTOR 42
#define VMSVGA_REG_GMR_MAX_IDS 43
#define VMSVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH 44
#define VMSVGA_REG_TRACES \
45 /* Enable trace-based updates even when FIFO is on */
#define VMSVGA_REG_GMRS_MAX_PAGES \
46 /* Maximum number of 4KB pages for all GMRs */
#define VMSVGA_REG_MEMORY_SIZE \
47 /* Total dedicated device memory excluding FIFO */
#define VMSVGA_REG_TOP 48 /* Must be 1 more than the last register */
#define VMSVGA_FIFO_MIN 0
#define VMSVGA_FIFO_MAX 1
#define VMSVGA_FIFO_NEXT_CMD 2
#define VMSVGA_FIFO_STOP 3
#define VMSVGA_FIFO_CAPABILITIES 4
#define VMSVGA_FIFO_FLAGS 5
#define VMSVGA_FIFO_FENCE 6
#define VMSVGA_FIFO_3D_HWVERSION 7
#define VMSVGA_FIFO_PITCHLOCK 8
#define VMSVGA_FIFO_CURSOR_ON 9
#define VMSVGA_FIFO_CURSOR_X 10
#define VMSVGA_FIFO_CURSOR_Y 11
#define VMSVGA_FIFO_CURSOR_COUNT 12
#define VMSVGA_FIFO_CURSOR_LAST_UPDATED 13
#define VMSVGA_FIFO_RESERVED 14
#define VMSVGA_FIFO_CURSOR_SCREEN_ID 15
#define VMSVGA_FIFO_DEAD 16
#define VMSVGA_FIFO_3D_HWVERSION_REVISED 17
#define VMSVGA_FIFO_3D_CAPS 18
#define VMSVGA_FIFO_3D_CAPS_LAST = 19
#define VMSVGA_FIFO_GUEST_3D_HWVERSION 20
#define VMSVGA_FIFO_FENCE_GOAL 21
#define VMSVGA_FIFO_BUSY 22
#define VMSVGA_FIFO_NUM_REGS 23
#define VMSVGA_FIFO_CAP_NONE 0
#define VMSVGA_FIFO_CAP_FENCE (1 << 0)
#define VMSVGA_FIFO_CAP_ACCELFRONT (1 << 1)
#define VMSVGA_FIFO_CAP_PITCHLOCK (1 << 2)
#define VMSVGA_FIFO_CAP_VIDEO (1 << 3)
#define VMSVGA_FIFO_CAP_CURSOR_BYPASS_3 (1 << 4)
#define VMSVGA_FIFO_CAP_ESCAPE (1 << 5)
#define VMSVGA_FIFO_CAP_RESERVE (1 << 6)
#define VMSVGA_FIFO_CAP_SCREEN_OBJECT (1 << 7)
#define VMSVGA_FIFO_CAP_GMR2 (1 << 8)
// #define VMSVGA_FIFO_CAP_3D_HWVERSION_REVISED VMSVGA_FIFO_CAP_GMR2
#define VMSVGA_FIFO_CAP_SCREEN_OBJECT_2 (1 << 9)
#define VMSVGA_FIFO_CAP_DEAD (1 << 10)
@vmsvga_info vmsvga;
MemSet(&vmsvga, 0, sizeof(@vmsvga_info));
U32 @vmsvga_reg_read(I64 index)
{
OutU32(vmsvga.io_base, index);
return InU32(vmsvga.io_base + 1);
}
U0 @vmsvga_reg_write(I64 index, U32 val)
{
OutU32(vmsvga.io_base, index);
OutU32(vmsvga.io_base + 1, val);
}
U0 @vmsvga_fifo_write(U32 value)
{
/* Need to sync? */
if ((vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] + sizeof(U32) == vmsvga.fifo[VMSVGA_FIFO_STOP]) || (vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] == vmsvga.fifo[VMSVGA_FIFO_MAX] - sizeof(U32) && vmsvga.fifo[VMSVGA_FIFO_STOP] == vmsvga.fifo[VMSVGA_FIFO_MIN])) {
//"Syncing because of full fifo\n";
// vmwareWaitForFB(pVMWARE);
}
vmsvga.fifo[vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] / sizeof(U32)] = value;
if (vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] == vmsvga.fifo[VMSVGA_FIFO_MAX] - sizeof(U32)) {
vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] = vmsvga.fifo[VMSVGA_FIFO_MIN];
} else {
vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] += sizeof(U32);
}
}
U0 @vmsvga_fifo_get_cap(U8* s, I64 cap)
{
"%32s:", s;
if ((vmsvga.fifo[VMSVGA_FIFO_CAPABILITIES] & cap) == cap)
"%s\n", "True";
else
"%s\n", "False";
}
I64 @vmsvga_init(I64 w, I64 h, I64 bpp)
{
I64 j;
j = PCIClassFind(0x030000, 0);
if (j < 0) {
//"VMSVGA device not found.\n";
return -1;
}
vmsvga.io_base = PCIReadU16(j.u8[2], j.u8[1], j.u8[0], 0x10) & ~(0x0F);
@vmsvga_reg_write(VMSVGA_REG_ID, VMSVGA_ID_2);
if (@vmsvga_reg_read(VMSVGA_REG_ID) == VMSVGA_ID_2) {
//"VMSVGA driver version 2 supported.\n";
} else {
//"VMSVGA device not supported.\n";
return -1;
}
vmsvga.width = w;
vmsvga.height = h;
vmsvga.bpp = bpp;
vmsvga.fb = @vmsvga_reg_read(VMSVGA_REG_FB_START);
vmsvga.fifo = @vmsvga_reg_read(VMSVGA_REG_MEM_START);
//"FIFO @ 0x%08X (%d bytes)\n", vmsvga.fifo,
// @vmsvga_reg_read(VMSVGA_REG_MEM_SIZE);
@vmsvga_reg_write(VMSVGA_REG_WIDTH, 1920);
@vmsvga_reg_write(VMSVGA_REG_HEIGHT, 1080);
@vmsvga_reg_write(VMSVGA_REG_BITS_PER_PIXEL, 32);
@vmsvga_reg_write(VMSVGA_REG_ENABLE, 1);
vmsvga.fifo[VMSVGA_FIFO_MIN] = 16;
vmsvga.fifo[VMSVGA_FIFO_MAX] = 16 + (10 * 1024);
vmsvga.fifo[VMSVGA_FIFO_NEXT_CMD] = 16;
vmsvga.fifo[VMSVGA_FIFO_STOP] = 16;
@vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 0);
@vmsvga_fifo_write(VMSVGA_CMD_UPDATE);
@vmsvga_fifo_write(0);
@vmsvga_fifo_write(0);
@vmsvga_fifo_write(0);
@vmsvga_fifo_write(0);
@vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 1);
return 0;
}
U0 @vmsvga_mouse_pointer_set(U32* pointer, I64 width, I64 height)
{
@vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 0);
@vmsvga_fifo_write(VMSVGA_CMD_DEFINE_ALPHA_CURSOR);
@vmsvga_fifo_write(VMSVGA_MOUSE_ID);
@vmsvga_fifo_write(0);
@vmsvga_fifo_write(0);
@vmsvga_fifo_write(width);
@vmsvga_fifo_write(height);
I64 x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
@vmsvga_fifo_write(pointer[(y * width) + x]);
}
}
@vmsvga_reg_write(VMSVGA_REG_CONFIG_DONE, 1);
}
U64 @vmsvga_get_framebuffer() { return vmsvga.fb; }
U0 @vmsvga_display_update() { @vmsvga_reg_write(VMSVGA_REG_ENABLE, 1); }
class @vmsvga
{
U0(*Init)
(I64 w, I64 h, I64 bpp);
U64(*FrameBuffer)
();
};
@vmsvga VMSVGA;
VMSVGA.FrameBuffer = &@vmsvga_get_framebuffer;
VMSVGA.Init = &@vmsvga_init;
"vmsvga ";

View file

@ -0,0 +1,148 @@
// https://wiki.osdev.org/VMware_tools
#define VMWARE_MAGIC 0x564D5868
#define VMWARE_PORT 0x5658
#define VMWARE_PORTHB 0x5659
#define CMD_GETVERSION 10
#define CMD_ABSPOINTER_DATA 39
#define CMD_ABSPOINTER_STATUS 40
#define CMD_ABSPOINTER_COMMAND 41
#define ABSPOINTER_ENABLE 0x45414552
#define ABSPOINTER_RELATIVE 0xF5
#define ABSPOINTER_ABSOLUTE 0x53424152
class @vmware_cmd
{
union {
U32 ax;
U32 magic;
};
union {
U32 bx;
I32 size;
};
union {
U32 cx;
U16 command;
};
union {
U32 dx;
U16 port;
};
U32 si;
U32 di;
};
U0 @vmware_send(@vmware_cmd* cmd)
{
U32 reg RAX reg_ax = cmd->ax;
U32 reg RBX reg_bx = cmd->bx;
U32 reg RCX reg_cx = cmd->cx;
U32 reg RDX reg_dx = cmd->dx;
U32 reg RSI reg_si = cmd->si;
U32 reg RDI reg_di = cmd->di;
reg_ax = VMWARE_MAGIC;
reg_dx = VMWARE_PORT;
asm {
IN AX, DX
}
cmd->ax = reg_ax;
cmd->bx = reg_bx;
cmd->cx = reg_cx;
cmd->dx = reg_dx;
cmd->si = reg_si;
cmd->di = reg_di;
}
Bool @vmware_backdoor_is_present()
{
@vmware_cmd cmd;
cmd.bx = ~VMWARE_MAGIC;
cmd.command = CMD_GETVERSION;
@vmware_send(&cmd);
if (cmd.bx != VMWARE_MAGIC || cmd.ax == 0xFFFFFFFF) {
return FALSE;
}
return TRUE;
}
U0 @vmware_ms_absolute()
{
@vmware_cmd cmd;
cmd.bx = ABSPOINTER_ENABLE;
cmd.command = CMD_ABSPOINTER_COMMAND;
@vmware_send(&cmd);
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
@vmware_send(&cmd);
cmd.bx = 1;
cmd.command = CMD_ABSPOINTER_DATA;
@vmware_send(&cmd);
cmd.bx = ABSPOINTER_ABSOLUTE;
cmd.command = CMD_ABSPOINTER_COMMAND;
@vmware_send(&cmd);
}
U0 @vmware_handle_mouse()
{
@vmware_cmd cmd;
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
@vmware_send(&cmd);
if (cmd.ax == 0xFFFF0000) {
@vmware_ms_absolute;
return;
}
if ((cmd.ax & 0xFFFF) < 4)
return;
cmd.bx = 4;
cmd.command = CMD_ABSPOINTER_DATA;
@vmware_send(&cmd);
I32 buttons = (cmd.ax & 0xFFFF);
I64 z = cmd.dx;
if (z > 1) {
z = -1;
}
MsSet(@lerp(cmd.bx, 0xffff, Display.width), @lerp(cmd.cx, 0xffff, Display.height), ms.pos.z + z);
Mouse.x = @lerp(cmd.bx, 0xffff, Display.width);
Mouse.y = @lerp(cmd.cx, 0xffff, Display.height);
// MsSet((cmd.bx * Display.width) / 0xffff, (cmd.cx * Display.height) / 0xffff, ms.pos.z + z);
ms.lb = buttons & 0x20;
ms.rb = buttons & 0x10;
}
U0 @vmware_ms_nop() { }
U0 @vmware_ms_update()
{
while (1) {
@vmware_handle_mouse;
Sleep(1);
}
}
U0 @vmware_tools_init()
{
if (!@vmware_backdoor_is_present) {
return;
}
@patch_jmp_rel32(&WinMsUpdate, &@vmware_ms_nop);
@vmware_ms_absolute;
Spawn(&@vmware_ms_update, , "VMwareMsUpdateTask");
}
@vmware_tools_init;
"vmware-tools ";

View file

@ -0,0 +1,475 @@
// Virtio.HC
//
// PCI virtio I/O registers.
//
#define VIRTIO_PCI_HOST_FEATURES 0 // Features supported by the host
#define VIRTIO_PCI_GUEST_FEATURES 4 // Features activated by the guest
#define VIRTIO_PCI_QUEUE_PFN 8 // PFN for the currently selected queue
#define VIRTIO_PCI_QUEUE_SIZE 12 // Queue size for the currently selected queue
#define VIRTIO_PCI_QUEUE_SEL 14 // Queue selector
#define VIRTIO_PCI_QUEUE_NOTIFY 16 // Queue notifier
#define VIRTIO_PCI_STATUS 18 // Device status register
#define VIRTIO_PCI_ISR 19 // Interrupt status register
#define VIRTIO_PCI_CONFIG 20 // Configuration data block
//
// PCI virtio status register bits
//
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
#define VIRTIO_CONFIG_S_DRIVER 2
#define VIRTIO_CONFIG_S_DRIVER_OK 4
#define VIRTIO_CONFIG_S_FAILED 0x80
//
// Ring descriptor flags
//
#define VRING_DESC_F_NEXT 1 // Buffer continues via the next field
#define VRING_DESC_F_WRITE 2 // Buffer is write-only (otherwise read-only)
#define VRING_DESC_F_INDIRECT 4 // Buffer contains a list of buffer descriptors
class @virtio_queue_buf
{
U64 address;
U32 length;
U16 flags;
U16 next;
};
class @virtio_avail
{
U16 flags;
U16 index;
U16 ring[256];
U16 int_index;
};
class @virtio_used_item
{
U32 index;
U32 length;
};
class @virtio_used
{
U16 flags;
U16 index;
@virtio_used_item ring[256];
U16 int_index;
};
class @virtio_queue
{
@virtio_queue_buf buffers[256];
@virtio_avail available;
U8 padding[3578];
@virtio_used used;
};
class @virtio_avail_buf
{
U32 index;
U64 address;
U32 length;
};
class @virtio_buf_info
{
U8* buffer;
U64 size;
U8 flags;
// If the user wants to keep same buffer as passed in this struct, use "true".
// otherwise, the supplied buffer will be copied in the queues' buffer
Bool copy;
};
// Virtio-blk.HC
#define BDT_VIRTIO_BLK 10
#define VIRTIO_BLK_T_IN 0
#define VIRTIO_BLK_T_OUT 1
#define VIRTIO_BLK_T_FLUSH 4
#define VIRTIO_BLK_MAX_BLK 0x400000 // Limit blkdev to 2G max, set to NULL to use entire disk (not recommended for RedSea)
class @virtio_blk
{
U16 port;
U32 blks;
@virtio_queue* vq;
I64 vq_size;
I64 vq_index;
U8 status;
};
class @virtio_blk_request
{
U32 type;
U32 priority;
U64 sector;
};
@virtio_blk virtio_blk;
MemSet(&virtio_blk, 0, sizeof(@virtio_blk));
I64 VirtioBlkInit()
{
I64 j;
// Scan for device
j = PCIClassFind(0x010000, 0);
if (j < 0) {
"\n[virtio-blk] No device found\n";
return -1;
}
virtio_blk.port = PCIReadU32(j.u8[2],
j.u8[1], j.u8[0], 0x10)
& 0xFFFFFFFC;
virtio_blk.blks = InU32(virtio_blk.port + VIRTIO_PCI_CONFIG);
// Reset Device
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, 0);
// Found Driver
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
// Set up virt queue
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SEL, 0);
virtio_blk.vq_size = InU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SIZE); // 256
virtio_blk.vq = CAllocAligned(sizeof(@virtio_queue), 4096, erythros_mem_task->code_heap);
OutU32(virtio_blk.port + VIRTIO_PCI_QUEUE_PFN, virtio_blk.vq / 4096);
// Init OK
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_DRIVER_OK);
virtio_blk.vq_index = 0;
}
// DskVIO.HC
U0 VIOFlush()
{
I64 j;
I64 vq_idx;
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), erythros_mem_task);
brq->type = VIRTIO_BLK_T_FLUSH;
brq->sector = NULL;
vq_idx = virtio_blk.vq->available.index % 256;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = &virtio_blk.status;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = 1;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = 0;
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
virtio_blk.vq_index += 2;
j = virtio_blk.vq->used.index;
virtio_blk.vq->available.index++;
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
while (j == virtio_blk.vq->used.index) {
Yield;
}
Free(brq);
}
Bool VIORBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
{
no_warn dv;
I64 i, j;
I64 vq_idx;
U64 addr;
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), erythros_mem_task);
for (i = 0; i < cnt; i++) {
brq->type = VIRTIO_BLK_T_IN;
brq->sector = blk + i;
vq_idx = virtio_blk.vq->available.index % 256;
addr = buf + (BLK_SIZE * i);
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
virtio_blk.vq_index += 3;
j = virtio_blk.vq->used.index;
virtio_blk.vq->available.index++;
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
while (j == virtio_blk.vq->used.index) {
Yield;
}
}
Free(brq);
return TRUE;
}
Bool VIOWBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
{
no_warn dv;
I64 i, j;
I64 vq_idx;
U64 addr;
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), erythros_mem_task);
for (i = 0; i < cnt; i++) {
brq->type = VIRTIO_BLK_T_OUT;
brq->sector = blk + i;
vq_idx = virtio_blk.vq->available.index % 256;
addr = buf + (BLK_SIZE * i);
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_NEXT;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
virtio_blk.vq_index += 3;
j = virtio_blk.vq->used.index;
virtio_blk.vq->available.index++;
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
while (j == virtio_blk.vq->used.index) {
Yield;
}
}
Free(brq);
VIOFlush;
return TRUE;
}
U0 RedSeaTryInit(CDrv* dv)
{
CRedSeaBoot br;
Bool unlock;
try {
unlock = DrvLock(dv);
BlkRead(dv, &br, dv->drv_offset, 1);
if (br.signature != MBR_PT_REDSEA || br.signature2 != 0xAA55)
return;
dv->fs_type = FSt_REDSEA;
CallExtStr("RedSeaFreeFreeLst", dv);
dv->spc = 1;
dv->size = br.sects;
dv->data_area = dv->drv_offset + br.bitmap_sects;
dv->root_clus = br.root_clus;
dv->fat1 = dv->fat2 = dv->drv_offset + 1;
CallExtStr("DrvFATBlkAlloc", dv);
if (unlock)
DrvUnlock(dv);
} catch if (unlock)
DrvUnlock(dv);
}
U8 MountVirtioBlk()
{ // Mount Virtio-blk device
CDrv* dv = DrvMakeFreeSlot(DrvNextFreeLet('A'));
CBlkDev* bd = BlkDevNextFreeSlot(dv->drv_let, BDT_RAM);
CRedSeaBoot* bs = CAlloc(BLK_SIZE, erythros_mem_task);
bd->max_blk = 512;
BlkDevAdd(bd, , TRUE, TRUE);
bd->type = BDT_VIRTIO_BLK;
if (VIRTIO_BLK_MAX_BLK) {
bd->max_blk = Min(VIRTIO_BLK_MAX_BLK, virtio_blk.blks);
} else {
bd->max_blk = virtio_blk.blks;
}
Free(bd->RAM_dsk);
dv->size = bd->max_blk + 1 - bd->drv_offset;
VIORBlks(dv, bs, 0, 1);
dv->root_clus = bs->root_clus;
dv->data_area = bs->bitmap_sects;
dv->next_free = NULL;
dv->last_free = NULL;
Free(bs);
RedSeaTryInit(dv);
return dv->drv_let;
}
// DskBlk2.HC
Bool BlkRead2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
{ // Read blk cnt from Drv to buf.
Bool res = TRUE, unlock;
CBlkDev* bd = dv->bd;
if (cnt <= 0)
return TRUE;
DrvChk(dv);
try {
unlock = DrvLock(dv);
CallExtStr("BlkDevInit", bd);
if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
throw('Drv');
if (bd->flags & BDF_READ_CACHE)
CallExtStr("RCache", dv, &buf, &blk, &cnt);
if (cnt > 0) {
switch (bd->type) {
case BDT_RAM:
MemCpy(buf, bd->RAM_dsk + blk << BLK_SIZE_BITS, cnt << BLK_SIZE_BITS);
break;
case BDT_ISO_FILE_READ:
case BDT_ISO_FILE_WRITE:
FBlkRead(bd->file_dsk, buf, blk, cnt);
break;
case BDT_ATA:
case BDT_ATAPI:
res = CallExtStr("ATARBlks", dv, buf, blk, cnt);
break;
case BDT_VIRTIO_BLK:
res = VIORBlks(dv, buf, blk, cnt);
break;
}
bd->last_time = tS;
if (bd->flags & BDF_READ_CACHE)
CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
}
if (unlock)
DrvUnlock(dv);
} catch if (unlock)
DrvUnlock(dv);
return res;
}
Bool BlkWrite2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
{ // Write blk cnt from buf to Drv.
Bool res = TRUE, unlock;
CBlkDev* bd = dv->bd;
if (cnt <= 0)
return TRUE;
DrvChk(dv);
try {
unlock = DrvLock(dv);
CallExtStr("BlkDevInit", bd);
if (bd->flags & BDF_READ_ONLY && !(bd->flags & BDF_READ_ONLY_OVERRIDE))
throw('BlkDev');
if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
throw('Drv');
if (cnt > 0) {
switch (bd->type) {
case BDT_RAM:
MemCpy(bd->RAM_dsk + blk << BLK_SIZE_BITS, buf, cnt << BLK_SIZE_BITS);
break;
case BDT_ISO_FILE_READ:
case BDT_ISO_FILE_WRITE:
FBlkWrite(bd->file_dsk, buf, blk, cnt);
break;
case BDT_ATA:
case BDT_ATAPI:
res = CallExtStr("ATAWBlks", dv, buf, blk, cnt);
break;
case BDT_VIRTIO_BLK:
res = VIOWBlks(dv, buf, blk, cnt);
break;
}
bd->last_time = tS;
if (bd->flags & BDF_READ_CACHE)
CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
}
if (unlock)
DrvUnlock(dv);
} catch if (unlock)
DrvUnlock(dv);
return res;
}
@patch_jmp_rel32(&BlkRead, &BlkRead2);
@patch_jmp_rel32(&BlkWrite, &BlkWrite2);
// DskBlkDev2.HC
CBlkDev* BlkDevChk2(CBlkDev* bd, Bool except = TRUE)
{ // Check for valid BlkDev. Throw exception.
if (bd->type == BDT_VIRTIO_BLK)
return bd;
if (!bd || bd->bd_signature != BD_SIGNATURE_VAL || !(BDT_NULL < bd->type < BDT_TYPES_NUM)) {
if (except)
throw('BlkDev');
else
return NULL;
} else
return bd;
}
@patch_jmp_rel32(&BlkDevChk, &BlkDevChk2);
// DskDrv2.HC
DefineLstLoad("ST_BLKDEV_TYPES2",
"NULL\0RAM\0ATA\0FILE_READ\0FILE_WRITE\0ATAPI\0NULL\0NULL\0NULL\0NULL\0VIRTIO\0");
U8 DrvTextAttrGet2(U8 drv_let = 0)
{ // Get color of drive.
U8* blkdev_text_attr2 = blkdev_text_attr;
U8* drv_text_attr2 = drv_text_attr;
I64 dta_size = 3;
drv_let = Let2Let(drv_let);
if (drv_let == 'A')
return BLACK << 4 | WHITE;
if ('A' <= drv_let <= 'Z')
return blkdev_text_attr2[Let2BlkDevType(drv_let)] << 4 | drv_text_attr2[drv_let % dta_size];
else
return BLACK << 4 | WHITE;
}
U0 DrvRep2()
{ // Drive report.
CDrv* dv;
CBlkDev* bd;
I64 ch, i, drv_let, attr;
U8* st;
"\nDefined Drives:\n";
for (i = 0, dv = blkdev.drvs; i < DRVS_NUM; i++, dv++) {
if (dv->dv_signature == DRV_SIGNATURE_VAL) {
bd = dv->bd;
drv_let = Drv2Let(dv);
if (Bt(&dv->fs_type, FStf_DISABLE))
ch = '-';
else if (drv_let == blkdev.boot_drv_let)
ch = ':';
else
ch = '+';
attr = DrvTextAttrGet(drv_let);
"\dFG,%d\d\dBG,%d\d%C %-8Z %-10Z %04X %04X %02X\n",
attr & 15, attr >> 4, drv_let, dv->fs_type &FSG_TYPE_MASK, "ST_DRV_TYPES",
bd->type, "ST_BLKDEV_TYPES2", bd->base0, bd->base1, bd->unit;
if (st = DrvModelNum(drv_let)) {
"Model#:%s\n", st;
Free(st);
}
if (st = DrvSerialNum(drv_let)) {
"Serial#:%s\n", st;
Free(st);
}
if (bd->type == BDT_ISO_FILE_READ || bd->type == BDT_ISO_FILE_WRITE)
"File=\"%s\"\n", bd->file_dsk_name;
"%016X-%016X\n\dFG\d\dBG\d", dv->drv_offset, dv->drv_offset + dv->size - 1;
}
}
"Home Dir:\"%s\"\n", blkdev.home_dir;
}
@patch_jmp_rel32(&DrvTextAttrGet, &DrvTextAttrGet2);
@patch_jmp_rel32(&DrvRep, &DrvRep2);
VirtioBlkInit;
MountVirtioBlk;
if (Let2Drv('A', 0) && !Let2Drv('A')->root_clus) {
"[virtio-blk] RedSea filesystem not initialized, formatting.\n";
Fmt('A', , FALSE, FSt_REDSEA);
Cd("M:/System/");
}
"virtio-blk ";

51
System/FFI/Base.HC Normal file
View file

@ -0,0 +1,51 @@
#define PUSH_SYSV_REGS \
asm {PUSH RCX PUSH RDX PUSH RBX PUSH RBP PUSH RSI PUSH RDI PUSH R8 PUSH R9 PUSH \
R10 PUSH R11 PUSH R12 PUSH R13 PUSH R14 PUSH R15}
#define POP_SYSV_REGS \
p0 = p0; \
p1 = p1; \
p2 = p2; \
p3 = p3; \
p4 = p4; \
p5 = p5; \
asm {POP R15 POP R14 POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP RDI POP \
RSI POP RBP POP RBX POP RDX POP RCX}
#define GET_SYSV_ARGS \
asm {PUSH R9 PUSH R8 PUSH RCX PUSH RDX PUSH RSI PUSH RDI} \
I64 reg RDI p0; \
I64 reg RSI p1; \
I64 reg RDX p2; \
I64 reg RCX p3; \
I64 reg R8 p4; \
I64 reg R9 p5; \
asm {POP RDI POP RSI POP RDX POP RCX POP R8 POP R9}
#define MOV_ANS_RAX asm { MOV[&ans], RAX }
#define MOV_PARAM0_RDI asm {MOV [&param0], RDI}
I64 param0;
I64 elf_argc;
U8** elf_argv;
asm {
_ELF_CALL::
PUSH RBP
MOV RBP,RSP
MOV RAX,U64 SF_ARG1[RBP]
MOV RDI,U64 SF_ARG2[RBP]
MOV RSI,U64 SF_ARG3[RBP]
TEST RAX,RAX
JZ @@05
CALL RAX
@@05: POP RBP
RET1 8
}
U0 _main()
{
MOV_PARAM0_RDI
CallInd(_ELF_CALL, param0, elf_argc, elf_argv);
UserTaskCont;
}
U0 _exit() { UserTaskCont; }

301
System/FFI/ELF64.HC Normal file
View file

@ -0,0 +1,301 @@
#define EI_NIDENT 16
#define EM_X86_64 0x3E
#define ET_EXEC 2
#define ET_DYN 3
U0 @elf64_debug_print(U8 fmt, ...)
{
// FIXME: Remove unnecessary debug_print statements and PrintErr for errors.
no_warn fmt, argc, argv;
}
class Elf64_Ehdr {
U8 e_ident[EI_NIDENT]; /* Magic number and other info */
U16 e_type; /* Object file type */
U16 e_machine; /* Architecture */
U32 e_version; /* Object file version */
U64 e_entry; /* Entry point virtual address */
U64 e_phoff; /* Program header table file offset */
U64 e_shoff; /* Section header table file offset */
U32 e_flags; /* Processor-specific flags */
U16 e_ehsize; /* ELF header size in bytes */
U16 e_phentsize; /* Program header table entry size */
U16 e_phnum; /* Program header table entry count */
U16 e_shentsize; /* Section header table entry size */
U16 e_shnum; /* Section header table entry count */
U16 e_shstrndx; /* Section header string table index */
};
class Elf64_Shdr {
U32 sh_name; /* Section name (string tbl index) */
U32 sh_type; /* Section type */
U64 sh_flags; /* Section flags */
U64 sh_addr; /* Section virtual addr at execution */
U64 sh_offset; /* Section file offset */
U64 sh_size; /* Section size in bytes */
U32 sh_link; /* Link to another section */
U32 sh_info; /* Additional section information */
U64 sh_addralign; /* Section alignment */
U64 sh_entsize; /* Entry size if section holds table */
};
class Elf64_Sym {
U32 st_name; /* Symbol name (string tbl index) */
U8 st_info; /* Symbol type and binding */
U8 st_other; /* Symbol visibility */
U16 st_shndx; /* Section index */
U64 st_value; /* Symbol value */
U64 st_size; /* Symbol size */
};
class PLT_entry {
U8 pad[0x10];
};
class RELA_entry {
U64 r_offset;
U64 r_info;
I64 r_addend;
};
class Elf {
union {
U8* u8;
Elf64_Ehdr* ehdr;
} I64 size;
U8* dynstr;
Elf64_Sym* dynsym;
PLT_entry* plt;
RELA_entry* rela_dyn;
RELA_entry* rela_plt;
Elf64_Sym* strtab;
Elf64_Sym* symtab;
I64 rela_dyn_size;
I64 rela_plt_size;
I64 strtab_size;
I64 symtab_size;
};
U0(*_start)
();
U0 unimplemented_symbol()
{
I32 s = 0xDEADF00D;
PrintWarn("Unimplemented symbol: %s\n", s);
Dbg;
while (1)
Sleep(1);
}
Bool is_valid_elf(Elf* elf)
{
Bool res = TRUE;
if (MemCmp(elf->u8 + 1, "ELF", 3)) {
@elf64_debug_print("Invalid signature (not ELF).\n");
res = FALSE;
}
if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
@elf64_debug_print("Invalid object file type.\n");
res = FALSE;
}
if (elf->ehdr->e_machine != EM_X86_64) {
@elf64_debug_print("Invalid architecture.\n");
res = FALSE;
}
return res;
}
U0 process_elf_section_header_table(Elf* elf)
{
Elf64_Shdr* shdr = elf->u8 + elf->ehdr->e_shoff;
Elf64_Shdr* shdr_shstrtab = shdr + elf->ehdr->e_shstrndx;
U8* shstrtab = elf->u8 + shdr_shstrtab->sh_offset;
I64 i = 0;
while (i < elf->ehdr->e_shnum) {
if (!StrCmp(shstrtab + shdr->sh_name, ".symtab")) {
@elf64_debug_print("found symtab at 0x%08x, size = %d\n", shdr->sh_offset,
shdr->sh_size);
elf->symtab = elf->u8 + shdr->sh_offset;
elf->symtab_size = shdr->sh_size;
}
if (!StrCmp(shstrtab + shdr->sh_name, ".strtab")) {
@elf64_debug_print("found strtab at 0x%08x, size = %d\n", shdr->sh_offset,
shdr->sh_size);
elf->strtab = elf->u8 + shdr->sh_offset;
elf->strtab_size = shdr->sh_size;
}
if (shdr->sh_addr) {
MemCpy(shdr->sh_addr, elf->u8 + shdr->sh_offset, shdr->sh_size);
if (!StrCmp(shstrtab + shdr->sh_name, ".dynstr"))
elf->dynstr = shdr->sh_addr;
if (!StrCmp(shstrtab + shdr->sh_name, ".dynsym"))
elf->dynsym = shdr->sh_addr;
if (!StrCmp(shstrtab + shdr->sh_name, ".plt"))
elf->plt = shdr->sh_addr;
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.dyn")) {
elf->rela_dyn = shdr->sh_addr;
elf->rela_dyn_size = shdr->sh_size / shdr->sh_entsize;
}
if (!StrCmp(shstrtab + shdr->sh_name, ".rela.plt")) {
elf->rela_plt = shdr->sh_addr;
elf->rela_plt_size = shdr->sh_size / shdr->sh_entsize;
}
if (!StrCmp(shstrtab + shdr->sh_name, ".bss") || !StrCmp(shstrtab + shdr->sh_name, ".tbss")) {
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
@elf64_debug_print(
"Zeroed out section '%s' at physical address 0x%06x, size = %d bytes\n",
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
} else
@elf64_debug_print(
"MemCpy section '%s' to physical address 0x%06x, size = %d bytes\n",
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
if (!StrCmp(shstrtab + shdr->sh_name, ".bss")) {
MemSet(shdr->sh_addr, NULL, shdr->sh_size);
@elf64_debug_print("MemSet section '%s' at physical address 0x%06x to NULL, "
"size = %d bytes\n",
shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size);
}
}
shdr++;
i++;
}
}
U0 process_elf_rela_dyn_entries(Elf* elf)
{
I64 i;
U8* entry_name;
RELA_entry* rela_dyn = elf->rela_dyn;
for (i = 0; i < elf->rela_dyn_size; i++) {
entry_name = elf->dynstr + elf->dynsym[(rela_dyn->r_info >> 32)].st_name;
@elf64_debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset);
@elf64_debug_print("entry name = '%s'\n", entry_name);
if (!StrCmp(entry_name, "__libc_start_main")) {
*(rela_dyn->r_offset)(U64*) = &_main;
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: &_main\n",
entry_name);
}
if (!StrCmp(entry_name, "stdin")) {
*(rela_dyn->r_offset)(U64*) = 0;
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 0);
}
if (!StrCmp(entry_name, "stdout")) {
*(rela_dyn->r_offset)(U64*) = 1;
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 1);
}
if (!StrCmp(entry_name, "stderr")) {
*(rela_dyn->r_offset)(U64*) = 2;
@elf64_debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 2);
}
rela_dyn++;
}
}
CHashClass* get_symbol_hash_entry(U8* entry_name)
{
I64 i;
CHashSrcSym* sym;
CHashTable* tbl = Fs->hash_table;
while (tbl) {
for (i = 0; i < tbl->mask; i++) {
sym = tbl->body[i];
while (sym) {
if (sym->type == HTT_CLASS)
if (!StrCmp(sym->str, entry_name))
return sym;
sym = sym->next;
}
}
tbl = tbl->next;
}
return NULL;
}
U64 get_symbol_address(U8* entry_name)
{
CHash* h = HashFind(entry_name, Fs->hash_table, Fs->hash_table->mask);
if (!h)
return NULL;
switch (h->type) {
case HTT_GLBL_VAR:
return h(CHashGlblVar*)->data_addr;
break;
case HTT_FUN:
return h(CHashFun*)->exe_addr;
break;
default:
return NULL;
break;
}
return NULL;
}
U0 process_elf_rela_plt_entries(Elf* elf)
{
I64 i;
U32 handler;
U32* patch;
U8* entry_name;
Bool symbol_exists;
PLT_entry* plt = elf->plt;
RELA_entry* rela_plt = elf->rela_plt;
plt++;
for (i = 0; i < elf->rela_plt_size; i++) {
symbol_exists = FALSE;
entry_name = elf->dynstr + elf->dynsym[(rela_plt->r_info >> 32)].st_name;
handler = MAlloc(sizeof(unimplemented_symbol), erythros_mem_task->code_heap);
MemCpy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol));
patch = handler + 0x0A;
*patch = entry_name;
@patch_jmp_rel32(plt, handler);
@patch_call_rel32(handler + 0x16, &PrintErr);
//@patch_call_rel32(handler + 0x21, &_exit);
if (!StrCmp(entry_name, "__libc_start_main")) {
symbol_exists = TRUE;
@patch_jmp_rel32(plt, &_main);
@elf64_debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name);
}
if (get_symbol_address(entry_name)) {
symbol_exists = TRUE;
@patch_jmp_rel32(plt, get_symbol_address(entry_name));
@elf64_debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name,
entry_name);
}
if (!symbol_exists)
@elf64_debug_print(
"Set value for .rela.plt entry '%s' to &unimplemented_symbol\n",
entry_name);
rela_plt++;
plt++;
}
}
U0 load_elf(...)
{
if (argc < 1) {
PrintErr("Not enough arguments.\n");
return;
}
if (!FileFind(argv[0])) {
PrintErr("File not found: %s\n", argv[0]);
return;
}
Elf elf;
elf.u8 = FileRead(argv[0], &elf.size);
@elf64_debug_print("Load file '%s', size = %d bytes\n", argv[0], elf.size);
if (!is_valid_elf(&elf)) {
PrintErr("File is not a valid ELF x86-64 executable.\n");
return;
}
process_elf_section_header_table(&elf);
process_elf_rela_dyn_entries(&elf);
process_elf_rela_plt_entries(&elf);
_start = elf.ehdr->e_entry;
elf_argc = argc;
elf_argv = argv;
}

324
System/FFI/LibC.HC Normal file
View file

@ -0,0 +1,324 @@
#define stdin 0
#define stdout 1
#define stderr 2
U0 bcmp()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
MemCmp(p0, p1, p2);
POP_SYSV_REGS
}
U0 calloc()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
CAlloc(p0 * p1, erythros_mem_task->code_heap);
POP_SYSV_REGS
}
U0 free()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
Free(p0);
POP_SYSV_REGS
}
I64 @isatty()
{
return 0;
}
U0 isatty()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
// Dbg;
@isatty;
POP_SYSV_REGS
}
I64 @fwrite(U8* ptr, I64 size, I64 nmemb, U64 stream)
{
U8* tmp;
switch (stream) {
case stdout:
case stderr:
tmp = CAlloc((size * nmemb) + 1, erythros_mem_task->code_heap);
MemCpy(tmp, ptr, (size * nmemb));
#ifdef QEMU_RUN_TESTS
QemuDebugMsg(tmp);
#endif
DocPutS(adam_task->put_doc, tmp);
Free(tmp);
// if (!MemCmp(tmp, "VERIFICATION FAILED", 19))
// Break;
break;
default:
break;
}
return size * nmemb;
}
U0 fwrite()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@fwrite(p0, p1, p2, p3);
POP_SYSV_REGS
}
U64 @getentropy(U8* buffer, U64 length)
{
I64 i;
for (i = 0; i < length; i++)
buffer[i] = RandU64;
return 0;
}
U0 getentropy()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@getentropy(p0, p1);
POP_SYSV_REGS
}
U0 htonl()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
EndianU32(p0);
POP_SYSV_REGS
}
U0 ntohl()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
EndianU32(p0);
POP_SYSV_REGS
}
U0 htons()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
EndianU16(p0);
POP_SYSV_REGS
}
U0 ntohs()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
EndianU16(p0);
POP_SYSV_REGS
}
U64 @malloc(I64 size)
{
U64 res = MAlloc(size, malloc_mem_task[malloc_current_mem_task % MALLOC_MEM_TASK_COUNT]->code_heap);
malloc_current_mem_task++;
return res;
}
U0 malloc()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@malloc(p0);
POP_SYSV_REGS
}
U0 memcmp()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
MemCmp(p0, p1, p2);
POP_SYSV_REGS
}
U0 memcpy()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
MemCpy(p0, p1, p2);
POP_SYSV_REGS
}
U8* @memmove(U8* dest, U8* src, I64 n)
{
I64 i;
U8* from = src;
U8* to = dest;
if (from == to || n == 0)
return dest;
if (to > from && to - from < n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy in reverse, to avoid overwriting from */
for (i = n - 1; i >= 0; i--)
to[i] = from[i];
return dest;
}
if (from > to && from - to < n) {
/* to overlaps with from */
/* <from......> */
/* <to........> */
/* copy forwards, to avoid overwriting from */
for (i = 0; i < n; i++)
to[i] = from[i];
return dest;
}
MemCpy(dest, src, n);
return dest;
}
U0 memmove()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@memmove(p0, p1, p2);
POP_SYSV_REGS
}
U0 memset()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
MemSet(p0, p1, p2);
POP_SYSV_REGS
}
U0 putc()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
PutChars(p0);
POP_SYSV_REGS
}
U0 rand()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
RandU64;
POP_SYSV_REGS
}
U8* @realloc(U8* ptr, I64 size)
{
U8* new;
if (!ptr) {
new = MAlloc(size, erythros_mem_task->code_heap);
} else {
new = MAlloc(size, erythros_mem_task->code_heap);
MemCpy(new, ptr, size);
Free(ptr);
}
return new;
}
U0 realloc()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@realloc(p0, p1);
POP_SYSV_REGS
}
// FIXME: It is non-obvious how to take a [u8] and convert it to a
// formatted string in Jakt, so we have to do this hack for
// now. Hopefully, this will change soon.
U0 sprintf()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
StrPrint(p0, p1, p2, p3, p4, p5);
POP_SYSV_REGS
}
I64 @strncmp(U8* s1, U8* s2, I32 n)
{
U64 u1, u2;
while (n-- > 0) {
u1 = *s1++;
u2 = *s2++;
u1 = u1 & 0xff;
u2 = u2 & 0xff;
if (u1 != u2)
return u1 - u2;
if (u1 == '\0')
return 0;
}
return 0;
}
U0 strncmp()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@strncmp(p0, p1, p2);
POP_SYSV_REGS
}
U0 strcmp()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
StrCmp(p0, p1);
POP_SYSV_REGS
}
U0 strlen()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
StrLen(p0);
POP_SYSV_REGS
}
I64 tos_nist_offset = 5020;
#define NIST_TIME_OFFSET (tos_nist_offset - local_time_offset / CDATE_FREQ)
public
I64 CDate2Unix(CDate dt)
{ // TempleOS datetime to Unix timestamp.
return ToI64((dt - Str2Date("1/1/1970")) / CDATE_FREQ + NIST_TIME_OFFSET);
}
I64 @time(I64* ptr)
{
no_warn ptr;
return CDate2Unix(Now);
}
U0 time()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@time(p0);
POP_SYSV_REGS
}
U0 toupper()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
ToUpper(p0);
POP_SYSV_REGS
}
U0 __assert_fail()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
"%s:%d: %s: %s\n", p1, p2, p3, p0;
Break;
POP_SYSV_REGS
}

35
System/FFI/New.HC Normal file
View file

@ -0,0 +1,35 @@
U0 _ZdlPv()
{
// operator delete(void*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
Free(p0);
POP_SYSV_REGS
}
U0 _ZdlPvm()
{
// operator delete(void*, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
Free(p0);
POP_SYSV_REGS
}
U0 _Znwm()
{
// operator new(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
MAlloc(p0, erythros_mem_task);
POP_SYSV_REGS
}
U0 _ZnwmRKSt9nothrow_t()
{
// operator new(unsigned long, std::nothrow_t const&)
PUSH_SYSV_REGS
GET_SYSV_ARGS
MAlloc(p0, erythros_mem_task);
POP_SYSV_REGS
}

288
System/Jakt/DC.HC Normal file
View file

@ -0,0 +1,288 @@
U0 _Z8dc_aliasm()
{
// dc_alias(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCAlias(p0);
POP_SYSV_REGS
}
U0 _Z7dc_blotmmmm()
{
// dc_blot(unsigned long, unsigned long, unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrBlot(p0, p1, p2, p3);
POP_SYSV_REGS
}
U8* @dc_buffer(CDC* dc) { return dc->body; }
U0 _Z9dc_bufferm()
{
// dc_buffer(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_buffer(p0);
POP_SYSV_REGS
}
I64 @dc_color(CDC* dc) { return dc->color; }
U0 _Z8dc_colorm()
{
// dc_color(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_color(p0);
POP_SYSV_REGS
}
U0 @dc_copy(CDC* dest, I64 x, I64 y, CDC* src)
{
// If position is off-screen, return
if (x > dest->width - 1 || y > dest->height - 1)
return;
// If device context dimensions match, MemCpy and return
if (dest->width_internal == src->width_internal && dest->height == src->height) {
MemCpy(dest->body, src->body, dest->width_internal * dest->height);
return;
}
CDC* dc1 = DCAlias(dest);
CDC* dc2 = DCAlias(src);
I64 src_line = 0;
I64 src_row = 0;
I64 clip_y = 0;
// Handle horizontal clipping left
while (x < 0) {
dc2->x0++;
x++;
}
// Handle vertical clipping top
while (y < 0) {
dc2->body += src->width_internal;
dc2->y0++;
y++;
}
// default, clip line to copy as width-left off screen
src_line = src->width - dc2->x0;
if (-dc2->x0 + x + src->width >= dest->width) {
src_line -= ((-dc2->x0 + x + src->width) - dest->width);
}
dc2->body += dc2->x0;
clip_y = y;
while (src_row < (src->height - dc2->y0) && clip_y < dest->height) {
MemCpy(dc1->body + (y * dest->width) + x, dc2->body, src_line);
dc2->body += src->width_internal;
dc1->body += dest->width_internal;
clip_y++;
src_row++;
}
Free(dc2);
Free(dc1);
}
U0 _Z7dc_copymmmm()
{
// dc_copy(unsigned long, unsigned long, unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_copy(p0, p1, p2, p3);
POP_SYSV_REGS
}
U0 _Z10dc_destroym()
{
// dc_destroy(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCDel(p0);
POP_SYSV_REGS
}
U0 _Z14dc_draw_circlemlll()
{
// dc_draw_circle(unsigned long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrCircle3(p0, p1, p2, 0, p3);
POP_SYSV_REGS
}
U0 _Z19dc_draw_filled_rectmllll()
{
// dc_draw_filled_rect(unsigned long, long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrRect(p0, p1, p2, p3, p4);
POP_SYSV_REGS
}
U0 _Z12dc_draw_linemllll()
{
// dc_draw_line(unsigned long, long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrLine3(p0, p1, p2, 0, p3, p4, 0);
POP_SYSV_REGS
}
U0 _Z13dc_draw_pixelmll()
{
// dc_draw_pixel(unsigned long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrPlot(p0, p1, p2);
POP_SYSV_REGS
}
U0 _Z7dc_fillmm()
{
// dc_fill(unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCFill(p0, p1);
POP_SYSV_REGS
}
CDC* @dc_gr_dc() { return gr.dc; }
U0 _Z8dc_gr_dcv()
{
// dc_gr_dc()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_gr_dc();
POP_SYSV_REGS
}
I64 @dc_height(CDC* dc) { return dc->height; }
U0 _Z9dc_heightm()
{
// dc_height(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_height(p0);
POP_SYSV_REGS
}
U0 _Z17dc_load_from_filePKc()
{
// dc_load_from_file(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GRRead(p0);
POP_SYSV_REGS
}
U0 _Z6dc_newmm()
{
// dc_new(unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCNew(p0, p1);
POP_SYSV_REGS
}
U0 _Z11dc_pixel_atmll()
{
// dc_pixel_at(unsigned long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GrPeek(p0, p1, p2);
POP_SYSV_REGS
}
U0 _Z16dc_replace_colormmm()
{
// dc_replace_color(unsigned long, unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCColorChg(p0, p1, p2);
POP_SYSV_REGS
}
U0 _Z13dc_screenshotv()
{
// dc_screenshot()
PUSH_SYSV_REGS
GET_SYSV_ARGS
DCScrnCapture(1);
POP_SYSV_REGS
}
U0 _Z15dc_save_to_filePKcm()
{
// dc_save_to_file(char const*, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GRWrite(p0, p1);
POP_SYSV_REGS
}
U0 @dc_set_color(CDC* dc, I64 color) { dc->color = color; }
U0 _Z12dc_set_colorml()
{
// dc_set_color(unsigned long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_set_color(p0, p1);
POP_SYSV_REGS
}
U0 @dc_set_thickness(CDC* dc, I64 thickness) { dc->thick = thickness; }
U0 _Z16dc_set_thicknessml()
{
// dc_set_thickness(unsigned long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_set_thickness(p0, p1);
POP_SYSV_REGS
}
I64 @dc_thickness(CDC* dc) { return dc->thick; }
U0 _Z12dc_thicknessm()
{
// dc_thickness(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_thickness(p0);
POP_SYSV_REGS
}
I64 @dc_width(CDC* dc) { return dc->width; }
U0 _Z8dc_widthm()
{
// dc_width(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_width(p0);
POP_SYSV_REGS
}
I64 @dc_width_internal(CDC* dc) { return dc->width_internal; }
U0 _Z17dc_width_internalm()
{
// dc_width_internal(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@dc_width_internal(p0);
POP_SYSV_REGS
}

53
System/Jakt/IOPort.HC Normal file
View file

@ -0,0 +1,53 @@
U0 _Z14ioport_read_u8t()
{
// ioport_read_u8(unsigned short)
PUSH_SYSV_REGS
GET_SYSV_ARGS
InU8(p0);
POP_SYSV_REGS
}
U0 _Z15ioport_read_u16t()
{
// ioport_read_u16(unsigned short)
PUSH_SYSV_REGS
GET_SYSV_ARGS
InU16(p0);
POP_SYSV_REGS
}
U0 _Z15ioport_read_u32t()
{
// ioport_read_u32(unsigned short)
PUSH_SYSV_REGS
GET_SYSV_ARGS
InU32(p0);
POP_SYSV_REGS
}
U0 _Z15ioport_write_u8th()
{
// ioport_write_u8(unsigned short, unsigned char)
PUSH_SYSV_REGS
GET_SYSV_ARGS
OutU8(p0, p1);
POP_SYSV_REGS
}
U0 _Z16ioport_write_u16tt()
{
// ioport_write_u16(unsigned short, unsigned short)
PUSH_SYSV_REGS
GET_SYSV_ARGS
OutU16(p0, p1);
POP_SYSV_REGS
}
U0 _Z16ioport_write_u32tj()
{
// ioport_write_u32(unsigned short, unsigned int)
PUSH_SYSV_REGS
GET_SYSV_ARGS
OutU32(p0, p1);
POP_SYSV_REGS
}

72
System/Jakt/Input.HC Normal file
View file

@ -0,0 +1,72 @@
U0 _Z16input_get_stringPKc()
{
// input_get_string(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
GetStr(p0);
POP_SYSV_REGS
}
Bool @input_key_down(U8 scancode) { return Bt(kbd.down_bitmap, scancode); }
U0 _Z14input_key_downh()
{
// input_key_down(unsigned char)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@input_key_down(p0);
POP_SYSV_REGS
}
Bool @input_mouse_left() { return ms.lb; }
U0 _Z16input_mouse_leftv()
{
// input_mouse_left()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@input_mouse_left();
POP_SYSV_REGS
}
Bool @input_mouse_right() { return ms.rb; }
U0 _Z17input_mouse_rightv()
{
// input_mouse_right()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@input_mouse_right();
POP_SYSV_REGS
}
I64 @input_mouse_x() { return ms.pos.x; }
U0 _Z13input_mouse_xv()
{
// input_mouse_x()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@input_mouse_x();
POP_SYSV_REGS
}
I64 @input_mouse_y() { return ms.pos.y; }
U0 _Z13input_mouse_yv()
{
// input_mouse_y()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@input_mouse_y();
POP_SYSV_REGS
}
U0 _Z17input_press_a_keyv()
{
// input_press_a_key()
PUSH_SYSV_REGS
GET_SYSV_ARGS
PressAKey;
POP_SYSV_REGS
}

289
System/Jakt/OS.HC Normal file
View file

@ -0,0 +1,289 @@
U0 _Z8os_blinkPKc()
{
// os_blink(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
F64 frequency = Str2F64(p0);
Print("called os_blink(%.1f)\n", frequency);
Blink(frequency);
POP_SYSV_REGS
}
U64 @os_call(U8* function_name, U64 arg)
{
if (!function_name)
return NULL;
if (!StrLen(function_name))
return NULL;
CHash* h = HashFind(function_name, Fs->hash_table, Fs->hash_table->mask);
if (!h)
return NULL;
if (h->type & HTT_FUN == HTT_FUN) {
CallInd(h(CHashFun*)->exe_addr, arg);
} else {
return NULL;
}
}
U0 _Z7os_callmm()
{
// os_call(unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_call(p0, p1);
POP_SYSV_REGS
}
U0 _Z16os_device_callocj()
{
// os_device_calloc(unsigned int)
PUSH_SYSV_REGS
GET_SYSV_ARGS
CAllocAligned(p0, 4096, erythros_mem_task->code_heap);
POP_SYSV_REGS
}
U0 _Z7os_exitv()
{
// os_exit()
PUSH_SYSV_REGS
GET_SYSV_ARGS
UserTaskCont;
POP_SYSV_REGS
}
U8* @os_file_picker(U8* path, U8* glob)
{
U8* full_path = CAlloc(StrLen(path) + StrLen(glob) + 4, erythros_mem_task);
CatPrint(full_path, "%s/%s", path, glob);
CDirEntry* de = FilesFind(full_path);
Free(full_path);
CDirEntry* tmpde;
U8* file_list = NULL;
U8* selected_file = NULL;
I64 list_pos = 0;
I64 list_size = 0;
tmpde = de;
while (tmpde) {
list_size += StrLen(tmpde->name) + 2;
tmpde = tmpde->next;
}
file_list = CAlloc(list_size, erythros_mem_task);
tmpde = de;
while (tmpde) {
StrCpy(file_list + list_pos, tmpde->name);
list_pos += StrLen(tmpde->name) + 1;
tmpde = tmpde->next;
}
I64 list_index = Adam("PopUpPickLst(0x%08x);\n", file_list);
Free(file_list);
list_pos = 0;
if (list_index < 0) {
DirTreeDel(de);
return StrNew("", erythros_mem_task);
}
tmpde = de;
while (tmpde) {
if (list_index == list_pos) {
selected_file = CAlloc(StrLen(path) + StrLen(tmpde->name) + 4, erythros_mem_task);
CatPrint(selected_file, "%s/%s", path, tmpde->name);
break;
}
StrCpy(file_list + list_pos, tmpde->name);
list_pos++;
tmpde = tmpde->next;
}
DirTreeDel(de);
return selected_file;
}
U0 _Z14os_file_pickerPKcS0_()
{
// os_file_picker(char const*, char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_file_picker(p0, p1);
POP_SYSV_REGS
}
U8* @os_files_list(U8* path)
{
U8* full_path = CAlloc(StrLen(path) + 4, erythros_mem_task);
CatPrint(full_path, "%s", path);
CDirEntry* de = FilesFind(full_path);
Free(full_path);
CDateStruct ds;
CDirEntry* tmpde;
U8* file_list = NULL;
I64 list_size = 0;
tmpde = de;
while (tmpde) {
list_size += StrLen(tmpde->name) + 48; // Should be enough for filename, date,
// filesize + semicolon separators
tmpde = tmpde->next;
}
if (!list_size)
return NULL;
file_list = CAlloc(list_size, erythros_mem_task);
tmpde = de;
I64 counter = 0;
while (tmpde) {
if (counter > 0) {
StrCpy(file_list + StrLen(file_list), "|");
}
StrCpy(file_list + StrLen(file_list), tmpde->name);
if (tmpde->attr & RS_ATTR_DIR)
StrCpy(file_list + StrLen(file_list), "/");
StrCpy(file_list + StrLen(file_list), ";");
Date2Struct(&ds, tmpde->datetime);
StrPrint(file_list + StrLen(file_list), "%04d-%02d-%02d %02d:%02d", ds.year,
ds.mon, ds.day_of_mon, ds.hour, ds.min);
StrCpy(file_list + StrLen(file_list), ";");
StrPrint(file_list + StrLen(file_list), "%d", tmpde->size);
tmpde = tmpde->next;
counter++;
}
DirTreeDel(de);
return file_list;
}
U0 _Z14os_path_existsPKc()
{
// os_path_exists(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
FileFind(p0);
POP_SYSV_REGS
}
U0 _Z13os_files_listPKc()
{
// os_files_list(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_files_list(p0);
POP_SYSV_REGS
}
Bool @os_is_vm()
{
CRAXRBCRCXRDX res;
CPUId(0x40000000, &res);
if (res.rbx == 0x4B4D564B)
return TRUE;
return FALSE;
}
U0 _Z8os_is_vmv()
{
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_is_vm;
POP_SYSV_REGS
}
U0 @os_pc_speaker(F64 frequency)
{
I64 period;
if (!frequency)
OutU8(0x61, InU8(0x61) & ~3);
else {
period = ClampI64(SYS_TIMER_FREQ / frequency, 1, U16_MAX);
OutU8(0x43, 0xB6);
OutU8(0x42, period);
OutU8(0x42, period.u8[1]);
OutU8(0x61, 3 | InU8(0x61));
}
}
U0 _Z13os_pc_speakerPKc()
{
// os_pc_speaker(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
F64 frequency = Str2F64(p0);
@os_pc_speaker(frequency);
POP_SYSV_REGS
}
U0 _Z9os_randomv()
{
// os_random()
PUSH_SYSV_REGS
GET_SYSV_ARGS
RandU64;
POP_SYSV_REGS
}
U0 _Z19os_read_entire_filePKcPl()
{
// os_read_entire_file(char const*, long*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
FileRead(p0, p1);
POP_SYSV_REGS
}
U0 @os_screenshot()
{
CDC* dc = DCScrnCapture(, erythros_mem_task);
// Image.Write("B:/screenshot.png", dc);
DCDel(dc);
}
U0 _Z13os_screenshotv()
{
// os_screenshot()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_screenshot;
POP_SYSV_REGS
}
U8* @os_to_uppercase(U8* instr)
{
if (!instr)
return NULL;
if (!StrLen(instr))
return NULL;
U8* outstr = CAlloc(StrLen(instr) + 1, erythros_mem_task);
I64 i;
for (i = 0; i < StrLen(instr); i++)
outstr[i] = ToUpper(instr[i]);
return outstr;
}
U0 _Z15os_to_uppercasePKc()
{
// os_to_uppercase(char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@os_to_uppercase(p0);
POP_SYSV_REGS
}
U0 _Z20os_write_entire_filePKcPhl()
{
// os_write_entire_file(char const*, unsigned char*, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
FileWrite(p0, p1, p2);
POP_SYSV_REGS
}

62
System/Jakt/PCI.HC Normal file
View file

@ -0,0 +1,62 @@
U0 _Z8pci_findl()
{
// pci_find(long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIClassFind(p0, 0);
POP_SYSV_REGS
}
U0 _Z11pci_read_u8llll()
{
// pci_read_u8(long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIReadU8(p0, p1, p2, p3);
POP_SYSV_REGS
}
U0 _Z12pci_read_u16llll()
{
// pci_read_u16(long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIReadU16(p0, p1, p2, p3);
POP_SYSV_REGS
}
U0 _Z12pci_read_u32llll()
{
// pci_read_u32(long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIReadU32(p0, p1, p2, p3);
POP_SYSV_REGS
}
U0 _Z12pci_write_u8llllh()
{
// pci_write_u8(long, long, long, long, unsigned char)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIWriteU8(p0, p1, p2, p3, p4);
POP_SYSV_REGS
}
U0 _Z13pci_write_u16llllt()
{
// pci_write_u16(long, long, long, long, unsigned short)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIWriteU16(p0, p1, p2, p3, p4);
POP_SYSV_REGS
}
U0 _Z13pci_write_u32llllj()
{
// pci_write_u32(long, long, long, long, unsigned int)
PUSH_SYSV_REGS
GET_SYSV_ARGS
PCIWriteU32(p0, p1, p2, p3, p4);
POP_SYSV_REGS
}

37
System/Jakt/Time.HC Normal file
View file

@ -0,0 +1,37 @@
U0 _Z9time_busyl()
{
// time_busy(long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
Busy(p0);
POP_SYSV_REGS
}
I64 @time_jiffies() { return cnts.jiffies; }
U0 _Z12time_jiffiesv()
{
// time_jiffies()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@time_jiffies;
POP_SYSV_REGS
}
U0 _Z8time_nowv()
{
// time_now()
PUSH_SYSV_REGS
GET_SYSV_ARGS
Now;
POP_SYSV_REGS
}
U0 _Z10time_sleepl()
{
// time_sleep(long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
Sleep(p0);
POP_SYSV_REGS
}

94
System/Jakt/Window.HC Normal file
View file

@ -0,0 +1,94 @@
U0 @window_draw_it(CTask* task, CDC* dc)
{
if (task->user_data)
@dc_copy(dc, task->pix_left, task->pix_top, task->user_data);
}
CTask* @window_user()
{
CTask* task = Spawn(&UserCmdLine, , , 0);
TaskWait(task);
XTalk(task,
"while (1) { StrCpy(Fs->task_title, Fs->task_name); Sleep(1); };\n");
return task;
}
CTask* @window_create()
{
CTask* task = @window_user;
task->draw_it = &@window_draw_it;
return task;
}
U0 _Z13window_createv()
{
// window_create()
PUSH_SYSV_REGS
GET_SYSV_ARGS
@window_create();
POP_SYSV_REGS
}
U0 _Z14window_destroym()
{
// window_destroy(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
Kill(p0);
POP_SYSV_REGS
}
Bool @window_is_focused(CTask* task) { return task == sys_focus_task; }
U0 _Z17window_is_focusedm()
{
// window_is_focused(unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@window_is_focused(p0);
POP_SYSV_REGS
}
U0 @window_set_coordinates(CTask* task, I64 top, I64 left, I64 bottom,
I64 right)
{
task->win_top = top;
task->win_left = left;
task->win_bottom = bottom;
task->win_right = right;
}
U0 _Z22window_set_coordinatesmllll()
{
// window_set_coordinates(unsigned long, long, long, long, long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@window_set_coordinates(p0, p1, p2, p3, p4);
POP_SYSV_REGS
}
U0 @window_set_context(CTask* task, CDC* dc) { task->user_data = dc; }
U0 _Z18window_set_contextmm()
{
// window_set_context(unsigned long, unsigned long)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@window_set_context(p0, p1);
POP_SYSV_REGS
}
U0 @window_set_title(CTask* task, U8* title)
{
StrCpy(task->task_name, title);
StrCpy(task->task_title, title);
}
U0 _Z16window_set_titlemPKc()
{
// window_set_title(unsigned long, char const*)
PUSH_SYSV_REGS
GET_SYSV_ARGS
@window_set_title(p0, p1);
POP_SYSV_REGS
}

View file

@ -0,0 +1,65 @@
class AnimationContext2D {
U64 signature;
I64 duration; // in jiffies
I64 index; // current frame
I64 length; // length in frames
I64 timer; // timer + cnts.jiffies = current ticks
Context2D** frame; // Context2D*, ...
};
AnimationContext2D* @animation2d_new_from_frames(Context2D** frames, I64 length,
I64 duration = 250)
{
if (!frames || !length)
return NULL;
AnimationContext2D* actx = CAlloc(sizeof(AnimationContext2D));
actx->signature = 'animated';
actx->frame = frames;
actx->length = length;
actx->duration = duration;
return actx;
}
Context2D* @animation2d_frame(AnimationContext2D* actx)
{
I64 ticks = cnts.jiffies;
if (!actx)
return NULL;
if (!actx->length)
return NULL;
if (actx->index > actx->length - 1)
actx->index = 0;
if (!actx->index && !actx->timer)
actx->timer = ticks;
while (ticks >= actx->timer + actx->duration) {
actx->timer += actx->duration;
actx->index++;
if (actx->index > actx->length - 1)
actx->index = 0;
}
return actx->frame[actx->index];
}
Bool @animation2d_is_animation(AnimationContext2D* actx)
{
return T(actx->signature == 'animated', TRUE, FALSE);
}
U0 @animation2d_reset(AnimationContext2D* actx) { actx->index = 0; }
class @animation2d
{
Context2D* (*Frame)(AnimationContext2D* actx);
U0 (*Reset)(AnimationContext2D* actx);
Bool (*IsAnimation)(AnimationContext2D* actx);
AnimationContext2D* (*NewFromFrames)(Context2D** frames, I64 length,
I64 duration = 250);
};
@animation2d Animation2D;
Animation2D.IsAnimation = &@animation2d_is_animation;
Animation2D.NewFromFrames = &@animation2d_new_from_frames;
Animation2D.Frame = &@animation2d_frame;
Animation2D.Reset = &@animation2d_reset;
"animation2d ";

143
System/Libraries/Audio.HC Normal file
View file

@ -0,0 +1,143 @@
// WAV header spec information:
// https://web.archive.org/web/20140327141505/https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
// http://www.topherlee.com/software/pcm-tut-wavformat.html
class @sound_file_wav_header
{
// RIFF Header
U8 riff_header[4]; // Contains "RIFF"
I32 wav_size; // Size of the wav portion of the file, which follows the first
// 8 bytes. File size - 8
U8 wave_header[4]; // Contains "WAVE"
// Format Header
U8 fmt_header[4]; // Contains "fmt " (includes trailing space)
I32 fmt_chunk_size; // Should be 16 for PCM
I16 audio_format; // Should be 1 for PCM. 3 for IEEE Float
I16 num_channels;
I32 sample_rate;
I32 byte_rate; // Number of bytes per second. sample_rate * num_channels *
// Bytes Per Sample
I16 sample_alignment; // num_channels * Bytes Per Sample
I16 bit_depth; // Number of bits per sample
// Data
U8 data_header[4]; // Contains "data"
I32 data_bytes; // Number of bytes in data. Number of samples * num_channels *
// sample byte size
U8 bytes[0]; // Remainder of wave file is bytes
};
I64 @audio_get_available_output_stream()
{
I64 stream = 0;
while (FifoI64Cnt(Audio.output[stream].data))
stream++;
if (stream > AUDIO_MAX_STREAMS - 1)
return -1;
return stream;
}
Bool @audio_buffer_is_wav(@sound_file_wav_header* wav, I64 size)
{
if (!MemCmp(&wav->riff_header, "RIFF", 4) && !MemCmp(&wav->wave_header, "WAVE", 4))
return TRUE;
return FALSE;
}
U0 @audio_free_sound(Sound* snd)
{
if (!snd)
return;
if (snd->data)
Free(snd->data);
Free(snd);
}
I64 @audio_play_sound(Sound* snd)
{
I64 i;
I64 stream = @audio_get_available_output_stream;
if (stream < 0)
return stream;
if (!snd->data || !snd->length)
return stream;
for (i = 0; i < snd->length; i++)
FifoI64Ins(Audio.output[stream].data, snd->data[i]);
return stream;
}
Sound* @audio_sound_from_buffer(U32* buf, I64 length)
{
if (!buf || !length)
return NULL;
Sound* snd = CAlloc(sizeof(Sound));
snd->rate = 44100;
snd->channels = 2;
snd->bits = 16;
snd->data = buf;
snd->length = length;
return snd;
}
U32* @audio_buffer_mono_to_stereo(U16* buf, I64 size)
{
U32* out = CAlloc(size * 2);
I64 i;
for (i = 0; i < size / 2; i++) {
out[i].u16[0] = buf[i];
out[i].u16[1] = buf[i];
}
return out;
}
U32* @audio_buffer_copy(U32* buf, I64 size)
{
U32* out = MAlloc(size);
MemCpyU32(out, buf, size / 4);
return out;
}
Sound* @audio_sound_from_file(U8* filename)
{
if (!FileSystem.PathExists(filename))
return NULL;
I64 length = 0;
U32* buf = NULL;
I64 size = 0;
U8* data = FileSystem.ReadFile(filename, &size);
if (!data)
return NULL;
if (@audio_buffer_is_wav(data, size)) {
@sound_file_wav_header* wav = data;
if (wav->fmt_chunk_size == 16 && wav->audio_format == 1 && wav->sample_rate == 48000) {
switch (wav->num_channels) {
case 1:
buf = @audio_buffer_mono_to_stereo(&wav->bytes, wav->data_bytes);
length = wav->data_bytes / 2;
break;
case 2:
buf = @audio_buffer_copy(&wav->bytes, wav->data_bytes);
length = wav->data_bytes / 4;
break;
}
}
}
Free(data);
return @audio_sound_from_buffer(buf, length);
}
U0 @audio_snd(I8 ona = 0) { Audio.wavegen.frequency = Ona2Freq(ona); }
Audio.SoundFromFile = &@audio_sound_from_file;
Audio.FreeSound = &@audio_free_sound;
Audio.PlaySound = &@audio_play_sound;
Sound* @snd_beep = Audio.SoundFromFile("/mnt/redsea/t/Media/Sounds/Beep.wav");
U0 @audio_beep() { Audio.PlaySound(@snd_beep); }
Audio.Beep = &@audio_beep;
Function.Patch(&Snd, &@audio_snd);
"audio ";

View file

@ -0,0 +1,90 @@
U8* @base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
U8* @base64_decode(U8* input, I64* output_length)
{
I64 input_length = StrLen(input);
if (input_length % 4 != 0) {
return NULL; // Invalid Base64 input length
}
// Calculate the expected output length
*output_length = (3 * input_length) / 4;
if (input[input_length - 1] == '=') {
(*output_length)--;
}
if (input[input_length - 2] == '=') {
(*output_length)--;
}
// Allocate memory for the decoded data
U8* decoded_data = CAlloc(*output_length, erythros_mem_task);
if (decoded_data == NULL) {
return NULL; // Memory allocation failed
}
// Initialize variables for decoding process
I32 i, j = 0;
U32 sextet_bits = 0;
I64 sextet_count = 0;
U32 base64_value;
U8* char_pointer;
U8 input_find_buf[2];
input_find_buf[1] = NULL;
// Loop through the Base64 input and decode it
for (i = 0; i < input_length; i++) {
// Convert Base64 character to a 6-bit value
base64_value = 0;
if (input[i] == '=') {
base64_value = 0;
} else {
input_find_buf[0] = input[i];
char_pointer = StrFirstOcc(@base64_chars, input_find_buf);
if (char_pointer == NULL) {
Free(decoded_data);
return NULL; // Invalid Base64 character
}
base64_value = char_pointer - @base64_chars;
}
// Combine 6-bit values into a 24-bit sextet
sextet_bits = (sextet_bits << 6) | base64_value;
sextet_count++;
// When a sextet is complete, decode it into three bytes
if (sextet_count == 4) {
decoded_data[j++] = (sextet_bits >> 16) & 0xFF;
decoded_data[j++] = (sextet_bits >> 8) & 0xFF;
decoded_data[j++] = sextet_bits & 0xFF;
sextet_bits = 0;
sextet_count = 0;
}
}
return decoded_data;
}
U8* @base64_encode(U8* input, I64 input_length)
{
I64 i;
U8 buf[3];
I64 c = 0;
U8* output = CAlloc(input_length * 2, erythros_mem_task);
for (i = 0; i < input_length; i += 3) {
buf[0] = input[i];
buf[1] = @t((i + 1 < input_length), input[i + 1], 0);
buf[2] = @t((i + 2 < input_length), input[i + 2], 0);
output[c++] = @base64_chars[(buf[0] & 0xfc) >> 2];
output[c++] = @base64_chars[((buf[0] & 0x03) << 4) + ((buf[1] & 0xf0) >> 4)];
output[c++] = @t((i + 1 < input_length), @base64_chars[((buf[1] & 0x0f) << 2) + ((buf[2] & 0xc0) >> 6)], '=');
output[c++] = @t((i + 2 < input_length), @base64_chars[buf[2] & 0x3f], '=');
}
output[c] = '\0';
return output;
}
"base64 ";

View file

@ -0,0 +1,146 @@
extern class Context2D;
extern U32 Color(I64 r, I64 g, I64 b, I64 a = 255);
extern U32 Peek2D(Context2D* ctx, I64 x, I64 y);
class BitmapFont {
U8* name;
U8* char_map;
I64 line_height;
U16 bitmap[4096];
};
class @bitmap_font_list
{
@bitmap_font_list* prev;
@bitmap_font_list* next;
BitmapFont* font;
};
class @bitmapfont
{
@bitmap_font_list* fonts;
U0 (*Add)(BitmapFont* font);
BitmapFont* (*GetByName)(U8* name);
U0 (*Init)();
};
// BitmapFont* @bitmapfont_new_from_bdf_data(Context2D* ctx, U8* bdf_data)
//{
// BitmapFont* font = CAlloc(sizeof(BitmapFont));
//
// I64 bdf_lines_max = 0;
// U8** bdf_lines = String.Split(bdf_data, , &bdf_lines_max);
//
// I64 char_pos = 0;
// I64 char_x_pos = 0;
// I64 i, w;
// I64 xx, yy;
// /*
// while (*char_map++) {
// // Clear character bitmap
// for (i = 0; i < 16; i++) {
// font->bitmap[(char_pos * 16) + i] = 0;
// }
// // Get character width
// w = 0;
// for (xx = 0; xx < 16; xx++) {
// if (Peek2D(ctx, char_x_pos + xx, 0) == Color(255, 0, 0)) {
// w = xx;
// break;
// }
// }
// // Extract bitmap
// for (yy = 0; yy < 16; yy++) {
// for (xx = 0; xx < w + 1; xx++) {
// if (Peek2D(ctx, char_x_pos + xx, yy) == Color(0, 0, 0)) {
// font->bitmap[(char_pos * 16) + yy] |= 0x8000 >> xx;
// }
// }
// //"%016b\n", font->bitmap[(char_pos * 16) + yy];
// }
// char_pos++;
// char_x_pos += w + 1;
// }
// */
// return font;
// }
BitmapFont* @bitmapfont_new_from_context2d(Context2D* ctx, U8* name,
U8* char_map, I64 fixed_width = 0)
{
BitmapFont* font = CAlloc(sizeof(BitmapFont));
font->name = StrNew(name);
font->char_map = StrNew(char_map);
I64 char_pos = 0;
I64 char_x_pos = 0;
I64 i, w;
I64 xx, yy;
while (*char_map++) {
// Clear character bitmap
for (i = 0; i < 16; i++) {
font->bitmap[(char_pos * 16) + i] = 0;
}
w = fixed_width;
if (!w) {
// Get character width
for (xx = 0; xx < 16; xx++) {
if (Peek2D(ctx, char_x_pos + xx, 0) == Color(255, 0, 0)) {
w = xx;
break;
}
}
}
// Extract bitmap
for (yy = 0; yy < 16; yy++) {
for (xx = 0; xx < w + 1; xx++) {
if (Peek2D(ctx, char_x_pos + xx, yy) == Color(0, 0, 0)) {
font->bitmap[(char_pos * 16) + yy] |= 0x8000 >> xx;
}
}
//"%016b\n", font->bitmap[(char_pos * 16) + yy];
}
char_pos++;
char_x_pos += w + 1;
}
return font;
}
@bitmapfont BitmapFonts;
U0 @bitmap_fonts_add(BitmapFont* font)
{
@bitmap_font_list* fonts = BitmapFonts.fonts;
while (fonts->next) {
fonts = fonts->next;
}
@bitmap_font_list* font_list_item = CAlloc(sizeof(@bitmap_font_list));
font_list_item->prev = fonts;
font_list_item->font = font;
fonts->next = font_list_item;
}
BitmapFont* @bitmap_fonts_get_by_name(U8* name)
{
@bitmap_font_list* fonts = BitmapFonts.fonts;
while (fonts) {
if (fonts->font) {
if (!StrCmp(fonts->font->name, name))
return fonts->font;
}
fonts = fonts->next;
}
return NULL;
}
U0 @bitmap_fonts_init()
{
BitmapFonts.fonts = CAlloc(sizeof(@bitmap_font_list));
}
BitmapFonts.Add = &@bitmap_fonts_add;
BitmapFonts.GetByName = &@bitmap_fonts_get_by_name;
BitmapFonts.Init = &@bitmap_fonts_init;
BitmapFonts.Init();
"bitmapfont ";

View file

@ -0,0 +1,125 @@
#define CLIP_MSG_NULL 0
#define CLIP_MSG_INSERT 1
#define CLIP_MSG_REMOVE 2
#define CLIP_TYPE_NULL 0
#define CLIP_TYPE_TEXT 1
#define CLIP_TYPE_DATA 2
class @clipboard_item
{
I64 length;
I64 type;
};
class ClipboardTextItem : @clipboard_item {
U8* text;
}
class ClipboardDataItem : @clipboard_item {
U8* data;
}
class @clipboard_list_item
{
@clipboard_list_item* prev;
@clipboard_list_item* next;
@clipboard_item* item;
};
class @clipboard
{
CTask* task;
I64 length;
@clipboard_list_item* items;
U0 (*Init)();
U0 (*Insert)(I64 type, U8* data);
I64 (*Length)();
U0(*Task)
();
};
@clipboard Clipboard;
U0 @clipboard_add(@clipboard_item* item)
{
@clipboard_list_item* items = Clipboard.items;
while (items->next) {
items = items->next;
}
@clipboard_list_item* new_item = CAlloc(sizeof(@clipboard_list_item));
new_item->prev = items;
new_item->item = item;
items->next = new_item;
Clipboard.length++;
Clipboard.items->prev = new_item;
}
U0 @clipboard_init() { Clipboard.items = CAlloc(sizeof(@clipboard_list_item)); }
I64 @clipboard_length() { return Clipboard.length; }
U0 @clipboard_ipc_queue_process()
{
IpcMessage* msg;
msg = Ipc.MsgRecv();
if (msg) {
switch (msg->type) {
case CLIP_MSG_INSERT:
@clipboard_add(msg->payload);
break;
case CLIP_MSG_REMOVE:
// FIXME: Handle this
break;
default:
break;
}
Free(msg);
}
}
U0 @clipboard_insert_text(U8* text)
{
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
ClipboardTextItem* item = CAlloc(sizeof(ClipboardTextItem));
item->length = StrLen(text);
item->type = CLIP_TYPE_TEXT;
item->text = text;
msg->client = NULL; // FIXME: Do we care about client here? :/
msg->type = CLIP_MSG_INSERT;
msg->payload = item;
System.Log(Fs, "Sent message → ClipInsert -> \"%s\"", text);
Ipc.MsgSend(Clipboard.task, msg);
}
U0 @clipboard_insert(I64 type, U8* data)
{
switch (type) {
case CLIP_TYPE_TEXT:
@clipboard_insert_text(data);
break;
case CLIP_TYPE_DATA:
// Reserved
break;
default:
break;
}
}
U0 @clipboard_task()
{
Ipc.InitQueue(Fs);
Clipboard.task = Fs;
System.Log(Fs, "Task running at 0x%08x", Fs);
while (1) {
@clipboard_ipc_queue_process();
Sleep(1);
}
}
Clipboard.Init = &@clipboard_init;
Clipboard.Insert = &@clipboard_insert;
Clipboard.Length = &@clipboard_length;
Clipboard.Task = &@clipboard_task;
"clipboard ";

View file

@ -0,0 +1,49 @@
Silent(ON);
#define FB_NONE 0x00
#define FB_VMSVGA 0x01
I64 @display_init(I64 width, I64 height, I64 bpp, I64 driver)
{
I64 err;
Display.width = width;
Display.height = height;
Display.bpp = bpp;
Display.driver = driver;
Display.fb = NULL;
switch (Display.driver) {
case FB_VMSVGA:
err = VMSVGA.Init(Display.width, Display.height, Display.bpp);
if (err)
return err;
Display.fb = VMSVGA.FrameBuffer();
Display.Update = &@vmsvga_display_update;
break;
default:
//"Unsupported display driver\n";
return -1;
break;
}
text.cols = Display.width / 8;
text.rows = Display.height / 16;
text.raw_col = 0;
return 0;
}
I64 @display_get_width() { return Display.width; }
I64 @display_get_height() { return Display.height; }
I64 @display_get_bpp() { return Display.bpp; }
I64 @display_get_driver() { return Display.driver; }
Display.Init = &@display_init;
Display.Width = &@display_get_width;
Display.Height = &@display_get_height;
Display.Bpp = &@display_get_bpp;
Display.Driver = &@display_get_driver;
Silent(OFF);
"display ";

View file

@ -0,0 +1,234 @@
#define FS_TYPE_UNSUPPORTED -1
#define FS_TYPE_SYSTEM 0
#define FS_TYPE_REDSEA 1
#define FS_TYPE_9P 2
#define DE_TYPE_FILE 0
#define DE_TYPE_DIR 1
class @dir_entry
{
U8 mode;
U8 type;
U32 atime;
U32 mtime;
U64 size;
U8 name[255];
U8 uid[255];
U8 gid[255];
@dir_entry* next;
};
extern Bool @plan9fs_file_find(U8* path);
extern @dir_entry* @plan9fs_get_files(U8* path);
extern U8* @plan9fs_read_file(U8* path, I64* size);
extern I64 @plan9fs_write_file(U8* path, U64 buffer, I64 size);
class @filesystem
{
I64 root_fs_type;
@dir_entry (*GetFiles)(U8* path);
U8* (*GetFileExtension)(U8* path);
U0 (*Init)();
Bool (*PathExists)(U8* path);
U8* (*ReadFile)(U8* path, I64* size);
I64* (*WriteFile)(U8* path, U64 buffer, I64 size);
};
@filesystem FileSystem;
U8* @filesystem_resolve_path(U8* path)
{
U8* abs_path = CAlloc(StrLen(path));
I64 argc;
I64 i;
I64 pos = 0;
U8** argv;
U8** outv;
U8* path_cpy = StrNew(path);
argv = String.Split(path_cpy, '/', &argc);
outv = CAlloc(sizeof(U64) * argc);
for (i = 0; i < argc; i++) {
if (!(!StrCmp(argv[i], ".") || !StrCmp(argv[i], "") || !StrCmp(argv[i], ".."))) {
outv[pos] = argv[i];
pos++;
}
if (!StrCmp(argv[i], "..")) {
pos = Max(0, pos - 1);
}
}
for (i = 0; i < pos; i++)
String.Append(abs_path, "/%s", outv[i]);
Free(path_cpy);
Free(outv);
if (abs_path[StrLen(abs_path) - 1] == '/')
abs_path[StrLen(abs_path) - 1] = NULL;
if (!StrLen(abs_path))
StrCpy(abs_path, "/");
return abs_path;
}
I64 @filesystem_get_type(U8* path)
{
if (!MemCmp(path, "/mnt/redsea/", 12) && StrLen(path) > 12)
return FS_TYPE_REDSEA;
if (!MemCmp(path, "/sys/", 5))
return FS_TYPE_SYSTEM;
return FileSystem.root_fs_type;
}
@dir_entry* @filesystem_get_files_9p(U8* path)
{
return @plan9fs_get_files(path);
}
@dir_entry* @filesystem_get_files_redsea(U8* path)
{
CDirEntry* de = FilesFind(path);
CDirEntry* tmpde = NULL;
@dir_entry* entries = NULL;
@dir_entry* entry = NULL;
@dir_entry* new = NULL;
if (de) {
entries = CAlloc(sizeof(@dir_entry));
entry = entries;
tmpde = de;
while (tmpde) {
new = CAlloc(sizeof(@dir_entry));
entry->next = new;
StrCpy(&new->name, &tmpde->name);
StrCpy(&new->uid,
"templeos"); // No file ownership in TempleOS
StrCpy(&new->gid,
"templeos"); // No file ownership in TempleOS
new->size = tmpde->size;
new->type = T(IsDir(tmpde->full_name), 1, 0);
entry = new;
tmpde = tmpde->next;
}
DirTreeDel(de);
return entries;
}
return NULL;
}
@dir_entry* @filesystem_get_files(U8* path)
{
if (!path)
return NULL;
U8 buf[512];
I64 type = @filesystem_get_type(path);
switch (type) {
case FS_TYPE_SYSTEM:
SysHlt;
break;
case FS_TYPE_REDSEA:
StrPrint(&buf, "%c:%s", ToUpper(path[12]), path + 13);
if (buf[StrLen(&buf) - 1] == ':')
buf[StrLen(&buf)] = '/';
if (buf[StrLen(&buf) - 1] == '/')
buf[StrLen(&buf)] = '.';
return @filesystem_get_files_redsea(&buf);
break;
case FS_TYPE_9P:
return @filesystem_get_files_9p(path);
break;
default:
break;
}
return NULL;
}
U8* @filesystem_get_file_extension(U8* path)
{
return (StrLastOcc(path, ".") + 1);
}
Bool @filesystem_path_exists(U8* opath)
{
if (!opath)
return FALSE;
U8 buf[512];
U8* path = @filesystem_resolve_path(opath);
I64 type = @filesystem_get_type(path);
switch (type) {
case FS_TYPE_SYSTEM:
return NULL;
SysHlt;
break;
case FS_TYPE_REDSEA:
StrPrint(&buf, "%c:%s", ToUpper(path[12]), path + 13);
if (buf[StrLen(&buf) - 1] == ':')
buf[StrLen(&buf)] = '/';
if (buf[StrLen(&buf) - 1] == '/')
buf[StrLen(&buf)] = '.';
Free(path);
return FileFind(&buf);
break;
case FS_TYPE_9P:
return NULL;
Free(path);
return @plan9fs_file_find(path);
break;
default:
break;
}
return NULL;
}
U8* @filesystem_read_file(U8* path, I64* size)
{
if (!path)
return FALSE;
U8 buf[512];
I64 type = @filesystem_get_type(path);
switch (type) {
case FS_TYPE_SYSTEM:
SysHlt;
break;
case FS_TYPE_REDSEA:
StrPrint(&buf, "%c:%s", ToUpper(path[12]), path + 13);
return FileRead(&buf, size);
break;
case FS_TYPE_9P:
return @plan9fs_read_file(path, size);
break;
default:
break;
}
return NULL;
}
I64 @filesystem_write_file(U8* path, U8* buffer, I64 size)
{
if (!path || !buffer || !size)
return FALSE;
U8 buf[512];
I64 type = @filesystem_get_type(path);
switch (type) {
case FS_TYPE_SYSTEM:
SysHlt;
break;
case FS_TYPE_REDSEA:
StrPrint(&buf, "%c:%s", ToUpper(path[12]), path + 13);
return FileWrite(&buf, buffer, size);
break;
case FS_TYPE_9P:
return @plan9fs_write_file(path, buffer, size);
break;
default:
break;
}
return NULL;
}
FileSystem.GetFiles = &@filesystem_get_files;
FileSystem.GetFileExtension = &@filesystem_get_file_extension;
FileSystem.PathExists = &@filesystem_path_exists;
FileSystem.ReadFile = &@filesystem_read_file;
FileSystem.WriteFile = &@filesystem_write_file;
"filesystem ";

View file

@ -0,0 +1,28 @@
U0 @function_insert_call(U32 from, U32 to)
{
*(from(U8*)) = 0xE8;
*((from + 1)(I32*)) = to - from - 5;
}
U0 @function_patch(U32 from, U32 to)
{
*(from(U8*)) = 0xE9;
*((from + 1)(I32*)) = to - from - 5;
}
class @function
{
U0(*InsertCall)
(U32 from, U32 to);
U0(*Patch)
(U32 from, U32 to);
};
@function Function;
Function.InsertCall = &@function_insert_call;
Function.Patch = &@function_patch;
// usage: Function.InsertCall(addr, &func);
// usage: Function.Patch(&old_func, &new_func);
"function ";

File diff suppressed because it is too large Load diff

398
System/Libraries/Gui.HC Normal file
View file

@ -0,0 +1,398 @@
extern class Widget;
extern class Window;
#define WIN_FLAGS_NULL 0x0
#define WIN_FLAGS_NO_REINDEX 0x1 // Wallpaper, taskbar, etc.
#define WIN_FLAGS_RESIZABLE 0x2
#define WIN_FLAGS_MOVABLE 0x4
#define WIN_FLAGS_ICON 0x8
#define WIN_FLAGS_TITLE_BAR 0x10
#define WIN_FLAGS_MIN_BUTTON 0x20
#define WIN_FLAGS_MAX_BUTTON 0x40
#define WIN_FLAGS_CLOSE_BUTTON 0x80
#define WIN_FLAGS_MINIMIZED 0x100
#define WIN_FLAGS_MAXIMIZED 0x200
#define WIN_FLAGS_HIDDEN 0x400
#define WIN_FLAGS_NOHILIGHT 0x800
#define WIN_FLAGS_SKIP 0x1000
#define WIN_FLAGS_NOFILL 0x2000
#define WIN_FLAGS_MENU 0x4000
#define WIN_FLAGS_MAX 0x10000
#define WIN_SIGNATURE 0x1596e3c1c62c34b929d75cded8c0
#define WIN_FLAGS_DEFAULT \
(WIN_FLAGS_RESIZABLE | WIN_FLAGS_MOVABLE | WIN_FLAGS_ICON | WIN_FLAGS_TITLE_BAR | WIN_FLAGS_MIN_BUTTON | WIN_FLAGS_MAX_BUTTON | WIN_FLAGS_CLOSE_BUTTON)
class @widget_callbacks
{
U0 (*change)(Widget* widget);
U0 (*clicked)(Widget* widget);
U0 (*repaint)(Widget* widget);
};
class @widget_origin
{
I64 x;
I64 y;
I64 width;
I64 height;
I64 mouse_x;
I64 mouse_y;
};
class Widget {
Bool change;
I64 id;
I64 type;
I64 x;
I64 y;
I64 width;
I64 height;
I64 opacity;
U64 flags;
U8* tag;
Widget* echo;
Window* parent_win;
Context2D* backing_store;
Context2D* pointer;
@widget_callbacks callback;
@widget_origin origin;
};
class @window_widgets_list
{
@window_widgets_list* prev;
@window_widgets_list* next;
Widget* widget;
};
class @window_origin
{
I64 x;
I64 y;
I64 width;
I64 height;
I64 mouse_x;
I64 mouse_y;
};
class @window_position
{
I64 x;
I64 y;
}
class @window_buttons
{
Bool minimize;
Bool maximize;
Bool close;
};
class @window_callbacks
{
U0 (*minimize)(Window* win);
U0 (*maximize)(Window* win);
U0 (*mouseat)(Window* win);
U0 (*keypress)(Window* win, I64 key);
U0 (*repaint)(Window* win);
U0 (*close)(Window* win);
};
class @window_mouse
{
I64 x;
I64 y;
Bool left;
Bool right;
};
class @window_event
{ // FIXME: Better name?
I64 x;
I64 y;
Bool left;
Bool right;
};
class Window {
U64 signature;
Bool alpha;
Bool refresh;
Bool repainting;
CTask* client;
I64 x;
I64 y;
I64 width;
I64 height;
I64 opacity;
U64 flags;
U8 title[512];
I64 title_bar_x;
I64 title_bar_width;
I64 min_width;
I64 min_height;
Context2D* icon;
Context2D* backing_store;
Context2D* pointer;
Context2D* render_ctx;
Context2D* resize_ctx;
Widget* mouse_down_widget;
Widget* focused_widget;
Widget* hovered_widget;
@window_buttons button;
@window_callbacks callback;
@window_origin origin;
@window_mouse mouse;
@window_event left_btn_down; // FIXME: put these in a Window.event.xxx class?
@window_event left_btn_up;
@window_event right_btn_down;
@window_event right_btn_up;
@window_widgets_list* widget;
};
class @gui_widget
{
Bool (*IsHovered)(Window* win, Widget* widget);
U0 (*SetCallback)(Widget* widget, U8* name, U64 callback);
U0 (*SetEcho)(Widget* widget, Widget* echo);
U0 (*SetFont)(Widget* widget, U8* font_name);
U0 (*SetMousePointer)(Widget* widget, Context2D* pointer);
U0 (*ClearMousePointer)(Widget* widget);
U0 (*SetOpacity)(Widget* widget, I64 opacity);
U0 (*SetText)(Widget* widget, U8* text);
};
class @gui_window
{
U0 (*Center)(Window* win, Bool horz = TRUE, Bool vert = TRUE);
U0 (*DisableAlphaChannel)(Window* win);
U0 (*EnableAlphaChannel)(Window* win);
U0 (*Hide)(Window* win);
Bool (*IsHovered)(Window* win);
Bool (*IsVisible)(Window* win);
U0 (*SetCallback)(Window* win, U8* name, U64 callback);
U0 (*SetFocus)(Window* win);
U0 (*SetIcon)(Window* win, Context2D* icon);
U0 (*SetMousePointer)(Window* win, Context2D* pointer);
U0 (*ClearMousePointer)(Window* win);
U0 (*SetOpacity)(Window* win, I64 opacity);
U0 (*SetTitle)(Window* win, U8* text);
U0 (*SetPosition)(Window* win, I64 x, I64 y);
U0 (*SetZIndex)(Window* win, I64 index);
U0 (*Show)(Window* win);
U0 (*Refresh)(Window* win);
};
class @gui
{
@gui_widget Widget;
@gui_window Window;
U0 (*App)();
Widget* (*InitWidget)(Widget* widget, Window* win, I64 type, I64 x, I64 y,
I64 width, I64 height);
Widget* (*CreateWidget)(Window* win, I64 type, I64 x, I64 y, I64 width,
I64 height);
};
@gui Gui;
I64 @gui_app_header_size;
U8* @gui_app_header_data = FileRead("M:/Include/Gui.HC", &@gui_app_header_size);
U0 @gui_app()
{
CDoc* @gui_app_header_doc = DocNew;
DocLoad(@gui_app_header_doc, @gui_app_header_data, @gui_app_header_size);
ExeDoc(@gui_app_header_doc);
}
Bool @gui_window_flag_is_set(Window* win, U64 flag)
{
if (!win)
return FALSE;
if (win->flags & flag == flag)
return TRUE;
return FALSE;
}
Bool @gui_window_is_hovered(Window* win)
{
if (Mouse.x > win->x && Mouse.x < win->x + win->width && Mouse.y > win->y && Mouse.y < win->y + win->height)
return TRUE;
return FALSE;
}
Bool @gui_window_is_visible(Window* win)
{
if (!win)
return NULL;
return !@gui_window_flag_is_set(win, WIN_FLAGS_HIDDEN);
}
U0 @gui_widget_destroy(Widget* widget)
{
Window* win = widget->parent_win;
@window_widgets_list* widgets = win->widget;
@window_widgets_list* prev;
@window_widgets_list* next;
while (widgets) {
if (widgets->widget == widget) {
prev = widgets->prev;
next = widgets->next;
if (prev)
prev->next = next;
if (next)
next->prev = prev;
// FIXME: Free widget and child data
widget->type = NULL;
}
widgets = widgets->next;
}
}
U0 @gui_widget_repaint(Window* win, Widget* widget, I64 type) { }
U0 @gui_window_repaint(Window* win, I64 type)
{
@system_log(Fs, "Repainting window 0x%08x [%s]", win, win->title);
}
U0 @gui_window_center(Window* win, Bool horz = TRUE, Bool vert = TRUE)
{
if (!win)
return;
I64 x = win->x;
I64 y = win->y;
if (horz)
x = (Display.Width() / 2) - (win->width / 2);
if (vert)
y = (Display.Height() / 2) - (win->height / 2);
win->x = x;
win->y = y;
}
U0 @gui_window_set_position(Window* win, I64 x, I64 y)
{
if (!win)
return;
win->x = x;
win->y = y;
}
U0 @gui_window_set_icon(Window* win, Context2D* icon)
{
if (!win || !icon)
return;
Bool refresh = FALSE;
if (win->icon != icon)
refresh = TRUE;
win->icon = icon;
if (refresh)
Gui.Window.Refresh(win);
}
U0 @gui_window_set_mouse_pointer(Window* win, Context2D* pointer)
{
if (!win)
return;
win->pointer = pointer;
}
U0 @gui_window_clear_mouse_pointer(Window* win)
{
if (!win)
return;
win->pointer = NULL;
}
U0 @gui_window_set_opacity(Window* win, I64 opacity)
{
if (!win)
return;
Bool refresh = FALSE;
if (win->opacity != opacity)
refresh = TRUE;
win->opacity = ClampI64(opacity, 0, 255);
if (refresh)
Gui.Window.Refresh(win);
}
U0 @gui_window_set_title(Window* win, U8* text)
{
if (!win || !text)
return;
if (!StrLen(text))
return;
if (StrLen(text) > 511) {
MemCpy(&win->title, text, 512);
win->title[511] = NULL;
return;
}
StrCpy(&win->title, text);
Gui.Window.Refresh(win);
}
U0 @gui_window_callback_close(Window* win) { }
U0 @gui_window_callback_maximize(Window* win)
{
win->flags |= WIN_FLAGS_MAXIMIZED;
}
U0 @gui_window_callback_minimize(Window* win)
{
win->flags |= WIN_FLAGS_MINIMIZED;
}
U0 @gui_window_disable_alpha_channel(Window* win) { win->alpha = FALSE; }
U0 @gui_window_enable_alpha_channel(Window* win) { win->alpha = TRUE; }
U0 @gui_window_hide(Window* win) { win->flags |= WIN_FLAGS_HIDDEN; }
U0 @gui_window_show(Window* win)
{
win->flags &= ~WIN_FLAGS_HIDDEN;
win->flags &= ~WIN_FLAGS_MINIMIZED;
}
U0 @gui_window_set_callback(Window* win, U8* name, U64 callback)
{
if (!win || !name || !callback)
return;
if (!StrCmp(name, "minimize"))
win->callback.minimize = callback;
if (!StrCmp(name, "maximize"))
win->callback.maximize = callback;
if (!StrCmp(name, "mouseat"))
win->callback.mouseat = callback;
if (!StrCmp(name, "keypress"))
win->callback.keypress = callback;
if (!StrCmp(name, "repaint"))
win->callback.repaint = callback;
if (!StrCmp(name, "close"))
win->callback.close = callback;
}
Gui.App = &@gui_app;
Gui.Window.Center = &@gui_window_center;
Gui.Window.DisableAlphaChannel = &@gui_window_disable_alpha_channel;
Gui.Window.EnableAlphaChannel = &@gui_window_enable_alpha_channel;
Gui.Window.Hide = &@gui_window_hide;
Gui.Window.IsHovered = &@gui_window_is_hovered;
Gui.Window.IsVisible = &@gui_window_is_visible;
Gui.Window.SetCallback = &@gui_window_set_callback;
Gui.Window.SetIcon = &@gui_window_set_icon;
Gui.Window.SetMousePointer = &@gui_window_set_mouse_pointer;
Gui.Window.ClearMousePointer = &@gui_window_clear_mouse_pointer;
Gui.Window.SetOpacity = &@gui_window_set_opacity;
Gui.Window.SetTitle = &@gui_window_set_title;
Gui.Window.SetPosition = &@gui_window_set_position;
Gui.Window.Show = &@gui_window_show;
"gui ";

628
System/Libraries/Http.HC Normal file
View file

@ -0,0 +1,628 @@
#define HTTP_TMP_DIRECTORY "B:/Tmp"
#define HTTP_CACHE_DIRECTORY "B:/Tmp/Cache"
#define HTTP_FETCH_BUFFER_SIZE 1024 << 15
#define SEDECIM_USER_AGENT_STRING "Mozilla/5.0 (compatible; Sedecim/1.0; TempleOS) (KHTML, like Gecko)"
class @http_buffer
{
I64 length;
U8* data;
};
class @http_status
{
U8* protocol;
I64 code;
U8* text;
};
class @http_response
{
I64 state;
TlsSocket* s;
@http_status status;
JsonObject* headers;
@http_buffer body;
Bool headers_parsed;
};
class @http_url
{
U8* scheme;
U8* host;
I64 port;
U8* path;
U8* query;
U8* fragment;
};
class @http_request
{
@http_url* url;
U8* buf;
U8* data;
I64 type;
JsonObject* headers;
@http_response* response;
};
#define HttpResponse @http_response
#define HttpUrl @http_url
#define HTTP_FETCH_BUFFER_SIZE 16777216
#define HTTP_PARSE_SCHEME 0
#define HTTP_PARSE_SCHEME_FS 1
#define HTTP_PARSE_HOST 2
#define HTTP_PARSE_PORT 3
#define HTTP_PARSE_PATH 4
#define HTTP_PARSE_QUERY 5
#define HTTP_PARSE_FRAGMENT 6
#define HTTP_MIN_REQUEST_BUFFER_SIZE 16384
#define HTTP_PARSE_URL_FIFO_SIZE 1024
#define HTTP_REQ_GET 0
#define HTTP_REQ_HEAD 1
#define HTTP_REQ_POST 2
#define HTTP_REQ_PUT 3
#define HTTP_STATE_UNSENT 0
#define HTTP_STATE_OPENED 1
#define HTTP_STATE_HEADERS_RECEIVED 2
#define HTTP_STATE_LOADING 3
#define HTTP_STATE_DONE 4
U8* @http_string_from_fifo(CFifoU8* f)
{
U8 ch;
I64 i = 0;
U8* str = CAlloc(FifoU8Cnt(f) + 1, erythros_mem_task);
while (FifoU8Cnt(f)) {
FifoU8Rem(f, &ch);
str[i] = ch;
i++;
}
FifoU8Flush(f);
return str;
}
U0 @http_free_url(@http_url* url)
{
if (!url)
return;
if (url->scheme)
Free(url->scheme);
if (url->host)
Free(url->host);
if (url->path)
Free(url->path);
if (url->query)
Free(url->query);
if (url->fragment)
Free(url->fragment);
Free(url);
}
U0 @http_free_response(@http_response* resp)
{
if (!resp)
return;
// FIXME: Free response headers JSON object
Free(resp);
}
@http_url* @http_parse_url(U8* str)
{
if (!str)
return NULL;
U8* buf = NULL;
U8 hex[3];
I64 i = 0;
I64 state = HTTP_PARSE_SCHEME;
CFifoU8* consume_fifo = FifoU8New(HTTP_PARSE_URL_FIFO_SIZE, erythros_mem_task);
@http_url* url = CAlloc(sizeof(@http_url), erythros_mem_task);
while (1) {
switch (str[i]) {
case 0:
switch (state) {
case HTTP_PARSE_HOST:
url->host = @http_string_from_fifo(consume_fifo);
url->path = StrNew("/", erythros_mem_task);
goto done_parsing_url;
break;
case HTTP_PARSE_PORT:
buf = @http_string_from_fifo(consume_fifo);
url->port = Str2I64(buf);
Free(buf);
url->path = StrNew("/", erythros_mem_task);
goto done_parsing_url;
break;
case HTTP_PARSE_PATH:
url->path = @http_string_from_fifo(consume_fifo);
goto done_parsing_url;
break;
case HTTP_PARSE_QUERY:
url->query = @http_string_from_fifo(consume_fifo);
goto done_parsing_url;
break;
case HTTP_PARSE_FRAGMENT:
url->fragment = @http_string_from_fifo(consume_fifo);
goto done_parsing_url;
break;
default:
goto done_parsing_url;
break;
}
break;
case '#':
switch (state) {
case HTTP_PARSE_PATH:
url->path = @http_string_from_fifo(consume_fifo);
FifoU8Ins(consume_fifo, str[i]);
state = HTTP_PARSE_FRAGMENT;
break;
case HTTP_PARSE_QUERY:
url->query = @http_string_from_fifo(consume_fifo);
FifoU8Ins(consume_fifo, str[i]);
state = HTTP_PARSE_FRAGMENT;
break;
}
break;
case '?':
switch (state) {
case HTTP_PARSE_PATH:
url->path = @http_string_from_fifo(consume_fifo);
FifoU8Ins(consume_fifo, str[i]);
state = HTTP_PARSE_QUERY;
break;
}
break;
case '/':
switch (state) {
case HTTP_PARSE_SCHEME:
state = HTTP_PARSE_SCHEME_FS;
goto keep_consuming_url_chars;
break;
case HTTP_PARSE_SCHEME_FS:
FifoU8Ins(consume_fifo, str[i]);
url->scheme = @http_string_from_fifo(consume_fifo);
if (!StrCmp(url->scheme, "http://"))
url->port = 80;
if (!StrCmp(url->scheme, "https://"))
url->port = 443;
state = HTTP_PARSE_HOST;
break;
case HTTP_PARSE_HOST:
url->host = @http_string_from_fifo(consume_fifo);
FifoU8Ins(consume_fifo, str[i]);
state = HTTP_PARSE_PATH;
break;
case HTTP_PARSE_PORT:
buf = @http_string_from_fifo(consume_fifo);
url->port = Str2I64(buf);
Free(buf);
FifoU8Ins(consume_fifo, str[i]);
state = HTTP_PARSE_PATH;
break;
case HTTP_PARSE_PATH:
goto keep_consuming_url_chars;
break;
}
break;
case ':':
switch (state) {
case HTTP_PARSE_SCHEME:
case HTTP_PARSE_PATH:
case HTTP_PARSE_QUERY:
case HTTP_PARSE_FRAGMENT:
goto keep_consuming_url_chars;
break;
case HTTP_PARSE_HOST:
url->host = @http_string_from_fifo(consume_fifo);
state = HTTP_PARSE_PORT;
break;
}
break;
default:
keep_consuming_url_chars:
switch (state) {
case HTTP_PARSE_PATH:
case HTTP_PARSE_QUERY:
switch (str[i]) {
case '0' ... '9':
case 'A' ... 'Z':
case 'a' ... 'z':
case '?':
case '&':
case '/':
case '=':
// !'()*-._~
case '!':
case '\'':
case '(':
case ')':
case '*':
case '-':
case '.':
case '_':
case '~':
case '%':
FifoU8Ins(consume_fifo, str[i]);
break;
default:
FifoU8Ins(consume_fifo, '%');
StrPrint(hex, "%02X", str[i]);
FifoU8Ins(consume_fifo, hex[0]);
FifoU8Ins(consume_fifo, hex[1]);
break;
}
break;
default:
FifoU8Ins(consume_fifo, str[i]);
break;
}
break;
}
i++;
}
done_parsing_url:
FifoU8Flush(consume_fifo);
FifoU8Del(consume_fifo);
return url;
}
U0 @http_parse_response_headers(@http_response* resp, U8* buffer, I64 length)
{
if (!resp || !buffer || !length)
return;
U64 response_data_ptr = StrFind("\r\n\r\n", buffer);
if (!response_data_ptr)
return;
resp->body.data = response_data_ptr + 4;
resp->body.data[-4] = NULL;
JsonObject* headers = Json.CreateObject(erythros_mem_task);
U8** lines = NULL;
I64 lines_count = 0;
I64 i;
I64 j;
U8* key_ptr = NULL;
U8* value_ptr = NULL;
lines = String.Split(buffer, '\n', &lines_count);
U8* resp_protocol = lines[0];
U8* resp_status_code = StrFind(" ", resp_protocol) + 1;
U8* resp_text = StrFind(" ", resp_status_code + 1);
(*StrFind(" ", resp_protocol)) = NULL;
(*StrFind(" ", resp_status_code)) = NULL;
resp->status.protocol = StrNew(resp_protocol, erythros_mem_task);
resp->status.code = Str2I64(resp_status_code);
resp->status.text = StrNew(resp_text, erythros_mem_task);
for (i = 1; i < lines_count; i++) {
for (j = 0; j < StrLen(lines[i]); j++) {
if (lines[i][j] == ':' && lines[i][j + 1] == ' ') {
lines[i][j] = NULL;
key_ptr = lines[i];
value_ptr = lines[i] + j + 2;
(*StrFind("\r", value_ptr)) = NULL;
headers->set(key_ptr, value_ptr, JSON_STRING);
goto @http_next_header_line;
}
}
@http_next_header_line:
}
resp->headers = headers;
resp->headers_parsed = TRUE;
}
Bool @http_detect_response_headers(U8* buf, I64 len)
{
if (len < 4)
return FALSE;
I64 i;
for (i = 0; i < len - 4; i++) {
if (!MemCmp(buf + i, "\r\n\r\n", 4))
return TRUE;
}
return FALSE;
}
I64 @http_req(@http_request* req)
{
if (!req)
return NULL;
if (!req->url || !req->buf || !req->response)
return NULL;
if (!req->url->scheme || !req->url->host || !req->url->path)
return NULL;
if (req->type == HTTP_REQ_POST && !req->data)
return NULL;
if (req->type == HTTP_REQ_PUT && !req->data)
return NULL;
@http_response* resp = req->response;
U8* buf = NULL;
U8* headers_buf = "";
I64 cnt = 1;
I64 len = NULL;
buf = CAlloc(HTTP_MIN_REQUEST_BUFFER_SIZE, erythros_mem_task);
if (req->headers) {
headers_buf = CAlloc(HTTP_MIN_REQUEST_BUFFER_SIZE, erythros_mem_task);
JsonKey* key = req->headers->keys;
while (key) {
StrPrint(headers_buf + StrLen(headers_buf), "%s: %s\r\n", key->name, key->value);
key = key->next;
}
}
switch (req->type) {
case HTTP_REQ_GET:
StrPrint(buf,
"GET %s%s HTTP/1.0\r\n"
"Host: %s\r\n"
"%s"
"User-Agent: " SEDECIM_USER_AGENT_STRING
"\r\n\r\n",
req->url->path, req->url->query, req->url->host, headers_buf);
break;
case HTTP_REQ_HEAD:
StrPrint(buf,
"HEAD %s%s HTTP/1.0\r\n"
"Host: %s\r\n"
"%s"
"User-Agent: " SEDECIM_USER_AGENT_STRING
"\r\n\r\n",
req->url->path, req->url->query, req->url->host, headers_buf);
break;
case HTTP_REQ_POST:
StrPrint(buf,
"POST %s%s HTTP/1.0\r\n"
"Host: %s\r\n"
"%s"
"User-Agent: " SEDECIM_USER_AGENT_STRING
"\r\n"
"Content-Length: %d\r\n\r\n",
req->url->path, req->url->query, req->url->host, headers_buf,
StrLen(req->data));
StrPrint(buf + StrLen(buf), req->data);
break;
case HTTP_REQ_PUT:
StrPrint(buf,
"PUT %s%s HTTP/1.0\r\n"
"Host: %s\r\n"
"%s"
"User-Agent: " SEDECIM_USER_AGENT_STRING
"\r\n"
"Content-Length: %d\r\n\r\n",
req->url->path, req->url->query, req->url->host, headers_buf,
StrLen(req->data));
StrPrint(buf + StrLen(buf), req->data);
break;
}
TlsSocket* s = NULL;
resp->s = NULL;
if (!StrCmp(req->url->scheme, "http://")) {
s = @tcp_socket_create(req->url->host, req->url->port);
resp->s = s;
while (s->state != TCP_SOCKET_STATE_ESTABLISHED)
Sleep(1);
}
if (!StrCmp(req->url->scheme, "https://")) {
s = @tls_socket_create(req->url->host, req->url->port);
resp->s = s;
while (!@tls_established(s->ctx))
Sleep(1);
}
resp->state = HTTP_STATE_OPENED;
s->send(buf, StrLen(buf));
while (cnt || s->state != TCP_SOCKET_STATE_CLOSED) {
cnt = s->receive(req->buf + len, 1024);
len += cnt;
switch (resp->state) {
case HTTP_STATE_LOADING:
resp->body.length += cnt;
break;
case HTTP_STATE_HEADERS_RECEIVED:
resp->body.length = (req->buf + len) - resp->body.data;
resp->state = HTTP_STATE_LOADING;
break;
case HTTP_STATE_OPENED:
if (@http_detect_response_headers(req->buf, len)) {
@http_parse_response_headers(resp, req->buf, len);
resp->state = HTTP_STATE_HEADERS_RECEIVED;
}
break;
}
Sleep(1);
}
if (!resp->headers_parsed)
@http_parse_response_headers(resp, req->buf, len);
resp->state = HTTP_STATE_DONE;
req->buf[len] = NULL;
Free(buf);
if (StrLen(headers_buf) > 0) {
Free(headers_buf);
}
s->close();
resp->s = NULL;
Free(req);
return len;
}
Bool @http_scheme_is_https(@http_url* url)
{
if (!url || !url->scheme)
return FALSE;
return !MemCmp(url->scheme, "https", 5);
}
@http_response* @http_get(@http_url* url, U8* buf, U64 return_req = NULL, JsonObject* headers = NULL)
{
@http_response* resp = CAlloc(sizeof(@http_response), erythros_mem_task);
@http_request* req = CAlloc(sizeof(@http_request), erythros_mem_task);
if (return_req)
MemCpy(return_req, &req, sizeof(U64));
req->url = url;
req->buf = buf;
req->type = HTTP_REQ_GET;
req->headers = headers;
req->response = resp;
Spawn(&@http_req, req, "HTTPGetRequest");
return resp;
}
@http_response* @http_head(@http_url* url, U8* buf, JsonObject* headers = NULL)
{
@http_response* resp = CAlloc(sizeof(@http_response), erythros_mem_task);
@http_request* req = CAlloc(sizeof(@http_request), erythros_mem_task);
req->url = url;
req->buf = buf;
req->type = HTTP_REQ_HEAD;
req->headers = headers;
req->response = resp;
Spawn(&@http_req, req, "HTTPHeadRequest");
return resp;
}
@http_response* @http_post(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL)
{
@http_response* resp = CAlloc(sizeof(@http_response), erythros_mem_task);
@http_request* req = CAlloc(sizeof(@http_request), erythros_mem_task);
req->url = url;
req->buf = buf;
req->type = HTTP_REQ_POST;
req->headers = headers;
req->data = data;
req->response = resp;
Spawn(&@http_req, req, "HTTPPostRequest");
return resp;
}
@http_response* @http_put(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL)
{
@http_response* resp = CAlloc(sizeof(@http_response), erythros_mem_task);
@http_request* req = CAlloc(sizeof(@http_request), erythros_mem_task);
req->url = url;
req->buf = buf;
req->type = HTTP_REQ_PUT;
req->headers = headers;
req->data = data;
req->response = resp;
Spawn(&@http_req, req, "HTTPPutRequest");
return resp;
}
class @http
{
@http_response* (*Get)(@http_url* url, U8* buf, U64* req = NULL, JsonObject* headers = NULL);
@http_response* (*Head)(@http_url* url, U8* buf, JsonObject* headers = NULL);
@http_response* (*Post)(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL);
@http_response* (*Put)(@http_url* url, U8* buf, U8* data, JsonObject* headers = NULL);
};
@http Http;
Http.Get = &@http_get;
Http.Head = &@http_head;
Http.Post = &@http_post;
Http.Put = &@http_put;
Bool @http_is_resource_cached(U8* src, U8* cache_directory = HTTP_CACHE_DIRECTORY)
{
U8 buf[512];
U8* src_md5 = md5_string(src, StrLen(src));
StrCpy(buf, cache_directory);
StrPrint(buf + StrLen(buf), "/%s", src_md5);
Free(src_md5);
return FileFind(buf);
}
U0 @http_cache_resource(U8* src, U8* data, I64 size, U8* cache_directory = HTTP_CACHE_DIRECTORY)
{
U8 buf[512];
U8* src_md5 = md5_string(src, StrLen(src));
StrCpy(buf, cache_directory);
StrPrint(buf + StrLen(buf), "/%s", src_md5);
Free(src_md5);
FileWrite(buf, data, size);
}
U8* @http_get_cached_resource_filename(U8* src, U8* cache_directory = HTTP_CACHE_DIRECTORY)
{
U8* buf = CAlloc(512, erythros_mem_task);
U8* src_md5 = md5_string(src, StrLen(src));
StrCpy(buf, cache_directory);
StrPrint(buf + StrLen(buf), "/%s", src_md5);
Free(src_md5);
return buf;
}
U0 @http_init_tmp_and_cache_directories()
{
if (!FileFind(HTTP_TMP_DIRECTORY))
DirMk(HTTP_TMP_DIRECTORY);
if (!FileFind(HTTP_CACHE_DIRECTORY))
DirMk(HTTP_CACHE_DIRECTORY);
}
@http_response* fetch(U8* url_string, U8* fetch_buffer)
{
if (!url_string || !fetch_buffer)
return NULL;
HttpUrl* url = @http_parse_url(url_string);
if (!url)
return NULL;
@http_response* resp = Http.Get(url, fetch_buffer);
while (resp->state != HTTP_STATE_DONE)
Sleep(1);
return resp;
}
I64 curl(U8* url_string)
{
if (!url_string)
return 0;
U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, erythros_mem_task);
@http_response* resp = fetch(url_string, fetch_buffer);
if (!resp)
return 0;
if (resp->body.length) {
U8* buf = resp->body.data;
while (*buf) {
if (*buf == '\d')
"\d\d";
else
"%c", *buf;
++buf;
}
"\n";
}
Free(fetch_buffer);
return resp->body.length;
}
I64 download(U8* path, U8* url_string)
{
if (!path || !url_string)
return 0;
U8* fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, erythros_mem_task);
@http_response* resp = fetch(url_string, fetch_buffer);
if (!resp)
return 0;
if (resp->body.length) {
FileWrite(path, resp->body.data, resp->body.length);
}
Free(fetch_buffer);
return resp->body.length;
}
"http ";

55
System/Libraries/Image.HC Normal file
View file

@ -0,0 +1,55 @@
class @image
{
Context2D* (*FileToContext2D)(U8* filepath);
Context2D* (*BufferToContext2D)(U8* buffer, I64 size);
};
@image Image;
Context2D* @image_buffer_to_context2d(U8* buffer, I64 size)
{
if (!buffer || !size) {
return NULL;
}
I32 x;
I32 y;
I32 comp;
I32 code = @stbi_info_from_memory(buffer, size, &x, &y, &comp);
if (code != 1) {
return NULL;
}
U8* pixels = @stbi_load_from_memory(buffer, size, &x, &y, &comp, 4);
if (!pixels) {
return NULL;
}
Context2D* ctx = CAlloc(sizeof(Context2D));
ctx->width = x;
ctx->height = y;
ctx->bpp = 32;
ctx->fb = pixels;
I64 i;
for (i = 0; i < x * y; i++) {
ctx->fb(U32*)[i] = @image_pixel_flip_rgb_bgr(ctx->fb(U32*)[i]);
}
return ctx;
}
Context2D* @image_file_to_context2d(U8* filepath)
{
if (!FileFind(filepath)) {
return NULL;
}
I64 size = NULL;
U8* buffer = FileRead(filepath, &size);
if (!buffer || !size) {
return NULL;
}
Context2D* ctx = @image_buffer_to_context2d(buffer, size);
Free(buffer);
return ctx;
}
Image.FileToContext2D = &@image_file_to_context2d;
Image.BufferToContext2D = &@image_buffer_to_context2d;
"image ";

54
System/Libraries/Ipc.HC Normal file
View file

@ -0,0 +1,54 @@
#define ipc user_data.u32[0]
#define IPC_QUEUE_SIZE 1024
#define IPC_ENQUEUE_LIMIT 16
class IpcMessage {
CTask* client;
I64 type;
U64 payload;
I64 i64;
};
class @ipc
{
U0 (*InitQueue)(CTask* task);
IpcMessage* (*MsgRecv)();
U0 (*MsgSend)(CTask* task, IpcMessage* msg);
};
U0 @ipc_queue_init(CTask* task)
{ // Initialize a task's IpcMessage Queue
MemSetU32(task->pad, 0, 1);
if (!task->ipc) {
task->ipc = FifoI64New(IPC_QUEUE_SIZE, task->code_heap);
}
}
IpcMessage* @ipc_msg_recv()
{ // Receive a IpcMessage from current task's
// message queue.
U64 msg_ptr;
if (!FifoI64Cnt(Fs->ipc))
return FALSE;
FifoI64Rem(Fs->ipc, &msg_ptr);
return msg_ptr;
}
U0 @ipc_msg_send(
CTask* task,
IpcMessage* msg)
{ // Send a IpcMessage to a task (client or server)
if (FifoI64Cnt(task->ipc) > IPC_ENQUEUE_LIMIT) {
Free(msg);
return;
}
FifoI64Ins(task->ipc, msg);
}
@ipc Ipc;
Ipc.InitQueue = &@ipc_queue_init;
Ipc.MsgRecv = &@ipc_msg_recv;
Ipc.MsgSend = &@ipc_msg_send;
"ipc ";

1550
System/Libraries/Json.HC Normal file

File diff suppressed because it is too large Load diff

104
System/Libraries/RawText.HC Normal file
View file

@ -0,0 +1,104 @@
I64 @rawtext_output_port = NULL;
U0 @rawtext_detect_qemu()
{
CRAXRBCRCXRDX res;
CPUId(0x40000000, &res);
if (res.rbx == 'KVMK')
@rawtext_output_port = 0xe9;
}
U0 @rawtext_detect_vbox()
{
I64 res = PCIClassFind(0x088000, 0);
if (res >= 0)
@rawtext_output_port = 0x504;
}
U0 @dbg_put_char(I64 ch)
{
OutU8(@rawtext_output_port, ch);
if (!System.text_mode)
return;
Context2D* fb = Graphics2D.FrameBufferContext2D();
text.raw_flags &= ~RWF_SHOW_DOLLAR;
if (ch > '~' && ch != 219)
ch = ' ';
I64 row, col;
if (!(text.raw_flags & RWF_SHOW_DOLLAR)) {
if (ch == '$$') {
if (text.raw_flags & RWF_IN_DOLLAR) {
text.raw_flags &= ~RWF_IN_DOLLAR;
if (!(text.raw_flags & RWF_LAST_DOLLAR)) {
text.raw_flags &= ~RWF_LAST_DOLLAR;
return;
}
} else {
text.raw_flags |= RWF_IN_DOLLAR | RWF_LAST_DOLLAR;
return;
}
}
text.raw_flags &= ~RWF_LAST_DOLLAR;
if (text.raw_flags & RWF_IN_DOLLAR)
return;
}
if (ch == '\t') {
@dbg_put_char(CH_SPACE);
while (text.raw_col & 7)
@dbg_put_char(CH_SPACE);
} else if (ch == CH_BACKSPACE) {
text.raw_col--;
@dbg_put_char(CH_SPACE);
text.raw_col--;
} else if (ch == '\n') {
@dbg_put_char(CH_SPACE);
while (text.raw_col % text.cols)
@dbg_put_char(CH_SPACE);
} else if (Bt(char_bmp_displayable, ch)) {
row = text.raw_col / text.cols % text.rows;
col = text.raw_col % text.cols;
if (text.raw_flags & RWF_SCROLL && text.raw_col && !row && !col) {
CopyRect2D(fb, 0, -16, fb);
Rect2D(fb, 0, Display.Height() - 16, Display.Width(), 16, 0x0);
text.raw_col -= text.cols;
row = text.rows - 1;
}
ConsolePrint2D(fb, col * 8, row * 16, , , "%c", ch);
text.raw_col++;
}
}
Bool @kd_raw_putkey(I64 ch, I64)
{
if (IsRaw) {
@dbg_put_char(ch);
return TRUE;
} else
return FALSE;
}
Bool @kd_raw_puts(U8* st)
{
I64 ch;
if (IsRaw) {
while (ch = *st++)
@dbg_put_char(ch);
return TRUE;
} else
return FALSE;
}
U0 @rawdr_dummy(CTask*) { }
CKeyDevEntry* tmp_kde = keydev.put_key_head;
while (tmp_kde->put_s != &KDRawPutS)
tmp_kde = tmp_kde->next;
tmp_kde->put_key = &@kd_raw_putkey;
tmp_kde->put_s = &@kd_raw_puts;
Function.Patch(&RawDr, &@rawdr_dummy);
@rawtext_detect_qemu;
@rawtext_detect_vbox;
"rawtext ";

46
System/Libraries/Rsa.HC Normal file
View file

@ -0,0 +1,46 @@
Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
I64 @rsa_import(U8* der_bytes, I64 der_len, U64 key)
{
U64 reg RDI rdi = der_bytes;
U64 reg RSI rsi = der_len;
U64 reg RDX rdx = key;
no_warn rdi, rsi, rdx;
asm {
MOV RAX, RSA_IMPORT
CALL RAX
}
}
I64 @rsa_create_signature(U8* sig, I64* siglen, U8* hash, I64 hashlen, U64 key)
{
U64 reg RDI rdi = sig;
U64 reg RSI rsi = siglen;
U64 reg RDX rdx = hash;
U64 reg RCX rcx = hashlen;
U64 reg R8 r8 = key;
no_warn rdi, rsi, rdx, rcx, r8;
asm {
MOV RAX, RSA_CREATE_SIGNATURE
CALL RAX
}
}
I64 @rsa_verify_signature(U8* sig, I64 siglen, U8* hash, I64 hashlen, I32* stat, U64 key)
{
U64 reg RDI rdi = sig;
U64 reg RSI rsi = siglen;
U64 reg RDX rdx = hash;
U64 reg RCX rcx = hashlen;
U64 reg R8 r8 = stat;
U64 reg R9 r9 = key;
no_warn rdi, rsi, rdx, rcx, r8, r9;
asm {
MOV RAX, RSA_VERIFY_SIGNATURE
CALL RAX
}
}
Silent(0);
"rsa ";

View file

@ -0,0 +1,8 @@
class @session
{
U8 home[4096];
U8 hostname[256];
@user user;
};
"session ";

235
System/Libraries/Sha256.HC Normal file
View file

@ -0,0 +1,235 @@
#define CHUNK_SIZE 64
#define TOTAL_LEN_LEN 8
/*
* ABOUT bool: this file does not use bool in order to be as pre-C99 compatible
* as possible.
*/
/*
* Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are
* reproduced here. When useful for clarification, portions of the pseudo-code
* are reproduced here too.
*/
/*
* Initialize array of round constants:
* (first 32 bits of the fractional parts of the cube roots of the first 64
* primes 2..311):
*/
U32 k[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
U32 _h[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
class buffer_state {
U8* p;
I32 len;
I32 total_len;
I32 single_one_delivered; /* bool */
I32 total_len_delivered; /* bool */
};
U32 right_rot(U32 value, U32 count)
{
/*
* Defined behaviour in standard C for all count where 0 < count < 32,
* which is what we need here.
*/
return value >> count | value << (32 - count);
}
U0 init_buf_state(buffer_state* state, U8* input, I32 len)
{
state->p = input;
state->len = len;
state->total_len = len;
state->single_one_delivered = 0;
state->total_len_delivered = 0;
}
/* Return value: bool */
I32 calc_chunk(U8* chunk, buffer_state* state)
{
I32 space_in_chunk;
if (state->total_len_delivered) {
return 0;
}
if (state->len >= CHUNK_SIZE) {
MemCpy(chunk, state->p, CHUNK_SIZE);
state->p += CHUNK_SIZE;
state->len -= CHUNK_SIZE;
return 1;
}
MemCpy(chunk, state->p, state->len);
chunk += state->len;
space_in_chunk = CHUNK_SIZE - state->len;
state->p += state->len;
state->len = 0;
/* If we are here, space_in_chunk is one at minimum. */
if (!state->single_one_delivered) {
*chunk++ = 0x80;
space_in_chunk -= 1;
state->single_one_delivered = 1;
}
/*
* Now:
* - either there is enough space left for the total length, and we can
* conclude,
* - or there is too little space left, and we have to pad the rest of this
* chunk with zeroes. In the latter case, we will conclude at the next
* invokation of this function.
*/
if (space_in_chunk >= TOTAL_LEN_LEN) {
I32 left = space_in_chunk - TOTAL_LEN_LEN;
I32 len = state->total_len;
I32 i;
MemSet(chunk, 0x00, left);
chunk += left;
/* Storing of len * 8 as a big endian 64-bit without overflow. */
chunk[7] = (len << 3);
len >>= 5;
for (i = 6; i >= 0; i--) {
chunk[i] = len;
len >>= 8;
}
state->total_len_delivered = 1;
} else {
MemSet(chunk, 0x00, space_in_chunk);
}
return 1;
}
/*
* Limitations:
* - Since input is a pointer in RAM, the data to hash should be in RAM, which
* could be a problem for large data sizes.
* - SHA algorithms theoretically operate on bit strings. However, this
* implementation has no support for bit string lengths that are not multiples
* of eight, and it really operates on arrays of bytes. In particular, the len
* parameter is a number of bytes.
*/
U0 calc_sha_256(U8* hash, U8* input, I32 len)
{
/*
* Note 1: All integers (expect indexes) are 32-bit U32 integers and addition
* is calculated modulo 2^32. Note 2: For each round, there is one round
* constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
* Note 3: The compression function uses 8 working variables, a through h
* Note 4: Big-endian convention is used when expressing the constants in this
* pseudocode, and when parsing message block data from bytes to words, for
* example, the first word of the input message "abc" after padding is
* 0x61626380
*/
/*
* Initialize hash values:
* (first 32 bits of the fractional parts of the square roots of the first 8
* primes 2..19):
*/
U32 h[8];
U32 i, j;
MemCpy(h, _h, sizeof(U32) * 8);
/* 512-bit chunks is what we will operate on. */
U8 chunk[64];
buffer_state state;
init_buf_state(&state, input, len);
while (calc_chunk(chunk, &state)) {
U32 ah[8];
U8* p = chunk;
/* Initialize working variables to current hash value: */
for (i = 0; i < 8; i++)
ah[i] = h[i];
/* Compression function main loop: */
for (i = 0; i < 4; i++) {
/*
* The w-array is really w[64], but since we only need
* 16 of them at a time, we save stack by calculating
* 16 at a time.
*
* This optimization was not there initially and the
* rest of the comments about w[64] are kept in their
* initial state.
*/
/*
* create a 64-entry message schedule array w[0..63] of 32-bit words
* (The initial values in w[0..63] don't matter, so many implementations
* zero them here) copy chunk into first 16 words w[0..15] of the message
* schedule array
*/
U32 w[16];
U32 s0, s1;
for (j = 0; j < 16; j++) {
if (i == 0) {
w[j] = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
p += 4;
} else {
/* Extend the first 16 words into the remaining 48 words w[16..63] of
* the message schedule array: */
s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ (w[(j + 1) & 0xf] >> 3);
s1 = right_rot(w[(j + 14) & 0xf], 17) ^ right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10);
w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1;
}
s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
U32 ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
U32 temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
U32 maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
U32 temp2 = s0 + maj;
ah[7] = ah[6];
ah[6] = ah[5];
ah[5] = ah[4];
ah[4] = ah[3] + temp1;
ah[3] = ah[2];
ah[2] = ah[1];
ah[1] = ah[0];
ah[0] = temp1 + temp2;
}
}
/* Add the compressed chunk to the current hash value: */
for (i = 0; i < 8; i++)
h[i] += ah[i];
}
/* Produce the final hash value (big-endian): */
for (i = 0, j = 0; i < 8; i++) {
hash[j++] = (h[i] >> 24);
hash[j++] = (h[i] >> 16);
hash[j++] = (h[i] >> 8);
hash[j++] = h[i];
}
}
"sha256 ";

52
System/Libraries/Shell.HC Normal file
View file

@ -0,0 +1,52 @@
#define SHELL_HISTORY_LIMIT 1025
#define SHELL_INPUT_FIFO_SIZE 65536
class @shell_env_var
{
@shell_env_var* prev;
@shell_env_var* next;
U8 key[256];
U8 value[1024];
};
class @shell_autocomplete
{
I64 depth;
I64 length[8];
U8*** entries;
};
class @shell_history
{
I64 index;
I64 limit;
I64 pos;
U8** entries;
};
class @shell_readline
{
@shell_autocomplete autocomplete;
@shell_history history;
};
class @shell
{
CFifoU8* input;
CFifoU8* output;
CTask* task;
Bool break;
Bool exit;
I64 answer;
@shell_env_var* env;
@shell_history history;
@shell_readline readline;
@session* session;
U8 cwd[4096];
U8 PS1[512];
U8 PS2[512];
U8 PS3[512];
U8 PS4[512];
};
"shell ";

155
System/Libraries/Stdio.HC Normal file
View file

@ -0,0 +1,155 @@
class @stdio
{
U0 (*ReadLine)(@shell* sh, U8* str);
U0 (*WriteLine)(@shell* sh, U8* fmt, ...);
};
U0 @stdio_write_line(@shell* sh, U8* fmt, ...)
{
if (!sh)
return;
if (!fmt || !sh->output)
return;
U8* buf;
if (argc) {
buf = StrPrintJoin(NULL, fmt, argc, argv);
} else {
buf = StrNew(fmt, erythros_mem_task);
}
I64 i;
for (i = 0; i < StrLen(buf); i++)
FifoU8Ins(sh->output, buf[i]);
Free(buf);
}
I64 @stdio_handle_control_chars(@shell* sh)
{
if (!FifoU8Cnt(sh->input))
return 0;
U8 char;
FifoU8Rem(sh->input, &char);
switch (char) {
case '[':
if (!FifoU8Cnt(sh->input))
return 0;
FifoU8Rem(sh->input, &char);
switch (char) {
case 'A':
return SC_CURSOR_UP;
break;
case 'B':
return SC_CURSOR_DOWN;
break;
case 'D':
return SC_CURSOR_LEFT;
break;
case 'C':
return SC_CURSOR_RIGHT;
break;
default:
return 0;
break;
}
break;
default:
return 0;
break;
}
}
U0 @stdio_read_line_history_back(@shell* sh, I64 pos)
{
if (sh->history.index < 0)
sh->history.index = 0;
while (pos > 0) {
FifoU8Ins(sh->input, '\x8');
pos--;
}
U8* char = sh->history.entries[sh->history.index];
while (*char)
FifoU8Ins(sh->input, *char ++);
if (sh->history.index > -1) {
sh->history.index--;
}
}
U0 @stdio_read_line_history_fwd(@shell* sh, I64 pos)
{
if (sh->history.index < sh->history.pos) {
sh->history.index++;
}
if (sh->history.index > sh->history.pos)
sh->history.index = sh->history.pos;
while (pos > 0) {
FifoU8Ins(sh->input, '\x8');
pos--;
}
U8* char = sh->history.entries[sh->history.index];
while (*char)
FifoU8Ins(sh->input, *char ++);
}
U0 @stdio_read_line(@shell* sh, U8* str)
{
U8 char = NULL;
U8 line[4096];
I64 pos = 0;
if (!str || !sh)
return;
sh->history.index = sh->history.pos - 1;
while (char != '\x3' && char != '\n') {
while (FifoU8Cnt(sh->input)) {
FifoU8Rem(sh->input, &char);
switch (char) {
case 3:
@stdio_write_line(sh, "^C");
break;
case 8:
if (pos > 0) {
line[StrLen(line) - 1] = NULL;
FifoU8Ins(sh->output, '\x8');
pos--;
} else
FifoU8Ins(sh->output, '\x7');
break;
case 13:
break;
case 27:
switch (@stdio_handle_control_chars(sh)) {
case SC_CURSOR_UP:
@stdio_read_line_history_back(sh, pos);
break;
case SC_CURSOR_DOWN:
@stdio_read_line_history_fwd(sh, pos);
break;
default:
break;
}
break;
case 32...127:
line[pos] = char;
FifoU8Ins(sh->output, char);
pos++;
break;
};
}
Sleep(1);
}
line[pos] = NULL;
switch (char) {
case '\x3':
StrCpy(str, "");
break;
case '\n':
StrCpy(str, &line);
break;
};
FifoU8Ins(sh->output, '\n');
}
@stdio Stdio;
Stdio.ReadLine = &@stdio_read_line;
Stdio.WriteLine = &@stdio_write_line;
"stdio ";

171
System/Libraries/String.HC Normal file
View file

@ -0,0 +1,171 @@
#define TRIM_BOTH 0
#define TRIM_LEFT 1
#define TRIM_RIGHT 2
U0 @string_append(U8* dst, U8* fmt, ...)
{
U8* buf;
if (argc) {
buf = StrPrintJoin(NULL, fmt, argc, argv);
} else {
buf = StrNew(fmt, erythros_mem_task);
}
U8* src = buf;
StrCpy(dst + StrLen(dst), src);
Free(buf);
}
Bool @string_is_number(U8* s)
{
while (*s) {
switch (*s) {
case '-':
case '.':
case '0' ... '9':
break;
default:
return FALSE;
break;
}
s++;
}
return TRUE;
}
Bool @string_begins_with(U8* fragment, U8* str)
{
if (!fragment || !str)
return FALSE;
if (StrLen(fragment) > StrLen(str))
return FALSE;
return !MemCmp(fragment, str, StrLen(fragment));
}
Bool @string_ends_with(U8* fragment, U8* str)
{
if (!fragment || !str)
return FALSE;
if (StrLen(fragment) > StrLen(str))
return FALSE;
return !MemCmp(fragment, str + StrLen(str) - StrLen(fragment), StrLen(fragment));
}
U8* @string_replace(U8* s, U8* oldW, U8* newW)
{
if (!StrFind(oldW, s)) {
return StrNew(s, erythros_mem_task);
}
U8* result;
I64 i, cnt = 0;
I64 newWlen = StrLen(newW);
I64 oldWlen = StrLen(oldW);
for (i = 0; s[i] != NULL; i++) {
if (StrFind(oldW, &s[i]) == &s[i]) {
cnt++;
i += oldWlen - 1;
}
}
result = MAlloc(i + cnt * (newWlen - oldWlen) + 1, erythros_mem_task);
i = 0;
while (*s) {
if (StrFind(oldW, s) == s) {
StrCpy(&result[i], newW);
i += newWlen;
s += oldWlen;
} else
result[i++] = *s++;
}
result[i] = NULL;
return result;
}
U8** @string_split(U8* s, U8 ch = '\n', I64* cnt)
{
U8 check_buf[4];
StrPrint(check_buf, "%c", ch);
if (!StrFind(check_buf, s)) {
U8** same_arr = CAlloc(sizeof(U8*) * 1, erythros_mem_task);
same_arr[0] = s;
*cnt = 1;
return same_arr;
}
U8* p = s;
cnt[0] = 0;
while (*p) {
if (*p == ch)
cnt[0]++;
p++;
}
if (!(cnt[0]))
return NULL;
cnt[0]++;
I64 i = -1;
U8** arr = CAlloc(sizeof(U8*) * cnt[0], erythros_mem_task);
p = s;
while (*p) {
if (*p == ch || i < 0) {
i++;
arr[i] = p;
if (*p == ch) {
arr[i]++;
*p = NULL;
}
}
p++;
}
return arr;
}
Bool @string_trim_ch(U8 s_ch, U8 trim_ch)
{
if (!s_ch) {
return FALSE;
}
if (!trim_ch) {
return (s_ch == ' ' || s_ch == '\r' || s_ch == '\n' || s_ch == '\t');
} else {
return (s_ch == trim_ch);
}
}
U0 @string_trim(U8* s, U8 ch = NULL, I64 mode = TRIM_BOTH)
{
Bool trim_ch = @string_trim_ch(*s, ch);
if (mode == TRIM_BOTH || mode == TRIM_LEFT) {
while (trim_ch) {
StrCpy(s, s + 1);
trim_ch = @string_trim_ch(*s, ch);
}
}
trim_ch = @string_trim_ch(s[StrLen(s) - 1], ch);
if (mode == TRIM_BOTH || mode == TRIM_RIGHT) {
while (trim_ch) {
s[StrLen(s) - 1] = NULL;
trim_ch = @string_trim_ch(s[StrLen(s) - 1], ch);
}
}
}
class @string
{
U0(*Append)
(U8 * dst, U8 * fmt, ...);
Bool (*BeginsWith)(U8* fragment, U8* str);
Bool (*EndsWith)(U8* fragment, U8* str);
Bool (*IsNumber)(U8* s);
U8* (*Replace)(U8* s, U8* oldW, U8* newW);
U8** (*Split)(U8* s, U8 ch = '\n', I64 * cnt);
U0 (*Trim)(U8* s, U8 ch = NULL, I64 mode = TRIM_BOTH);
};
@string String;
String.Append = &@string_append;
String.BeginsWith = &@string_begins_with;
String.EndsWith = &@string_ends_with;
String.IsNumber = &@string_is_number;
String.Replace = &@string_replace;
String.Split = &@string_split;
String.Trim = &@string_trim;
"string ";

104
System/Libraries/System.HC Normal file
View file

@ -0,0 +1,104 @@
class @system
{
Bool text_mode;
CFifoI64* log_fifo;
U8* build_info;
U8* (*BuildInfo)();
U0 (*Init)();
U0 (*Log)(CTask* task, U8* fmt, ...);
U0 (*PowerOff)();
};
@system System;
U0 @system_lex_warn(CCmpCtrl* cc,
U8* str = NULL)
{ // Print warn msg, then, LexPutPos().
if (!MemCmp(str, "Assign U0 ", 10))
return; // suppress "Assign U0 " warnings
if (str)
PrintWarn(str);
if (cc->htc.fun) {
"in fun '%s'.\n", cc->htc.fun->str;
if (IsRaw)
"%s\n", cc->htc.fun->src_link;
else {
"$LK,\"%s\"$\n", cc->htc.fun->src_link;
AdamErr("%s\n", cc->htc.fun->src_link);
}
} else
LexPutPos(cc);
cc->warning_cnt++;
}
U0 @system_print_warn(U8* fmt, ...)
{ // Print "Warn:" and msg in blinking red.
if (!MemCmp(fmt, "Unused var", 10))
return; // suppress "Unused var" warnings
if (!MemCmp(fmt, "Using 64-bit reg var.", 21))
return; // suppress "Using 64-bit reg var." warnings
U8* buf = StrPrintJoin(NULL, fmt, argc, argv);
GetOutOfDollar;
"%,p %,p %,p %,p " ST_WARN_ST "%s", Caller, Caller(2), Caller(3), Caller(4),
buf;
Free(buf);
}
@patch_jmp_rel32(&LexWarn, &@system_lex_warn);
@patch_jmp_rel32(&PrintWarn, &@system_print_warn);
U8* @system_build_info() { return System.build_info; }
U0 @system_log(CTask* task, U8* fmt, ...)
{
U8* buf = StrPrintJoin(NULL, fmt, argc, argv);
U8* str = buf;
U32 color;
MemCpyU32(&color, &task->task_name, 1);
if (!color) {
color = RandU32 * 1048576;
MemCpyU32(&task->pad, &color, 1);
}
U8* log_msg = MAlloc(1024);
StrPrint(log_msg, "[\x1b[38;2;%d;%d;%dm%16s\x1b[0m] %s\n",
color.u8[3] << 5 & 0xFF, color.u8[2] << 4 & 0xFF,
color.u8[1] << 3 & 0xFF, Fs->task_name, buf);
FifoI64Ins(System.log_fifo, log_msg);
Free(buf);
}
U0 @system_log_task()
{
I64 log_msg;
while (1) {
while (FifoI64Cnt(System.log_fifo)) {
FifoI64Rem(System.log_fifo, &log_msg);
"%s", log_msg;
Free(log_msg);
}
Sleep(1);
}
}
U0 @system_power_off()
{
OutU16(0x4004, 0x3400);
OutU16(0x0604, 0x2000);
OutU16(0xB004, 0x2000);
}
U0 @system_init()
{
System.build_info = FileRead("build_info.TXT");
System.log_fifo = FifoI64New(1024);
Spawn(&@system_log_task, , , T(mp_cnt, 1, 0));
}
System.BuildInfo = &@system_build_info;
System.Init = &@system_init;
System.Log = &@system_log;
System.PowerOff = &@system_power_off;
System.Init();
"system ";

49
System/Libraries/Theme.HC Normal file
View file

@ -0,0 +1,49 @@
class @theme_colors
{
U32 active_border;
U32 hilight;
};
class @theme_window
{
I64 min_width;
I64 min_height;
};
class @theme_bitmap_fonts
{
BitmapFont* menu;
BitmapFont* monospace;
BitmapFont* sans;
}
class @theme_pointers
{
Context2D* pointer;
Context2D* pen;
Context2D* move;
Context2D* link;
AnimationContext2D* wait;
Context2D* horz;
Context2D* vert;
Context2D* text;
Context2D* cross;
Context2D* dgn1;
Context2D* dgn2;
Context2D* help;
Context2D* alternate;
Context2D* unavailable;
};
class @theme
{
U8* path;
@theme_colors color;
@theme_bitmap_fonts font;
@theme_pointers pointer;
@theme_window window;
Context2D* wallpaper;
U0 (*window_repaint)(Window* win, I64 type);
};
"theme ";

115
System/Libraries/Tlse.HC Normal file
View file

@ -0,0 +1,115 @@
#define TLS_V12 0x0303
Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
U64 @tls_create_context(U8 is_server, U16 version)
{
U64 reg RDI rdi = is_server;
U64 reg RSI rsi = version;
no_warn rdi, rsi;
asm {
MOV RAX, TLS_CREATE_CONTEXT
CALL RAX
}
}
I32 @tls_sni_set(U64 context, U8* sni)
{
U64 reg RDI rdi = context;
U64 reg RSI rsi = sni;
no_warn rdi, rsi;
asm {
MOV RAX, TLS_SNI_SET
CALL RAX
}
}
I32 @tls_client_connect(U64 context)
{
U64 reg RDI rdi = context;
no_warn rdi;
asm {
MOV RAX, TLS_CLIENT_CONNECT
CALL RAX
}
}
U8* @tls_get_write_buffer(U64 context, U32* outlen)
{
U64 reg RDI rdi = context;
U64 reg RSI rsi = outlen;
no_warn rdi, rsi;
asm {
MOV RAX, TLS_GET_WRITE_BUFFER
CALL RAX
}
}
U0 @tls_buffer_clear(U64 context)
{
U64 reg RDI rdi = context;
no_warn rdi;
asm {
MOV RAX, TLS_BUFFER_CLEAR
CALL RAX
}
}
I32 @tls_connection_status(U64 context)
{
U64 reg RDI rdi = context;
no_warn rdi;
asm {
MOV RAX, TLS_CONNECTION_STATUS
CALL RAX
}
}
U0 @tls_consume_stream(U64 context, U8* buf, I32 buf_len, U64 certificate_verify)
{
U64 reg RDI rdi = context;
U64 reg RSI rsi = buf;
U64 reg RDX rdx = buf_len;
U64 reg RCX rcx = certificate_verify;
no_warn rdi, rsi, rdx, rcx;
asm {
MOV RAX, TLS_CONSUME_STREAM
CALL RAX
}
}
I32 @tls_read(U64 context, U8* buf, U32 size)
{
U64 reg RDI rdi = context;
U64 reg RSI rsi = buf;
U64 reg RDX rdx = size;
no_warn rdi, rsi, rdx;
asm {
MOV RAX, TLS_READ
CALL RAX
}
}
I32 @tls_write(U64 context, U8* data, U32 len)
{
U64 reg RDI rdi = context;
U64 reg RSI rsi = data;
U64 reg RDX rdx = len;
no_warn rdi, rsi, rdx;
asm {
MOV RAX, TLS_WRITE
CALL RAX
}
}
I32 @tls_established(U64 context)
{
U64 reg RDI rdi = context;
no_warn rdi;
asm {
MOV RAX, TLS_ESTABLISHED
CALL RAX
}
}
Silent(0);

10
System/Libraries/User.HC Normal file
View file

@ -0,0 +1,10 @@
class @user
{
I64 uid;
U8 name[256];
U8 fullname[256];
U8 passwd[256];
U8 groups[256];
}
"user ";

834
System/Libraries/Widget.HC Normal file
View file

@ -0,0 +1,834 @@
#define TextInputWidget BitmapFontTextInputWidget
#define TextLabelWidget BitmapFontTextLabelWidget
#define WIDGET_TYPE_NULL 0
#define WIDGET_TYPE_BUTTON 1
#define WIDGET_TYPE_CHECKBOX 2
#define WIDGET_TYPE_RADIO 3
#define WIDGET_TYPE_INPUT 4
#define WIDGET_TYPE_LABEL 5
#define WIDGET_TYPE_CONTEXT2D 6
#define WIDGET_TYPE_TTF_INPUT 7
#define WIDGET_TYPE_TTF_LABEL 8
#define WIDGET_TYPE_HORZ_SLIDER 9
#define WIDGET_TYPE_VERT_SLIDER 10
#define WIDGET_TYPE_TERMINAL 11
#define WIDGET_TYPE_HORZ_SCROLLBAR 12
#define WIDGET_TYPE_VERT_SCROLLBAR 13
#define WIDGET_TYPE_MENU_ITEM 14
#define WIDGET_TYPE_LISTVIEW 15
#define TERMINAL_MAX_COLS 1920 / 8
#define TERMINAL_STATE_OUTPUT 0
#define TERMINAL_STATE_CONSUME_BEGIN 1
#define TERMINAL_STATE_CONSUME_CTRL_SEQ 2
#define TERMINAL_STATE_CONSUME_OS_CMD 3
#define TERMINAL_STATE_CONSUME_END 99
asm {
TERMINAL_COLOR_TABLE::
DU32 0xff000000, 0xff800000, 0xff008000, 0xff808000, 0xff000080, 0xff800080, 0xff008080, 0xffc0c0c0, 0xff808080, 0xffff0000, 0xff00ff00, 0xffffff00, 0xff0000ff, 0xffff00ff, 0xff00ffff, 0xffffffff, 0xff000000, 0xff00005f, 0xff000087, 0xff0000af, 0xff0000d7, 0xff0000ff, 0xff005f00, 0xff005f5f, 0xff005f87, 0xff005faf, 0xff005fd7, 0xff005fff, 0xff008700, 0xff00875f, 0xff008787, 0xff0087af, 0xff0087d7, 0xff0087ff, 0xff00af00, 0xff00af5f, 0xff00af87, 0xff00afaf, 0xff00afd7, 0xff00afff, 0xff00d700, 0xff00d75f, 0xff00d787, 0xff00d7af, 0xff00d7d7, 0xff00d7ff, 0xff00ff00, 0xff00ff5f, 0xff00ff87, 0xff00ffaf, 0xff00ffd7, 0xff00ffff, 0xff5f0000, 0xff5f005f, 0xff5f0087, 0xff5f00af, 0xff5f00d7, 0xff5f00ff, 0xff5f5f00, 0xff5f5f5f, 0xff5f5f87, 0xff5f5faf, 0xff5f5fd7, 0xff5f5fff, 0xff5f8700, 0xff5f875f, 0xff5f8787, 0xff5f87af, 0xff5f87d7, 0xff5f87ff, 0xff5faf00, 0xff5faf5f, 0xff5faf87, 0xff5fafaf, 0xff5fafd7, 0xff5fafff, 0xff5fd700, 0xff5fd75f, 0xff5fd787, 0xff5fd7af, 0xff5fd7d7, 0xff5fd7ff, 0xff5fff00, 0xff5fff5f, 0xff5fff87, 0xff5fffaf, 0xff5fffd7, 0xff5fffff, 0xff870000, 0xff87005f, 0xff870087, 0xff8700af, 0xff8700d7, 0xff8700ff, 0xff875f00, 0xff875f5f, 0xff875f87, 0xff875faf, 0xff875fd7, 0xff875fff, 0xff878700, 0xff87875f, 0xff878787, 0xff8787af, 0xff8787d7, 0xff8787ff, 0xff87af00, 0xff87af5f, 0xff87af87, 0xff87afaf, 0xff87afd7, 0xff87afff, 0xff87d700, 0xff87d75f, 0xff87d787, 0xff87d7af, 0xff87d7d7, 0xff87d7ff, 0xff87ff00, 0xff87ff5f, 0xff87ff87, 0xff87ffaf, 0xff87ffd7, 0xff87ffff, 0xffaf0000, 0xffaf005f, 0xffaf0087, 0xffaf00af, 0xffaf00d7, 0xffaf00ff, 0xffaf5f00, 0xffaf5f5f, 0xffaf5f87, 0xffaf5faf, 0xffaf5fd7, 0xffaf5fff, 0xffaf8700, 0xffaf875f, 0xffaf8787, 0xffaf87af, 0xffaf87d7, 0xffaf87ff, 0xffafaf00, 0xffafaf5f, 0xffafaf87, 0xffafafaf, 0xffafafd7, 0xffafafff, 0xffafd700, 0xffafd75f, 0xffafd787, 0xffafd7af, 0xffafd7d7, 0xffafd7ff, 0xffafff00, 0xffafff5f, 0xffafff87, 0xffafffaf, 0xffafffd7, 0xffafffff, 0xffd70000, 0xffd7005f, 0xffd70087, 0xffd700af, 0xffd700d7, 0xffd700ff, 0xffd75f00, 0xffd75f5f, 0xffd75f87, 0xffd75faf, 0xffd75fd7, 0xffd75fff, 0xffd78700, 0xffd7875f, 0xffd78787, 0xffd787af, 0xffd787d7, 0xffd787ff, 0xffd7af00, 0xffd7af5f, 0xffd7af87, 0xffd7afaf, 0xffd7afd7, 0xffd7afff, 0xffd7d700, 0xffd7d75f, 0xffd7d787, 0xffd7d7af, 0xffd7d7d7, 0xffd7d7ff, 0xffd7ff00, 0xffd7ff5f, 0xffd7ff87, 0xffd7ffaf, 0xffd7ffd7, 0xffd7ffff, 0xffff0000, 0xffff005f, 0xffff0087, 0xffff00af, 0xffff00d7, 0xffff00ff, 0xffff5f00, 0xffff5f5f, 0xffff5f87, 0xffff5faf, 0xffff5fd7, 0xffff5fff, 0xffff8700, 0xffff875f, 0xffff8787, 0xffff87af, 0xffff87d7, 0xffff87ff, 0xffffaf00, 0xffffaf5f, 0xffffaf87, 0xffffafaf, 0xffffafd7, 0xffffafff, 0xffffd700, 0xffffd75f, 0xffffd787, 0xffffd7af, 0xffffd7d7, 0xffffd7ff, 0xffffff00, 0xffffff5f, 0xffffff87, 0xffffffaf, 0xffffffd7, 0xffffffff, 0xff080808, 0xff121212, 0xff1c1c1c, 0xff262626, 0xff303030, 0xff3a3a3a, 0xff444444, 0xff4e4e4e, 0xff585858, 0xff626262, 0xff6c6c6c, 0xff767676, 0xff808080, 0xff8a8a8a, 0xff949494, 0xff9e9e9e, 0xffa8a8a8, 0xffb2b2b2, 0xffbcbcbc, 0xffc6c6c6, 0xffd0d0d0, 0xffdadada, 0xffe4e4e4, 0xffeeeeee;
}
U8 widget_self_set1[0x1F] = { 0x55, 0x48, 0x8B, 0xEC, 0x56, 0x48, 0x8B, 0x75,
0x10, 0x56, 0x48, 0xBB, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x53, 0xE8, 0x0, 0x0,
0x0, 0x0, 0x5E, 0x5D, 0xC2, 0x08, 0x0 };
class ButtonWidget : Widget {
BitmapFont* font;
U8 text[1024];
Context2D* image;
U32 color;
};
class MenuItemWidget : Widget {
BitmapFont* font;
U8 text[128];
Context2D* icon;
U32 color;
U8* path;
Window* submenu;
};
class Context2DWidget : Widget {
Bool fast_copy;
Context2D* ctx;
};
class CheckBoxWidget : Widget {
Bool checked;
};
class RadioButtonWidget : Widget {
I64 group;
Bool selected;
};
class @terminal_widget_attr
{
Bool bold;
Bool underline;
Bool blink;
Bool negative;
Bool invisible;
}
class @terminal_widget_col
{
U32 background;
U32 foreground;
U8 char;
};
class @terminal_widget_color
{
U32 background;
U32 foreground;
U32 cursor;
};
class @terminal_widget_cursor
{
I64 x;
I64 y;
Bool hidden;
};
class @terminal_widget_row
{
@terminal_widget_col col[TERMINAL_MAX_COLS];
};
class @terminal_widget_scroll
{
I64 x;
I64 y;
};
class @terminal_widget_size
{
I64 rows;
I64 cols;
};
class @terminal_widget_stored
{
@terminal_widget_attr attr;
@terminal_widget_color color;
@terminal_widget_cursor cursor;
};
class TerminalWidget : Widget {
CFifoU8* input;
CFifoU8* output;
Bool refresh;
U8 consumed_chars[256];
I64 state;
I64 last_fg_color_set;
@terminal_widget_attr attr;
@terminal_widget_color color;
@terminal_widget_cursor cursor;
@terminal_widget_row* row;
@terminal_widget_scroll scroll;
@terminal_widget_scroll max;
@terminal_widget_size size;
@terminal_widget_stored stored;
};
class BitmapFontTextInputWidget : Widget {
BitmapFont* font;
Bool blink;
Bool in_drag;
Bool is_password;
I64 cursor_index;
I64 mouse_drag_origin_x;
I64 mouse_drag_index;
I64 selected_region_start;
I64 selected_region_end;
I64 x_offset;
U32 color;
U8 text[1024];
U8 password[1024];
U0 (*SetFont)(U8* font);
U0 (*SetText)(U8* text);
}
class BitmapFontTextLabelWidget : Widget {
BitmapFont* font;
U32 color;
U8 text[1024];
U0 (*SetFont)(U8* font);
U0 (*SetText)(U8* text);
};
class TrueTypeTextInputWidget : Widget {
U8* font;
U32 color;
U32 bgcolor;
I64 size;
U8* text;
U8* prev_text;
};
class TrueTypeTextLabelWidget : Widget {
U8* font;
U32 color;
U32 bgcolor;
I64 size;
U8* text;
U8* prev_text;
};
class HorizontalSliderWidget : Widget {
Bool in_drag;
I64 scroll;
I64 max;
I64 value;
};
class VerticalSliderWidget : Widget {
Bool in_drag;
I64 scroll;
I64 max;
I64 value;
};
class HorizontalScrollBarWidget : Widget {
Bool in_drag;
I64 scroll;
I64 max;
I64 value;
};
class VerticalScrollBarWidget : Widget {
Bool in_drag;
I64 scroll;
I64 length;
I64 max;
I64 value;
};
class @list_view_item
{
@list_view_item* prev;
@list_view_item* next;
Context2D* icon;
U8 text[1024];
};
class ListViewWidget : Widget {
BitmapFont* font;
U32 color;
@list_view_item* items;
};
U0 @gui_widget_set_echo(Widget* widget, U8* echo)
{
if (!widget || !echo)
return;
widget->echo = echo;
}
U0 @gui_widget_set_font(Widget* widget, U8* font_name)
{
if (!widget || !font_name)
return;
if (!StrLen(font_name))
return;
switch (widget->type) {
case WIDGET_TYPE_BUTTON:
widget(ButtonWidget*)->font = BitmapFonts.GetByName(font_name);
break;
case WIDGET_TYPE_INPUT:
widget(BitmapFontTextInputWidget*)->font = BitmapFonts.GetByName(font_name);
break;
case WIDGET_TYPE_LABEL:
widget(BitmapFontTextLabelWidget*)->font = BitmapFonts.GetByName(font_name);
break;
case WIDGET_TYPE_LISTVIEW:
widget(ListViewWidget*)->font = BitmapFonts.GetByName(font_name);
break;
default:
break;
}
}
U0 @gui_widget_set_mouse_pointer(Widget* widget, Context2D* pointer)
{
if (!widget)
return;
widget->pointer = pointer;
}
U0 @gui_widget_clear_mouse_pointer(Widget* widget)
{
if (!widget)
return;
widget->pointer = NULL;
}
U0 @gui_widget_set_opacity(Widget* widget, I64 opacity)
{
if (!widget)
return;
widget->opacity = ClampI64(opacity, 0, 255);
}
U0 @gui_widget_set_callback(Widget* widget, U8* name, U64 callback)
{
if (!widget || !name || !callback)
return;
if (!StrCmp(name, "change"))
widget->callback.change = callback;
if (!StrCmp(name, "clicked"))
widget->callback.clicked = callback;
if (!StrCmp(name, "repaint"))
widget->callback.repaint = callback;
}
U0 @gui_widget_set_text(Widget* widget, U8* text)
{
if (!widget)
return;
switch (widget->type) {
case WIDGET_TYPE_BUTTON:
StrCpy(&widget(ButtonWidget*)->text, text);
break;
case WIDGET_TYPE_INPUT:
StrCpy(&widget(TextInputWidget*)->text, text);
break;
case WIDGET_TYPE_LABEL:
StrCpy(&widget(TextLabelWidget*)->text, text);
break;
case WIDGET_TYPE_MENU_ITEM:
StrCpy(&widget(MenuItemWidget*)->text, text);
break;
default:
break;
}
}
Bool @widget_is_hovered(I64 x, I64 y, Widget* widget)
{
if (Mouse.x > x && Mouse.x < x + widget->width && Mouse.y > y && Mouse.y < y + widget->height)
return TRUE;
return FALSE;
}
Bool @gui_widget_is_hovered(Window* win, Widget* widget)
{
return @widget_is_hovered(win->x + widget->x, win->y + widget->y, widget);
}
U0 @widget_add_widget_to_list(Window* win, Widget* widget)
{
if (!win || !widget)
return;
@window_widgets_list* widgets_list = win->widget;
while (widgets_list->next) {
widgets_list = widgets_list->next;
}
@window_widgets_list* widget_list_item = CAlloc(sizeof(@window_widgets_list));
widget_list_item->widget = widget;
widget_list_item->prev = widgets_list;
widgets_list->next = widget_list_item;
}
U0 @widget_input_backspace(BitmapFontTextInputWidget* widget)
{
I64 i;
I64 len = StrLen(&widget->text);
for (i = widget->cursor_index - 1; i < len; i++) {
widget->text[i] = widget->text[i + 1];
}
}
U0 @widget_input_clear_selected_region(BitmapFontTextInputWidget* widget)
{
widget->selected_region_start = -1;
widget->selected_region_end = -1;
}
Bool @widget_input_delete_selected_region(BitmapFontTextInputWidget* widget)
{
I64 i;
I64 j;
if (widget->selected_region_start != -1 && widget->selected_region_end != -1) {
j = widget->selected_region_start;
for (i = widget->selected_region_end + 1; i < StrLen(&widget->text) + 1;
i++) {
widget->text[j++] = widget->text[i];
}
widget->text[j] = NULL;
widget->cursor_index = widget->selected_region_start;
@widget_input_clear_selected_region(widget);
return TRUE;
}
return FALSE;
}
U0 @widget_input_delete_at_cursor(BitmapFontTextInputWidget* widget)
{
I64 i;
I64 len = StrLen(&widget->text);
for (i = widget->cursor_index; i < len; i++)
widget->text[i] = widget->text[i + 1];
@widget_input_clear_selected_region(widget);
}
U0 @widget_input_insert_char(BitmapFontTextInputWidget* widget, I64 char)
{
U8 buf[1024];
U8* pos;
I64 i, j, k;
j = 0;
for (i = widget->cursor_index; i < StrLen(&widget->text); i++) {
buf[j++] = widget->text[i];
}
buf[j] = NULL;
for (i = widget->cursor_index; i < 1024; i++) {
widget->text[i] = NULL;
}
widget->text[StrLen(&widget->text)] = char;
pos = &buf;
while (*pos) {
widget->text[StrLen(&widget->text)] = *pos;
pos++;
}
@widget_input_clear_selected_region(widget);
widget->cursor_index++;
}
U0 @widget_input_insert_scancode(BitmapFontTextInputWidget* widget, I64 key)
{
U8 buf[1024];
U8* pos;
I64 i, j, k;
j = 0;
for (i = widget->cursor_index; i < StrLen(&widget->text); i++) {
buf[j++] = widget->text[i];
}
buf[j] = NULL;
for (i = widget->cursor_index; i < 1024; i++) {
widget->text[i] = NULL;
}
if (!Bt(kbd.down_bitmap, SC_SHIFT))
widget->text[StrLen(&widget->text)] = NORMAL_KEY_SCAN_DECODE_TABLE(U8*)[key];
else
widget->text[StrLen(&widget->text)] = SHIFT_KEY_SCAN_DECODE_TABLE(U8*)[key];
pos = &buf;
while (*pos) {
widget->text[StrLen(&widget->text)] = *pos;
pos++;
}
@widget_input_clear_selected_region(widget);
widget->cursor_index++;
}
U0 @widget_input_insert_text(BitmapFontTextInputWidget* widget, U8* text)
{
while (*text) {
@widget_input_insert_char(widget, *text);
text++;
}
}
Bool @widget_input_handle_key(BitmapFontTextInputWidget* widget)
{
I64 key = Keyboard.active_key;
I64 tS = Keyboard.active_key_tS;
if (widget->cursor_index > StrLen(&widget->text) || widget->cursor_index < 0)
widget->cursor_index = 0;
if (widget->selected_region_start > widget->selected_region_end) {
@widget_input_clear_selected_region(widget);
}
if (key && tS != Keyboard.last_key_tS) {
switch (key) {
case SC_DELETE:
if (widget->selected_region_start != -1 && widget->selected_region_end != -1) {
@widget_input_delete_selected_region(widget);
} else {
@widget_input_delete_at_cursor(widget);
}
break;
case SC_HOME:
if (Bt(kbd.down_bitmap, SC_SHIFT) && widget->selected_region_start) {
widget->selected_region_start = 0;
if (widget->selected_region_end == -1)
widget->selected_region_end = widget->cursor_index - 1;
} else {
@widget_input_clear_selected_region(widget);
widget->cursor_index = 0;
}
break;
case SC_CURSOR_LEFT:
/*
"widget->selected_region_start : %d \n", widget->selected_region_start;
"widget->selected_region_end : %d \n", widget->selected_region_end;
"widget->cursor_index : %d \n", widget->cursor_index;
*/
if (widget->cursor_index)
widget->cursor_index--;
if (!Bt(kbd.down_bitmap, SC_SHIFT)) {
if (widget->selected_region_start != -1)
widget->cursor_index = widget->selected_region_start;
@widget_input_clear_selected_region(widget);
} else {
if (Bt(kbd.down_bitmap, SC_CTRL) && widget->selected_region_start) {
widget->selected_region_start = 0;
if (widget->selected_region_end == -1)
widget->selected_region_end = widget->cursor_index;
break;
}
switch (widget->selected_region_start) {
case -1:
widget->selected_region_start = widget->cursor_index;
widget->selected_region_end = widget->cursor_index;
break;
// case 0:
// break;
default:
if (widget->cursor_index > widget->selected_region_start) {
widget->selected_region_end = widget->cursor_index - 1;
} else {
widget->selected_region_start = widget->cursor_index;
if (widget->selected_region_start == widget->selected_region_end) {
@widget_input_clear_selected_region(widget);
}
}
break;
}
}
break;
case SC_END:
if (Bt(kbd.down_bitmap, SC_SHIFT)) {
widget->selected_region_start = widget->cursor_index;
widget->selected_region_end = StrLen(&widget->text) - 1;
} else {
@widget_input_clear_selected_region(widget);
widget->cursor_index = StrLen(&widget->text);
}
break;
case SC_CURSOR_RIGHT:
/*
"widget->selected_region_start : %d \n", widget->selected_region_start;
"widget->selected_region_end : %d \n", widget->selected_region_end;
"widget->cursor_index : %d \n", widget->cursor_index;
*/
if (!Bt(kbd.down_bitmap, SC_SHIFT)) {
if (widget->selected_region_end != -1)
widget->cursor_index = widget->selected_region_end;
@widget_input_clear_selected_region(widget);
} else {
if (Bt(kbd.down_bitmap, SC_CTRL)) {
widget->selected_region_start = widget->cursor_index;
widget->selected_region_end = StrLen(&widget->text) - 1;
break;
}
switch (widget->selected_region_start) {
case -1:
widget->selected_region_start = widget->cursor_index;
widget->selected_region_end = widget->cursor_index;
break;
default:
if (widget->cursor_index == widget->selected_region_start) {
widget->selected_region_start = widget->cursor_index + 1;
} else
widget->selected_region_end = widget->cursor_index;
break;
}
}
if (widget->cursor_index < StrLen(&widget->text))
widget->cursor_index++;
break;
case SC_BACKSPACE:
if (@widget_input_delete_selected_region(widget))
return TRUE;
if (widget->cursor_index < 1)
break;
@widget_input_backspace(widget);
@widget_input_clear_selected_region(widget);
widget->cursor_index--;
break;
case 0x02 ... 0x0D:
case 0x10 ... 0x1B:
case 0x1E ... 0x29:
case 0x2B ... 0x35:
case 0x39:
if (Bt(kbd.down_bitmap, SC_CTRL)) {
switch (ScanCode2Char(key)) {
case 'a':
if (StrLen(&widget->text)) {
widget->selected_region_start = 0;
widget->selected_region_end = StrLen(&widget->text) - 1;
}
break;
case 'c':
case 'x':
if (widget->selected_region_start != -1 && widget->selected_region_end != -1) {
U64 pos = &widget->text;
pos += widget->selected_region_start;
U8* text = StrNew(pos);
text[widget->selected_region_end - widget->selected_region_start + 1] = NULL;
Clipboard.Insert(CLIP_TYPE_TEXT, text);
if (ScanCode2Char(key) == 'x')
@widget_input_delete_selected_region(widget);
}
break;
case 'v':
// FIXME: Clipboard.Paste?
if (Clipboard.length) {
@widget_input_delete_selected_region(widget);
if (Clipboard.items->prev) {
if (Clipboard.items->prev->item->type == CLIP_TYPE_TEXT) {
@widget_input_insert_text(
widget,
Clipboard.items->prev->item(ClipboardTextItem*)->text);
}
}
}
break;
}
break;
}
@widget_input_delete_selected_region(widget);
@widget_input_insert_scancode(widget, key);
break;
default:
//@widget_input_delete_selected_region(widget);
break;
};
return TRUE;
}
return FALSE;
}
Widget* @widget_create_widget(Window* win, I64 type, I64 x, I64 y, I64 width,
I64 height)
{
if (!win || !type)
return NULL;
I64 size_of_widget;
Widget* widget;
switch (type) {
case WIDGET_TYPE_NULL:
return NULL;
case WIDGET_TYPE_BUTTON:
size_of_widget = sizeof(ButtonWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_CHECKBOX:
size_of_widget = sizeof(CheckBoxWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_TERMINAL:
size_of_widget = sizeof(TerminalWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_RADIO:
size_of_widget = sizeof(RadioButtonWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_INPUT:
size_of_widget = sizeof(TextInputWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_LABEL:
size_of_widget = sizeof(TextLabelWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_LISTVIEW:
size_of_widget = sizeof(ListViewWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_MENU_ITEM:
size_of_widget = sizeof(MenuItemWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_TTF_LABEL:
size_of_widget = sizeof(TrueTypeTextLabelWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_CONTEXT2D:
size_of_widget = sizeof(Context2DWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_HORZ_SLIDER:
size_of_widget = sizeof(HorizontalSliderWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_VERT_SLIDER:
size_of_widget = sizeof(VerticalSliderWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_HORZ_SCROLLBAR:
size_of_widget = sizeof(HorizontalScrollBarWidget) * 2;
goto @widget_create_set_values;
case WIDGET_TYPE_VERT_SCROLLBAR:
size_of_widget = sizeof(VerticalScrollBarWidget) * 2;
goto @widget_create_set_values;
default:
return NULL;
}
@widget_create_set_values : widget = CAlloc(size_of_widget);
widget->type = type;
widget->x = x;
widget->y = y;
widget->width = width;
widget->height = height;
widget->parent_win = win;
switch (type) {
case WIDGET_TYPE_TERMINAL:
widget(TerminalWidget*)->backing_store = NewContext2D(Display.Width(), Display.Height());
widget(TerminalWidget*)->input = FifoU8New(65536);
widget(TerminalWidget*)->color.background = Color(0, 0, 0);
widget(TerminalWidget*)->color.foreground = Color(217, 217, 217);
widget(TerminalWidget*)->color.cursor = Color(217, 217, 0);
widget(TerminalWidget*)->row = CAlloc(sizeof(@terminal_widget_row) * 2000);
break;
case WIDGET_TYPE_INPUT:
widget(TextInputWidget*)->color = Color(0, 0, 0);
widget(TextInputWidget*)->cursor_index = -1;
widget(TextInputWidget*)->mouse_drag_index = -1;
widget(TextInputWidget*)->selected_region_start = -1;
widget(TextInputWidget*)->selected_region_end = -1;
break;
case WIDGET_TYPE_LABEL:
widget(TextLabelWidget*)->color = Color(0, 0, 0);
widget(TextLabelWidget*)->SetText = CAlloc(0x1F, Fs->code_heap);
I32 addr = widget(TextLabelWidget*)->SetText;
MemCpy(addr, widget_self_set1, 0x1F);
MemCpy(addr + 12, &widget, 8);
Function.InsertCall(addr + 21, Gui.Widget.SetText);
break;
case WIDGET_TYPE_LISTVIEW:
widget(ListViewWidget*)->color = Color(0, 0, 0);
widget(ListViewWidget*)->items = CAlloc(sizeof(@list_view_item));
break;
}
@widget_add_widget_to_list(win, widget);
return widget;
}
U0 @widget_init_widget(Widget* widget, Window* win, I64 type, I64 x, I64 y,
I64 width, I64 height)
{
if (!win || !widget || !type)
return;
I64 size_of_widget = 0;
I32 addr = NULL;
switch (type) {
case WIDGET_TYPE_NULL:
return;
case WIDGET_TYPE_BUTTON:
size_of_widget = sizeof(ButtonWidget);
break;
case WIDGET_TYPE_CHECKBOX:
size_of_widget = sizeof(CheckBoxWidget);
break;
case WIDGET_TYPE_TERMINAL:
size_of_widget = sizeof(TerminalWidget);
break;
case WIDGET_TYPE_RADIO:
size_of_widget = sizeof(RadioButtonWidget);
break;
case WIDGET_TYPE_INPUT:
size_of_widget = sizeof(TextInputWidget);
break;
case WIDGET_TYPE_LABEL:
size_of_widget = sizeof(TextLabelWidget);
break;
case WIDGET_TYPE_LISTVIEW:
size_of_widget = sizeof(ListViewWidget);
break;
case WIDGET_TYPE_MENU_ITEM:
size_of_widget = sizeof(MenuItemWidget);
break;
case WIDGET_TYPE_TTF_LABEL:
size_of_widget = sizeof(TrueTypeTextLabelWidget);
break;
case WIDGET_TYPE_CONTEXT2D:
size_of_widget = sizeof(Context2DWidget);
break;
case WIDGET_TYPE_HORZ_SLIDER:
size_of_widget = sizeof(HorizontalSliderWidget);
break;
case WIDGET_TYPE_VERT_SLIDER:
size_of_widget = sizeof(VerticalSliderWidget);
break;
case WIDGET_TYPE_HORZ_SCROLLBAR:
size_of_widget = sizeof(HorizontalScrollBarWidget);
break;
case WIDGET_TYPE_VERT_SCROLLBAR:
size_of_widget = sizeof(VerticalScrollBarWidget);
break;
default:
break;
}
MemSet(widget, NULL, size_of_widget);
widget->type = type;
widget->x = x;
widget->y = y;
widget->width = width;
widget->height = height;
widget->parent_win = win;
switch (type) {
case WIDGET_TYPE_TERMINAL:
widget(TerminalWidget*)->backing_store = NewContext2D(Display.Width(), Display.Height());
widget(TerminalWidget*)->input = FifoU8New(65536);
widget(TerminalWidget*)->color.background = Color(0, 0, 0);
widget(TerminalWidget*)->color.foreground = Color(217, 217, 217);
widget(TerminalWidget*)->color.cursor = Color(217, 217, 0);
widget(TerminalWidget*)->row = CAlloc(sizeof(@terminal_widget_row) * 2000);
break;
case WIDGET_TYPE_INPUT:
widget(TextInputWidget*)->color = Color(0, 0, 0);
widget(TextInputWidget*)->cursor_index = -1;
widget(TextInputWidget*)->mouse_drag_index = -1;
widget(TextInputWidget*)->selected_region_start = -1;
widget(TextInputWidget*)->selected_region_end = -1;
widget(TextInputWidget*)->color = Color(0, 0, 0);
widget(TextInputWidget*)->SetText = CAlloc(0x1F, Fs->code_heap);
addr = widget(TextInputWidget*)->SetFont;
MemCpy(addr, widget_self_set1, 0x1F);
MemCpy(addr + 12, &widget, 8);
Function.InsertCall(addr + 21, Gui.Widget.SetFont);
addr = widget(TextInputWidget*)->SetText;
MemCpy(addr, widget_self_set1, 0x1F);
MemCpy(addr + 12, &widget, 8);
Function.InsertCall(addr + 21, Gui.Widget.SetText);
break;
case WIDGET_TYPE_LABEL:
widget(TextLabelWidget*)->color = Color(0, 0, 0);
widget(TextLabelWidget*)->SetText = CAlloc(0x1F, Fs->code_heap);
addr = widget(TextLabelWidget*)->SetFont;
MemCpy(addr, widget_self_set1, 0x1F);
MemCpy(addr + 12, &widget, 8);
Function.InsertCall(addr + 21, Gui.Widget.SetFont);
addr = widget(TextLabelWidget*)->SetText;
MemCpy(addr, widget_self_set1, 0x1F);
MemCpy(addr + 12, &widget, 8);
Function.InsertCall(addr + 21, Gui.Widget.SetText);
break;
case WIDGET_TYPE_LISTVIEW:
widget(ListViewWidget*)->color = Color(0, 0, 0);
widget(ListViewWidget*)->items = CAlloc(sizeof(@list_view_item));
break;
}
@widget_add_widget_to_list(win, widget);
}
Gui.CreateWidget = &@widget_create_widget;
Gui.InitWidget = &@widget_init_widget;
Gui.Widget.IsHovered = &@gui_widget_is_hovered;
Gui.Widget.SetCallback = &@gui_widget_set_callback;
Gui.Widget.SetEcho = &@gui_widget_set_echo;
Gui.Widget.SetFont = &@gui_widget_set_font;
Gui.Widget.SetMousePointer = &@gui_widget_set_mouse_pointer;
Gui.Widget.ClearMousePointer = &@gui_widget_clear_mouse_pointer;
Gui.Widget.SetOpacity = &@gui_widget_set_opacity;
Gui.Widget.SetText = &@gui_widget_set_text;
"widget ";

103
System/MakeSystem.HC Normal file
View file

@ -0,0 +1,103 @@
/* clang-format off */
DocMax(adam_task);
WinMax(adam_task);
WinToTop(adam_task);
#include "Setup/Environment";
// Erythros system drivers
"drivers: { ";
#include "Drivers/Audio";
#include "Drivers/Display";
#include "Drivers/Mouse";
#include "Drivers/Pci";
#include "Drivers/Virtio-blk";
#include "Drivers/VMSVGA";
#include "Drivers/VMwareTools";
#include "Drivers/AC97";
"}\n";
// FFI support files
#include "FFI/Base";
#include "FFI/LibC";
#include "FFI/New";
#include "FFI/ELF64";
// stb_image library
#include "Utilities/Image";
load_elf("M:/build/bin/image");
// Jakt support files
#include "Jakt/OS";
#include "Jakt/IOPort";
#include "Jakt/PCI";
#include "Jakt/Time";
#include "Libraries/Tlse";
load_elf("M:/build/bin/tlse");
// Networking APIs
#include "Api/Dns.HC";
#include "Api/Icmp.HC";
#include "Api/Ipv4.HC";
#include "Api/MD5.HC";
#include "Api/NetInfo.HC";
#include "Api/Tcp.HC";
#include "Api/Tls.HC";
// Erythros system libraries
"libraries: { ";
#include "Libraries/Function";
#include "Libraries/Base64";
#include "Libraries/String";
#include "Libraries/BitmapFont";
#include "Libraries/Display";
#include "Libraries/FileSystem";
#include "Libraries/Graphics2D";
#include "Libraries/Animation2D";
#include "Libraries/Image";
#include "Libraries/Json";
#include "Libraries/Rsa";
#include "Libraries/Sha256";
#include "Libraries/System";
#include "Libraries/RawText";
#include "Libraries/User";
#include "Libraries/Session";
#include "Libraries/Shell";
#include "Libraries/Stdio";
#include "Libraries/Http";
#include "Libraries/Audio";
#include "Libraries/Gui";
#include "Libraries/Ipc";
#include "Libraries/Clipboard";
#include "Libraries/Widget";
#include "Libraries/Theme";
"}\n";
@http_init_tmp_and_cache_directories;
load_elf("M:/build/bin/net");
// Networking Utilities
#include "Utilities/Dns";
#include "Utilities/NetRep";
#include "Utilities/Ping";
#include "Utilities/Time";
Spawn(_start, , "Net Task");
TimeSync;
#include "Core/Compositor";
#include "Core/FileSystem";
#include "Core/Menu";
#include "Core/MessageBox";
#include "Core/Shell";
#include "Core/ShellCommands";
#include "Core/SystemTray";
#include "Core/SystemStarter";
#include "Setup/Init";
/* clang-format on */

229
System/Setup/Environment.HC Normal file
View file

@ -0,0 +1,229 @@
AutoComplete(0);
#define include_noreindex #include
I64 tos_nist_offset = 5603; // UTC -4
#define NIST_TIME_OFFSET (tos_nist_offset - local_time_offset / CDATE_FREQ)
public
I64 CDate2Unix(CDate dt)
{ // TempleOS datetime to Unix timestamp.
return ToI64((dt - Str2Date("1/1/1970")) / CDATE_FREQ + NIST_TIME_OFFSET);
}
public
CDate Unix2CDate(I64 timestamp)
{ // Unix timestamp to TempleOS datetime.
return (timestamp - NIST_TIME_OFFSET) * CDATE_FREQ + Str2Date("1/1/1970");
}
// FIXME: Put these in a "Builtin" library?
U0 FifoU8Cpy(CFifoU8* f, U8* s)
{
if (!f || !s)
return;
while (*s)
FifoU8Ins(f, *s++);
}
Bool KeyDown(I64 sc) return Bt(kbd.down_bitmap, sc);
I64 T(Bool _condition, I64 _true, I64 _false)
{
if (_condition)
return _true;
return _false;
}
asm
{
_MEMCPY_U16::
PUSH RBP
MOV RBP,RSP
PUSH RSI
PUSH RDI
CLD
MOV RDI,U64 SF_ARG1[RBP]
MOV RSI,U64 SF_ARG2[RBP]
MOV RCX,U64 SF_ARG3[RBP]
REP_MOVSW
MOV RAX,RDI
POP RDI
POP RSI
POP RBP
RET1 24
_MEMCPY_U32::
PUSH RBP
MOV RBP,RSP
PUSH RSI
PUSH RDI
CLD
MOV RDI,U64 SF_ARG1[RBP]
MOV RSI,U64 SF_ARG2[RBP]
MOV RCX,U64 SF_ARG3[RBP]
REP_MOVSD
MOV RAX,RDI
POP RDI
POP RSI
POP RBP
RET1 24
_MEMCPY_U64::
PUSH RBP
MOV RBP,RSP
PUSH RSI
PUSH RDI
CLD
MOV RDI,U64 SF_ARG1[RBP]
MOV RSI,U64 SF_ARG2[RBP]
MOV RCX,U64 SF_ARG3[RBP]
REP_MOVSQ
MOV RAX,RDI
POP RDI
POP RSI
POP RBP
RET1 24
}
public _extern _MEMCPY_U16 U16* MemCpyU16(U16* dst, U16* src, I64 cnt);
public
_extern _MEMCPY_U32 U32* MemCpyU32(U32* dst, U32* src, I64 cnt);
public
_extern _MEMCPY_U64 U64* MemCpyU64(U64* dst, U64* src, I64 cnt);
I64 @lerp(U32 val, U32 mx1, U32 mx2)
{
F64 r = (val & mx1) / ToF64(mx1);
return ToI64(r * mx2);
}
U0 @patch_call_rel32(U32 from, U32 to)
{
*(from(U8*)) = 0xE8;
*((from + 1)(I32*)) = to - from - 5;
}
U0 @patch_jmp_rel32(U32 from, U32 to)
{
*(from(U8*)) = 0xE9;
*((from + 1)(I32*)) = to - from - 5;
}
CMemBlk* ShrinkMemBlkByPags(CMemBlk* from, I64 count)
{
from->pags -= count;
U64 to = from;
to += count * MEM_PAG_SIZE;
MemCpy(to, from, MEM_PAG_SIZE);
return to;
}
U0 @sse_enable()
{
/* clang-format off */
asm
{
MOV_EAX_CR0
AND AX, 0xFFFB // clear coprocessor emulation CR0.EM
OR AX, 0x2 // set coprocessor monitoring CR0.MP
MOV_CR0_EAX
MOV_EAX_CR4
OR AX, 3 << 9 // set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
MOV_CR4_EAX
}
/* clang-format on */
}
U0 @sse_enable_on_all_cores()
{
I64 i;
for (i = 1; i < mp_cnt; i++)
Spawn(&@sse_enable, , , i);
}
I64 @t(Bool _condition, I64 _true, I64 _false)
{
if (_condition)
return _true;
return _false;
}
U0 @erythros_mem_task_loop()
{
while (1) {
Sleep(1);
};
}
// Before doing anything else, we:
// 1. Mark memory in code heap below 0x1000000 as used.
sys_code_bp->mem_free_lst->next->pags = 0;
// 2. Free up 64MB at bottom of code heap for non-HolyC programs
sys_code_bp->mem_free_lst = ShrinkMemBlkByPags(sys_code_bp->mem_free_lst, 131072);
// 3. Enable SSE
@sse_enable;
// 4. Init mem_tasks
CTask* erythros_mem_task = Spawn(&@erythros_mem_task_loop, , "ErythrosMemTask");
#define MALLOC_MEM_TASK_COUNT 4
CTask** malloc_mem_task = CAlloc(sizeof(CTask*) * MALLOC_MEM_TASK_COUNT, erythros_mem_task);
I64 malloc_current_mem_task = 0;
U0 @malloc_mem_tasks_init()
{
U8* scratch_buffer[64];
I64 i;
for (i = 0; i < MALLOC_MEM_TASK_COUNT; i++) {
StrPrint(scratch_buffer, "ErythrosMallocTask%d", i);
malloc_mem_task[i] = Spawn(&@erythros_mem_task_loop, , scratch_buffer);
}
}
@malloc_mem_tasks_init;
U0 dd() { DocDump(adam_task->put_doc); }
//@patch_jmp_rel32(&Fault2, &Reboot); // Reboot instead of crashing to the debugger
U0 NoBeep(I8, Bool) {};
@patch_jmp_rel32(&Beep, &NoBeep); // Don't delay on beep when entering debugger
Bool BlkDevLock2(CBlkDev* bd)
{
BlkDevChk(bd);
while (bd->lock_fwding)
bd = bd->lock_fwding;
if (!Bt(&bd->locked_flags, BDlf_LOCKED) || bd->owning_task != Fs) {
while (LBts(&bd->locked_flags, BDlf_LOCKED))
Sleep(Rand * 10);
bd->owning_task = Fs;
return TRUE;
} else
return FALSE;
}
Bool DrvLock2(CDrv* dv)
{
DrvChk(dv);
BlkDevLock2(dv->bd);
if (!Bt(&dv->locked_flags, DVlf_LOCKED) || dv->owning_task != Fs) {
while (LBts(&dv->locked_flags, DVlf_LOCKED))
Sleep(Rand * 10);
dv->owning_task = Fs;
return TRUE;
} else
return FALSE;
}
@patch_jmp_rel32(&BlkDevLock, &BlkDevLock2); // Patch BlkDevLock so we don't deadlock on multiple tasks reading from virtio disk
@patch_jmp_rel32(&DrvLock, &DrvLock2); // Patch DrvLock so we don't deadlock on multiple tasks reading from virtio disk
U0 @erythros_mem_task_loop()
{
while (1) {
Sleep(1);
};
}
CTask* erythros_mem_task = Spawn(&@erythros_mem_task_loop, , "ErythrosMemTask");

65
System/Setup/Init.HC Normal file
View file

@ -0,0 +1,65 @@
// Save pointer to TempleOS system-wide (CTRL-ALT) callbacks
U64 tos_fp_cbs_enabled = keydev.fp_ctrl_alt_cbs;
U64 tos_fp_cbs_disabled = CAlloc(0xD0);
U0 @erythros_init()
{
I64 err = 0;
// Initialize Display
if (!Display.Driver()) {
err = Display.Init(1920, 1080, 32, FB_VMSVGA);
}
if (err) {
DocClear(Fs->put_doc);
"No supported display device found.";
while (1) {
Sleep(1);
};
}
// Initialize Mouse
Mouse.Init();
Spawn(Mouse.Task, , "Mouse");
// Enable debug output
Raw(ON);
DocDump(adam_task->put_doc);
// Disable TempleOS system-wide (CTRL-ALT) callbacks
keydev.fp_ctrl_alt_cbs = tos_fp_cbs_disabled;
// Suspend TempleOS Window Manager task
Suspend(sys_winmgr_task);
// Reassign VGA writes to a random buffer to avoid collision with SVGA FB
text.vga_alias = MAlloc(1048576, adam_task);
// Initialize Graphics2D Library
Graphics2D.Init();
"\x1b[2J\x1b[H";
//"%s\n", System.BuildInfo();
switch (Display.Driver()) {
case FB_VMSVGA:
"Display driver is: VMSVGA\n";
break;
}
// Initialize FileSystem
// FileSystem.Init();
// Initialize Compositor
Compositor.Init();
// Spawn Compositor
Spawn(Compositor.Task, , "Compositor");
// Spawn SystemStarter
Spawn(SystemStarter.Task, , "SystemStarter", 1);
}
@erythros_init;

View file

@ -0,0 +1,35 @@
I64 @shell_cmd_aplay(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
if (argc < 2) {
StrPrint(&buf, "Error reading file %s\n", argv[1]);
Stdio.WriteLine(sh, "Usage: aplay [OPTION]... [FILE]...\n");
return 1;
}
I64 i;
I64 size;
Sound* snd;
U8* filename = NULL;
for (i = 1; i < argc; i++) {
filename = @shell_expand_relative_path(sh, argv[i]);
System.Log(Fs, "filename: %s", filename);
if (FileSystem.PathExists(filename)) {
snd = Audio.SoundFromFile(filename);
if (!snd) {
StrPrint(&buf, "%s: Error playing audio file\n", filename);
Stdio.WriteLine(sh, &buf);
Free(filename);
return 1;
}
Audio.PlaySound(snd);
Audio.FreeSound(snd);
Free(filename);
} else {
StrPrint(&buf, "%s: No such file or directory\n", filename);
Stdio.WriteLine(sh, &buf);
Free(filename);
return 1;
}
}
return 0;
}

View file

@ -0,0 +1,19 @@
I64 @shell_cmd_cat(@shell* sh, I64 argc, U8** argv)
{
if (argc < 2)
return 0;
I64 i;
I64 j;
I64 size = 0;
U8* filename = NULL;
U8* buf = NULL;
for (i = 1; i < argc; i++) {
filename = @shell_expand_relative_path(sh, argv[i]);
buf = FileSystem.ReadFile(filename, &size);
for (j = 0; j < size; j++)
FifoU8Ins(sh->output, buf[j]);
Free(buf);
Free(filename);
}
return 0;
}

View file

@ -0,0 +1,9 @@
I64 @shell_cmd_clear(@shell* sh, I64 argc, U8** argv)
{
if (argc > 1) {
Stdio.WriteLine(sh, "esh: clear: too many arguments\n");
return 1;
}
Stdio.WriteLine(sh, "\x1b[2J\x1b[0;0H");
return 0;
}

View file

@ -0,0 +1,10 @@
I64 @shell_cmd_echo(@shell* sh, I64 argc, U8** argv)
{
I64 i;
for (i = 1; i < argc; i++) {
Stdio.WriteLine(sh, argv[i]);
Stdio.WriteLine(sh, " ");
}
Stdio.WriteLine(sh, "\n");
return 0;
}

View file

@ -0,0 +1,5 @@
I64 @shell_cmd_esh(@shell* sh, I64 argc, U8** argv)
{
@shell_input_loop(sh);
return 0;
}

View file

@ -0,0 +1,14 @@
I64 @shell_cmd_history(@shell* sh, I64 argc, U8** argv)
{
I64 i;
I64 j;
U8 buf[512];
for (i = 0; i < sh->history.pos; i++) {
StrPrint(&buf, "%05d %s\n", i + 1, sh->history.entries[i]);
j = 0;
while (buf[j] == '0')
buf[j++] = ' ';
Stdio.WriteLine(sh, &buf);
}
return 0;
}

View file

@ -0,0 +1,47 @@
#define ETHERNET_FRAME_SIZE 1400
I64 @shell_cmd_ifconfig(@shell* sh, I64 argc, U8** argv)
{
NetInfoRequest* req = @net_info_request;
U64 en0_mac = req->mac_address;
U32 en0_addr = req->ipv4_address;
U32 en0_mask = req->ipv4_netmask;
U32 en0_bcast = req->ipv4_address | ~req->ipv4_netmask;
U8 buf[512];
Stdio.WriteLine(sh, "en0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu %d\n",
ETHERNET_FRAME_SIZE - 18);
Stdio.WriteLine(sh,
" inet %d.%d.%d.%d netmask %d.%d.%d.%d broadcast "
"%d.%d.%d.%d\n",
en0_addr.u8[3], en0_addr.u8[2], en0_addr.u8[1], en0_addr.u8[0],
en0_mask.u8[3], en0_mask.u8[2], en0_mask.u8[1], en0_mask.u8[0],
en0_bcast.u8[3], en0_bcast.u8[2], en0_bcast.u8[1], en0_bcast.u8[0]);
Stdio.WriteLine(sh,
" ether %02x:%02x:%02x:%02x:%02x:%02x txqueuelen 0 "
"(Ethernet)\n",
en0_mac.u8[5], en0_mac.u8[4], en0_mac.u8[3], en0_mac.u8[2], en0_mac.u8[1],
en0_mac.u8[0]);
Stdio.WriteLine(sh, " RX packets %d bytes %d\n", req->rx_frames,
req->rx_bytes);
Stdio.WriteLine(sh, " RX errors %d dropped %d overruns %d frame %d\n", 0,
0, 0, 0); // TODO
Stdio.WriteLine(sh, " TX packets %d bytes %d\n", req->tx_frames,
req->tx_bytes);
Stdio.WriteLine(sh,
" TX errors %d dropped %d overruns %d carrier %d "
"collisions %d\n",
0, 0, 0, 0, 0); // TODO
Stdio.WriteLine(sh, "\n");
return 0;
}

View file

@ -0,0 +1,70 @@
#define @shell_cmd_ls_opt_a 0
#define @shell_cmd_ls_opt_l 1
I64 @shell_cmd_ls_output(@shell* sh, U8* arg_path, I64 flags)
{
U8 buf[512];
U8* path = @shell_expand_relative_path(sh, arg_path);
if (!FileSystem.PathExists(path)) {
StrPrint(&buf, "ls: cannot access '%s': No such file or directory\n", path);
Stdio.WriteLine(sh, &buf);
Free(path);
return 2;
}
@dir_entry* tmpf = NULL;
@dir_entry* tmpf2 = NULL;
tmpf = FileSystem.GetFiles(path);
if (tmpf)
while (tmpf) {
if (tmpf->type == DE_TYPE_DIR) {
StrCpy(&buf, "\x1b[1;34m");
Stdio.WriteLine(sh, &buf);
}
StrPrint(&buf, "%s\x1b[0m %u\n", &tmpf->name, tmpf->size);
Stdio.WriteLine(sh, &buf);
tmpf2 = tmpf;
tmpf = tmpf->next;
Free(tmpf2);
}
Free(path);
return 0;
}
I64 @shell_cmd_ls(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
U8* options_list = "al";
U64 options_err = NULL;
I64 dir_cnt = 0;
I64 flags = NULL;
I64 rval = 0;
I64 i;
switch (@shell_parse_opts(sh, options_list, argc, argv, &flags, &options_err,
TRUE)) {
case SHELL_OPTS_ERR_INVALID_OPT:
StrPrint(&buf, "ls: invalid option -- '%s'\n", options_err);
Stdio.WriteLine(sh, &buf);
break;
default:
break;
}
if (options_err) {
Free(options_err);
return 2;
}
for (i = 1; i < argc; i++)
if (argv[i][0] != '-')
dir_cnt++;
if (!dir_cnt) {
return @shell_cmd_ls_output(sh, &sh->cwd, flags);
} else {
for (i = 1; i < argc; i++)
if (argv[i][0] != '-') {
rval = Max(rval, @shell_cmd_ls_output(sh, argv[i], flags));
}
}
return rval;
}

View file

@ -0,0 +1,32 @@
I64 @shell_cmd_nslookup(@shell* sh, I64 argc, U8** argv)
{
if (argc < 2) {
// TODO: Interactive mode
return 0;
}
if (argc > 2) {
// TODO: Server argument
}
NetInfoRequest* req = @net_info_request;
U32 resolver_ip = req->dns_server_address;
Free(req);
Stdio.WriteLine(sh, "Server: %d.%d.%d.%d\n", resolver_ip.u8[3],
resolver_ip.u8[2], resolver_ip.u8[1], resolver_ip.u8[0]);
Stdio.WriteLine(sh, "Address: %d.%d.%d.%d#53\n\n", resolver_ip.u8[3],
resolver_ip.u8[2], resolver_ip.u8[1], resolver_ip.u8[0]);
U32 res_ip = @dns_query(argv[1]);
if (res_ip == U32_MAX) {
Stdio.WriteLine(sh, "** server can't find %s: NXDOMAIN\n\n", argv[1]);
return 1;
}
Stdio.WriteLine(sh, "Non-authoritative answer:\n");
Stdio.WriteLine(sh, "Name: %s\n", argv[1]);
Stdio.WriteLine(sh, "Address: %d.%d.%d.%d\n\n", res_ip.u8[3], res_ip.u8[2],
res_ip.u8[1], res_ip.u8[0]);
return 0;
}

View file

@ -0,0 +1,8 @@
I64 @shell_cmd_open(@shell* sh, I64 argc, U8** argv)
{
if (argc < 2) {
Stdio.WriteLine(sh, "open: path required\n");
return 1;
}
return @systemstarter_open(sh, argc, argv);
}

View file

@ -0,0 +1,58 @@
I64 @shell_cmd_ping(@shell* sh, I64 argc, U8** argv)
{
if (argc < 2) {
Stdio.WriteLine(sh, "ping: usage error: Destination address required\n");
return 1;
}
U8* host = argv[1];
if (!host || !StrLen(host)) {
Stdio.WriteLine(sh, "Invalid host specified\n");
return PING_ERR_INVALID_HOST;
}
I64 count = 4;
U32 addr = @dns_query(host);
if (addr == U32_MAX) {
Stdio.WriteLine(sh, "Host not found\n");
return PING_ERR_HOST_NOT_FOUND;
}
U16 iden = (RandU16 * SysTimerRead) & 0xFFFF;
I64 start_jiffies;
U32 reply = NULL;
I64 res = 0;
U16 seq = 0;
I64 loss = 0;
IcmpRequest* request = CAlloc(sizeof(IcmpRequest), Fs->code_heap);
Stdio.WriteLine(sh, "PING %s (%d.%d.%d.%d): %d data bytes\n",
host, addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], PING_PAYLOAD_SIZE);
I64 i;
for (i = 0; i < count; i++) {
start_jiffies = cnts.jiffies;
reply = @icmp_echo_request(addr, iden, seq, request, i);
if (!reply) {
Stdio.WriteLine(sh, "Request timeout for icmp_seq %d\n", seq);
++loss;
res = 1;
} else {
Stdio.WriteLine(sh, "%d bytes from %d.%d.%d.%d: icmp_seq=%d ttl=%d time=%d ms\n",
reply.u16[1], addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], seq, reply.u16[0], cnts.jiffies - start_jiffies);
}
while (cnts.jiffies < start_jiffies + 1000 && i < (count - 1))
Sleep(1);
++seq;
}
Free(request);
Stdio.WriteLine(sh, "--- %d.%d.%d.%d ping statistics ---\n", addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0]);
Stdio.WriteLine(sh, "%d packets transmitted, %d packets received, %0f",
seq, seq - loss, (loss * 1.0 / seq * 1.0) * 100);
Stdio.WriteLine(sh, "%c", 37);
Stdio.WriteLine(sh, " packet loss\n");
return res;
}

View file

@ -0,0 +1,5 @@
I64 @shell_cmd_poweroff(@shell* sh, I64 argc, U8** argv)
{
System.PowerOff();
return 0;
}

View file

@ -0,0 +1,7 @@
I64 @shell_cmd_pwd(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
StrPrint(&buf, "%s\n", &sh->cwd);
Stdio.WriteLine(sh, &buf);
return 0;
}

View file

@ -0,0 +1,5 @@
I64 @shell_cmd_reboot(@shell* sh, I64 argc, U8** argv)
{
Reboot;
return 0;
}

View file

@ -0,0 +1,4 @@
I64 @shell_cmd_sh(@shell* sh, I64 argc, U8** argv)
{
return @shell_cmd_esh(sh, argc, argv);
}

View file

@ -0,0 +1,81 @@
#define @shell_cmd_uname_opt_s 0
#define @shell_cmd_uname_opt_n 1
#define @shell_cmd_uname_opt_r 2
#define @shell_cmd_uname_opt_v 3
#define @shell_cmd_uname_opt_m 4
#define @shell_cmd_uname_opt_p 5
#define @shell_cmd_uname_opt_i 6
#define @shell_cmd_uname_opt_o 7
#define @shell_cmd_uname_opt_a 8
I64 @shell_cmd_uname(@shell* sh, I64 argc, U8** argv)
{
I64 i;
CDateStruct* ds = CAlloc(sizeof(CDateStruct));
Date2Struct(ds, sys_compile_time);
U8* options_list = "snrvmpioa";
U64 options_err = NULL;
U8* ds_m = "JanFebMarAprMayJunJulAugSepOctNovDec";
U8* ds_d = "SunMonTueWedThuFriSat";
U8* ds_mm = " ";
U8* ds_dd = " ";
U8 buf[512];
I64 flags = NULL;
StrCpy(&buf, "");
if (argc < 2)
flags |= 1 << @shell_cmd_uname_opt_s;
switch (
@shell_parse_opts(sh, options_list, argc, argv, &flags, &options_err)) {
case SHELL_OPTS_ERR_INVALID_OPT:
StrPrint(&buf, "uname: invalid option -- '%s'\n", options_err);
Stdio.WriteLine(sh, &buf);
break;
case SHELL_OPTS_ERR_EXTRA_OPD:
StrPrint(&buf, "uname: extra operand '%s'\n", options_err);
Stdio.WriteLine(sh, &buf);
break;
default:
break;
}
if (options_err) {
Free(options_err);
Free(ds);
return 1;
}
if (flags & 1 << @shell_cmd_uname_opt_a)
flags = 0x01FF; // Set all options.
for (i = 0; i < 8; i++) {
switch (flags & 1 << i) {
case 1 << @shell_cmd_uname_opt_s:
String.Append(&buf, Define("DD_OS_NAME_VERSION"));
*StrLastOcc(&buf, "V") = NULL;
break;
case 1 << @shell_cmd_uname_opt_n:
String.Append(&buf, "%s ", &sh->session->hostname);
break;
case 1 << @shell_cmd_uname_opt_r:
String.Append(&buf, "%1.2f ", sys_os_version);
break;
case 1 << @shell_cmd_uname_opt_v:
MemCpy(ds_mm, ds_m + ((ds->mon - 1) * 3), 3);
MemCpy(ds_dd, ds_d + (ds->day_of_week * 3), 3);
String.Append(&buf, "%s %s %d %02d:%02d:%02d UTC %d ", ds_dd, ds_mm,
ds->day_of_mon, ds->hour, ds->min, ds->sec, ds->year);
break;
case 1 << @shell_cmd_uname_opt_m:
case 1 << @shell_cmd_uname_opt_p:
case 1 << @shell_cmd_uname_opt_i:
String.Append(&buf, "x86_64 ");
break;
case 1 << @shell_cmd_uname_opt_o:
String.Append(&buf, "Erythros ");
break;
default:
break;
}
}
Stdio.WriteLine(sh, &buf);
Stdio.WriteLine(sh, "\n");
Free(ds);
return 0;
}

View file

@ -0,0 +1,28 @@
I64 @shell_cmd_whoami(@shell* sh, I64 argc, U8** argv)
{
U8* options_list = "";
U64 options_err = NULL;
I64 flags = NULL;
I64 res = 0;
U8 buf[512];
switch (
@shell_parse_opts(sh, options_list, argc, argv, &flags, &options_err)) {
case SHELL_OPTS_ERR_INVALID_OPT:
StrPrint(&buf, "uname: unrecognized option -- '%s'\n", options_err);
Stdio.WriteLine(sh, &buf);
res = 1;
break;
case SHELL_OPTS_ERR_EXTRA_OPD:
StrPrint(&buf, "uname: extra operand '%s'\n", options_err);
Stdio.WriteLine(sh, &buf);
res = 1;
break;
default:
Stdio.WriteLine(sh, &sh->session->user.name);
Stdio.WriteLine(sh, "\n");
break;
}
if (options_err)
Free(options_err);
return res;
}

View file

@ -0,0 +1,24 @@
I64 @shell_cmd_wpset(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
if (argc < 2) {
return 0;
}
I64 size = 0;
U64 fbuf = FileSystem.ReadFile(argv[1], &size);
if (!fbuf) {
StrPrint(&buf, "Error reading file %s\n", argv[1]);
Stdio.WriteLine(sh, &buf);
return 1;
}
Context2D* new = Image.BufferToContext2D(fbuf, size);
Free(fbuf);
if (!new) {
StrPrint(&buf, "Error in Image.BufferToContext2D\n");
Stdio.WriteLine(sh, &buf);
return 1;
}
Compositor.SetWallpaper(new);
DelContext2D(new);
return 0;
}

10
System/Utilities/Dns.HC Normal file
View file

@ -0,0 +1,10 @@
U0 DnsQuery(U8* host)
{
U32 result = @dns_query(host);
if (result == U32_MAX) {
"Error looking up host %s\n", host;
return;
}
"Query for %s: %d.%d.%d.%d\n", host, result.u8[3], result.u8[2], result.u8[1],
result.u8[0];
}

414
System/Utilities/Image.HC Normal file
View file

@ -0,0 +1,414 @@
Silent(1); // This is needed to suppress "Function should return val" warnings for wrappers to non-HolyC functions
// class @image
// {
// CDC* (*FromBuffer)(U8* buffer, I64 len);
// CDC* (*Load)(U8* filename);
// CDC* (*Write)(U8* filename, CDC* dc);
// };
//
// @image Image;
class @image_frame
{
CDC* dc;
CSprite* sprite;
I64 delay;
};
class @image_collection
{
@image_frame** frames;
I64 count;
I64 current;
I64 jiffies;
I64 index;
@image_collection* next;
};
I64 @image_cbgr24_to_4_bit(CBGR24* ptr, Bool dither_probability)
{
I64 res, k;
if (dither_probability) {
k = RandU32;
if (SqrI64(ptr->r) + SqrI64(ptr->g) + SqrI64(ptr->b) >= 3 * SqrI64(k.u8[0]))
res = 8;
else
res = 0;
if (ptr->r >= k.u8[1])
res |= RED;
if (ptr->g >= k.u8[2])
res |= GREEN;
if (ptr->b >= k.u8[3])
res |= BLUE;
} else {
if (SqrI64(ptr->r) + SqrI64(ptr->g) + SqrI64(ptr->b) >= SqrI64(0x80)) {
res = 8;
if (ptr->r >= 0x80)
res |= RED;
if (ptr->g >= 0x80)
res |= GREEN;
if (ptr->b >= 0x80)
res |= BLUE;
} else {
res = 0;
if (ptr->r >= 0x40)
res |= RED;
if (ptr->g >= 0x40)
res |= GREEN;
if (ptr->b >= 0x40)
res |= BLUE;
}
}
return res;
}
#define IMAGE_DITHER_NONE 0
#define IMAGE_DITHER_NATIVE 1
#define IMAGE_DITHER_FLOYDSTEINBERG 2
U0 @image_render_4bit_floydstein(U8* buffer, I32 width, I32 height)
{
U64 reg RDI rdi = buffer;
U64 reg RSI rsi = width;
U64 reg RDX rdx = height;
no_warn rdi, rsi, rdx;
asm {
MOV RAX, RENDER_4BIT_FLOYDSTEIN
CALL RAX
}
}
CDC* @image_render_16color_native(U8* pixels, I32 x, I32 y, Bool dither)
{
I64 i;
I64 j;
I64 cnt = 0;
CBGR24 cbgr24;
CDC* dc = DCNew(x, y);
for (i = 0; i < y; i++)
for (j = 0; j < x; j++) {
cbgr24.r = pixels[cnt];
cbgr24.g = pixels[cnt + 1];
cbgr24.b = pixels[cnt + 2];
if (!pixels[cnt + 3])
dc->color = TRANSPARENT;
else
dc->color = @image_cbgr24_to_4_bit(&cbgr24, dither);
GrPlot(dc, j, y - i - 1);
cnt += 4;
}
return dc;
}
CBGR24 @image_palette_std[COLORS_NUM] = {
0x000000, 0x0000AA, 0x00AA00, 0x00AAAA,
0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA,
0x555555, 0x5555FF, 0x55FF55, 0x55FFFF,
0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF
};
CBGR24 @image_dif_rgb(CBGR24 from, CBGR24 to)
{
CBGR24 dif;
dif.r = to.r - from.r;
dif.g = to.g - from.g;
dif.b = to.b - from.b;
return dif;
}
F64 @image_dist_rgb(CBGR24 from, CBGR24 to)
{
CBGR24 dif = @image_dif_rgb(from, to);
F64 dist = dif.r * dif.r + dif.g * dif.g + dif.b * dif.b;
return dist;
}
I64 @image_get_4bit_color(CBGR24* cbgr24)
{
F64 dist = -1, tempDist;
I64 i;
I64 color = TRANSPARENT;
for (i = 0; i < COLORS_NUM; i++) {
tempDist = @image_dist_rgb(*cbgr24, @image_palette_std[i]);
if (tempDist < dist || dist < 0) {
dist = tempDist;
color = i;
}
}
return color;
}
CDC* @image_render_16color_floydsteinberg(U8* pixels, I32 width, I32 height)
{
@image_render_4bit_floydstein(pixels, width, height);
I64 i;
I64 j;
I64 cnt = 0;
CBGR24 cbgr24;
CDC* dc = DCNew(width, height);
for (i = 0; i < height; i++)
for (j = 0; j < width; j++) {
cbgr24.r = pixels[cnt];
cbgr24.g = pixels[cnt + 1];
cbgr24.b = pixels[cnt + 2];
if (!pixels[cnt + 3])
dc->color = TRANSPARENT;
else
dc->color = @image_get_4bit_color(&cbgr24);
GrPlot(dc, j, height - i - 1);
cnt += 4;
}
return dc;
}
CDC* @image_generate_dc_from_pixels(U8* pixels, I32 width, I32 height, Bool dither = IMAGE_DITHER_FLOYDSTEINBERG)
{
switch (dither) {
case IMAGE_DITHER_NONE:
case IMAGE_DITHER_NATIVE:
return @image_render_16color_native(pixels, width, height, dither);
break;
case IMAGE_DITHER_FLOYDSTEINBERG:
return @image_render_16color_floydsteinberg(pixels, width, height);
break;
default:
break;
}
return NULL;
}
U8* @image_load_gif_from_memory(U8* buffer, I64 len, I64** delays, I64* x, I64* y,
I64* z)
{
U64 reg RDI rdi = buffer;
U64 reg RSI rsi = len;
U64 reg RDX rdx = delays;
U64 reg RCX rcx = x;
U64 reg R8 r8 = y;
U64 reg R9 r9 = z;
no_warn rdi, rsi, rdx, rcx, r8, r9;
asm {
MOV RAX, IMAGE_LOAD_GIF_FROM_MEMORY
CALL RAX
}
}
U8* @stbi_failure_reason()
{
asm {
MOV RAX, STBI_FAILURE_REASON
CALL RAX
}
}
I32 @stbi_info_from_memory(U8* buffer, I64 len, I64* x, I64* y, I64* comp)
{
U64 reg RDI rdi = buffer;
U64 reg RSI rsi = len;
U64 reg RDX rdx = x;
U64 reg RCX rcx = y;
U64 reg R8 r8 = comp;
no_warn rdi, rsi, rdx, rcx, r8;
asm {
MOV RAX, STBI_INFO_FROM_MEMORY
CALL RAX
}
}
U8* @stbi_load_from_memory(U8* buffer, I64 len, I64* x, I64* y,
I64* channels_in_file, I64 desired_channels)
{
U64 reg RDI rdi = buffer;
U64 reg RSI rsi = len;
U64 reg RDX rdx = x;
U64 reg RCX rcx = y;
U64 reg R8 r8 = channels_in_file;
U64 reg R9 r9 = desired_channels;
no_warn rdi, rsi, rdx, rcx, r8, r9;
asm {
MOV RAX, STBI_LOAD_FROM_MEMORY
CALL RAX
}
}
U32* @stbi_write_png_to_mem(U32* pixels, I32 stride_bytes, I32 x, I32 y, I32 n, I32* out_len)
{
U64 reg RDI rdi = pixels;
U64 reg RSI rsi = stride_bytes;
U64 reg RDX rdx = x;
U64 reg RCX rcx = y;
U64 reg R8 r8 = n;
U64 reg R9 r9 = out_len;
no_warn rdi, rsi, rdx, rcx, r8, r9;
asm {
MOV RAX, STBI_WRITE_PNG_TO_MEM
CALL RAX
}
}
CDC* @image_load(U8* filename)
{
if (!filename || !FileFind(filename)) {
// PrintErr("Image file not found.\n");
return NULL;
}
I64 len;
I32 x;
I32 y;
I32 comp;
U8* buffer = FileRead(filename, &len);
I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
if (code != 1) {
Free(buffer);
return NULL;
}
U8* pixels = @stbi_load_from_memory(buffer, len, &x, &y, &comp, 4);
Free(buffer);
CDC* dc = @image_generate_dc_from_pixels(pixels, x, y);
Free(pixels);
return dc;
}
U32 @image_rgba_color_table[16] = {
0xff000000, 0xffaa0000, 0xff00aa00, 0xffaaaa00,
0xff0000aa, 0xffaa00aa, 0xff0055aa, 0xffaaaaaa,
0xff555555, 0xffff5555, 0xff55ff55, 0xffffff55,
0xff5555ff, 0xffff55ff, 0xff55ffff, 0xffffffff
};
U32 @image_get_rgba_color(I64 color)
{
if (color > 15)
return 0;
return @image_rgba_color_table[color];
}
U32* @image_get_rgba_buffer_from_dc_body(CDC* dc)
{
if (!dc)
return NULL;
U32* pixels = CAlloc((dc->width * dc->height) * 4, erythros_mem_task);
I64 x;
I64 y;
I64 p = 0;
for (y = 0; y < dc->height; y++)
for (x = 0; x < dc->width; x++)
pixels[p++] = @image_get_rgba_color(GrPeek(dc, x, y));
return pixels;
}
U0 @image_write(U8* filename, CDC* dc)
{
if (!dc) {
PrintErr("Device context is NULL.\n");
return;
}
I32 out_len;
U32* rgba_buffer = @image_get_rgba_buffer_from_dc_body(dc);
if (!rgba_buffer) {
PrintErr("RGBA buffer is NULL.\n");
return;
}
U8* png_buffer = @stbi_write_png_to_mem(rgba_buffer, dc->width * 4, dc->width, dc->height, 4, &out_len);
if (!png_buffer) {
PrintErr("PNG buffer is NULL.\n");
Free(rgba_buffer);
return;
}
FileWrite(filename, png_buffer, out_len);
Free(rgba_buffer);
Free(png_buffer);
}
U32 @image_pixel_flip_rgb_bgr(U32 src)
{
U32 dst;
dst.u8[0] = src.u8[2];
dst.u8[1] = src.u8[1];
dst.u8[2] = src.u8[0];
dst.u8[3] = src.u8[3];
return dst;
}
CDC* @image_from_buffer(U8* buffer, I64 len)
{
I32 x = 0;
I32 y = 0;
U8* pixels = NULL;
CDC* dc = NULL;
I32 comp;
I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
if (code != 1) {
return NULL;
}
pixels = @stbi_load_from_memory(buffer, len, &x, &y, &comp, 4);
if (!pixels)
PopUpOk(@stbi_failure_reason);
dc = @image_generate_dc_from_pixels(pixels, x, y);
Free(pixels);
return dc;
}
@image_collection* @image_collection_from_buffer(U8* buffer, I64 len)
{
I64 i;
I32* delays;
I32 x;
I32 y;
I32 z;
I32 comp;
I32 code = @stbi_info_from_memory(buffer, len, &x, &y, &comp);
if (code != 1) {
return NULL;
}
U64 pixels = @image_load_gif_from_memory(buffer, len, &delays, &x, &y, &z);
if (!pixels)
PopUpOk(@stbi_failure_reason);
if (!z)
return NULL; // no frames?
@image_collection* collection = CAlloc(sizeof(@image_collection), erythros_mem_task);
@image_frame* frame;
collection->frames = CAlloc(sizeof(@image_frame*) * z, erythros_mem_task);
collection->count = z;
for (i = 0; i < z; i++) {
frame = CAlloc(sizeof(@image_frame), erythros_mem_task);
frame->dc = @image_generate_dc_from_pixels(pixels, x, y);
frame->sprite = DC2Sprite(frame->dc);
frame->delay = delays[i];
collection->frames[i] = frame;
pixels += (x * y) * 4;
}
return collection;
}
// Image.FromBuffer = &@image_from_buffer;
// Image.Load = &@image_load;
// Image.Write = &@image_write;
Silent(0);
U0 Screenshot(U8* custom_filename = NULL, Bool output_filename_to_focus_task = FALSE)
{
CDC* dc = DCScrnCapture;
U8 filename[256];
CDateStruct ds;
if (custom_filename)
StrCpy(filename, custom_filename);
else {
Date2Struct(&ds, Now);
StrPrint(filename, "C:/Tmp/ScrnShots/%04d-%02d-%02d-%02d-%02d-%02d.png", ds.year, ds.mon, ds.day_of_mon, ds.hour, ds.min, ds.sec);
}
@image_write(filename, dc);
DCDel(dc);
if (output_filename_to_focus_task)
XTalk(sys_focus_task, filename);
};
U0 @screenshot_hotkey(I64)
{
Screenshot("C:/Home/Screenshot.png", TRUE);
}
CtrlAltCBSet('S', &@screenshot_hotkey, "", , FALSE);

View file

@ -0,0 +1,22 @@
U0 NetRep()
{
NetInfoRequest* req = @net_info_request;
"MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n", req->mac_address.u8[5], req->mac_address.u8[4],
req->mac_address.u8[3], req->mac_address.u8[2],
req->mac_address.u8[1], req->mac_address.u8[0];
"IPv4 address : %d.%d.%d.%d\n", req->ipv4_address.u8[3], req->ipv4_address.u8[2],
req->ipv4_address.u8[1], req->ipv4_address.u8[0];
"IPv4 netmask : %d.%d.%d.%d\n", req->ipv4_netmask.u8[3], req->ipv4_netmask.u8[2],
req->ipv4_netmask.u8[1], req->ipv4_netmask.u8[0];
"IPv4 network : %d.%d.%d.%d\n", req->ipv4_network.u8[3], req->ipv4_network.u8[2],
req->ipv4_network.u8[1], req->ipv4_network.u8[0];
"IPv4 gateway : %d.%d.%d.%d\n", req->ipv4_gateway.u8[3], req->ipv4_gateway.u8[2],
req->ipv4_gateway.u8[1], req->ipv4_gateway.u8[0];
"DNS server (port) : %d.%d.%d.%d (%d)\n", req->dns_server_address.u8[3], req->dns_server_address.u8[2],
req->dns_server_address.u8[1], req->dns_server_address.u8[0], req->dns_server_port;
"RX bytes : %d\n", req->rx_bytes;
"RX frames : %d\n", req->rx_frames;
"TX bytes : %d\n", req->tx_bytes;
"TX frames : %d\n", req->tx_frames;
Free(req);
}

72
System/Utilities/Ping.HC Normal file
View file

@ -0,0 +1,72 @@
#define PING_ERR_INVALID_HOST 1
#define PING_ERR_HOST_NOT_FOUND 2
#define PING_PAYLOAD_SIZE 56
I64 @ping_err(I64 code)
{
switch (code) {
case PING_ERR_INVALID_HOST:
"Invalid host specified\n";
return 1;
break;
case PING_ERR_HOST_NOT_FOUND:
"Host not found\n";
return 2;
break;
default:
"Unspecified error\n";
return -1;
}
}
I64 Ping(U8* host, I64 count = 4)
{
if (!host)
return @ping_err(PING_ERR_INVALID_HOST);
if (!StrLen(host))
return @ping_err(PING_ERR_INVALID_HOST);
U32 addr = @dns_query(host);
if (addr == U32_MAX)
return @ping_err(PING_ERR_HOST_NOT_FOUND);
U16 iden = (RandU16 * SysTimerRead) & 0xFFFF;
I64 start_jiffies;
U32 reply = NULL;
I64 res = 0;
U16 seq = 0;
I64 loss = 0;
IcmpRequest* request = CAlloc(sizeof(IcmpRequest), Fs->code_heap);
"PING %s (%d.%d.%d.%d): %d data bytes\n",
host, addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], PING_PAYLOAD_SIZE;
I64 i;
for (i = 0; i < count; i++) {
start_jiffies = cnts.jiffies;
reply = @icmp_echo_request(addr, iden, seq, request, i);
if (!reply) {
"Request timeout for icmp_seq %d\n", seq;
++loss;
res = 1;
} else {
"%d bytes from %d.%d.%d.%d: icmp_seq=%d ttl=%d time=%d ms\n",
reply.u16[1], addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0], seq, reply.u16[0], cnts.jiffies - start_jiffies;
}
while (cnts.jiffies < start_jiffies + 1000 && i < (count - 1))
Sleep(1);
++seq;
}
Free(request);
"--- %d.%d.%d.%d ping statistics ---\n", addr.u8[3], addr.u8[2], addr.u8[1], addr.u8[0];
"%d packets transmitted, %d packets received, %0f",
seq, seq - loss, (loss * 1.0 / seq * 1.0) * 100;
PutChars(37);
" packet loss\n";
return res;
}

92
System/Utilities/Time.HC Normal file
View file

@ -0,0 +1,92 @@
U0 @time_cmos_update_byte(I64 time_reg, I64 val)
{
OutU8(0x70, time_reg);
OutU8(0x71, val);
}
I64 @time_dec_to_bcd(I64 val)
{
return (((val / 10) << 4) | (val % 10));
}
U0 @time_update(U8* date_str, I64 mS_delta, I64 hour_offset)
{
no_warn mS_delta;
Bool is_bcd;
OutU8(0x70, 0x0B);
if (InU8(0x71) & 4)
is_bcd = FALSE;
else
is_bcd = TRUE;
I64 date_argc;
U8** date_argv = String.Split(date_str, ' ', &date_argc);
I64 month = DefineMatch(date_argv[2], "ST_MONTHS") + 1;
I64 day = Str2I64(date_argv[1]);
I64 year = Str2I64(date_argv[3] + 2);
I64 century = 20;
date_argv[4][2] = NULL;
date_argv[4][5] = NULL;
I64 hour = Str2I64(date_argv[4]);
I64 minute = Str2I64(date_argv[4] + 3);
I64 second = Str2I64(date_argv[4] + 6);
// FIXME: Handle month boundaries, and 12 hour time
hour += hour_offset;
if (hour < 0) {
hour += 24;
--day;
} else if (hour > 23) {
hour -= 24;
++day;
}
if (is_bcd) {
century = @time_dec_to_bcd(century);
year = @time_dec_to_bcd(year);
month = @time_dec_to_bcd(month);
day = @time_dec_to_bcd(day);
hour = @time_dec_to_bcd(hour);
minute = @time_dec_to_bcd(minute);
second = @time_dec_to_bcd(second);
}
@time_cmos_update_byte(0x32, century);
@time_cmos_update_byte(0x09, year);
@time_cmos_update_byte(0x08, month);
@time_cmos_update_byte(0x07, day);
@time_cmos_update_byte(0x04, hour);
@time_cmos_update_byte(0x02, minute);
@time_cmos_update_byte(0x00, second);
}
I64 @time_tz_offset()
{
return -4;
}
U0 @time_query(Bool set = FALSE)
{
U8 buf[1024];
@http_url* url = @http_parse_url("http://time.google.com");
@http_response* resp = Http.Head(url, &buf);
while (resp->state != HTTP_STATE_DONE)
Sleep(1);
I64 mS_delta = cnts.jiffies;
"Set current date and time to %s ", resp->headers->@("Date");
if (!set)
set = YorN;
else
"\n";
if (set)
@time_update(resp->headers->@("Date"), mS_delta, @time_tz_offset);
}
U0 TimeSync()
{
Sleep(500);
@time_query(1);
}