flat assembler
Message board for the users of flat assembler.

Index > DOS > read computer time in millisecs? random numbers?

Author
Thread Post new topic Reply to topic
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
geekbasic@gmx.com 07 Jan 2023, 07:13
What is the best way to return the computers internal clock for timing?
Is it read in millisecs? Is there a DOS or BIOS interrupt?

I want to read it to a dw segment.

While I am asking questions, I am also wondering what is the simplest way to generate a random number in asm.
Post 07 Jan 2023, 07:13
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
revolution 07 Jan 2023, 07:57
geekbasic@gmx.com wrote:
What is the best way to return the computers internal clock for timing?
Is it read in millisecs? Is there a DOS or BIOS interrupt?

I want to read it to a dw segment.
The "best" way is ambiguous. It depends upon what you need. If you want the wall-clock time then you can read the RTC chip through I/O ports. If you want "high speed" timing there is a hardware counter circuit you can also read with I/O ports. There is also the TSC counter in the CPU on modern CPUs, which can be read with the RDTSC instruction.
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.
Do you mean pseudo-random numbers? A good search term is PRNG. There are thousands of methods and algorithms that can do this. It depends upon what you need as to which is suitable for you.

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.
Post 07 Jan 2023, 07:57
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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?
Post 07 Jan 2023, 08:34
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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    
Post 07 Jan 2023, 09:32
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
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?
The PRNG space is huge.

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.
Post 07 Jan 2023, 11:14
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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
Post 07 Jan 2023, 11:49
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
revolution 07 Jan 2023, 14:12
geekbasic@gmx.com wrote:
Do I have this correct?
random = (multiplier * seed + increment) % modulus
That looks 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.
Post 07 Jan 2023, 14:12
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
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)    
With the values given it gives a maximal length LCG of 65536
Post 08 Jan 2023, 03:15
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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
Post 08 Jan 2023, 06:33
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
revolution 08 Jan 2023, 06:45
You need to take the remainder. Your code use the quotient instead.
Post 08 Jan 2023, 06:45
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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
    
Post 08 Jan 2023, 07:35
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
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    
Post 08 Jan 2023, 08:24
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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 Smile

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    
Post 08 Jan 2023, 08:57
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
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    
Post 08 Jan 2023, 09:03
View user's profile Send private message Visit poster's website Reply with quote
geekbasic@gmx.com



Joined: 25 Oct 2022
Posts: 71
Location: Arizona
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.
    
Post 08 Jan 2023, 09:17
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
revolution 09 Jan 2023, 06:58
geekbasic@gmx.com wrote:
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.
https://wiki.osdev.org/Programmable_Interval_Timer

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.
Post 09 Jan 2023, 06:58
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
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.
Post 10 Jan 2023, 09:24
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20062
Location: In your JS exploiting you and your system
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.
Yeah, you are correct. You can turn off the speaker output via port 0x61 bit 0.
Post 10 Jan 2023, 11:33
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.