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
|
;;; reloc.s - relocate code. this is just one puzzle piece.
;;; see README.txt for details.
; uncomment to enable user-friendly "MEMLO is too high" error.
; adds ~80 bytes to the code size. also, makes reloc.xex non-relocatable.
; currently not a problem, but may be in the future.
;verbose_memlo_check = 1
.export _main
.include "atari.inc"
;start_addr = $6c00
start_addr = $71c0
; 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_pages = zp_addr ; 1 byte
table_ptr = zp_addr+1 ; 2 bytes
dest_ptr = zp_addr+3 ; 2 bytes
code_ptr = zp_addr+5 ; 2 bytes
shfreg = zp_addr+7 ; 1 byte
pagecount = zp_addr+8 ; 1 byte
.org start_addr - 6
.word $ffff
.word start_addr
.word end_addr - 1
_main:
; if MEMLO isn't on a page boundary, move it up to the next
; page, e.g. $1cfc => $1d00.
lda MEMLO
beq calc_offset
inc MEMLO+1
lda #0
sta MEMLO
calc_offset:
lda code_start+1
sec
sbc MEMLO+1
sta offset_pages
bcs relocate_code
.ifdef verbose_memlo_check
; whoops, MEMLO is too high, print message and wait for keystroke
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
.else
freeze: bcc freeze ; wait for the user to press Reset.
.endif
relocate_code:
; adjust addresses while moving the code.
; point to the relocation table...
lda #<table
sta table_ptr
lda #>table
sta table_ptr+1
; ...and to the code we're moving...
lda code_start
sta code_ptr
lda code_start+1
sta code_ptr+1
; ...and to the destination we're moving to.
lda MEMLO
sta dest_ptr
lda MEMLO+1
sta dest_ptr+1
; pagecount = (code_end >> 8) - (code_start >> 8) + 2
lda code_end+1
sec
sbc code_start+1
tax
inx
inx
stx pagecount
ldx #0
lda (table_ptr,x)
sta shfreg
; 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.
ldy #0
move_loop:
cpx #8
bne shiftit
; bump bitmap pointer
inc table_ptr
bne tpok
inc table_ptr+1
tpok:
; get next byte of bitmap
ldx #0
lda (table_ptr,x)
sta shfreg
shiftit:
; get next bit from bitmap byte
inx
lda (code_ptr),y
asl shfreg
bcc dontadj
sbc offset_pages
dontadj:
sta (dest_ptr),y
iny
bne move_loop
inc code_ptr+1
inc dest_ptr+1
dec pagecount
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
lda code_end
sec ; add 1 extra (we want code len + 1)
adc MEMLO
sta MEMLO
lda code_end+1
adc MEMLO+1
sta MEMLO+1 ; MEMLO now MEMLO + code length + 1 byte
; do we have a run address?
lda code_run+1
beq do_init ; no: $00xx not valid.
; point RUNAD at adjusted run address.
sec
sbc offset_pages
sta RUNAD+1
lda code_run
sta RUNAD
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.
sec
sbc offset_pages ; call at relocated location, of course.
sta code_init+1
jmp (code_init)
; done
done:
rts
.ifdef verbose_memlo_check
whoops_msg: .byte "MEMLO is too high! Press any key to exit.", EOL
whoops_len = (*-whoops_msg)
.endif
end_addr:
.out .sprintf("reloc.s code size $%04x (%d)", (* - start_addr), (* - start_addr))
; this was for testing only. mkrelocxex.c adds the init address.
; .word INITAD
; .word INITAD+1
; .word _main
|