How to fix programs or games for IDE64


Tomas Pribyl - dundera@volny.cz
Soci/Singular - soci@c64.rulez.org

Revision 1.1 - 7.4.2002

General rules:

IDE DOS forbidden routines

$FF8A (or $FD15) - RESTOR
Description: Restore default system and interrupt vectors
Replace with: VECTOR

Guess what happens when the original vectors are restored... The call to this routine can be NOPed in most cases, but if the program destroys the vectors, use VECTOR to backup at the beginning and than restore vectors.

$FF8D (or $FD1A) - VECTOR
Description: Initialize system and interrupt vectors
Implementation: Standard
Communication registers: X, Y, C
Preparatory routines: None
Error returns: None
Registers affected: A, X, Y

Not really forbidden, until the original vector content is preserved. If C flag is high, it backups the vectors to (X,Y), if C=0 it loads the new vectors from (X,Y). Here's an example for replacing RESTOR:

	ldx #<temp	;32 bytes unused memory
	ldy #>temp	;for backup
	sec
	jsr Vector
	jmp startme	;start program
	...
	jsr restor	;RESTOR replaced
	...

restor	ldx #<temp
	ldy #>temp
	clc
	jmp Vector

Non IDE routines

$FFB1 (or $ED0C) - LISTEN
Description: Command a device on the serial bus to listen
Replace with: CHKOUT (see SECOND)
$FF93 (or $EDB9) - SECOND
Description: Send secondary address for LISTEN
Replace with: CHKOUT

LISTEN and SECOND is used to select output serial device, like CHKOUT does. Backup register X if needed for CHKOUT.

	lda $ba		;device number
	jsr Listen
	lda #15+$60	;channel 15
	jsr Second
$FFB4 (or $ED09) - TALK
Description: Command a device on the serial bus to TALK
Replace with: CHKIN (see TKSA)
$FF96 (or $EDC7) - TKSA
Description: Send a secondary address to a device commanded to TALK
Replace with: CHKIN

TALK and TKSA is used to select input serial device, like CHKIN does. Backup register X if needed for CHKIN.

	lda $ba		;device number
	jsr Talk
	lda #15+$60	;channel 15
	jsr Tksa
$FFA5 (or $EE13) - ACPTR
Description: Get data from the serial bus
Replace with: CHRIN

Don't forget to replace serial open to OPEN, and TALK+TKSA to CHKIN.

$FFA8 (or $EDDD) - CIOUT
Description: Transmit a byte over the serial bus
Replace with: CHROUT

Don't forget to replace serial open to OPEN, and LISTEN+SECOND to CHKOUT.

$FFAB (or $EDEF) - UNTLK
Description: Send an UNTALK command
Replace with: CLRCHN

Backup register X if needed.

$FFAE (or $EDFE) - UNLSN
Description: Send an UNLISTEN command
Replace with: CLRCHN

Backup register X if needed.

$F3D5 - direct call to serial OPEN
Replace with: OPEN
$F642 - direct call to serial CLOSE
Replace with: CLOSE
$F34A - direct call OPEN
Replace with: OPEN
$F291 - direct call CLOSE
Replace with: CLOSE
$F20E - direct call CHKIN
Replace with: CHKIN
$F250 - direct call CHKOUT
Replace with: CHKOUT
$F157 - direct call CHRIN
Replace with: CHRIN
$F13E - direct call GETIN
Replace with: GETIN
$F1CA - direct call CHROUT
Replace with: CHROUT
$F4A5 - direct call LOAD
Replace with: LOAD
$F5ED - direct call SAVE
Replace with: SAVE
$F32F - direct call CLALL
Replace with: CLALL
$F333 - direct call CLRCHN
Replace with: CLRCHN

IDE DOS Extended routines

$FFB7 - READST
Description: Read status word
Implementation: Standard
Communication registers: None
Preparatory routines: None
Error returns: None
Status: None
Registers affected: A
	jsr ChrIn	; read data from file
	sta Data,y
	jsr ReadSt	; test status
	and #$40	; End of File flag
	bne EndOfFile
$90 - device status
BITMEANING
7Device not present
6End of File
5(Tape CRC error)
4Verify/read error (Tape read error)
3(Tape long block)
2(Tape short block)
1No more bytes
0Timeout
$FF90 - SETMSG
Description: Control kernal message printing
Implementation: Standard
Communication registers: A
Preparatory routines: None
Error returns: None
Status: None
Registers affected: A
	lda #$00
	jsr Setmsg	;turn off messages	
$9D - messages
BITMEANING
7Full error messages (LOADING, etc.)
6Kernal error messages (I/O ERROR#x)
5..0Undefined
$FFE1 - STOP
Description: Check if stop was pressed
Implementation: Standard
Communication registers: A
Preparatory routines: None
Error returns: None
Status: None
Registers affected: A, X

Returns Z=1 and calls CLRCHN if stop was pressed

	jsr Stop
	beq stop	;Stop was pressed
$FFBA - SETLFS
Description: Sets Logical File Number, Device Number, and Secondary Address
Implementation: Standard
Communication registers: A, X, Y
Preparatory routines: None
Error returns: None
Status: None
Registers affected: None

Also known as the first three parameters of BASIC OPEN command.

	lda #filenumber	; file number (1-255)
	ldx $ba		; actual device number
	ldy #secaddy	; secondary address
	jsr SetLfs
DEVICE NUMBERDEVICE
0Keyboard
1Datassette(TM)
2RS-232C device
3CRT display
4..5Serial bus printer
6..7Serial bus plotter
8..11CBM serial bus disk drive
12Primary IDE controller, Master
13Primary IDE controller, Slave
14PC-link (serial or parallel)
15..30Other
31..255Illegal

SECONDARY ADDRESS
0Read access (load)
1Write access (save)
2..14Data channel
15Status/command channel
16..127Illegal
BIT 7 setNo command string(?)
$FFBD - SETNAM
Description: Sets filename for OPEN/LOAD/SAVE routine
Implementation: Standard
Communication registers: A, X, Y
Preparatory routines: None
Error returns: None
Status: None
Registers affected: None
	lda #8		; filename length
	ldx #<Name	; pointer to filename, low byte
	ldy #>Name	; pointer to filename, high byte
	jsr SetNam
	...

Name	.text "filename"
$FFC0 - OPEN
Description: Open a logical file
Implementation: IDE Extended
Communication registers: None
Preparatory routines: SETLFS, SETNAM
Error returns: 1, 2, 4, 5, 6, 7, 240 (see errorcodes)
Status: $00, $80 (see READST)
Registers affected: A, X, Y
$FFC3 - CLOSE
Description: Close a logical file
Implementation: IDE Extended
Communication registers: A
Preparatory routines: None
Error returns: 0, 240 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X, Y
	lda #FileNumber	; opened file number
	jsr Close
$FFC6 - CHKIN
Description: Set standard input
Implementation: IDE Extended
Communication registers: X
Preparatory routines: valid OPEN
Error returns: 3, 5, 6 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X
	ldx #FileNumber	; opened file number
	jsr ChkIn
$FFC9 - CHKOUT
Description: Set standard output
Implementation: IDE Extended
Communication registers: X
Preparatory routines: valid OPEN
Error returns: 0, 3, 5, 7 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X
	ldx #FileNumber	; opened file number
	jsr ChkOut
$FFCF - CHRIN
Description: Get a character from standard input
Implementation: IDE Extended
Communication registers: None
Preparatory routines: valid OPEN, CHKIN
Error returns: 6 (see errorcodes)
Status: $00, $40, $42, $52, $80 (see READST)
Registers affected: A (Y but not for file I/O)
	ldy #0
	jsr ChrIn
	sta Data,y
	iny
$FFE4 - GETIN
Description: Get a character from standard input
Implementation: IDE Extended
Communication registers: None
Preparatory routines: valid OPEN, CHKIN
Error returns: 6 (see errorcodes)
Status: $00, $40, $42, $52, $80 (see READST)
Registers affected: A (X, Y but not for file I/O)
	ldy #0
	jsr Getin
	sta Data,y
	iny
$FFD2 - CHROUT
Description: Output a character to standard output
Implementation: IDE Extended
Communication registers: A
Preparatory routines: valid OPEN, CHKOUT
Error returns: 7 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: None
	lda #$00
	jsr ChrOut
$FFE7 - CLALL
Description: Close all files and set standard input/output to keyboard/CRT
Implementation: IDE Extended
Communication registers: None
Preparatory routines: None
Error returns: 5 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X
	jsr ClAll	; close all files, set default I/O
	jmp Run		; run program
$FFCC - CLRCHN
Description: Set standard input/output to keyboard/CRT
Implementation: IDE Extended
Communication registers: None
Preparatory routines: None
Error returns: 5 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X
	lda #1
	jsr Close
	jsr ClrChn	; set default I/O
$FFD5 - LOAD
Description: Load memory from file
Implementation: IDE Extended
Communication registers: A, X, Y
Preparatory routines: SETLFS, SETNAM
Error returns: 0, 4, 5, 8, 9, 16 (see errorcodes)
Status: $00, $10, $40, $42, $50, $52, $80 (see READST)
Registers affected: A, X, Y

Note: It's possible to load from $0400 to $FFFF (IDE64 switch $01 memory configuration register automatically).

	lda #1		; filename length
	ldx #<dirnam
	ldy #>dirnam	; filename pointer
	jsr SetNam

	lda #1		; file number
	ldx $ba		; actual device number
	ldy #0		; sec.address 0=new location, 1=original location
	jsr SetLfs

	lda #$00	; load flag (1=verify)
	ldx #<dirbuff
	ldy #>dirbuff	; new start address
	jsr load
	bcc LoadOk
	...
	rts

LoadOk	stx $ae		; new end after load/verify
	sty $af
	...
	rts

dirnam	.text "$"
$FFD8 - SAVE
Description: Save memory to file
Implementation: IDE Extended
Communication registers: A, X, Y
Preparatory routines: SETLFS, SETNAM
Error returns: 0, 5, 8, 9 (see errorcodes)
Status: $00, $03, $80 (see READST)
Registers affected: A, X, Y

Note: It's possible to save RAM from $0400 to $9FFF and $C000-$CFFF

DataBegin = $fb

	lda #1		; file number
	ldx $ba		; actual device number
	ldy #0		; sec.address
	jsr SetLfs

	lda #8		; filename length
	ldx #<Name
	ldy #>Name	; filename pointer
	jsr SetNam

	lda #<$1000
	sta DataBegin	; begin
	lda #>$1000
	sta DataBegin+1
	lda #DataBegin
	ldx #<$8000
	ldy #>$8000	; end
	jsr Save

Name	.text "filename"
$DEF1 - WRITE
Description: Save data block to IDE device
Implementation: IDE only
Communication registers: A, X, Y
Preparatory routines: valid OPEN, CHKOUT
Error returns: 5, 7, 9 (see errorcodes)
Status: $00, $80 (see READST)
Registers affected: A, X, Y

Note: Not possible to save under I/O. (eg. saveing from $D800 will save color ram) To access RAM under the BASIC and KERNAL ROM, set $01 correctly.

If you want your application using READ/WRITE make runnable on a not IDE64 equiped machine, check for IDE64 presence before calling these two calls, and use standard routines instead of, as described for serial drives. (Imagine what happens at JSR $DEF1 if there's open I/O space at $DE00-$DEFF...)

	lda $de60	;Check IDE64
	cmp #$49
	bne old
	lda $de61
	cmp #$44
	bne old
	lda $de62
	cmp #$45
	bne old
	lda #zp
	jsr $def1
	bcc ok
	cmp #9		;illegal device
	beq old2	;tried to use call on non-IDE64 device
ok
	...
	rts

old2	ldx #channel
	jsr Chkout
old	...		;old byte by byte routine
	rts
$DEF4 - READ
Description: Load data block from IDE device
Implementation: IDE only
Communication registers: A, X, Y
Preparatory routines: valid OPEN, CHKIN
Error returns: 5, 6, 9 (see errorcodes)
Status: $00, $40, $42, $52, $80 (see READST)
Registers affected: A, X, Y

Note: This routine does not load under I/O. (e.g. reading to $D800 will overwrite color ram)

	lda #1		; Source filenumber
	ldx $ba		; Actual device number
	ldy #0		; Secondary address for read
	jsr SetLfs
	lda #outputname-inputname
	ldx #<inputname
	ldy #>inputname
	jsr SetNam
	jsr Open	; Open input file

	lda #2		; Destination filenumber
	ldx $ba		; Actual device number
	ldy #1		; Secondary address for write
	jsr SetLfs
	lda #status-outputname
	ldx #<outputname
	ldy #>outputname
	jsr SetNam
	jsr Open	; Open output file

	lda #<StartAdd
	sta $fb
	lda #>StartAdd	; Buffer start address
	sta $fc

	ldx #1		; Set input to source file
	jsr ChkIn

	ldx #2
	jsr ChkOut	; Set output to destination file

loop	lda #$fb	; Pointer to zeropage with start address
	ldx #<BlockSize
	ldy #>BlockSize	; Block size
	jsr Read	; READ
	bit $90		; READST
	php		; Status to stack

	lda #$fb
	jsr Write	; WRITE
	plp		; Status
	bvc loop	; test End Of File

	lda #2
	jsr Close	; Close output file
	lda #1
	jsr Close	; Close input file
	jsr ClAll	; Set default I/O device
	rts

inputname .text "/BIN/INPUT-FILE"
outputname .text "/TMP/OUTPUT-FILE"
status	.byte 0
Errors returned by previous routines:
CODEMEANING
0Routine terminated by the <STOP> key
1Too many open files
2File already open
3File not open
4File not found
5Device not present
6File is not an input file
7File is not an output file
8File name is missing
9Illegal device number
16Out of memory
240Top-of-memory change RS-232 buffer allocation/deallocation

Memory configurations

IDE64 has four configurations of external memory. STND, 8kB, 16kB and OPEN. External RAM is available only in OPEN configuration. External RAM is not intended for users! Please DO NOT change any information in External RAM!

Switching is made by WRITE instruction on following addresses in I/O area:

	sta $deff	; switch to STND configuration
	sta $defe	; switch to OPEN configuration
	sta $defd	; switch to 8kB configuration
	sta $defc	; switch to 16kB configuration

Normally, the system is in the STND configuration, only additional code is located between $DE60-$DEFF. Most of the system vectors are redirected to this area. If you call I/O routines in the kernal table between $FFC0 and $FFE7 you use IDE DOS extended routines. The memory configuration is changed, External ROM is turned on (and external RAM sometimes too). System checkes, if you access IDE device or standard device. If you access standard device, External ROM is disabled and system calls standard routines in C64/C128 ROM.

STND configuration (/EXROM=high, /GAME=high)
Same configuration as without IDE64 controller, only additional code in I/O area $DE60-$DEFF is present. IRQ is served the same way as in normal C64.
8kB configuration (/EXROM=low, /GAME=high)
Same configuration as STND, only additional IDE ROM memory is present between $8000-$9FFF. If you have IRQ routine located between $8000-$BFFF, you must redirect IRQ vector to special serving routine in different area, there switch $01 to disable ROM and then call original IRQ. After IRQ you must restore switch $01 for IDE DOS!
16kB configuration (/EXROM=low, /GAME=low)
Same configuration as STND, only additional IDE ROM memory is present between $8000-$BFFF. Original BASIC ROM is disabled. Filename can't be in RAM between $A000-$BFFF.
OPEN configuration (/EXROM=high, /GAME=low)
External IDE RAM is between $1000-$7FFF. It can be accessed only by CPU, not by VIC. Additional IDE ROM memory is present between $8000-$BFFF, high part is mirrored between $E000-$FFFF. IRQ is served by special way, which delays IRQ/NMI by 30-40 cycles.

Limitations

IDE64 extended routines uses all hardware memory configurations: STND, 8kB, 16kB and OPEN.

$01 memory configuration scheme (the lowest 3 bits)

CONFIGURATION$1000-$7FFF$8000-$9FFF$A000-$BFFF$D000-$DFFF$E000-$FFFF
0STNDRAMRAMRAMRAMRAM
8kBRAMRAMRAMRAMRAM
16kBRAMRAMRAMRAMRAM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
1STNDRAMRAMRAMCHAR ROMRAM
8kBRAMRAMRAMCHAR ROMRAM
16kBRAMRAMRAMRAMKERNAL ROM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
2STNDRAMRAMBASIC ROMCHAR ROMKERNAL ROM
8kBRAMRAMRAMCHAR ROMKERNAL ROM
16kBRAMRAMIDE ROM HIGHCHAR ROMKERNAL ROM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
3STNDRAMRAMBASIC ROMCHAR ROMKERNAL ROM
8kBRAMIDE ROM LOWBASIC ROMCHAR ROMKERNAL ROM
16kBRAMIDE ROM LOWIDE ROM HIGHCHAR ROMKERNAL ROM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
4STNDRAMRAMRAMRAMRAM
8kBRAMRAMRAMRAMRAM
16kBRAMRAMRAMRAMRAM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
5STNDRAMRAMRAMI/ORAM
8kBRAMRAMRAMI/ORAM
16kBRAMRAMRAMI/ORAM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
6STNDRAMRAMRAMI/OKERNAL ROM
8kBRAMRAMRAMI/OKERNAL ROM
16kBRAMRAMIDE ROM HIGHI/OKERNAL ROM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH
7STNDRAMRAMBASIC ROMI/OKERNAL ROM
8kBRAMIDE ROM LOWBASIC ROMI/OKERNAL ROM
16kBRAMIDE ROM LOWIDE ROM HIGHI/OKERNAL ROM
OPENIDE RAMIDE ROM LOWIDE ROM HIGHI/OIDE ROM HIGH

How to serve IRQ located $8000-$9FFF if you want to use IDE DOS extended routines

Original IRQ vector points at routine on $8000.

($0314/$0315 = $8000)
$8000	...		; original IRQ routine
	...
	...
	rti

Now you must change IRQ vector to special serving routine which is out of $8000-$9FFF.

	sei
	lda $0314
	sta OldIrq
	lda $0315
	sta OldIrq+1	; Store old irq vector

	lda #>NewIrq
	sta $0314
	lda #<NewIrq
	sta $0315	; Set new irq vector
	cli
	rts

O.K. IRQ vector is redirected to the new location. Now you can use IDE DOS extended routines for I/O access do IDE device. Please note, that longer call IRQ routine adds some delay which can cause 'raster bugs'.

	* = $1000	; out of $8000-$9fff
NewIrq			; now actual A,X,Y, return address and status are in STACK
	lda $01
	pha		; store original memory configuration to STACK

	lda #$34
	sta $01		; set new memory configuration, disable rom $8000-$9FFF

	lda #<EndOfIrq	; return address after RTI
	pha
	lda #>EndOfIrq	; return address after RTI
	pha
	lda #$24	; statusword after RTI (disabled IRQ)
	pha
	jmp (OldIrq)	; Call original IRQ routine under ROM
			; It will end with RTI instruction, 3 bytes are taken out
			; from STACK, statusword and return address. Program will
			; continue at return address.

EndOfIrq		; this is called by RTI from original IRQ with disabled IRQ
	pla
	sta $01		; restore original memory configuration
	rti		; 3 bytes are taken out from STACK, original return
			; address and status. Program will continue at
			; original return adress.

After finishing IDE DOS extended routines you can return back original IRQ.

	sei
	lda OldIrq	; original IRQ
	sta $0314
	lda OldIrq+1
	sta $0315
	cli
	rts

How to restart C64 IDE DOS

Note that code have to be located at $02-$7FFF and $C000-$CFFF.

bank_port0 = $de32
cfg_8      = $defd

	lda #$37
	sta $01		; set memory configuration
	sta bank_port0	; set page 0 IDE ROM
	sta cfg_8	; set 8kB configuration
	jmp ($fffc)	; call normal RESET routine

How to reset and detect C64 IDE device correctly

	lda #1
	ldx $ba		; actual device number
	ldy #15		; secondary address = command
	jsr SetLfs

	lda #2		; filename length
	ldx #>init_cmd
	ldy #<init_cmd
	jsr SetNam
	jsr Open
	php
	lda #$01
	jsr Close
	plp
	bcs init_err	; error during OPEN
	rts		; initialized OK

init_err
	...
	...
	rts

init_cmd .text "I:"	; init command

How to read error channel messages

	lda #1
	ldx $ba		; actual device number
	ldy #15		; status/command channel
	jsr SetLfs

	lda #$00
	jsr SetNam	; no filename
	jsr Open	; open status channel
	bcc OpenOk
	lda #1		; error during open
	jsr Close
	sec
	rts


OpenOk	ldx #1
	jsr ChkIn	; set status channel as input
	jsr ChrIn	; read first status character
	asl
	asl
	asl
	asl
	sta Hlp		; store

	jsr ChrIn	; read second status character
	and #$0f
	ora Hlp
	pha		; converted value
                      
Look4Comma
	jsr ChrIn	; get next status character
	cmp #","
	bne Look4Comma

	ldy #0
GetStatusChar
	jsr ChrIn
	cmp #","
	beq EndOfStatus
	sta DiskStatus,y
	iny
	jmp GetStatusChar
               
EndOfStatus
	lda #$00
	sta DiskStatus,y; make NULL terminated string
	jsr ChrIn
	cmp #13
	bne EndOfStatus
	
	lda #1
	jsr Close
	jsr ClrChn

	pla		; status number
	cmp #1		; C=0 if status ok
	rts

How to check IDE64 is enabled

	lda $de60	;Check IDE64
	cmp #$49	;I
	bne no
	lda $de61
	cmp #$44	;D
	bne no
	lda $de62
	cmp #$45	;E
	bne no
	...		;present
	
no	...		;not present

Meaning of IDEDOS error messages

00, OK,00,00
Everything seems to be ok.
23, READ ERROR,00,00
Media error/device timeout. More common when using buggy CDs.
26, WRITE PROTECT ON,00,00
File/device is write protected.
29, DISK ID MISMATCH,00,00
CD changed in drive.
31, SYNTAX ERROR,00,00
Unknown/not implemented command.
62, FILE NOT FOUND,00,00
File couldn't be found.
63, FILE EXISTS,00,00
File/directory already exists.
64, FILE TYPE MISMATCH,00,00
Tried to use unknown filetypes/changedir into file.
73, IDE DOS Vx.xx IDE64,00,00
Identify string for harddrive.
73, IDE DOS Vx.xx CDROM,00,00
Identify string for cdrom.
74, DRIVE NOT READY,00,00
No disk in cdrom.
80, HDD ERROR,00,00
Other errors, which don't have a number yet, like dir not empty.