aboutsummaryrefslogtreecommitdiff
path: root/reloc.s
blob: c4c3c580b7454795ac9082d83a49e107a4a1b4fe (plain)
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

 .export _main
 .include "atari.inc"

 start_addr = $6c00

 ; mkrelocxex.c appends this stuff.
 code_start = end_addr
 code_end   = end_addr+2
 code_run   = end_addr+4
 code_init  = end_addr+6
 table      = end_addr+8

 zp_addr    = FR0
 offset_lo  = zp_addr
 offset_hi  = zp_addr+1
 table_ptr  = zp_addr+2 ; 2 bytes
 dest_ptr   = table_ptr
 code_ptr   = zp_addr+4 ; 2 bytes
 fixup      = zp_addr+6

 .org start_addr - 6
 .word $ffff
 .word start_addr
 .word end_addr - 1

_main:
 lda code_start
 sec
 sbc MEMLO

 sta offset_lo
 lda code_start+1
 sbc MEMLO+1
 sta offset_hi

 bcs memlo_ok

 ; whoops, MEMLO is too high
whoops:
 ldx #0
 lda #<whoops_msg
 sta ICBAL
 lda #>whoops_msg
 sta ICBAL+1
 lda #whoops_len
 sta ICBLL
 stx ICBLH
 lda #PUTCHR
 sta ICCOM
 jsr CIOV
exitwait:
 lda CH
 cmp #$ff
 beq exitwait
 lda #$ff
 sta CH
 lda #0
 sta COLOR2
 rts

memlo_ok:
 ; 1st fixup pass, hi bytes: table comes right after our code
 sta fixup
 lda #<table
 sta table_ptr
 lda #>table
 sta table_ptr+1
 jsr fixup_addrs
 
 ; 2nd fixup pass, lo bytes: table_ptr already points to table
 lda offset_lo
 sta fixup
 jsr fixup_addrs

 ; absolute addresses are fixed up, now move the code.
 lda code_start
 sta code_ptr
 lda code_start+1
 sta code_ptr+1
 lda MEMLO
 sta dest_ptr
 lda MEMLO+1
 sta dest_ptr+1

 ; x = (code_end >> 8) - (code_start >> 8) + 2
 lda code_end+1
 sec
 sbc code_start+1
 tax
 inx
 inx
 ldy #0

 ; this moves a page at a time, meaning if code_end isn't
 ; on an even page boundary, we move a little more than
 ; needed. it won't hurt anything, and it follows the
 ; KISS principle.
move_loop:
 lda (code_ptr),y
 sta (dest_ptr),y
 iny
 bne move_loop
 inc code_ptr+1
 inc dest_ptr+1
 dex
 bne move_loop


 ; bump MEMLO to point one byte past the end of the moved code.
 lda code_end
 sec
 sbc code_start
 sta code_end
 lda code_end+1
 sbc code_start+1
 sta code_end+1
 inc code_end
 bne ceok
 inc code_end+1 ; code_end is now the code length + 1 byte
ceok:
 lda code_end
 clc
 adc MEMLO
 sta MEMLO
 lda code_end+1
 adc MEMLO+1
 sta MEMLO+1 ; MEMLO now MEMLO + code length + 1 byte

 ; is RUNAD in our code space? If not, it points somewhere
 ; within DOS, and shouldn't be altered.
 lda RUNAD+1
 cmp code_start+1
 bcc do_init

 ; fix up RUNAD
 lda RUNAD
 sec
 sbc offset_lo
 lda RUNAD+1
 sbc offset_hi

do_init:
 ; if there's an init address, call it (just like DOS would).
 lda code_init+1
 beq done          ; if hi byte is 0, assume lo byte is also 0.

 lda code_init     ; subtract offset
 sec
 sbc offset_lo
 sta code_init
 lda code_init+1
 sbc offset_lo
 sta code_init+1

 jmp (code_init)

 ; done
done:
 rts

fixup_addrs:
 ldy #1
 lda (table_ptr),y
 sta code_ptr+1
 dey
 lda (table_ptr),y
 sta code_ptr
 inc table_ptr  ; point to next entry
 bne tp1ok
 inc table_ptr+1
tp1ok:
 inc table_ptr
 bne tp2ok
 inc table_ptr+1
tp2ok:
 ora code_ptr+1 ; quit if we hit $0000 in the table
 beq done
 lda (code_ptr),y ; Y still 0
 sec
 sbc fixup
 sta (code_ptr),y
 jmp fixup_addrs

whoops_msg: .byte "MEMLO is too high! Press any key to exit.", EOL
 whoops_len = (*-whoops_msg)

end_addr:

; this was for testing only. mkrelocxex.c adds the init address.
; .word INITAD
; .word INITAD+1
; .word _main