(Reprinted from C= Hacking #12)
@(#): trick: 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.
@(A): 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.
@(A): 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.
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 # sta $a000 ; at $A000 to bypass
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