12

Commodore disk drives such as the 1541 used with the Commodore 64 and similar computers, and the earlier 8050 used with the PET series, had their own 6502 CPU and 2 kB or so of RAM used to run the code that read/wrote the disk and talked to the computer via the serial bus protocol.

Obscure uses of the Commodore 8050/1541 disk drive discusses downloading new programs that do interesting things such as directly search the disk (returning only the results), change how the disk is read and/or written, and so on.

How exactly does one send a new routine to the drive and execute it?

Note that I'm not asking here how I can make sure the code I send works with Commodore DOS or whatever else is in the drive or anything like that; I just want to understand how I could send code to the drive that would, e.g., LDA #0, and then how I would execute that, even if I can't see the result (and even if it requires resetting the drive afterwards to make it usable again).

1
  • Most common usage for this was software fast loaders that relied on 1541's RAM, rather than replacing its ROM like JiffyDOS. Coolest usage for this was Fast Hack 'Em, which allowed two 1541s to duplicate disks very quickly without needing a C64 connected to them, once the little app was downloaded to the drives.
    – Brian H
    Commented Sep 9, 2019 at 16:35

3 Answers 3

12

How exactly does one send a new routine to the drive and execute it?

Usually by executing the Memory-Write command twice followed by UserN command, as described in chapter 8 of the floppy manual (*1).

Once to place the routine to be used and then to setup its address as user function (or whatever it is supposed to replace).

A suitable function to do so may look like this:

1000 REM Write to Floppy RAM
1010 REM AD  -> Address to be written to
1020 REM DA$ -> Data (code) to be stored
1030 OPEN 15,8,15 : REM Open command channel
1040 PRINT#15,"M-W";CHR$(AD);CHR$(AD/256);CHR$(LEN(DA$)) : REM Send Memory Write command
1050 PRINT#15,DA$ : REM Send Data (code)
1060 CLOSE 15
1070 Return

Alternative, a Memory-Execute can be inserted, to execute the code right away:

1055 PRINT#15,"M-E";CHR$(AD);CHR$(AD/256)

(The code shown is made for C64/1541/DOS 2.6 and from memory, thus no warranty on correctness. Nonetheless the instructions used should work on next to all Commodore computers/drives. Of course you may want to look up the specific address layout for the drive you're using, as well as more advanced command if using a newer BASIC.)

The function can move any data to any place (*2).

If it's about extending DOS, then pointers within DOS needs to be updated. This can be done as well with the above function (without the M-E line). Of course addresses to be used for program, and how to patch pointers and where they are located, depends on the drive in use and the DOS installed. So you may want to take a look at the corresponding DOS disassemblies.


Going into all detail of Commodore DOS and possible usage is way too broad for this (or any single question), so here only a few hints. While they work on most drives, this will use the 1541 as base.

  • Using a file buffer is a great place to store a routine (best one were no other file operation is interfering). 1541 got 5 buffers, of 256 bytes each, located at $300.

  • If it's about making simple routines for parallel processing, then the User-Functions U3..U8 are made for that (*3). Their entry points are located at $500+((N-3)*3) (*4) and can be called by simply sending the User command ("Un") as often as desired.

  • Further parameters can be put in the command string - just keep it short as the command buffer is just 66 (?) bytes.

  • Since the default pointer for the User commands is in buffer 2, writing your code to $500 may set up "Un"-entry point and code in one operation.

  • In fact, all of that can be done in one operation if the code resides on floppy, Block-Execute ("B-F"), which loads a floppy block into a buffer and executes right there. Of course the block has to be written to disk first.

  • A more sophisticated way is to use explicit Block-Read operations to 'page in' code blocks, from disk, to be executed by User function (or Memory-Execute). Now even complex module/overlay solutions are possible.

  • The most simple way to get information back it reading some memory (like the very same buffer the code has been written to or parts thereof) with Memory Read ("M-R").

  • Otherwise writing to the error buffer can be used to return results as error messages

  • Or put it into file buffer of a file that has been opened previous and just read it. This may need a bit of fiddling with buffer internals, but may be the most rewarding way.


*1 - Isn't it funny how much pseudo secrets are running around this when everything needed is described in the manual?

*2 - Ofc, only useful if there's RAM and only if none of the locations are used by the parts of DOS involved with this command.

*3 - U1/2 points to otherwise useful functions and can't be redirected as easy.

*4 - Yes, that's right in buffer #2. But there's also a pointer somewhere that can be made to locate them else were

7
  • 1
    What does the 15,8,15 mean in the OPEN statement, and how does one actually execute the code?
    – cjs
    Commented Sep 8, 2019 at 16:53
  • 1
    @CurtJ.Sampson It's the way Commodore machines talk to devices. values are file#,device#,secondary_address. Secondary address 15 is, on a floppy, the command channel . You may want to check the C64 UM for more. There are countless ways of execution. Direct via 'M-E', indirect via 'Un' functions, block-related via 'B-E' or using any extension to DOS itself. CMD-DOS is bad. But describing the very basics would go way beyond a single question. I added some hints for search, beyond that you may want to read the fine manuals
    – Raffzahn
    Commented Sep 8, 2019 at 17:13
  • I think it would be worthwhile to update the answer to explain why one uses 15 in place of a filename, that 8 is the address of the first disk drive, and 15 accesses the command channel for drive 8. I agree that there's no need to go into the details about Commodore DOS and how to be compatible or interface with it; this question is simply about how one sends code to the drive and executes it on the drive.
    – cjs
    Commented Sep 8, 2019 at 17:21
  • @CurtJ.Sampson The first 15 is the file number and the second 15 is not instead of the filename, but the secondary channel. A filename would be (part of) the next (4th) parameter, the command string. Opening a command channel does not need one. An OPEN command is quite essential, it is safe to assume that someone asking for the very details of a in floppy memory operation already has basic exposure to Commodore I/O handling. Adding more details in a meaningful way may be way below the topic asked. Readers may complain about too much information cluttering the answer. Don't you think so?
    – Raffzahn
    Commented Sep 8, 2019 at 17:38
  • I have basic exposure to Commodore I/O handling in that I understand commands like LOAD "somefile",8,1. I'm not so familiar with the OPEN command or whether, e.g., the first 15 can just be any random number I choose or whether it has some meaning. (All the rest of the code seems fairly obvious to me.) Of course, it's up to you to decide whether you're answering the question for the original poster or for whatever you feel they should know or not know.
    – cjs
    Commented Sep 8, 2019 at 17:55
4

How exactly does one send a new routine to the drive and execute it?

The file called "instructions" in the "1541 Fastload Routine" directory explains:

"The fast-loader consists of two separate programs: one program in the C-64 controls the loading process, and another program in the 1541 sends over the file that is being loaded.

The 1541 code sits immediately at the end of the C-64 code, and when the program is run, it transfers the 1541 code into the disk drive and executes it.".

See the directory for numerous examples and source code. If you can read .ARC compressed files everything seems to be packaged in this directory.

Another explanation is offered on the webpage: "The Ultimate Commodore 64 Talk @25C3":

"Since loading an application or a game takes minutes on an unmodified C64, several 'floppy speeders' appeared (either as software on disk or built into applications, as ROM extension cartridges, or as internal replacement ROMs), that consisted of implementations of more optimized protocols for the IEC bus for both the C64 and the 1541.

The 1541 code was uploaded using the old bus protocol. Such a new protocol would for example not do a handshake on every bit using the clock line, but shift a complete byte through in 4 steps, two bits at a time, using the clock and data line at the same time.

This would of course only work if both CPUs were not interrupted. VIC timing on the C64 side could already affect this, so many floppy speeders turned off the screen while loading.".

[Note: It is possible to avoid screen blanking by using timing protocols based on scan line information on the Vic chip to transfer only on those boundaries – the horizontal flyback and vertical flyback windows.]

A more in-depth explanation is offered on the webpage: "A 256 Byte Autostart Fast Loader for the Commodore 64", it even explains the trick of writing the code on a disk sector and executing on the drive without transfering the program over the bus.

0

An alternative way is this:

Transfer 256 bytes to drive at $500

10 OPEN2,8,2,"#2"
20 READ F
30 FOR X=1 TO 255
40 READ B:PRINT#2,CHR$(B);
50 NEXT
60 PRINT#2,CHR$(F);
70 CLOSE 2
80 DATA 0,1,2,3,4,5,6.....

In basic it sounds weird but in assembler is easier and shorter to code than all the M-W

Note: I transfer the first byte as the last because the block pointer starts at 1 and not at 0 and overflows at 0 after reaching 255.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .