How does RND() and TIMER work?

Hardware Hacking, Programming and Game Solutions/Cheats
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

How does RND() and TIMER work?

Post by jedie »

Does anyone know how RND() and the TIMER work on the Dragon? How are they generated?
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
User avatar
tormod
Posts: 416
Joined: Sat Apr 27, 2013 12:06 pm
Location: Switzerland
Contact:

Re: How does RND() and TIMER work?

Post by tormod »

The TIMER counter at $0112 is updated by the IRQ service routine at $9D3D. The RND() function is at $9772 and uses random seeds stored at $0115-9. You can see a commented disassembly here: http://sourceforge.net/p/toolshed/code/ ... .asm#l4410
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: How does RND() and TIMER work?

Post by jedie »

Currently in DragonPy the TIMER is not updated:

Code: Select all

?TIMER
 0
?TIMER
 0
TIMER=3
?TIMER
 3
RND() seems to work...

In DragonPy is a mechanism to debug memory read/writes to specific addresses. I add those middleware callbacks for $0115-9 with: I have add some debug log output with: https://github.com/jedie/DragonPy/commi ... 0d369a280e

The output is on startup (after RAM test):

Code: Select all

b7ce| write $80 RND() seed to: $0115
b7ce| write $4f RND() seed to: $0116
b7ce| write $c7 RND() seed to: $0117
b7ce| write $52 RND() seed to: $0118
b7ce| write $59 RND() seed to: $0119
A ?RND(0) outputs this:

Code: Select all

978e| read $4f RND() seed from: $0116
978e| read $c7 RND() seed from: $0117
9793| read $52 RND() seed from: $0118
9793| read $59 RND() seed from: $0119
97aa| write $2b RND() seed to: $0118
97aa| write $fe RND() seed to: $0119
97b5| write $7f RND() seed to: $0116
97b5| write $8f RND() seed to: $0117
The first RND(0) number is 0.498278379

Theses outputs are the same on every startup on Dragon 32 and 64 ROM, incl. the OP addresses.

I also add the debug output to the CoCo config with https://github.com/jedie/DragonPy/commi ... eb8d54816f
and get this:

Code: Select all

a59c| write $80 RND() seed to: $0115
a59c| write $4f RND() seed to: $0116
a59c| write $c7 RND() seed to: $0117
a59c| write $52 RND() seed to: $0118
a59c| write $59 RND() seed to: $0119
Same values but from other addresses...


Currently IRQs doesn't appear in DragonPy... Will the RND seed be filled with new values if IRQ worked?
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
sorchard
Posts: 530
Joined: Sat Jun 07, 2014 9:43 pm
Location: Norwich UK

Re: How does RND() and TIMER work?

Post by sorchard »

If I recall correctly, the RND() function is very predictable, and always gives the same sequence each time the machine is powered up, so I don't think there is any connection to the IRQ or timer.

Programs that used random numbers often had a statement like A = RND(TIMER) somewhere to mix things up a bit and make the sequence more random.
Stew
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: How does RND() and TIMER work?

Post by jedie »

I would like to implement IRQ in DragonPy, so that the TIMER will work.

I have looked into "Inside the Dragon": There is no page 193:
The Dragon does not use all the M6809's interrupts but
only makes use of the IRQ and FIRQ interrupts.
and on page 197:
The normal interrupt (IRQ) is derived from the video
circuitry which provides an interrupt request every 20
milliseconds, that is, in correspondence with every
cycle of the mains frequency. This will be slightly
different in countries where the mains frequency is
60Hz rather than 50Hz. The role played by this
interrupt is to update the system clock which is used
by the BASIC function TIMER as well as the functions
SOUND and PLAY.
So i will be concentrate on the normal IRQ first. I looked into XRoar sources.
If i understand it right then, the mc6847.[c|h] implements the VDG in a very finely divided steps (Really hardcore stuff, wow!)...
Finally it does this:

Code: Select all

	event_queue(&MACHINE_EVENT_LIST, &vdg->hs_rise_event);
	event_queue(&MACHINE_EVENT_LIST, &vdg->hs_fall_event);
I wont reimplement that. I would like to know if i can do this in a easier way.
Maybe just "call" IRQ every x CPU cycles, if CC.I is set?!?!

But how many CPU cycles is between every IRQ?
Every 50Hz? Wo many cycles are 50Hz?


From viewtopic.php?f=5&t=379&p=2248#p2248 :
The latest Dragon's use a 14.218 MHz crystal to give a CPU frequency (/16) of 0.888625 MHz, a cycle time of 1.125334 us whereas the CoCo and original Dragon's use the NTSC standard frequency of 14.31818 MHz to give a CPU frequency of 0.894886 MHz or 1.117460 us per cycle. This was to reduce the harmonic interaction between the CPU frequency and that of the PAL crystal ( 4.433619 MHz ) used to generate the TV output, giving a 'cleaner' video signal. The later Dragon's therefore run approx 6261 CPU cycles per second slower than the CoCo and earlier Dragon's.
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
sorchard
Posts: 530
Joined: Sat Jun 07, 2014 9:43 pm
Location: Norwich UK

Re: How does RND() and TIMER work?

Post by sorchard »

Working from old notes, so at the risk of getting something wrong...

The VDG is synchronised perfectly with the 6809 due to the action of the SAM, so the number of cycles per video frame will be constant and independent of the crystal frequency.

57 cycles per line
312 lines per frame (262 lines from VDG + 50 extra lines from PAL circuitry)
57 * 312 = 17784 cycles per frame

So if you call your irq routine every 17784 cycles then this will be close to emulating the relationship between interrupted and interrupting code.

The actual frequency does depend on the main crystal:

CPU clock = crystal frequency / 16
Video frame rate = CPU clock / 17784

A 14.318 MHz crystal gives a frame rate of 50.3Hz
A 14.218 MHz crystal gives a frame rate of 50.0Hz

It may be that the slower crystal has a better harmonic interaction with the colour burst frequency but it also gives a more nominal frame rate. In an NTSC CoCo, there are 57*262 = 14934 cycles per frame which gives a frame rate of 59.9Hz from a 14.318 MHz crystal.

I would bet that at the time the Dragon was being designed, 14.318MHz crystals were standard and low cost parts, and 14.218MHz crystals were non-standard and relatively expensive.
Stew
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: How does RND() and TIMER work?

Post by jedie »

Thanks for the information, that's help me much. I will implement this soon. But currently i working to limit the Speed to realtime: https://github.com/jedie/DragonPy/compa ... ...df7eaeb :D
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: How does RND() and TIMER work?

Post by jedie »

I have IMHO now a better implementation via callback, with: https://github.com/jedie/DragonPy/commi ... 1d5b476194

I see that IRQ is called and PC goes to $010c but the TIMER value doesn't increase as it should :cry:
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
jedie
Posts: 655
Joined: Wed Aug 14, 2013 12:23 pm
Location: germany
Contact:

Re: How does RND() and TIMER work?

Post by jedie »

I have update the CoCo code and add a logging routine for the TIMER read/write: https://github.com/jedie/DragonPy/compa ... ...804f7b4

Coco logging looks like this:

Code: Select all

...
MainProcess/MainThread $a7d5 *** IRQ, set PC to $010c	E.......
MainProcess/MainThread 8955| read byte $00 TIMER value from: $0112
MainProcess/MainThread 8955| read byte $61 TIMER value from: $0113
MainProcess/MainThread 895a| write word $0062 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $00 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $62 TIMER value to: $0113
MainProcess/MainThread $a7d3 *** IRQ, set PC to $010c	E.......
MainProcess/MainThread 8955| read byte $00 TIMER value from: $0112
MainProcess/MainThread 8955| read byte $62 TIMER value from: $0113
MainProcess/MainThread 895a| write word $0063 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $00 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $63 TIMER value to: $0113
MainProcess/MainThread $a7d3 *** IRQ, set PC to $010c	E.......
MainProcess/MainThread 8955| read byte $00 TIMER value from: $0112
MainProcess/MainThread 8955| read byte $63 TIMER value from: $0113
MainProcess/MainThread 895a| write word $0064 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $00 TIMER value to: $0112
MainProcess/MainThread 895a| write byte $64 TIMER value to: $0113
...
So the TIMER value will be increase as in assembly:

Code: Select all

1123 8955 BE 01 12 L8955        LDX TIMVAL      GET REAL TIME CLOCK
1124 8958 30 01                 LEAX $01,X      INCREMENT IT
1125 895A BF 01 12              STX TIMVAL      SAVE IT
The Dragon logs look like this:

Code: Select all

MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.H.....
Only if i do ?TIMER or TIMER=123 i see the access:

Code: Select all

...
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.......
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.......
MainProcess/MainThread 9d59| read byte $00 TIMER value from: $0112
MainProcess/MainThread 9d59| read byte $02 TIMER value from: $0113
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.H.....
...
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.......
MainProcess/MainThread $bbca *** IRQ, set PC to $010c	E.......
MainProcess/MainThread 9d55| write word $0002 TIMER value to: $0112
MainProcess/MainThread 9d55| write byte $00 TIMER value to: $0112
MainProcess/MainThread 9d55| write byte $02 TIMER value to: $0113
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
MainProcess/MainThread $bbc8 *** IRQ, set PC to $010c	E.H.....
...

Hm...
... too many ideas and too little time ... Related stuff written in Python:
Dragon 32 emulator / PyDC - Python Dragon 32 converter: https://github.com/jedie/DragonPy
DWLOAD server / Dragon-Lib and other stuff: https://github.com/6809
sorchard
Posts: 530
Joined: Sat Jun 07, 2014 9:43 pm
Location: Norwich UK

Re: How does RND() and TIMER work?

Post by sorchard »

Could it be because the Dragon ROM checks the MSB of $ff03 is set to confirm vsync is the source of the irq before branching to the TIMER update code?
Stew
Post Reply