Tagged: C128 Toggle Comment Threads | Keyboard Shortcuts

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

    Categories: Commodore ( 38 )

    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
    Tags: C128   

    Categories: Commodore ( 38 )

    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
    Tags: C128   

    Categories: Commodore ( 38 )

    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: C128, ,   

    Categories: Commodore ( 38 )

    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 4:27 pm on September 1, 2014 Permalink | Reply
    Tags: C128, ,   

    Categories: Commodore ( 38 )

    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!

     

    Herdware

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





     
  • Jay Versluis 1:42 pm on August 30, 2014 Permalink | Reply
    Tags: C128, , , sd2iec   

    Categories: Commodore ( 38 )

    How to use SD2IEC: a quick command reference 

    Photo 28-08-2014 19 30 57
    I’ve ordered an SD2IEC a few weeks ago from Nic over at http://sd2iec.co.uk. It’s an SD card reader for Commodre computers and emulates many features of the 1541 Floppy Disk Drive.

    I went for the Limited Edition made from authentic recycled C64 plastic – so this little critter used to be a real C64! This has to be one of the coolest gadgets for any Commodore fan in the 21st century. And we know many people like gadgets of any kinds, to do many different things, that’s why we have lists of Electronics & Gadgets – Top9Rated.

    Nic and several other sellers on eBay build the hardware, while the software was developed by Ingo Korb with contributions from others. In this article I’ll explain how you can get the most out of the SD2IEC.

    Reading and Writing Files

    The SD2IEC works out of the box with standard disk commands (such as LOAD and SAVE). This will save a BASIC programme as PRG just like your Commodore would save it to floppy disk.

    You can create, switch into and remove directories on the SD card with your SD2IEC (or your “contemporary” computer of course). You can also read from and write to D64/D71/D81 image files and I’ll explain how this works further down.

    When you’re in a subdirectory (or a disk image) all read/write operations are performed there until you switch images or directories. We can do this by communicating with the SD2IEC via the command channel, as if we’re speaking to a 1541.

    Looks like the SD card is hot-swappable without the need to “safe-eject” as long as none of the lights are on, indicating read/write access.

    Speaking to your SD2IEC

    To issue commands on your SD2IEC we must open that trusty old command channel on the device (number 15), PRINT# the command and then optionally close the channel again. Here’s how to do that:

    OPEN 15,8,15
    PRINT#15,”your command here”
    CLOSE 15
    

    You can also write this on a single line:

    OPEN 15,8,15,”your command”:CLOSE 15
    

    You can also leave channel 15 open and issue more commands. Notice though that if you use any other disk command after opening channel 15 (such as LOAD or SAVE) all channels including 15 are closed automatically. Commands such as DIRECTORY do not close the channel.

    I find it helpful to add these things to one my my programmable keys (C128 and Plus/4) via the KEY command:

    // open command with trailing quotes
    KEY 1,"OPEN 15,8,15,"+CHR$(34)
    

    Hit F1, then type one of the commands below. If the channel is already open, simply issue the command via PRINT#15,”command”.

    Directories

    The CD command lets you navigate the directory structure much like you would in Linux and Windows from the command line.

    // change into the root directory
    CD//
    
    // enter directory “mydir” (relative to where you are)
    CD/mydir/
    
    // enter directory “mydir” (absolute from root directory)
    CD//mydir/
    
    // navigate up one level (like the Linux cd .. command)
    CD:←
    

    You can also create directories to stay organised without the need for other tools. That’s where the MD command comes in handy:

    // create directory in the current location
    MD:mydir
    
    // create directory inside another directory
    MD/mydir/:otherdir
    
    // create a directory absolute to root
    MD//mydir/:otherdir
    

    When you’re done with a directory you can delete it with RD. Note that only empty directories can be deleted, otherwise you’ll get a FILE EXISTS error:

    // remove in current directory
    RD:mydir
    
    // remove directory absolute to root
    RD//mydir/:otherdir
    

    Mounting Disk Images

    The CD command can also be used to mount D64/D71/D81 image files, just as if they were standard directories. The same syntax applies as with switching directories:

    // mount “myimage.d64” in current directory
    CD:myimage.d64
    
    // mount “myimage.d64” in subdirectory /mydir (relative)
    CD/mydir/myimage.d64
    
    // or absolute
    CD//mydir/myimage.d64
    
    // unmount a disk image (go back SD card directory in which the image resides)
    CD:←
    

    You don’t have to mount disk images of course and can instead use a FAT or FAT32 formatted SD card just like it was a floppy disk. However, CBM DOS can only address a maximum of 144 files in a directory which means that a cheap 4GB SD card will be exhausted by this limitation before it’s anywhere near full.

    Changing the Device Address

    By default the SD2IEC is set to be drive number 8. But like its vintage counterpart you can change this to 9,10 or 11 using the following command:

    // change from drive 8 to drive 9
    OPEN 15,8,15
    PRINT#15,“U0>"+CHR$(9)
    CLOSE 15
    

    Replace the CHR$ value with the desired drive number. Just like a real 1541 drive, the SD2IEC will not remember this change upon reset. You can however save the above to its internal EEPROM which will survive subsequent reboots:

    // save drive number to EEPROM
    OPEN 15,9,15
    PRINT#15,“XW"
    CLOSE 15
    

    Replace 9 with your actual drive number.

    Troubleshooting

    If you encounter flashing lights on your device, then the SD2IEC is trying to tell you something. On the C128 and Plus/4 you can read out the disk error channel by looking at the DS$ system variable:

    // print disk error channel on C128 and Plus/4
    PRINT DS$
    

    This will show you what went wrong (FILE EXISTS, FILE NOT FOUND, etc). On the C64 this is a little harder and requires you to write a small programme to read those values out. This is necessary because the INPUT# command can not be used in direct mode:

    // read disk error channel on C64
    10 open 15,8,15
    20 input#15,a,b$,c,d
    30 print a;b$,c,d
    40 close 15
    

    There’s a lot more you can do with this device – check out the full documentation in the README file at Ingo Korb’s website:





     
  • Jay Versluis 11:54 am on August 7, 2014 Permalink | Reply
    Tags: Assembly, C128   

    Categories: Commodore ( 38 )

    How to locate and set the cursor on your Commodore C128 

    Commodore LogoSadly the Commodore machines don’t offer a routine to locate or set the current cursor position via BASIC. There is however a Kernel routine named PLOT which can do this in Machine Language.

    Here’s how we can utilise it.

    Getting the Cursor Position

    . 01300  38       sec
    . 01301  20 f0 ff jsr $fff0
    . 01304  8e 00 14 stx $1400
    . 01307  8c 01 14 sty $1401
    . 0130a  60       rts
    

    This snippet sets the carry flag, calls the PLOT routine at $FFF0 and returns the cursor position in the X and Y registers. We’ll put them in a safe place into $1400 and $1401 to use.

    You can call it from BASIC with

    SYS DEC("1300")
    PRINT PEEK(DEC"1400") : rem row
    PRINT PEEK (DEC("1401") : rem coumn
    

    Setting the Cursor Position

    . 01310  18       clc
    . 01311  ae 00 14 ldx $1400
    . 01314  ac 01 14 ldy $1401
    . 01317  20 f0 ff jsr $fff0
    . 0131a  60       rts
    

    Call it from BASIC by POKEing your desired coordinates into $1400 and $1401, then call SYS DEC(“1310”).

    This snippet will do the reverse of the above: populate the X and Y registers from our safe place and then call PLOT. We clear the carry flag first, because it decides if the position is read (carry clear) or set (carry set).

    PLOT should work fine on the C64 and Plus/4 as well but I didn’t get a chance to test it yet.





     
  • Jay Versluis 9:01 am on July 25, 2014 Permalink | Reply
    Tags: C128, ,   

    Categories: Commodore ( 38 )

    How to merge BASIC programmes on your Commodore C64, C128 and Plus/4 

    Commodore LogoYou can merge / combine / concatenate BASIC programmes on a Commodore computer. It’s often useful to develop shorter chunks of code and put them together for a larger app.

    Sadly there’s no built-in command that lets you do this, even though the C128 user guide hints that the CONCAT command can do this (if you’ve ever tried you know that this is not the case and only works for sequential data files).

    With a few peeks and pokes we can accomplish what we need. Here’s how:

    • after loading the fist programme, we’ll set the start of BASIC to the end of programme 1
    • then we’ll load the second programme
    • and finally return the start of BASIC to the first one

    We’re left with both programmes in memory – without any changes in line numbers. Depending on how you write the code a quick RENUMBER command may be necessary here. Even though you can run a programme when the line numbers are out of sequence, GOSUB and GOTO routines may not work as expected.

    The commands are slightly different for each machine.

    On the C64

    The start of BASIC is stored in 43/44, and ends just before the variables begin – which is stored in 45/46. Load your first programme, then type this:

    n=(peek(45)+256*peek(46))-2
    poke 44,n/256
    poke 43,n-256*peek(46)
    

    Load the second part, then reset the BASIC pointers to where the first part resides:

    poke 43,1
    poke 44,8
    clr
    

    On the Plus/4

    The Plus/4 works almost identical to the C64 in regards to storing the BASIC pointers, however the start of BASIC is at a different address. Load your first programme, then type this (just like on the C64):

    n=(peek(45)+256*peek(46))-2
    poke 44,n/256
    poke 43,n-256*peek(46)
    

    Load the second part, then reset the BASIC pointers:

    poke 43,1
    poke 44,16
    clr
    

    On the C128

    The C128 stores variables in a different memory bank. Therefore it has a dedicated pointer to the end of BASIC which is missing on the other computers. The memory map is slightly different, and the start of BASIC is stored in 45/46. Load your first programme, then type this:

    n=peek(174)+256*peek(175)-2
    poke 46,n/256
    poke 45,n-256*peek(46)
    

    Load the second part, then reset the BASIC pointers:

    poke 45,1
    poke 46,28
    clr
    

    And there you have it! Run any of these routines right after a reset or restart.

    Thanks to Rick Kephart for this tip. You may also be interested in another (more complex) solution to this puzzle by Jim Butterfield in which he reads each byte and merges them back on disk.





     
  • Jay Versluis 1:33 pm on July 8, 2014 Permalink | Reply
    Tags: C128,   

    Categories: Commodore ( 38 )

    How to load and save binary data on the C128 and Plus/4 

    Commodore LogoThanks to BASIC 7.0’s powerful commands we can load and save arbitrary sections of memory to disk using BLOAD and BSAVE.

    This was useful for dealing with machine code as well as raw image data. Both commands work on the C128 and Plus/4, but sadly the C64 was lacking in this area.

    BLOAD and BSAVE work much like DLOAD and DSAVE with the addition that you can specify a section of memory – like this:

    // save contents of the 40 column display
    BSAVE "FILENAME", P1024 TO P2023
    
    // save the contents of hi-res graphics and colour separately
    BSAVE "IMAGE", P8192 TO P16181
    BSAVE "COLOURS", P7168 TO P8167
    
    // save graphics and colour information together
    BSAVE "FULL IMAGE", P7168 TO P16181
    

    BLOAD doesn’t need additional parameters and will load a file back into memory where it originally resided – just like the standard LOAD “FILE”,8,1 (with the 1 at the end). You can however provide the starting memory and make the file load somewhere else instead:

    // load file to a different place in memory
    BLOAD "FILENAME", P10000
    




     
  • Jay Versluis 8:29 pm on July 6, 2014 Permalink | Reply
    Tags: C128, ,   

    Categories: Commodore ( 38 )

    How to set the time on your Commodore C64 / C128 

    Commodore LogoDid you know that the Commodore computers had a built-in clock? It wasn’t an exact science, and it didn’t show the date, but it was able to display a 24hr clock.

    It did this by counting how many seconds your machine was switched on, accessible in two variables TI and TI$ (short for TIME).

    TI$ is a string value in the format HHMMss, counting seconds. 112347 would mean 11:23am and 47 seconds. TI$ will be 000000 when you switch on the system. You can set it just like any other variable to set the time:

    TI$ = "094500"
    

    TI counts the number of 60th seconds since the system was switched on (don’t ask):

    print int(ti/60);" seconds since startup"
    

    Both values reset when you reset the system. Like all internal computer clocks it is relatively accurate, but does drift a second or two per day (we don’t notice this on modern systems because those talk to an NTP server once every day and sync themselves).

    TI and TI$ are present on the C64, C128, Plus/4 and many other Commodore variants.





     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel