Category: Commodore Toggle Comment Threads | Keyboard Shortcuts

30 years after its release it’s time to re-kindle with my first computer spirit – the C64. This was my first computer which changed my life forever. Thanks to VICE we can now enjoy the little guy again on modern hardware. Here are all my notes on how to talk to him, and his other friends from the 8 Bit Age.

  • Jay Versluis 11:43 am on March 14, 2018 Permalink | Reply
    Tags: , , , VIC-20   

    Categories: Commodore ( 26 )

    String Operations on Commodore Computers 

    Commodore BASIC has some interesting and simple string functions built in. Three of them are self explanatory: LEN, LEFT$ and RIGHT$. But others, like the mysterious MID$ and INSTR functions, are a little tricker, and I can never remember how they works.

    So here’s a quick recap on how they all work.

    LEN (A$)

    Returns the length of any given string. For example,

    a$=”the cake is a lie”

    print len (a$)

    returns 17, which is the number of characters in our string.

    LEFT$ (A$,X)

    The LEFT$ function takes the x left characters from a given string. Here’s an example:

    print left$(a$,3)

    We get “one”, because those are the 3 leftmost characters in our string a$.

    RIGHT$ (A$,X)

    Likewise, RIGHT$ takes the x right characters from any given string:

    print right$(a$,5)

    Here we get “three”, because those are the 5 right characters of a$.

    MID$ (A$,X,Y)

    MID$ is a little more complex. It takes x characters from a given string, starting at position y. Let’s look at our earlier example again:

    print mid$(a$,5,3)

    We get “two”, because those are the 3 characters, starting at position 5. The first position in all these string operations counts as one rather than zero.

    But did you know that MID$ can also be used to assign and replace different characters in a string? Consider this:

    print a$

    Now we’ve replaced the 3 characters in our string with another string, starting at position 5.

    I had no idea it cold do that! All these string operations work in all variations of the Commodore BASIC, except for the MID$ assignment which only works on the Plus/4 and the C128.


    INSTR (A$, B$)

    On the Plus/4 and C128, we can even check if one string is contained in another and at which position this occurs. Consider this:

    a$="the cake is a lie"
    print instr(a$,b$)

    In our example, INSTR returns 5 because “cake” has been found at position 5 of “the cake is a lie”.

    We can also specify a position from which the search shall be started like this:

    print instr(a$,b$,6)

    Now INSTR returns 0 because “cake” has not been found beyond position 6 of our input string.

  • Jay Versluis 11:04 pm on March 13, 2018 Permalink | Reply

    Categories: Commodore ( 26 )

    How to play sounds and music on the Commodore Plus/4 

    The Plus/4 has a total of two voices thanks to its integrated TED chip, which is also responsible for rendering text and graphics on screen. The first voice can play square waves, while the second one can generate either square wave sounds or white noise.

    Let’s see how we can make him play a tune.

    We can use some BASIC keywords to make the Plus/4 be all musical. First we need to turn up the volume by using the VOL command. We can set this to anything between 0 and 8.

    VOL 8

    Next we can use the SOUND command to make each channel play a note, like so:

    SOUND 1,400,60

    This will play a one-second long note on channel 1.

    (More …)

  • Jay Versluis 5:16 pm on January 13, 2016 Permalink | Reply
    Tags: , ,   

    Categories: Commodore ( 26 )

    How to use direct block access commands in Commodore DOS 

    Commodore-Logo-PaddedWe can access each sector’s raw data on Commodore disk drives with direct block access commands. Supported drives include the 1541, 1571, the VICE emulator as well as the SD2IEC card reader (for the most part).

    Each single sided floppy contains 35 sectors, while a double sided 1571 formatted disk contains 70 sectors. Each track contains between 17 and 21 sectors depending on how far inside or outside they are. Each sector contains 255 bytes we can read or write.

    Sectors are the same as blocks: only the directory refers to them as “blocks” and shows us how many we have available.

    We’ll need to open two channels to our disk drives: a command channel and a data channel. Here’s how to do it:
    (More …)

  • Jay Versluis 9:17 am on January 2, 2016 Permalink | Reply

    Categories: Commodore ( 26 )

    How to save data to your C128 RAM Expansion Unit (REU) 

    Commodore-Logo-PaddedWith a RAM Expansion Unit (REU), the Commodore 128 could address up to 512k of data. That was huge in the late eighties! All you needed was one of those REUs, plug it into your cartridge port, and so much more super fast memory was at your fingertips.

    But even with such a cartridge at hand, how do we actually make use of it from CBM BASIC 7.0? With three funky commands called STASH, FETCH and SWAP. Here’s how we can use them.

    The REUs cannot be addressed directly, like other memory in your computer. Instead, data has to be either copied from the C128 to the REU, or vice versa, or swapped out. All three commands take the same four parameters:

    • number of bytes to transfer
    • location in the C128 memory to start
    • REU bank (0-7, depending on the size of the REU)
    • location in the REU bank memory

    This sounds more cryptic than it actually is: the largest REU split 512k over 8 banks of 64k, so that the 8bit operating system could address it.

    So to store 200 bytes of C128 memory, starting at location 5000, saving it inside the REU’s bank 0, location 0, we can use the STASH command like so:

    STASH 200,3000,0,0

    To retrieve our data later and bring it back to the same C128 location as before, we can use FETCH with the same parameters:

    FETCH 200,3000,0,0

    Rather than copying, we can also exchange data in two places by literally swapping it over. Again the parameters are the same:

    SWAP 200,3000,0,0

    Enabling REUs in VICE

    Although I have a physical C128, I do not have a real REU. Maybe one day I’ll find one on eBay, but until then there’s an easy way to emulate an REU using VICE.

    To enable one, head over to Settings – Resource Inspector – Cartridges – REU. Pick the size you like, and even a hard disk location to make the contents of your REU survive restarts.

    Screen Shot 2016-01-02 at 09.08.35

    Screen Shot 2016-01-02 at 09.09.19

    Demo Listing

    Here’s a quick test that allows us to store an arbitrary message in memory, then stash or retrieve it from an attached REU.

    100 print "1 store message in internal memory"
    110 print "2 print message"
    120 print "3 stash message in reu"
    130 print "4 retrieve message from reu"
    140 print "5 clear internal memory"
    141 print "6 quit"
    150 input "your choice";m
    160 on m gosub 1010,1090,1210,1410,1310,2000
    170 run
    1000 :
    1010 rem input and save
    1020 input "what shall we store";a$
    1030 for i=1 to len(a$)
    1040 c$=mid$(a$,i,1)
    1050 poke 3000+i,asc(c$)
    1060 next
    1070 return
    1080 :
    1090 rem retrieve message
    1100 print:print "retrieving..."
    1110 for i=1 to 100
    1120 c=peek(3000+i)
    1130 b$=b$+chr$(c)
    1140 next
    1150 print b$
    1160 return
    1200 :
    1210 rem stash to reu
    1220 stash 200,3000,0,0
    1230 return
    1300 :
    1310 rem clear internal memory
    1320 for i=1 to 200
    1330 poke 3000+i,0
    1340 next
    1350 return
    1400 :
    1410 rem retrieve from reu
    1420 fetch 200,3000,0,0
    1430 return
    2000 :
    2010 rem the end
    2020 end

    We don’t see many BASIC listings these days anymore. Enjoy!

  • Jay Versluis 5:58 pm on December 27, 2015 Permalink | Reply

    Categories: Commodore ( 26 )

    How to create relative data files on your Commodore 128 

    Commodore-Logo-PaddedThe CBM DOS can write “relative data” onto disk, based on individual records. The advantage is that all space is allocated once and then randomly accessed if and when it’s needed – much like the tracks on a CD.

    This approach is different from sequential files, which have to read or write the whole file in one go (Apple call this “reading and writing atomically”). So finding the 50th record in a sequential file can take some time, whereas retrieving record 50 from a relative file is just as fast as retrieving record 1 or record 157.

    Let’s see how we can deal with such relative files in CBM BASIC 7.0. This works with all physical disk drives (1541, 1571, etc) as well as the SD2IEC card reader – but not with your trusty old Datasette (for obvious reasons).

    This was revolutionary back in 1982!

    Creating Relative Files

    Here we create a new file with a REL extension using the DOPEN# keyword. When we do this for the first time, we need to tell the operating system how much data we’d like to allocate per record. Anything up to 255 bytes is fine. We’ll use 100 bytes in this example (the letter L followed by the length).

    10 dopen#1,"rel test",l100
    20 for i=1 to 100
    30 record#1,i
    40 a$="record"+str$(i)
    45 print "writing record ";i
    50 print#1,a$
    60 next
    70 close 1
    80 print "all done!"

    When we create a new file, CBM DOS doesn’t know how many records we’ll need, but it will create as many as necessary on the file. Here we create 100 entries using a for loop and write them to disk using the PRINT# statement.

    Notice that before saving a record, we must position the record pointer using RECORD#. That’s how our data is written to the correct position on disk. The whole loop will take a while to complete, but all space will be allocated on the disk as soon as we CLOSE the file.

    To add more records, we’ll simply position the record pointer to a later entry – even if one does not yet exist. CBM DOS will create the last record and all records leading up to it so they can be used later, up to a maximum of 65535 (space permitting obviously).

    Reading data from Relative Files

    Much like with sequential data, we can either use INPUT# or GET# to grab the data for an individual record. INPUT# reads everything until a CHR$(13) is found and works much faster, while GET# reads data one character at a time and is a lot slower.

    10 input"which record shall we retrieve";r$
    20 dopen#1,"rel test"
    30 record#1,val(r$)
    40 input#1,a$
    50 close 1
    60 print:print a$:print
    70 print"need another one? (y/n)"
    80 getkey k$:if k$="y" then goto 10:else end

    Here we ask the user which record we want to retrieve. Next we open our relative file using DOPEN#, position to the desired RECORD# and then read in its value using INPUT#. When we’re done we close the file and ask if the user wants to retrieve another record.

    While this type of data access is quick and convenient, it doesn’t help much unless you know which record is stored at which position. Think of an address book application: to find “Johnny Appleseed” can’t be done unless you sift through every single record until you find him.

    Commodore therefore suggest to use sequential files alongside relative files, in which things like a last name could be saved together with the record number. Likewise, another sequential file could hold all records for the first names, and appended accordingly when a new record is created, or replaced when updated.

  • Jay Versluis 5:36 pm on December 27, 2015 Permalink | Reply

    Categories: Commodore ( 26 )

    How to create sequential files on your Commodore C128 

    Commodore-Logo-PaddedSequential files are files to which we can write arbitrary data and read it back later. We can even append data to the file later without having to re-write the whole file.

    This works with the Datasette (tape drive) as well as floppy drives. Here’s how to do it in CBM BASIC 7.0:

    Creating Sequential Files

    The C128 has a few special commands up its sleeve to aid us in this task. Here we create a new file using the DOPEN keyword and write 100 statements to it.

    10 dopen#1,"@seq test",w
    20 for i=1 to 100
    30 a$="record"+str$(i)
    40 print#1,a$
    50 print "writing ";a$
    60 next
    70 close 1

    We’re creating a new sequential file (SEQ extension rather than the usual PRG), using the w after the filename so that BASIC knows to create the file. The @ sign in front of the file name makes sure this file is overwritten every time we run the programme – omit it if you don’t want that functionality.

    Next we create a loop and generate a variable spelling RECORD 1, RECORD 2, etc. That’s your data. Each entry may be up to 127 characters in length (I believe) and is saved to the file by using the PRINT# keyword. Anything we could print to the screen, we can also print to a file.

    With each new PRINT# command, a carriage return is saved to disk. This can come in handy when we’re reading the data back in. If you need special characters to separate your data, feel free to use them.

    Appending data to Sequential Files

    If we need to add anything to the file (much like Linux would add to the end of a text file using the “greater than” symbol), we can use the APPEND# keyword:

    10 append#1,"seq test"
    20 for i=101 to 150
    30 a$="record"+str$(i)
    40 print#1,a$
    50 print "appending ";a$
    60 next
    70 close 1

    APPEND# opens the file for adding data and positions the pointer after the last entry in our file. We’ll do something very similar as above, creating records 101 to 150 and adding them to the file. Make sure to CLOSE the file so all data is saved to disk.

    Reading Sequential Data

    Much like PRINT# can be used to write data to disk, we can use INPUT# to read data back – almost equivalent to the INPUT keyword get get user input from a keyboard. The only difference is that out input comes from a different device:

    10 dopen#1,"seq test"
    20 for i=1 to 150
    30 input#1,a$
    40 print a$
    50 next
    60 close 1

    Here we open our file with DOPEN# and grab each entry in the file using the INPUT# keyword. This will automatically position the next INPUT# when a carriage return is received (CHR$(13)).

    This will work just fine as long as the original data does not contain any special characters, like a scary comma. If you need such characters, or if you want to ignore the carriage return, you can also use the GET# command to read one character at a time from disk. This is dramatically slower though.

    Notice that there’s no “end of file” marker as such: right now we need to know how many entries there are and read them accordingly. Reading beyond the data in our file will simply repeat the last record (on real devices) or crash your system (on VICE).

  • Jay Versluis 10:30 am on December 24, 2015 Permalink | Reply

    Categories: Commodore ( 26 )

    How to switch the Commodore 1571 into 1541 mode and back 

    Commodore-Logo-PaddedThe Commodore 1571 floppy drive powers up in compatibility mode and behaves just like a 1541 drive – unless a C128 is connected and sends it a fast serial transfer.

    They did this because otherwise the drive wouldn’t be compatible with the C64 and Plus/4.

    The C128 speaks to its attached disk drive when we power it on, and that’s how a 1571 turns on its super fast magical properties. Holding down the Commodore key will boot into C64 mode, and the 1571 will in turn reset itself to 1541 emulation mode.

    Lucky for us, we can change this with a simple command, no matter what computer we’re using or which mode we’re currently in. This is done by first opening a command channel and then sending the 1571 the following switch command:

    // switch on 1571 mode
    OPEN 15,8,15,"U0>M1"
    CLOSE 15
    // switch on 1541 mode
    OPEN 15,8,15,"U0>M0"
    CLOSE 15

    This will let the 1571 behave like a 1571 even when attached to a C64 or Plus/4: format floppies on both sides and enjoy 1328 blocks without flipping disks. Sadly it won’t transfer data any faster because the attached computers just can’t handle it.

    Conversely, if we want to use a C128 and have our drive remain in 1541 mode for testing or compatibility, it works equally well.

    Rebooting your computer will reset this value. As far as I know, there is no way to test which mode the drive is in before issuing either of the above commands.

  • Jay Versluis 9:33 am on June 15, 2015 Permalink | Reply
    Tags: , ,   

    Categories: Commodore ( 26 )

    How to split a long string into separate words in Commodore BASIC 

    Here’s a quick word splitter routine for CBM BASIC. It takes phrase and “explodes” all words into an array, removing spaces. Feel free to adopt it for your own needs.

    10 rem word splitter
    20 rem splits a long phrase into words at a space
    30 input "tell me something";a$
    40 rem clear current array of words
    50 for i=0 to 10: wd$(i)="": next: wd=1
    60 rem detect spaces
    70 for i=1 to len(a$)
    80 wd$(0)=mid$(a$,i,1)
    90 if wd$(0)=" " then wd=wd+1: next
    100 wd$(wd)=wd$(wd)+wd$(0): next
    110 print
    120 rem print all words
    130 for i=1 to 10
    140 if wd$(i)="" then 160
    150 print wd$(i)
    160 next

    Line 50 clears an array of 10 words called WD$(n). We also setup a word counter called WD. So in this example we can only detect a maximum of ten words. If you need more, you must DIM the array first.

    Lines 70 loops through all characters in our phrase. We make use of the first element in our array WD$(0) to store each single character. If it’s a space, we’ll increase the word counter and move on to the next character. If it’s not (line 80), then we’ll add this character to the current word which is stored in WD$(WD).

    The rest of the code simply prints each word on a new line to see our handy work.

  • Jay Versluis 7:49 pm on September 2, 2014 Permalink | Reply

    Categories: Commodore ( 26 )

    Commodore Plus/4 Screen Memory Map (Display RAM) 


    The Commodore Plus/4 – like his other brothers – has memory areas in which screen characters and colour values are stored independently from each other. Because it uses the TED chip these memory addresses are different from the C64 and C128.

    Each character from the top left corner to the bottom right corner occupies one byte of memory, 40 columns across in 25 rows.

    Screen Memory

    • starts at 3072 (decimal) or $0C00 (hex)
    • ends at 4071 (decimal) or $0FE7 (hex)

    You can POKE screen display codes into each position using this formula:

    POKE 3072 + X + 40 * Y, Z
    // where 
    // X = current column
    // Y = current row
    // Z = screen display code
    // write the letter X into the top left corner of the screen
    POKE 3072, 24

    Note that there’s a difference between screen display codes (A = 1, B = 2…) and ASCII/PETSCII codes (A = 65, B = 66…). The latter are used when printing single charters via CHR$ or returning their values via ASC.


    Plus/4 Screen Memory

    Colour Memory

    • starts at 2048 (decimal) or $0800 (hex)
    • ends at 3047 (decimal) or $0BE7 (hex)

    You can POKE colour values into each cursor position using the following formula:

    POKE 2048 + X + 40 * Y, C + L * 16
    // where 
    // X = current column
    // Y = current row
    // C = colour value (0 to 15)
    // L = luminance value (0 to 7)
    // turn the character in the top left corner of the screen light red
    POKE (2048+0+0), 2 + 7 * 16
    // or
    POKE 2048, 114

    Luminance is a TED feature. It was an attempt to increase the colour palette which still only consisted of 16 colours (like on the C64) by adding brightness to each value. When compared to the “pure” 16 colours of its brothers, the Plus/4 palette looks a little weird (for example, the standard BASIC background is white according to its colour value, even though it appears to be some shade of “dirty grey”).

    You can add 128 to each value to make the character flash at the same frequency as the cursor.

    Contrary to the Plus/4 user guide, the colour values are as follows (values in brackets show the luminance variations):

    0  (16 32 48 64 80 96 112) = black
    1  (17 33 49 65 81 97 113) = white
    2  (18 34 50 66 82 98 114) = red
    3  (19 35 51 67 83 99 115) = cyan
    4  (20 36 52 68 84 100 116) = purple
    5  (21 37 53 69 85 101 117) = green
    6  (22 38 54 70 86 102 118) = blue
    7  (23 39 55 71 87 103 119) = yellow
    8  (24 40 56 72 88 104 120) = orange
    9  (25 41 57 73 89 105 121) = brown
    10 (26 42 58 74 90 106 122) = yellow-green
    11 (27 43 59 75 91 107 123) = pink
    12 (28 44 60 76 92 108 124) = blue green
    13 (29 45 61 77 93 109 125) = light blue
    14 (30 46 62 78 94 110 126) = dark blue
    15 (31 47 63 79 95 111 127) = light green

    Plus/4 Colour Memory

  • Jay Versluis 4:27 pm on September 1, 2014 Permalink | Reply
    Tags: , ,   

    Categories: Commodore ( 26 )

    The evolution of Flow Control in Commodore BASIC 

    Commodore-Logo-PaddedWhen I had a C64 back in the days, the only IF/THEN statement we could create in BASIC v2 was just that: IF and THEN.

    There was no ELSE, and everything had to be on a single line. It was the only flow control we had back in those days, and it was a severe limitation compared to what we can do with higher level languages today.

    Here’s an example:

    10 input x
    20 if x=1 then print"x is one" : goto 100

    Such constructs weren’t very convenient when you wanted to react to anything other than the tested value. Instead you had to write an IF statement for every possible other outcome which could make the code very cluttered with several GOTO statements.

    When the Plus/4 came out Commodore added the ELSE statement. It still had to be on the same line as the IF and THEN, but at least we had ELSE as an option:

    10 input x
    20 if x=1 then print "x is one" : else print "x is not one"

    ELSE needs to be on the same line as the IF/THEN statement and separated by a colon.

    ELSE was a very handy addition to the BASIC arsenal, but it still didn’t solve the problem that your evaluation had to be cramped into a single line. This was improved on the C128 with the introduction of BEGIN/BEND.

    BEGIN and BEND allowed you to execute as many lines of code as you wanted between those statements when a condition was true (or false). Used together with IF, THEN and ELSE it was a very useful thing to have:

    10 input x
    20 if x=1 then begin
    30 print "x is one"
    40 print "which is like totally awesome"
    50 rem more code here
    60 bend : else begin
    70 print "x was obviously not one"
    80 print "which is a shame"
    90 print "but that's life"
    100 bend

    BEGIN means that everything starting from now until BEND is met will be executed as part of the IF/THEN statement – as many lines of code you like.

    When BEND is met the code continues as normal – but we can also use ELSE after a colon on the same line, indicating that this is still part of the first IF/THEN statement. Here we can use BEGIN again for as many lines as we like, until we finish with BEND again.

    It was very very cool!



    sys 32800,123,45,6 to show these credits (thanks, team!)

compose new post
next post/next comment
previous post/previous comment
show/hide comments
go to top
go to login
show/hide help
shift + esc