▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓                                                                                  ▓▓░░
▓▓        Floating-point Instruction Bytecode Generator & Recompiler, v1.0.0        ▓▓░░
▓▓                               (for fasm 1.73.11+)                                ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓                          http://xk7.ru/r/s/inc/fpb.zip                           ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▓▓░░
▓▓                                                                                  ▓▓░░
▓▓  Copyright © 2021 Евгений Красников (Eugene Krasnikov aka Jin X)                 ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓  Permission is hereby granted, free of charge, to any person obtaining a copy    ▓▓░░
▓▓  of this software and associated documentation files (the "Software"), to deal   ▓▓░░
▓▓  in the Software without restriction, including without limitation the rights    ▓▓░░
▓▓  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       ▓▓░░
▓▓  copies of the Software, and to permit persons to whom the Software is           ▓▓░░
▓▓  furnished to do so, subject to the following conditions:                        ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓  The above copyright notice and this permission notice shall be included in all  ▓▓░░
▓▓  copies or substantial portions of the Software.                                 ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      ▓▓░░
▓▓  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        ▓▓░░
▓▓  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     ▓▓░░
▓▓  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          ▓▓░░
▓▓  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   ▓▓░░
▓▓  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   ▓▓░░
▓▓  SOFTWARE.                                                                       ▓▓░░
▓▓                                                                                  ▓▓░░
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░
  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░


WHAT IS THIS?
=============

Floating-point Instruction Bytecode Generator & Recompiler, v1.0.0.

In fact, this is a packer of x86 FPU instructions with ≈ 3-4x efficiency (not taking into account recompiler code and constants).
All you need is just define some symbolic constants, include this file in your fasm project, use a few macros and perform a call to recompiled function.
Macros allow you:
- to check module version compatibility;
- to declare set of bytecode instructions (used FPU instructions and their aliases);
- to insert code of bytecode recompiler;
- to generate bytecode (replacing native x86 FPU code);
- to define constants/variables and their aliases (names);

  >>> FEATURES <<<

• Definition of FPU operations in easy to read format of Reverse Polish Notation (RPN).
• Flexibly customizable recompiler which creates native x86 functions from bytecode (that is, fast for execution).
• Native code copier (so you can mix FPU instructions with native x86 code inside bytecode block).
• Declaration of up to 11-13 bytecode instructions associated with 2-byte FPU (...or not only FPU?...) instructions (this number is sufficient for most purposes).
• Definition of up to 17 2-byte integer and floating-point constants.
• Definition of up to 16 4-byte floating-point variables.
• Declaration of unlimited subfunctions with custom names inside bytecode block.
• Possibility to override some predefined bytecode instructions associated with native x86 instructions.
• The size of recompiler is ≈ 50..74 bytes (for 1..13 types of bytecode instructions, with no support of native code copier, variables and subfunctions)
  to ≈ 65..85 / 76..96 bytes (for 1..11 types of bytecode instructions, with max functionality) including writting of input stream offset to register.
  This size can vary depending on used options of macro 'fpb_recompile!'.

  >>> TERMS <<<

I will use the following terms in this file:
• bytecode (bytecode block) - sequence of bytecode instructions (block of code, function);
• bytecode instruction - one element of bytecode block associated with native instruction;
• native instruction (code) - instruction (set of instructions) encoded in ready-to-execute format of x86 CPU;
• opcode - numeric representation of instruction;
• subfunction - part of bytecode ending with return ('ret') bytecode instruction;
• recompiler - native code that translates bytecode into native code (ready-to-execute function);
• native code copier - block of recompiler that copies native x86 code from bytecode block into destination function;
• nibble - half of a byte;
• alias - symbolic name (consisting of letter, digits and other characters);
• symbolic constant - fasm constant (defined with 'define', 'equ' directives or '=' sign);
• constants and variables - constants and variables defined with macros 'fpb_flt', 'fpb_int' and 'fpb_var';
• constant / variable array - set of constants / variables, also offset of the 1st constant / variable;
• int and float (value, variable, etc) - integer and floating-point (value, variable, etc);
• offset - address offset within a single segment;
• pointer - pair of segment and offset.

  >>> WHAT DOES THE CODE LOOK LIKE? <<<

* Let's first consider the simplest variant...

define fpb_copy_support 0			; disable native code copier support
define fpb_var_support 0			; disable variable support

include 'fpb.inc'				; include fpb generator & recompiler
fpb_check_version 1,0,0				; check fpb module version compatibility

; Declare fpb bytecode instructions
declare_fpb	*, fmulp
declare_fpb	sincos, fsincos
; . . .
		mov	di,$200			; destination offset for recompilation
		fpb_recompile! fpb_nibbles, bh=0 ; insert recompiler code here (doesn't require 'call')
; . . .
		mov	bx,consts		; constant array (doesn't require offset correction because fpb_data_offset is not defined)
		call	$200			; call recompiled function
; . . .
	fpb_nibbles:
	  fpb_start	consts			; start of bytecode generation
		fpb	rad * sincos		; convert degrees to radians and calc sin & cos
	  fpb_end				; end of bytecode generation

	consts = $-2				; don't define this symbolic constant as label here, otherwise an error can be generated (-2 is required because the 1st constant is float, not integer, see below)
		fpb_flt	rad, 0.0174533		; define 2-byte float constant with name 'rad' and value pi/180

* Not so hard, really? :))
* And now let's consider more complex case...

define fpb_copy_support 1			; enable native code copier support
define fpb_var_support 1			; enable variable support
define fpb_before_ret salc			; one-byte native instruction before 'ret' in recompiled function
fpb_data_offset =	vars-consts		; offset of constants and variables

include 'fpb.inc'				; include fpb generator & recompiler
fpb_check_version 1,0,0				; check fpb module version compatibility

; Declare fpb bytecode instructions
declare_fpb	+, faddp
declare_fpb	^2, fmul st0,st0
declare_fpb	0, fldz
declare_fpb	sin, fsin
; . . .
		mov	di,bp			; destination offset for recompilation
		fpb_recompile! fpb_nibbles, bh=0, ax=0 ; insert recompiler code here (doesn't require 'call')
; . . .
		mov	bx,consts-fpb_data_offset ; constant and variable arrays (with offset correction)
		call	bp			; call recompiled function
; . . .
	fpb_nibbles:
	  fpb_start	consts			; start of bytecode generation
		fpb	x pi + sin ^2 0		; list of bytecode aliases (sequence of RPN-operations in fact)
	    fpb_copy_start			; start of native code block
		fcomp
		fnstsw	ax
		sahf
		jnb	@F
		lea	ax,[bp+fpb_offset_subfn_lt0] ; offset of subfunction 'lt0'
		jmp	ax
	@@:
	    fpb_copy_end			; end of native code block
		fpb	=res			; store value to variable 'res'
	    fpb_subfn	lt0			; declare new subfunction (and its offset as 'fpb_offset_subfn_lt0' relative to start of native code block (bp))
		fpb	2 + =res
	  fpb_end				; end of bytecode generation

	consts = $				; don't define this symbolic constant (and 'vars' too) as label here, otherwise an error can be generated
		fpb_int	2			; define 2-byte integer constant with name and value '2'
		fpb_flt	pi, 3.1415927		; define 2-byte float constant 'pi' with specified value
	vars = $
		fpb_var	x, 0.0			; declare 4-byte float variable 'x' with initial value 0.0
		fpb_var	res			; declare 4-byte float variable 'res' without initial value

  >>> BYTECODE FORMAT <<<

Bytecode is just a sequence of bytecode instructions (encoded as a sequence of opcodes with their operands). Each opcode is encoded as nibble (the 1st nibble is encoded in higher
4 bits, the 2nd one is encoded in lower 4 bits). So bytecode is nibblecode in fact :).
Number of bytecode instructions declared by macro 'declare_fpb' is stored to internal symbolic constant '@$fpb_gen_bytecode_ins'. Let's denote this value as BIC (bytecode instruction count).
* Thus opcodes from 0 to BIC-1 encodes native instructions assocoated with declared bytecode instructions.
* Opcode BIC encodes bytecode instructions 'copy' and 'ret'. If native code copier is not supported then this definitely means 'ret' instruction. Otherwise the next nibble X contains
  operand. If X = 0 then opcode BIC treats as 'ret' bytecode instruction. If X > 0 then it treats as 'copy' bytecode instruction and X means the number of native bytes that must be copied
  (actually the number of bytes to copy = X + copymin-1, see below).
* Opcodes BIC+1 and BIC+2 encode bytecode instructions 'ldc' and 'ldic'. The next nibble X contains the number of constant in constant array which must be loaded into FPU stack.
  Address of this constant is calculated as (reg + fpb_data_offset + X*2).
* If variable support is enabled then opcodes BIC+3 and BIC+4 encode bytecode instructions 'ldv' and 'stv'. The next nibble X contains the number of variable in variable array which must be
  loaded/store into/from FPU stack. Address of this variable is calculated as (reg + fpb_data_offset + X*4).

  >>> ABOUT ERROR MESSAGES <<<

Framed error messages generated by this module contain macro name in square brackets where this error occured.
In rare cases you can see strange error message (this is a feature of fasm, I don't know how to fix it yet). I recommend you to look at details of fasm error message carefully in this case.


USAGE DETAILS
=============

  >>> PREPARATION <<<

First define symbolic constant 'fpb_copy_support' (BEFORE including this module):
1 - enable native code copier;
0 - disable native code copier [default value (if not defined)].

Next define symbolic constant 'fpb_var_support' (BEFORE including this module):
1 - enable variable support (and 'ldv', 'stv' bytecode instructions) [default value (if not defined)];
0 - disable variable support.

Also define symbolic constant 'fpb_data_offset' (BEFORE including this module and ONLY if variable support is enabled) [default is 0 (if not defined)].
Just write 'fpb_data_offset = vars-consts', where 'vars' is offset of variable array, 'consts' is offset of constant array.
Actually you shouldn't do this if you don't see an error message that some variable is out of reach (you will see this when
  [number of constants + (number of variables) * 2] is more than 32).

Then include this module (include 'fpb.inc').
After that I recommend to add 'fpb_check_version' macro with specification of major, middle, minor version numbers of module you are using (see example above).

Declare bytecode instructions using macro 'declare_fpb'. The 1st parameter is bytecode instruction alias to use later as argument of macro 'fpb'.
The 2nd parameter is corresponding x86 instruction (it can contain commas). You can omit it if it matches the alias (e.g. 'declare_fpb faddp').
Maximal number of bytecode instruction types is 11 if variables are supported or 13 if not.
E.g.:
declare_fpb	+, faddp
declare_fpb	-, fsubp
declare_fpb	*, fmulp
declare_fpb	/, fdivp
declare_fpb	x2, fadd st0,st0
declare_fpb	^2, fmul st0,st0
declare_fpb	abs, fabs
declare_fpb	sqrt, fsqrt
declare_fpb	sin, fsin
declare_fpb	cos, fcos
declare_fpb	arctg, fpatan

Actually, you can use any instruction, not only FPU (e.g. 'mov ax,dx' or 'call cx') but any native instruction associated with bytecode instruction must be exactly 2 byte long!
Note that you can use aliases that contain only literal. Literal can consist of letters, digits, pseudographics and characters '_', '.', '@', '$', '%', '^', '!', '?',
e.g. 'sqrt', 'log2', 'fp_atan', '0.314e1', '5x', '^2', '.' and even '¼', '√', '²', '@$%', 'wat!?', 'плюс', '__', or strings in single or double quotes: '"hi <man>"')
or a SINGLE special character (only one of the following: '+', '-', '*', '/', '=', '(', ')', '[', ']', ':', '~', '<,>' (comma must be enclosed with angle brackets here);
you can't use characters '\', '|', '&', '#', '<', '>', '{', '}', '`', "'", '"', ';', spaces and tabs unless inside strings.
So you can't use aliases like '-1.5', '1e+6', 'a/b', '**', ';)', 'hello world' because they mix literals and special characters (e.g. '-' and '1.5' or 'a', '/' and 'b'),
consist of multiple special characters (e.g. '**') or contain illegal characters (e.g. ';' or space). Don't worry, you will see an error message in this case ;)

There're 6 predefined bytecode instructions:
- ret (usage: 'fpb ret') - end of subfunction / code block (you must not use it, macros 'fpb_end' and 'fpb_subfn' do it themselves);
- copy (usage: 'fpb copy,n') - copy n following native code bytes ('copy,0' is equivalent to 'ret') [available only if native code copier support is enabled];
- ldc (usage: 'fpb ldc,n') - load 32-bit float constant from [reg + fpb_data_offset + n*2] into FPU stack;
- ldic (usage: 'fpb ldic,n') - load 16-bit integer constant from [reg + fpb_data_offset + n*2] into FPU stack;
- ldv (usage: 'fpb ldv,n') - load 32-bit float variable from [reg + fpb_data_offset*2 + n*2] into FPU stack;
- stv (usage: 'fpb stv,n') - store and pop 32-bit float variable to [reg + fpb_data_offset*2 + n*2] from FPU stack;
Register 'reg' here is 'fnreg' register, specified for macro 'fpb_recompile!' (see below) [default is 'bx'].
Values of 'n' for all predefined bytecode instructions must be in range from 0 to 15 inclusively.
ATTENTION! I strongly recommend to use 'fpb_copy_start' / 'fpb_copy_end' instead of 'copy' and constant/variable aliases instead of 'ldc', 'ldic', 'ldv', 'stv' (see below).

You can define symbolic constants 'fpb_opcode_BCI' (before using macro 'fpb_recompile!'), where BCI is 'ldc', 'ldic', 'ldv', 'stv' to override native instructions for these bytecode instructions.
Note that opcode must be 3 bytes long, where the last byte is 8-bit offset of indirect addressing with offset. Default definitions (when constants are not defined) are:
- ldc: fld dword [reg+1]
- ldic: fild word [reg+1]
- ldv: fld dword [reg+1]
- stv: fstp dword [reg+1]
E.g. if you don't want to pop value from FPU stack after storing variable, you can write 'define fpb_opcode_stv fst dword [reg+1]'.
Remember that only 2 first bytes of each opcode will be stored and an offset of indirect addressing will be added after that (fpb_data_offset + X*2 for 'ldc' and 'ldic';
fpb_data_offset*2 + X*4 for 'ldv' and 'stv', where X is bytecode instruction operand (e.g. X = 3 for 'fpb ldc, 3')).
I strongly recommend to use 'reg' name inside definition to specify addressing register.

Native instruction of 'ret' bytecode instruction is predefined as 'nop' + 'ret'.
You can replace this 'nop' with useful instruction by defining constant 'fpb_before_ret' (e.g. 'define fpb_before_ret salc').

  >>> USING GENERAL FPB MACROS <<<

Macro 'fpb_recompile!' inserts recompiler code (this code is placed 'as is' and doesn't require 'call' or something else). Offset of bytecode block (input stream) is specified as
the 1st parameters 'bytecode'. Other parameters can specify different options. Read description of this macro for more information (in fpb.inc).
Before using 'fpb_recompile!' you must set register pair es:di to compiled function pointer (output stream);
Bytecode block (input stream) must be located at address dseg:bytecode, where 'dseg' is value of corresponding option [default is ds], 'bytecode' is the 1st parameter of macro;

To use recompiled function just set required register values and perform near call. Generally you need to set 'fnreg' register (see description of macro 'fpb_recompile'; default is bx)
to offset of constant array minus fpb_data_offset (mov bx,consts-fpb_data_offset).

Macros 'fpb_start', 'fpb_end', 'fpb', 'fpb_subfn', 'fpb_copy_start' and 'fpb_copy_end' are made to generate floating-point bytecode.
Macro 'fpb_start' starts bytecode generation and requires a parameter 'data' which is offset of constant array. The name of main (zero) subfunction can be specified as the 2nd parameter.
Macro 'fpb_end' ends bytecode generation. You can't use any other bytecode generation macros after 'fpb_end' except for 'fpb_start'.
Macro 'fpb_subfn' declares new subfunction. It can get a subfunction name as parameter. A symbolic constant 'fpb_offset_subfn#' will be defined when using macros 'fpb_start' and
  'fpb_subfn' (where # is a serial number, starting from 0). Also symbolic constant 'fpb_offset_subfn_NAME' will be defined if subfunction name is specified.
Macro 'fpb_copy_start' and 'fpb_copy_end' declares a block of native code. You can place any native x86 instructions between them. The lengths of this code must be in range from
  copymin to copymin+14 (see option 'copymin' of macro 'fpb_recompile!' in fpb.inc; default size range is 1..15). However, you can use multiple blocks of native code one after the other.
  These 2 macros are allowed only if native code copier is enabled (fpb_copy_support <> 0).

Macro 'fpb' generates bytecode instructions. It gets list of bytecode instruction, constants and variables aliases separated by space.
  During recompilation instruction aliases declared by macro 'declare_fpb' are translated into native instructions specified on declaration.
  Constant and variables aliases means pushing corresponding value into FPU stack and translated into native instructions associated with 'ldc', 'ldic' and 'ldv' bytecode instructions
    (see information about symbolic constants 'fpb_opcode_BCI' above).
  Equal sign followed by variable alias (e.g. '=var') means storing top FPU stack value into variable (and popping it from stack, at least if 'stv' bytecode instruction is not overridden)
    and translated into native instruction associated with 'stv' bytecode instruction.
  E.g. let's assume that we declared bytecode instructions '/', '*', defined constants 'pi', '180' and variables 'x', 'y'. Then macros 'fpb x pi * 180 / =y' will be equivalent to
    the following expression in high-level language: 'y = x * pi / 180'.

Macro 'fpb_end' also defines 5 extra symbolic constants:
- fpb_recompiled_code_size - the size of native code after recompilation (including copied native code);
- fpb_subfn_count - number of subfunctions (including the main);
- fpb_bytecode_count - number of bytecode instructions (bytecode instruction with operand counts as one instruction);
- fpb_nibble_count - number of bytecode nibbles (excluding copied native code);
- fpb_copied_code_size - size of copied native code.
So you can use these constants in your code.

To define fpb constants use macros 'fpb_flt' (for floating-point values) and 'fpb_int' (for integer values).
To define variables use macro 'fpb_var'. You can define up to 16 (or 17, see below) constants and variables. Variables MUST be placed AFTER constants!
E.g.:
fpb_int 10
fpb_flt 2.0
fpb_flt pi, 3.14159275359
fpb_var temp

The 1st parameter is alias, the 2nd one is value. If value is absent then value = alias for constants and undefined ('?') for variables, so this is equivalent to:
fpb_int 10, 10
fpb_flt 2.0, 2.0
fpb_flt pi, 3.14159275359
fpb_var temp, ?

It's better to define integer constants before floating-point ones, this way you can define up to 17 (instead of 16) constants (not variables).
I recommend to define constant and variable positions as constants ('consts = $', 'vars = $') instead of label ('consts:', 'vars:'), otherwise an error can be generated.
IMPORTANT! If the 1st constant is a floating-point value then offset of constant array must be 2 bytes less than offset of the 1st macro 'fpb_flt' ('consts = $-2', see example above).
Constants must be aligned by word (16 bits) relative to constant array, variables must be aligned by dword (32 bits) relative to constant array + fpb_data_offset.
If you'll see the message about wrong alignment of variable then just add a word ('rw 1') before variable array.
Please don't forget to add fractional part ('.') for float values (e.g. 'fpb_var x, 10' will define wrong initial value for variable 'x', you should use '10.0').
Note that float value is stored as 16-bit value. How is it possible? It's simple. Only higher word of 32-bit single precision float is stored. This accuracy is enough for most cases.
So the real offset of float constant is 2 byte less than offset of point where macros 'fpb_flt' is used. If you need higher accuracy then define it as variable with initialized value.

When calling recompiled function, access to constants and variables depends on 'fpb_data_offset' symbolic constant value:
- offset of constant array is calculated as 'reg + fpb_data_offset';
- offset of variable array is calculated as 'reg + fpb_data_offset*2';
where 'reg' is 'fnreg' resigter (see description of macro 'fpb_recompile!' in fpb.inc).
So as I wrote above you must set 'reg' to constant array offset - fpb_data_offset (e.g. mov bx,consts-fpb_data_offset)
And accordingly, you should set 'fpb_data_offset = vars-consts', where 'vars' is offset of variable array, 'consts' is offset of constant array.
Value of 'fpb_data_offset' must be even number in range from -64 to 32 (value 0 saves 2 bytes of code size). [default is 0 (if not defined)].
I remind you that this symbolic constant must be defined BEFORE including this file.

  >>> NOTES <<<

I recommend you to examine the comments before each macros in fpb.inc for more understanding. Especially the comments of macro 'fpb_recompile!'.

See 'fpb_demo.fasm' and HerbaKaif + HK256 intros (https://www.pouet.net/prod.php?which=88492) as examples and enjoy! ;)

Brief statistics of HerbaKaif intro:
- bytecode contains 244 nibbles (= 122 bytes = 169 opcodes; average length of 1 bytecode instruction ≈ 1.44 nibbles ≈ 0.72 bytes);
- size of recompiled function = 413 bytes (compression ratio ≈ 3.39x; average length of 1 unpacked native x86 instruction ≈ 2.44 bytes);
- size of recompiler = 74 bytes (including writting of input stream offset to register, although this is partly part of another code);
- compression ratio including bytecode recompiler = 413/(122+74) ≈ 2.11x.


CONTACTS
========

Feel free to contact the author (bug and inaccuracy reports, suggestions for ideas and optimizations):
* via Telegram: @jinxonik (preferably)
* by e-mail: jin_x@list.ru


┌─── ───────── ───────────────────────── ────────── -··
│ ┌─── ───────── ───────────────────── ────────── ────┐
│ │ ┌─── ───────── ───────────────── ▄▄▄▄▄▄▄▄▄─ ────┐ │
│ │ │ ┌─── ───────── ───────────── ▄▀      ▄▀ ────┐ │ │
│ │ │ │ ┌─── ▄▄▄▄▄▄▄▄▄ ────────  ▄▀      ▄▀ ────┐ │ │ │
│ │ │ │ │     ▀▄      ▀▄       ▄▀      ▄▀     : │ │ │ │
│ │ │ │ │       ▀       ▀▄   ▄▀      ▄▀       │ │ │ │ │
│ │ │ │ │  ████████████▀  ▀▄▀ ▄██▀ ▄▀   ▄███  │ │ │ │ │
│ │ │ │ │         ▄██▀      ▄██▀ ▄▀   ▄██▀██  │ │ │ │ │
│ │ │ │ │       ▄██▀  ▀▄  ▄██▀ ▄▀   ▄██▀  ██  │ │ │ │ │
│ │ │ │ │     ▄██▀   ▄▀ ▄██▀    ▀ ▄██▀    ██  │ │ │ │ │
│ │ │ │ │   ▄██▀   ▄▀ ▄██▀ ▄    ▄██▀      ██  │ │ │ │ │
│ │ │ │ │  ▀▀▀   ▄▀  ▀▀▀ ▄▀ ▀▄ ▀▀▀  ▀▄    ▀▀  │ │ │ │ │
│ │ │ │ :      ▄▀      ▄▀     ▀▄      ▀▄      │ │ │ │ │
│ │ │ └───── ▄▀      ▄▀ ─────── ▀▀▀▀▀▀▀▀▀ ────┘ │ │ │ │
│ │ └───── ▄▀      ▄▀ ─────────── ───────── ────┘ │ │ │
│ └───── ─▀▀▀▀▀▀▀▀▀ ─────────────── ───────── ────┘ │ │
└───── ────────── ─────────────────── ───────── ────┘ │
··-— ────────── ─────────────────────── ───────── ────┘

========================[ Greetings to... ]=========================
Řrřola  HellMood^DESiRE  TomCat^Abaddon  superogue^Marquee Design
Digimind  g0blinish  frag^fsqrt  Baudsurfer^RSi  sensenstahl
Kuemmel  iONic^Astroidea  baze^3SC  gopher^Alcatraz  Optimus
Dresdenboy^Citavia  Georgy Lomsadze  wbcbz7^sibCrew  DArt^Fenomen
AdamBazaroff^ExcessTeam  bfox^insiders  TmK^deMarche  Manwe^SandS
Dolphin Soft  ryg^Farbrausch  provod^jetlag … all sizecoders & YOU
====================================================================

.------------< Welcome to sizecoding chats!!! >-----------.
| https://discord.gg/NeCSgBTZmh • https://t.me/sizecoders |
'---------------------------------------------------------'
