CON
_CLKMODE = XTAL3
_XINFREQ = 20_000_000
samples = 7000
var
long ptr
long time[noparse][[/noparse]2]
byte check
byte lockout
long cog,IR_Pin, buffer[noparse][[/noparse]7000]
obj serial : "FullDuplexSerial"
pub Main : okay
buf_ptr := @buffer
IR_Pin := 0
ptr := 0
lockout := 0
serial.start(31,30,0,9600)
okay := cog := cognew(@SAMPLE, @IR_PIN)
repeat
check := serial.rxcheck
if check <> 255
ptr := 0
repeat samples
serial.dec(buffer[noparse][[/noparse]ptr])
ptr++
dat
SAMPLE org 0
rdlong t1, par
mov IR_pin_mask, #1
shl IR_pin_mask, t1
andn dira, IR_pin_mask
'add t1, #4
'mov buf_ptr, t1
wait mov t2, #0
test IR_pin_mask, ina wc
rcl t2, #1
tjnz t2, #wait
'once data is detected then keep sampling until the buffer is full
mov t3, samp
mov t4, cnt
wrlong t4, buf_ptr
add buf_ptr, #4
look test IR_pin_mask, ina wc
mov t2, #0
rcl t2, #1
xor t2, negate_mask
and t2, #1
wrlong t2, buf_ptr
add buf_ptr, #4
mov Time1, cnt
add Time1, Delay
waitcnt Time1, Delay
djnz t3, #look
mov t4, cnt
wrlong t4, buf_ptr
samp long 6998
negate_mask long $FFFFFFFF
buf_ptr long 0
Delay long 2000
t1 res 1
t2 res 1
t3 res 1
t4 res 1
IR_pin_mask res 1
Time1 RES 1
FIT 496
So is this a professional forum?·
This is a hobby for me, no stress, no sarcasm from work, no road-rage from riding my bicycle to work, no yelling at the kids.....Am I in the wrong place?
I guess I should apologize for my slack-jawed-yokel comments about PAR, I was thinking CNT at the time.·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
I'd rather have a bottle in front of me than a frontal labotomy
deSilva said...
There is a difference between biting sarcasm and subtle irony, I think Mightor understands quite well
I think it is a cultural difference, though. I'm from the Netherlands and you're from Germany (as far as I have managed to ascertain) and we tend to use sarcasm and irony (and even a touch of cynicism) in our every day language. I am not sure if that's the same in the US [noparse]:)[/noparse]
As for irony :P I found a bug in your Machine Language guide. When specifying multiple flags (like WC and WZ) you need put a "," in between each effect. On page 18, example code ex07[noparse][[/noparse]A|B], you forgot these. I came across when I was looking for a way to divide 80_000_000 by 200. The code on page 20 for division won't work for that because the resultant quotient is longer than 16 bits.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
Thank you Mightor - the routines are limited to 16 bit results and unsigned. I copied them from Parallax.
To my knowledge no one has published "true" and "signed" 32-bit routines. I think this has to be done by distinguishing the different cases (<16 bit, >16 bit); signed is simple, just the rules from school.
I have been aware of the "comma" for some time, and I asked myself: "Why does no-one complain?". The error is also in the BNF-Syntax.
The funny thing is that I have never really used a double flag in my whole Propeller life. You need it for every => or =< check with CMPS or even CMP.
But for some reason, the knowledge of > or < seemed always sufficient... Or are there luring bugs in my little programs?
K, after much hacking and sawing, this is the code I've come up with to divide a 32bit long by another. One thing kind of puzzles me though and that's these lines:
cmp cpufreq, pwmfreq wc
IF_C add _t, #1
I am not sure why I need it, but when I leave them out, my result is shifted wrongly. Anyway, here's the code. I'd love some feedback as this is basically my first attempt at binary maths from scratch (pretty much from scratch).
{{
Demonstration of binary division in ASM
}}
DAT
propArr Byte "-\|/"
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
VAR
long quotient
long freq
long pwm
OBJ
Debug: "SerialMirror"
PUB start
Debug.start(31, 30, 0, 57600)
showWaiting
pwm := 80000
qAddr := @quotient
fAddr := @freq
cognew(@divide, @pwm)
waitcnt(clkfreq + cnt) ' wait a sec for cog to be ready
Debug.str(string("freq: "))
Debug.dec(freq)
Debug.CrLf
Debug.str(string(10,13))
Debug.str(string("quotient: "))
Debug.CrLf
Debug.bin(80_000_000/pwm, 32)
Debug.CrLf
Debug.bin(quotient, 32)
Debug.CrLf
Debug.dec(quotient)
Debug.str(string(10,13))
waitcnt(clkfreq/10 + cnt)
PRI showWaiting | i
repeat i from 0 to 70
Debug.tx(propArr[noparse][[/noparse]i//4])
waitcnt(clkfreq / 15 + cnt)
Debug.str(string(27, "[noparse][[/noparse]K"))
Debug.str(string(27, "[noparse][[/noparse]0;0H"))
DAT
org
divide rdlong pwmfreq, par ' PWM frequency
rdlong cpufreq, #0 ' CPU frequency
wrlong cpufreq, fAddr
:shiftloop
shl pwmfreq, #1 wc, wz ' If the bit 31 was 1, this will be in the C
IF_NZ_AND_NC add _t, #1
IF_NZ_AND_NC jmp #:shiftloop ' If no carry detected or number isn't 0, loop
rcr pwmfreq, #1 ' right shift carry back onto number
cmp cpufreq, pwmfreq wc
IF_C add _t, #1
:divloop cmpsub cpufreq, pwmfreq wc 'if y =< x then subtract it, set C
rcl _q,#1 'rotate c into quotient
shr pwmfreq, #1
djnz _t,#:divloop 'loop until done
wrlong _q, qAddr ' write our quotient to the HUB
:noploop nop ' just loop forever, keeps the cog alive.
jmp #:noploop
_q long 0
qAddr long 0
cpufreq long 0
pwmfreq long 0
_t long 0
fAddr long 0
Please be gentle...
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
I love a bit of gentle sarcasm (and some not so gentle) but its not the easiest thing to get across in text, combined with some "unusual wording" it can just come across as rudeness. So I'd just be careful all.
To simplify the debugging of "arithmetic" routines it is generally helpfull to reduce the wordlength to 2. So you have just the numbers 0,1,2, and 3 to occur (unsigned).
Now lets consider the following cases:
A: 2/2
B: 2/3
Both should yield 1 eventually
A:
A1: Shifting the denominator to the left breaks the first loop immediately, leaving pwm = 2 and _t = 1
A2: No _t correction
A3: COMPSUB will leave a carry and the second loop will break, leaving the quotient as 1 -> GOOD!
B:
B1 Shifting the denominator to the left stops he first loop immediately, leaving pwm = 3 and _t = 1
B2: unclear correction of _t as 2<3, leaving _t = 2
B3: COMPSUB will NOT leave a carry in the first loop run, quotient = 0 -> BAD!
B3.1: But you get a second chance:
pwm /= 2
COMPSUB will leave a carry, quotient = 1 -> GOOD!
But this is not an EXPLANATION, just what happens...
Now remember your school math (no sarcasm intended!)
When you divide - say - 12,345 by 13 - how do you start?
NOT devide 12 by 13 - poppycock!
And if you would do that it would require AN ADITIONAL LOOP RUN!
This is exactly the situation you identify by CMP cpu, pwm WC
Normally you would start with 123 by 13.
So to make the algorithm closer to school math I should have coded:
cmp cpufreq, pwmfreq wc
IF_C shr pwmfreq, #1
Incrementing _t however has the same effect.
But this is not yet EXACTLY how you do it in school - you do not blow up the denominator up the highest possible value.
Rather you shift it to a position where it is just somewhat smaller than (or equal to) the counter.
So it would be more transparent (and even faster!) when using a different loop check in the first loop... Something like
I went through the Propeller man page and in the section for the CMP command it's a bit unclear what the "WZ" flag does:
Propeller Manual said...
CMP (Compare Unsigned) compares the unsigned values of Value1 and Value2. The Z and C
flags, if written, indicate the relative equal, and greater or lesser relationship between the two.
If the WZ effect is specified, the Z flag is set (1) if Value1 equals Value2. If the WC effect is
specified, the C flag is set (1) if Value1 is less than Value2.
Which is the effect of WZ? Does it set Z when it is "greater or equal" or just when it is equal?
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
Mightor,
Z is set when the two values are equal. CMP does a 2's complement subtraction and Z is set when the result of the subtraction is zero. C is set when there is a carry (borrow) out of the subtraction. If you look at the definition of CMP in the Propeller manual, you'll see that it's a SUB instruction with no result written (NR).
There is whole set of mnemonics for it - have a look at the manual p.369 table 5.2
Condition Instruction Synonyms
IF_E if equal IF_Z
IF_NE if not equal IF_NZ
IF_A if above IF_NC_AND_NZ or IF_NZ_AND_NC
IF_B if below IF_C
IF_AE if above or equal IF_NC
IF_BE if below or equal IF_C_OR_Z or IF_Z_OR_C
I am sure I mention this in the tutorial - but I mention so many things, don't I
I knew about some of those, they're great. I used them in my code before [noparse]:)[/noparse] I was just verifying whether I was right wrg to the flags being set/not set and their meaning. I hadn't noticed the "IF_E" and "IF_A" ones though, they're nice and a lot less cryptic looking than IF_Z and IF_NC_AND_NZ. Thanks for pointing them out. I'm at work now but I'll have a look at tweaking that first loop to make use of CMP in a bit [noparse]:)[/noparse]
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
I've been trying to figure out how to setup that shiftloop using your CMP suggestion but I really cannot figure it out. I even took my laptop to work so I could hack it during lunch, all to no avail. I'm afraid I must throw in the towel. Could you please show me what you had in mind?
Thanks,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
Originally you are shifting to the left up to last possible position.
The new idea is: Just shift the denominator until it is equal or just a little bit below the counter.
When you overdo shifting to the left, your check (CMP) will not work as the important high bit is hiding in the carry! You have to take this also into account
On the Propeller the flags are only modified if you specify on a instruction to use execution effects. Your add instruction does not use such an effect. So why you are thinking the flags could be changed by add?
A flag will be changed only on an instruction if you use the WC or WZ effect unlike other microcontrollers. This is clearly described in the manual for each instruction.
In your code snippet this occur on the
"cmp cpufreq, pwmfreq wc"
and
"IF_B shl pwmfreq, #1 wc"
instructions.
The "IF_B" on the shl instruction depends on the result of the cmp instruction before. The C flag is only changed by the shl instruction if C flag was set before otherwise the shl instruction is treated as NOP.
If I managed to absorb and understand all the information that was in the Propeller Manual I probably wouldn't be on this forum asking my questions. [noparse]:)[/noparse] There's a serious amount of information in there to digest. For some reason I thought they were just cleared if the instruction was not a NOP. I thought the WZ and WC flags were there to allow them to be set if you chose to.
In any case, I managed to figure it out. This is what I have now:
Is the IF_A jump right after the cmp something that would be considered and OK way to get out of the loop or is this a little unaesthetic in ASM? I hope I didn't cause some of the more hardcore ASM programmers to get a little nauseous reading my newbie code.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
This is what a certain deSilva has written some monthes ago:
said...
..... we have to understand how these two flags are set, reset, or left unchanged. Every experienced assembly programmer knows that this can be a nightmare! A small instruction inserted for some reason in a chain of instructions can destroy a clever and efficient algorithm build on a flag. 8080 instructions are extremely clever, in that 8-bit instructions generally influence flags and 16-bit instructions don’t – with exceptions…
Now there is good news! You can forget all past problems, as a Propeller instruction will only influence a flag, if you tell it so! This is indicated by some “postfix” notation: you write WC (“with carry flag”[noparse];)[/noparse] or WZ (“with zero flag”[noparse];)[/noparse] at the end of the instruction.
Now you only have to memorize in what situation an instruction CAN set any flag (if it is allowed to do this). Some basic rules are:
- Moves, arithmetic, and logical instructions change Z whether the result is zero or not.
- Arithmetic instructions change C according to an “overflow”
- Logical instructions set C to form an even parity of all bits.
For the rest of instructions this is more complex
My excuse is that I am neither experienced in ASM nor was I ever an 8080 ASM programmer [noparse]:)[/noparse] I think you even put that passage in your tutorial, yeah, page 15, just checked. When you pasted it like that I instantly recognised it.
But back to my little piece of new code... Is this what you meant, deSilva?
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
This is my final test program for long division in ASM. Thanks to deSilva for helping me iron out bugs and everyone else who responded.
Gr,
Mightor
{{
Demonstration of binary division in ASM
}}
DAT
propArr Byte "-\|/"
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
VAR
long quotient
long pwm
long t
OBJ
Debug: "SerialMirror"
PUB start
Debug.start(31, 30, 0, 57600)
showWaiting
pwm := 80_000
qAddr := @quotient
tAddr := @t
cognew(@divide, @pwm)
waitcnt(clkfreq + cnt) ' wait a sec for cog to be ready
Debug.str(string("freq: "))
Debug.dec(clkfreq)
Debug.CrLf
Debug.str(string("pwm: "))
Debug.dec(pwm)
Debug.CrLf
Debug.CrLf
Debug.str(string("quotient: "))
Debug.CrLf
Debug.str(string("SPIN: "))
Debug.bin(clkfreq/pwm, 32)
Debug.CrLf
Debug.str(string("ASM: "))
Debug.bin(quotient, 32)
Debug.CrLf
Debug.str(string("SPIN: "))
Debug.dec(clkfreq/pwm)
Debug.CrLf
Debug.str(string("ASM: "))
Debug.dec(quotient)
Debug.CrLf
Debug.str(string("_t: "))
Debug.dec(t)
waitcnt(clkfreq/10 + cnt)
PRI showWaiting | i
repeat i from 0 to 70
Debug.tx(propArr[noparse][[/noparse]i//4])
waitcnt(clkfreq / 15 + cnt)
Debug.str(string(27, "[noparse][[/noparse]K"))
Debug.str(string(27, "[noparse][[/noparse]0;0H"))
DAT
org
divide rdlong pwmfreq, par ' PWM frequency
rdlong cpufreq, #0 ' CPU frequency
cmp pwmfreq, #0 wz ' Check for division by 0
IF_Z mov _q, #0 ' Make quotient 0 in this case
IF_Z jmp #:divret ' Go to exit
cmp pwmfreq, cpufreq wc, wz ' Check pwmfreq > cpufreq
IF_A mov _q, #0 ' Make quotient 0 in this case
IF_A jmp #:divret ' Goto exit
:shiftloop
cmp pwmfreq, cpufreq wc, wz ' Compare pwmfreq and cpufreq
IF_A jmp #:exitshiftloop ' pwmfreq is greater than cpufreq, so exit
IF_BE add _t, #1 ' increment _t counter
IF_B shl pwmfreq, #1 wc, wz ' shift pwmfreq to left, track C and Z
IF_NZ_AND_NC jmp #:shiftloop ' if no carry and pwmfreq != 0, keep going
:exitshiftloop
IF_A rcr pwmfreq, #1 ' If pwmfreq was shifted too much, shift carry back
:divloop cmpsub cpufreq, pwmfreq wc ' if y =< x then subtract it, set C
rcl _q,#1 ' rotate c into quotient
shr pwmfreq, #1 ' Shift pwmfreq to right
djnz _t,#:divloop ' loop until done
:divret mov _t, #255 ' for debugging purposes, to check if we're not
wrlong _t, tAddr ' looping infinitely
wrlong _q, qAddr ' write our quotient to the HUB
:noploop nop ' just loop forever, keeps the cog alive.
jmp #:noploop
_q long 0
qAddr long 0
cpufreq long 0
pwmfreq long 0
_t long 0
tAddr long 0
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
Comments
So is this a professional forum?·
This is a hobby for me, no stress, no sarcasm from work, no road-rage from riding my bicycle to work, no yelling at the kids.....Am I in the wrong place?
I guess I should apologize for my slack-jawed-yokel comments about PAR, I was thinking CNT at the time.·
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
I'd rather have a bottle in front of me than a frontal labotomy
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
As for irony :P I found a bug in your Machine Language guide. When specifying multiple flags (like WC and WZ) you need put a "," in between each effect. On page 18, example code ex07[noparse][[/noparse]A|B], you forgot these. I came across when I was looking for a way to divide 80_000_000 by 200. The code on page 20 for division won't work for that because the resultant quotient is longer than 16 bits.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
To my knowledge no one has published "true" and "signed" 32-bit routines. I think this has to be done by distinguishing the different cases (<16 bit, >16 bit); signed is simple, just the rules from school.
I have been aware of the "comma" for some time, and I asked myself: "Why does no-one complain?". The error is also in the BNF-Syntax.
The funny thing is that I have never really used a double flag in my whole Propeller life. You need it for every => or =< check with CMPS or even CMP.
But for some reason, the knowledge of > or < seemed always sufficient... Or are there luring bugs in my little programs?
I am not sure why I need it, but when I leave them out, my result is shifted wrongly. Anyway, here's the code. I'd love some feedback as this is basically my first attempt at binary maths from scratch (pretty much from scratch).
Please be gentle...
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
Graham
Now lets consider the following cases:
A: 2/2
B: 2/3
Both should yield 1 eventually
A:
A1: Shifting the denominator to the left breaks the first loop immediately, leaving pwm = 2 and _t = 1
A2: No _t correction
A3: COMPSUB will leave a carry and the second loop will break, leaving the quotient as 1 -> GOOD!
B:
B1 Shifting the denominator to the left stops he first loop immediately, leaving pwm = 3 and _t = 1
B2: unclear correction of _t as 2<3, leaving _t = 2
B3: COMPSUB will NOT leave a carry in the first loop run, quotient = 0 -> BAD!
B3.1: But you get a second chance:
pwm /= 2
COMPSUB will leave a carry, quotient = 1 -> GOOD!
But this is not an EXPLANATION, just what happens...
Now remember your school math (no sarcasm intended!)
When you divide - say - 12,345 by 13 - how do you start?
NOT devide 12 by 13 - poppycock!
And if you would do that it would require AN ADITIONAL LOOP RUN!
This is exactly the situation you identify by CMP cpu, pwm WC
Normally you would start with 123 by 13.
So to make the algorithm closer to school math I should have coded:
Incrementing _t however has the same effect.
But this is not yet EXACTLY how you do it in school - you do not blow up the denominator up the highest possible value.
Rather you shift it to a position where it is just somewhat smaller than (or equal to) the counter.
So it would be more transparent (and even faster!) when using a different loop check in the first loop... Something like
perhaps
Give it a try! But mind the zeroes!
Post Edited (deSilva) : 9/3/2007 6:58:27 PM GMT
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
Z is set when the two values are equal. CMP does a 2's complement subtraction and Z is set when the result of the subtraction is zero. C is set when there is a carry (borrow) out of the subtraction. If you look at the definition of CMP in the Propeller manual, you'll see that it's a SUB instruction with no result written (NR).
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
I am sure I mention this in the tutorial - but I mention so many things, don't I
Post Edited (deSilva) : 9/4/2007 7:21:50 AM GMT
I knew about some of those, they're great. I used them in my code before [noparse]:)[/noparse] I was just verifying whether I was right wrg to the flags being set/not set and their meaning. I hadn't noticed the "IF_E" and "IF_A" ones though, they're nice and a lot less cryptic looking than IF_Z and IF_NC_AND_NZ. Thanks for pointing them out. I'm at work now but I'll have a look at tweaking that first loop to make use of CMP in a bit [noparse]:)[/noparse]
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
I've been trying to figure out how to setup that shiftloop using your CMP suggestion but I really cannot figure it out. I even took my laptop to work so I could hack it during lunch, all to no avail. I'm afraid I must throw in the towel. Could you please show me what you had in mind?
Thanks,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
The new idea is: Just shift the denominator until it is equal or just a little bit below the counter.
When you overdo shifting to the left, your check (CMP) will not work as the important high bit is hiding in the carry! You have to take this also into account
I knew what I had to do, it's the execution of the plan that's not going quite as planned [noparse]:)[/noparse]
This is what I have now:
But this doesn't work because the "add" modifies C and Z, I can't switch around the add and the shl either. Ugh.
Please give me some new hints man, I'm really stuck :P
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
In your code snippet this occur on the
"cmp cpufreq, pwmfreq wc"
and
"IF_B shl pwmfreq, #1 wc"
instructions.
The "IF_B" on the shl instruction depends on the result of the cmp instruction before. The C flag is only changed by the shl instruction if C flag was set before otherwise the shl instruction is treated as NOP.
If I managed to absorb and understand all the information that was in the Propeller Manual I probably wouldn't be on this forum asking my questions. [noparse]:)[/noparse] There's a serious amount of information in there to digest. For some reason I thought they were just cleared if the instruction was not a NOP. I thought the WZ and WC flags were there to allow them to be set if you chose to.
In any case, I managed to figure it out. This is what I have now:
Is the IF_A jump right after the cmp something that would be considered and OK way to get out of the loop or is this a little unaesthetic in ASM? I hope I didn't cause some of the more hardcore ASM programmers to get a little nauseous reading my newbie code.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
But back to my little piece of new code... Is this what you meant, deSilva?
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.
Gr,
Mightor
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.