1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
; Load an Atari DOS executable, including support for init and run vectors
; Parameters: nul-terminated filename pointer in A/X (lo/hi)
; Return value: 0 for success, nonzero on error, never returns if exe has
; a run address.
; TODO: learn ca65 syntax better, port this to ca65
; Usage: build with "dasm aexec.dasm -f3 -oaexec.xex"
; "Link" to your program with "cat aexec.xex yourprog.xex > newprog.xex"
; int __fastcall__ (*atari_exec_p)(char *) = (int __fastcall__ (*)(char *))0x600;
; #define atari_exec(x) ((*atari_exec_p)(x))
; then call with: atari_exec("D:PROGRAM.XEX");
; If the file can't be opened for whatever reason, atari_exec() returns a
; nonzero result (the Atari error number).
; If the file loads OK and has a run address, atari_exec() never returns
; (runs the loaded program, then exits to DOS if/when the program exits).
; This means there's no memory conflict between caller and callee.
; If there was no run address, atari_exec returns 0 to the caller.
; The caller is responsible for making sure the loaded program doesn't
; step on the memory used by the caller or atari_exec itself!
; If the file is openable but invalid (not a XEX, or truncated), current
; atari_exec() implementation hangs the machine with a red screen, since
; there's no way to know whether the partially-loaded program overwrote
; the caller...
processor 6502
include "equates.inc"
tmp = $d4 ; aka FR0
loadaddr = $d6
main = $0600
org main-6
word $FFFF
word main
word endmain-1
org main
pha
txa
pha
jsr fclose
pla
tax
pla
sta ICBAL+16
stx ICBAH+16
sta tmp
stx tmp+1
ldy #0
; do an OPEN #1,4,0,$filename
; get length of filename
fnameloop:
lda (tmp),y
beq fndone
iny
bne fnameloop
; set IOCB #1 buffer addr, buffer len, etc
fndone:
sty ICBLL+$10
lda #0
sta ICBLH+$10
sta ICAX2+$10
; init these to 0 so we can tell if they change
sta RUNAD
sta RUNAD+1
sta INITAD
sta INITAD+1
ldx #4
stx ICAX1+$10
dex
stx ICCOM+$10 ; cmd #3 = OPEN
ldx #$10
jsr CIOV ; do the OPEN
bpl readheader
tya ; CIO returns error code in Y reg
ldx #0
rts
fail:
lda #$48
sta 710
hang bne hang
readheader:
; read 2 bytes into local buffer
jsr read2bytes
bpl ok
cpy #136 ; EOF
bne fail
beq close_file ; if at EOF, close the file
; if they're $ffff, try again
ok:
lda tmp
tax
and tmp+1
cmp #$ff
beq readheader
; store those 2 bytes in loadaddr
lda tmp+1
sta loadaddr+1
stx loadaddr
; read 2 more bytes into local buffer (end addr)
jsr read2bytes
bmi fail
; subtract loadaddr from end addr, add 1, to get length
; store length into IOCB
lda loadaddr
sta ICBAL+$10
lda loadaddr+1
sta ICBAH+$10
sec
lda tmp
sbc loadaddr
sta ICBLL+$10
lda tmp+1
sbc loadaddr+1
sta ICBLH+$10
inc ICBLL+$10
bne noinc
inc ICBLH+$10
noinc:
jsr read_segment
bmi fail
; if INITAD modified, JSR there
lda INITAD
ora INITAD+1
beq readheader
jsr do_init
lda #0
sta INITAD
sta INITAD+1
beq readheader ; branch always
do_init
jmp (INITAD)
close_file:
jsr fclose
; JSR through RUNAD if it's not zero
lda RUNAD
ora RUNAD+1
beq norun
jsr do_run
jmp (DOSVEC) ; does not return
; If there was no run address, we were probably loading an R: driver, so
; our caller is safe to return to
norun
lda #0 ; return 0
tax
rts
read2bytes:
lda #tmp
sta ICBAL+$10
lda #2
sta ICBLL+$10
lda #0
sta ICBAH+$10
sta ICBLH+$10
read_segment:
lda #C_GETCHR
sta ICCOM+$10
ldx #$10
jmp CIOV
do_run
jmp (RUNAD)
fclose
lda #C_CLOSE
sta ICCOM+$10
ldx #$10
jmp CIOV
endmain
; word RUNAD
; word RUNAD+1
; word main
|