Let’s play with commutable memory on Thomson MO6 and Olivetti Prodest PC 128 microcomputers


In the previous article of this series on Vintage Computing we saw how memory was organised on the Thomson MO6 and the Olivetti Prodest PC 128 8bit microcomputers. In this article we’ll put some concepts to practice and have some fun with the commutable memory.

Before you keep reading this article I recommend to open the previous one in another tab to use it as a reference for this one. To open the previous article on another tab click here.

So, as a first exercise to play with MO6/PC128 commutable memory let’s learn how to hack the BASIC 128 Interpreter which is fun and will help us to understand more how memory is being mapped.

Please note: Don’t just read what follows, try it on your MO6/PC128 for real. If you don’t have one then get the dcmoto emulator. In this article here I tell you how to install dcmoto emulator on Windows, Linux, BSD and macOS, so no excuses! The most important thing you can do to help yourself to learn Computer Science for real is to test everything in practice and never assume an article or a book is correct by definition.

Playing with commutable memory

From the initial menu of the MO6 or PC 128 let’s choose option 1 and get into the BASIC 128 Interpreter.

As we learned from the previous article, commutable memory allows us to access more memory than the 6809E can address by switching (commuting) memory pages. We can do that in BASIC 128 using the command BANK. Before we start playing with it let’s see how we can display which bank is active (and, in this case, which one is also the one active by default). To display which memory bank is active type:

PRINT BANK

The output will be:ย 6

This means BASIC 128 sets BANK 6 as default bank for the memory area between $6000 and $9FFF. There is a reason for that… it sets BANK 6 because it’s the most far from BANK 1. BANK 1 is very special in BASIC 128, it’s used as starting point to store our BASIC program.

Let’s check that. Because we are in BANK 6 let’s see what’s the content of memory address $6002, to do this in BASIC we can use the instruction PEEK:

PRINT PEEK(&H6002)

Please note: In Microsoft SIMIV BASIC to specify an hexadecimal number we need to use the prefix &H before the number.

If we have just started the computer and did not load anything in memory before the test above, the output of this command will most likely be 0 (zero).

So, lets create a very minimalistic and useless program to test our understanding of the memory mapping on the MO6/PC 128:

10 REM A USELESS PROGRAM

This program is very useless as it simply sets a comment at line 10, however it’s actually very useful to hack the BASIC 128… ๐Ÿ˜‰

Now let’s repeat the previous PEEK command and see if the value of &H6002 has changed. We’ll immediately observe that nothing has changed.

Let’s change the commuted memory bank to BANK 1 and repeat the PEEK command again. To commute (or switch) a memory bank in BASIC 128 we can use the following instruction:

BANK 1

(where 1 is the Bank 1). Now let’s check again the value at location &H6002:

PRINT PEEK(&H6002)

This time the output will be different, it will show us an 11 (eleven), this is a code used by the BASIC 128 to define the beginning of a program. The reason the output has changed is because the BASIC interpreter has requested to the MO6/PC128 Monitor Routines to “swap” the memory page (6 with 1) mapped on addresses between &H6000 and &H9FFF and so, when executing PEEK instruction it now picks the value contained in a different memory page.

Let’s check our program code by executing a LIST command:

LIST

Program is ok and we can see our line 10 and the comment.

Now let’s set location &H6002 value from 11 to 0, to do this we can use the instruction POKE:

POKE &H6002,0

The output will be just an OK (which means “Ok I set the value at the specified memory address as you wanted”), so let’s LIST our program again using LIST command and… wow the program is gone!

Don’t panic lol, let’s set the value stored in &H6002 back to 11 and see what happens:

POKE &H6002,11

And then list our program again…

LIST

The entire program is back! Ok,ย let’s leave it for now and let’s learn more by running a peek at the next location:

PRINT PEEK(&H6003)

This time we’ll get a 10, let’s try the next location then:

PRINT PEEK(&H6004)

The output is going to be interesting this time, the output will be 140

This value is interesting because it’s actually the Token value of the instruction REM in SIMIV BASIC 128! How do I know that? It’s simple and we’ll see how to understand this in a minute, meanwhile let’s keep reading the next memory location’s value:

PRINT PEEK(&H6005)

We’ll get a 32, this is the ASCII Standard value for the Space Character, so let’s read the next memory location value:

PRINT PEEK(&H6006)

This time the value is 65 which is the ASCII Standard value for the letter A. Ok, next one:

PRINT PEEK(&H6007)

Again 32 (so space character)ย and if we start to put all these values together in their corresponding ASCII characters we get our comment back A USELESS PROGRAM. I’ll let you keep trying to read all the bytes until a zero value is reported (the zero means end of the line).

If you are not familiar with the ASCII standard: You can display the character by adding CHR$(PEEK(&6008)) function around the peek function. Do not use CHR$ for the line-number location and the BASIC instruction (the REM Token code) location.

So in the end we’ll have something like:

10 140 A USELESS PROGRAM

If you want to confirm that the 10 is the line number then try to renumber your source code with the command:

RENUM 20

And then read again the value at location &H6003 and see it will show you 20 this time.

So the 140 must be the token value for the instruction REM! ๐Ÿ˜‰

You can test this by replacing the REM instruction with a PRINT like:

20 PRINT A

and check again the value at &H6004.

We can switch back bank to 6 and check these memory locations. If you do that, you’ll see that PEEK command will show you again all zeros when on a different BANK (or different values if you had your MO6/PC128 running other stuff before following this article).

A question at this point would be: So, if only one bank is active at the time mapped to location between &H6000 and &9FFFF, how does the BASIC interpreter store my program from BANK 1 onwards no matter which BANK is active? The answer is simple, the BASIC interpreter switches the BANK transparently when it detects the user has typed a line-number (or has executed a specific command to manipulate the program source code). Then it does what it has to do to the source and then switches back BANK to what the user has set (or the default if the user hasn’t set any) before returning control to the user.

We can simulate this behaviour in our BASIC programs too. For example, copy content from BANK 1 to, let’s say, BANK 4. To avoid overwriting the BASIC code in BANK 1 we can use a BASIC loop directly from the editor like the following:

FOR ADDR=&H6000 TO &H9FFF:BANK 1:BV=PEEK(ADDR):BANK 4:POKE ADDR,BV:NEXT

The code above will switch to bank 1, read the value at the address &H6000 (and on each iteration the address will be incremented by 1 extra byte), switch to BANK 6 and then store it on the same address on BANK 6.

“Ok I am confused now… If only one bank can be active per time, how can the program above work??” Simple, the program above works because its code is being stored in the BASIC Buffers area (between $2100 and $5FFF) and so are the values stored in BV variable, so, when switching BANK to 4 the BASIC Interpreter can still read the value in BV and still has the whole mini program available. This is why BASIC’s buffers are NOT stored in the commutable memory! ๐Ÿ˜‰

Ok last example/exercise for this article is a famous trick on the MO6/PC 128, which allowed little hackers like me (at the time) to remove protection from protected programs written in BASIC.

This so called protection was a simple mechanism offered by Microsoft SIMIV BASIC to protect the source code of a BASIC program to allow its commercial distribution and protect the program creator’s copyright.

1. Set memory bank back to default, bank 6:

BANK 6

2. Load your BASIC protected program.

If you don’t have one, don’t despair, you can create a BASIC protected program by saving it with the option ,P:

SAVE "CASS:MYPROG.BAS",P

Then erase your program from memory with the instruction:

NEW

And, finally, load your protected program with:

LOAD "CASS:MYPROG.BAS"

3. Let’s check that the SIMIV BASIC protection is working by typing LIST command that will generate an error message “Protected Program” when the loaded program is protected:

LIST

4.ย Let’s start hacking the protection. First of all let’s execute the command NEW to tell the BASIC Interpreter to delete the loaded program (this won’t erase the source from memory, it will only set the value in location &H6002 to zero):

NEW

5. Switch active bank to 1:

BANK 1

6. Execute the following loop from the BASIC editor:

FOR ADDR=&H6004 TO &H9FFF:IF PEEK(ADDR)=0 THEN POKE &H6002,ADDR-&H6000 ELSE NEXT

7. Now list the program source by executing LIST command again and voila! Protection is gone ๐Ÿ™‚

Modern Computing Inheritance

As promised in the beginning of these Vintage Computing articles, here is the Modern Computing byte of knowledge that we get from commutable memory:

Commutable Memory principle is in fact at the base of modern Operating Systems’ SWAP Memory. Modern OS swap unused memory pages from central memory to the disk in order to make space for Applications that require more memory.

This is it for now, thanks for reading and, if you enjoyed this post, please support my blog by visiting my on-line hacking and engineering merchandise shop on redbubble.com by clicking here, thank you!:)

 

Advertisements

8 thoughts on “Let’s play with commutable memory on Thomson MO6 and Olivetti Prodest PC 128 microcomputers

    • Hi and thanks,
      so SAVEM allows you to save the content of memory cells in a .BIN file, so, in theory, it’s possible to do what you are asking, however in practice things are more complicated.

      First off, commutable memory makes it available only one BANK per time, so, if you want to use SAVEM, your entire program must reside in a single BANK (let’s say BANK 1 for the sake of the discussion).

      BASIC variables are stored in the memory area between $2000 and $5FFF (in BANK 0) just after the Monitor registers locations, the BASIC Buffers and the BASIC Stack. So usually your first variable should reside in an address between $2A5F and $2A61 (also depending on the variable type etc.). You can test this easily in BASIC itself by typing the following line with the computer just restarted and fully clean:

      A%=1:PRINT HEX$(VARPTR(A%))

      The function VARPTR gives you back the “pointer” to that variable content (aka the first memory location where that variable value is stored) and the function HEX$ transform that location number into Hexadecimal notation (for easier reading). Depending on your variable type BASIC will use multiple bytes to store its value (for example a decimal number will take something like 4 bytes of memory, while an integer number as in the example will take 2 bytes).

      To display the value of A% using PEEK try the following trick:

      PRINT PEEK(VARPTR(A%)):PRINT PEEK(VARPTR(A%)+1)

      This will show you that the first byte is set to 0 (zero), while the second to 1 (as we assigned in the previous line). If you assign a value bigger than 255 then the first location will also be used to store it. Try with 256 instead of 1 and see what happens.

      For the sake of the discussion the biggest value you can store in an integer variable in SIMIV BASIC 128 is 32767 (if you try with values bigger than that the BASIC interpreter will give you an Overflow error).

      So, to use SAVEM and store both variables values and code in a single file you need to make sure you respect those simple (but really important) rules:

      1) Make sure the right BANK is selected and that your entire BASIC program resides in a single memory BANK
      2) That you know from which address exactly your variables are stored (this will be your initial address for SAVEM)

      However there is another issue here… when you will run your program variables will be initialised again and that means that the values you have stored in your BINARY file and loaded statically will most likely be lost, so your program should also be written in a way that won’t reinitialise those variables again and that may become problematic to write, however here is a suggestion:

      a) Write your program in two separate programs, one that only initialise variables (and that you can store in BANK 1 for example) and one that just use those variables as initialised by the first program. Store the 2nd program in BANK 2 or 3.
      b) Before using the SAVEM command switch to the BANK containing the second program and then use SAVEM.

      I had no time to test this approach, however I know the two programs will use same variables in BANK 0 (which is not commutable) so, in theory this may work, to you the testing of such an approach and let me know if it works on not ๐Ÿ™‚

      Good luck and thanks for reading my blog!
      – Paolo

      Like

      • After 30 years I solved! ๐Ÿ™‚ I thank you for letting me play with the pc128 again!

        My solution:

        Enter the following code in the emulator (select 1 SIMIV BASIC 128, then select simulate keyboard from the menu file)

        10 CLS
        20 SCREEN 1,2,3
        30 PRINT “0123446789ABCDEF”
        40 DIM T%(1000)
        50 A=1000
        60 FOR I = O TO 15
        70 GET(I,0)-(I,0),T%(A)
        80 PRINT I;” “;T%(A)
        90 A = T%(A)
        100 NEXT I
        110 PRINT “SAVEM “+CHR$(34)+”VARIABLE”+CHR$(34)+CHR$(44)+”&H”+HEX$(VARPTR(T%))+CHR$(44)+”&H2FFF”+CHR$(44)+”&H”+HEX$(VARPTR(T%))
        120 PRINT:PRINT:PRINT
        130 PRINT “SAVEM “+CHR$(34)+”VALUE”+CHR$(34)+CHR$(44)+”&H”+HEX$(VARPTR(T%(1000)))+CHR$(44)+”&H”+HEX$(VARPTR(T%(0)))+CHR$(44)+”&H”+HEX$(VARPTR(T%(1000)))

        1) After RUN press the two SAVEM texts on the screen to save the .BIN flies
        2) Proceed with a HARDWARE RESET of the emulator or PC
        3) Enter the following code and RUN

        10 CLS
        20 DIM T%(1000)
        30 A=1000
        40 LOADM “VARIABLE”
        50 LOADM “VALUE”
        60 FOR I=0 TO 15
        70 PUT(0,I),T%(A)
        80 A=T%(A)
        90 NEXT I

        now I can use the variable and the values โ€‹โ€‹stored in the .BIN files

        Thanks Paolo! I hope to read more articles on pc128 soon

        Liked by 1 person

  1. I was doing tests with the GET and PUT instructions that allow to save screen portions in a matrix, I wanted to memorize the value of these variables in a .BIN file

    Liked by 1 person

    • If this is what you need to do then probably a best way to do this is to use SAVEM to save the video memory in a BIN file, unless you really need each video memory byte values in your matrix.

      In some game of the past this technique was used to load the splash-screen of a game while some music was being played on the background. If your intent is to use it the same way then I would recommend to try to use a data file to store the values of your video memory and load them like line by line, or even better byte by byte and see if there is enough CPU cycles to play some music while the image is loaded. However to make it smooth this may requires some buffering first and possibly using a Disk Image instead of the tape.

      Like

      • Speaking of music on the PC128, do you have any idea why it was so difficult to have background sound? The same command PLAY blocked the PC until the end of the song. I remember only one videogame that had background music. On the Commodore 64 when starting a piece of music from BASIC this remained in the background and you could continue to work on the basic editor.

        Liked by 1 person

      • So the truth about this is a bit complicated… I would not compare the PC128 to the C64 (C64 had a quite powerful dedicated chip for sound the SID). However the PC128 actually has 3 channels sound BUT the SIMIV BASIC only used one. To simulate background music on the PC128 in BASIC you can try to use the ON INTERVAL function which jumps to a given routine ever x intervals and you need to write your music code in a way that returns after every single note (possibly even returning every half a note, which is something I used to actually generate background noise more than background music, like shooting noise in games). In SIMIV BASIC it’s going to be painful. However in Assembly it’s actually better.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s