summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/i386/ts_386ex/tools/dos_sup/loader_hybrid_com.asm
blob: cd951ec8fd4ee92c1e48d4fa0c5dea09dcc77dfe (plain) (blame)
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