flat assembler
Message board for the users of flat assembler.
Index
> DOS > read computer time in millisecs? random numbers? |
Author |
|
revolution 07 Jan 2023, 07:57
geekbasic@gmx.com wrote: What is the best way to return the computers internal clock for timing? geekbasic@gmx.com wrote: While I am asking questions, I am also wondering what is the simplest way to generate a random number in asm. If you mean a real RNG, then your task definitely won't be simple. Genuine RNGs are tricky and usually slow. A pure software solution is impossible by definition. You will need hardware of some sort to give you the source bits. Some modern CPUs support the RDRAND instruction which claim to give random numbers. But so far the implementations are not open source and might not truly be random. Trust it at your own risk. |
|||
07 Jan 2023, 07:57 |
|
geekbasic@gmx.com 07 Jan 2023, 08:34
I really want what would be compatible with the most x86 machines.
Do you have any references to the "high speed" timing with ports? This seems to be what I am looking for. I think a pseudo-random generator is good enough for my needs. I will look more into PNRG. What would you say is the most simple solution regardless of effectiveness? |
|||
07 Jan 2023, 08:34 |
|
geekbasic@gmx.com 07 Jan 2023, 09:32
I am looking at using int 1ah,
This is my code so far. Code: rem system time library rem returns system time in ticks split to highorder and loworder rem each tick is 55 millisecs and there's 18 ticks per second rem daypassed returns 0 if 24 hours passed or 1 if not define highorder = 0, loworder = 0, daypassed = 0 sub systemtime beginasm xor ah,ah int 26 mov [highorder],cx mov [loworder],dx mov byte [daypassed],al endasm return |
|||
07 Jan 2023, 09:32 |
|
revolution 07 Jan 2023, 11:14
geekbasic@gmx.com wrote: I think a pseudo-random generator is good enough for my needs. I will look more into PNRG. What would you say is the most simple solution regardless of effectiveness? I guess a super-simple algorithm would be an LCG. They are easy to code. For throw away code this might be fine. I personally like the xoshiro class for simple stuff. A bit more effort to code, but still quite simple IMO. If you need a cryptographically secure PRNG, then you'll need something more complex. For all of these the seeding requirements might be the most difficult part. |
|||
07 Jan 2023, 11:14 |
|
geekbasic@gmx.com 07 Jan 2023, 11:49
I'm trying to figure out how to properly seed LCG in 16 bit.
What should I try for the modulus, multiplier, and increment? I can't use anything greater than 65536. There seems to be a lack of definitive examples. I know I have struggled with this before. Do I have this correct? random = (multiplier * seed + increment) % modulus |
|||
07 Jan 2023, 11:49 |
|
revolution 07 Jan 2023, 14:12
geekbasic@gmx.com wrote: Do I have this correct? Although I would hesitate to call the result "random". Usually the result updates the seed for the next iteration. So: seed = (multiplier * seed + increment) % modulus LCG's are very low quality, so parameter selection isn't very critical. If you choose three values that are coprime to each other then it's usually enough. |
|||
07 Jan 2023, 14:12 |
|
revolution 08 Jan 2023, 03:15
Here is some basic python code to experiment with simple LCG parameters:
Code: #!/bin/python3 multiplier = 54321 modulus = 1 << 16 addend = 1 start_seed = 0 counter = 0 seed = start_seed while counter <= modulus: counter += 1 seed = (seed * multiplier + addend) % modulus if seed == start_seed: print("Multiplier %u, Modulus %u, Addend %u: Count %u" % (multiplier, modulus, addend, counter)) for _ in range(20): # print the first 20 values print(seed) seed = (seed * multiplier + addend) % modulus quit(0) print("Something bad happened") quit(1) |
|||
08 Jan 2023, 03:15 |
|
geekbasic@gmx.com 08 Jan 2023, 06:33
For some reason I am only able to go up to 65535.
I chose 2 as the multiplier and 1 as the increment. I figured out how to calculate a range between 0 and n-1 and added it as a feature. I create a divisor by dividing a modulus by n range then divide the random number by that. I have two versions of my code. One in 32 bit interpreter and one in 16 bit compiler. The 32 bit interpreter runs fine continually, but my 16 bit compiled program only calculates the first 14 values and then just gives zeroes. Like this.. 37 75 50 1 2 5 11 22 45 90 81 62 25 50 0 0 0 0 0 0 0 etc... The 32 bit interpreter does print the same numbers, but keeps going. For some reason the 16 bit program is unable to continue generating the random numbers. Here's my code. Output generated by my compiler Code: use16 org 256 ; 2 do .do1: ; 4 let randomlimit = 100 mov ax,100 mov [randomlimit],ax ; 5 gosub generaterandom call generaterandom ; 7 print random mov ax,[random] call echovalue call newline ; 8 pause call pauseprogram ; 10 loop push 1 pop ax cmp ax,1 je .do1 ; 13 rem pseudo random number generation library ;pseudo random number generation library ; 14 rem uses linear congruential generators ;uses linear congruential generators ; 16 define randommodulus = 65535, randommultiplier = 2, randomincrement = 1 ; 17 define randomseed = 12345, randomlimit = 10, randomdivisor = 0, random = 0 ; 19 sub generaterandom generaterandom: ; 21 let randomseed = randommultiplier * randomseed + randomincrement push [randommultiplier] push [randomseed] pop [operand2] pop [operand1] mov ax,[operand1] mov bx,[operand2] mul bx push ax push [randomincrement] pop [operand2] pop [operand1] mov ax,[operand1] add ax,[operand2] push ax pop ax mov [randomseed],ax ; 22 let randomseed = randomseed / randommodulus mov dx,0 mov ax,[randomseed] mov bx,[randommodulus] div bx mov [randomseed],ax ; 23 carry randomseed mov [randomseed],dx ; 24 let random = randomseed mov ax,[randomseed] mov [random],ax ; 25 let randomdivisor = randommodulus / randomlimit mov dx,0 mov ax,[randommodulus] mov bx,[randomlimit] div bx mov [randomdivisor],ax ; 26 let random = random / randomdivisor mov dx,0 mov ax,[random] mov bx,[randomdivisor] div bx mov [random],ax ; 28 if random >= randomlimit then mov ax,[random] mov bx,[randomlimit] cmp ax,bx jb .endif1 ; 30 let random = randomlimit - 1 mov ax,[randomlimit] sub ax,1 mov [random],ax ; 32 endif .endif1: ; 34 return ret pauseprogram: xor ah,ah int 22 ret echostring: mov ah,9 int 33 ret echovalue: xor cx,cx mov bx,10 .valuestack: xor dx,dx div bx add dx,'0' push dx inc cx test ax,ax jnz .valuestack mov ah,2 .writedigit: pop dx int 33 loop .writedigit ret newline: mov dx,crlf call echostring ret crlf db 13,10,36 operand1 dw 0 operand2 dw 0 randommodulus dw 65535 randommultiplier dw 2 randomincrement dw 1 randomseed dw 12345 randomlimit dw 10 randomdivisor dw 0 random dw 0 FTCBASIC 16 bit compiler code Code: use random.inc do let randomlimit = 100 gosub generaterandom print random pause loop random.inc include file for FTCBASIC Code: rem pseudo random number generation library rem uses linear congruential generators define randommodulus = 65535, randommultiplier = 2, randomincrement = 1 define randomseed = 12345, randomlimit = 10, randomdivisor = 0, random = 0 sub generaterandom let randomseed = randommultiplier * randomseed + randomincrement let randomseed = randomseed / randommodulus carry randomseed let random = randomseed let randomdivisor = randommodulus / randomlimit let random = random / randomdivisor if random >= randomlimit then let random = randomlimit - 1 endif return Craft Basic 32 bit interpreter code that I used for comparison This version works continually Code: define randommodulus = 65535, randommultiplier = 2, randomincrement = 1 define randomseed = 12345, randomlimit = 100, randomdivisor = 0, random = 0 do gosub generaterandom print random wait loop sub generaterandom let randomseed=(randommultiplier*randomseed+randomincrement)%randommodulus let random = randomseed let randomdivisor = randommodulus / randomlimit let random = int: random / randomdivisor if random >= randomlimit then let random = random - 1 endif return Last edited by geekbasic@gmx.com on 08 Jan 2023, 07:31; edited 2 times in total |
|||
08 Jan 2023, 06:33 |
|
revolution 08 Jan 2023, 06:45
You need to take the remainder. Your code use the quotient instead.
|
|||
08 Jan 2023, 06:45 |
|
geekbasic@gmx.com 08 Jan 2023, 07:35
I'm confused about what you said about the quotient.
this carries the remainder into randomseed for some reason it breaks after a number of iterations Code: let randomseed = randommultiplier * randomseed + randomincrement let randomseed = randomseed / randommodulus carry randomseed Code: ; 21 let randomseed = randommultiplier * randomseed + randomincrement push [randommultiplier] push [randomseed] pop [operand2] pop [operand1] mov ax,[operand1] mov bx,[operand2] mul bx push ax push [randomincrement] pop [operand2] pop [operand1] mov ax,[operand1] add ax,[operand2] push ax pop ax mov [randomseed],ax ; 22 let randomseed = randomseed / randommodulus mov dx,0 mov ax,[randomseed] mov bx,[randommodulus] div bx mov [randomseed],ax ; 23 carry randomseed mov [randomseed],dx |
|||
08 Jan 2023, 07:35 |
|
revolution 08 Jan 2023, 08:24
I missed the "carry ..." thing.
In assembly we can use the high part of the result to feed the next stage. Code: mov ax,[seed] mov dx,[multiplier] mul dx ; result is in dx:ax a 32-bit number add ax,[addend] adc dx,0 mov cx,[modulus] div cx mov [seed],dx ;output is in dx |
|||
08 Jan 2023, 08:24 |
|
geekbasic@gmx.com 08 Jan 2023, 08:57
Wow! That's nicely written code.
I was just reading this post and was becoming more confused. https://board.flatassembler.net/topic.php?t=5617 You had posted there 10 years ago I tried the code you shared and it works beautifully compared to what I had wrote. Still, I wonder what part of my code went haywire... At least this exercise helped me find and fix more problems with my compiler. It makes sense that your code is so much better. Handwritten asm always beats compiler generated code! Here's how I implemented your code. It generates a number between 0 and randlimit-1. Code: rem pseudo random number generation library rem uses linear congruential generators define randmodulus = 65535, randmultiplier = 2, randaddend = 1 define randseed = 12345, randlimit = 10, randdivisor = 0, rand = 0 sub generaterand beginasm mov ax,[randseed] mov dx,[randmultiplier] mul dx add ax,[randaddend] adc dx,0 mov cx,[randmodulus] div cx mov [randseed],dx mov [rand],dx endasm let randdivisor = randmodulus / randlimit let rand = rand / randdivisor if rand >= randlimit then let rand = randlimit - 1 endif return |
|||
08 Jan 2023, 08:57 |
|
revolution 08 Jan 2023, 09:03
If you use a modulus of 2^16 (65536) then you don't need the div.
Code: ; only works for modulus of 2^16 mov ax,[seed] mov dx,[multiplier] mul dx ; ignore the high part in dx, use the implicit modulus of 2^16 add ax,[addend] mov [seed],ax ;output is in ax |
|||
08 Jan 2023, 09:03 |
|
geekbasic@gmx.com 08 Jan 2023, 09:17
That clears up some confusion about some of the examples I have seen.
The reason I am using 65535 is that 65536gives me this error with FASM Code: randmodulus dw 65536 processed: randmodulus dw 65536 error: value out of range. |
|||
08 Jan 2023, 09:17 |
|
revolution 09 Jan 2023, 06:58
geekbasic@gmx.com wrote: I really want what would be compatible with the most x86 machines. Channel 1 appears to be long dead. So I guess the only real option is to reprogram channel 0 for the rate you want. You might be able to get away with using channel 2 if the frequency is above normal human hearing and you don't have any dogs nearby. Might be fun to try though. |
|||
09 Jan 2023, 06:58 |
|
DimonSoft 10 Jan 2023, 09:24
Shouldn’t Channel 2 be specifically programmed to send its ticks to a speaker? AFAIR, it won’t produce any sound unless set up so, but will still tick.
|
|||
10 Jan 2023, 09:24 |
|
revolution 10 Jan 2023, 11:33
DimonSoft wrote: Shouldn’t Channel 2 be specifically programmed to send its ticks to a speaker? AFAIR, it won’t produce any sound unless set up so, but will still tick. |
|||
10 Jan 2023, 11:33 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.