Scrolling works!!1!

This commit is contained in:
jaseg 2021-07-21 17:28:47 +02:00
parent a4d24551d5
commit c2d5c19dcc
8 changed files with 137 additions and 80 deletions

View file

@ -42,7 +42,7 @@
<Option Name="IPUserFilesDir" Val="$PIPUSERFILESDIR"/>
<Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/>
<Option Name="EnableBDX" Val="FALSE"/>
<Option Name="WTXSimLaunchSim" Val="611"/>
<Option Name="WTXSimLaunchSim" Val="616"/>
<Option Name="WTModelSimLaunchSim" Val="0"/>
<Option Name="WTQuestaLaunchSim" Val="0"/>
<Option Name="WTIesLaunchSim" Val="0"/>

View file

@ -8,6 +8,8 @@ input in_blank, in_vsync, in_hsync,
input [19:0] glyphmem_data,
output [15:0] glyphmem_r_addr,
input [11:0] scroll_y,
output out_hsync, out_vsync,
output [7:0] out_red,
output [7:0] out_green,
@ -158,11 +160,11 @@ always @(posedge clk) begin
end
if (in_vsync_last && !in_vsync) begin
glyph_y <= 0;
px_y <= 0;
glyph_y <= scroll_y / FONT_GLYPH_H;
px_y <= scroll_y % FONT_GLYPH_H;
end
end
end
endmodule
endmodule

View file

@ -180,6 +180,7 @@ wire [19:0] glyph_buffer_w_data;
wire win_blank;
wire [11:0] win_w;
wire [11:0] win_h;
wire [11:0] win_scroll_y;
wire out_data_en;
wire out_data_valid;
wire [7:0] matcher_debug;
@ -306,6 +307,8 @@ term_renderer #(
.in_blank(win_blank),
.in_vsync(in_vsync),
.in_hsync(in_hsync),
.scroll_y(win_scroll_y),
.out_hsync(),
.out_vsync(),
@ -347,6 +350,7 @@ window_matcher window_matcher_i (
.win_locked(win_locked),
.win_w(win_w),
.win_h(win_h),
.win_scroll_y(win_scroll_y),
.win_x_dbg(win_x_dbg),
.win_y_dbg(win_y_dbg),
@ -368,4 +372,4 @@ window_matcher window_matcher_i (
.out_blue(out_blue)
);
endmodule
endmodule

View file

@ -25,6 +25,7 @@ module window_matcher(
output reg [11:0] win_h,
output reg [11:0] win_right,
output reg [11:0] win_bottom,
output reg [11:0] win_scroll_y,
output reg win_locked,
input [7:0] win_red, [7:0] win_green, [7:0] win_blue,
@ -208,6 +209,8 @@ module window_matcher(
reg [11:0] win_y_int;
reg [11:0] win_w_int;
reg [11:0] win_h_int;
reg [11:0] win_scroll_y_int;
reg [11:0] win_sof_marker_int;
assign win_x_dbg = win_x_int;
assign win_y_dbg = win_y_int;
@ -221,17 +224,11 @@ module window_matcher(
ST_MAT_RX3 = 5'b01000,
ST_MAT_DATA = 5'b10000;
reg [4:0] matcher_state;
wire matched = matcher_state[4];
reg matched;
reg [11:0] dval_x_reg;
reg [11:0] dval_y_reg;
always @(posedge clk) begin
if (rst == 1) begin
out_data_valid <= 0;
out_data_en <= 0;
end
if (rst || in_vsync) begin
matcher_state <= ST_MAT_WAITING;
win_x_int <= 0;
@ -239,68 +236,82 @@ module window_matcher(
win_w_int <= 0;
win_h_int <= 0;
dval_x_reg <= 0;
dval_y_reg <= 0;
win_scroll_y_int <= 0;
win_sof_marker_int <= 0;
matched <= 0;
out_data_en <= 0;
out_data_valid <= 0;
end
if (!rst) begin
if (in_blank) begin
/* Reset state if the header is only partially contained in this frame */
if (matcher_state != ST_MAT_DATA) begin
matcher_state <= ST_MAT_WAITING;
end
if (!in_blank_reg) begin
if (matcher_state != ST_MAT_DATA) begin
out_data_en <= 0;
end
matcher_state <= ST_MAT_WAITING;
end
end else begin
case (matcher_state)
ST_MAT_WAITING: begin
if (in_pxd_pattern_match) begin
matcher_state <= ST_MAT_RX0;
win_x_int <= scan_x_reg[7];
win_y_int <= scan_y;
if (!matched) begin
win_x_int <= scan_x_reg[7];
win_y_int <= scan_y;
end
end
end
ST_MAT_RX0: begin
matcher_state <= ST_MAT_RX1;
win_sof_marker_int <= in_pxd;
end
ST_MAT_RX1: begin
matcher_state <= ST_MAT_RX2;
if (!matched) begin
win_scroll_y_int <= in_pxd;
end
end
ST_MAT_RX2: begin
matcher_state <= ST_MAT_RX3;
win_w_int <= in_pxd;
if (!matched) begin
win_w_int <= in_pxd;
end
end
ST_MAT_RX3: begin
matcher_state <= ST_MAT_DATA;
win_h_int <= in_pxd;
out_data_valid <= 1;
out_data_en <= 1;
dval_x_reg <= 13;
dval_y_reg <= 0;
if (!matched) begin
matched <= 1;
win_h_int <= in_pxd;
end
if (win_sof_marker_int[7]) begin
out_data_valid <= 1;
out_data_en <= 1;
end else if (out_data_en) begin
out_data_valid <= 1;
end
dval_x_reg <= 13;
end
ST_MAT_DATA: begin
if (out_data_en) begin
dval_x_reg <= dval_x_reg + 1;
end
if (dval_x_reg == win_w_int || in_blank) begin
out_data_valid <= 0;
end
end
endcase
end
if (matcher_state == ST_MAT_DATA) begin
/* blank */
if (scan_x == win_x_int && out_data_en) begin
out_data_valid <= 1;
dval_x_reg <= 1;
end else if (out_data_en) begin
dval_x_reg <= dval_x_reg + 1;
end
if (dval_x_reg == win_w_int || in_blank) begin
out_data_valid <= 0;
end
if (!in_blank_reg && in_blank) begin
dval_y_reg <= dval_y_reg + 1;
end
if (dval_y_reg == win_h_int) begin
out_data_en <= 0;
end
end
end
end
@ -351,6 +362,7 @@ module window_matcher(
win_y <= 0;
win_right <= 0;
win_bottom <= 0;
win_scroll_y <= 0;
end else begin
if (in_vsync_reg == 0 && in_vsync == 1) begin
@ -363,6 +375,7 @@ module window_matcher(
win_y <= win_y_int;
win_right <= win_x_int + win_w_int + 1;
win_bottom <= win_y_int + win_h_int + 1;
win_scroll_y <= win_scroll_y_int;
end else begin
win_w <= 0;
@ -371,6 +384,7 @@ module window_matcher(
win_y <= 0;
win_right <= 0;
win_bottom <= 0;
win_scroll_y <= 0;
end
end
end

View file

@ -32,7 +32,7 @@
},
{
"cell_type": "code",
"execution_count": 55,
"execution_count": 61,
"id": "preliminary-virus",
"metadata": {},
"outputs": [],
@ -104,12 +104,13 @@
" def encode_val(val):\n",
" return np.array([(val//65536)&0xff, (val%65536//256)&0xff, (val%256)&0xff]).astype(int)\n",
" \n",
" win_data[0, :8, :3] = WINDOW_MAGIC\n",
" win_data[0, 8, :3] = encode_val(win_x)\n",
" win_data[0, 9, :3] = encode_val(win_y)\n",
" win_data[0, 10, :3] = encode_val(win_w)\n",
" win_data[0, 11, :3] = encode_val(win_h)\n",
" win_data[0, :12, 3] |= AlphaFlags.WIN_HEADER.value;\n",
" for y in range(win_h):\n",
" win_data[y, :8, :3] = WINDOW_MAGIC\n",
" win_data[y, 8, :3] = encode_val(0x85 if y%3 == 0 else 0)\n",
" win_data[y, 9, :3] = encode_val(y)\n",
" win_data[y, 10, :3] = encode_val(win_w)\n",
" win_data[y, 11, :3] = encode_val(win_h)\n",
" win_data[y, :12, 3] |= AlphaFlags.WIN_HEADER.value;\n",
" \n",
" win_h, win_w, _ = win_data.shape\n",
" h, w, _ = fb.shape\n",
@ -721,7 +722,7 @@
},
{
"cell_type": "code",
"execution_count": 56,
"execution_count": 62,
"id": "7da9e05f-aa7e-42e6-b1ee-2c6453468bf3",
"metadata": {},
"outputs": [
@ -731,13 +732,13 @@
"(-0.5, 229.5, 109.5, -0.5)"
]
},
"execution_count": 56,
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"image/png": "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\n",
"text/plain": [
"<Figure size 864x864 with 1 Axes>"
]
@ -757,7 +758,7 @@
},
{
"cell_type": "code",
"execution_count": 57,
"execution_count": 63,
"id": "4d3a1a2d-4b31-47cf-8502-b45ac31a74e0",
"metadata": {},
"outputs": [],
@ -771,7 +772,7 @@
},
{
"cell_type": "code",
"execution_count": 58,
"execution_count": 64,
"id": "f542f9bc-671b-4b4e-a8f6-ece54783d619",
"metadata": {},
"outputs": [

View file

@ -323,7 +323,7 @@ int main(void)
con_printf("Received %d bytes: %s\r\n", rv, rd_buf);
*/
con_printf_blocking(" #%u", i);
con_printf_blocking(" #%u heap memory usage: %zu used / %zu free / %zu fresh blocks", i, ta_num_used(), ta_num_free(), ta_num_fresh());
/*
if (k == 0)
spif_printf(&spif, "\33[1m");
@ -379,6 +379,13 @@ int main(void)
spif_printf(&spif, "\033[1;91mDecryption error!\033[0m\nInvalid payload size %zx\n", payload_len);
continue;
}
con_printf_blocking("Payload size %zu\r\n", payload_len);
for (size_t i=0; i<payload_len; i++) {
con_printf_blocking("%02x ", payload_buf[i+4]);
if (i%16 == 15)
con_printf_blocking("\r\n");
}
con_printf_blocking("\r\n");
unsigned char file_key[16];
err = parse_age_buf(&ks, payload_buf + sizeof(uint32_t), payload_len+1, file_key);

View file

@ -39,10 +39,15 @@ FONT_FG_COLOR = '#e0e010'
def pack_html(payload_img_cid, hint_img_data):
return textwrap.dedent(f'''\
<html style="margin: 0; padding: 0; border: 0; width: 100%; height: 100%">
<body style="border-style: solid; border-width: 2px; border-color: {BORDER_COLOR}; margin: 0; padding: 0; \
background-color: {BACKGROUND_COLOR}; background-image: url({data_url(hint_img_data)}); \
width: calc(100% - 4px); height: calc(100% - 4px); overflow: hidden">
<img src="cid:{payload_img_cid}" style="margin: 0; padding: 0"/>
<body style="margin: 0; padding: 0; border: 0; width: 100%; \
background-color: {BACKGROUND_COLOR}; background-image: url({data_url(hint_img_data)})">
<div style="margin: 0; padding: 2px; width: calc(100% - 4px)">
<img src="cid:{payload_img_cid}" style="margin: 0; padding: 0"/>
</div>
<div style="border-left: 2px; border-top: 2px; border-bottom: 2px; border-right: 20px; \
border-style: solid; border-color: {BORDER_COLOR}; margin: 0; padding: 0; \
width: calc(100% - 2px - 20px); height: calc(100% - 4px); position: fixed; top: 0; left: 0">
</div>
</body>
</html>
''').strip()
@ -84,8 +89,11 @@ def img_to_bytes(img, fmt='png'):
@click.option('--smtp-port', type=int, default=465)
@click.option('-k', '--key', 'keys', multiple=True, required=True)
@click.option('-s', '--subject', default='tachibana/age encrypted message')
@click.option('--payload-width', type=int, default=300)
@click.option('--payload-height', type=int)
@click.argument('content', type=click.File('r'))
def create_age_email(content, sender, recipients, cc, bcc, keys, subject, smtp_server, smtp_port, smtp_user, smtp_password):
def create_age_email(content, sender, recipients, cc, bcc, keys, subject, smtp_server, smtp_port, smtp_user,
smtp_password, payload_width, payload_height):
encrypted = age_encrypt(content.read(), keys)
@ -106,9 +114,11 @@ def create_age_email(content, sender, recipients, cc, bcc, keys, subject, smtp_s
# remove <...> angle brackets from cids in <img> tags
msg.set_content(pack_html(payload_img_cid[1:-1], hint_img_data), subtype='html')
w = max(200, math.ceil(math.sqrt(len(encrypted))))
h = math.ceil((len(encrypted) + 64) / w)
img_bytes = img_to_bytes(tb_data_encoder.data_encode(encrypted, w, h))
if payload_height is None:
payload_height = len(encrypted) // 16 * 16
img_bytes = img_to_bytes(tb_data_encoder.data_encode(encrypted, payload_width, payload_height))
#with open('/tmp/test.png', 'wb') as f:
# f.write(img_bytes)
msg.add_related(img_bytes, 'image', 'png', cid=payload_img_cid)
if not smtp_server:
@ -122,7 +132,7 @@ def create_age_email(content, sender, recipients, cc, bcc, keys, subject, smtp_s
smtp.login(smtp_user, smtp_password)
smtp.send_message(msg)
smtp.quit()
print('Message sent!')
print('Message sent!', file=sys.stderr)
if __name__ == '__main__':
#print(pack_html('foobar'))

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import sys
import math
import numpy as np
import click
@ -22,26 +23,44 @@ def tb_int_to_px(val):
return np.array([(val//0x10000)&0xff, (val//0x100)&0xff, val&0xff], dtype=np.uint8)
def data_encode(data, width, height):
HEADER_W = 12
assert width > HEADER_W
data = struct.pack('>I', len(data)) + data
data = np.frombuffer(data, dtype=np.uint8)
for i in range(len(data) - 4):
print(f'{data[i+4]:02x} ', end='')
if i%16 == 15 and i > 0:
print()
data_expanded = np.repeat(data, 2).reshape((-1, 2))
data_expanded[:, 0] &= 0xf0
data_expanded[:, 1] = (data_expanded[:, 1] & 0x0f) << 4
data_expanded = data_expanded.flatten()
win = np.zeros([height*width*3], dtype=np.uint8)
win[12*3:12*3+len(data_expanded)] = data_expanded
win = win.reshape((height, width, 3))
payload_w = width-HEADER_W
line_capacity = payload_w*3
print(payload_w, line_capacity, line_capacity / 2)
block_height = math.ceil(len(data_expanded)/line_capacity)
data_expanded = np.hstack([data_expanded, np.zeros(line_capacity*block_height - len(data_expanded), dtype=np.uint8)])
block = np.zeros((block_height, payload_w, 3), dtype=np.uint8)
block[:,:,:] = data_expanded.reshape((block_height, payload_w, 3))
win[0, :8] = WINDOW_MAGIC
win[0, 8] = tb_int_to_px(0)
win[0, 9] = tb_int_to_px(0)
win[0, 10] = tb_int_to_px(width)
win[0, 11] = tb_int_to_px(height)
payload_area = np.vstack(
[block] * (height // block_height) +
[np.zeros((height % block_height, payload_w, 3), dtype=np.uint8)])
header = np.zeros((height, HEADER_W, 3), dtype=np.uint8)
for y in range(height):
header[y, :8] = WINDOW_MAGIC
if y % block_height == 0 and y + block_height <= height:
header[y, 8] = tb_int_to_px(0x88)
else:
header[y, 8] = tb_int_to_px(0)
header[y, 9] = tb_int_to_px(y)
header[y, 10] = tb_int_to_px(width)
header[y, 11] = tb_int_to_px(height)
#print('out:', ' '.join(f'{x:02x}' for x in win.flatten()[:256]))
return Image.fromarray(win)
return Image.fromarray(np.hstack([header, payload_area]))
@click.command()
@click.option('-w', '--width', type=int, default=400)