Saturday, October 4, 2014

Show 147 - Jeff Ledger and the Micromite Companion

For show 147, I speak with Jeff Ledger, longtime Commodore and Propeller enthusiast, and creator of the Micromite Companion (MMC). The MMC is a single-board kit computer with two brains - a Micromite, and a Propeller. Jeff explains how these two come together to make a computer styled in the retro spirit, but with modern day parts. (Old skool computing, modern day fun?)

Links:

Early Digital Research CP/M Source Code (Computer History Museum)
The PropellerPowered website
The PropellerPowered Forums
Direct link to the MP3

This episode is Copyright 2014 by Earl Evans.
Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.

Sunday, August 3, 2014

Getting REAL with Retrochallenge

23:59 GMT is staring me in the face, daring me to get my Fortran program done in time. But alas, it wasn't meant to be.

I'm throwing in the towel, with two of nine programs done. As explained in my last post, I'm actually really happy with the time I spent. It taught me a lot and I intend to keep going with programming these platforms. The effort has, though, reminded me of Murphy's Law...and to never enumerate your juvenile poultry until the proper process of incubation has thoroughly materialized.

I was so, so close on the Fortran program. Ironically, the roadblock is a problem I thought I'd solved early on - random numbers.

My PDP-11 is a lot of fun. But it has a shortcoming that really got in my way for Retrochallenge - no Floating Point Processor. The 11/23+ CPU card (KDF11-BA) can have floating point, or not. On mine, it's not. Therefore, to use Fortran, I have to ensure I never, ever use floating point in the program, and I have to use a special library in the LINK phase to even get the programs to run. Fortran really likes floating point, and is somewhat loathe to run without it. I guess that makes sense for a language derived from "Formula Translating System".

The pseudo-random number generators out there for Fortran, including the one that comes with the DEC compiler, also typically want floating point. I ain't got none. So there you have it.

I thought I'd found a good INTEGER based algorithm, but it turned out to be so darned complicated to use that there's no way I'd implement it in time to save the day.

Workflows


So instead, here is my promised post about modern=>retro workflows.

I've learned so much during this RC that it's tough to write it all down. However, here are some workflow highlights from my programming efforts.

I wanted to write my source code on a modern PC because I'm lazy. There, I said it. I like (no, love) modern programmer's text editors. I've always tried to be the first on my block with cool, syntax-highlighting, macro-enabled editors. Brief was one of my favorites of all time.

For this excursion, I chose Jedit. I'm not really a fan of Java client-side applications, but Jedit is an exception. It's clean, feature-filled, and supports Forth and Fortran natively. I used it for both the Atari 800/X-Forth and PDP-11/Fortran projects.

But, how to get the source code to the target platforms? Easy! Well, heh, I probably spent more time figuring this stuff out than it would have taken me to edit on the local platforms. But hey, take time to save time, I always say. Actually, maybe that's the first time I've said that. But I'll always say it from now on.

For Atari 800/X-Forth:

  • I used aspeqt (an open-source Atari peripheral emulator) to simulate disk drives for the Atari, connected via an SIO2PC adapter. In aspeqt, I mounted a Windows folder as a virtual drive. To the Atari, the folder looked like a disk drive, so I could transparently load things from it.
  • Alas, the Atari uses ATASCII, and Jedit naturally edits in ASCII. So when I wanted to make the Forth source file available to the Atari, I had to convert it. For this, I used a cute little utility called Dratex. It's simple, but fast, and works easily.
  • Once the source file was in ATASCII format, I loaded it from the virtual folder (that the Atari saw as drive D2), and voila!

 For PDP-11/Fortran 77:

  • E11 is a most excellent PDP-11 emulator, and is free for personal/hobbyist use. I admit that during most of the Fortran development, I used the emulator. (Keeping a real PDP-11 with dual RL02 drives running for extended periods makes the electric company happy, but makes my pocketbook sad.)
  • E11 can map the main "SLU" console (and, actually, many of the emulated terminal interfaces) to a Telnet session. From there, I connected to my emulated PDP-11 with the Tera Term terminal emulator program.
  • Once code in Jedit was ready for a test, I would open a Tera Term session to E11, and simply paste the code into the terminal window. Tera Term has a configurable delay in between lines when pasting, and this helps the host machine to keep up. Once the source code was pasted, I would save it, compile it, and test!

Thanks as always to the folks running the Retrochallenge for a fun outing. I just love it. I hope to participate every time, till smoking capacitors do us part.

See you next time,

- Earl

Friday, August 1, 2014

Craps program for the Epson PX-8 in BASIC

Without any ado, here's the Retrochallenge 2014 SC craps program that I wrote for the Epson PX-8 in BASIC.

First, a video demonstration:



Now, the source code:

1000 REM Remove menu bar and set text screen mode
1010 SCREEN 0,0,0
1020 CLS
1030 PRINT "Welcome to RC-CRAPS! What is your name: ";
1040 LINE INPUT N$
1050 REM Get random seed from 614.4 KHz clock at Z80 I/O reg 0 and 1
1060 R=INP(1)*256+INP(0)
1070 R=R-32768!
1080 RANDOMIZE R
1090 PRINT "Hello ";N$;"! Would you like instructions? (Y/N): ";
1100 YN$=INPUT$(1)
1110 IF YN$="y" OR YN$="Y" THEN GOSUB 1630
1120 BR%=1000
1130 CLS
1140 PRINT "You have ";
1150 PRINT USING "$#####";BR%;
1160 PRINT " in the bank."
1170 PRINT "Your bet? (1-50 dollars, or Q to quit: ";
1180 LINE INPUT BT$
1190 IF LEFT$(BT$,1) = "q" OR LEFT$(BT$,1) = "Q" THEN 1870
1200 BT% = VAL(BT$)
1210 IF BT%<1 OR BT%>50 THEN 1170
1220 PRINT "(P)ASS/win or (D)ON'T PASS/lose?: ";
1230 PD$=INPUT$(1)
1240 PRINT
1250 IF INSTR("PpDd",PD$)=0 THEN 1220
1260 PRINT
1270 GOSUB 1800
1280 IF (P%=7) OR (P%=11) THEN 1320
1290 IF (P%=2) OR (P%=3) THEN 1370
1300 IF (P%=12) THEN 1420
1310 GOTO 1450
1320 REM win
1330 PRINT "You win!"
1340 IF PD$="p" OR PD$="P" THEN PRINT "You bet you would win. Good job!":BR%=BR%+BT%
1350 IF PD$="d" OR PD$="D" THEN PRINT "You bet against yourself. OOPS!":BR%=BR%-BT%
1360 GOTO 1140
1370 REM lose
1380 PRINT "You lose!"
1390 IF PD$="p" OR PD$="P" THEN PRINT "You bet you would win. OOPS!":BR%=BR%-BT%
1400 IF PD$="d" OR PD$="D" THEN PRINT "You bet against yourself. Good job!":BR%=BR%+BT%
1410 GOTO 1140
1420 REM push
1430 PRINT "You rolled boxcars (12). No win or loss."
1440 GOTO 1140
1450 REM continue point roll
1460 PT%=P%
1470 PRINT "Your point is";PT%
1480 GOSUB 1800
1490 IF P% = 7 THEN 1530
1500 IF PT% = P% THEN 1570
1510 PRINT "Keep playing!"
1520 GOTO 1470
1530 PRINT "You lose"
1540 IF PD$="p" OR PD$="P" THEN PRINT "You bet you would win. OOPS!":BR%=BR%-BT%
1550 IF PD$="d" OR PD$="D" THEN PRINT "You bet against yourself. Good job!":BR%=BR%+BT%
1560 GOTO 1140
1570 PRINT "You win"
1580 IF PD$="p" OR PD$="P" THEN PRINT "You bet you would win. Good job!":BR%=BR%+BT%
1590 IF PD$="d" OR PD$="D" THEN PRINT "You bet against yourself. OOPS!":BR%=BR%-BT%
1600 GOTO 1140
1610 GOTO 1140
1620 END
1630 CLS
1640 PRINT "RC-CRAPS rules:"
1650 PRINT "1. The bank gives you an initial bankroll. Use it wisely!"
1660 PRINT "2. Make a bet on PASS (win) or DON'T PASS (loss). Max bet is $50."
1670 PRINT "3. Make an initial roll of the dice:"
1680 PRINT "   7 or 11 wins. 2 or 3 loses. 12 is a 'do over' (no win or loss)"
1690 PRINT
1700 PRINT "Press any key for next page...";
1710 NT$=INPUT$(1)
1720 CLS
1730 PRINT "4. Any other roll sets your 'point'. Rolling this again is your goal."
1740 PRINT "5. Roll again until you roll your point for a win, or 7 for a loss."
1750 PRINT "6. Good luck! Remember, what happens in RC-CRAPS, stays in RC-CRAPS!"
1760 PRINT
1770 PRINT "Press any key to continue...";
1780 NT$=INPUT$(1)
1790 RETURN
1800 REM Two dice at random
1810 PRINT "Press any key to roll the dice!"
1820 NT$=INPUT$(1):PRINT
1830 D1%=INT(RND(1)*6)+1:D2%=INT(RND(1)*6)+1
1840 P%=D1%+D2%
1850 PRINT "You rolled a";D1%;"and a";D2%;"for a";P%;"!"
1860 RETURN
1870 CLS
1880 PRINT "Thanks for playing RC-CRAPS!"
1890 PRINT "You left with $";BR%;"in the bank!"
1900 END

Travails and Triumphs

I started this year's summer Retrochallenge with a simple goal - develop three games (craps, roulette, and 21) for three different computer/language combos:

  • PDP-11 with Fortran 77
  • Epson PX-8 with BASIC
  • Atari 800 with Forth

The logic of the games is not tough, especially if you simplify it a bit for computer play. However, I bumped into enough issues that in the end, I will likely only have 1 game (craps) for each of the platforms.

On the face of it, this might seem like a disappointment, but I'm actually pretty pleased. This Retrochallenge got me programming again, revived my love for an old friend (Fortran), and taught me more things about Forth, a language and programming paradigm which I'm really coming to respect.

We've been given a couple of extra days to finish our RC entries, so I'll probably have the Fortran game ready by then. For now, I wanted to share some things I've learned (or re-learned) along the way about writing code for my target platforms.

TL;DR Version



I'm covering quite a bit of ground below. If you're tight on time, here is the summary:

  • PX-8: It's tough in BASIC to manage a big program, mostly due to line numbers and the lack of named subroutines. Also, an 8-line display on your target platform makes this even more challenging.
  • Atari/X-Forth: Lack of complete documentation and mediocre text I/O meant I had to do some "roll your own" development.
  • PDP-11/Fortran 77: Printing control characters for terminal control (e.g., clearing the screen) turned out to be tough to figure out. Solved this by creating a BYTE array, putting the control codes into the array with a DATA statement, then calling an OS subroutine for raw output.

Below - the gory details...

If it's BASIC, why is it hard

 

First up, BASIC on the PX-8.

Programming in BASIC is often a shoot from the hip exercise. The interpretive, interactive environment lends itself to this. But the freedom isn't without cost.

For instance - line numbers are, quite frankly, a drag. I'm pretty sure the PX-8 BASIC is a Microsoft derivative (it has that style to it), so it's a serviceable language, thankfully with a RENUMBER statement. You missed some code and need to insert it? No problem, go for it (assuming you left space - didn't you?), and then use RENUMBER to smooth things out again. For the uninitiated, the RENUMBER command takes a BASIC program with erratic line numbers like this:

10 PRINT
12 PRINT "Oops, I left this line out"
15 PRINT "And this one, too!"
20 GOTO 12

and makes it look like this:


10 PRINT
20 PRINT "Oops, I left this line out"
30 PRINT "And this one, too!"
40 GOTO 20

Notice that along with renumbering the program to even spaces of 10, it also fixes things like the GOTO statement, putting the new correct line number into the code.

This is all well and good, but it can wreak havoc on your code if you've set aside chunks of line numbers for subroutines. For example:

10 PRINT "I'll call a subroutine at line 1000".
20 GOSUB 1000
30 PRINT "I'm back from the subroutine."
40 GOTO 9999 : REM END
1000 PRINT "I'm in the subroutine at line 1000."
1010 RETURN
9999 END

If I RENUMBER this program, the subroutine's nice line number separation from the rest of the program will be lost.

If I could call a subroutine by name or label instead of by line number, then everything would be awesome! But I can't. So as a BASIC program gets bigger, it's harder and harder to manage.

To make things even more spicy, there is only an 8 line screen on the PX-8. You can't see many line numbers all at once, making it more difficult to get the big picture of your program.

Getting past these issues can be tough. Here are some possible solutions:

  • Plan out your subroutines in advance. Set aside line number chunks for them. Make sure you have plenty of spacing so you don't wander into your subroutines from your main code. Don't RENUMBER until the very end, or at least, until you're tired of knowing where your subroutines are :-)
  • Use ranges in the LIST command to help see small chunks of the code at one time without it scrolling up the screen.
  • Keep a notebook beside you (or a Notepad window) to scribble some notes on where things are in your program, what variables you use for what purpose, etc.)

Bringing Forth The Text


The Atari 800 has several varieties of the Forth environment. For Retrochallenge, after some experimentation, I chose X-Forth, for three reasons:

  • X-Forth uses standard ASCII (or in this case, ATASCII) files for source code, rather than relying on the more primitive and frustrating "screen" concept of older Forths. This makes it possible to edit your Forth words in a nice text editor, then load them into the Forth environment.
  • Also, X-Forth is sort of a hybrid between the older figForth and the more modern ANSI Forth. While not all of the newer standard is supported, there is enough there to make life more pleasant.
  • It's GPL licensed.

Right off the bat, the first challenge with X-Forth was documentation. The web page notes "more detailed tutorial to come!", but for now, there's an amount of hunt and peck required to figure things out.

This hit me first when trying to figure out acquiring and processing text (e.g., the user's name). Older Forth systems seem a bit lacking when it comes to text I/O and character string manipulation. Compared to BASIC's INPUT, LINE INPUT, MID$, CHR$, etc., the offerings in Forth are pretty pedestrian. That's the bad news. The good news is, much of Forth is pretty close to the metal, and the location of things in memory is quite visible to the programmer. Don't have a word you need? Just write it! And that's what I did.

The EXPECT word in Forth prompts the user for input. You provide EXPECT with a maximum input length and memory location, and EXPECT puts the user input into that memory area, followed by one or more null (0) bytes. Let's say you type HELLO [return]. In memory, at the location you specified, will go 6 bytes - the ASCII for H E L L O, and a zero byte. In X-Forth's EXPECT word, there is no way to figure out how many characters the user typed. This turns out to be a problem, as we'll see.

The TYPE word in Forth prints text of a specified length from a specified memory address. If EXPECT is like INPUT, then TYPE is sort of like PRINT. However, TYPE doesn't know about the null byte at the end - it just outputs the length of text you give it. This means if you set aside 25 characters for the user's name, and the user only typed 7 characters in the EXPECT statement (above), then TYPE will output not just the name, but also the rest of the 25 characters, most of which will be garbage.

Note that EXPECT guarantees a zero byte at the end of the input. So, I wrote a PRINT word in Forth, which simply starts at the provided memory address and prints the bytes one at a time until it encounters the zero terminator. Here's the source code:

: PRINT ( addr -- )
    ( Prints chars starting at )
    ( addr until reaches null )
    ( Better than TYPE, which )
    ( outputs the nulls and )
    ( other junk in the string )
    ( area. )
    BEGIN DUP C@ 0= IF
        DROP 1
    ELSE
        DUP C@ EMIT 1 + 0 THEN
    UNTIL
    ;


If in the future I want to use Forth for some more advanced programming, it would benefit me to write some words (perhaps in assembly) to do more robust I/O, conversion, and substring processing.

Also, Forth heavily uses the "stack", a LIFO area of memory for storing intermediate values as your program executes. To be true to the language, I used the stack as much as possible, rather than punting and using defined variables (which Forth supports). To make sure the "stack effect" of my words was as intended, I would open a Notepad and run through the code, updating a pretend stack as I went. Doing this bench test exercise revealed subtle bugs in my code and was quite enlightening.

CLEARing up Fortran


For the PDP-11 version of craps, I'm assuming the user has a VT100/ANSI-compatible terminal. (Oh no! Don't make your code platform-dependent!) So, I wanted to print out some control codes to clear the screen, home the cursor, etc. This, too, was a challenge.

First, Fortran 77 has a function to convert an integer to a character: CHAR(int). However, that function isn't supported in PDP-11 Fortran 77. Bummer! How do I get a non-printable value into a character string so I can print it?

Answer: I don't. The easiest way was to populate a BYTE array with the correct codes, then call a SYSLIB function from Fortran to write the values. Here's some sample source code:

        PROGRAM CLSCR
        BYTE CLS(5)
        BYTE HOME(4)
        DATA CLS/27,'[','2','J',128/
        DATA HOME/27,'[','H',128/
        CALL PRINT(CLS)
        CALL PRINT(HOME)
        END


Note the use of the DATA statement - this takes values and crams them into variables. It gave me a way to get non-printable values (like ASCII 27 for ESC) into a BYTE array.

Also, note the CALL PRINT statements. PRINT is a function in the system library that takes an address as an argument, then prints characters from that address forward until it reaches a 0 or 128 byte. A zero termination will result in a CR/LF being printed following the characters, where a 128 will result in no line termination (this is what I wanted).

Interestingly, the PRINT subroutine is very similar to the operation of the PRINT word I created for X-Forth!

That's all that I'll bore you with for now. Suffice it to say, I'm having a blast. It's very likely I'll keep going with these projects once the Retrochallenge is over - it will be fun to leverage the knowledge I've accumulated.

I have two more blog posts to come - one on the workflows I set up to program on a PC and move the code to the target platforms, and finally, a blog post (hopefully) on the final craps game in Fortran. Stay tuned...

Monday, July 28, 2014

RC Craps for Atari 800/X-Forth

To recap - for this year's summer Retrochallenge, I want to write 9 games - 3 each on 3 different platform/language combinations. Two of the nine are complete! And with a recent time extension granted by our illustrious organizer, I might just get all 9 done.

This blog post documents Craps on the Atari 800, using the X-Forth language.

X-Forth is a derivative of figForth, but intends to be more compatible with the ANSI Forth standard. However, it's not completely ANSI-compatible yet. This means it can take some digging and trial and error to figure out which Forth words are implemented, and whether their behavior is figForth or ANSI. While there is no manual, there are a couple of sample programs that helped me riddle this all out.

I'd like to go more into details on some of the things I learned, but while I go write the deep-dive, here's a video demo of the program, and the source code. Note that neither are incredibly impressive. There is no color, sound or graphics - weird for a game on the Atari. Right now, it's all text. However, when you look at how a Forth program is unpacked - words within words, culminating in atomic definitions - it won't be hard to modify the program in a modular fashion to get some more exciting UI implemented.

Here's the video:


And here's the source code:

( RC-CRAPS for Atari 800/X-Forth )
( Version 0.1 )
( Licensed under Creative Commons )
( BY-NC-SA 3.0 )
( By Earl Evans )
( www.retrobits.com )
( for Retrochallenge SC 2014! )

( TO-DO: )
( Add graphics and sound! )
( Find better psuedo-random )
( number generator. )
( Make input routines more )
( bulletproof, perhaps with )
( assembly-language words. )

125 EMIT ( Clear screen )

CR
." RC-CRAPS for Atari 800/X-Forth." CR
." Version 0.1"
." Licensed under Creative Commons" CR
." creativecommons.org/licenses/" CR
."      by-nc-sa/3.0/" CR CR
." RANDOM word courtesy of" CR
." James M. Reneau, Ph.D. via Creative" CR
." Commons at:" CR
." www.renejm.net/" CR
."      6502FIGForthHandyRandom" CR

CR
." Initializing variables"

1000 VARIABLE BANKROLL
0 VARIABLE BET
21 CONSTANT NAME-LEN
-1 VARIABLE PLAYER NAME-LEN ALLOT
PLAYER NAME-LEN 32 FILL
0 VARIABLE POINT
0 VARIABLE SEEDHIGH
0 VARIABLE SEEDLOW
0 VARIABLE RANDOMSEED
0 VARIABLE RANDOMKICK

CR
." Loading Utility Functions"

: WAITKEY ( -- )
    ( Waits for a keypress )
    KEY DROP ;

: CLS ( -- )
    ( Clears the Atari screen )
    125 EMIT ;

: PRINT ( addr -- )
    ( Prints chars starting at )
    ( addr until reaches null )
    ( Better than TYPE, which )
    ( outputs the nulls and )
    ( other junk in the string )
    ( area. )
    BEGIN DUP C@ 0= IF
        DROP 1
    ELSE
        DUP C@ EMIT 1 + 0 THEN
    UNTIL
    ;

: POKEY-RND ( -- n )
    ( Gets a psuedo random value )
    ( from the Atari 800 POKEY )
    ( chip, leaves on stack. )
    53770 C@
    ;

CR
." Loading RANDOM"

: RANDOM ( M -- N )
  ( RETURN A RANDOM INTEGER FROM 0 TO M-1 )
  ( NOT VERY GOOD AND WILL CYCLE FOR VALUES )
  (   - WORKS OK WITH 100, AND 10)
  ( BY JAMES RENEAU - 2012-05-12 )
  (   - LICENSED UNDER CREATIVE COMMONS A-NC-SA )
  RANDOMSEED @
  67 * 103 + ABS 16383 MOD
  DUP RANDOMSEED !
  RANDOMKICK @
  101 + ABS 16383 MOD
  DUP RANDOMKICK !
  + SWAP MOD
;

CR
." Loading ASK-NAME"

: ASK-NAME ( -- )
    ( Asks for player name, places in PLAYER )
    CR ." Please enter your name: "
    PLAYER NAME-LEN EXPECT
    ;

CR
." Loading ASK-YN"

: ASK-YN ( -- n )
    ( Asks yes or no, if y or Y leaves 1 on
    ( the stack, 0 otherwise )
    KEY DUP 89 = SWAP 121 = OR IF 1 ELSE 0 THEN
    ;

CR
." Loading ASK-BET"

: ASK-BET ( -- n )
    ( Asks for a bet amount 1 - 50, leaves )
    ( on stack )
    CR ." Enter bet: "
    QUERY BL WORD HERE NUMBER DROP SWAP DROP
    BET !
    ;

CR
." Loading GET-RN"

: GET-RN ( -- n )
    ( Leaves random number 1 to 6 on stack )
    6 RANDOM 1 +
    ;

CR
." Loading SHOW-HELP"

: SHOW-HELP ( -- )
    ( Displays help for playing the game )
    CLS
    ." Make a bet $1-$50." CR
    ." Throw the first roll." CR
    ." 7 or 11 wins. 2 or 3 loses." CR
    ." 12 pushes." CR CR
    ." Any other roll becomes your 'point'." CR
    ." Roll until you get your point (win)" CR
    ." or get a 7 (lose)." CR CR
    ." Press any key to continue..."
    WAITKEY
    CLS
    ;

CR
." Loading INTRO"

: INTRO ( -- )
    ( Provides an intro to the game )
    CLS CR
    ." Welcome to RC-CRAPS version 0.1!" CR
    ." Would you like instructions (Y/N)? "
    ASK-YN IF SHOW-HELP THEN
    ;

CR
." Loading ROLL"

: ROLL ( -- n )
    ( One roll of the dice, leaves )
    ( dice total on stack )
    CR
    ." Press any key to roll dice..."
    WAITKEY
    GET-RN GET-RN
    CR ." You rolled " DUP . ." + " SWAP DUP . ." = " + DUP .
    POINT @ DUP 0= IF
        DROP
    ELSE
        CR ." You are trying for a " .
    THEN
    ;

CR
." Loading CHECK-WIN"

: CHECK-WIN ( n -- n )
    ( Expects total of latest roll )
    ( on the stack. Leaves a code )
    ( on the stack of 0=roll again, )
    ( 1=win, 2=loss, 3=tie. )
    POINT @ 0= IF ( First roll )
        DUP 7 = SWAP DUP 11 = ROT OR IF ( win )
            DROP 1
        ELSE DUP 2 = SWAP DUP 3 = ROT OR IF ( loss )
            DROP 2
        ELSE DUP 12 = IF ( tie )
            DROP 3
        ELSE POINT ! 0 THEN THEN THEN ( roll again )
    ELSE ( not the first roll )
        DUP POINT @ = IF ( win )
            DROP 1
        ELSE DUP 7 = IF ( loss )
            DROP 2
        ELSE DROP 0 THEN THEN ( roll again )
    THEN
    ;
          

CR
." Loading PLAY-ROUND"

: PLAY-ROUND ( -- n )
    ( Plays a round of the game )
    ( Leaves 1 on the stack for )
    ( another round, or 0 on the )
    ( stack to quit. )
    ASK-BET
    0 POINT ! ( Set initial "point" to zero )
    BEGIN
        ROLL CHECK-WIN
        DUP 0= IF
        CR ." Roll again!"
        ELSE DUP 1 = IF
            CR ." You win!"
            BANKROLL @ BET @ + BANKROLL !
        ELSE DUP 2 = IF
            CR ." You lose!"
            BANKROLL @ BET @ - BANKROLL !
        ELSE DUP 3 = IF
            CR ." Boxcars - no win or loss!"
        THEN THEN THEN THEN
    UNTIL
    CR ." Your bankroll is: $" BANKROLL @ .
    CR ." Play again (Y/N)? "
    ASK-YN 0= IF 1 ELSE 0 THEN
    ;

CR
." Loading PLAY-GAME"

: PLAY-GAME ( -- )
    ( Plays the entire game, exits )
    ( to OK when done )
    POKEY-RND SEEDHIGH !
    INTRO
    POKEY-RND SEEDLOW !
    ASK-NAME
    POKEY-RND RANDOMKICK ! SEEDHIGH @ SEEDLOW @ * RANDOMSEED !
    BEGIN PLAY-ROUND UNTIL
    CR ." Thanks, " PLAYER PRINT
    CR ." for playing RC-CRAPS!"
    CR ." Your final bankroll was: $" BANKROLL @ . CR CR
    ;

  CR
." RC-CRAPS load complete!" CR
." Type PLAY-GAME [return] to play." CR
CR

 

Monday, July 7, 2014

Well, that's random

Why random numbers?

In order to make a fun game of chance, you need random numbers. Dice rolls, card shuffles, or a roulette wheel are no fun if they're predictable.

Mommy, where do randoms come from?

There are mathematical formulas that can provide a stream of seemingly random numbers. These are called pseudo-random number generators, or PRNGs. You start off a PRNG with a truly random "seed" number, from which the PRNG derives a sequence of numbers. Although this sequence gives the appearance of being random, it is completely predictable - each time the PRNG is started with the same seed, it will produce the same resulting sequence of numbers.

It all starts with a seed...

Thus, the initial seed for a PRNG must be truly as random as possible. Computers are great at many things, but being unpredictable isn't one of them - so to get some randomness (or "entropy" as it's called in Information Theory), we need the human touch. In this case, quite literally.

For each of the platforms I'm using for my Retrochallenge entry (Atari 800, Epson PX-8, PDP-11/23+), I wanted to find a suitably fast-changing clock or register that could be used as a seed. To get this value at a random time, you ask the user to press a key to perform some action (e.g., get their initial bankroll - thanks Ian!). After their key press, a value is sampled from the high-speed clock and used as the seed value. As long as that clock is fast enough, then voila, there's your random number seed.

Harvesting the seed

The PX-8 was the most ideal. There is a 614.4 KHz clock that updates a rolling 16-bit register, the value of which can be read with from the BASIC "INP" function. This function reads values from Z80 I/O registers. Since 614.4 KHz will update the value more than 600,000 times per second, it is unlikely in the extreme that a user could predict when to press a key even if they wanted to.

The Atari 800 and PDP-11 were not as good, but still passable. Each have memory-mapped registers that update at 60 Hz. Typically called "ticks", these are internal clocks for the computer's functions. In the Atari, it's part of the ANTIC video chip, and for the PDP-11, it's a component of the line clock. Reading the value of minutes, seconds and ticks after a user key press should provide an ample source of randomness. On the PDP-11, there is a system library function, callable from Fortran, called "GTIM", that provides the current count of ticks. On the Atari, in Forth, the value can be read with "C@", the Forth equivalent of a one-byte PEEK function.

Now we have a seed, how 'bout some numbers?

PX-8 BASIC also has a PRNG built-in. You seed it with the "RANDOMIZE" statement, then use the RND function to get random numbers (fractions 0<=x<1). For Forth and Fortran, I need to provide the PRNGs; however, there are happily some good ones online.

So hey, pal, no counting cards in my RC casino!

Monday, June 30, 2014

Retrochallenge 2014 - Get Set

Now it's 11:37 PM Pacific Time, and the excitement is building on the West Coast of the US, where Retrochallenge 2014 is about to commence!

The computers are ready. But, alone, are they enough to stand up to the tests of character and strength that await them? Take heart, there is help from the sidelines.

The Raspberry Pi

Serving once again as a trusty virtual disk drive for the PX-8, the Raspberry Pi is ready to retrieve and store BASIC programs:

The Raspberry Pi wishes it was a PX-8

 SIO2PC

Hey, the PX-8 isn't the only kid that gets to play with a virtual disk drive. The SIO2PC, a device which conveniently hooks an Atari 8-bit system to a modern PC, festooned with blinky lights, is standing by:

Tomorrow, these LEDs shall blink


The Manuals

Books are the stuff of life, and the reference materials are also waiting in their corner. Here are, in no particular order, the Atari 800 "Purple Book", the KED editor (PDP-11) quick reference, and the Epson PX-8 BASIC Reference Guide:





Notice that nothing is powered on... No, my friends, that celebration comes in just a few minutes, as these digital animals are let out of their time-boxed cage! BWA HA HA HA HA HA.

Ha.

See you in the morning!