6809 ASM: Exchanging data between Assembly code and the BASIC interpreter on the Olivetti Prodest PC 128 and the Thomson MO6 (Part 5)


After seeing which methods are available to exchange data between the Motorola 6809E machine code and the SIMIV BASIC interpreter, let’s see an important programming technique that can help reduce the number of parameters passing on each single call.

Disclaimer

The techniques, examples and concepts described in this series of articles are made for the Olivetti Prodest PC 128 and Thomson MO6 microcomputers. However they can be easily applied to the Thomson MO5 series, (I have tried to make the code as compatible as possible with the MO5 series). They can be converted for the TO7, TO8 and TO9 series, however to keep the size of the articles as small as possible, I did not add specific sections with the converted memory addresses for the TO7/TO8 and TO9. If you want me to extend my articles to the TO7, TO8 and TO9 series please let me know in the comments below, thanks!

Please note: The Machine code presented in this series of articles is not optimised for performance by design. The reason behind this choice is that I want the articles to be as clear and as easy to read as possible for everyone interested in:

a) Learning the very basics of Assembly programming.

b) Help every possible PC 128, MO6, MO5 owner to learn to be able to create programs that can use all the potentials of these old systems.

Introduction

In the old days of 8bit computers RAM was a very small and an important resource. To have an idea of this think about:

  • The 24 bytes of total RAM available on the Atari 2600 console.
  • The 5KB on the Commodore VIC20.
  • The 16KB on the ZX Spectrum, later expanded to 48KB.
  • The “whopping” 128KB of the PC 128/Commodore 128/Spectrum + 128 (and from 128KB on it required BANK SWITCHING).
  • The 256KB (up to max 512KB) of the very last 8bit generation like the Dragon 256, the Thomson TO8/TO9 and MSX2(+).

Moreover 8bit computers had, well an 8bit data bus which was the only way to transfer machine code instructions AND data to and from the memory.  In comparison with modern computers which have a 64bit wide data bus (or more), the old 8bits would be literally like a Fiat 500 competing with a Lamborghini Diablo 😀

So a lot of programming techniques of the time were focused on saving space in memory and try to reduce data and instructions transfer as much as possible. One of these techniques was to try to “pack” multiple values that required less than 8bit to be represented into a single 8bit value.

The perfect candidate to demonstrate this technique is the so called Flags data-type.

For beginners: in computing, a Flag is generally a value that can be represented either with a 0 or 1 (TRUE or FALSE, ON or OFF, you get the idea!). By convention, generally 0 means disabled or off while 1 means enabled or on. An example could be SNDEFX=1 (SouND EFfects) which could be interpreted by our code as “enable sound effects” if set to 1 and, when set to 0, the code would not produce any sound effect.

Since computers can address at least 8bits per time (so it’s not possible to address a single bit per time and therefore a flag per time), using 8bit or more for a single flag always results in a waste of memory (and if you have to pass multiple flags then even in a waste of CPU cycles). So, it’s always convenient to pack multiple flags within a single variable to save memory (and operations on the data bus). We’ll call this variable a flag-set. However, using flag-sets, introduces a bit more complexity in the coding. Let’s have a look at how we can achieve this in the BASIC interpreter.

Practical examples

BASIC Code

Since a flag takes just 1 bit, in an int value in SIMIV BASIC 128 we can pack up to 16 flags (that’s quite a lot!).

Setting a flag in a flag-set

For example if we want to encode 4 flags in a single INT variable we can use the following technique in BASIC:

100 REM Initialisation
110 FLAGSET%=0: ' We reset all flags to 0
120 REM Let's set the first bit of FLAGSET% to 1
130 FLAGSET%=FLAGSET% OR &B00000001
140 REM Now let's set the 4th bit of FLAGSET% to 1
150 FLAGSET%=FLAGSET% OR &B00001000
160 REM Let's display the results
170 PRINT FLAGSET%

For beginners: Please do not forget we count bits from your right to your left! So the 1st bit is the one set to 1 here: &B00000001 , while the 2nd is the one set to 1 here: &B00000010 and so on and so forth. Also don’t forget that we refer to the Most Significant bit (or MSb) with the bit to your far left (so in this case the one set to 1 here &B10000000) and with Less Significant bit (or LSb) the 1st bit basically.

Also the OR operation is called logic OR.

If you copied the code above correctly then when executing it you’ll see it’ll display 9 on the screen. 9 is the decimal notation for the binary number &B00001001, so we can see we have stored two flags in a single variable!

The great feature of the OR function is that, because of the logic it uses, it won’t clean up the other bits in our flag set. It sets only the bit we are interested into and to set it we need to specify 1 in the OR bit-mask.

For beginners: a bit-mask is a binary number used as a “mask” were the zeros and ones are used to convert a value depending on the operation we are using, in our case the operation is a OR.

Reset a flag in a flag-set

But if OR doesn’t reset (or zero) a flag then how can I reset a specific flag in a flag-set? 

We can use another logic operator: AND

FLAGSET%=FLAGSET% AND &B11110111

The above code allows us to clean (aka RESET) a specific flag in a flag-set. In the example flag in bit 4, which will be reset to 0 while all the other flags will be left untouched. In technical terms the binary number &B11110111 is used by AND as a bit-mask where the 0 basically is forbidding the value in FLAGSET% (at the same position) to be considered.

Read the state of a flag in a flag-set

Ok so how can I check if a specific flag in a flag-set is set using the BASIC language? Simple we can use the function AND again, this time in a IF statement and with a different bit-mask.

For example if we want to check if Flag 4 is set or not we can use:

180 IF (FLAGSET% AND &B00001000) THEN PRINT "FLAG 4 IT'S ENABLED" ELSE PRINT "FLAG 4 IT'S DISABLED"

Here above you can clearly see another advantage of this technique, if you want to check if 2 flags are set in the same IF statement you just need to set them in the bit-mask and you’ll check them both at the same time and using a single expression which is faster in BASIC than evaluating 2 separate expressions like IF X=1 AND Y=1 THEN… 😉

Please note: If you want to POKE a flag-set in a known location to pass it to your ASM code remember that you have max 8 flags you can use if you want to keep a single POKE instruction to pass the whole flag set to the machine code.

ASM Code

In ASM things works in a very similar way.

Please note: the ASM below is written using the data-exchange method explained on my first article of this series. It requires you to PEEK the value from the BASIC in a known location “KLOC” if you want to use the ASM with the BASIC interpreter.

Setting flags in a flag-set

The routine below simply set flag 2 in the flag-set stored at KLOC location.

  • To read the result from the BASIC interpreter remember to PEEK location KLOC+1 after executed the machine code generated by the ASM below
  • To assemble the code below (and all the other examples in this article) using the Infogrames Assembler, from the Assembler mode type:
    • zone $5F00
    • ass rm,tl
  • To run the code in the TBUG debugger (to see how it works)
    • after assembled it, enter TBUG mode by typing
      • tbug
    • then run the code in the debugger using
      • p $5F02
    • Remember to exit the debugger using CTRL + C before it execute the RTS instruction
00100 * Initialisation
00110         ORG    $5F00
00120         FCB    $00,$00
00130 KLOC    EQU    $5F00
00200 *
00210 * Load the flag-set parameter
00220 START   LDD    KLOC
00230         NOP          * <- code added just to help debugging
00240 *
00250 * Set bit 2 of the 1st byte of the flag-set to 1
00260         ORA    #%0010
00270         STD    KLOC
00900 *
00910 * Return to BASIC
00920         RTS
00930         END    START

The code above has been written to make it easy to be debugged in the TBUG to look at what happens during its execution. So it’s not written with performance in mind. The NOP instructions for example, do nothing but help to align the real code in the debugger so when you debug it you can see Register values changing in a slow enough rate to understand the logic.

Reset a flag in a flag-set

The code below reads a 16bit flag-set from a known location in KLOC and reset a flag on the 2nd byte of the 16bit flag-set.

  • If you want to use POKE from the BASIC, because POKE only supports 8bit values then you can POKE a value in KLOC+1 (&H5F01).
  • If you want to set the flag-set via TBUG from the Infogrames Assembler then:
    • Assemble the code as described before
    • Then enter TBUG mode as described before
    • And then insert the value directly in $5F00 using the command (remember the hexadecimal number all in capital letters)
      • I $5F00
    • Type a couple of bytes, for example first 12 (and then press enter) and then A (capital A) and then press enter and then press CTRL + C (to exit the direct Insert mode)
    • Finally start the debugging using
      • p $5F02
    • Remember to stop the debugger using CTRL + C before it executes the RTS instruction
00100 * Initialisation
00110         ORG     $5F00
00120         FCB     $00,$00
00130 KLOC    EQU     $5F00
00200 *
00210 * Load the flag-set parameter
00220 START   PSHU    D,DPR,X,Y,S
00230         LDD     KLOC
00240         NOP                 * <- code added just to help debugging
00250 *
00260 * Reset flag 2 on the 2nd byte 1 is enabled
00270         ANDB    #%11111101
00280         STD     KLOC
00900 *
00910 * Return to BASIC
00920         PULU    D,DPR,X,Y,S
00930         RTS
00940         END     START

Check the state of a flag in a flag-set

The code below reads a 16bit flag-set from a known location in KLOC and check IF flag 2 on the 2nd byte of the 16bit flag-set is set to 1. If you want to use POKE from the BASIC to pass a value to the routine below use same logic as on previous example.

Please note: this code starts at $5F04 because we set two bytes for the T and F values at lines 00140 and 00150.

00100 * Initialisation
00110         ORG     $5F00
00120 KLOC    EQU     $5F00
00130         FCB     $00,$00
00140 PTRUE   FCC     /T/
00150 PFALSE  FCC     /F/
00200 *
00210 * Load the flag-set parameter
00220 START   PSHU    D,DPR,X,Y,S
00230         LDD     KLOC
00240         NOP                * <- code added to help debugging
00250 *
00260 * Check if flag 2 on the 2nd byte is enabled
00270         ANDB    #%00000010
00280         CMPB    #%00000010
00290         BEQ     F2ENAB
00300         LDX     #PFALSE    * F2 is NOT set
00310         JMP     PRINT
00400 F2ENAB  LDX     #PTRUE     * F2 is set
00500 PRINT   LDB     ,X+
00510         SWI
00520         FCB     $02
00900 *
00910 * Return to BASIC
00920 DONE    PULU    D,DPR,X,Y,S
00920         RTS
00930         END     START

In the code above, if we set F2 (Flag 2) on the less significant byte (so address KLOC+1 or $5F01), then the assembly will display the letter T (for TRUE) on the screen, otherwise it will display the letter F (for FALSE). Again verify if a flag is set it’s very simple, we just need to use a bit-mask that will expose only that flag and then do a CMPB of Register B with a literal value that has only F2 set. Because CMP does a subtraction between the two, if they are identical then the Z flag in the register CC will be set so the BEQ (Branch if EQual) will jump to the address pointed by label F2ENAB (F2 is ENABled), which will load the letter T address in register X and then LDB will load that letter value in Register B, call the SWI $02 which will print T on the screen.

Ok so you can clearly see the convenience here: If we have multiple flags to be passed to the machine code, by packing them up in a single 8bit value will make it easy to pass them to the ASM as well as requiring much less instructions to fetch the data and just 1 or 2 ASM instructions to extract the flag value.

Packing more data than just a flag

Ok now that you know how it works let’s see if we can pack values that requires more than a single bit to be represented.

Let’s for example pack two 4bit (nibbles) into a single byte:

For beginners: a nibble (or a 4bit value) can represent numbers:

  • In Hexadecimal from $0 to $F.
  • In binary from %0000 to %1111.
  • In decimal from 0 to 15 (so max 16 values).

Because a nibble is bigger than 1bit, it requires a mask also bigger than 1bit. So for example to pack two nibbles in one byte:

In BASIC

100 REM Initialisation
110 PARAMS%=0: ' We reset all values to 0
120 REM Let's set the 1st NIBBLE of PARAMS% to 11
130 PARAMS%=PARAMS% OR 11
140 REM Let's set the 2nd NIBBLE of PARAMS% to 1
150 PARAMS%=PARAMS% OR ( 1 * 16 )
160 REM Let's display the results
170 PRINT PARAMS%

As you can see, in this case we used decimal values and so we had to multiply 1 per 16 at line 150 to make sure it gets stored in the right 2nd NIBBLE of PARAMS%.

Now let’s see how it works with the AND function.

Clear up the 1st nibble

PARAMS%=PARAMS% AND &B11110000

Clear up the 2nd nibble

PARAMS%=PARAMS% AND &B00001111

Check the 1st nibble value

PRINT (PARAMS% AND 15) ' Or you could use &B00001111

Check the 2nd nibble value

PRINT (PARAMS% AND 240)/16 ' Or you could use &B11110000 instead of 240

In ASM

If we store our byte in register B:

Set the 1st nibble (to value $D)

00250        ORB     #%00001101

Set the 2nd nibble (to value $D)

Using binary values

00250        ORB     #%11010000

Using Hexadecimal values (value $B0)

00250        ORB     #$B0

Read the 1st nibble

00250        ANDB    #%00001111

Read the 2nd nibble

In ASM you can either use the 2nd nibble directly and so just use something quick and simple like:

00100         ORG      $5F00
00110 START   ORB      #$B000 * Just load the 2nd nibble with 1011 = $B0 ... 00200         ANDB     #$FF00 * Takes only the 2nd nibble = %11110000 ... 00900         RTS 
00910         END START

Please note: When using the 2nd nibble directly don’t forget to multiple your values by 16 if you use decimal notation (or by 10 if you use hexadecimal notation) first! So, for example:

  • if you want to check if 2nd nibble value is equal to $04, you need to use a CMPB #$40
  • if you want to check if 2nd nibble value is equal to 7, you need to use a CMPB #112.

Or (for example) you could rotate the nibbles in the register you are using and then take always the 1st nibble:

00100         ORG      $5F00
00110 START   PSHU     D,DPR,X,Y,S
00120         LDB      #$00
00130         ORB      #$B0 * Just load the 2nd nibble B0 = 1011....
00140         NOP           * <- code added to help debugging

00200         RORB          * Rotate the B register of an entire nibble (4 bits)
00210         RORB
00220         RORB
00230         RORB
00240         NOP           * <- code added to help debugging
00250         NOP

00300         ANDB     #$0F * We rotated the 2nd nibble on the 1st so we take the 1st nibble now
00310         CMPB     #$0B
00320         NOP           * <- code added to help debugging
00330         NOP 
... Add a BEQ/BNE/etc. code block here to check the CMPB results 
00900         PULU     D,DPR,X,Y,S
00910         RTS
00920         END      START

The lines from 260 to 290 are one example of how you could simulate a /16 in 6809 ASM, there are better and faster ways to do that, but code optimisation is not a goal here.

So, what this technique could be useful for?

Ok some practical examples could be:

  • You are writing a game that requires two joysticks to drive a vehicle for example (a robot) so using the above technique you could read both joysticks directions and put one value per nibble and then pass back a single byte to the BASIC interpreter. Given that a game needs to constantly re-read the joystick then this technique would be quite useful in this case.
  • Your game needs to read some keyboard keys and one joystick direction at the same time. A joystick has 8 directions and 1 fire button so definitely can be packed in max 16 values (from 0 to 7 for the direction so the first 3 bits used for direction and then the 4th bit used for the fire button flag) and we could pack the flags for a space bar pressed (to lunch a power-bomb for example) and the letter E to quit the game in the 2nd nibble.
  • The PC 128 has max 16 colours so you can use the first nibble to represent the colour code and then the 2nd nibble a coded action (like 1 for remove, 2 for add, 3 for select etc…). Or you can use the 1st nibble to represent the foreground colour code and the 2nd nibble the background colour code.
  • If you are writing code that creates Windows then you could pack the window flags into a byte (like activate the close icon, the zoom icon, the resize icon etc.) and give all these info to the machine code in once single byte.

Ok that’s it for now, thanks for reading and I hope you’ve found some useful information here. If you enjoyed this post, please don’t forget to support my blog by:

  • Visiting my on-line hacking and engineering merchandise shop on redbubble.com by clicking here
  • Or you can also make a donation with the PayPal link in the column on your right
  • Or share this article

If you like my articles and want to keep getting informed on new ones you can follow me on on of those 21st Century thingies called FacebookTwitterInstagram or Pinterest

And as always if you have any questions please feel free to use the comments section below.

Thank you! 🙂

What Next?