I guess I just build a...screensaver?

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
Post Reply
User avatar
Marco
Posts: 24
Joined: 08 Dec 2019, 15:33

I guess I just build a...screensaver?

Post by Marco » 26 Dec 2019, 11:02

Merry Christmas to all!

The intend was to spend the Christmas holidays assembling the Gigatron (besides entertaining the wife and family), but impatience got the better of me weeks ago and the Gigatron has been happily blinking its lights ever since. I was about to write "humming along" but it isn't, not even softly, and that's a bit of a letdown really (just kidding). So, I've been doing some BASIC (really basic) stuff on it and reliving my school days, when life was much more simpler and in a time when the Dead Sea merely had an annoying cold... Those were the days of stealing computer time on C64 demo computers at the V&D store in town after school hours (and occasionally during school hours at well, as a matter of fact), hoping you'd be able to finish typing in the code you had jotted down on paper the night before, before someone from the store would pat you on the shoulder and told you to b*gger off! In that unfortunate case you would migrate to the Bijenkorf, another well known store, and try your luck there. Ahhh, if only I had a time machine...

Anyway...before the forum moderators tap me on the shoulder for wasting too many electrons, here's a little BASIC code I made up. When showing off the Gigatron to friends and family, one way of demoing is with the Mandelbroth, which looks nice and complex and has a clock. But non-IT people don't really get it, so I was looking for something simpler. In BASIC. Marcel posted hist Cosinus code a while back and that fitted the bill quite nicely. So I blatantly copied it, randomized it a bit, changed the background, and added a clock in retro-looking red digits. Just for fun.

Now, the clock is of course a bit of a challenge, in a programming language which doesn't keep time, on a computer which doesn't either. Yes I know, the Gigatron does have a clock and a good thing too it does otherwise it wouldn't work at all, but that's besides my point. So what can we do? We cheat of course! Sort of. For we know the Gigatron is a single tasking computer, and therefore it is quite safe to assume that a loop in BASIC code will take exactly the same time every time it is interpreted, as long as the amount of code within each loop iteration is constant (or in other words: not too many jumps). My goal was to have an accuracy of no more than a 1 minute deviation in 24 hours. And that works. For me I clocked and fine tuned this particular loop time to 23.56 seconds, but I am really curious if this number is constant on other Gigatrons as well. Should be... The clock is in 24 hour format, as it should be, but ye Americans can be easily adapted to 12 hour format I guess.

One final word: do NOT use this clock for important time keeping. I do NOT take any responsibility should you miss an appointment or whatever. Just saying ;)

And here's the code. It wont save to Mr. McPlugface so use an Arduino instead. The code is also on Github in Contrib/Crouze.

Code: Select all

10 poke 42,0:poke43,2
20 'Q = loop duration in
30 '1/100s, adjust to
40 'finetune the clock
50 mode1:cls:T=150:Q=2356
60 ?"Set Hour and Minute"
70 input H:if H<0 goto 70
80 if H>23 goto 70
90 input M:if M<0 goto 90
100 if M>59 goto 90
110 cls:C=rnd(23)+1
120 P=(rnd(24)+4)*16
130 U=5000:V=0
140 if T<6000 goto 220
150 M=M+1:T=T-6000
160 if M<60 goto 220
170 M=0:H=H+1
180 If H=24 H=0
190 'unREM line 220 to
200 'show 1/100s for
210 'finetuning clock
220 REM at 2,112:?T;
230 L=rnd(128)+2
240 at L,1:?"0 :0";
250 Z=0:if H<10 Z=6
260 at L+Z,1:?H;
270 Z=0:if M<10 Z=6
280 at L+Z+18,1:?M;
290 for X=0 to 159
300 Y=68-U/100
310 poke Y*256+X,C
320 U=U+V:V=V-U/P
330 next X:T=T+Q
340 if C>500 goto 110
350 C=C+23
360 if C%64=0 goto 350
370 at L,1:?"     ";
380 goto 140
Will get you something like this:

Image


Enjoy your holidays (if you have),
Marco

User avatar
marcelk
Posts: 490
Joined: 13 May 2018, 08:26

Re: I guess I just build a...screensaver?

Post by marcelk » 27 Dec 2019, 21:23

Thanks! There is an accurate software clock ticking at 59.58 Hz on address 14, but it wraps around every 4 seconds. If you sample it often enough however, you can make a very good clock out of it. The Mandelbrot clock is derived from it.

User avatar
Marco
Posts: 24
Joined: 08 Dec 2019, 15:33

Re: I guess I just build a...screensaver?

Post by Marco » 27 Dec 2019, 23:23

Cool!

Maybe I haven't browsed thoroughly enough, but is there a list available of memory addresses (and their specific functions)? I'm reasonably happy with the 20 second deviation per day as i I have it now, but as long as there is room for improvement...

Cheers,
Marco

User avatar
marcelk
Posts: 490
Joined: 13 May 2018, 08:26

Re: I guess I just build a...screensaver?

Post by marcelk » 28 Dec 2019, 00:24

The README.md file in the root of the GitHub repo should be a good starting point. That's where I looked up the address :D

User avatar
marcelk
Posts: 490
Joined: 13 May 2018, 08:26

Re: I guess I just build a...screensaver?

Post by marcelk » 03 Jan 2020, 21:25

Now there's an example program in BASIC/Clock.gtb:

Code: Select all

'Keeping time using the
'59.98 Hz frame counter
10 input H,M,S:B=peek(14)
20 T=S*60:V=3599:goto 50
30 if T>=V T=T-V: M=M+1
40 if M>59 M=M-60:H=H+1
50 print H;":";M/10;M%10;
60 S=T/60:?":";S/10;S%10
70 A=B:B=peek(14):A=B-A
80 if A<0 A=A+256
90 T=T+A:goto 30
Note that the Gigatron frame rate is 6.25 MHz / (521*200) cycles per frame = 59.9806 frames per second. This gives nearly exactly 3599 frames per minute, with the remaining error under the crystal's tolerances.

User avatar
Marco
Posts: 24
Joined: 08 Dec 2019, 15:33

Re: I guess I just build a...screensaver?

Post by Marco » 03 Jan 2020, 21:47

Hi Marcel,

Thanks for that. I've already rewritten my code using peek(14) and am now checking its accuracy. I had reckoned one complete cycle on address 14 takes 258 / 59.98 = 4.268 seconds, and use that number to check if a minute has passed. Should work equally well. But I'm undecided yet which way I'll go (i.e. which has less code with equal accuracy).

By the way, clever and simple how you take care of leading zeroes, hadn't thought of it like that.

I'll post the new code once I'm happy with it ;)

User avatar
Marco
Posts: 24
Joined: 08 Dec 2019, 15:33

Re: I guess I just build a...screensaver?

Post by Marco » 05 Jan 2020, 18:50

This is the (for now) final code. I'm now down to a less than 1 second deviation per day, and that's good enough for me ;)

Code: Select all

'graph display with clock
'(hr:min) using 59.98Hz
'counter. UnREM lines 175
'and 235 to see seconds,
'adjust I (1/100 sec/min)
'and J (1/100 sec/hour)
'for clock finetuning.
10 poke 42,0:poke 43,2
20 A=0:B=0:I=-3:J=8:cls
30 ?"Hour, Minutes";
40 ?" and Seconds:":?
50 input H:if H<0 goto 50
60 if H>23 goto 50
70 input M:if M<0 goto 70
80 if M>59 goto 70
90 input S:if S<0 goto 90
100 if S>59 goto 90
110 poke 14,0:S=(S+2)*100
120 cls:C=rnd(23)+1
130 P=(rnd(24)+4)*16
140 U=5000:V=0
150 Z=S+(427/256*B)
160 if Z<6000 goto 210
170 M=M+1:S=S+I-6000
175 'Z=Z-6000
180 if M<60 goto 210
190 M=0:H=H+1:S=S+J
200 if H=24 H=0
210 T=rnd(128)+2:at T,1
220 ?H/10;H%10;":";
230 ?M/10;M%10;
235 'at2,112:?Z/100;" ";
240 for X=0 to 159
250 Y=68-U/100
260 poke Y*256+X,C
270 U=U+V:V=V-U/P
280 B=peek(14)
290 if B<A S=S+427
300 A=B:next X
310 if C>500 goto 120
320 C=C+23
330 if C%64=0 C=C+23
340 at T,1:?"     ";
350 goto 150
The file graphclock.gtb is also on Github in Contrib/Crouze.

at67
Posts: 205
Joined: 14 May 2018, 08:29

Re: I guess I just build a...screensaver?

Post by at67 » 19 Jan 2020, 07:59

I was inspired by all the clocks being posted, so I thought I'd add my own to this thread; the timing code for this clock also originated from this thread.

https://streamable.com/285jt

Code: Select all

'Keeping time using the
'59.98 Hz frame counter

const TICKS = 60
const SECSX = &h60A0
const SECSY = &h61A0
const MINSX = &h62A0
const MINSY = &h63A0
const HOURX = &h64A0
const HOURY = &h65A0

const CENTERX = 80
const CENTERY = 59

const SECSLEN = 45
const MINSLEN = 37
const HOURLEN = 29

const BKCOLOUR = &h10

'Seconds
def byte(SECSX, x, -90.0, 270.0, TICKS) = cos(x)*SECSLEN + CENTERX
def byte(SECSY, y, -90.0, 270.0, TICKS) = sin(y)*SECSLEN + CENTERY

'Minutes
def byte(MINSX, x, -90.0, 270.0, TICKS) = cos(x)*MINSLEN + CENTERX
def byte(MINSY, y, -90.0, 270.0, TICKS) = sin(y)*MINSLEN + CENTERY

'Hours
def byte(HOURX, x, -90.0, 270.0, TICKS) = cos(x)*HOURLEN + CENTERX
def byte(HOURY, y, -90.0, 270.0, TICKS) = sin(y)*HOURLEN + CENTERY

poke 162,BKCOLOUR
cls

B=peek(14)

input "Enter Time ", H,"H:";2;, M,"M:";2;, S,"S:";2;
at 2,0 : print "                          ";
if H=0
    H=12
else
    H=H%13
endif
M=M%60
S=S%60

HH=H : MM=M : SS=S

Hm12=H%12
Md12=M/12 : MMd12=Md12

gosub drawDial
gosub drawClock

20 T=S*60 : V=3599 : goto 50
30 if T>=V then T=T-V : M=M+1
40 if M>59 then M=M-60 : H=H+1
50 if H>12 then H=H-12
60 S=T/60 : if S<>SS then gosub drawClock : SS=S
70 A=B : B=peek(14) : A=B-A
80 if A<0 then A=A+256
90 T=T+A : goto 30 'use T=T+2500 for debugging

drawClock:
    gosub drawDigits
    gosub drawSeconds
    gosub drawMinutes
    gosub drawHours
return

drawDigits:
    poke 162,&h00
    poke 163,&h0C : at 57,82 : print H/10;H%10;":";M/10;M%10;":";S/10;S%10;
    poke 162,BKCOLOUR
return

drawSeconds:
    gosub eraseSecondHand
    i=SS : gosub drawTicks
    gosub drawSecondHand
return

drawMinutes:
    if M<>MM 
        gosub eraseMinuteHand
        MM=M : Md12=M/12
        if Md12<>MMd12
            gosub eraseHourHand
            MMd12=Md12
        endif
    endif
    gosub drawMinuteHand
return

drawHours:
    if H<>HH
        HH=H : Hm12=H%12
    endif
    Hx5=(Hm12 LSL 2)+Hm12
    gosub drawHourHand
return

drawTicks:
    x = peek(SECSX + i)
    y = peek(SECSY + i) + 8
    poke (y LSL 8) + x, &h3F
return


drawDial:
    ox=CENTERX : oy=CENTERY + 8
    cr=44 : cc=&h00 : gosub jitterCircle
    cr=46 : cc=&h00 : gosub jitterCircle
    cr=48 : cc=&h01 : gosub jitterCircle
    cr=52 : cc=&h03 : gosub jitterCircle
    cr=50 : cc=&h02 : gosub jitterCircle
    
    for i=0 to TICKS-1
        gosub drawTicks
    next i
return

eraseSecondHand:
    poke 163,BKCOLOUR : line CENTERX,CENTERY, peek(SECSX + SS),peek(SECSY + SS)
return
    
drawSecondHand:
    poke 163,&h3F : line CENTERX,CENTERY, peek(SECSX + S),peek(SECSY + S)
return

eraseMinuteHand:
    poke 163,BKCOLOUR : line CENTERX,CENTERY, peek(MINSX + MM),peek(MINSY + MM)
return

drawMinuteHand:
    poke 163,&h2A : line CENTERX,CENTERY, peek(MINSX + M),peek(MINSY + M)
return

eraseHourHand:
    poke 163,BKCOLOUR: line CENTERX,CENTERY, peek(HOURX + Hx5 + MMd12),peek(HOURY + Hx5 + MMd12)
return

drawHourHand:
    poke 163,&h15 : line CENTERX,CENTERY, peek(HOURX + Hx5 + Md12),peek(HOURY + Hx5 + Md12)
return

jitterCircle:
    for oxx=ox-1 to ox+1
        for oyy=oy-1 to oy+1
            poke 163,cc
            circle oxx, oyy, cr
        next oyy
    next oxx
return

User avatar
Marco
Posts: 24
Joined: 08 Dec 2019, 15:33

Re: I guess I just build a...screensaver?

Post by Marco » 19 Jan 2020, 10:38

at67 wrote:
19 Jan 2020, 07:59
I was inspired by all the clocks being posted, so I thought I'd add my own to this thread; the timing code for this clock also originated from this thread.
Very nice, looks good!

I had already noted that it makes sense to add 2 seconds to S as entered by the user, which is about the time it takes for the initial setup before, literally, the clock starts ticking. As it is now, the clock is 2 seconds off from the start. Just a small detail ;)

User avatar
marcelk
Posts: 490
Joined: 13 May 2018, 08:26

Re: I guess I just build a...screensaver?

Post by marcelk » 19 Jan 2020, 22:19

Marco wrote:
19 Jan 2020, 10:38
As it is now, the clock is 2 seconds off from the start. Just a small detail ;)
In the example code, I mitigate this delay by fetching the frame counter immediately following the input:

Code: Select all

10 input H,M,S:B=peek(14)
This way, the mismatch is reduced to the time it takes for parsing the last user input expression(*), and the dispatch of the peek statement. These should take much less than a second, but I haven't profiled.

(*)There is a funny story related to what INPUT really does: INPUT treats the typed user text as an expression. So you can type in numbers, but also calculations (1+2*3), including variable names etcetera. Any expression that is valid in an assignment goes with INPUT as well. At VCF Berlin one visitor toggled in a nice "Guess My Number" program. The program picked a secret number and asks for guesses. It then only told you if your guess was too high or too low (or correct). The secret number was stored in a variable of course. So you could beat the game at the first turn by typing the name, N, of that variable! [As far as I can tell, only Sinclair BASIC has this same "backdoor".]

Post Reply