flat assembler
Message board for the users of flat assembler.
Index
> Main > FASMLIB (FASM Standard Library Project) Goto page Previous 1, 2, 3, 4, 5 Next |
Author |
|
Vasilev Vjacheslav 10 Feb 2007, 09:31
for proper rounding in ftoa maybe better to use
110000111111b instead of: 110000000000b ? |
|||
10 Feb 2007, 09:31 |
|
Vasilev Vjacheslav 10 Feb 2007, 09:40
if someone wants i can post my version of 'double to ascii' algo
ps. also i can post assembler equivalent of visual basic mid(), left() and right() functions, imho very useful for working with strings |
|||
10 Feb 2007, 09:40 |
|
Mac2004 10 Feb 2007, 10:53
Quote: if someone wants i can post my version of 'double to ascii' algo That would be nice from you... |
|||
10 Feb 2007, 10:53 |
|
Vasilev Vjacheslav 10 Feb 2007, 13:28
ok, it is a mid(), left(), right() implementation in assembler
ps. a bit later i put dtoa
_________________ [not enough memory] |
|||||||||||
10 Feb 2007, 13:28 |
|
dead_body 10 Feb 2007, 18:22
what happened to some files in fasmlib?
where the directory "linux"? |
|||
10 Feb 2007, 18:22 |
|
vid 11 Feb 2007, 10:19
this thread is pretty old, don't mind it/
|
|||
11 Feb 2007, 10:19 |
|
Vasilev Vjacheslav 11 Feb 2007, 14:51
here is my version of ftoa/dtoa (pretty long)
_________________ [not enough memory] |
|||||||||||
11 Feb 2007, 14:51 |
|
Dopefish 03 Jan 2008, 17:41
Reverend wrote: Hello all The ATOF routine seems to output different numbers based on how many are input. I do some calculations based on the number stored in st0. Here are some examples. The left number is the input (atof), and the second number is what ends up in st0. Code: INPUT OUTPUT 1 1.0 1. 21.1000000000 1.0 3.00999999910 1.00 1.20099999910 1.000 1.0201000000 1.0000 1.00200999910 1.00000 1.0002010000 1.000000 1.0000201000 1.0000000 1.00000200910 1.00000000 1.0000002010 1.000000000 1.0000000201 1.0000000000 1.0000000020 1.00000000000 1.0000000002 1.000000000000 1.0000000000 |
|||
03 Jan 2008, 17:41 |
|
vid 03 Jan 2008, 19:19
i have finished pretty good working atof, with lot of settings, and VERY good precision (because it doesn't use FPU - that also removes lot of problems).
It will be in FASMLIB 0.9.0, or I can post it in advance, if someone's interested... |
|||
03 Jan 2008, 19:19 |
|
Dopefish 08 Jan 2008, 15:55
I'm interested.
I'm always looking for a good itoa, atoi, ftoa or atof. |
|||
08 Jan 2008, 15:55 |
|
vid 08 Jan 2008, 16:12
this one is very precise, and thus also very slow
here is ugly copy-pastery of functions involved: Code: ;; @name conv._qfp_add ;; @desc ;; This is internal procedure - do not call it directly. ;; Adds two quad-precision floating point numbers. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be any of qfpnum1, qfpnum2. ;; @arg qfpnum1 ;; Pointer to first quad precision floating point number. ;; @arg qfpnum2 ;; Pointer to second quad precision floating point number. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large (above or equal binary 1e2147483648) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_add dest, qfpnum1, qfpnum2 local temp rd 4 push ebx ecx edx esi edi ;esi = qfpnum1, edx = qfpnum2 mov esi, [qfpnum1] mov edx, [qfpnum2] ;EDI = temp lea edi, [temp] ;ebx = position of highest bit in qfpnum1 mov ecx, 2*32 bsr eax, dword [esi+8] jnz @f sub ecx, 32 bsr eax, dword [esi+4] jnz @f sub ecx, 32 bsr eax, dword [esi] jz .qfp1_zero @@: add eax, ecx add eax, dword [esi+12] mov ebx, eax ;EAX = position of highest bit in qfpnum2 mov ecx, 2*32 bsr eax, dword [edx+8] jnz @f sub ecx, 32 bsr eax, dword [edx+4] jnz @f sub ecx, 32 bsr eax, dword [edx] jz .qfp2_zero @@: add eax, ecx add eax, dword [edx+12] ;EBX position of highest bit total cmp eax, ebx jbe @f mov ebx, eax @@: ;set destination mantissa to zero mov dword [edi], 0 mov dword [edi+4], 0 mov dword [edi+8], 0 ;ECX = destination exponent = highbit - 3*32 + 1 lea ecx, [ebx - 3*32 + 1] mov dword [edi + 12], ecx ;add first dword of mantissa libcall conv._qfp_dword, esi, ecx mov dword [edi], eax libcall conv._qfp_dword, edx, ecx add dword [edi], eax adc dword [edi+4], 0 add ecx, 32 ;add second dword of mantissa libcall conv._qfp_dword, esi, ecx add dword [edi+4], eax libcall conv._qfp_dword, edx, ecx add dword [edi+4], eax adc dword [edi+8], 0 add ecx, 32 ;add third dword of mantissa libcall conv._qfp_dword, esi, ecx add dword [edi+8], eax libcall conv._qfp_dword, edx, ecx add dword [edi+8], eax ;if we don't need to add another bit, we're done jnc .done ;insert top bit rcr dword [edi+8], 1 rcr dword [edi+4], 1 rcr dword [edi], 1 add dword [edi+12], 1 jo .error_overflow jmp .done ;QFP2 is zero, return QFP1 .qfp2_zero: mov edi, esi jmp .done ;QFP1 is zero, return QFP2 .qfp1_zero: mov edi, edx jmp .done .done: ;copy result from temp (pointed by EDI) to dest mov esi, [dest] mov eax, [edi] mov [esi], eax mov eax, [edi+4] mov [esi+4], eax mov eax, [edi+8] mov [esi+8], eax mov eax, [edi+12] mov [esi+12], eax ;return dest mov eax, esi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc .error_internal: mov eax, ERR_FASMLIB jmp .rc endp Code: ;; @name conv._qfp_div ;; @desc ;; This is internal procedure - do not call it directly. ;; Divides quad-precision floating point number by dword. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be same as divident ;; @arg divident ;; Pointer to quad precision floating point number which we divide. ;; Can be same as dest. ;; @arg divisor ;; Unsigned dword by which we divide. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too small (below binary 1e-(2147483648 - 95)) ;; When number approaches minimal exponent, FASMLIB rather returns error than reducing ;; precision by using fewer bits of mantissa. ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_div dest, divident, divisor push ebx ecx edx esi edi pushf ;EDI = dest mov edi, [dest] ;copy divident to dest (if they aren't same) mov esi, [divident] cmp esi, edi je @f cld movsd movsd movsd movsd @@: ;done if dest mantissa=0 cmp dword [edi], 0 jne taken @f cmp dword [edi+4], 0 jne taken @f cmp dword [edi+8], 0 jne taken @f mov dword [edi+12], 0 ;reset exponent to 0 if mantissa is 0 jmp .done @@: ;EBX = divisor mov ebx, [divisor] ;divide mantissa xor edx, edx mov eax, dword [edi+8] div ebx mov dword [edi+8], eax mov eax, dword [edi+4] div ebx mov dword [edi+4], eax mov eax, dword [edi] div ebx mov dword [edi], eax ;shift in more bits from result, as long as there is place at top of number (eg. upper bit is 0) .another_dword: ;if remainder is 0, we are done, there are no more set bits in result test edx, edx jz .done ;EAX = get another dword of result xor eax, eax div ebx ;insert bits from resulting dword into dest mov ecx, 32 .another_bit: ;if this is last dword of result(EDX=0) and all set bits of this dword are done (EAX=0), then stop test edx, edx jz .done test eax, eax jnz taken @f @@: ;if high order bit is 1, we can't insert any more bits bt dword [edi+8], 31 jc untaken .done ;adjust exponent dec dword [edi+12] jo untaken .error_underflow ;error if we can't get result with enough precision ;shift upper bit from EAX into mantissa shl eax, 1 ;get high bit of EAX to CF rcl dword [edi], 1 ;and shift it into number rcl dword [edi+4], 1 rcl dword [edi+8], 1 jc untaken .error_internal ;continue with next bit dec ecx jnz taken .another_bit ;continue with next dword jmp .another_dword .done: mov eax, edi .rnc: clc .r: popf pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_underflow: mov eax, ERR_OUT_OF_RANGE jmp .rc .error_internal: mov eax, ERR_FASMLIB jmp .rc endp Code: ;routine to extract dword at given bit position in qfp proc conv._qfp_dword qfpnum, index push esi ecx ebx mov esi, [qfpnum] mov ecx, [index] sub ecx, dword [esi+12] ;check which dwords we need cmp ecx, 96 jge .outside cmp ecx, 64 jge .in2 cmp ecx, 32 jge .in1 cmp ecx, 0 jge .in0 cmp ecx, -31 jl .outside .low: xor eax, eax mov ebx, dword [esi] add ecx, 32 shrd eax, ebx, cl jmp .r .in0: mov eax, dword [esi] mov ebx, dword [esi+4] shrd eax, ebx, cl jmp .r .in1: sub ecx, 32 mov eax, dword [esi+4] mov ebx, dword [esi+8] shrd eax, ebx, cl jmp .r .in2: sub ecx, 64 mov eax, dword [esi+8] shr eax, cl jmp .r .outside: xor eax, eax .r: pop ebx ecx esi ret endp Code: ;; @name conv._qfp_mul ;; @desc ;; This is internal procedure - do not call it directly. ;; Multiplies quad-precision floating point number by dword. ;; @arg dest ;; Pointer to buffer for destination number. ;; Can be same as qfpnum ;; @arg qfpnum ;; Pointer to quad precision floating point number which we multiply ;; Can be same as dest. ;; @arg multiplier ;; Unsigned dword by which we multiply. ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large (above or equal binary 1e2147483648) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp_mul dest, qfpnum, multiplier push ebx ecx edx esi edi ;EDI = dest mov edi, [dest] ;ESI = qfpnum mov esi, [qfpnum] ;EBX = multiplier mov ebx, [multiplier] ;copy exponent mov eax, dword [esi+12] mov dword [edi+12], eax ;multiply mantissa mov eax, dword [esi] mul ebx mov dword [edi], eax mov eax, dword [esi+4] mov ecx, edx mul ebx add eax, ecx mov dword [edi+4], eax mov ecx, edx mov eax, dword [esi+8] mul ebx add eax, ecx mov dword [edi+8], eax ;if we have some more bits, then shift them to mantissa mov eax, edx .insert_bit: ;no more bits? test eax, eax jz .done ;shift bit into result shr eax, 1 rcr dword [edi+8], 1 rcr dword [edi+4], 1 rcr dword [edi], 1 ;adjust exponent inc dword [edi+12] jo untaken .error_overflow ;follow with next bit jmp .insert_bit .done: mov eax, edi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc endp Code: ;; @name conv._qfp2t ;; @desc ;; This is internal procedure - do not call it directly. ;; Converts quad-precision floating point format to non-normalized ;; tenbyte (80-bit) floating point format, used natively by FPU. ;; @arg dest ;; Pointer to 10 byte buffer for destination number. ;; @arg qfpnum ;; Pointer to quad precision floating point number which we convert ;; @arg sign ;; Sign of destination number (our QFP don't have sign) ;; @ret ;; CF set on error, otherwise ;; EAX = dest ;; @err ERR_OUT_OF_RANGE ;; Resulting number is too large to be converted to tenbyte number ;; (absolute above 1e16384 or under 1e-16383) ;; @err ERR_INTERNAL ;; Bug in FASMLIB. Please report. ;; @note Quad-precision floating point number consists of 96bit mantissa ;; (3 dwords, low order dword first). Follows 32bit (dword) signed exponent, ;; that holds position of low order bit of mantissa. ;; There is no sign. Number doesn't need to be normalized. proc conv._qfp2t dest, qfpnum, sign push ebx ecx edx esi edi ;EDI = dest mov edi, [dest] ;ESI = qfpnum mov esi, [qfpnum] ;ebx = position of highest bit in qfpnum1 mov ecx, 2*32 bsr eax, dword [esi+8] jnz @f sub ecx, 32 bsr eax, dword [esi+4] jnz @f sub ecx, 32 bsr eax, dword [esi] jz .qfp_zero @@: add eax, ecx add eax, dword [esi+12] mov ebx, eax ;save mantissa lea eax, [ebx-31] libcall conv._qfp_dword, esi, eax mov dword [edi+4], eax lea eax, [ebx-63] libcall conv._qfp_dword, esi, eax mov dword [edi], eax ;get exponent mov eax, ebx add eax, 16383 cmp eax, 32767 ja .error_overflow ;set sign in exponent cmp [sign], 0 je @f bts eax, 15 @@: ;save sign+exponent mov word [edi+8], ax jmp .done ;special case: number is zero .qfp_zero: mov dword [edi], 0 mov dword [edi+4], 0 mov word [edi+8], 0 jmp .done .done: mov eax, edi .rnc: clc .r: pop edi esi edx ecx ebx ret .rc: stc jmp .r .error_overflow: mov eax, ERR_OUT_OF_RANGE jmp .rc endp This is the actual routine. You can change the values in 10^e computing to increase speed od routine and decrease accurancy a bit. currently they are all "1", eg. max. accurancy. Code: ;; @name extconv.fp_t ;; @desc ;; Convert floating point from decimal ASCII string representation to ;; FPU double-extended (80bit) floating point number, and returns it in ST0. ;; @ret ;; CF set on error, otherwise ;; ST0 = floating point number ;; @arg string ;; Pointer to string containing floating point number. ;; @arg buflen ;; Size of string's buffer. ;; You can set this to -1 (FFFFFFFFh) if string is quaranteed to be properly zero-terminated. ;; @arg allowsign ;; Sign allowance mode: 0 = no sign accepted, 1 = minus only accepted, 2 = minus and plus accepted. ;; @arg allowexp ;; If 1, exponent is allowed. ;; @err ERR_NOT_FLOAT ;; Number is not proper floating point number. ;; @err ERR_INVALID_MODE ;; allowsign > 2, or allowexp > 1. ;; @err ERR_ZERO_SIZE ;; buflen=0. ;; @warn ;; FPU must be initialized and there must be place on FPU stack. This procedure operates same ;; as FLD instruction. ;; @note ;; If allowexp=0, then "e" character (like any other) immediately behind number simply stops conversion, ;; number is converted with exponent 0, and no error is returned. ;; @note ;; This function doesn't use FPU by itself (only to load result to ST0), and internal ;; computations are done with very precision (96-bit mantissa, 32-bit exponent), so result ;; should usually be very precise. ;; @nodep conv._qfp_add ;; @nodep conv._qfp_mul ;; @nodep conv._qfp_div ;; @nodep conv._qfp2t proc extconv.fp_t string, buflen, allowsign, allowexp locals bn rd 4 temp rd 4 dest rd 4 firstdigit dd ? lastdigit dd ? exp dd ? ;exponent adjustment for mantissa digits behind dot sign dd ? ;sign: 0 is +, 1 is - dot dd ? ;dot flag: if 1, dot was already scanned tbnum rd 3 ;ten-byte number endl push esi ;error is buflen=0 cmp [buflen], 0 je .error_zero_size ;check allowances cmp [allowsign], 2 ja .error_mode cmp [allowexp], 1 ja .error_mode ;ESI = string mov esi, [string] mov [exp], 0 mov [dot], 0 mov [sign], 0 ;-------------------------------------------------- ;scan number lodsb ;check plus cmp [allowsign], 2 jne @f cmp al, '+' jne @f lodsb jmp .sign_done @@: ;check minus cmp [allowsign], 0 je @f cmp al, '-' jne @f mov [sign], 1 lodsb @@: .sign_done: mov [firstdigit], esi dec [firstdigit] ;we are behind first char now ; check first digit cmp al, '0' jb .error_bad_float cmp al, '9' ja .error_bad_float ;now scan digits jmp .scan_mantissa_start .scan_mantissa: lodsb .scan_mantissa_start: cmp al, '.' je .scan_dot cmp al, 'e' je .scan_exponent cmp al, '0' jb .end_scan cmp al, '9' ja .end_scan ;it is digit - if we are behind dot, than adjust exponent cmp [dot], 0 je .scan_mantissa dec [exp] jmp .scan_mantissa .scan_dot: cmp [dot], 0 ;was dot already found? jne .error_bad_float mov [dot], 1 jmp .scan_mantissa .end_scan: lea eax, [esi-2] ;we are now BEHIND last character, which is not part of mantissa mov [lastdigit], eax jmp .scan_done .scan_exponent: ;save address of last digit lea eax, [esi-2] mov [lastdigit], eax ;if exponent is disallowed, then 'e' character ends scanning cmp [allowexp], 0 jne @f jmp .scan_done @@: ;get exponent push ebx ;eax = buflen - (esi - string) = remaining size of buffer mov ebx, esi sub ebx, [string] mov eax, [buflen] sub eax, ebx pop ebx libcall conv.dec_id, esi, eax jc .error_exponent add [exp], eax .scan_done: ;-------------------------------------------------- ; calculate 10^exp ;initialize our floating point bignum mov dword [bn], 1 ;mantissa=1 mov dword [bn+4], 0 mov dword [bn+8], 0 mov dword [bn+12], 0 ;exponent=0 ;check exponent cmp [exp], 0 jg .positive_exponent je .exponent_done ;exponent is negative neg [exp] jmp .negative_exponent ;loop to calculate bn = 10^exp .positive_exponent: ;eax = tentable[min(exp,9) - 1] mov eax, [exp] cmp eax, 0 je .exponent_done cmp eax, 1 jbe @f mov eax, 1 @@: sub [exp], eax dec eax mov eax, [conv.tentable + 4*eax] ;multiply bn with eax libcall conv._qfp_mul, addr bn, addr bn, eax jc .rc jmp .positive_exponent .negative_exponent: ;eax = tentable[min(eax,9) - 1] mov eax, [exp] cmp eax, 0 je .exponent_done cmp eax, 1 jbe @f mov eax, 1 @@: sub [exp], eax dec eax mov eax, [conv.tentable + 4*eax] ;multiply bn with eax libcall conv._qfp_div, addr bn, addr bn, eax jc .rc jmp .negative_exponent .exponent_done: ;-------------------------------------------------- ; fun part - cycle through mantissa to calculate final value mov [temp], 0 mov [temp+4], 0 mov [temp+8], 0 mov [temp+12], 0 mov [dest], 0 mov [dest+4], 0 mov [dest+8], 0 mov [dest+12], 0 ; ESI = last digit of mantissa mov esi, [lastdigit] jmp .first_digit .another_digit: ;end loop? dec esi cmp esi, [firstdigit] jb .parse_done .first_digit: mov al, [esi] ;skip dot cmp al, '.' je .another_digit ;calculate digit*bn sub al, '0' movzx eax, al libcall conv._qfp_mul, addr temp, addr bn, eax jc .rc ;add digit*bn to result libcall conv._qfp_add, addr dest, addr dest, addr temp jc .rc ;multiply bn by 10 libcall conv._qfp_mul, addr bn, addr bn, 10 jc .rc ;continue loop jmp .another_digit .parse_done: ;-------------------------------------------------- ; now convert qfp to tenbyte floating point number libcall conv._qfp2t, addr tbnum, addr dest, [sign] jc .rc fld tbyte [tbnum] .rnc: clc cld .r: pop esi ret .rc: stc jmp .r .error_exponent: ;translate ERR_NOT_DECIMAL to ERR_NOT_FLOAT cmp eax, ERR_NOT_DECIMAL je .error_bad_float jmp .rc .error_mode: mov eax, ERR_INVALID_MODE jmp .rc .error_bad_float: mov eax, ERR_NOT_FLOAT jmp .rc .error_zero_size: mov eax, ERR_ZERO_SIZE jmp .rc endp idata { if used conv.tentable | INCLUDE_ALL_DATA eq 1 conv.tentable dd 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 end if } |
|||
08 Jan 2008, 16:12 |
|
Dopefish 11 Jan 2008, 04:27
Excellent.
Thank you very much. |
|||
11 Jan 2008, 04:27 |
|
Dopefish 03 Feb 2008, 02:46
Are there any examples around of doing conversions with unicode?
I'm in the middle of converting everything over to unicode and the only thing left that I'm really having trouble with is the conversion of integers to characters. The ITOA function posted on the previous page by Reverend doesn't work with unicode. |
|||
03 Feb 2008, 02:46 |
|
vid 03 Feb 2008, 03:26
i was thinking about unicode support, but it's low priority now.
ascii to unicode (eg. if all characters are below 128) is very easy, otherwise things become more complicated. |
|||
03 Feb 2008, 03:26 |
|
Dopefish 03 Feb 2008, 05:09
It's just for integer to unicode. (I think)
I'm writing to an .ini file and I convert the integer to unicode and write it out with WritePrivateProfileStringW. This works fine for the regular strings, but I can't seem to convert the integer to the unicode equivalent. I'm also converting a floating point to be displayed on a text label, which depends on converting integers. This doesn't seem to work, though. |
|||
03 Feb 2008, 05:09 |
|
vid 03 Feb 2008, 13:32
integer to UTF16 unicode is same as integer to ascii, just instead of byte ascii character, save it as word (eg. upper byte is zero).
or, you can convert to ascii, and then convert resulting string to unicode, by inserting zero byte after every byte of string. |
|||
03 Feb 2008, 13:32 |
|
Dopefish 03 Feb 2008, 17:48
It makes sense, unfortunately I think that's beyond me with using someone else's conversion procedures.
I do need to convert from unicode to integer/ascii to float and back to unicode. Basically, the user types in some numbers, with a period possibly, and then they press a convert button, it reads in that input, does the calculations, and outputs it. I can post the full source if it will help out. I've just really been getting in asm lately and my knowledge of it is fairly lacking. |
|||
03 Feb 2008, 17:48 |
|
Dopefish 03 Feb 2008, 18:04
Alright, here's the source for the previous version (without unicode support) and the new one I'm working on that is supposed to have unicode support in the end.
The code is terrible, I'm sure. Any help, pointers, tips, etc. is always more than welcome. Thanks. Edit: Another thing I just noticed is that depending on the height of the title bar, the content is shifted down. I've attached two images to show what I mean.
|
|||||||||||||||||||||||||||||
03 Feb 2008, 18:04 |
|
Madis731 05 Feb 2008, 11:18
These kind of tools are so narrow that they're probably useless. It has a GUI which takes some input, some output and an answer.
These kind of tools become better when they have multiple algorithms inside. Maybe some other graphics related stuff. Then if you are dealing with a graphics-related problem you can take this tool ***Calculator ***-meaning whatever (not only fov) and have it run in the background. Otherwise - nicely done! |
|||
05 Feb 2008, 11:18 |
|
Goto page Previous 1, 2, 3, 4, 5 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.