Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Tuesday, May 12, 2020

TuT-TuT on the Jupiter Ace: Part 2

Leave a Comment
Welcome to the second and final article on Porting the ZX81 and ZX Spectrum Game Tut-Tut to the Jupiter Ace:  This time George Beckett takes us through the gritty details of programming in Forth, leaving few treasures undiscovered in the process.

If you missed Part 1, be sure to read up before continuing below.

Jupiter Ace Game: TuT-TuT
Archaeology is just as fun in the Jupiter Ace version of TuT-TuT

The Forth Tut-Tut is Ace

Previously, I told you a little about what motivated me to port Tut-tut to the Ace and promised to describe how the game is written. However, before we get on to the game, I should explain a bit about the Forth language, which was developed in the late 1960s for the control system of a telescope. Since then, its scope has grown, being used for a range of scientific and other serious applications in the 1980s and 90s. Even today, Forth lives on and can be found at the heart of various embedded systems.

The basis of any Forth system is a dictionary of words that encapsulate the functions of the machine. There are simple words for manipulating data (bytes of memory) or performing rudimentary arithmetic, as well as more complex words for printing on the screen, making sound, and saving or loading files. The programmer combines these words together to make new words, continuing to build up the functionality until the final program is represented by one top-level word. In that way, writing a Forth program is a bottom-up process, starting at the lowest-level elements and working up. In contrast, the design of a Forth program is a top-down process. You iteratively break the program’s function down into smaller and smaller elements until you reach the level of the built-in words.

The overall structure of Tut-tut is relatively simple, with the player completing a sequence of levels, which involves collecting objects, unlocking doors, dodging mummies and finding the exit. The bulk of the logic is in how each level plays: how the player navigates the maze, collects objects and is chased by the mummies.

In Rabbit Run, the core of the game was a loop in which the player first had the opportunity to make their move and then each possible outcome was tested for—in that case, eating carrots, falling down mole holes, or being caught by a fox. At the end of the loop, the fox made their move and it was back to the beginning.  The same game loop could be used for Tut-tut, though replacing foxes by mummies, carrots by gems, and so on.

For both Rabbit Run and Tut-tut, the game state is held in the display buffer. For example, to check the location the player wants to move to, you look up what is there in the display buffer. This is useful for saving both memory and time. In Tut-tut (and Rabbit Run) on the ZX Spectrum, objects are distinguished by colour. This makes it very fast to check what an object is by looking up its colour using the ATTRIB function (or PEEK-ing the right location in the display buffer). The Ace has a monochrome display, so this approach does not carry over immediately. However, the Ace display is very simple, formed from a two-dimensional array of characters, just like the attributes on the Spectrum is as two-dimensional array of colour values, and so it should be just as quick to look up the character code in the Ace’s screen memory as it is to look up a colour on the Spectrum. Thus, a crucial word to define in the Forth version of Tut-tut is SCREEN, which retrieves the character at a particular location in the level:

    : SCREEN ( X Y -- CHAR )
        SWAP 32 * + ( 32 CHARS PER ROW )
        9216 +  ( START OF SCREEN MEMORY )
        C@
    ;


For player movement, I decided to use the Rabbit Run core as the starting point for Tut-tut, though there were some differences. Surprisingly, Ace Forth does not include a CASE statement (which would allow a number of different program paths to be followed depending on the value of an input field—such as, a key-press or the object at a location). For example:

    ( X Y -- NEW_X NEW_Y )
    INKEY
    CASE
        ASCII P OF 1+ ENDOF            ( RIGHT )
        ASCII O OF 1- ENDOF            ( LEFT )
        ASCII A OF SWAP 1+ SWAP ENDOF  ( DOWN )
        ASCII Q OF SWAP 1- SWAP ENDOF  ( UP )
    ENDCASE


Sadly, there is no CASE word in Ace Forth, so the above code would not work. I investigated options to implement a CASE structure (a big selling point of Forth is the ability to add your own commands). However, while I found a few candidates on the Internet and in 1980’s magazines, none of them proved to be very effective nor reliable. I therefore decided to use brute force, writing multiple IF statements. This is an ugly approach though has proved adequate for Tut-tut.

Another weakness of FORTH (and, to a lesser extent, BASIC) is that it is difficult to define large input datasets—in this case, I was thinking about how to get the data for game levels into the game. In the BASIC version, David used DATA statements, RESTORE-ing to the correct line and then READ-ing the data for each level. However, FORTH does not have a DATA statement and the method for entering data described in the Ace manual is very laborious. In the end, I decided to bypass FORTH and to define the level data in binary code blocks to be loaded into memory inside suitable ALLOT’ed arrays. For example:

Tut-tut UDGs and character codes on the Ace.
    ( RESERVES 4,000 BYTES FOR LEVEL DATA )
    CREATE GAMELEVELS 4000 ALLOT
    GAMELEVELS 4000 BLOAD tut-tut.lev


There is a reasonable amount of logic involved in drawing each level in Tut-tut, as a level is stored in a compressed form to save memory. In the original ZX Spectrum version, a technique based on trios of character cells was use to reduce the memory requirements of a level by a factor of three. However, since finishing the BASIC version, David updated the compression strategy to one inspired by the GIF image format and described on his website [link]. This gives better compression and has been used in the ZX81 version to allow more levels to be added. Given that David planned to back-port his new compression algorithm to the ZX Spectrum, I decided to adopt the GIF-inspired approach for the Ace.

On the Ace version of Tut-tut, the logic for drawing a level is encoded in a word called DRAWLEVEL. Using Forth is an advantage here, as it excels at integer arithmetic, which is fundamental to how the encoding works. The only complication, for the Ace version, comes because I needed to remap the character values encoded in the (ZX81) level data onto the correct Ace character codes. For example, in David’s level encoding, the four sliding walls are represented by values 5, 6, 7, and 8. For the Ace version, I need to map these onto inverse-video ASCII characters 177, 178, 179, and 180. The same is true for the keys, gems, bracelets, and so on. This remapping of the character encoding represents the majority of the work of DRAWLEVEL, as you can see if you look at the source code.

As well as level data, Tut-tut also contains a reasonable amount of text for the instructions and splash screen, plus user-defined graphics. I also encoded these into two other binary code blocks, called MESSAGES and CHARSET, respectively.  CHARSET includes two sets of graphics. There are stylised versions of capital letters that overwrite the default bitmaps for these letters, the same as on the ZX Spectrum version, and a set of objects, held in character code 1—14 (except character 13, which is reserved for carriage return).

Otherwise, the port to Ace Forth was relatively straightforward (accepting I needed to brush up on my Forth skills). I tried to follow good Forth programming practice and to keep the most important game data (the position of the player plus temporary variables) on the stack.  This, and the fact that the state of the level is kept in the display memory, means there are relatively few variables needed: for score, air, keys, mummy locations, and a couple of useful flags to help in quickly exiting from the depths of program loops.

Some words involve a fair amount of stack acrobatics, which is a common trait of Forth programs. A particularly complex stack is required for the word CHECKLOCK (which moves a sliding wall, if possible). At one point, CHECKLOCK has a stack depth of 14 numbers, to hold the various permutations of player moving a sliding wall, not having the right key, and/ or a sliding wall being unable to move because of a blocking object behind it.

The word MOVEMUMMY also descends into some scary stack manipulation when the mummy needs to change direction. What was a relatively simple logic in the BASIC version, to make the mummy follow the player, proved particularly challenging in Forth, because I tried to avoid using variables as much as possible. MOVEMUMMY is the element of the program that I most struggled with, spending a good few hours debugging mummies that wandered off the screen or worse still teleported into other parts of the Ace’s memory, typically causing it to crash.

Talking about debugging, this was one of my bugbears with Ace Forth. The programming environment on the Ace is not typical for Forth. Most contemporary versions of Forth required the programmer to enter program source code into screens, which correspond to space reservation on disk (or tape). Manipulating screens was often a clumsy process, though it did mean the programmer could access and change any part of the program at any time.

Instead, on the Ace, you interactively develop the dictionary using : (colon), EDIT, and REDEFINE, creating the new words that make up your program in the active dictionary. If you need to fix a bug or change a word, you must first EDIT the word, which creates a second copy of the word at the top of the dictionary. Then, to get rid of the old version of the word and update any references to the old definition, you must remember to REDEFINE the word, otherwise you can quickly end up with confused dictionaries with multiple versions of a word in use. Because the dictionary is a stack of words, if you forget to REDEFINE a word after changing it, and then define further words, you end up trapped in a situation where you cannot remove the out-of-date version of the word nor can you update the definition of any other words that link to that out-of-date version. The only work around is to FORGET everything that you did afterwards and then re-enter the lost words. On my first attempt at Tut-tut, I fell into this trap on several occasions, and ended up with a hopelessly corrupted dictionary with stale links that made the game unstable.

Eventually, it became clear I needed to start again, from scratch. This was not all bad, as it gave me the opportunity to fix some wrong decisions I had made in that first attempt. I suspect that when writing a program on the Ace, this was a relatively common requirement. Unless you have a very thorough design, the first attempt at a program was likely to end up as a prototype, because once you had moved on from a word definition, you could not go back.

Debugging is not a strong point for Ace Forth (nor Forth, in general). The common trick from the 1980s (because no one does that now!) of adding print statements at key points in a program is slightly more tricky in Forth, as you have to make sure not to affect the state of the stack. The Ace User Guide has a useful word, named .S, which prints out a full copy of the stack without affecting it. I took .S and extended it a little, so that the stack is printed on the first line of the display (a line that is not used in Tut-tut):

: .S ( -- ) ( PRINT STACK )
  15419 @ HERE 12 + ( FIND THE TOP AND BOTTOM OF THE STACK )
  OVER OVER – ( IF TOP = BOTTOM, WE ARE DONE )
  IF
    DO
      I @ . 2
    +LOOP
  ELSE
    DROP DROP
  THEN
;

: .T ( PRINT STACK ON ROW 0 )
  0 0 AT 32 SPACES ( CLEAR ANY PREVIOUS TEXT )
  0 0 AT .S
;


Otherwise, debugging typically meant studying the source code and manually tracking the changes to the state of the stack after each line. It was sometimes quite laborious; though I noticed, as the game developed, I tended to make fewer mistakes, so suspect my Forth competence was growing. I also found useful tips by looking at other people’s code, especially some of the old magazine listings archived on www.jupiter-ace.co.uk, as well as the Ace ROM disassembly [link].

The full listing of Tut-tut is available on GitHub [link]. There are four files. The main file, called ‘tut-tut.fs’, is the Forth source for all of the words, including constants, arrays, and a small amount of machine code. The other three files contain the level data ‘tut-tut_levels.asm’, the help text ‘tut-tut_messages.asm’, and the user-defined graphics ‘tut-tut_charset.asm’. These need to be assembled using a Z80 assembler, such as Z80ASM [https://savannah.nongnu.org/projects/z80asm], to create binary files that can then be inserted into the memory on an Ace emulator, such as EightyOne; into arrays called GAMELEVELS, MESSAGES, and CHARSET, respectively, which have been pre-allocated appropriately.

The process for inserting the binary blocks is a little involved, so I outline it here for GAMELEVELS.

1. First, assemble the source file ‘tut-tut_levels.asm’ and make sure to write a label file, so you can work out how big the binary block is. Using Z80ASM, the following command would do the trick:

z80asm –L –o tut-tut_levels.bin tut-tut_levels.asm

The block is assembled to address 0x0000, though this is not significant. If you print the labels, you should see a label END, which is used to work out the length of the block. At the time of writing, my test version has three levels and END is reported as 0x017D (or, decimal 381).

2. In your Ace emulator, with your in-progress Tut-tut dictionary loaded, create some space for the game levels, with:

CREATE GAMELEVELS 381 ALLOT

—substituting the right value for the length of the block.

3. Check where GAMELEVELS is located in memory, using

GAMELEVELS .

—and use the address returned as the start point to which you load the block—for example, using [File] [Load Memory Block] on EightyOne.

4.    If necessary, remember to:

REDEFINE GAMELEVELS

—if you have a previous version of the array earlier in the dictionary.

I hope you will agree that the Ace version of Tut-tut is a reasonable tribute to David Stephenson’s original and that you will consider attempting to type in the game. I never found a way to automatically output the program from EightOne, so tut-tut.fs has been transcribed by hand. This means I have almost certainly made some mistakes—just like back in the 1980s!

Having written the game, I have come to like Forth a little more, and I have developed a fondness for the Jupiter Ace. Richard and Steven were right that a micro powered by Forth is better able to run software without the need for machine code and I suspect I would indeed have produced more useful programs if I had discovered Forth in the 1980s. I like to think I would have been happy had I owned a Jupiter Ace rather than a ZX Spectrum, when I was younger. However, I suspect I would have been envious of my Commodore- and Spectrum-owning friends and would have missed classic games like Jet Pac and Manic Miner too much.


Read More

Tuesday, May 05, 2020

TuT-TuT on the Jupiter Ace: Part 1

Leave a Comment

Exciting news this week as Tut-Tut launches on the Jupiter Ace: As a special treat to celebrate, guest blogger George Beckett takes us through his journey in porting the game to the Forth Language an onto the Jupiter Ace.

George has spent the last few months converting the original BASIC source listings and some of the latter adaptations made for the ZX81 and 2020 Spectrum versions of the game.

Join us for a new series of articles as we dig up an ancient micro treasure and explore the depths of game conversion.


Evolution of Tut-Tut: ZX Spectrum BASIC, ZX81, ZX Spectrum 2020 Edition, Jupiter Ace.

Porting TuT-TuT to the ACE

Towards the end of August (2019), David Stephenson (a.k.a. zx81adventures) started posting screenshots, on Twitter, from a new ZX Spectrum game that he was working on. The previews showed an arcade, maze game with bright, clear graphics and an Ancient Egyptian theme. What made the game stand out was that David was using Sinclair BASIC to write the entire game: it was to be a type-in game, harking back to the 1980’s home computing scene, when it was not uncommon to invest many hours into typing in, debugging, and playing such games. Given Sinclair BASIC’s sluggish performance and appetite for consuming memory, David needed to employ some careful tuning and compression tricks to achieve reasonable speed from, and to fit the game into, the rubber-keyed 48k Spectrum.

Very Early BASIC version of Tut-Tut on the ZX Spectrum.
The game, called Tut-tut, was finally published in the online retro-computing magazine Paleotronic in early November [link], along with a YouTube review and taster from Villordsutch [link]. Those who did not want to type in the game could download a copy from Paleotronic’s website. However, the download included a subtle warning that it was not quite complete: If you wanted to be sure of having the full game, you needed to invest the time to type it in yourself.

Tut-tut had some similarities to a very rudimentary game I had made, called Rabbit Run, as part of a series of articles I had written on programming for the Spectrum Show Magazine (issues 16—26) [link]. I had produced a few different versions, including several BASIC versions, a machine-code version, and—a little more unusually—a version written in Forth. The Forth version of Rabbit Run worked surprisingly well, being similar in speed and size to the machine code version, though being much quicker to write.

I started to think about revisiting my Forth version of Rabbit Run and using it as the basis for a port of Tut-tut. However, rather than write it for the ZX Spectrum, I decided I would try to write it for the Jupiter Ace, a less well-known British micro from the early 1980s that was unusual in that it had Forth as its built-in language, rather than the ubiquitous BASIC.

The Ace was developed by Richard Altwasser and Steven Vickers, who had been designers at Sinclair Research until they left to form their own company called Jupiter Cantab. The Ace was their first (and only) computer, and was launched in 1983. The Ace had much in common with the ZX81, which almost certainly inspired it but, by using Forth instead of BASIC, the limited RAM (2 kilobytes, built-in, usable) was much less of a constraint than had been the case for the ZX81.

Source - https://k1.spdns.de/Vintage/Sinclair/80/Jupiter%20Ace/Advertisements/
Sadly, while Forth was compact and efficient, its more primitive functionality and idiosyncratic syntax (based on Reverse Polish notation [link]) put people off, and the machine only ever sold a few thousand units. Also, the arrival, soon after, of Sinclair’s next micro, the ZX Spectrum, which for similar money gave you 16kb of RAM and full-colour, high-resolution graphics would have made the Ace look a poor proposition.

The demise of the Ace signalled a change in the way people viewed home computers. The hobbyists and electronics tinkerers of the 1970s had been replaced by a new breed of users who wanted to play arcade-like games and do useful things such as organise their accounts or run small businesses. The Ace, which was billed as “the programmer’s computer”, arrived on the scene too late, after most micro hobbyists had moved on.

I missed out on the Ace, in the early 1980s, instead being lured by the pull of the ZX Spectrum. However, I probably spent at least as much time trying to program my Spectrum as I did playing games. I soon outgrew BASIC, so spent much of my time wrestling with machine code and crashing my computer. Perhaps if I had started out with an Ace rather than the Spectrum, my programming would have been more fulfilling (if a little less colourful). In an attempt to find out if this was the case, I downloaded an Ace emulator (EightyOne), purchased a copy of the 35th-anniversary Ace User Guide, and set to work on a new version of Tut-tut.

Tut-Tut on Jupiter Ace.
The finished product is available from GitHub [link] as either a TAP file to be used in an emulator, such as EightyOne, or a source code that you can type in and modify, consisting of a Forth program occupying around 4kb once compiled and three assembly language files, which hold various game data to be assembled and loaded into the Forth program as binary code blocks.

While based on David’s Sinclair BASIC version, the Ace version uses the newer levels from the ZX81 version and, thanks to some help from David, includes the extra features such as bracelets, amulets, and a hidden treasury for the final level.

The game requires roughly 12kb of memory, so if you are lucky enough to own a real Jupiter Ace, you will also need a 16kb RAM pack as well. Alternatively, you could buy and build a Minstrel 4th [link], a modern day Z80 microcomputer that is compatible with the Ace and can run Tut-tut without any problems.

In the next blog post, I will provide some insight into how the game is written in the hope it will encourage some to have a go at typing in the game rather than loading it from a tape file. However, for now, I suggest you power up your Ace (or Minstrel 4th), or install an Ace emulator, and get on with some Egyptian-themed adventuring.



Read More

Sunday, November 19, 2017

The Sinclair ZX–Micro Computer: System Variables Comparison Chart

Leave a Comment
Sometimes when you've written an application for the ZX81 or ZX Spectrum you wish to port it to the other, or even back to the ZX80. Sometimes you've used a system variables and you need an easy reference chart to look up correct system address on the target system and move on. But you say, there is no chart, or there was no chart until now.

I got tired of looking through the manuals every time I need to check some System Variable values and decided to just put them all in a chart. Hopefully you might find it similarly useful. A PNG graphic is available below, and if preferred a PDF version can be downloaded.

If anybody notices some glaring or even slightly disturbing error, let me know and I get them ironed out and fixed up.


One final thought, It's quite interesting to see just how many changes were made to the ZX line of computers in the space of 3 or 4 years. There are the obvious physical differences of course, then there are changes witnessed in the System Variables above.

Read More

Friday, October 27, 2017

RetroChallenge 2017/10: Part 7

Leave a Comment

Developing Software on the NEC PC-8401?


Just a bit of fun for this entry. After experimenting with some software development last post, it quickly became apparent that writing software directly on the PC-8401 is a little inconvenient (at least without a memory expansion). This lead to the use of Z88dk for external development and a little cross-compiling to CPM to suite our purposes.

I couldn't help thinking that although the outcome was successful, it seemed a little like cheating. So lets cheat a tiny bit less.

As we know, the PC-8401 comes with a very capable Vt100 compatible Terminal program, one that's perfect for interfacing with LINUX. Now with minimal imagination required  lets pretend it's 1985 and we're developing on some extremely expensive time sharing remote platform tended by white coat wearing professionals.

There are many ways to configure serial / rs232 terminal sessions within LINIX, so here's one way, and one that's really simple although not a permanent on boot ready option.

First, we need to find the correct Serial Port. I'm using a USB to serial adaptor and need to know which exact device to target.

From the command line in LINUX issue: dmesg | grep USB

This yields a load of messages, but I'm looking for something as below.

[   22.444199] usbserial: USB Serial support registered for pl2303
[   22.444774] usb 1-10: pl2303 converter now attached to ttyUSB1


Now knowing knowing which USB device is the serial converter, it's an easy matter to setup a terminal console.

sudo stty -F /dev/ttyUSB1 cols 80 rows 15 1200
TERM=vt100 ksh </dev/ttyUSB1 > /dev/ttyUSB1 2>&1 &


Once that's all done, I can start up the PC-8401s Terminal program, configure it to 1200 baud 1 stop bit, no parity and 8 bit word length and I'm up and running a LINUX remote console. From there it's time to start up a text editor, VI, Emacs or Nano and get developing.

NEC-PC8401 with and open LINUX console running nano and editing code

Once some very impressive software has been developed, it'll need to be transferred to the PC-8401 to be executed in all it's glory, and LINUX provides a couple of easy to use xmodem commands to do just that.

To send files from LINUX via Xmodem:
sx my_file.com

To receive files from the PC-8401
rx my_file.com

Now there is absolutely no excuse, not in the slightest to use a 27 inch widescreen monitor to interact with a high powered modern PC to write applications.

Finally, thanks to Mike Spooner for indirectly generating the idea behind this post.

See RetroChallenge IntroPart 1Part_2Part 3Part 4, Part 5Part 6Part 7Part 8
Read More

Wednesday, October 25, 2017

RetroChallenge 2017/10: Part 6

2 comments

Writting New Software for an NEC PC-8401


As noted previously, the PC-8401 came sans BASIC interpreter, which was quite an anathema in the day. This lack of BASIC presents no real impediment in producing new software for the machine today, even back in the 80s a CPM targeted variety of BASIC could be sourced for the machine (if not a specific PC-8401 version). 30 years on, being equipt with a Z80A processor and the CPM 2.2 operating systems puts this laptop a good position for some software development.


Getting into BASIC


To start we'll need to copy BASIC interpreters onto the PC-8401. AS with all software, BASIC will be taking up valuable chunks of the 32k reserved as drive space. Our own BASIC text files will then need to fit in whatever space remains. Ideally we'd have a RAM extension, either a PC-8406A 32K RAM pack or a PC-8407A 128K pack, in order to program something of a reasonable size. Still even without a RAM pack we can at least try out a number of BASIC flavors.

Both the versions of BASIC mentioned below are available from Gary J. Webes' excellent NEC PC-8201 support site WEB8201, where he holds a selection of software for the NEC PC-8401. NEC PC-8401. Check out the NEC 8401A / 8500 File Collection portion of the File Downloads page.

Microsoft BASIC


In the stakes to use the greatest amount of memory possible, Microsoft BASIC wins hands down. The last CPM version 5.29 will take up huge 23.4k of 'disk space' and when executed leaves a little over 1k of active memory on the PC-8401 to play around with. On the plus side, it's the version of BASIC that's the most familiar, and baring some commands unavailable in the CPM version, there are a wealth of examples laying around to learn from.

Unfortunately the size of MS BASIC makes it a non stater, it's more of a curiosity than something of practical usage. There are compilers available for MS BASIC, though a you still require libraries on the disk to run complied applications. Due to lack of disk space, compilation would need to be run on a host computer before moving the end product over to the PC-8401.


ZBAS


Luckily there are other options, ZBAS is quite a usable option only requiring 16.4k, leaving us with some room to write programs interactively, and some space to save these applications to disk.

Interestingly and extra fortunately when saving files, ZBAS tokenises BASIC Commands, much like on a 128k ZX Spectrum. Thus allowing for some larger than expected applications to be saved on our rather limited 'solid' state storage.

If your going to leave BASIC laying around on the System, then ZBAS is clearly the one to have.


Moving to Modern 'C' Developement


While there are plenty of period development platforms and languages available for CPM, including 'C's and Pascals, you'll need to run these in a emulated environment as there is no room on the PC-8401 for such niceties. A load of CPM software and utilities are available on the retroarchive, although none of these programs are specific to the PC-8401.



Z88DK


Perhaps the easiest way to pursue software development for a CPM machine and the PC-8401 today is via the use of Z88dk. Z88dk is a 'C' compiler targeting TRS80s, Commodore 128s, Sinclair ZX81s and just about any machine containing a Z80 processor. Specifically for out purposes there is a dedicated CPM library.

In order to test Z88DK out with the PC-8401 I borrowed from an older ZX81 program I wrote up some time ago, "ZX Roman Numeral-Izer". The program is a simple roman numeral format checker, it takes Roman numerals as an argument and then correctly formats them to the agreed standards (Standards that even the Romans didn't always adhere to). After a couple of minor changes to remove some specific ZX81 extensions and put in some specific PC-8401-isations the project compiled perfectly.




If you're interested then the full source is below, bit of a rush and there will be better ways to achieve the same result I'm sure, still proof of concept proven.


NEC PC-8401 Roman Numeral-iser Listing

// To Compile for CPM do as below.
// zcc +cpm -lm -o rome.com rome.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef enum { true = 0, false = !true } bool;

static short isaNumArab[7] = {1, 5, 10,50,100,500,1000};
static char csaNumRome[8] = "ivxlcdm";

// Function Get Arabic value of Roman Numeral
short GetArabValue(char csMyNumerals[15]){

 char csMyNumeral[2];
 char csNumRome[2];

 short iNumberNow = 0;
 short iNumberLast = 0;
 short iNumberTotal = 0;

 short iCount1 = 0;
 short iCount2 = 0;

 bool bTest = false;
 bool bNegative = false;

 //Go through sMyNumerals input string backwards for easier rule matching
 for (iCount1=strlen (csMyNumerals)-1; iCount1>=0; iCount1--){
  strncpy(csMyNumeral, csMyNumerals+(iCount1*1), 1);
  csMyNumeral[1]='\0';

  for (iCount2=0; iCount2<=strlen(csaNumRome)-1; iCount2++){
   strncpy(csNumRome, csaNumRome+(iCount2*1), 1);
   csNumRome[1]='\0';
   bTest = strnicmp(csMyNumeral, csNumRome,1);

   if (bTest == true){
    iNumberNow = isaNumArab[iCount2];

    if ((iNumberNow > iNumberLast) || ((iNumberNow == iNumberLast) && (bNegative == false))){
     iNumberTotal = iNumberTotal + iNumberNow;
     bNegative = false;
    } else {
     iNumberTotal = iNumberTotal - iNumberNow;
     bNegative = true;
    }
   }
   iNumberLast = iNumberNow;
  }
 }

 return iNumberTotal;
}

// Function Get Roman value of Arabic Numeral
void PrintRomeNumerals(int iMyNumberArab){
 char csNumRome[16];
 char csMyNumerals[16];
 char csMyNumeral[2];

 short iTemp[16];

 short iNumberNow;

 short iCount1 = 0;
 short iCount2 = 0;
 short iCount3 = 15;

 memset(csNumRome,0,strlen(csNumRome));

 memset(iTemp, 0, sizeof(iTemp));

 if(iMyNumberArab>=4000){
  strcpy(csNumRome,"Max Value 3999");
 } else {

  itoa(iMyNumberArab,csMyNumerals,10);
  //csMyNumerals[15]='\0';
  //Go through sMyNumerals input string backwards for easier rule matching

  for (iCount1=strlen(csMyNumerals)-1; iCount1>=0; iCount1--){

   strncpy(csMyNumeral, csMyNumerals+(iCount1*1), 1);
   iNumberNow = atoi(csMyNumeral);

   switch (iNumberNow) {
    case 1: case 2: case 3:
     for (iCount2=0;iCount2<iNumberNow;iCount2++){

      iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];

      iCount3--;
     }

    break;

    case 4:

      iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+1];
      iTemp[iCount3-1] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
      iCount3=iCount3-2;
    break;

    case 5: case 6: case 7: case 8:

     for (iCount2=0;iCount2<iNumberNow-5;iCount2++){

      iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
      iCount3--;
     }
     iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+1];
     iCount3--;
    break;

    case 9:

     iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+2];
     iTemp[iCount3-1] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
     iCount3=iCount3-2;
    break;
   }

  }
 }

 printf("\n Correct Format: ");
 for (iCount2=0;iCount2<16;iCount2++){
  if(iTemp[iCount2]!=0){
   printf("%c",iTemp[iCount2]);
  }

 }
 printf("\n");

 return;
}


void BannerLine(short iLength, char cCharter){
 short iCount1 = 0;
 for (iCount1=0; iCount1<iLength; iCount1++){
   printf("%c",cCharter);
 }
}


void BannerSet(void){
 short iCount1 = 0;
 printf("\n");
 BannerLine(32,149);
 printf( "\n PC-8401 Roman Numeral-iser\n" );
 BannerLine(32,149);

 for(iCount1=0;iCount1<=strlen(csaNumRome)-1;iCount1++){

  printf("\n Numeral: %c = Numeric: %d",csaNumRome[iCount1],isaNumArab[iCount1]);
  if(iCount1 == strlen(csaNumRome)-1){
   printf("\n");
  }
 }

 BannerLine(32,149);

 printf("\n Roman Numeral Format Check\n");

 BannerLine(32,149);

}


short PressAnyKey(void){

 char chr;

 BannerLine(32,149);
 printf("\n Input Another Numeral? Y/N: ");
 chr = getchar();

 if(chr == 'n' || chr == 'N'){
  return 1;
 } else {
  return 0;
 }
}


main(void)
{

 char csMyNumerals[15];

 bool PressKey = true;

 char csMyNumberRome[15];
 int iMyNumberArab = 0;

 while( PressKey != false ) {

  BannerSet();

  printf("\n Roman Numerals: ");
  fgets(csMyNumerals,15,stdin);
  iMyNumberArab = GetArabValue(csMyNumerals);
  printf(" Numeric  Value: %d",iMyNumberArab);

  PrintRomeNumerals(iMyNumberArab);

  memset(csMyNumerals,0,strlen(csMyNumerals));
  memset(csMyNumberRome,0,strlen(csMyNumberRome));

  PressKey = PressAnyKey();

 }
}

See RetroChallenge IntroPart 1Part_2Part 3Part 4, Part 5Part 6Part 7Part 8
Read More

Thursday, August 17, 2017

Model 100 micro SD Card reader, the MyTeSed

7 comments
After the initial testing with a full sized SD card reader shield and Ardunio Uno, I wanted to know how easy it would be to shrink the TRS-80 Model 100 SD card interfaces size down to something a little more acceptable. I thought about building the whole design from the ground up, in the end the abundance of cheap modules from China can't be beaten on price (or convenience).

Before getting into the excitement of the details, the other major half of the fun of building these projects is naming them, so I'm calling this interface the 'MyTeSed', standing for My Model 'T' Serial Drive. Now that out of the way, on with where I'm up to with the project to date.

Testing the functionality of the MyTeSed on a Breadboard 

The Hardware


I've gone with a Arduino Nano for the heart of the project, the footprint of the Nano is barely larger than a atmega328p chip and far smaller if including the ancillary components. The Nano also nicely takes care of power regulation, has a USB port for easy re-programming, and behaves identically to a Uno. Thus making the Nano one of the most inclusive of the reduced sized Arduinos' to use.

Micro SD Card Reader Module
The revised hardware also substitutes the full sized SD card shield with an all in one micro USB card module. The SD module accommodates either 5v or 3.3v signals, making it an ideal choice for use with the Nano. It's a standard module avaliable from just about anywhere, including Ebay and various online Chinese merchants of all things electronica.

After a little experimentation I've also decided to set up software serial on pins D8 and D9 of the Nano. Using software serial as the conduit to the T100 / T102 / T200 leaves the Nanos' standard serial pins D1 and D2 free to use as a pass-through (actually via the Nanos' USB) which will be used as direct connections to a PC. As in the previous version (blog entry Using SD Cards with the TRS-80 Model 100) a MAX323 IC handles the RS232 to TTL serial conversion.

The when complete the MyTeSeD can be powered from a 6v to 9v battery supply (like most Arduino projects), in addition usage of the Nano conveniently provides a means to power the interface via the mini USB port, allowing battery packs or a USB power source etc.


MyTeSed Circuit Diagram

Arduino Code


This is not by any measure the final code to be using with the interface, for the moment it serves as proof of concept. I'm still yet to go back a write up a more permanent solution, it does however work for now, just expect some drastic changes for the foreseeable future.

In essence the current idea is for MyTeSed to listen for instructions in plain text over the serial line, 'LOAD' and 'SAVE' for example, then either send data back or shift files around on the SD card. Regardless there is a lot left to implement, such as file deletion, access to directories and error checking.
// **************************************************************************
// **** MyTeSed: T100 SD Card Reader for Arduino Nano and SD Card Reader ****
// **************************************************************************
// ** David Stephenson 2017-08-23  **
// ** Version 0.01                 **
// **********************************
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>

enum SERIAL_MODE {FREE, COMMAND, DATA_IN, DATA_OUT, FILES_OUT};

const byte LF=10;
const byte CR=13;
const byte EF=26;

SoftwareSerial mySerial(12, 13); // RX, TX

class CardReader {
 private:
  SERIAL_MODE eSerialMode = FREE;
  //DRIVE_COMMAND eDriveMode = NONE;

  File MyFile;
  
  String sFileName;
  String sInString;
  
  unsigned long TimeLastUpdate = 0;
  
  // Class Private Functions
  void LoadBas() {
   
   char cRead, cLast;

   if(eSerialMode == DATA_OUT){
    MyFile = SD.open(sFileName);

    if(MyFile){
     while(MyFile.available()){
      cRead=MyFile.read();
      if (cRead== LF){
       if (cLast != CR){
        mySerial.write(CR);
       }
       mySerial.write(LF);
      } else {
       mySerial.write(cRead);
       cLast = cRead;
      }
     }
    }
    MyFile.close();
    mySerial.write(EF);
    eSerialMode = FREE;
   }
  }

  void SaveBas(char cInChar) {
   
   if(eSerialMode == DATA_IN){
    if(cInChar != EF){
     MyFile.print(cInChar);
    } else { 
     MyFile.close();
     eSerialMode = FREE;
    }
   }  
  }
  
  void FilesOut() {
   
  }
  
  
  void commandIn(){
   String KEYWORDS[7] = {"LOAD","SAVE","KILL","FILES","MOVE","CP2SD","CP2R"};
   String sInSubString;

   sInString.trim();

   Serial.print(sInString);

   if (sInString.length() >= 3){
    for (byte bKeyword = 0 ; bKeyword < 8 ; bKeyword++){
     sInSubString = sInString.substring(0,KEYWORDS[bKeyword].length() );
     sInSubString.toUpperCase();
     if (sInSubString.indexOf(KEYWORDS[bKeyword])!=-1){
      if (KEYWORDS[bKeyword] == "LOAD") {
       sFileName = sInString.substring(4);
       sFileName.trim();
       delay(500);
       eSerialMode = DATA_OUT;
       LoadBas();
      }

      else if (KEYWORDS[bKeyword] == "SAVE" || KEYWORDS[bKeyword] == "CP2D") {
       sFileName = sInString.substring(4);
       sFileName.trim();
       if (SD.exists(sFileName)) {
        SD.remove(sFileName);
       }
       MyFile = SD.open(sFileName, FILE_WRITE);
       eSerialMode = DATA_IN;
      }
      
      else if (KEYWORDS[bKeyword] == "FILES") {
       eSerialMode = FILES_OUT;
       FilesOut();
      }
     }
    }
   }

   //sInCommand = false;
   sInString = "";
   
  }
 
 public:
 
 void SerialIn(char cInChar){
  switch(eSerialMode) {
   case FREE:
    if(cInChar == CR){
     eSerialMode = COMMAND;
     commandIn();
    } else {
     sInString += cInChar;
    }
    break;
   case DATA_IN:
     SaveBas(cInChar);
    break;
  }
 }

};

CardReader MyCard;

void setup()

{
  // Open serial communications and wait for port to open:
  Serial.begin(1200);
    //57I1D
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // set the data rate for the SoftwareSerial port
  mySerial.begin(1200);

  if(!SD.begin(10)){
   mySerial.println("fail");
    return;
  } else {
 mySerial.println("mango");
 Serial.println("mangos");
  }
}



void loop()
{
 char cInChar;
 if (mySerial.available()) {
  cInChar = (char)mySerial.read();
  //sInString += cInChar;
  MyCard.SerialIn(cInChar);
  Serial.write(cInChar);
 }
}

Talking With a Model 100


Saving, loading and copying files from the micro SD card requires first sending a command and a filename to the serial port, then engaging the normal BASIC save / load to serial command. The COM port should be set to 1200 Baud, 7 Bit Word length, Ignore parity.

For example, saving the active BASIC file:
OPEN "COM:57I1D" FOR OUTPUT AS 1: PRINT #1,"SAVE MYFILE.BA": CLOSE 1: SAVE "COM:57I1D"

Or loading a BASIC file from the Interface:
OPEN "COM:57I1D" FOR OUTPUT AS 1: PRINT #1,"LOAD MYFILE.BA": CLOSE 1: LOAD "COM:57I1D"

Possibly the easiest method to save or load  BASIC program is to add some of the below lines to a program listings. Alternatively an option once the code base is a little more on the stable side would be the development of a menu-ing  system, but that's for latter.

Save a BASIC file to SD Card

900 OPEN "COM:57I1D" FOR OUTPUT AS 1
910 PRINT #1,"save testme.ba"
920 CLOSE 1
930 SAVE "COM:57I1D"

Load BASIC file to SD Card

10 OPEN "COM:57I1D" FOR OUTPUT AS 1
20 PRINT #1,"load testme.ba"
30 CLOSE 1
40 LOAD "COM:57I1D"

Copy a RAM Text File to the SD Card

10 MAXFILES=2
20 OPEN "RAM:TXT2RD.DO" FOR INPUT AS 1
30 OPEN "COM:57I1D" FOR OUTPUT AS 2
40 PRINT #2, "SAVE TXTFIL.DO"
50 LINE INPUT #1, Z$
60 PRINT #2,Z$
70 IF EOF(1) THEN GOTO 90
80 GOTO 50
90 PRINT #2, CHR$(26)
100 CLOSE 1: CLOSE 2

Copy a File from the SD Card to a RAM Text File

10 MAXFILES=2
20 OPEN "RAM:TXT2RD.DO" FOR OUTPUT AS 1
30 OPEN "COM:57I1D" FOR OUTPUT AS 2
40 PRINT #2, "LOAD TXTFIL.DO"
50 LINE INPUT #2, Z$
60 PRINT #1,Z$
70 IF EOF(1) THEN GOTO 90
80 GOTO 50
90 PRINT #1, CHR$(26)
100 CLOSE 1: CLOSE 2

Next Time


The next stage of proceedings is to design a circuit board, should be a simple enough task. Unlike all the other projects in this blog I'm planning on getting the PCB produced by a fabrication house, and hopefully all that will go well.


Read More