RUN64:Moving to 64 Mode

By Doug Cotton (doug.cotton@the-spa.com)

Reprinted with permission. Copyright (c) 1996 Creative Micro Designs, Inc.

Various routines have been used over the years to allow programs to move from 128 mode to 64 mode without user intervention. With the advent of modified Kernal ROMs (JiffyDOS, RAMLink, and others) many of the methods that work on stock machines have either failed to do the job completely, and in some cases fail all together.

RUN64 is the answer to those users looking to worm their way into 64 mode without having to be concerned with the different Kernal ROMs. The program is presented here in two ways: as a BASIC program that will move to 64 mode and load the program you request, and as assembly language source for ML programmers.

BASIC Notes: The BASIC version uses the ML code produced by the assembly language source. This is found in the data statements beginning at line 660. When you run it, the program will ask for the file name, device number, and file load type (BASIC or ML). The first two parameters should be self-explanatory, but the load type may confuse you. If the file you're loading is itself a small loader (1, 2 or 3 blocks) then it will almost always be an ML program. Likewise, if you usually load the file with a ",8,1" at the end of the load statement, it's ML. If you're loading a larger file, or a file that you normally load with just a ",8", then use the BASIC option.

Also, if you remove the REM instructions from lines 150 through 180 the program becomes a dedicated loader. Just specify the file name and other options within those lines.

How The Routine Works

RUN64 performs its trick by masquerading as a cartridge. When started, the code copies the payload routines into $8000, with the special header that signifies a cartridge is present. It then resets the system. The system initializes and checks for a cartridge. When it finds the payload routines, it executes them just like it would any cartridge. The pseudo-cartridge routines then switch out BASIC, call the remainder of the KERNAL init routines, switch BASIC in, call some BASIC init routines, set the "load" and "run" lines on screen, dump some "returns" into the keyboard buffer, and finally jump into the BASIC interpreter.

Assembly Language Notes:

The source code is pretty well documented, and ML programmers should have little trouble figuring out what everything does. Take note of the Buddy Assembler .off pseudo-op used a few lines below the code label. This adjusts all fixed references within the code that follows it to execute properly at $8000.

The code uses some indirect vectors (ibv, ibr and ibm) to overcome not having an indirect jsr opcode, and switches out BASIC ROM temporarily since the KERNAL finishes intializing by indirectly jumping through the address at $a000. Since the target application hasn't been loaded yet, the code must put its own address at $a000 to regain control.

To use the routine, just set up a file name at filename, put a device number in $ba, set the load type in sa1flag, then execute the routine.

Source Code:

   100 rem run64.bas (c) 1996 creative micro designs, inc.
   110 :
   120 print "{CLEAR/HOME}run64"
   130 print
   140 :
   150 rem f$="filename" : rem filename
   160 rem dv=peek(186)  : rem device number (8, 9, 10, etc.)
   170 rem l$="a"        : rem load type (a=basic, b=ml [,1])
   180 rem goto 310
   190 :
   200 input "filename";f$
   210 input "{2 SPACES}device";dv$ : if dv$="" then 230
   220 poke 186,val(dv$)
   230 dv = peek(186)
   240 print
   250 print "select a or b"
   260 print "{2 SPACES}a.
   load";chr$(34);f$;chr$(34);",";right$(str$(dv),len(str$(dv))-1)
   270 print "{2 SPACES}b.
   load";chr$(34);f$;chr$(34);",";right$(str$(dv),len(str$(dv))-1);",1"
   280 get l$ : if l$<>"a" and l$<>"b" then goto 280
   290 print
   300 print l$;" selected"
   310 print
   320 print "going to 64 mode!"
   330 :
   340 : rem poke in main ml
   350 :
   360 i = 6144
   370 read d
   380 if d = -1 then 450
   390 poke i,d
   400 i = i + 1
   410 goto 370
   420 :
   430 : rem poke in filename
   440 :
   450 for i = 0 to len(f$)-1
   460 : poke 6356+i, asc(mid$(f$,i+1,1))
   470 next i
   480 poke 6356+i,0
   490 :
   500 : rem poke in device number
   510 :
   520 if dv$="" then 570
   530 poke 186,val(dv$)
   540 :
   550 : rem check load type
   560 :
   570 poke 6324,0
   580 if l$="b" then poke 6324,1
   590 :
   600 : rem sys to ml
   610 :
   620 sys6144
   630 :
   640 : rem ml data
   650 :
   660 data 32,115,239,160,0,185,22,24
   670 data 153,0,128,200,208,247,165,186
   680 data 141,157,128,76,77,255,9,128
   690 data 9,128,195,194,205,56,48,169
   700 data 0,141,4,128,120,169,0,141
   710 data 22,208,32,132,255,32,135,255
   720 data 169,230,133,1,169,43,141,0
   730 data 160,169,128,141,1,160,76,248
   740 data 252,169,231,133,1,32,148,128
   750 data 32,151,128,32,154,128,162,0
   760 data 189,159,128,240,6,32,210,255
   770 data 232,208,245,162,0,189,190,128
   780 data 240,6,32,210,255,232,208,245
   790 data 162,0,189,180,128,240,6,32
   800 data 210,255,232,208,245,173,158,128
   810 data 240,10,169,44,32,210,255,169
   820 data 49,32,210,255,169,145,32,210
   830 data 255,32,210,255,173,157,128,133
   840 data 186,162,0,189,185,128,240,6
   850 data 157,119,2,232,208,245,173,158
   860 data 128,208,2,169,4,133,198,76
   870 data 157,227,108,149,227,108,152,227
   880 data 108,155,227,0,0,17,17,68
   890 data 86,61,80,69,69,75,40,49
   900 data 56,54,41,58,76,79,65,68
   910 data 34,0,34,44,68,86,0,13
   920 data 82,213,13,0,70,73,76,69
   930 data 78,65,77,69,0,-1



; RUN64.SRC
; Doug Cotton & Mark Fellows
; (c) 1996 Creative Micro Designs, Inc.
;
        .org    $1800
        .obj    run64.obj

run64   jsr     $ef73   ; go slow
;
        ldy     #0      ; copy cartridge
-       lda     code,y  ; code to $8000
        sta     $8000,y
        iny
        bne     -
;
        lda     $ba     ; get device number
        sta     dvtemp  ; and store it
;
        jmp     $ff4d   ; go 64
;
code    .byt    $09,$80 ; cold start
        .byt    $09,$80 ; warm start
        .byt    $c3,$c2,$cd,$38,$30     ; cbm80
;
        .off    $8009   ; offset code
;
        lda     #$00    ; disable
        sta     $8004   ; cartridge code
        sei             ; disable interrupts
;
        lda     #$00    ; zero out
        sta     $d016   ; VIC control Register
;
        jsr     $ff84   ; initialize I/O
        jsr     $ff87   ; initialize RAM
        lda     #$e6    ; switch in RAM
        sta     $01     ; at $A000
        lda     #reenter       ; BASIC statup during
        sta     $a001   ; initialization
;
        jmp     $fcf8   ; let Kernal finish up
;
reenter lda     #$e7    ; back from Kernal, set
        sta     $01     ; $A000 back to ROM
;
        jsr     ibv     ; initialize vectors
        jsr     ibr     ; initialize RAM
        jsr     ibm     ; initialize memory
;
        ldx     #$00    ; output screen text
-       lda     part1,x ; to form LOAD statement
        beq     +
        jsr     $ffd2
        inx
        bne     -
;
+       ldx     #$00    ; print filename to be
-       lda     filename,x      ; loaded at end of
        beq     +       ; LOAD statement
        jsr     $ffd2
        inx
        bne     -

;
+       ldx     #$00    ; print device
-       lda     part2,x ; variable at end
        beq     +       ; of LOAD statement
        jsr     $ffd2
        inx
        bne     -
;
+       lda     sa1flag ; check secondary
        beq     +       ; address flag for load
        lda     #','    ; type, and print a
        jsr     $ffd2   ; comma and a 1 at end
        lda     #'1'    ; of LOAD statement if
        jsr     $ffd2   ; load type is ML
;
+       lda     #$91    ; print two CRSR up
        jsr     $ffd2
        jsr     $ffd2
;
        lda     dvtemp  ; get device number
        sta     $ba     ; and store
;
+       ldx     #$00    ; put [RETURN]rU[RETURN]
-       lda     keydata,x       ; into keyboard buffer
        beq     +
        sta     $0277,x
        inx
        bne     -
;
+       lda     sa1flag ; get load type and
        bne     +       ; branch if it is ML (1)
        lda     #$04    ; if not ML, change .A
+       sta     $c6     ; store kybd buffer NDX
;
        jmp     $e39d   ; enter BASIC
;
ibv     jmp     ($e395) ; initialize vectors
ibr     jmp     ($e398) ; initialize RAM
ibm     jmp     ($e39b) ; initialize memory
;
dvtemp  .byt    $00     ; device number temp
sa1flag .byt    $00     ; load type (1=ML,
                        ; 0=BASIC)
;
part1   .byt    $11,$11 ; 2 CRSR up
        .byt    'dv=peek(186):load'
        .byt    $22     ; quote
        .byt $00
;
part2   .byt    $22     ; quote
        .byt    ',dv'
        .byt $00
;
keydata .byt    $0d     ; [RETURN]
        .byt    'rU'    ; shortcut for RUN
        .byt    $0d     ; [RETURN]
        .byt    $00
;
filename        .byt    'filename'      ; name of file to load
        .byt $00        ; 00 byte must follow filename!
;
        .end


Document Revision B