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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
|
; loader_hybrid_com.asm
;
; This is a DOS command-line loader for RTEMS executables running on
; the Technologic Systems TS-1325 Embedded PC.
;
; It loads a DOS file given on the command line to the address `KernelBase',
; and then transfers control there. It uses DOS file I/O commands to read from
; the A: flash disk, and direct memory access to read from the C: ramdisk.
;
; Copying uses protected flat mode, so kernelbase could be above 1MB.
; It does not initialize protected mode before transferring control
; to the RTEMS executable image.
;
; Compile with: nasm -o loader.com loader_hybrid_com.asm
;
; Tony Ambardar (c) 1999
; E.C.E. Department
; University of British Columbia
%include "ts1325.inc" ; Some useful LED and button macros
; IMPORTANT: [org xxx] MUST be the same as RelocAddr below.
[org E000h]
[bits 16]
; Only these three definitions may need to change
KernelBase equ 08000h ; Where (32-bit) to locate and run RTEMS executable
RelocSeg equ 9000h ; Segment to relocate code.
RelocAddr equ 0E000h ; Address to relocate code, same as "org" above
; Next three used in GDT
RelocBase equ RelocSeg*16
Reloc15 equ RelocBase & 0FFFFh
Reloc23 equ RelocBase / 10000h
Buffer equ RelocAddr+400h ; In same segment as RelocSeg
BuffSiz equ 200h ; Size of disk read + copy
StackSeg equ RelocSeg
StackSiz equ 40h
StackAddr equ Buffer+BuffSiz+StackSiz
; Used to jump to kernel in real mode
KernelAddr equ KernelBase & 0FFFFh
KernelSeg equ (KernelBase - KernelAddr) / 16
; Used to load from the ramdisk
Extended equ 100000h ; Start of extended memory / C: ramdisk
OffsetBPB equ 0Bh ; Start of BIOS param block in bootsector
; Command-line parameters
ParamLen equ 80h ; Byte length of command line params
ParamStr equ 82h ; Start of param string
; The ORG address above means pre-relocation addresses are wrong. The
; following macro fixes these up.
%define PRE_RELOC_ADDR(addr) (addr-CodeStart+100h)
CodeStart:
mov dx, PRE_RELOC_ADDR(Greet)
mov ah, 9h
int 21h
mov ax, 0b021h ; Exit to DOS if push-button switch pressed
int 15h
and al, 01h ; Bit 0 == 0 if button pressed
jz ButtonExit
xor cx, cx
mov cl, [ParamLen] ; See if there is a command line arg
jcxz NameError
dec cx ; Nix leading space. Is this standard?
cmp cx, 12 ; Limit to 12 chars: e.g. ABCDEFGH.IJK
jg NameError
; Damn. Should make sure of no ':' or '\' chars too.
; Required by "relocated" [org] statement above
mov di, PRE_RELOC_ADDR(FName)
mov si, ParamStr
repne
movsb ; Copy command line arg
; Make sure no ':' in filename. This forces using the default dir.
mov di, PRE_RELOC_ADDR(FName)
mov al, ':'
mov cx, 12
repne
scasb
je NameError
jmp Relocate
ButtonExit:
mov dx, PRE_RELOC_ADDR(Button)
jmp short DosPrint
NameError:
mov dx, PRE_RELOC_ADDR(FError)
jmp short DosPrint
DosError: ; Only call this AFTER relocation
mov dx, RError
DosPrint:
mov ah, 9h
int 21h
DosExit:
mov ax, 04C00h ; DOS Function: Exit program
int 21h ; Call DOS. Terminate Program
Relocate: ; Move this code down to RelocAddr
cld
mov ax, RelocSeg
mov es, ax ; Set destination = RelocSeg:RelocAddr
mov di, RelocAddr
mov si, 100h ; Source is ds:0100h i.e. a COM file
mov cx, CodeEnd - CodeStart ; Size of all code
repne
movsb
; continue in copied code
jmp RelocSeg:RelocAddr + (RelocStart - CodeStart)
RelocStart:
cli
mov ax, StackSeg
mov ss, ax
mov sp, StackAddr
mov ax, cs
mov ds, ax
mov es, ax ; Setup segments and stack
sti
mov ah, 19h
int 21h
mov [DDrive], al ; Save current default drive
mov ax, 3d00h ; DOS Function: Open the file for reading
mov dx, FName ; Presume DS points at filename segment
int 21h
jc DosError
GoodOpen:
mov [FHndl], ax ; Save file handle
mov al, [DDrive] ; Check if loading from C: drive (ramdisk)
cmp al, 2
je LoadRamdisk
LoadDosdisk:
; Here we are loading from A: drive. Use DOS calls to load the file into
; extended memory. Then copy from extended memory to `KernelBase'. This way
; we avoid overwriting DOS file I/O structures if reading directly into
; conventional (<640K) memory.
mov edi, Extended ; Destination for code read @ 1 Meg
ReadLoop:
mov ah,3fh ; DOS Function: Read data from the file
mov bx, [FHndl]
mov dx, Buffer ; Address of data buffer
mov cx, BuffSiz ; Request BuffSiz bytes
int 21h
jc DosError
GoodRead:
cmp ax, cx ; EOF reached? AX = # bytes read
pushf
add ax, 3
shr ax, 2 ; Copy buffer by dwords, # = (ax + 3)/4
movzx ecx, ax
mov esi, RelocBase + Buffer ; Source for copy, destination is in edi
call CopyData32 ; Do protected-mode copy
popf
je ReadLoop ; Still data left, so read next chunk
mov esi, Extended ; Source for copy @ 1 Meg
mov ecx, edi ; Make count in dwords
sub ecx, esi
add ecx, 3
shr ecx, 2
mov edi, KernelBase ; Destination copy
call CopyData32 ; Move code into conventional memory
jmp RunKernel
LoadRamdisk:
; Here we are loading from C: drive. Use protected mode to directly access
; the virtual disk sectors in extended memory and copy to `KernelBase'.
; This way we avoid using DOS file I/O calls, except for an `open' earlier
; which tells us the file exists.
; Copy C: "bootsector" to buffer and save the BIOS parameter block
mov esi, Extended
mov edi, RelocBase + Buffer ; Must be a 32-but address...
mov ecx, 80h
call CopyData32
mov si, Buffer + OffsetBPB
mov di, SavBPB
mov cx, EndBPB - SavBPB
repne
movsb
; Calculate FAT, root dir, and data start addresses for the ramdisk
xor eax, eax
mov ebx, eax
mov ecx, ebx
mov ax, [ResSec]
mov bl, [NumFAT]
imul bx, [SecFAT]
mov cx, [NRoot]
shr cx, 4 ; 10h directory entries per sector
add bx, ax
add cx, bx
mov dx, [BpSect]
imul ax, dx
imul bx, dx
imul cx, dx
add eax, Extended
add ebx, Extended
add ecx, Extended
mov [BegFAT], eax
mov [BegRoot], ebx
mov [BegData], ecx
; Convert the saved filename to format used in directory entry. Assume
; there's a `.' in it. Hopefully this won't haunt us later...
mov di, FName ; Find the `.'
mov al, '.'
mov cx, 12
repne
scasb
mov bx, di ; di points to filename extension
mov di, DirName
mov si, FName
mov cx, bx ; Make count
sub cx, si
dec cx
repne ; Copy initial part of filename
movsb
mov di, bx ; Find the terminating zero
xor al,al
mov cx, 4
repne
scasb
mov cx, di ; Make count
sub cx, bx
dec cx
mov si, bx
mov di, DirName + 8
repne ; Copy filename extension
movsb
mov si, DirName ; Convert the stupid thing to upper case
mov di, si
mov cx, 11
Cvt2Upper:
lodsb
cmp al, 'a'
jb NotLow
cmp al, 'z'
ja NotLow
xor al, 20h
NotLow:
stosb
loop Cvt2Upper
; Now load in the root directory (temporarily) to find the first cluster
; of our file. Use KernelSeg:KernelAddr as temporary storage.
mov esi, [BegRoot]
mov edi, KernelBase
xor ecx, ecx
mov cx, [NRoot]
shl cx, 3 ; Each root entry is 8 dwords
call CopyData32
mov dx, [NRoot] ; Max # of dir entries
mov cx, KernelSeg ; Setup segment selector for comparison
mov es, cx
mov di, KernelAddr
FindEntry:
mov cx, 11
mov si, DirName
push di
rep cmpsb
pop di
je GotEntry
add di, 20h ; Point to next dir entry
dec dx
jnz FindEntry
int 3h ; Should never get here...
GotEntry:
mov eax, KernelBase ; Setup initial address for copy
mov [CurrDst], eax
add di, 32 - 6 ; Load first cluster number
mov ax, [es:di]
mov cx, ds ; Fix `es' selector just in case
mov es, cx
LoadKernel:
call LoadCluster ; Load cluster `ax' to [CurrDst], update [CurrDst]
call NextCluster ; Get next cluster number in ax
cmp ax, 0FF8h ; Repeat until EOF
jb LoadKernel
RunKernel:
mov ax, KernelSeg ; Setup data segment and transfer control
mov ds, ax
jmp KernelSeg:KernelAddr ; Huzzah!!
; Load cluster `ax' to [CurrDst], update [CurrDst]
LoadCluster:
push ax
sub ax, 2 ; Cluster numbers start at 2
movzx eax, ax
xor ecx, ecx ; Calculate bytes in a cluster
mov cl, [SpClst]
imul cx, [BpSect]
imul eax, ecx
add eax, [BegData] ; Start of cluster
shr ecx, 2 ; Cluster size in dwords
mov esi, eax ; Copy source
mov edi, [CurrDst] ; Copy destination
call CopyData32
mov [CurrDst], edi ; Update dest
pop ax ; Restore cluster number
ret
; Search FAT (FAT12 format) for next cluster in file after `ax'.
NextCluster:
movzx ecx, ax ; Calculate offset into FAT
shr ax, 1
pushf
add cx, ax
mov esi, [BegFAT] ; Copy word containing next cluster to buffer
add esi, ecx
mov edi, RelocBase + Buffer
xor ecx, ecx
inc ecx
call CopyData32
mov ax, [Buffer] ; Handle odd/even cluster numbers
popf
jnc EvenCluster
shr ax, 4
EvenCluster:
and ax, 0FFFh
ret
; Enable the A20 line for accesses to extended memory.
EnableA20:
in al,92h
or al,2
jmp short $+2
jmp short $+2
jmp short $+2
out 92h,al
ret
; The CopyData32 routine copies ecx dwords from esi to edi. Both esi
; and edi hold 32-bit values. CopyData32 runs in 32-bit protected mode.
CopyData32:
cli
call EnableA20 ; Put here in case file I/O screws with this
; or with the GDTR
lgdt [GDTStart] ; Initialize GDTR for 32-bit protected mode
mov eax, cr0
or al, 1
mov cr0, eax ;go to real flat mode
; LED_GRN
; PSW_WAIT
jmp dword 8h : RelocBase+ProtJmp
[bits 32]
ProtJmp:
; LED_YEL
; PSW_WAIT
mov ax, 10h
mov ds, ax
mov es, ax
mov ss, ax
rep movsd ;copy the sector to where it should be
mov ax, 20h
mov ds, ax
mov es, ax
mov ss, ax
; LED_RED
; PSW_WAIT
jmp 18h : RealJmp1 ;use code segment with 64K limit
[bits 16]
RealJmp1:
; LED_OFF
; PSW_WAIT
mov eax, cr0 ;back to real segmented mode
and eax, 0fffffffeh
mov cr0, eax
jmp RelocSeg : RealJmp2
RealJmp2:
; LED_GRN
; PSW_WAIT
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
sti
ret
; Storage for a Dos 3+ BIOS Parameter Block (for the C: ramdisk)
SavBPB:
BpSect dw 0h ; Bytes per sector, always 512
SpClst db 0h ; Sectors per cluster
ResSec dw 0h ; Num of reserved sectors
NumFAT db 0h ; Num of FATs
NRoot dw 0h ; Num of root directory entries
TotSec dw 0h ; Total sectors
Media db 0h ; Media descriptor byte
SecFAT dw 0h ; Sectors per FAT
EndBPB:
CurrDst dd 0h ; Current destination address for copying RTEMS exec
; Important (32-bit) address for the C: ramdisk
BegFAT dd 0h ; Start of the FAT
BegRoot dd 0h ; Start of root directory
BegData dd 0h ; Start of data clusters
DDrive db 0h ; Default drive: 0h = A:, 2h = C:
DirName times 11 db 32 ; Room for 8.3 directory entry name
FName times 13 db 0 ; Room for a 12 character null-terminated string
FHndl dw 0000h
Greet db "RTEMS DOS Loader (c) 1999 Tony R. Ambardar",13,10,"$"
Button db "Button pressed -- Aborting.",13,10,"$"
FError db "Missing or incorrect file name.",13,10,"$"
RError db "Error opening or reading file.",13,10,"$"
; Global Descriptor Table used for protectd mode.
; Store the GDTR in the first null GDT entry
GDTStart:
dw GDTEnd - GDTStart - 1
dd RelocBase + GDTStart
dw 0
; base=0h, limit=4Gb, present, code, exec/read, conform, 32-bit
dw 0ffffh ;seg. lim. [15:0]
dw 0 ;base[15:0]
db 0 ;base[23:16]
db 9eh ;p=1,dpl=0,s=1 ; code: execute/read, conforming
db 0cfh ;c: gran=4K, D/B=1(32-bit) ; f: seg. lim. [19:16]
db 0 ;base[31:24]
; base=0h, limit=4Gb, present, data, read/write exp. up, 32-bit SP
dw 0ffffh ;seg. lim. [15:0]
dw 0 ;base[15:0]
db 0 ;base[23:16]
db 92h ;p=1,dpl=0,s=1 ; data: read/write expand-up
db 0cfh ;c: gran=4K, D/B=1(32-bit) ; f: seg. lim. [19:16]
db 0 ;base[31:24]
; base=0h, limit=ffffh, present, code, exec/read, conform, 16-bit
; NOTE: this descriptor is used to change back to real mode.
dw 0ffffh ;seg. lim. [15:0]
dw Reloc15 ;base[15:0]
db Reloc23 ;base[23:16]
db 9eh ;p=1,dpl=0,s=1 ; code: execute/read, conforming
db 000h ;4: gran=1 byte, D/B=0(16-bit) ; 0: seg. lim. [19:16]
db 0 ;base[31:24]
; base=0h, limit=ffffh, present, data, read/write exp. up, 16-bit SP
; NOTE: this descriptor is used to change back to real mode.
dw 0ffffh ;seg. lim. [15:0]
dw Reloc15 ;base[15:0]
db Reloc23 ;base[23:16]
db 92h ;p=1,dpl=0,s=1 ; data: read/write expand-up
db 000h ;0: gran=1 byte, D/B=0(16-bit) ; 0: seg. lim. [19:16]
db 0 ;base[31:24]
GDTEnd:
CodeEnd: ; end-of-code marker for copy
|